22
33[ ![ build workflow] ( https://github.com/redis/go-redis/actions/workflows/build.yml/badge.svg )] ( https://github.com/redis/go-redis/actions )
44[ ![ PkgGoDev] ( https://pkg.go.dev/badge/github.com/redis/go-redis/v9 )] ( https://pkg.go.dev/github.com/redis/go-redis/v9?tab=doc )
5- [ ![ Documentation] ( https://img.shields.io/badge/redis-documentation-informational )] ( https://redis.uptrace.dev / )
5+ [ ![ Documentation] ( https://img.shields.io/badge/redis-documentation-informational )] ( https://redis.io/docs/latest/develop/clients/go / )
66[ ![ Go Report Card] ( https://goreportcard.com/badge/github.com/redis/go-redis/v9 )] ( https://goreportcard.com/report/github.com/redis/go-redis/v9 )
77[ ![ codecov] ( https://codecov.io/github/redis/go-redis/graph/badge.svg?token=tsrCZKuSSw )] ( https://codecov.io/github/redis/go-redis )
88
1717## Supported versions
1818
1919In ` go-redis ` we are aiming to support the last three releases of Redis. Currently, this means we do support:
20- - [ Redis 7.2] ( https://raw.githubusercontent.com/redis/redis/7.2/00-RELEASENOTES ) - using Redis Stack 7.2 for modules support
21- - [ Redis 7.4] ( https://raw.githubusercontent.com/redis/redis/7.4/00-RELEASENOTES ) - using Redis Stack 7.4 for modules support
22- - [ Redis 8.0] ( https://raw.githubusercontent.com/redis/redis/8.0/00-RELEASENOTES ) - using Redis CE 8.0 where modules are included
23- - [ Redis 8.2] ( https://raw.githubusercontent.com/redis/redis/8.2/00-RELEASENOTES ) - using Redis CE 8.2 where modules are included
20+ - [ Redis 8.0] ( https://raw.githubusercontent.com/redis/redis/8.0/00-RELEASENOTES ) - using Redis CE 8.0
21+ - [ Redis 8.2] ( https://raw.githubusercontent.com/redis/redis/8.2/00-RELEASENOTES ) - using Redis CE 8.2
22+ - [ Redis 8.4] ( https://raw.githubusercontent.com/redis/redis/8.4/00-RELEASENOTES ) - using Redis CE 8.4
2423
2524Although the ` go.mod ` states it requires at minimum ` go 1.18 ` , our CI is configured to run the tests against all three
2625versions of Redis and latest two versions of Go ([ 1.23] ( https://go.dev/doc/devel/release#go1.23.0 ) ,
2726[ 1.24] ( https://go.dev/doc/devel/release#go1.24.0 ) ). We observe that some modules related test may not pass with
2827Redis Stack 7.2 and some commands are changed with Redis CE 8.0.
28+ Although it is not officially supported, ` go-redis/v9 ` should be able to work with any Redis 7.0+.
2929Please do refer to the documentation and the tests if you experience any issues. We do plan to update the go version
3030in the ` go.mod ` to ` go 1.24 ` in one of the next releases.
3131
@@ -43,10 +43,6 @@ in the `go.mod` to `go 1.24` in one of the next releases.
4343
4444[ Work at Redis] ( https://redis.com/company/careers/jobs/ )
4545
46- ## Documentation
47-
48- - [ English] ( https://redis.uptrace.dev )
49- - [ 简体中文] ( https://redis.uptrace.dev/zh/ )
5046
5147## Resources
5248
@@ -55,16 +51,18 @@ in the `go.mod` to `go 1.24` in one of the next releases.
5551- [ Reference] ( https://pkg.go.dev/github.com/redis/go-redis/v9 )
5652- [ Examples] ( https://pkg.go.dev/github.com/redis/go-redis/v9#pkg-examples )
5753
54+ ## old documentation
55+
56+ - [ English] ( https://redis.uptrace.dev )
57+ - [ 简体中文] ( https://redis.uptrace.dev/zh/ )
58+
5859## Ecosystem
5960
60- - [ Redis Mock ] ( https://github.com/go-redis/redismock )
61+ - [ Entra ID (Azure AD) ] ( https://github.com/redis/ go-redis-entraid )
6162- [ Distributed Locks] ( https://github.com/bsm/redislock )
6263- [ Redis Cache] ( https://github.com/go-redis/cache )
6364- [ Rate limiting] ( https://github.com/go-redis/redis_rate )
6465
65- This client also works with [ Kvrocks] ( https://github.com/apache/incubator-kvrocks ) , a distributed
66- key value NoSQL database that uses RocksDB as storage engine and is compatible with Redis protocol.
67-
6866## Features
6967
7068- Redis commands except QUIT and SYNC.
@@ -75,7 +73,6 @@ key value NoSQL database that uses RocksDB as storage engine and is compatible w
7573- [ Scripting] ( https://redis.uptrace.dev/guide/lua-scripting.html ) .
7674- [ Redis Sentinel] ( https://redis.uptrace.dev/guide/go-redis-sentinel.html ) .
7775- [ Redis Cluster] ( https://redis.uptrace.dev/guide/go-redis-cluster.html ) .
78- - [ Redis Ring] ( https://redis.uptrace.dev/guide/ring.html ) .
7976- [ Redis Performance Monitoring] ( https://redis.uptrace.dev/guide/redis-performance-monitoring.html ) .
8077- [ Redis Probabilistic [ RedisStack]] ( https://redis.io/docs/data-types/probabilistic/ )
8178- [ Customizable read and write buffers size.] ( #custom-buffer-sizes )
@@ -429,6 +426,144 @@ vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello")
429426res , err := rdb.Do (ctx, " set" , " key" , " value" ).Result ()
430427` ` `
431428
429+ ## Typed Errors
430+
431+ go-redis provides typed error checking functions for common Redis errors:
432+
433+ ` ` ` go
434+ // Cluster and replication errors
435+ redis.IsLoadingError (err) // Redis is loading the dataset
436+ redis.IsReadOnlyError (err) // Write to read-only replica
437+ redis.IsClusterDownError (err) // Cluster is down
438+ redis.IsTryAgainError (err) // Command should be retried
439+ redis.IsMasterDownError (err) // Master is down
440+ redis.IsMovedError (err) // Returns (address, true) if key moved
441+ redis.IsAskError (err) // Returns (address, true) if key being migrated
442+
443+ // Connection and resource errors
444+ redis.IsMaxClientsError (err) // Maximum clients reached
445+ redis.IsAuthError (err) // Authentication failed (NOAUTH, WRONGPASS, unauthenticated)
446+ redis.IsPermissionError (err) // Permission denied (NOPERM)
447+ redis.IsOOMError (err) // Out of memory (OOM)
448+
449+ // Transaction errors
450+ redis.IsExecAbortError (err) // Transaction aborted (EXECABORT)
451+ ` ` `
452+
453+ ### Error Wrapping in Hooks
454+
455+ When wrapping errors in hooks, use custom error types with ` Unwrap ()` method (preferred) or ` fmt.Errorf ` with ` %w` . Always call ` cmd.SetErr ()` to preserve error type information:
456+
457+ ` ` ` go
458+ // Custom error type (preferred)
459+ type AppError struct {
460+ Code string
461+ RequestID string
462+ Err error
463+ }
464+
465+ func (e *AppError) Error () string {
466+ return fmt.Sprintf (" [%s ] request_id=%s : %v " , e.Code , e.RequestID , e.Err )
467+ }
468+
469+ func (e *AppError) Unwrap () error {
470+ return e.Err
471+ }
472+
473+ // Hook implementation
474+ func (h MyHook) ProcessHook (next redis.ProcessHook ) redis.ProcessHook {
475+ return func (ctx context.Context , cmd redis.Cmder ) error {
476+ err := next (ctx, cmd)
477+ if err != nil {
478+ // Wrap with custom error type
479+ wrappedErr := &AppError{
480+ Code: " REDIS_ERROR" ,
481+ RequestID: getRequestID (ctx),
482+ Err: err,
483+ }
484+ cmd.SetErr (wrappedErr)
485+ return wrappedErr // Return wrapped error to preserve it
486+ }
487+ return nil
488+ }
489+ }
490+
491+ // Typed error detection works through wrappers
492+ if redis.IsLoadingError (err) {
493+ // Retry logic
494+ }
495+
496+ // Extract custom error if needed
497+ var appErr *AppError
498+ if errors.As (err, &appErr) {
499+ log.Printf (" Request: %s " , appErr.RequestID )
500+ }
501+ ` ` `
502+
503+ Alternatively, use ` fmt.Errorf ` with ` %w` :
504+ ` ` ` go
505+ wrappedErr := fmt.Errorf (" context: % w" , err)
506+ cmd.SetErr (wrappedErr)
507+ ` ` `
508+
509+ ### Pipeline Hook Example
510+
511+ For pipeline operations, use ` ProcessPipelineHook` :
512+
513+ ` ` ` go
514+ type PipelineLoggingHook struct {}
515+
516+ func (h PipelineLoggingHook) DialHook (next redis.DialHook ) redis.DialHook {
517+ return next
518+ }
519+
520+ func (h PipelineLoggingHook) ProcessHook (next redis.ProcessHook ) redis.ProcessHook {
521+ return next
522+ }
523+
524+ func (h PipelineLoggingHook) ProcessPipelineHook (next redis.ProcessPipelineHook ) redis.ProcessPipelineHook {
525+ return func (ctx context.Context , cmds []redis.Cmder ) error {
526+ start := time.Now ()
527+
528+ // Execute the pipeline
529+ err := next (ctx, cmds)
530+
531+ duration := time.Since (start)
532+ log.Printf (" Pipeline executed %d commands in %v " , len (cmds), duration)
533+
534+ // Process individual command errors
535+ // Note: Individual command errors are already set on each cmd by the pipeline execution
536+ for _ , cmd := range cmds {
537+ if cmdErr := cmd.Err (); cmdErr != nil {
538+ // Check for specific error types using typed error functions
539+ if redis.IsAuthError (cmdErr) {
540+ log.Printf (" Auth error in pipeline command %s : %v " , cmd.Name (), cmdErr)
541+ } else if redis.IsPermissionError (cmdErr) {
542+ log.Printf (" Permission error in pipeline command %s : %v " , cmd.Name (), cmdErr)
543+ }
544+
545+ // Optionally wrap individual command errors to add context
546+ // The wrapped error preserves type information through errors.As()
547+ wrappedErr := fmt.Errorf (" pipeline cmd %s failed: % w" , cmd.Name (), cmdErr)
548+ cmd.SetErr (wrappedErr)
549+ }
550+ }
551+
552+ // Return the pipeline-level error (connection errors, etc.)
553+ // You can wrap it if needed, or return it as-is
554+ return err
555+ }
556+ }
557+
558+ // Register the hook
559+ rdb.AddHook (PipelineLoggingHook{})
560+
561+ // Use pipeline - errors are still properly typed
562+ pipe := rdb.Pipeline ()
563+ pipe.Set (ctx, " key1" , " value1" , 0 )
564+ pipe.Get (ctx, " key2" )
565+ _ , err := pipe.Exec (ctx)
566+ ` ` `
432567
433568## Run the test
434569
0 commit comments