Skip to content

Instantly share code, notes, and snippets.

@jakejscott
Created October 5, 2011 15:01
Show Gist options
  • Save jakejscott/1264646 to your computer and use it in GitHub Desktop.
Save jakejscott/1264646 to your computer and use it in GitHub Desktop.
Lightspeed ORM second level cache using MongoDB (Doesn't support expirying items yet..todo)
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Loot.Logging;
using Mindscape.LightSpeed.Caching;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
namespace Loot
{
public class MongoCache : ICache, IDisposable
{
/// <summary>
/// Assumes that MongoDB and Lightspeed are both setup to use Local Time and not Utc Time!
/// </summary>
public MongoCache(MongoDatabase database)
{
this.database = database;
cache = this.database.GetCollection<MongoCacheItem>("Cache");
cache.RemoveAll(SafeMode.True);
}
public object GetItem(string key)
{
var cacheItem = cache.FindOneById(key);
if (cacheItem == null)
return null;
if (DateTime.Now > cacheItem.ExpireAt)
{
log.Info(string.Format("Found an item that has expired at {0}. The time now is {1}.", cacheItem.ExpireAt, DateTime.Now));
return null;
}
using (var stream = new MemoryStream(cacheItem.Value))
{
var formatter = new BinaryFormatter();
return formatter.Deserialize(stream);
}
}
public void AddItem(string key, object item, TimeSpan expiry)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, item);
var bytes = StreamHelper.ReadToEnd(stream);
var cacheItem = new MongoCacheItem
{
Id = key,
Value = bytes,
ExpireAt = DateTime.Now + expiry
};
log.Info(string.Format("Adding item to expire at {0}. The time now is {1}.", cacheItem.ExpireAt, DateTime.Now));
cache.Insert(cacheItem, SafeMode.True);
}
}
public void SetItem(string key, object item, TimeSpan expiry)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, item);
var bytes = StreamHelper.ReadToEnd(stream);
var expiresAt = DateTime.Now + expiry;
log.Info(string.Format("Setting item to expire at {0}. The time now is {1}.", expiresAt, DateTime.Now));
cache.Update(Query.EQ("_id", key), Update.Set("Value", bytes).Set("ExpireAt", expiresAt), SafeMode.True);
}
}
public void RemoveItem(string key)
{
log.Info("Removing item. The time now is " + DateTime.Now);
cache.Remove(Query.EQ("_id", key), SafeMode.True);
}
~MongoCache()
{
Cleanup();
}
public void Dispose()
{
Cleanup();
GC.SuppressFinalize(this);
}
private void Cleanup()
{
if (disposed)
return;
disposed = true;
cache.RemoveAll(SafeMode.True);
}
private readonly ILog log = LogManager.GetLogger(typeof(MongoCache));
private bool disposed = false;
private readonly MongoDatabase database;
private readonly MongoCollection<MongoCacheItem> cache;
}
public class MongoCacheItem
{
public string Id { get; set; }
public byte[] Value { get; set; }
public DateTime ExpireAt { get; set; }
}
public class StreamHelper
{
public static byte[] ReadToEnd(Stream stream)
{
long originalPosition = stream.Position;
stream.Position = 0;
try
{
byte[] readBuffer = new byte[4096];
int totalBytesRead = 0;
int bytesRead;
while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
{
totalBytesRead += bytesRead;
if (totalBytesRead == readBuffer.Length)
{
int nextByte = stream.ReadByte();
if (nextByte != -1)
{
byte[] temp = new byte[readBuffer.Length * 2];
Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
readBuffer = temp;
totalBytesRead++;
}
}
}
byte[] buffer = readBuffer;
if (readBuffer.Length != totalBytesRead)
{
buffer = new byte[totalBytesRead];
Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
}
return buffer;
}
finally
{
stream.Position = originalPosition;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment