/// <summary> /// Stores data to cache. /// /// If data does not already exist for this key on the server, or if the key is being<br/> /// deleted, the specified value will not be stored.<br/> /// The server will automatically delete the value when the expiration time has been reached.<br/> /// <br/> /// If compression is enabled, and the data is longer than the compression threshold<br/> /// the data will be stored in compressed form.<br/> /// <br/> /// As of the current release, all objects stored will use serialization. /// </summary> /// /// <param name="cmdType">action to take (set, add, replace)</param> /// <param name="key">key to store cache under</param> /// <param name="value">object to cache</param> /// <param name="expiry">expiration, if any</param> /// <param name="sock">SockIO to use</param> /// /// <returns><code>true</code> if successful, <code>fale</code> or exception otherwise</returns> private bool SetItem(SetCommandType cmdType, string key, object value, DateTime expiry, SockIOPool.SockIO sock) { // store flags int flags = 0; // byte array to hold data byte[] val; if(expiry.Equals(DateTime.MinValue)) expiry = new DateTime(0); // serialize the object // unless client request data to be not serialized // in which case, we will go with string representation // but only for a select few classes if (!(value is string || value is Int32 || value is Double || value is Single || value is Int64 || value is SByte || value is Int16 || value is Char || value is StringBuilder)) { // we can ONLY not serialize the above classes // this is generally a bad idea as we should always // just use serialization. // but, it is useful for sharing data between non-.NET // and also for storing ints for the increment method if(log.IsInfoEnabled) log.Info("storing data as a string for key: " + key + " for class: " + value.GetType().FullName); val = UTF8Encoding.UTF8.GetBytes(value.ToString()); } else { if(log.IsInfoEnabled) log.Info("serializing for key: " + key + " for class: " + value.GetType().FullName); try { MemoryStream bos = new MemoryStream(); new BinaryFormatter().Serialize((new BinaryWriter(bos)).BaseStream, value); val = bos.ToArray(); flags |= F_SERIALIZED; } catch(SerializationException se) { if(log.IsErrorEnabled) log.Error("failed to serialize obj: " + value.ToString(), se); // return socket to pool and bail sock.Close(); #if EXCEPTIONS throw new MemcachedException(se); #else return false; #endif } } // now try to compress if we want to // and if the length is over the threshold if (compressEnable && val.Length > compressThreshold) { if(log.IsInfoEnabled) log.Info("trying to compress data, size prior to compression: " + val.Length); try { MemoryStream bos = new MemoryStream(val.Length); GZipOutputStream gos = new GZipOutputStream(bos); gos.Write(val, 0, val.Length); gos.Close(); // store it and set compression flag val = bos.ToArray(); flags |= F_COMPRESSED; if(log.IsInfoEnabled) log.Info("compression succeeded, size after: " + val.Length); } // TODO Not sure which specific exception(s) is/are thrown catch(Exception e) { if(log.IsErrorEnabled) log.Error("Exception while compressing stream", e); } } // now write the data to the cache server StringBuilder cmd = new StringBuilder(); switch(cmdType) { case SetCommandType.Add: cmd.Append("add"); break; case SetCommandType.Replace: cmd.Append("replace"); break; case SetCommandType.Set: cmd.Append("set"); break; default: // As if this can actually happen throw new ArgumentException("cmdType"); } cmd.Append(" "); cmd.Append(key); cmd.Append(" "); cmd.Append(flags.ToString()); cmd.Append(" "); cmd.Append(expiry.Ticks / 1000); cmd.Append(" "); cmd.Append(val.Length.ToString()); cmd.Append("\n"); try { sock.Write(UTF8Encoding.UTF8.GetBytes(cmd.ToString())); sock.Write(val); sock.Write(UTF8Encoding.UTF8.GetBytes("\n")); sock.Flush(); // get result code string line = sock.ReadLine(); if(log.IsInfoEnabled) log.Info("memcache cmd (result code): " + cmd + " (" + line + ")"); switch(line) { case STORED: if(log.IsInfoEnabled) log.Info("data successfully stored for key: " + key); return true; case NOTSTORED: if(log.IsInfoEnabled) log.Info("data not stored in cache for key: " + key); #if EXCEPTIONS throw new NotStoredException(key); #else return false; #endif default: if(log.IsErrorEnabled) log.Error("error storing data in cache for key: " + key + " -- length: " + val.Length); #if EXCEPTIONS throw new MemcachedException(line); #else return false; #endif } } catch(IOException ioe) { if(log.IsErrorEnabled) log.Error("exception thrown while writing bytes to server on set", ioe); try { sock.TrueClose(); } catch(IOException ioee) { if(log.IsErrorEnabled) log.Error("failed to close socket : " + sock.ToString(), ioee); } sock = null; #if EXCEPTIONS throw new SocketException(e); #else return false; #endif } finally { if(sock != null) sock.Close(); } }
/// <summary> /// Get item from cache /// </summary> /// <param name="key">Key of desired item</param> /// <param name="sock">Socket to use</param> /// <returns>Desired object</returns> private object GetItem(string key, SockIOPool.SockIO sock) { try { StringBuilder cmd = new StringBuilder(); cmd.Append("get "); cmd.Append(key); cmd.Append("\n"); if(log.IsDebugEnabled) log.Debug("memcache get command: " + cmd); sock.Write(UTF8Encoding.UTF8.GetBytes(cmd.ToString())); sock.Flush(); // build empty map // and fill it from server IDictionary hm = new Hashtable(); LoadItems(sock, hm); // debug code if(log.IsDebugEnabled) log.Debug("memcache: got back " + hm.Count + " results"); // return the value for this key if we found it // else return null object result = hm[key]; if(result == null) #if EXCEPTIONS throw new NotFoundException(key); #else return false; #endif return result; } catch(IOException ioe) { if(log.IsErrorEnabled) log.Error("exception thrown while trying to get object from cache for key: " + key, ioe); try { sock.TrueClose(); } catch(IOException ioee) { if(log.IsErrorEnabled) log.Error("failed to close socket : " + sock.ToString(), ioee); } sock = null; #if EXCEPTIONS throw new SocketException(e); #else return false; #endif } finally { if(sock != null) sock.Close(); } }
/// <summary> /// Increments/decrements the value at the specified key by inc. /// /// From Javadocs: /// Note that the server uses a 32-bit unsigned integer, and checks for<br/> /// underflow. In the event of underflow, the result will be zero. Because<br/> /// java lacks unsigned types, the value is returned as a 64-bit integer.<br/> /// The server will only decrement a value if it already exists;<br/> /// if a value is not found, -1 will be returned. /// /// .NET supports unsigned types so refactor /// </summary> /// /// <param name="cmdType">increment/decrement type</param> /// <param name="key">cache key</param> /// <param name="qty">amount to incr or decr</param> /// <param name="sock">SockIO to use</param> /// /// <returns> new value or -1 if not exist</returns> private long IncrDecrItem(IncrDecrCommandType cmdType, string key, long qty, SockIOPool.SockIO sock) { // Assumes non-null arguments if(log.IsDebugEnabled) log.Debug("memcache incr/decr command: " + cmdType.ToString()); // now write the data to the cache server StringBuilder cmd = new StringBuilder(); switch(cmdType) { case IncrDecrCommandType.Increment: cmd.Append("incr"); break; case IncrDecrCommandType.Decrement: cmd.Append("decr"); break; default: // As if this can actually happen throw new ArgumentException("cmdType"); } cmd.Append(" "); cmd.Append(key); cmd.Append(" "); cmd.Append(qty.ToString()); cmd.Append("\n"); try { sock.Write(UTF8Encoding.UTF8.GetBytes(cmd.ToString())); sock.Flush(); // get result back string line = sock.ReadLine(); if(Regex.IsMatch("[^0-9-]", line)) { return long.Parse(line); } else if(NOTFOUND.Equals(line)) { if(log.IsInfoEnabled) log.Info("key not found to incr/decr for key: " + key); #if EXCEPTIONS throw new NotFoundException(); #else // TODO Hacky return -1; #endif } if(log.IsErrorEnabled) log.Error("error incr/decr key: " + key); #if EXCEPTIONS throw new MemcachedException(cmd.ToString()); #else // TODO Hacky return -1; #endif } catch(IOException ioe) { // exception thrown if(log.IsErrorEnabled) log.Error("exception thrown while trying to incr/decr", ioe); try { sock.TrueClose(); } catch(IOException ioee) { if(log.IsErrorEnabled) log.Error("failed to close socket : " + sock.ToString(), ioee); } sock = null; #if EXCEPTIONS throw new SocketException(e); #else return -1; #endif } finally { if(sock != null) sock.Close(); } }
/// <summary> /// Delete item /// </summary> /// <param name="key">Key to delete</param> /// <param name="expiry">Expiration to use</param> /// <param name="sock">SockIO to use</param> /// <returns><code>true</code> if successful, <code>fale</code> or exception otherwise</returns> private static bool DeleteItem(string key, DateTime expiry, SockIOPool.SockIO sock) { // Assumes non-null arguments // build command StringBuilder command = new StringBuilder(); command.Append("delete "); command.Append(key); if(!expiry.Equals(DateTime.MinValue)) { command.Append(" "); command.Append(expiry.Ticks / 1000); } command.Append("\n"); try { sock.Write(UTF8Encoding.UTF8.GetBytes(command.ToString())); sock.Flush(); // if we get appropriate response back, then we return true string line = sock.ReadLine(); switch(line) { case DELETED: return true; case NOTFOUND: #if EXCEPTIONS throw new NotFoundException(); #else return false; #endif default: #if EXCEPTIONS throw new MemcachedException(line); #else return false; #endif } } catch(IOException ioe) { if(log.IsErrorEnabled) log.Error("exception thrown while writing bytes to server on delete", ioe); try { sock.TrueClose(); } catch(IOException ioee) { if(log.IsErrorEnabled) log.Error("failed to close socket : " + sock.ToString(), ioee); } sock = null; #if EXCEPTIONS throw new SocketException(e); #else return false; #endif } finally { if(sock != null) sock.Close(); } }