groupcache/README.md

153 lines
5.3 KiB
Markdown
Raw Normal View History

2013-07-24 00:18:04 +00:00
# groupcache
A modified version of [group cache](https://github.com/golang/groupcache) with
support for `context.Context`, [go modules](https://github.com/golang/go/wiki/Modules),
and explicit key removal and expiration. See the `CHANGELOG` for a complete list of
modifications.
2013-07-24 00:18:04 +00:00
## 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/mailgun/groupcache
2013-07-24 00:18:04 +00:00
### 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. 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
2013-07-24 00:18:04 +00:00
### **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".
2013-07-24 00:18:04 +00:00
## 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
2013-07-31 07:45:16 +00:00
other callers come in, via the same process or via RPC requests
2013-07-24 00:18:04 +00:00
from peers, they block waiting for the load to finish and get the
2013-07-31 07:45:16 +00:00
same answer. If not, RPC to the peer that's the owner and get
2013-07-24 00:18:04 +00:00
the answer. If the RPC fails, just load it locally (still with
local dup suppression).
2013-07-24 00:18:04 +00:00
## Example
```go
import (
"context"
"fmt"
"log"
"time"
"github.com/mailgun/groupcache/v2"
)
func ExampleUsage() {
2019-06-10 20:35:07 +00:00
// Keep track of peers in our cluster and add our instance to the pool `http://localhost:8080`
pool := groupcache.NewHTTPPoolOpts("http://localhost:8080", &groupcache.HTTPPoolOptions{})
2019-06-10 20:35:07 +00:00
// Add more peers to the cluster
pool.Set("http://peer1:8080", "http://peer2:8080")
2019-06-10 20:35:07 +00:00
server := http.Server{
Addr: "localhost: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`
if user, err := fetchUserFromMongo(ctx, id); err != nil {
return err
}
// Set the user in the groupcache to expire after 5 minutes
if err := dest.SetProto(&user, time.Now().Add(time.Minute*5)); err != nil {
return err
}
return nil
},
))
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)
}
}
```
2013-07-24 00:18:04 +00:00