feat(cooler): Cache.Pull returns and removes a value atomically
- Cache.Pull(ctx, key) ([]byte, bool, error), Laravel's Cache::pull
- Redis driver runs GETDEL (Redis 6.2+); memory driver uses otter Compute
with InvalidateOp under the per-key lock; expired keys report not found
- TestPullSmoke over the memory driver and miniredis, ExampleCache_Pull
- README and docs/services/caching.md ("One-time values")
This commit is contained in:
49
modules/cooler/pull_smoke_test.go
Normal file
49
modules/cooler/pull_smoke_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package cooler
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestPullSmoke takes a value exactly once through Pull on both drivers, and
|
||||
// reports an expired value as not found.
|
||||
func TestPullSmoke(t *testing.T) {
|
||||
run := func(t *testing.T, c Cache, advance func(time.Duration)) {
|
||||
t.Helper()
|
||||
ctx := t.Context()
|
||||
|
||||
if err := c.Set(ctx, "code", []byte("v"), 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v, ok, err := c.Pull(ctx, "code"); err != nil || !ok || string(v) != "v" {
|
||||
t.Fatalf("first Pull = %q %v %v, want \"v\" true nil", v, ok, err)
|
||||
}
|
||||
if v, ok, err := c.Pull(ctx, "code"); err != nil || ok || v != nil {
|
||||
t.Fatalf("second Pull = %q %v %v, want nil false nil", v, ok, err)
|
||||
}
|
||||
if v, ok, err := c.Get(ctx, "code"); err != nil || ok || v != nil {
|
||||
t.Fatalf("Get after Pull = %q %v %v, want nil false nil", v, ok, err)
|
||||
}
|
||||
|
||||
if err := c.Set(ctx, "short", []byte("x"), 50*time.Millisecond); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
advance(100 * time.Millisecond)
|
||||
if v, ok, err := c.Pull(ctx, "short"); err != nil || ok || v != nil {
|
||||
t.Fatalf("Pull of an expired key = %q %v %v, want nil false nil", v, ok, err)
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("memory", func(t *testing.T) {
|
||||
c, err := NewMemoryCache("acme:", 100)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
run(t, c, func(time.Duration) { time.Sleep(80 * time.Millisecond) })
|
||||
})
|
||||
|
||||
t.Run("redis", func(t *testing.T) {
|
||||
mr, client := miniClient(t)
|
||||
run(t, NewRedisCache(client, "acme:"), mr.FastForward)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user