Skip to content

Commit 6459d31

Browse files
authored
Merge pull request #75 from vimeo/lru_ttl_support
TTL/Expiration setting & propagation
2 parents de0239d + 1165af2 commit 6459d31

30 files changed

+1652
-321
lines changed

.github/workflows/go.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
strategy:
88
matrix:
99
os: [macOS-latest, ubuntu-latest]
10-
goversion: ['1.23', '1.24']
10+
goversion: ['1.24', '1.25']
1111
steps:
1212
- name: Set up Go ${{matrix.goversion}} on ${{matrix.os}}
1313
uses: actions/setup-go@v3
@@ -19,7 +19,7 @@ jobs:
1919
uses: actions/checkout@v1
2020

2121
- name: gofmt
22-
if: ${{matrix.goversion == '1.24'}}
22+
if: ${{matrix.goversion == '1.25'}}
2323
run: |
2424
[[ -z $(gofmt -l $(find . -name '*.go') ) ]]
2525
@@ -68,7 +68,7 @@ jobs:
6868

6969
# Run all consistenthash Fuzz tests for 30s with go 1.24
7070
- name: Fuzz Consistent-Hash
71-
if: ${{matrix.goversion == '1.24'}}
71+
if: ${{matrix.goversion == '1.25'}}
7272
env:
7373
GO111MODULE: on
7474
run: go test -fuzz=. -fuzztime=30s ./consistenthash
@@ -82,7 +82,7 @@ jobs:
8282
8383
# Run the lru Fuzz tests for 30s with go 1.24 (in paranoid mode)
8484
- name: Fuzz Consistent-Hash
85-
if: ${{matrix.goversion == '1.24'}}
85+
if: ${{matrix.goversion == '1.25'}}
8686
env:
8787
GO111MODULE: on
8888
run: go test --tags=paranoidgcll -fuzz=. -fuzztime=30s ./lru

compattest/compat_test.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func TestFetchModeCompat(t *testing.T) {
6565
hydMode peercfg.GalaxyHandlerMode
6666
prefixSelfName bool
6767
echoKey bool
68+
keyExpiry time.Time
6869

6970
enablePeek bool
7071
}
@@ -79,6 +80,7 @@ func TestFetchModeCompat(t *testing.T) {
7980
expVal string // ignored if one of the below is set
8081
expNotFound bool
8182
expErr bool
83+
expiry time.Time
8284
}
8385

8486
v12WorkerPath := filepath.Join(t.TempDir(), "peerv1.2")
@@ -92,6 +94,8 @@ func TestFetchModeCompat(t *testing.T) {
9294
}
9395
}
9496

97+
baseTime := time.Now()
98+
9599
for _, tbl := range []struct {
96100
name string
97101
// Names will be "<version>-<index>" (where "<version>" is either "v1.2" or "head")
@@ -184,6 +188,7 @@ func TestFetchModeCompat(t *testing.T) {
184188
prefixSelfName: true,
185189
echoKey: true,
186190
enablePeek: false,
191+
keyExpiry: baseTime.Add(time.Hour * 31),
187192
},
188193
},
189194
headFetchKeys: []fetchKeyExp{
@@ -195,6 +200,7 @@ func TestFetchModeCompat(t *testing.T) {
195200
expVal: "head-0: " + chtest.SingleOwnerKey("head-0") + ": {some value}",
196201
expNotFound: false,
197202
expErr: false,
203+
expiry: baseTime.Add(time.Hour * 31),
198204
},
199205
{
200206
headHostIdx: 0,
@@ -204,6 +210,7 @@ func TestFetchModeCompat(t *testing.T) {
204210
expVal: "v1.2-0: " + chtest.FallthroughKey("v1.2-0", "head-0") + ": {some value}",
205211
expNotFound: false,
206212
expErr: false,
213+
expiry: time.Time{},
207214
},
208215
{
209216
headHostIdx: 0,
@@ -213,6 +220,7 @@ func TestFetchModeCompat(t *testing.T) {
213220
expVal: "v1.2-0: " + chtest.FallthroughKey("v1.2-0", "head-0") + ": {some value}",
214221
expNotFound: false,
215222
expErr: false,
223+
expiry: time.Time{},
216224
},
217225
},
218226
galaxyName: "fizzlebat",
@@ -230,6 +238,7 @@ func TestFetchModeCompat(t *testing.T) {
230238
prefixSelfName: true,
231239
echoKey: true,
232240
enablePeek: false,
241+
keyExpiry: baseTime.Add(time.Hour * 37),
233242
},
234243
{
235244
fetchProto: peercfg.FetchGRPC,
@@ -240,6 +249,7 @@ func TestFetchModeCompat(t *testing.T) {
240249
prefixSelfName: true,
241250
echoKey: true,
242251
enablePeek: false,
252+
keyExpiry: baseTime.Add(time.Hour * 34),
243253
},
244254
{
245255
fetchProto: peercfg.FetchGRPC,
@@ -250,6 +260,7 @@ func TestFetchModeCompat(t *testing.T) {
250260
prefixSelfName: true,
251261
echoKey: true,
252262
enablePeek: false,
263+
keyExpiry: baseTime.Add(time.Hour * 31),
253264
},
254265
},
255266
headFetchKeys: []fetchKeyExp{
@@ -261,6 +272,7 @@ func TestFetchModeCompat(t *testing.T) {
261272
expVal: "", // ignored
262273
expNotFound: true,
263274
expErr: false,
275+
expiry: time.Time{},
264276
},
265277
{
266278
headHostIdx: 0,
@@ -270,6 +282,7 @@ func TestFetchModeCompat(t *testing.T) {
270282
expVal: "head-0: " + chtest.FallthroughKey("head-0", "head-1") + ": {some value}",
271283
expNotFound: false,
272284
expErr: false,
285+
expiry: baseTime.Add(time.Hour * 37),
273286
},
274287
{
275288
headHostIdx: 0,
@@ -279,6 +292,7 @@ func TestFetchModeCompat(t *testing.T) {
279292
expVal: "head-0: " + chtest.FallthroughKey("head-0", "head-1") + ": {some value}", // already cached
280293
expNotFound: false,
281294
expErr: false,
295+
expiry: baseTime.Add(time.Hour * 37),
282296
},
283297
{
284298
headHostIdx: 1,
@@ -288,6 +302,7 @@ func TestFetchModeCompat(t *testing.T) {
288302
expVal: "head-0: " + chtest.SingleOwnerKey("head-0") + ": {some value}",
289303
expNotFound: false,
290304
expErr: false,
305+
expiry: baseTime.Add(time.Hour * 37),
291306
},
292307
{
293308
headHostIdx: 1,
@@ -297,6 +312,7 @@ func TestFetchModeCompat(t *testing.T) {
297312
expVal: "head-2: " + chtest.FallthroughKey("head-2", "head-0") + ": {some value}",
298313
expNotFound: false,
299314
expErr: false,
315+
expiry: baseTime.Add(time.Hour * 31),
300316
},
301317
{
302318
headHostIdx: 1,
@@ -306,6 +322,7 @@ func TestFetchModeCompat(t *testing.T) {
306322
expVal: "head-0: " + chtest.FallthroughKey("head-1", "head-0") + ": {some value}",
307323
expNotFound: false,
308324
expErr: false,
325+
expiry: baseTime.Add(time.Hour * 37),
309326
},
310327
{
311328
headHostIdx: 1,
@@ -315,6 +332,7 @@ func TestFetchModeCompat(t *testing.T) {
315332
expVal: "head-0: " + chtest.FallthroughKey("head-1", "head-0") + ": {some value}",
316333
expNotFound: false,
317334
expErr: false,
335+
expiry: baseTime.Add(time.Hour * 37),
318336
},
319337
},
320338
galaxyName: "fizzlebat",
@@ -1034,7 +1052,8 @@ func TestFetchModeCompat(t *testing.T) {
10341052
Bytes: pInfo.opts.galaxySize,
10351053
HydrationMode: pInfo.opts.hydMode,
10361054
// peek isn't implemented for v1.2 peers (but it'll be ignored there anyway)
1037-
Peek: peek,
1055+
Peek: peek,
1056+
Expiry: pInfo.opts.keyExpiry,
10381057
}},
10391058
}
10401059
}
@@ -1121,9 +1140,13 @@ func TestFetchModeCompat(t *testing.T) {
11211140

11221141
g := ugs.gs[0]
11231142
sc := galaxycache.StringCodec("")
1124-
_, getErr := g.GetWithOptions(ctx, fk.getOpts, fk.key, &sc)
1143+
bgInfo, getErr := g.GetWithOptions(ctx, fk.getOpts, fk.key, &sc)
11251144
ugs.u.SetIncludeSelf(origIncSelf) // restore the IncludeSelf value
11261145

1146+
if !bgInfo.Expiry.Equal(fk.expiry) {
1147+
t.Errorf("key idx %d: unexpected expiry: %s; expected %s", i, bgInfo.Expiry, fk.expiry)
1148+
}
1149+
11271150
nfErr := (galaxycache.NotFoundErr)(nil)
11281151
isNotFound := errors.As(getErr, &nfErr)
11291152
if isNotFound {

compattest/head_server_test.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ import (
2424
gchttp "github.com/vimeo/galaxycache/http"
2525
)
2626

27-
func genBackendGetter(galaxy peercfg.Galaxy, selfName string) galaxycache.GetterFunc {
28-
return func(ctx context.Context, key string, codec galaxycache.Codec) error {
27+
func genBackendGetter(galaxy peercfg.Galaxy, selfName string) galaxycache.GetterFuncWithInfo {
28+
return func(ctx context.Context, key string, codec galaxycache.Codec) (galaxycache.BackendGetInfo, error) {
2929
switch galaxy.HydrationMode {
3030
case peercfg.HandlerFail:
31-
return errors.New("not a useful error")
31+
return galaxycache.BackendGetInfo{}, errors.New("not a useful error")
3232
case peercfg.HandlerGRPCNotFound:
33-
return status.Errorf(codes.NotFound, "failed to extract key %q", key)
33+
return galaxycache.BackendGetInfo{}, status.Errorf(codes.NotFound, "failed to extract key %q", key)
3434
case peercfg.HandlerNotFound:
35-
return fmt.Errorf("key %w", galaxycache.TrivialNotFoundErr{})
35+
return galaxycache.BackendGetInfo{}, fmt.Errorf("key %w", galaxycache.TrivialNotFoundErr{})
3636
case peercfg.HandlerSuccess:
3737
out := bytes.Buffer{}
3838
if galaxy.PrefixSelfName {
@@ -45,12 +45,12 @@ func genBackendGetter(galaxy peercfg.Galaxy, selfName string) galaxycache.Getter
4545
}
4646
out.WriteString("{some value}")
4747
if unmarErr := codec.UnmarshalBinary(out.Bytes()); unmarErr != nil {
48-
return fmt.Errorf("unmarshal failed: %w", unmarErr)
48+
return galaxycache.BackendGetInfo{}, fmt.Errorf("unmarshal failed: %w", unmarErr)
4949
}
50-
return nil
50+
return galaxycache.BackendGetInfo{Expiration: galaxy.Expiry}, nil
5151
default:
5252
}
53-
return nil
53+
return galaxycache.BackendGetInfo{}, nil
5454
}
5555
}
5656

@@ -92,7 +92,7 @@ func genUniverse(cfg peercfg.Config) (*galaxycache.Universe, []*galaxycache.Gala
9292
WarmTime: galaxy.Peek.WarmTime,
9393
}))
9494
}
95-
gs[i] = u.NewGalaxy(galaxy.Name, int64(galaxy.Bytes), genBackendGetter(galaxy, cfg.SelfName), gOpts...)
95+
gs[i] = u.NewGalaxyWithBackendInfo(galaxy.Name, int64(galaxy.Bytes), genBackendGetter(galaxy, cfg.SelfName), gOpts...)
9696
}
9797
return u, gs
9898
}
@@ -109,7 +109,14 @@ func runPeer(ctx context.Context, t testing.TB, u *galaxycache.Universe, priL, s
109109
Handler: &mux,
110110
ErrorLog: log.New(&prefixedTestLogWriter{t: t, prefix: "http: "}, cfg.SelfName+": ", log.Ltime|log.Ldate|log.Lmicroseconds),
111111
ConnState: func(conn net.Conn, cs http.ConnState) {
112-
t.Logf("%s: connection from %s to %s changed state to %s", cfg.SelfName, conn.RemoteAddr(), conn.LocalAddr(), cs)
112+
// Try not to log if the context has already been canceled. (likely because the test finished)
113+
// (logging after a test completes from another goroutine (intentionally) triggers the race detector)
114+
// Checking the context _may_ provide a synchronization edge that avoids that issue.
115+
select {
116+
case <-ctx.Done():
117+
default:
118+
t.Logf("%s: connection from %s to %s changed state to %s", cfg.SelfName, conn.RemoteAddr(), conn.LocalAddr(), cs)
119+
}
113120
},
114121
}
115122

compattest/peercfg/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ type Galaxy struct {
7272
PrefixSelfName bool
7373
Bytes uint64
7474
HydrationMode GalaxyHandlerMode
75+
Expiry time.Time
7576

7677
Peek *PeekConfig
7778
}

compattest/peerv1.2/go.mod

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
module github.com/vimeo/galaxycache/compattest/peerv1.2
22

3-
go 1.23.8
3+
go 1.24.0
44

55
require (
66
github.com/vimeo/galaxycache v1.2.2
77
github.com/vimeo/galaxycache/compattest/peercfg v0.0.0-00010101000000-000000000000
8-
google.golang.org/grpc v1.72.0
8+
google.golang.org/grpc v1.77.0
99
)
1010

1111
require (
1212
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
1313
github.com/vimeo/go-clocks v1.1.2 // indirect
1414
go.opencensus.io v0.22.5 // indirect
15-
golang.org/x/net v0.35.0 // indirect
16-
golang.org/x/sync v0.11.0 // indirect
17-
golang.org/x/sys v0.30.0 // indirect
18-
golang.org/x/text v0.22.0 // indirect
19-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
20-
google.golang.org/protobuf v1.36.6 // indirect
15+
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
16+
golang.org/x/sync v0.17.0 // indirect
17+
golang.org/x/sys v0.37.0 // indirect
18+
golang.org/x/text v0.30.0 // indirect
19+
google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba // indirect
20+
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect
21+
google.golang.org/protobuf v1.36.10 // indirect
2122
)
2223

2324
replace github.com/vimeo/galaxycache/compattest/peercfg => ../peercfg

0 commit comments

Comments
 (0)