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:
Jakub Zych
2026-10-20 22:58:44 +02:00
parent 26ebb6c98c
commit 5e0a04e4da
7 changed files with 164 additions and 4 deletions

View File

@@ -52,6 +52,22 @@ func (r *redisCache) Get(ctx context.Context, key string) ([]byte, bool, error)
return b, true, nil
}
// Pull runs GETDEL (Redis 6.2 or newer): the value is returned and removed in
// one command, so of two concurrent callers at most one sees found=true.
func (r *redisCache) Pull(ctx context.Context, key string) ([]byte, bool, error) {
if err := r.ready(); err != nil {
return nil, false, err
}
b, err := r.client.GetDel(ctx, r.key(key)).Bytes()
if errors.Is(err, redis.Nil) {
return nil, false, nil
}
if err != nil {
return nil, false, err
}
return b, true, nil
}
// Set stores value with SET … PX; a ttl of 0 or less stores it without expiry.
func (r *redisCache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error {
if err := r.ready(); err != nil {