Categorygithub.com/modernprogram/groupcache/v2
modulepackage
2.6.0
Repository: https://github.com/modernprogram/groupcache.git
Documentation: pkg.go.dev

# README

groupcache

A modified version of group cache with support for context.Context, go modules, and explicit key removal and expiration. See the CHANGELOG for a complete list of modifications.

Summary

groupcache is a caching and cache-filling library, intended as a replacement for memcached in many cases.

For API docs and examples, see http://godoc.org/github.com/modernprogram/groupcache/v2

Modifications from original library

  • Support for explicit key removal from a group. Remove() requests are first sent to the peer who owns the key, then the remove request is forwarded to every peer in the groupcache. NOTE: This is a best case design since it is possible a temporary network disruption could occur resulting in remove requests never making it their peers. In practice this scenario is very rare and the system remains very consistent. In case of an inconsistency placing a expiration time on your values will ensure the cluster eventually becomes consistent again.

  • Support for expired values. SetBytes(), SetProto() and SetString() now accept an optional time.Time which represents a time in the future when the value will expire. If you don't want expiration, pass the zero value for time.Time (for instance, time.Time{}). Expiration is handled by the LRU Cache when a Get() on a key is requested. This means no network coordination of expired values is needed. However this does require that time on all nodes in the cluster is synchronized for consistent expiration of values.

  • Now always populating the hotcache. A more complex algorithm is unnecessary when the LRU cache will ensure the most used values remain in the cache. The evict code ensures the hotcache never overcrowds the maincache.

Comparing Groupcache to memcached

Like memcached, groupcache:

  • shards by key to select which peer is responsible for that key

Unlike memcached, groupcache:

  • does not require running a separate set of servers, thus massively reducing deployment/configuration pain. groupcache is a client library as well as a server. It connects to its own peers.

  • comes with a cache filling mechanism. Whereas memcached just says "Sorry, cache miss", often resulting in a thundering herd of database (or whatever) loads from an unbounded number of clients (which has resulted in several fun outages), groupcache coordinates cache fills such that only one load in one process of an entire replicated set of processes populates the cache, then multiplexes the loaded value to all callers.

  • does not support versioned values. If key "foo" is value "bar", key "foo" must always be "bar".

Loading process

In a nutshell, a groupcache lookup of Get("foo") looks like:

(On machine #5 of a set of N machines running the same code)

  1. Is the value of "foo" in local memory because it's super hot? If so, use it.

  2. Is the value of "foo" in local memory because peer #5 (the current peer) is the owner of it? If so, use it.

  3. Amongst all the peers in my set of N, am I the owner of the key "foo"? (e.g. does it consistent hash to 5?) If so, load it. If other callers come in, via the same process or via RPC requests from peers, they block waiting for the load to finish and get the same answer. If not, RPC to the peer that's the owner and get the answer. If the RPC fails, just load it locally (still with local dup suppression).

Example

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/modernprogram/groupcache/v2"
)

func ExampleUsage() {

    // NOTE: It is important to pass the same peer `http://192.168.1.1:8080` to `NewHTTPPoolOpts`
    // which is provided to `pool.Set()` so the pool can identify which of the peers is our instance.
    // The pool will not operate correctly if it can't identify which peer is our instance.
    
    // Pool keeps track of peers in our cluster and identifies which peer owns a key.
    pool := groupcache.NewHTTPPoolOpts("http://192.168.1.1:8080", &groupcache.HTTPPoolOptions{})

    // Add more peers to the cluster You MUST Ensure our instance is included in this list else
    // determining who owns the key accross the cluster will not be consistent, and the pool won't
    // be able to determine if our instance owns the key.
    pool.Set("http://192.168.1.1:8080", "http://192.168.1.2:8080", "http://192.168.1.3:8080")

    server := http.Server{
        Addr:    "192.168.1.1:8080",
        Handler: pool,
    }

    // Start a HTTP server to listen for peer requests from the groupcache
    go func() {
        log.Printf("Serving....\n")
        if err := server.ListenAndServe(); err != nil {
            log.Fatal(err)
        }
    }()
    defer server.Shutdown(context.Background())

    // Create a new group cache with a max cache size of 3MB
    group := groupcache.NewGroup("users", 3000000, groupcache.GetterFunc(
        func(ctx context.Context, id string, dest groupcache.Sink) error {

            // Returns a protobuf struct `User`
            user, err := fetchUserFromMongo(ctx, id)
            if err != nil {
                return err
            }

            // Set the user in the groupcache to expire after 5 minutes
            return dest.SetProto(&user, time.Now().Add(time.Minute*5))
        },
    ))

    var user User

    ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
    defer cancel()

    if err := group.Get(ctx, "12345", groupcache.ProtoSink(&user)); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("-- User --\n")
    fmt.Printf("Id: %s\n", user.Id)
    fmt.Printf("Name: %s\n", user.Name)
    fmt.Printf("Age: %d\n", user.Age)
    fmt.Printf("IsSuper: %t\n", user.IsSuper)

    // Remove the key from the groupcache
    if err := group.Remove(ctx, "12345"); err != nil {
        log.Fatal(err)
    }
}

Note

The call to groupcache.NewHTTPPoolOpts() is a bit misleading. NewHTTPPoolOpts() creates a new pool internally within the groupcache package where it is uitilized by any groups created. The pool returned is only a pointer to the internallly registered pool so the caller can update the peers in the pool as needed.

# Packages

No description provided by the author
Package consistenthash provides an implementation of a ring hash.
Package groupcachepb is a generated protocol buffer package.
Package lru implements an LRU cache.
Package singleflight provides a duplicate function call suppression mechanism.
No description provided by the author

# Functions

AllocatingByteSliceSink returns a Sink that allocates a byte slice to hold the received value and assigns it to *dst.
ByteViewSink returns a Sink that populates a ByteView.
DeregisterGroupWithWorkspace removes group from group pool.
GetGroupWithWorkspace returns the named group previously created with NewGroup, or nil if there's no such group.
NewGroupWithWorkspace creates a coordinated group-aware Getter from a Getter.
NewHTTPPoolOptsWithWorkspace initializes an HTTP pool of peers with the given options.
NewHTTPPoolWithWorkspace initializes an HTTP pool of peers, and registers itself as a PeerPicker.
NewWorkspace creates an explicit workspace for workspace-aware APIs.
ProtoSink returns a sink that unmarshals binary proto values into m.
RegisterNewGroupHookWithWorkspace registers a hook that is run each time a group is created.
RegisterPeerPickerWithWorkspace registers the peer initialization function.
RegisterPerGroupPeerPickerWithWorkspace registers the peer initialization function, which takes the groupName, to be used in choosing a PeerPicker.
RegisterServerStartWithWorkspace registers a hook that is run when the first group is created.
SetLogger - this is legacy to provide backwards compatibility with logrus.
SetLoggerFromLogger - set the logger to an implementation of the Logger interface.
StringSink returns a Sink that populates the provided string pointer.
TruncatingByteSliceSink returns a Sink that writes up to len(*dst) bytes to *dst.

# Constants

The HotCache is the cache for items that seem popular enough to replicate to this node, even though it's not the owner.
The MainCache is the cache for items that this peer is the owner for.

# Variables

DefaultWorkspace is the default workspace used by non-workspace-aware APIs.
NowFunc returns the current time which is used by the LRU to determine if the value has expired.

# Structs

A ByteView holds an immutable view of bytes.
CacheStats are returned by stats accessors on Group.
ErrNotFound should be returned from an implementation of `GetterFunc` to indicate the requested value is not available.
ErrRemoteCall is returned from `group.Get()` when a remote GetterFunc returns an error.
A Group is a cache namespace and associated data loaded spread over a group of 1 or more machines.
HTTPPool implements PeerPicker for a pool of HTTP peers.
HTTPPoolOptions are the configurations of a HTTPPool.
LogrusLogger is an implementation of Logger that wraps logrus..
NoPeers is an implementation of PeerPicker that never finds a peer.
Stats are per-group statistics.
Workspace holds the "global" state for groupcache.

# Interfaces

A Getter loads data for a key.
Logger is a minimal interface that will allow us to use structured loggers, including (but not limited to) logrus.
PeerPicker is the interface that must be implemented to locate the peer that owns a specific key.
ProtoGetter is the interface that must be implemented by a peer.
A Sink receives data from a Get call.

# Type aliases

An AtomicInt is an int64 to be accessed atomically.
CacheType represents a type of cache.
A GetterFunc implements Getter with a function.