Singleflight is a really interesting package that 'provides a duplicate function call suppression mechanism.' It is rarely used but very powerful!

An overlooked Go Package: Single Flight


The singleflight package in Go provides a mechanism to suppress duplicate function calls, ensuring that only one execution is in-flight for a given key at a time. This is particularly useful in scenarios where multiple goroutines might simultaneously request the same resource, such as fetching data from an API or a database.

Why Use singleflight?

Consider an application that fetches data from an API or a database. If several requests trigger the same call, this might result in multiple identical database or API calls. These redundant requests can strain your system, leading to wasted CPU cycles, memory, and bandwidth. By using singleflight, we can eliminate these duplicate calls efficiently.

Example Usage

Here’s an example demonstrating how to use singleflight:

package main

import (
	"fmt"
	"sync"
	"time"

	"golang.org/x/sync/singleflight"
)

var (
	group singleflight.Group
	cache = make(map[string]string)
	mu    sync.Mutex
)

func fetchData(key string) (string, error) {
	// Simulate a slow operation
	time.Sleep(2 * time.Second)
	return "data for " + key, nil
}

func getData(key string) (string, error) {
	mu.Lock()
	if data, found := cache[key]; found {
		mu.Unlock()
		return data, nil
	}
	mu.Unlock()

	v, err, _ := group.Do(key, func() (interface{}, error) {
		data, err := fetchData(key)
		if err != nil {
			return nil, err
		}
		mu.Lock()
		cache[key] = data
		mu.Unlock()
		return data, nil
	})
	if err != nil {
		return "", err
	}
	return v.(string), nil
}

func main() {
	keys := []string{"key1", "key1", "key2", "key1", "key2"}
	var wg sync.WaitGroup
	for _, key := range keys {
		wg.Add(1)
		go func(k string) {
			defer wg.Done()
			data, err := getData(k)
			if err != nil {
				fmt.Println("Error:", err)
				return
			}
			fmt.Println("Fetched:", data)
		}(key)
	}
	wg.Wait()
}

In this example, even though we have multiple goroutines simultaneously requesting data for the same key, the fetchData function will only be called once per unique key due to the group.Do function from singleflight.

You may also see singleflight used in serverless scenarios. For instance, Google App Engine uses it as part of its init function since it does not have a main.go.