Checking for existence of list throws "Invalid Value" error

I wrote a function that checks for the existence of a list as so:

public static bool ListExists(this RedisClient redisClient, string listID)
    {
        bool returnValue = false;
        try
        {
            RedisKeyType keyType = redisClient.GetEntryType(listID);
            if (keyType == RedisKeyType.List)
            {
                returnValue = true;
            }
        }
        catch (Exception e)
        {
            throw e;
        }
        return returnValue;
    }

Most of the time this function works correctly, but on occasion when trying to execute the statement, RedisKeyType keyType = redisClient.GetEntryType(listID); I get the error ā€œInvalid Valueā€. Any ideas?

Can you print out what redisClient.Type(listID) returns when this error is throw.

Last value I get for

public static bool ListExists(this RedisClient redisClient, string listID)
        {
            bool returnValue = false;
            try
            {
                string type = redisClient.Type(listID);
                Console.WriteLine(type);
                RedisKeyType keyType = redisClient.GetEntryType(listID);
                if (keyType == RedisKeyType.List)
                {
                    returnValue = true;
                }
            }
            catch (Exception e)
            {
                throw e;
            }
            return returnValue;
        }

Is none

Just to be clear. I added:

string type = redisClient.Type(listID);
                Console.WriteLine(type);

Is this the result of Type() when the Exception occurs? ā€œnoneā€ is already handled by GetEntryType().

Please provide the full StackTrace of the Exception.

Calling redisClient.Type(listID) is what is throwing the error. The exception from throw CreateResponseError(ā€œInvalid valueā€); statement is the exception Iā€™m getting. The list that is passed in the listID param exists, and even if it doesnā€™t it shouldnā€™t throw an exception, should it?

Stack trace is:

   at ServiceStack.Redis.RedisNativeClient.GetEntryType(String key)
   at ServiceStack.Redis.IRedisClientExtensions.ListExists(RedisClient redisClient, String listID) in C:\...\Extensions\IRedisClientExtensions.cs:line 22

The Invalid value Error is thrown when the response of Type() is unknown. It canā€™t be ā€œnoneā€ thatā€™s causing it since itā€™s already handled. Anyway Iā€™ve updated the Exception message to include the unknown Type in this commit.

If you can update to use the latest v5.0.3 of the Redis Client thatā€™s now on MyGet the Exception message will now include the unknown Type.

1 Like

The updated code now throws an error message Invalid Type ā€˜1ā€™ in the same calling code that used to throw Invalid Value that it was supposed to fix
:frowning:

To be clear. Itā€™s in the calling of GetEntryType in the statement:

RedisKeyType keyType = redisClient.GetEntryType(listID);

The change only includes the unknown value in the Exception message, thereā€™s no issue with GetEntryType() implementation which handles all possible values of Redis TYPE command.

The 1 is invalid data for the TYPE command suggesting that the connection is corrupted, typically because the same connection is being shared across multiple threads. Note that only the Redis Client Managers are Thread Safe, the IRedisClient connection can only be used from the Thread that itā€™s resolved with (e.g. using GetClient()) and should be disposed immediately after use, i.e. it should not held in a static variable or maintained as a field in a singleton dependency, etc. The issue would be somewhere else in your code base thatā€™s not managing the Redis Client instances properly.

If you can provide a stand-alone repro I can run locally to reproduce the issue, Iā€™ll be able to determine the cause of the issue.

Why not make all the clients threadsafe by default? Performance overhead?
I think you are correct that not using the threadsafe clients cause the issue. Just need to confirm this.

As per the .NET guidelines and default behaviour, .NET instances especially those wrapping a resource or Network connection like an ADO.NET DB connection or a Socket are not ThreadSafe by default.

Iā€™m curious on the usage of the RedisManagerPool without the usage of a IOC framework. Could you give me an example of this? Hereā€™s my attempt:

RedisClient redisClient = redisConfiguration.RedisClient;
RedisManagerPool manPool = new RedisManagerPool(redisClient.Host);
using (IRedisClient redisClientTemp = manPool.GetClient())
 {
     RedisKeyType type = redisClientTemp.GetEntryType("MyKeyHere");
 }

So far using this methodology works. Wondering if this using stuff could be wrapped in an extension.

The RedisManagerPool is a Redis Client Manager which are all ThreadSafe, there should also only be a single instance of RedisManagerPool so all connections share the same pool.

If youā€™re not using an IOC then you can you can store it in a singleton like:

public class RedisConfig
{
    public static RedisConfig Instance = new RedisConfig();

    public IRedisClientsManager RedisManager { get; set; }

    public static IRedisClient GetClient() => Instance.RedisManager.GetClient();
}

The Redis Manager would need to be configured, e.g:

RedisConfig.Instance.RedisManager = new RedisManagerPool(redisHost);

Then your App can resolve clients with:

using (var redis = RedisConfig.GetClient())
{
    var type = redis.GetEntryType("MyKeyHere");
}

This looks strange, the redisConfiguration should ideally only be returning a host string, not a RedisClient instance:

RedisClient redisClient = redisConfiguration.RedisClient;

So all RedisClient instances are instead created from the RedisManagerPool.

newing up the ā€œRedisManagerā€ here does not workā€¦ were you thinking of a different type? Maybe this would be a more general definition that would abstract away the need to use the using block.

public class ThreadSafeRedisInstance
{
    public static ThreadSafeRedisInstance Instance = new ThreadSafeRedisInstance();

    public RedisManagerPool RedisManager { get; set; }

    public static IRedisClient GetClient() => Instance.RedisManager.GetClient();

    public static void ExecuteCommand(Action<IRedisClient> actionOnClient)
    {
        using (var redis = GetClient())
        {
            actionOnClient(redis);
        }
    }

    public static T ExecuteCommand<T>(Func<IRedisClient, T> actionOnClient)
    {
        using (var redis = GetClient())
        {
            T t = actionOnClient(redis);
            return t;
        }
    }
}

Setup would look like:

RedisClient redisClient = redisConfiguration.RedisClient;
ThreadSafeRedisInstance.Instance.RedisManager = new RedisManagerPool(redisClient.Host);

Calling code would look like:

bool listHasElements = false; 
listHasElements = ThreadSafeRedisInstance.ExecuteCommand(r => r.ListHasElements(listToConsume));

It should be RedisManagerPool or any of the other Redis Clients Managers.