using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Text;
namespace CYQ.Data.Cache
{
///
/// Memcached client main class.
/// Use the static methods Setup and GetInstance to setup and get an instance of the client for use.
///
internal class MemcachedClient : ClientBase
{
#region Static fields and methods.
private static LogAdapter logger = LogAdapter.GetLogger(typeof(MemcachedClient));
public static MemcachedClient Create(string configValue)
{
return new MemcachedClient(configValue);
}
//Private constructor
private MemcachedClient(string configValue)
{
hostServer = new HostServer(CacheType.MemCache, configValue);
}
#endregion
#region Set, Add, and Replace.
///
/// This method corresponds to the "set" command in the memcached protocol.
/// It will unconditionally set the given key to the given value.
/// Using the overloads it is possible to specify an expiry time, either relative as a TimeSpan or
/// absolute as a DateTime. It is also possible to specify a custom hash to override server selection.
/// This method returns true if the value was successfully set.
///
public bool Set(string key, object value) { return store("set", key, true, value, hash(key), 0); }
//public bool Set(string key, object value, uint hash) { return store("set", key, false, value, this.hash(hash), 0); }
public bool Set(string key, object value, TimeSpan expiry) { return store("set", key, true, value, hash(key), (int)expiry.TotalSeconds); }
//public bool Set(string key, object value, uint hash, TimeSpan expiry) { return store("set", key, false, value, this.hash(hash), (int)expiry.TotalSeconds); }
public bool Set(string key, object value, DateTime expiry) { return store("set", key, true, value, hash(key), getUnixTime(expiry)); }
// public bool Set(string key, object value, uint hash, DateTime expiry) { return store("set", key, false, value, this.hash(hash), getUnixTime(expiry)); }
///
/// This method corresponds to the "add" command in the memcached protocol.
/// It will set the given key to the given value only if the key does not already exist.
/// Using the overloads it is possible to specify an expiry time, either relative as a TimeSpan or
/// absolute as a DateTime. It is also possible to specify a custom hash to override server selection.
/// This method returns true if the value was successfully added.
///
//public bool Add(string key, object value) { return store("add", key, true, value, hash(key), 0); }
////public bool Add(string key, object value, uint hash) { return store("add", key, false, value, this.hash(hash), 0); }
//public bool Add(string key, object value, TimeSpan expiry) { return store("add", key, true, value, hash(key), (int)expiry.TotalSeconds); }
//public bool Add(string key, object value, uint hash, TimeSpan expiry) { return store("add", key, false, value, this.hash(hash), (int)expiry.TotalSeconds); }
//public bool Add(string key, object value, DateTime expiry) { return store("add", key, true, value, hash(key), getUnixTime(expiry)); }
//public bool Add(string key, object value, uint hash, DateTime expiry) { return store("add", key, false, value, this.hash(hash), getUnixTime(expiry)); }
///
/// This method corresponds to the "replace" command in the memcached protocol.
/// It will set the given key to the given value only if the key already exists.
/// Using the overloads it is possible to specify an expiry time, either relative as a TimeSpan or
/// absolute as a DateTime. It is also possible to specify a custom hash to override server selection.
/// This method returns true if the value was successfully replaced.
///
//public bool Replace(string key, object value) { return store("replace", key, true, value, hash(key), 0); }
//public bool Replace(string key, object value, uint hash) { return store("replace", key, false, value, this.hash(hash), 0); }
//public bool Replace(string key, object value, TimeSpan expiry) { return store("replace", key, true, value, hash(key), (int)expiry.TotalSeconds); }
//public bool Replace(string key, object value, uint hash, TimeSpan expiry) { return store("replace", key, false, value, this.hash(hash), (int)expiry.TotalSeconds); }
//public bool Replace(string key, object value, DateTime expiry) { return store("replace", key, true, value, hash(key), getUnixTime(expiry)); }
//public bool Replace(string key, object value, uint hash, DateTime expiry) { return store("replace", key, false, value, this.hash(hash), getUnixTime(expiry)); }
public enum CasResult
{
Stored = 0,
NotStored = 1,
Exists = 2,
NotFound = 3
}
//Private overload for the Set, Add and Replace commands.
private bool store(string command, string key, bool keyIsChecked, object value, uint hash, int expiry)
{
return store(command, key, keyIsChecked, value, hash, expiry, 0).StartsWith("STORED");
}
//Private overload for the Append and Prepend commands.
private bool store(string command, string key, bool keyIsChecked, object value, uint hash)
{
return store(command, key, keyIsChecked, value, hash, 0, 0).StartsWith("STORED");
}
//Private overload for the Cas command.
private CasResult store(string key, bool keyIsChecked, object value, uint hash, int expiry, ulong unique)
{
string result = store("cas", key, keyIsChecked, value, hash, expiry, unique);
if (result.StartsWith("STORED"))
{
return CasResult.Stored;
}
else if (result.StartsWith("EXISTS"))
{
return CasResult.Exists;
}
else if (result.StartsWith("NOT_FOUND"))
{
return CasResult.NotFound;
}
return CasResult.NotStored;
}
//Private common store method.
private string store(string command, string key, bool keyIsChecked, object value, uint hash, int expiry, ulong unique)
{
if (!keyIsChecked)
{
checkKey(key);
}
return hostServer.Execute(hash, "", delegate(MSocket socket)
{
SerializedType type;
byte[] bytes;
//Serialize object efficiently, store the datatype marker in the flags property.
try
{
bytes = Serializer.Serialize(value, out type, compressionThreshold);
}
catch (Exception e)
{
//If serialization fails, return false;
logger.Error("Error serializing object for key '" + key + "'.", e);
return "";
}
//Create commandline
string commandline = "";
switch (command)
{
case "set":
case "add":
case "replace":
commandline = command + " " + key + " " + (ushort)type + " " + expiry + " " + bytes.Length + "\r\n";
break;
case "append":
case "prepend":
commandline = command + " " + key + " 0 0 " + bytes.Length + "\r\n";
break;
case "cas":
commandline = command + " " + key + " " + (ushort)type + " " + expiry + " " + bytes.Length + " " + unique + "\r\n";
break;
}
//Write commandline and serialized object.
socket.Write(commandline);
socket.Write(bytes);
socket.Write("\r\n");
return socket.ReadResponse();
});
}
#endregion
#region Get
///
/// This method corresponds to the "get" command in the memcached protocol.
/// It will return the value for the given key. It will return null if the key did not exist,
/// or if it was unable to retrieve the value.
/// If given an array of keys, it will return a same-sized array of objects with the corresponding
/// values.
/// Use the overload to specify a custom hash to override server selection.
///
public object Get(string key) { ulong i; return get("get", key, true, hash(key), out i); }
// public object Get(string key, uint hash) { ulong i; return get("get", key, false, this.hash(hash), out i); }
private object get(string command, string key, bool keyIsChecked, uint hash, out ulong unique)
{
if (!keyIsChecked)
{
checkKey(key);
}
ulong __unique = 0;
object value = hostServer.Execute