patterncsharpMinor
MemoryCache as a message broker?
Viewed 0 times
memorycachemessagebroker
Problem
I'm in the middle of a project that was built around Microsoft's Message Queue service. During development all of our machines are on a domain and we were able to create a public queue accessible from the server and client.
When moving this to our test server, I discovered that both the test server and production servers are running in workgroup mode, no public queues available.
I'm experimenting with creating a webservice broker backed by a MemoryCache. Here is what I've got so far. Each key is posted into a FIFO queue due to the needs of the project.
Has anyone else done something similar? What about concurrency? Any other gotchas I should be concerned about?
UPDATED: Added suggested changes, and handled differences resulting from use of ConcurrentQueue and Lazy instantiation.
I also added an exten
When moving this to our test server, I discovered that both the test server and production servers are running in workgroup mode, no public queues available.
I'm experimenting with creating a webservice broker backed by a MemoryCache. Here is what I've got so far. Each key is posted into a FIFO queue due to the needs of the project.
Has anyone else done something similar? What about concurrency? Any other gotchas I should be concerned about?
UPDATED: Added suggested changes, and handled differences resulting from use of ConcurrentQueue and Lazy instantiation.
[WebMethod]
public bool Push(string key, string value)
{
if (String.IsNullOrWhiteSpace(key) || String.IsNullOrWhiteSpace(value)) return false;
// input validation removed for brevity
var queue = GetQueue(key);
queue.Enqueue(value);
return true;
}
[WebMethod]
public string Pop(string key)
{
var queue = GetQueue(key);
string result = "";
if (queue.TryDequeue(out result))
{
return result;
}
return null;
}
[WebMethod]
public List RemoveAll(string key)
{
var queue = GetQueue(key);
var list = queue.ToList();
queue.Clear();
return list;
}
private ConcurrentQueue GetQueue(string key)
{
var cache = MemoryCache.Default;
var queue = (Lazy>) (cache.AddOrGetExisting(key.ToLowerInvariant(),new Lazy>() , new CacheItemPolicy
{
AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration,
SlidingExpiration = TimeSpan.FromMinutes(5)
}) ?? cache[key.ToLowerInvariant()]);
return queue.Value;
}I also added an exten
Solution
What you have here is, IMHO, excellent.
Couple nitpicks:
-
This part (in the
Would look more consistent if written like this:
Or, since it's used as an
As for the
The first is simply to create a
The second is to continually remove from the collection until it's empty, e.g.
The former will be faster, but you also run the risk of losing some items you didn't intend to lose (then again, if the whole point is to empty the collection, that might not matter), and it only works if you have access to all of the variables holding references to the queue.
The latter is effective, but it runs the risk of running indefinitely if other threads are concurrently appending; it's also more expensive that simply allocating a new queue.
Couple nitpicks:
- I don't get why
RemoveAllis returning the original content instead ofvoid- also I'm expecting an exception to be thrown inGetQueueif the specifiedkeydoesn't exist.
-
This part (in the
Pop(string) method):var queue = GetQueue(key);
string result = "";Would look more consistent if written like this:
var queue = GetQueue(key);
var result = string.Empty;Or, since it's used as an
out parameter value on the next line (and therefore is compiler-enforced to be initialized after the call to TryDequeue(out string)), simply like this:var queue = GetQueue(key);
string result;As for the
Clear extension method, there's a potential tiny little issue here - see this MSDN thread:ConcurrentQueue's implementation is based on a particular lock-free algorithm that doesn't permit atomically clearing of the collection, hence why no Clear method is exposed. As such, you have two primary options.The first is simply to create a
new ConcurrentQueue instead of clearing the original, and then swap in the new for the original.The second is to continually remove from the collection until it's empty, e.g.
T ignored; while(cq.TryDequeue(out ignored));. Which you choose is largely dependent on the needs of your application. The former will be faster, but you also run the risk of losing some items you didn't intend to lose (then again, if the whole point is to empty the collection, that might not matter), and it only works if you have access to all of the variables holding references to the queue.
The latter is effective, but it runs the risk of running indefinitely if other threads are concurrently appending; it's also more expensive that simply allocating a new queue.
Code Snippets
var queue = GetQueue(key);
string result = "";var queue = GetQueue(key);
var result = string.Empty;var queue = GetQueue(key);
string result;Context
StackExchange Code Review Q#44077, answer score: 3
Revisions (0)
No revisions yet.