singleflight

package module
v1.0.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 24, 2026 License: MIT Imports: 4 Imported by: 1

README

Moonlight Singleflight

Performance Type Safety License Go Reference Go Report Card

"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away."

Moonlight Singleflight is a high-performance, generic, and zero-allocation implementation of the singleflight pattern for Go.

It was designed to replace the standard library's implementation in critical paths where Type Safety and GC Pressure are non-negotiable constraints.

⚡ Why Moonlight?

The standard golang.org/x/sync/singleflight is battle-tested but suffers from legacy constraints:

  1. Interface Boxing: It returns interface{}, forcing runtime reflection and type assertions (v.(string)).
  2. Heap Allocations: Even on cache misses (non-shared calls), it allocates closures and channels.
  3. Type Unsafe: A mismatch in type assertion causes a runtime panic.

Moonlight solves these by strictly adhering to a "Zero-Waste" philosophy:

Feature Standard Lib (x/sync) Moonlight Edition
Type Safety interface{} (Runtime Check) Generics [K, V] (Compile Time)
Allocations (Cache Miss) ~2 allocs/op 0 allocs/op
Allocations (Shared) Low Low
Overhead ~270ns ~220ns
Channel Creation Always Lazy (Only when needed)

🚀 Benchmarks

Benchmarks were conducted on an Intel Core i9-13900HX.

Benchmark_ThunderingHerd_Std-8      8108        140337 ns/op        12 B/op     0 allocs/op
Benchmark_ThunderingHerd_Moon-8     7903        141501 ns/op        22 B/op     0 allocs/op
Benchmark_RandomKeys_Std-8          8095        142929 ns/op        96 B/op     2 allocs/op
Benchmark_RandomKeys_Moon-8         8157        141236 ns/op         0 B/op     0 allocs/op
Benchmark_Overhead_Std-8            6059726      190.0 ns/op        75 B/op     0 allocs/op
Benchmark_Overhead_Moon-8           8650653      140.6 ns/op         3 B/op     0 allocs/op
  • RandomKeys (High Entropy): Simulates a cache-miss scenario where every request is unique. Moonlight achieves Zero Allocation by combining sync.Pool with a lazy-initialization strategy, reusing call objects safely.
  • Thundering Herd: Performance matches or exceeds the standard library while maintaining type safety.

📦 Installation

go get github.com/oy3o/singleflight

📖 Usage

Basic Usage

Stop casting interface{}. Define your types once, and let the compiler do the work.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/oy3o/singleflight"
)

func main() {
	// Create a Group typed for String Keys and User Struct Values.
	// No more casting interface{}!
	g := singleflight.NewGroup[string, *User]()

	ctx := context.Background()
	userID := "1001"

	// Do executes the function. If multiple goroutines call this with "1001",
	// only one function execution will happen.
	user, err, shared := g.Do(ctx, userID, func(ctx context.Context) (*User, error) {
		// Simulate DB call
		return getUserFromDB(ctx, userID)
	})

	if err != nil {
		panic(err)
	}

	fmt.Printf("User: %s, Shared: %v\n", user.Name, shared)
}

type User struct {
	Name string
}

func getUserFromDB(ctx context.Context, id string) (*User, error) {
	time.Sleep(10 * time.Millisecond)
	return &User{Name: "Moonlight"}, nil
}
Context Cancellation

Moonlight respects context propagation. If the leader (the execution) is cancelled, the context passed to the function is cancelled.

g.Do(ctx, key, func(ctx context.Context) (string, error) {
    select {
    case <-ctx.Done():
        return "", ctx.Err() // Handle cancellation gracefully
    case <-time.After(1 * time.Second):
        return "data", nil
    }
})

🧠 Design Philosophy

This implementation pushes Go's concurrency primitives to their limits:

  1. Lazy Synchronization: Channels are expensive. We only create them if a second caller actually arrives (dups > 0). If you are the only one (the "Leader"), the operation is purely synchronous.
  2. Object Pooling: We use a sync.Pool to reuse the internal call structs.
  3. Safety First: An object is only recycled if it is guaranteed to be "clean" (no panic, no pending waiters, no channels attached).

⚖️ License

Distributed under the MIT License. See LICENSE for more information.

✍️ Credits

Architected by Moonlight.

Created with ❤️ for oy3o. We don't just write code; we define the problem.


Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Group

type Group[K comparable, V any] struct {
	// contains filtered or unexported fields
}

Group 是 singleflight 的泛型实现,支持零值初始化。 相比标准库 x/sync/singleflight,提供三个差异化能力:

  • 泛型:消除 interface{} 的装箱/断言开销
  • Context:Follower 可因 context 取消而提前退出
  • sync.Pool:在无 Follower 的快路径上复用 call 对象

func (*Group[K, V]) Do

func (g *Group[K, V]) Do(
	ctx context.Context,
	key K,
	fn func(ctx context.Context) (V, error),
) (v V, err error, shared bool)

Do 对同一个 key 只允许一个 fn 在执行(Leader), 后续调用者(Follower)阻塞等待并共享结果。

shared 表示结果是否被多个调用者共享。

func (*Group[K, V]) Forget

func (g *Group[K, V]) Forget(key K)

Forget 使 Group 忘记指定 key。 下一次对该 key 的 Do 调用将执行 fn 而非等待先前的调用。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL