Redis on Appharbor - Booksleeve GetString exception - redis

i am trying to setup Redis on appharbor. I have followed their instructions and again i have an issue with the Booksleeve API. Here is the code i am using to make it work initially:
var connectionUri = new Uri(url);
using (var redis = new RedisConnection(connectionUri.Host, connectionUri.Port, password: connectionUri.UserInfo.Split(new[] { ':' }, 2)[1]))
{
redis.Strings.Set(1, "greeting", "welcome to remember your stuff!");
try
{
var task = redis.Strings.GetString(1, "greeting");
redis.Wait(task);
ViewBag.Message = task.Result;
}
catch (Exception)
{
// It throws an exception trying to wait for the task?
}
}
However, the issue is that it sets the string correctly, but when trying to retrieve the same string from the key value store, it throws a timeout exception waiting for the task to eexecute. However, this code works on my local redis server connection.
Am i using the API in a wrong way? or is this something related to Appharbor?
Thanks

Like a SqlConnection, you need to call Open() (otherwise your messages are queued for delivery).
Unlike SqlConnection, you should not fire up a RedisConnection each time you need it - it is intended to be used as a shared, thread-safe, multiplexer - i.e. a single connection is held somewhere and used by lots and lots of unrelated callers. Unless of course you only need to do one thing!

Related

How to span a ConcurrentDictionary across load-balancer servers when using SignalR hub with Redis

I have ASP.NET Core web application setup with SignalR scaled-out with Redis.
Using the built-in groups works fine:
Clients.Group("Group_Name");
and survives multiple load-balancers. I'm assuming that SignalR persists those groups in Redis automatically so all servers know what groups we have and who are subscribed to them.
However, in my situation, I can't just rely on Groups (or Users), as there is no way to map the connectionId (Say when overloading OnDisconnectedAsync and only the connection id is known) back to its group, and you always need the Group_Name to identify the group. I need that to identify which part of the group is online, so when OnDisconnectedAsync is called, I know which group this guy belongs to, and on which side of the conversation he is.
I've done some research, and they all suggested (including Microsoft Docs) to use something like:
static readonly ConcurrentDictionary<string, ConversationInformation> connectionMaps;
in the hub itself.
Now, this is a great solution (and thread-safe), except that it exists only on one of the load-balancer server's memory, and the other servers have a different instance of this dictionary.
The question is, do I have to persist connectionMaps manually? Using Redis for example?
Something like:
public class ChatHub : Hub
{
static readonly ConcurrentDictionary<string, ConversationInformation> connectionMaps;
ChatHub(IDistributedCache distributedCache)
{
connectionMaps = distributedCache.Get("ConnectionMaps");
/// I think connectionMaps should not be static any more.
}
}
and if yes, is it thread-safe? if no, can you suggest a better solution that works with Load-Balancing?
Have been battling with the same issue on this end. What I've come up with is to persist the collections within the redis cache while utilising a StackExchange.Redis.IDatabaseAsync alongside locks to handle concurrency.
This unfortunately makes the entire process sync but couldn't quite figure a way around this.
Here's the core of what I'm doing, this attains a lock and return back a deserialised collection from the cache
private async Task<ConcurrentDictionary<int, HubMedia>> GetMediaAttributes(bool requireLock)
{
if(requireLock)
{
var retryTime = 0;
try
{
while (!await _redisDatabase.LockTakeAsync(_mediaAttributesLock, _lockValue, _defaultLockDuration))
{
//wait till we can get a lock on the data, 100ms by default
await Task.Delay(100);
retryTime += 10;
if (retryTime > _defaultLockDuration.TotalMilliseconds)
{
_logger.LogError("Failed to get Media Attributes");
return null;
}
}
}
catch(TaskCanceledException e)
{
_logger.LogError("Failed to take lock within the default 5 second wait time " + e);
return null;
}
}
var mediaAttributes = await _redisDatabase.StringGetAsync(MEDIA_ATTRIBUTES_LIST);
if (!mediaAttributes.HasValue)
{
return new ConcurrentDictionary<int, HubMedia>();
}
return JsonConvert.DeserializeObject<ConcurrentDictionary<int, HubMedia>>(mediaAttributes);
}
Updating the collection like so after I've done manipulating it
private async Task<bool> UpdateCollection(string redisCollectionKey, object collection, string lockKey)
{
var success = false;
try
{
success = await _redisDatabase.StringSetAsync(redisCollectionKey, JsonConvert.SerializeObject(collection, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}));
}
finally
{
await _redisDatabase.LockReleaseAsync(lockKey, _lockValue);
}
return success;
}
and when I'm done I just ensure the lock is released for other instances to grab and use
private async Task ReleaseLock(string lockKey)
{
await _redisDatabase.LockReleaseAsync(lockKey, _lockValue);
}
Would be happy to hear if you find a better way of doing this. Struggled to find any documentation on scale out with data retention and sharing.

Transaction not working after unsuccessful connection to redis using StackExchange.Redis

I'm not sure, whether it is a bug or I'm doing something wrong. Here is the code:
using StackExchange.Redis;
ConnectionMultiplexer conn = null;
while (conn == null);
{
try
{
conn = ConnectionMultiplexer.Connect("localhost:6379");
}
catch (Exception)
{
conn = null;
Thread.Sleep(TimeSpan.FromSeconds(5));
}
}
var db = conn.GetDatabase();
var transaction = db.CreateTransaction();
var tasks = new List<Task>();
tasks.Add(transaction.HashSetAsync("key", "field", "value"));
if (transaction.Execute())
{
Task.WaitAll(tasks.ToArray());
}
When I run it with started redis (windows version - 2.6, 2.8.17, 2.8.19), everything works fine. If I start redis after few loops of the cycle, either it doesn't jump into if-statement or it jumps and get blocked on WaitAll(). If I try to check values in redis, they are stored.
This situation happens when we start server and forget to start redis. After postpone start of redis it gets stuck. The same problem appears when using batch instead of transaction.
Am I doing connection to multiplexer wrong or is it bug? (I found few that looked similar but I'm not sure)
It was a bug in older versions of StackExchange.Redis - 1.0.481, 1.0.488 (didn't test any older ones). With new version 1.1.553 it works fine (https://github.com/StackExchange/StackExchange.Redis/issues/200).

IRedisClient not disposed after using

I am using an ServiceStack IRedis client as follows
public static IRedisList<MyType> getList(string listkey)
{
using(var redis = new RedisClient())
{
var client = redis.As<MyType>();
return client.Lists[listkey];
}
}
public void AddSomething(MyType newType)
{
var list = getList("somekey);
list.Add(newType);.
}
according to the Redis-Server output, everytime I do this a new client connection is added, and it never is getting disposed. (Client count is always increasing).
Should I not be manipulating the IRedisList as such?
IRedisList is like a proxy wrapper for a redis server-side list which includes a reference to the RedisClient it was created with in order to talk Redis.
The using(var redis = new RedisClient()) { ... } statement does dispose of the Client connection, unfortunately when you make any further calls with the Disposed client it re-opens the connection again and since it isn't in a using statement or not manually disposed will keep the connection open.
To prevent this from happening, you should not be accessing the RedisList proxy outside of the using scope of the RedisClient in which it was created. If you still want to reference the list outside of this scope you should clone the contents into an In-Memory list with:
var disconnectedInMemoryCopy = client.Lists[listKey].ToList();
This returns a populated normal C# List<T> that's de-coupled from the RedisList proxy.

Error when trying add data to RavenDb

I'm using autofac and the interfaces are correctly resolved but this code fails with "No connection could be made because the target machine actively refused it 127.0.0.1:8081"
using (var store = GetService<IDocumentStore>())
{
using (var session = store.OpenSession())
{
session.Store(new Entry { Author = "bob", Comment = "My son says this", EntryId = Guid.NewGuid(), EntryTime = DateTime.Now, Quote = "I hate you dad." });
session.SaveChanges();
}
}
Here is the registration
builder.Register<IDocumentStore>(c =>
{
var store = new DocumentStore { Url = "http://localhost:8081" };
store.Initialize();
return store;
}).SingleInstance();
When I navigate to http://localhost:8081 I do get the silverlight management UI. Although I'm running a Windows VM and vmware and Silverlight5 don't play together. That's another issue entirely. Anyways does anyone see what I'm doing wrong here or what I should be doing differently? Thanks for any code, tips, or tricks.
On a side note, can I enter some dummy records from a command line interface? Any docs or examples of how I can do that?
Thanks All.
Just curious, are you switching RavenDB to listen on 8081? The default is 8080. If you're getting the management studio to come up, I suspect you are.
I'm not too familiar with autofac but, it looks like you're wrapping your singleton DocumentStore in a using statement.
Try:
using (var session = GetService<IDocumentStore>().OpenSession())
{
}
As far as dummy records go, the management studio will ask you if you want to generate some dummy data if your DB is empty. If you can't get silverlight to work in the VM, I'm not sure if there's another automated way to do it.
Perhaps using smuggler:
http://ravendb.net/docs/server/administration/export-import
But you'd have to find something to import.

LinQ to SQL : InvalidOperationException in a Windows Service

I have implemented a small Windows Service which runs every 9 minutes and writes the data which comes througth a webservice to the db.
Do do the db work I use Linq To SQL
using (var db = new DataClasses1DataContext())
{
var currentWeather = this.GetWeatherData();
//////TODO Add the data correct
var newEntry = new WeatherData()
{
test = currentWeather.dateGenerated.ToShortTimeString()
};
//var test = db.WeatherDatas.First();
db.WeatherDatas.InsertOnSubmit(newEntry); // this throws Invalid Operation Exception
db.SubmitChanges();
}
Why does it throw this exception? the same codeblock in a console programm runs nice
alt text http://img687.imageshack.us/img687/7588/unbenanntxb.png
Have you set up the connection string in app.Config correctly?
IIRC, the default constructor on an L2S DataContext reads the connection string from the config file. If the connection string points to the the wrong database (or one that doesn't exist) you may very well receive an exception.
This could also explain why this piece of code works when executed in a different context.