Skip to content

Commit 786176b

Browse files
Thomas StrombergThomas Stromberg
authored andcommitted
improve our disk caching layout for prefixes
1 parent 90566fc commit 786176b

File tree

2 files changed

+66
-9
lines changed

2 files changed

+66
-9
lines changed

persist/localfs/integration_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,64 @@ func TestFilePersist_Flush_Empty(t *testing.T) {
972972
}
973973
}
974974

975+
func TestFilePersist_Flush_RemovesFiles(t *testing.T) {
976+
dir := t.TempDir()
977+
fp, err := New[string, int]("test", dir)
978+
if err != nil {
979+
t.Fatalf("New: %v", err)
980+
}
981+
defer func() {
982+
if err := fp.Close(); err != nil {
983+
t.Logf("Close error: %v", err)
984+
}
985+
}()
986+
987+
ctx := context.Background()
988+
cacheDir := fp.(*persister[string, int]).Dir
989+
990+
// Store multiple entries
991+
for i := range 10 {
992+
if err := fp.Store(ctx, fmt.Sprintf("key-%d", i), i*100, time.Time{}); err != nil {
993+
t.Fatalf("Store: %v", err)
994+
}
995+
}
996+
997+
// Count .gob files on disk before flush
998+
countGobFiles := func() int {
999+
count := 0
1000+
_ = filepath.WalkDir(cacheDir, func(path string, d os.DirEntry, err error) error {
1001+
if err != nil {
1002+
return nil
1003+
}
1004+
if !d.IsDir() && filepath.Ext(path) == ".gob" {
1005+
count++
1006+
}
1007+
return nil
1008+
})
1009+
return count
1010+
}
1011+
1012+
beforeFlush := countGobFiles()
1013+
if beforeFlush != 10 {
1014+
t.Errorf("expected 10 .gob files before flush, got %d", beforeFlush)
1015+
}
1016+
1017+
// Flush
1018+
deleted, err := fp.Flush(ctx)
1019+
if err != nil {
1020+
t.Fatalf("Flush: %v", err)
1021+
}
1022+
if deleted != 10 {
1023+
t.Errorf("Flush deleted %d entries; want 10", deleted)
1024+
}
1025+
1026+
// Verify no .gob files remain
1027+
afterFlush := countGobFiles()
1028+
if afterFlush != 0 {
1029+
t.Errorf("expected 0 .gob files after flush, got %d", afterFlush)
1030+
}
1031+
}
1032+
9751033
func TestFilePersist_Flush_ContextCancellation(t *testing.T) {
9761034
dir := t.TempDir()
9771035
fp, err := New[string, int]("test", dir)

persist/localfs/localfs.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ package localfs
44
import (
55
"bufio"
66
"context"
7+
"crypto/sha256"
78
"encoding/gob"
9+
"encoding/hex"
810
"errors"
911
"fmt"
1012
"log/slog"
@@ -118,18 +120,15 @@ func (*persister[K, V]) ValidateKey(key K) error {
118120
}
119121

120122
// keyToFilename converts a cache key to a filename with squid-style directory layout.
121-
// Uses first 2 characters of key as subdirectory (e.g., "ab/abcd123.gob").
123+
// Hashes the key and uses first 2 characters of hex hash as subdirectory for even distribution
124+
// (e.g., key "http://example.com" -> "a3/a3f2...gob").
122125
func (*persister[K, V]) keyToFilename(key K) string {
123126
keyStr := fmt.Sprintf("%v", key)
127+
hash := sha256.Sum256([]byte(keyStr))
128+
hexHash := hex.EncodeToString(hash[:])
124129

125-
// Squid-style: use first 2 chars as subdirectory
126-
if len(keyStr) >= 2 {
127-
subdir := keyStr[:2]
128-
return filepath.Join(subdir, keyStr+".gob")
129-
}
130-
131-
// For single-char keys, use the char itself as subdirectory
132-
return filepath.Join(keyStr, keyStr+".gob")
130+
// Squid-style: use first 2 chars of hash as subdirectory
131+
return filepath.Join(hexHash[:2], hexHash+".gob")
133132
}
134133

135134
// Location returns the full file path where a key is stored.

0 commit comments

Comments
 (0)