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);
}