Skip to content

Commit 180dd42

Browse files
committed
gocached, cmd/gocached: add storage tiering and a separate SQLite dir
Our AWS cache instance stores everything on its ephemeral NVMe disk, so the whole cache is lost on every reboot and its size is capped by the local disk. Let the blob directory live on a big slow filesystem (an S3 Files NFS mount) as the source of truth, with a new optional --hot-dir flag naming a fast local tier (NVMe) holding a bounded copy of recently used blobs, capped by --hot-capacity-gb. The new --sqlite-dir flag lets the metadata database live on its own volume (EBS), defaulting to the blob directory as before. PUTs write through to both tiers in one pass; a hot tier failure never fails the PUT since a later GET re-promotes. GETs try the hot tier first and asynchronously promote cold hits into it. The hot tier is tracked by an in-memory LRU index seeded from an mtime-ordered scan at startup, evicting least recently used files down to 95% of capacity; hot files are disposable copies, so eviction and reboot loss are always safe. When tiering is not enabled, behavior is unchanged. Updates tailscale/corp#44699 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: I298711f6ee38efa15304aad7295736197c3de084
1 parent d4ff6bc commit 180dd42

5 files changed

Lines changed: 1001 additions & 19 deletions

File tree

cmd/gocached/gocached.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import (
2222

2323
var (
2424
dir = flag.String("cache-dir", "", "cache directory, if empty defaults to <UserCacheDir>/gocached")
25+
sqliteDir = flag.String("sqlite-dir", "", "directory for the SQLite metadata database; if empty, defaults to the cache directory")
26+
hotDir = flag.String("hot-dir", "", "if non-empty, enable storage tiering with this directory as a fast tier (e.g. local NVMe) holding a bounded copy of recently used blobs; the cache directory remains the source of truth")
27+
hotCapacity = flag.Int("hot-capacity-gb", 600, "maximum size of the hot tier directory in GiB; only used with --hot-dir")
2528
verbose = flag.Bool("verbose", false, "be verbose")
2629
listen = flag.String("listen", ":31364", "listen address for the build-facing HTTP server")
2730
debugListen = flag.String("debug-listen", "", "if non-empty, listen address for the debug HTTP server (pprof, metrics, etc)")
@@ -65,12 +68,23 @@ func main() {
6568

6669
opts := []gocached.ServerOption{
6770
gocached.WithDir(*dir),
71+
gocached.WithSQLiteDir(*sqliteDir),
6872
gocached.WithVerbose(*verbose),
6973
gocached.WithMaxSize(int64(*maxSize) << 30),
7074
gocached.WithMaxAge(time.Duration(*maxAge) * 24 * time.Hour),
7175
gocached.WithShardPrefixLen(*shardPrefixLen),
7276
}
7377

78+
if *hotDir != "" {
79+
if *hotCapacity <= 0 {
80+
log.Fatal("--hot-capacity-gb must be positive when --hot-dir is set")
81+
}
82+
opts = append(opts,
83+
gocached.WithHotDir(*hotDir),
84+
gocached.WithHotCapacity(int64(*hotCapacity)<<30),
85+
)
86+
}
87+
7488
if *jwtIssuer != "" {
7589
if len(jwtClaims) == 0 {
7690
log.Fatal("must specify --jwt-claim at least once when --jwt-issuer is set")

0 commit comments

Comments
 (0)