Redis Timeout expired All pooled connections were in use

Hi Team,

We are currently using ServiceStack.Redis.Core version 5.9.2, and we have registered the PooledRedisClientManager using singleton. When trying to store, get, or clear data, we are using the using {} statement. However, we occasionally encounter the following error:

“Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use.”

How can we resolve this issue? Can we set the value for the maximum pool size? Currently, we haven’t set anything for the max pool size. If we don’t set the pool size, what is the default value? And if we want to configure the max pool size, how can we implement it?

Thank you.

Can you share any configuration you are using when you create the PooledRedisClientManager? Eg how it is instantiated in your AppHost? The max will depend on how you are using it, but you can override the RedisConfig.DefaultPoolSizeMultiplier which defaults to 50 to see if that helps.

You can also check on the clientManager.Config to see what the values are for MaxWritePoolSize and MaxReadPoolSize after it has been instantiated.

Hi,

I did not configure that property.

Default value i set for ConnectionTimout is 1000
image

These are my code :
image



for Config MaxWritePoolSize and MaxReadPoolSize i checked

What is the recommended value for setting MaxWritePoolSize and MaxReadPoolSize? And what is the suggested recommendation for setting RedisConfig.DefaultPoolSizeMultiplier in a production environment?

The defaults should be the enough for most people but if you have issues with pool exhaustion it’s typically a result of clients not being disposed correctly which we cover in Troubleshooting Redis connection issues.

If you’re using ServiceStack we display some internal metrics that may be insightful in the /admin-ui Dashboard.

1 Like

Hi Layoric,

i have tried to to use RedisConfig.DefaultPoolSizeMultiplier but i didn’t find that property or method

Can you explain to me what these MaxWritePoolSize and MaxReadPoolSize are for ?

The DefaultPoolSizeMultiplier is a static field on RedisConfig, however it might not be available in 5.9.2.

These are used when you have a Redis replication setup using Redis Sentinel. Since these values are highly dependent on your environment and workload, you will probably be best to monitor your server load, Redis load, size of your client pool, and command duration to try to identify possible related issues. A lot of this can be seen in the Redis Stats and Redis Profiling Admin UI @mythz has linked above, however that would require you upgrade your application.

1 Like

Hi All @layoric and @mythz

After investigating, we found that the issue is related pool.

We have an IdentityServer service that many users access to store and retrieve data from Redis continuously. At the same time, another service is sequentially retrieving data from Redis, which causes the error.

Log from IdentityServer

Log from other Service

Detail for the error

2023-07-18 16:44:37.3290 [ERROR] [09c07b3f-fc5e-4d36-8879-438114eed308] Xtremax.Cache.RedisCacheClientServiceStack Failed to retrieve cache with key '09c07b3f-fc5e-4d36-8879-438114eed308/84d6d4ee-494e-4070-a2f3-a556bd937f75', reason : Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use.
2023-07-18 16:44:35.2300 [ERROR] [09c07b3f-fc5e-4d36-8879-438114eed308] Xtremax.Cache.RedisCacheClientServiceStack Failed to retrieve cache with key '09c07b3f-fc5e-4d36-8879-438114eed308/84d6d4ee-494e-4070-a2f3-a556bd937f75', reason : Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use.

To replicate this, I created a sample service called SampleRedis, which performs concurrent store and retrieve operations. Another service hits SampleRedis using HttpClient. I used 3 sample services and 3 Replicas Redis Server (Master/Slave) and 3 Redis Sentinel.

SampleRedis Code

Sample Service when Hit SampleRedis Code

I tried hitting Service 1 100 times, Service 2 50 times, and Service 3 30 times.
During testing, the error occurred and it only happened on Service 3

2023-07-25 04:35:41.7051 [ERROR] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Failed to store cache with key 'AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1', reason : Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use."}
at ServiceStack.Redis.PooledRedisClientManager.GetClient() in C:\\BuildAgent\\work\\b2a0bfe2b1c9a118\\src\\ServiceStack.Redis\\PooledRedisClientManager.cs:line 262"}
Xtremax.Cache.RedisCacheClientServiceStack._Store(String key, Object value)"}
Xtremax.Cache.RedisCacheClientServiceStack.Store(String key, Object value)"}
2023-07-25 04:35:41.7051 [INFO] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Start storing cache with key : AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1
2023-07-25 04:35:42.7057 [ERROR] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Failed to store cache with key 'AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1', reason : Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use."}
at ServiceStack.Redis.PooledRedisClientManager.GetClient() in C:\\BuildAgent\\work\\b2a0bfe2b1c9a118\\src\\ServiceStack.Redis\\PooledRedisClientManager.cs:line 262"}
Xtremax.Cache.RedisCacheClientServiceStack._Store(String key, Object value)"}
Xtremax.Cache.RedisCacheClientServiceStack.Store(String key, Object value)"}
2023-07-25 04:35:42.7057 [INFO] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Start storing cache with key : AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1
2023-07-25 04:35:42.7057 [INFO] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Start storing cache with key : AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1
2023-07-25 04:35:43.7054 [ERROR] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Failed to store cache with key 'AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1', reason : Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use."}
at ServiceStack.Redis.PooledRedisClientManager.GetClient() in C:\\BuildAgent\\work\\b2a0bfe2b1c9a118\\src\\ServiceStack.Redis\\PooledRedisClientManager.cs:line 262"}
Xtremax.Cache.RedisCacheClientServiceStack._Store(String key, Object value)"}
Xtremax.Cache.RedisCacheClientServiceStack.Store(String key, Object value)"}
2023-07-25 04:35:43.7054 [INFO] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Start storing cache with key : AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1
2023-07-25 04:35:44.7060 [ERROR] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Failed to store cache with key 'AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1', reason : Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use."}
at ServiceStack.Redis.PooledRedisClientManager.GetClient() in C:\\BuildAgent\\work\\b2a0bfe2b1c9a118\\src\\ServiceStack.Redis\\PooledRedisClientManager.cs:line 262"}
Xtremax.Cache.RedisCacheClientServiceStack._Store(String key, Object value)"}
Xtremax.Cache.RedisCacheClientServiceStack.Store(String key, Object value)"}
2023-07-25 04:35:44.7060 [INFO] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Start storing cache with key : AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1
2023-07-25 04:35:44.7060 [ERROR] [77fa673f-513a-4723-853f-14b8e9bca70a] Xtremax.Cache.RedisCacheClientServiceStack Failed to store cache with key 'AuthSessionStore-f55f070a-713c-4ab0-a515-a3c338f8a5c1', reason : Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use."}
at ServiceStack.Redis.PooledRedisClientManager.GetClient() in C:\\BuildAgent\\work\\b2a0bfe2b1c9a118\\src\\ServiceStack.Redis\\PooledRedisClientManager.cs:line 262"}
Xtremax.Cache.RedisCacheClientServiceStack._Store(String key, Object value)"}
Xtremax.Cache.RedisCacheClientServiceStack.Store(String key, Object value)"}

We have followed the documentation and followed the instructions, such as using singleton and using statements.
Our code for using Redis Client like these

Singletone
image

Store Process

Retrieve Process

Based on my stimulate or replicate the issue and the code above, do you have any suggestions or improvements?

Can you publish a stand-alone repro on GitHub somewhere, I can’t tell the what Type and lifetimes of the different instances being used are.

Hi @layoric and @mythz

Using ServiceStack.Redis.Core version 5.9.2

Right now, the problem

Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use. System.TimeoutException: Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use.

isn’t happening in the production environment until today’s date. But we want to stay careful if it happens unexpectedly. Until now, this problem has only appeared when we’re using Redis Sentinel.

Could you give me sample code how to set the maximum pool size?
And how can we check the current pool size while Redis is running by looking at the logs, because we not use admin-ui Dashboard?

Thank You

The easiest way to set the pool size is by setting the global configuration:

RedisConfig.DefaultMaxPoolSize = 100;

More fine-grained configuration that applies just to a RedisSentinel instance can be done by overriding the default RedisManagerFactory, e.g:

var redisSentinel = new RedisSentinel(sentinelHost, masterName)
{
    RedisManagerFactory = (masters, replicas) => 
        new PooledRedisClientManager(masters, replicas,
            new RedisClientManagerConfig {
                MaxWritePoolSize = 100,
                MaxReadPoolSize = 200,
            })
};

You can get the instance of the PooledRedisClientManager that the RedisSentinel instance is using at runtime with:

var redisManager = (PooledRedisClientManager) redisSentinel.GetRedisManager();

Where you can get a snapshot of internal running stats with:

var internalInfo = redisManager.GetStats();
Console.WriteLine(internalInfo.Dump());
1 Like

Hi @mythz

Is there documentation to explain this ?

VersionString: 5.92,
writeClientsPoolSize: 20,
writeClientsCreated: 13,
writeClientsWithExceptions: 8,
writeClientsInUse: 0,
writeClientsConnected: 7,
readClientsPoolSize: 40,
readClientsCreated: 27,
readClientsWithExceptions: 3,
readClientsInUse: 6,
readClientsConnected: 9,
RedisResolver.ReadOnlyHostsCount: 2,
RedisResolver.ReadWriteHostsCount: 1

No but the names look pretty self explanatory, which ones need explaining?

These @mythz

What exceptions and how if we want to know inside or detail the exceptions ?

This is when the RedisClient had any exception, e.g. failed to connect. Most exceptions are logged with the Error Log Level, some Exceptions are logged as Debug (e.g. auto connection retries). Docs on enabling logging with different logging providers are in the Logging Docs.

hi @mythz

We’re using Redis HA (1 Master, 2 Slaves) with Sentinel, and we’ve tested a scenario like this:

  1. When the Redis Server goes down, the App Service tries to use the Redis Cache.
  2. Even after restarting the App Service and with the Redis Server still down, the App Service continues to use the Redis Cache.
  3. When the Redis Server is back up, the App Service, after restarting, tries to use the Redis Cache.

We’re facing an intermittent issue where we can write to Redis, but reading fails, and the RedisResolver.ReadOnlyHostCount doesn’t return the expected value of 2. However, when we access the Redis Server (by running commands on the pod), we can use redis-cli, and everything seems fine.

Do you have any suggestions for fixing this problem?

If writing to Redis works, and this issue is only temporary you could switch to using GetCacheClient() when GetReadOnlyClient() fails with InvalidOperationException.

The issue looks like the RedisSentinel is reporting that no Slaves are available, if it doesn’t resolve by itself you can force RedisSentinel to be recreate its pooled clients with:

var info = sentinel.ResetClients();

// Verify both are populated:
info.RedisMasters;
info.RedisSlaves;

Otherwise you can try switching to use a different managed pool implementation behavior with:

var redisSentinel = new RedisSentinel(sentinelHost, masterName)
{
    RedisManagerFactory = (master,slaves) => new RedisManagerPool(master)
};

Hi @mythz

Sorry, I know this thread has been quite old, and so far, in the production environment, the max pool size issue hasn’t occurred like it did yesterday. Regarding your request for the repository, I couldn’t provide it earlier due to many dependencies on NuGet components. Instead, we have been trying to identify the available features and classes used in our communication with the Redis client and Redis server based on the version when the issue occurred yesterday.

Actually i also cannot replicate the issue from PROD env (You can see from file *.log), i be able replicate the issue with another message but the issue almost same, that happened when retrieve data from redis cache

Here is the source code, and I’ve attempted various scenarios to replicate the issue in my dev environment, but have not been successful in replicating it same as prod env.

This repository contains content :

Scenario Replication:
Scenario 1
Redis Server UP :white_check_mark: (Expected)
Console App UP :white_check_mark: (Expected)

Scenario 2
Redis Server DOWN :warning: (This means I deleted all Redis pod servers)
Console APP ERROR when Store and Retrieve :white_check_mark: (Expected)

Scenario 3
Redis Server DOWN :warning: (This means I deleted all Redis pod servers)
Console APP Restart while Redis Server Still DOWN :white_check_mark: (Expected)

Scenario 4
Redis Server UP :white_check_mark:
Console APP UP after restart from scenario 3 - Retrieve process failed :x: (Not Expected)

Scenario 5
Redis Server UP :white_check_mark: (Expected)
Console APP Restart Again :white_check_mark: (Expected)

From those resources, I need your help to check if our code implementation is correct or not, and based on your guess from the production logs, can you figure out why the max pool size issue happened with our Redis client?

Thanks in advance

We can look at small minimal repros we can run locally to repro the issue, inspecting existing code bases requires a premium support incident.

Also please make sure you’re using the latest version, I can see v5.9.2 in the logs.

Kubernetes seems like overkill, are you the sole .NET developer working on projects that use ServiceStack.Redis?

My sample code is minimal and small; you can run it locally, but you will need to use a Redis server sentinel to reproduce it

But at that time only one user issue max pool size happened, and now already 500 user but until right now max pool size not occured anymore

Yes currently only me.

So our goal is to find out if our implementation of connecting to the Redis sentinel is incorrect, causing the max pool size issue, or if there might be some other issue causing it.

Oh, one more thing, is there a way for us to confirm that our license has been successfully applied without trying to send 6000 messages in one hour? For example, is there any log information that indicates a successful license application?

Your application only needs a singleton reference to the IRedisClientsManager returned by RedisSentinel.Start() so I don’t get the purpose of all the wrapper classes, but as long as it’s returning the same instance it shouldn’t make a difference.

The License Registration Exception will throw if the registering the license was invalid otherwise you can call LicenseFeature.ActivatedLicenseFeatures() to return what it’s licensed as. Should be All or RedisSku to use ServiceStack.Redis.