Simple:
newRdb.SubscribeHandler() reports "panic: nil pointer" after newRdb := redis.WithTimeout()
Because pubSubPool is not cloned
Sorry, English is not my native language, I wrote the details using AI.
Expected Behavior
A new Redis client instance created via WithTimeout() should retain the pubSubPool from the original client, allowing Pub/Sub operations (e.g., SubscribeHandler) to execute without memory errors.
Current Behavior
After calling WithTimeout() to clone a Redis client, using the new client for Pub/Sub operations triggers a nil pointer dereference panic. The error stack trace points to the pubSubPool field being nil in the cloned client:
panic: runtime error: invalid memory address or nil pointer dereference
go/pkg/mod/github.com/redis/go-redis/v9@v9.17.2/internal/pool/pubsub.go:37 +0x69
Root cause: The clone() method of baseClient does not copy the pubSubPool field to the new client instance.
Possible Solution
Modify the (c *baseClient) clone() *baseClient method in redis.go to explicitly clone the pubSubPool field from the original client to the new cloned instance.
Steps to Reproduce
- Initialize a base Redis client:
rdb := redis.NewClient(...)
- Clone the client with a custom timeout:
newRdb := rdb.WithTimeout(time.Second)
- Invoke Pub/Sub functionality on the cloned client:
newRdb.SubscribeHandler(ctx, channel, handler)
- The program panics with the nil pointer dereference error mentioned above.
Context (Environment)
- Affected Version:
github.com/redis/go-redis/v9 v9.17.2
- Problem Introduction: The issue emerged after the commit
https://github.com/redis/go-redis/commit/0ef6d0727d6a452b0ea6eeee6bef3a72d35495ba#diff-190fc9ceda3bbdbe723d493b8b99e2b9a0100c7d635bfe9b04246280bef6f67e
- Impact: Breaks Pub/Sub functionality for clients cloned via
WithTimeout(), blocking related message subscription/publishing workflows.
Detailed Description
The baseClient struct in go-redis/v9 includes a pubSubPool field responsible for managing Pub/Sub connections. The WithTimeout() method relies on the clone() method to create a new client instance with updated timeout settings. However, the current implementation of clone() omits the pubSubPool field, leaving it as nil in the cloned client. When the cloned client attempts to perform Pub/Sub operations, it accesses the uninitialized pubSubPool, resulting in the nil pointer panic.
Possible Implementation
Update the clone() method of baseClient in redis.go to include the pubSubPool field during cloning:
func (c *baseClient) clone() *baseClient {
c.maintNotificationsManagerLock.RLock()
maintNotificationsManager := c.maintNotificationsManager
c.maintNotificationsManagerLock.RUnlock()
clone := &baseClient{
opt: c.opt,
connPool: c.connPool,
onClose: c.onClose,
pubSubPool: c.pubSubPool, // Add this line to clone pubSubPool
pushProcessor: c.pushProcessor,
maintNotificationsManager: maintNotificationsManager,
streamingCredentialsManager: c.streamingCredentialsManager,
}
return clone
}
This change ensures the cloned client inherits the pubSubPool from the original client, eliminating the nil pointer dereference error.
Simple:
newRdb.SubscribeHandler()reports "panic: nil pointer" afternewRdb := redis.WithTimeout()Because pubSubPool is not cloned
Sorry, English is not my native language, I wrote the details using AI.
Expected Behavior
A new Redis client instance created via
WithTimeout()should retain thepubSubPoolfrom the original client, allowing Pub/Sub operations (e.g.,SubscribeHandler) to execute without memory errors.Current Behavior
After calling
WithTimeout()to clone a Redis client, using the new client for Pub/Sub operations triggers a nil pointer dereference panic. The error stack trace points to thepubSubPoolfield beingnilin the cloned client:Root cause: The
clone()method ofbaseClientdoes not copy thepubSubPoolfield to the new client instance.Possible Solution
Modify the
(c *baseClient) clone() *baseClientmethod inredis.goto explicitly clone thepubSubPoolfield from the original client to the new cloned instance.Steps to Reproduce
rdb := redis.NewClient(...)newRdb := rdb.WithTimeout(time.Second)newRdb.SubscribeHandler(ctx, channel, handler)Context (Environment)
github.com/redis/go-redis/v9v9.17.2https://github.com/redis/go-redis/commit/0ef6d0727d6a452b0ea6eeee6bef3a72d35495ba#diff-190fc9ceda3bbdbe723d493b8b99e2b9a0100c7d635bfe9b04246280bef6f67eWithTimeout(), blocking related message subscription/publishing workflows.Detailed Description
The
baseClientstruct ingo-redis/v9includes apubSubPoolfield responsible for managing Pub/Sub connections. TheWithTimeout()method relies on theclone()method to create a new client instance with updated timeout settings. However, the current implementation ofclone()omits thepubSubPoolfield, leaving it asnilin the cloned client. When the cloned client attempts to perform Pub/Sub operations, it accesses the uninitializedpubSubPool, resulting in the nil pointer panic.Possible Implementation
Update the
clone()method ofbaseClientinredis.goto include thepubSubPoolfield during cloning:This change ensures the cloned client inherits the
pubSubPoolfrom the original client, eliminating the nil pointer dereference error.