Pregunta

I'm receiving a SocketException when using BookSleeve to connect and add/get entries to/from my Redis server. The specific exception is: Only one usage of each socket address (protocol/network address/port) is normally permitted.

The stack trace points to: BookSleeve.RedisConnectionBase.Wait(Task task) in d:\Dev\BookSleeve\BookSleeve\RedisConnectionBase.cs:2050

My question is whether I'm doing something wrong in my code (highly likely) that isn't properly closing connections or something of that nature? The exception tends to be thrown after ~16000 operations or so are performed in about 60 seconds. That doesn't seem like an overly abundant amount of operations, right?

Relevant code is below and I'm happy to provide any other information.

public Cache.CacheEntry Get(string key, bool updateAccessed)
{
    var cacheKey = GetCacheableKey(key);    

    using (var conn = GetUnsecuredConnection())
    {
        var entry = conn.Get<Cache.CacheEntry>(cacheKey);
        return entry;
    }
}

public override void Add(string key, Cache.CacheEntry entry)
{
    var cacheKey = GetCacheableKey(key);    

    using (var conn = GetUnsecuredConnection())
    {
        using (var trans = conn.CreateTransaction())
        {
            trans.Set(cacheKey, entry);
            trans.Strings.Increment(DbNum, CacheEntryCountKey);
            trans.Strings.Increment(DbNum, CacheSizeKey, entry.DataLength);
            trans.Sets.Add(DbNum, CacheKeysKey, cacheKey);
            trans.Execute();
        }
    }
}

Retrieving a connection (this was largely copied from the BookSleeve unit tests:

protected RedisConnection GetUnsecuredConnection(bool open = true, bool allowAdmin = false, bool waitForOpen = false)
{
    var host = ConfigurationManager.AppSettings["Redis.BookSleeve.Host"];
    var unsecuredPort = int.Parse(ConfigurationManager.AppSettings["Redis.BookSleeve.UnsecuredPort"]); //TODO: get this setting in a safer manner

    var conn = new RedisConnection(host, unsecuredPort, syncTimeout: 5000, ioTimeout: 5000, allowAdmin: allowAdmin);
    conn.Error += (s, args) => Log.Error(args.Exception.Message + args.Cause, args.Exception, typeof(RedisProviderBookSleeve));
    if (open)
    {
        var openAsync = conn.Open();
        if (waitForOpen) conn.Wait(openAsync);
    }
    return conn;
}

Generic extension methods for the RedisConnection object:

public static T Get<T>(this RedisConnection conn, string key, int dbNum = 0)
{
    if (typeof (T) == typeof (byte[]))
    {
        var task = conn.Strings.Get(dbNum, key);
        var result = conn.Wait(task);
        return (T) (object) result;
    }
    else
    {
        var task = conn.Strings.GetString(dbNum, key);
        var result = conn.Wait(task);
        if (result == null || typeof(T) == typeof(string))
            return (T)(object)result;

        return JsonSerializer.DeserializeFromString<T>(result);
    }
}


public static void Set<T>(this RedisConnection conn, string key, T value, int dbNum = 0)
{
    if (typeof (T) == typeof (byte[]))
    {
        conn.Strings.Set(dbNum, key, value as byte[]);
        return;
    }

    var serializedValue = JsonSerializer.SerializeToString(value);
    conn.Strings.Set(dbNum, key, serializedValue);
}
¿Fue útil?

Solución

Marc's comment on the question led me to reconsider how I was utilizing connections. I came across this answer to another question:

Maintaining an open Redis connection using BookSleeve

I ended up using the code from the above linked answer, removed the 'GetUnsecuredConnection' method that I posted and now just retrieve the open connection before constructing/executing a Redis operation.

No more SocketException and everything seems to be behaving as expected. Thanks for the tip Marc! And thanks to @ofer-zelig for the connection code.

public Cache.CacheEntry Get(string key, bool updateAccessed)
{
    var cacheKey = GetCacheableKey(key);    

    var conn = RedisConnectionGateway.Current.GetConnection();

    var entry = conn.Get<Cache.CacheEntry>(cacheKey);
    return entry;        
}

public override void Add(string key, Cache.CacheEntry entry)
{
    var cacheKey = GetCacheableKey(key);    

    var conn = RedisConnectionGateway.Current.GetConnection();

    using (var trans = conn.CreateTransaction())
    {
        trans.Set(cacheKey, entry);
        trans.Strings.Increment(DbNum, CacheEntryCountKey);
        trans.Strings.Increment(DbNum, CacheSizeKey, entry.DataLength);
        trans.Sets.Add(DbNum, CacheKeysKey, cacheKey);
        trans.Execute();
    }        
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top