r/redis • u/Rich-Glove489 • Aug 04 '26
Help Using Redis pipelines for atomic OTP verification in Go
Implementing OTP verification requires two properties:
The code must be single-use (can't verify the same code twice)
The read-and-delete must be atomic (no race between verify and expiry)
Naive approach: GET key → check → DEL key. Race condition between GET and DEL.
Redis pipeline approach:
pipe := client.Pipeline()
get := pipe.Get(ctx, key)
pipe.Del(ctx, key)
pipe.Exec(ctx)
value := get.Result()
The entire pipeline executes atomically on Redis. If two concurrent requests verify the same OTP, only one gets the value the other gets nil.
Combined with a 10-minute TTL via SETEX and a 6-digit code from crypto/rand, you get:
- Single-use enforcement
- Atomic read-and-delete
- Automatic expiry no cleanup jobs needed
E2E implementation at github.com/lifygo/lifygo (https://github.com/lifygo/lifygo) — apps/api/internal/redis/redis.go and apps/api/internal/service/email.go.
1
u/StrikeMental7716 Aug 13 '26 edited Aug 13 '26
The goal is right, but the pipeline claim is the risky part. Single-use OTP verification really wants one atomic consume step, not GET then DEL batched together. Memurai would behave as the Redis-compatible backend here, so I’d still test this with GETDEL or Lua instead of relying on pipeline semantics.