Autopipelining automatically batches concurrent commands from multiple goroutines into Redis pipelines. This is intended for high-throughput/high-load scenarios.
Two modes of operation:
AutoPipeline() (Blocking): A drop-in replacement for a normal client. Each command call blocks until it executes and returns its own value/error. Per-goroutine ordering is preserved.AsyncAutoPipeline() (Deferred): Highest throughput. Command calls return immediately (returning a command object), and you read the results later. This keeps pipelines deep.
Important Caveats:
- Contexts: A command's context is NOT honored once it is queued; batches execute on the autopipeliner's own context. Use a plain client if you need per-command deadlines.
- Non-idempotent commands: On a dropped connection, a batch is retried as a whole. Non-idempotent commands might execute twice.
- Bypassed commands: Blocking commands (
BLPOP, WAIT), SHUTDOWN, MONITOR, and Do bypass batching.
// Blocking face: drop-in for a normal client, batched under the hood.
ap, err := rdb.AutoPipeline()
if err != nil {
log.Fatal(err)
}
defer ap.Close()
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key:%d", i)
if err := ap.Set(ctx, key, i, 0).Err(); err != nil { // blocks until executed
log.Printf("set %s: %v", key, err)
}
}(i)
}
wg.Wait()
// Async face: for maximum throughput
ctx := context.Background()
ap, err := rdb.AsyncAutoPipeline()
if err != nil {
log.Fatal(err)
}
defer ap.Close()
cmds := make([]*redis.StatusCmd, 0, 200)
for i := 0; i < 200; i++ {
cmds = append(cmds, ap.Set(ctx, fmt.Sprintf("key:%d", i), i, 0)) // returns immediately
}
for _, cmd := range cmds {
if err := cmd.Err(); err != nil { // blocks until executed
log.Printf("set: %v", err)
}
}