Skip to content

Commit cf916be

Browse files
authored
Merge pull request #8 from groupcache/thrawn/improve-docs
Improved readme documentation
2 parents 8df4c28 + b788ce5 commit cf916be

File tree

5 files changed

+137
-55
lines changed

5 files changed

+137
-55
lines changed

README.md

Lines changed: 101 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,47 @@
1-
# groupcache
21

3-
groupcache is a caching and cache-filling library, intended as a
4-
replacement for memcached in many cases.
2+
<h2 align="center">
3+
<img src="docs/groupcache-logo.png" alt="GroupCache Logo" width="800" /><br />
4+
Distributed Cache Library
5+
</h2>
56

6-
For API docs and examples, see http://godoc.org/github.com/groupcache/groupcache-go/v3
7-
7+
---
88

9-
## Comparing Groupcache to memcached
9+
[![CI](https://github.com/groupcache/groupcache-go/workflows/CI/badge.svg)](https://github.com/groupcache/groupcache-go/actions?query=workflow:"CI")
10+
[![GitHub tag](https://img.shields.io/github/tag/groupcache/groupcache-go?include_prereleases=&sort=semver&color=blue)](https://github.com/groupcache/groupcache-go/releases/)
11+
[![License](https://img.shields.io/badge/License-Apache-blue)](#license)
1012

11-
### **Like memcached**, groupcache:
12-
13-
* shards by key to select which peer is responsible for that key
14-
15-
### **Unlike memcached**, groupcache:
16-
17-
* does not require running a separate set of servers, thus massively
18-
reducing deployment/configuration pain. groupcache is a client
19-
library as well as a server. It connects to its own peers.
20-
21-
* comes with a cache filling mechanism. Whereas memcached just says
22-
"Sorry, cache miss", often resulting in a thundering herd of
23-
database (or whatever) loads from an unbounded number of clients
24-
(which has resulted in several fun outages), groupcache coordinates
25-
cache fills such that only one load in one process of an entire
26-
replicated set of processes populates the cache, then multiplexes
27-
the loaded value to all callers.
28-
29-
* does not support versioned values. If key "foo" is value "bar",
30-
key "foo" must always be "bar".
31-
32-
## Loading process
13+
Groupcache is a Go-based caching and cache-filling library designed to replace traditional caching solutions
14+
like MEMCACHED and REDIS in many scenarios.
3315

34-
In a nutshell, a groupcache lookup of **Get("foo")** looks like:
16+
## Why Use Groupcache?
17+
- Cost Reduction: Eliminates the need for external system dependencies and additional server infrastructure
18+
- Increased efficiency: Minimizes network calls to external systems and data sources
19+
- Load Protection: Prevents the thundering herd problem through the use of `singleflight` synchronization
3520

36-
(On machine #5 of a set of N machines running the same code)
21+
## How It Works
22+
Groupcache functions as an in-memory read-through cache with the following workflow:
23+
- When your application requests data, Groupcache uses key sharding to determine key ownership
24+
- For locally-owned keys:
25+
- If the key exists in the local cache, data is returned immediately
26+
- If the key is missing, Groupcache retrieves it from the data source, caches it, and returns it
27+
- Future requests for the same key are served directly from the hot cache
28+
- For keys owned by other peers:
29+
- Groupcache forwards the request to the appropriate peer instance
30+
- The owning peer handles the cache lookup and retrieval process
3731

38-
1. Is the value of "foo" in local memory because it's super hot? If so, use it.
32+
This architecture provides the following benefits:
33+
- Network efficiency by avoiding requests for locally-owned keys
34+
- Load protection by channeling identical key requests to a single Groupcache instance within the cluster
35+
- Avoid unnecessary network calls to external cache systems by using local memory for the cache
3936

40-
2. Is the value of "foo" in local memory because peer #5 (the current
41-
peer) is the owner of it? If so, use it.
37+
Thanks to `singleflight` synchronization, even if your application makes millions of requests for a
38+
specific key (e.g., "FOO"), Groupcache will only query the underlying data source once.
4239

43-
3. Amongst all the peers in my set of N, am I the owner of the key
44-
"foo"? (e.g. does it consistent hash to 5?) If so, load it and
45-
store in the local cache.. If other callers come in, via the same
46-
process or via RPC requests from peers, they block waiting for the load
47-
to finish and get the same answer. If not, RPC to the peer that's the
48-
owner and get the answer. If the RPC fails, just load it locally (still with
49-
local dup suppression).
40+
<div align="center">
41+
<img src="docs/simplified-diagram.png" alt="Simplified Diagram" width="500" /><br />
42+
</div>
5043

51-
## Example
44+
## Usage
5245

5346
```go
5447
import (
@@ -138,8 +131,38 @@ func ExampleUsage() {
138131
d.Shutdown(ctx)
139132
}
140133
```
134+
# Concepts
135+
### Groups
136+
GroupCache provides functionality to create multiple distinct "groups" through its `NewGroup()` function.
137+
Each group serves as a separate namespace or storage pool that is distributed across peers using consistent hashing.
138+
You should create a new group whenever you need to prevent key conflicts between similar or identical key spaces.
139+
140+
### GetterFunc
141+
When creating a new group with `NewGroup()`, you must provide a `groupcache.GetterFunc`. This function serves as the
142+
read-through mechanism for retrieving cache values that aren't present in the local cache. The function works in
143+
conjunction with `singleflight` to ensure that only one request for a specific key is processed at a time. Any
144+
additional requests for the same key will wait until the initial `groupcache.GetterFunc` call completes. Given
145+
this behavior, it's crucial to utilize the provided `context.Context` to properly handle timeouts and cancellations.
146+
147+
### The Cache Loading process
148+
In a nutshell, a groupcache lookup of **Get("foo")** looks like:
149+
150+
(On machine #5 of a set of N machines running the same code)
151+
152+
1. Is the value of FOO in local cache because it's super hot? If so, use it.
153+
2. Is the value of FOO in local memory because peer #5 (the current
154+
peer) is the owner of it? If so, use it.
155+
3. Amongst all the peers in my set of N, am I the owner of the key
156+
FOO? (e.g. does it consistent hash to 5?) If so, load it and
157+
store in the local cache. If other callers come in, via the same
158+
process or via RPC requests from peers, they block waiting for the load
159+
to finish and get the same answer. If not, RPC to the peer that's the
160+
owner and get the answer. If the RPC fails, just load it locally (still with
161+
local dup suppression).
141162

142-
# HTTP integration
163+
<img src="docs/sequence-diagram.svg" alt="Simplified Diagram" width="800" /><br />
164+
165+
# Integrating GroupCache with HTTP Services
143166
This is a quick guide on how to use groupcache in a service that is already listening for HTTP requests. In some
144167
circumstances you may want to have groupcache respond using the same HTTP port that non groupcache requests are
145168
received through. In this case you must explicitly create the `transport.HttpTransport` which can then be passed
@@ -198,9 +221,31 @@ func main() {
198221
}
199222
```
200223

201-
### Otter Cache
224+
# Comparing Groupcache to memcached
225+
226+
### **Like memcached**, groupcache:
227+
* shards by key to select which peer is responsible for that key
228+
229+
### **Unlike memcached**, groupcache:
230+
* does not require running a separate set of servers, thus massively
231+
reducing deployment/configuration pain. groupcache is a client
232+
library as well as a server. It connects to its own peers.
233+
* comes with a cache filling mechanism. Whereas memcached just says
234+
"Sorry, cache miss", often resulting in a thundering herd of
235+
database (or whatever) loads from an unbounded number of clients
236+
(which has resulted in several fun outages), groupcache coordinates
237+
cache fills such that only one load in one process of an entire
238+
replicated set of processes populates the cache, then multiplexes
239+
the loaded value to all callers.
240+
* does not support versioned values. If key "foo" is value "bar",
241+
key "foo" must always be "bar".
242+
243+
# Pluggable Internal Cache
244+
GroupCache supports replacing the default LRU cache implementation with alternative implementations.
245+
202246
[Otter](https://maypok86.github.io/otter/) is a high performance lockless cache suitable for high concurrency environments
203-
where lock contention is an issue. Typically, servers with over 40 CPUs and lots of concurrent requests.
247+
where lock contention is an issue. Typically, servers with over 40 CPUs and lots of concurrent requests would benefit
248+
from using an alternate cache implementation like Otter.
204249

205250
```go
206251
import "github.com/groupcache/groupcache-go/v3/contrib"
@@ -217,11 +262,11 @@ instance := groupcache.New(groupcache.Options{
217262
})
218263
```
219264

220-
#### Cache Size Implications
265+
#### Cache Size Implications for Otter
221266
Due to the algorithm Otter uses to evict and track cache item costs, it is recommended to
222267
use a larger maximum byte size when creating Groups via `Instance.NewGroup()` if you expect
223-
your cached items to be very large. This is because groupcache uses a "Main Cache" and a
224-
"Hot Cache" system where the "Hot Cache" is 1/8th the size of the maximum bytes requested.
268+
your cached items to be very large. This is because groupcache uses a "Main Cache" and a
269+
"Hot Cache" system where the "Hot Cache" is 1/8th the size of the maximum bytes requested.
225270

226271
Because Otter cache may reject items added to the cache which are larger than 1/10th of the
227272
total capacity of the "Hot Cache" this may result in a lower hit rate for the "Hot Cache" when
@@ -236,10 +281,16 @@ bytes in a Group to accommodate the maximum cache item. If you have no estimate
236281
of items in the groupcache, then you should monitor the `Cache.Stats().Rejected` stat for the cache
237282
in production and adjust the size accordingly.
238283

284+
285+
# Source Code Internals
286+
If you are reading this, you are likely in front of a Github page and are interested in building a custom transport
287+
or creating a Pull Request. In which case, the following explains the most of the important structs and how they
288+
interact with each other.
289+
239290
### Modifications from original library
240291
The original author of groupcache is [Brad Fitzpatrick](https://github.com/bradfitz) who is also the
241-
author of [memcached](https://memcached.org/). The original code repository for groupcache can be
242-
found [here](https://github.com/golang/groupcache) and appears to be abandoned. We have taken the liberty
292+
author of [memcached](https://memcached.org/). The original code repository for groupcache can be
293+
found [here](https://github.com/golang/groupcache) and appears to be abandoned. We have taken the liberty
243294
of modifying the library with additional features and fixing some deficiencies.
244295

245296
* Support for explicit key removal from a group. `Remove()` requests are
@@ -266,11 +317,7 @@ of modifying the library with additional features and fixing some deficiencies.
266317
transports can be used without needing access to the internals of the library.
267318
* Updated dependencies and use modern golang programming and documentation practices
268319
* Added support for optional internal cache implementations.
269-
270-
# Source Code Internals
271-
If you are reading this, you are likely in front of a Github page and are interested in building a custom transport
272-
or creating a Pull Request. In which case, the following explains the most of the important structs and how they
273-
interact with each other.
320+
* Many other code improvements
274321

275322
### groupcache.Instance
276323
Represents an instance of groupcache. With the instance, you can create new groups and add other instances to your
@@ -346,4 +393,3 @@ defer cluster.Shutdown(context.Background())
346393

347394
### Code Map
348395
![docs/code-diagram.png](docs/code-diagram.png)
349-

docs/groupcache-logo.png

137 KB
Loading

docs/sequence-diagram.mermaid

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
theme: base
3+
look: handDrawn
4+
---
5+
sequenceDiagram
6+
participant App
7+
participant LocalGroupCache
8+
participant PeerGroupCache
9+
participant DataSource
10+
11+
App->>LocalGroupCache: Request FOO
12+
alt FOO owned by LocalGroupcache
13+
alt FOO found in LocalGroupcache
14+
LocalGroupCache->>App: Return cached data
15+
else FOO not found in LocalGroupcache
16+
LocalGroupCache->>DataSource: Fetch data
17+
DataSource->>LocalGroupCache: Return data
18+
LocalGroupCache->>LocalGroupCache: Store in Main cache
19+
LocalGroupCache->>App: Return data
20+
else FOO owned by PeerGroupCache
21+
LocalGroupCache->>PeerGroupCache: Request FOO from peer
22+
alt FOO found in PeerGroupCache
23+
PeerGroupCache->>LocalGroupCache: Return cached data
24+
LocalGroupCache->>LocalGroupCache: Store data in Hot cache
25+
LocalGroupCache->>App: Return cached data
26+
else FOO not found in PeerGroupCache
27+
PeerGroupCache->>DataSource: Fetch data
28+
DataSource->>PeerGroupCache: Return data
29+
PeerGroupCache->>PeerGroupCache: Store in Local cache
30+
PeerGroupCache->>LocalGroupCache: Return data
31+
LocalGroupCache->>LocalGroupCache: Store in Hot cache
32+
LocalGroupCache->>App: Return data
33+
end
34+
end
35+
end

docs/sequence-diagram.svg

Lines changed: 1 addition & 0 deletions
Loading

docs/simplified-diagram.png

54.9 KB
Loading

0 commit comments

Comments
 (0)