singleflight

package module
v0.0.0-...-576d335 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2024 License: MIT Imports: 3 Imported by: 0

README

Build Status

singleflight

Package singleflight implements a call sharing mechanism.

Example usage

// This package demonstrates how an implementation of a HTTP server might use the singleflight
// package in order to minimize roundtrips to its database.
package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/smallstep/singleflight"
)

func main() {
	http.HandleFunc("/", handler)

	if err := http.ListenAndServe(":8080", nil); !errors.Is(err, http.ErrServerClosed) {
		log.Fatal(err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	name := r.URL.Query().Get("name")

	switch id, err := fetchCustomerID(r.Context(), name); {
	case err == nil:
		fmt.Fprintf(w, "%d", id)
	case errors.Is(err, errNotFound):
		renderCode(w, http.StatusNotFound)
	default:
		renderCode(w, http.StatusInternalServerError)
	}
}

// fetchCustomerID returns the id of the named customer.
//
// Concurrent callers of fetchCustomerID will share the result.
func fetchCustomerID(ctx context.Context, name string) (int64, error) {
	return caller.Call(ctx, name, doFetchCustomerID)
}

var (
	errNotFound = errors.New("not found")

	// caller is used by fetchCustomerID to reduce the number of calls to doFetchCustomerID.
	caller singleflight.Caller[string, int64]
)

func doFetchCustomerID(ctx context.Context) (id int64, err error) {
	time.Sleep(time.Millisecond << 7)

	if name := caller.KeyFromContext(ctx); name == "customer-1" {
		id = 1
	} else {
		err = errNotFound
	}

	return
}

func renderCode(w http.ResponseWriter, code int) {
	http.Error(w, http.StatusText(code), code)
}

Documentation

Overview

Package singleflight implements a call sharing mechanism.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Caller

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

Caller wraps the functionality of the call sharing mechanism.

A Caller must not be copied after first use.

func (*Caller[K, V]) Call

func (caller *Caller[K, V]) Call(ctx context.Context, key K, fn func(context.Context) (V, error)) (V, error)

Call calls fn and returns the results. Concurrent callers sharing a key will also share the results of the first call.

fn may access the key passed to Call via KeyFromContext.

func (*Caller[K, V]) KeyFromContext

func (*Caller[K, V]) KeyFromContext(ctx context.Context) K

KeyFromContext returns the key ctx carries. It panics in case ctx carries no key.

Jump to

Keyboard shortcuts

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