GetTimeToLiveAsync() returns 10675199.02:48:05.4775807 when redis shows TTL: -1

I think the GetTimeToLiveAsync() is not operating as expected. Currently GetTimeToLiveAsync() returns a TimeSpan.Value = 10675199.02:48:05.4775807, when in redis it shows a TTL of -1

I would expect when the TTL is -1, GetTimeToLiveAsync() should return NULL, and should only return a value when the TTL is actually set in redis. Or am I wrong?

Thanks!

The returned value comes from TimeSpan.MaxValue, eg the TTL is always large as the value won’t expire. If you are checking the TTL value, it is common to want to apply a rule around “If this object TTL will expire in X time, do something special, else continue.” so the expression will commonly be against a TimeSpan, eg

var ttl = await client.GetTimeToLiveAsync(key);
if(ttl < TimeSpan.FromSeconds(30)) {
  // Handle short expiration
} else {
  // Not going to expire for a while
}

So the MaxValue here matches an expected result, eg a very large TimeSpan. A null results if the key doesn’t exist, eg you try to fetch a TTL for a key that doesn’t exist and the check is not required.

I think having null for both “key does not exist” and infinite/no expiry would make the common use case of checking ‘How long until this key expires’ more complicated as some info is lost between .NET and what is stored in Redis (-2 for key doesn’t exist, -1 for no expiration).

See here for the login within ServiceStack.Redis -> https://github.com/ServiceStack/ServiceStack.Redis/blob/d5d1e8025bda1f3cc6b3bc0b728570a98fc08fca/src/ServiceStack.Redis/RedisClient.cs#L382-L391

Is there a particular use case there returning TimeSpan.Max value here complicates what you are trying to do? If you can share a code example that would help make it clearer.

Thanks for the correction.

No, I can check for TimeSpan.Max, and understand that means there is no TTL.

I just assumed that since there is already the ContainsKeyAsync() that checks for the existence of the key, the and since the GetTimeToLiveAsync() return type was nullable, that if there was not a value set (TTL:<1), OR if the key did not exists, same result would be a NULL as there is NO TTL on that key (existing or not).

I guess I thought the single concern principal would be at play here, and if I wanted to check to see if the key exists, I would use the ContainsKeyAsync(), and not the GetTimeToLiveAsync()

I think since Redis makes a distinction between the response, the Redis client is matching that with its use of TimeSpan.Max makes sense.

TIL in Redis 2.6 or older the behavior matched more I guess what you are expecting, I can imagine this changed for good reason, possibly to reduce the more chatty approach needing to make 2 calls rather than just 1 TTL call but I’m not sure :man_shrugging:.

In Redis 2.6 or older the command returns -1 if the key does not exist or if the key exist but has no associated expire.
https://redis.io/commands/TTL

Sounds about right. Thanks for your quick responses :slight_smile:

1 Like