Understanding Go's singleflight: A Simple, Practical Source Code Walkthrough

This article explains how Go's singleflight utility uses a lock and map to deduplicate concurrent calls, walks through its core structs and the Do method implementation, and shows how it improves system availability during traffic spikes.

Nullbody Notes
Nullbody Notes
Nullbody Notes
Understanding Go's singleflight: A Simple, Practical Source Code Walkthrough

Repository: https://github.com/gofish2020/easysingleflight

Data structures

The implementation relies on a lock and a map. The lock guarantees that only one request proceeds at a time, while the map stores a *call object per unique key so that identical requests can reuse the same result.

type (
    Group struct {
        calls map[string]*call // lazy initialization
        mu    sync.Mutex
    }
)

The call struct holds the outcome of a request and a channel used to notify waiting goroutines when the computation finishes.

type (
    call struct {
        done chan struct{} // closed when processing completes
        val  interface{}   // result value
        err  error         // error flag
    }
)

Do method flow

The public Do method implements the singleflight logic:

Lock the group and lazily create the calls map if it is nil.

If the supplied key already exists in g.calls, unlock, block on the existing call.done channel, then return the stored val, err, and true to indicate a cached result.

If the key is absent, allocate a new *call, initialize its done channel, store it in the map, and unlock.

Execute the provided function fn inside an anonymous function. A deferred recovery captures any panic, converting it to an error via newPanicError and setting val to nil.

After fn returns, reacquire the lock, delete the key from the map, unlock, and close call.done to unblock any waiting goroutines.

Return the computed val, err, and false to indicate the result was freshly computed.

func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error, bool) {
    // 1. lock and ensure map exists
    g.mu.Lock()
    if g.calls == nil {
        g.calls = make(map[string]*call)
    }

    // 2. duplicate call – wait for existing result
    if c, ok := g.calls[key]; ok {
        g.mu.Unlock()
        <-c.done // block until result is ready
        return c.val, c.err, true
    }

    // 3. first caller – create call object
    cl := new(call)
    cl.done = make(chan struct{})
    g.calls[key] = cl
    // 4. unlock before executing fn
    g.mu.Unlock()

    func() {
        // defer to handle panic and cleanup
        defer func() {
            if p := recover(); p != nil {
                cl.err = newPanicError(p)
                cl.val = nil
            }
            // 6. delete key from map
            g.mu.Lock()
            delete(g.calls, key)
            g.mu.Unlock()
            // 7. notify waiting goroutines
            close(cl.done)
        }()
        // 5. execute fn
        cl.val, cl.err = fn()
    }()

    return cl.val, cl.err, false
}

The flow ensures that concurrent requests with the same key are coalesced into a single execution, improving system availability by preventing burst traffic from triggering circuit breakers.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Concurrencyhigh availabilityGomapLocksingleflight
Nullbody Notes
Written by

Nullbody Notes

Go backend development, learning open-source project source code together, focusing on simplicity and practicality.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.