CacheAdapter–V2 Now with memcached support

Published Monday, July 04, 2011 3:00 PM

Previously I blogged about my CacheAdapter project that is available as a Nuget package and allows you to program against a single interface implementation, but have support for using Memory, ASP.NET Web or Windows AppFabric cache mechanisms via configuration.

I am happy to announce that my CacheAdapter now has support for memcached. Version 2 of the Nuget package is available here. Alternatively, all the source code is available here.

What this now means is you can write one line of code to get or store an item in the cache, and that code can automatically support using Windows AppFabric, memcached, MemoryCache or ASP.NET web cache. No code change required.

I am particularly happy about having memcached support as it means a few things:

  • Free / Open Source: It is a free, well established open source caching engine that is widely used in many applications.
  • Easy: It is easy to setup.
  • Simple and Cheap: It provides an alternative to using Windows AppFabric. AppFabric can be a little tricky to setup sometimes. If you are using Windows Azure, AppFabric is a simple checkbox BUT you need to pay extra for the privilege of using it based on how much you use the cache service. By contrast, memcached can be installed easily on Azure and requires no extra cost whatsoever.
  • Auto fail over support: In addition, Windows AppFabric has some limitations for a relatively small cache farm. AppFabric utilises a “lead host” to co-ordinate small cache farms of 3 or less cache servers. If the lead host goes down, they all go down. The memcached implementation has no reliance on any single point of failure so if one memcached node fails, requests will automatically be redirected to the nodes that are alive. If the dead node comes back alive again, it is re-introduced to the cache pool after about 1 minute or so. At worst, it results in a few cache misses.

Note: This release contains a few namespace changes that may break older versions if you are using the objects directly. Namely AppFabric object support has been moved from the Glav.CacheAdapter.Distributed namespace to the Glav.CacheAdapter.Distributed.AppFabric namespace. This is to allow differentiation from AppFabric and memcached within the distributed namespace.

In addition, at the request of some users, I have added a simple ‘Add’ method to the ICacheProvider interface for ease of use. The interface now looks like this:

public interface ICacheProvider
{
    T Get<T>(string cacheKey, DateTime absoluteExpiryDate, GetDataToCacheDelegate<T> getData, bool addToPerRequestCache = false) where T : class;
    T Get<T>(string cacheKey, TimeSpan slidingExpiryWindow, GetDataToCacheDelegate<T> getData, bool addToPerRequestCache = false) where T : class;
    void InvalidateCacheItem(string cacheKey);
    void Add(string cacheKey, DateTime absoluteExpiryDate, object dataToAdd);
    void Add(string cacheKey, TimeSpan slidingExpiryWindow, object dataToAdd);
    void AddToPerRequestCache(string cacheKey, object dataToAdd);
}

So that is it. I hope you enjoy using memcached support within the CacheAdapter.

by Glav

Comments

# manna said on Monday, July 04, 2011 10:51 AM

Hi Glav, nicely done! Really like your caching framework :)

A quick question: your framework doesn't provide for synchronizing cache access (i.e. multiple requests to read/write the same cache item simultaneously). Or is this something that is taken care of by the specific cache implementation used (currently I am using System.Runtime.Caching.MemoryCache)?

# Glav said on Monday, July 04, 2011 6:44 PM

Hi manna,

This is typically taken care of by the respective cache mechanism. AFAIK, each cache implementation takes care of this for you.

# Dan said on Monday, July 04, 2011 9:23 PM

Hey Glav,

How about adding a CacheSetting for None or Off. Just in case you want to turn it off for what ever reason in production. Or is this possible another way?

public enum CacheSetting

{

   Memory,

   Web,

   AppFabric,

   memcached,

None

}

Cheers

Dan.

# Glav said on Tuesday, July 05, 2011 6:39 PM

Hey Dan,

What I have generally done in the past is have cache settings for individual elements (say a cache expiry setting for customer list, one for customer type etc...) and set these specific settings to 0 if I dont want them c cached. Obviously this means work on your part to implement this. Having a cache setting of None would be relatively easy to do though. Can u provide an example use case of where or how you would see this being used though?

Thanks for the feedback.

# Dan said on Tuesday, July 05, 2011 8:00 PM

Hey Glav,

Use case

A developer doesn't not use a cache key explicit enough during development and the cached data is incorrect given the context. If this code makes it into a production environment and you can't easily do a release and fix the cache key due to change control processes etc then having a configurable switch to disable the cache would enable you to turn off caching until the bug is fixed. From my experience getting a config change past change control is quicker then getting a full blown code change into a production environment.

The use case is  just a fringe case but i have seen it happen in the past.

Proper testing should mean that this doesn't happen but you know that some times things like this get missed.

Setting the cache to none would just mean that it always retrieves the data from the source.

Cheers

Dan.

# Joe Audette said on Friday, July 22, 2011 2:13 PM

Hi Glav,

Nice improvements and thanks for specifying a license.

I really want to use this in the mojoPortal project but one thing holding me back is that we still support .NET 3.5 in mojoPortal. We use conditional compilation in mojoPortal so that we can use .NET 4 but leave things out or implement a different way to keep supporting .NET 3.5. We produce compiled packages for both  .NET 3.5 and .NET 4.

So I may have to fork your code for our use and use an alternate implementation of MemoryCache for .NET 3.5. I would love it if you could make it compatible so I wouldn't need to fork the code but I understand if you are only interested in supporting .NET 4. I wish I could drop support for .NET 3.5 in mojoPortal but we still have quite a few people using it.

Thanks for your great work! In any case it will save me a lot of time.

Best,

Joe

# Glav said on Sunday, July 24, 2011 12:53 AM

Hi Joe,

Main reason for .Net 4 only is the MemoryCache implementation. The AppFabric assemblies are .net  3.5 SP1 so it should be ok. It would be easy enough to remove the memory cache implementations though. I'll have a look at the alternatives in terms of supporting other similar cache mechanisms but aimed at 3.5. It sounds like it might be better to fork the code and customise it slightly for your own needs but I'll have a look. The memcached and ASP.NET web cache have no .Net 4 requirements.

# Glavs Blog said on Saturday, August 20, 2011 11:12 PM

Note: This post was originally going to detail the changes from 2.0 to 2.1, however in between the time

# Updates to the CacheAdapter Package said on Sunday, August 21, 2011 4:20 AM

Pingback from  Updates to the CacheAdapter Package

# Rasika said on Thursday, September 08, 2011 11:53 PM

Hi

I have used Glav.CacheAdapter and it's great, I have tested it with Memoery, Web, and AppFabric but I have a issue when I try with memcached ( I tried running windows version of memcached and looking for errors and everytime when Glav.CacheAdapter tries to save I see this error from memcached console output.

176: Client using the ascii protocol

<176 get new-key

> NOT FOUND new-key

>176 END

176: going from conn_parse_cmd to conn_mwrite

176: going from conn_mwrite to conn_new_cmd

176: going from conn_new_cmd to conn_waiting

176: going from conn_waiting to conn_read

<540 new auto-negotiating client connection

540: going from conn_new_cmd to conn_waiting

540: going from conn_waiting to conn_read

176: going from conn_read to conn_closing

<176 connection closed.

540: going from conn_read to conn_parse_cmd

540: Client using the ascii protocol

<540 set new-key 0 59.9189954 213

>540 CLIENT_ERROR bad command line format

540: going from conn_parse_cmd to conn_write

540: going from conn_write to conn_new_cmd

540: going from conn_new_cmd to conn_parse_cmd

<540 <string z:Type="System.String" z:Assembly="0" xmlns="http://schemas.microso

ft.com/2003/10/Serialization/" xmlns:z="http://schemas.microsoft.com/2003/10/Ser

ialization/">66e78de9-d3a8-41d6-99d4-4060412e50e3</string>

>540 ERROR

540: going from conn_parse_cmd to conn_write

540: going from conn_write to conn_new_cmd

540: going from conn_new_cmd to conn_waiting

540: going from conn_waiting to conn_read

540: going from conn_read to conn_closing

<540 connection closed.

Could you let me know what's the issue with  that?

Thanks

# Glav said on Friday, September 09, 2011 12:09 AM

Hi Rasika,

I'd be happy to try and resolve this issue. Could you please provide me a small bit of code that reproduces this issue? You can either log an issue via Bitbucket and provide an attachment  here:

bitbucket.org/.../issues

or you can email me at glav @ aspalliance.com

# Rasika said on Friday, September 09, 2011 1:06 AM

Please find the attachment

www.mediafire.com

# Glav said on Friday, September 09, 2011 1:35 AM

Hi Rasika,

Thanks for that. I have added the attachment to the issue log in Bitbucket and will look into the issue.

Thanks for reporting it.

# Glav said on Saturday, September 10, 2011 2:08 AM

HI Rasika,

Hopefully you got my email, however the problem you reported was because of incorrect port numbers. In the config file you had a port number of 22333 ( I think) which looks close to the default port number of AppFabric (which is 22233). However, if you simply run memcached with the default installation, it uses a default port 11211. Changing the port number in the config to 11211 should resolve the issue.

# Rasika said on Saturday, September 10, 2011 8:25 PM

Hi Glav

I don't think it is the issue, because i launched the memcachd by giving the port number, just now i executed it again with default port.

Moreover if that is the case, memcached shouldn't get any sort of input (as the port it listening to is differnt)

as I explained i can see the serialized object in memcached console as follows

<544 set cache-key 0 29.8999942 729

>544 CLIENT_ERROR bad command line format

544: going from conn_parse_cmd to conn_write

544: going from conn_write to conn_new_cmd

544: going from conn_new_cmd to conn_parse_cmd

<544 <CacheData z:Id="1" z:Type="CacheDataLib.CacheData" z:Assembly="CacheDataLi

b, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.

datacontract.org/2004/07/CacheDataLib" xmlns:i="www.w3.org/.../XMLSchema

-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"><_x003C

_AddedTime_x003E_k__BackingField>2011-09-11T10:23:07.12191+10:00</_x003C_AddedTi

me_x003E_k__BackingField><_x003C_Description_x003E_k__BackingField z:Id="2">My C

ache Data</_x003C_Description_x003E_k__BackingField><_x003C_Id_x003E_k__BackingF

ield>50b39d02-1dfa-4568-9741-ef5ffe3b9962</_x003C_Id_x003E_k__BackingField><_x00

3C_Key_x003E_k__BackingField z:Id="3">cache-key</_x003C_Key_x003E_k__BackingFiel

d></CacheData>

>544 ERROR

544: going from conn_parse_cmd to conn_write

544: going from conn_write to conn_new_cmd

544: going from conn_new_cmd to conn_waiting

544: going from conn_waiting to conn_read

544: going from conn_read to conn_closing

<544 connection closed.

And this is the out put in CacheAdapter

CacheAdapterMemcachedTest.vshost.exe Error: 0 : 11/09/2011 10:22:45: Unable to Add item to memcached cache: ClientError,

CacheAdapterMemcachedTest.vshost.exe Information: 0 : 11/09/2011 10:22:45: Adding item [cache-key] to cache with expiry date/time of [11/09/2011 10:23:10].

CacheAdapterMemcachedTest.vshost.exe Error: 0 : 11/09/2011 10:23:07: Unable to Add item to memcached cache: ClientError,

CacheAdapterMemcachedTest.vshost.exe Information: 0 : 11/09/2011 10:23:07: Adding item [cache-key] to cache with expiry date/time of [11/09/2011 10:23:37].

Now here we can clearly see the communication happened, but something else not working.

# Glav said on Saturday, September 10, 2011 11:45 PM

HI Rasika,

Sent u an email but basically the code example u sent me worked fine for me after I changed the port number. Check the email for details.

# Roberto said on Thursday, March 29, 2012 1:07 PM

You will support NCache and enterprise library caching application block?

# Glav said on Thursday, March 29, 2012 5:21 PM

Hi Roberto,

I hadn't thought about it but I certainly could add support if people want that. The enterprise library caching application block is not something I would typically target as it is an abstraction in itself and I would rather not provide an abstraction over an abstraction, not to mention the dependencies that would entail. I dont have much experience with NCache but if people want it, I'd be happy to take a look.

If it is something you really want, please log an issue/feature request over at

bitbucket.org/.../issues

# Glavs Blog said on Thursday, March 29, 2012 5:26 PM

Note: If you are unfamiliar with the CacheAdapter library and what it does, you can read all about its

# Roberto said on Wednesday, April 11, 2012 9:48 AM

Hello, I created a MyGeneration template for the firebird database, which generates a data access layer, using the glav.cacheadapter as a caching layer.

the name of the template is: Firebird DAAB - 3.0.0.0 With Cache Adapter

that may access the site: www.mygenerationsoftware.com/.../default.aspx, the link Template Library

# Bud Nesbit said on Thursday, April 18, 2013 3:32 PM

I love the idea of having CacheAdapter as a way of switching between HttpRuntime.Cache and AppFabric, but when I configure my web.config for AppFabric, I get an error (following).  It works great for "Memory", but fails for "AppFabric".  I used Nuget.

I've Googled but nobody else seems to be having this problem....

The type initializer for 'Glav.CacheAdapter.Core.DependencyInjection.AppServices' threw an exception. ---> Microsoft.ApplicationServer.Caching.DataCacheException: ErrorCode:SubStatus:Authorization token passed by user Invalid. ---> System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.

# Glav said on Thursday, April 18, 2013 7:28 PM

Hi Bud,

It simply looks like you have an incorrect character as the auth key for the AppFabric cache. If you can send your config through I can have a quick look. You can contact me via @glav on twitter or glav AT aspalliance.com

# manight said on Friday, May 24, 2013 4:42 PM

Hallo Glav,

is this project still alive and kicking? :)

I was looking right in those days to some abstraction over the base asp.net caching just in case I will switch to a multi server cloud environment (who knows what happens when you launch your SaaS software LOL).

Another question (I haven't read your docs yet so forgive me), does AppFabric (guess yes) and MEMCached (guess not............) have all the invalidation features of the asp.net cache or the .net cache like cache priority (that can even be set to NEVER EXPIRE), fixed times and sliding times?

Thanks in advance

# Glav said on Friday, May 24, 2013 9:42 PM

Hi Manight,

Yes its still alive even though I only sporadically do changes to it. I still have an outstanding PULL request to integrate.

However, I do plan further development around for things like cache dependencies and Redis support.

To answer your questions though, this abstraction layer currently only offers the lowest level of API that is common across all platforms so specific cache invalidation features are not in place and this one of the things I will be working on. That is, to provide cache invalidation across all platforms using the same API. So while directly asp.net cache and AppFabric do support advanced invalidation features, memcached does not, so the API only supports basic item invalidation at this point.

Also, memcached does not support sliding cache expiry so defaults to explicit date time expiry.

Feel free to lodge issues/tickets on the Bitbucket site where the code is hosted.

# manight said on Saturday, May 25, 2013 9:34 AM

Thank you Glav for your responsiveness... I  will definitely implement your soultion right now.

Yes adding advance invalidation features (where supported would be great). For example now I can program it against .NET cache with all his sliding and priority invalidation features, so to squeeze at best my cache for more relevant objects, then if the thing grows and I have to switch to a distributed cache, those features won't be that important anymore because of added horsepower and could be simply ignored.

A nice programming interface could be to request the expiration duration (or fixed time) as mandatory since this is common across al platforms and allow you to switch seamlessly from one to another, and then add at least the .NET/AppFabric (since this is a .NET cache layer project I think this makes sense) advanced invalidations that gets used only when the provider targets those systems.

So for example you always add with key/object/time and in case of .NET/ASP.NET/AppFabric provider if you add Sliding Time OR Cache Priority, the TIME simply gest ignored, while if you use the other providers, the Sliding Time OR Cache Priority. This will let you program one time and get the best of all worlds.

About the "provider" I mentioned, it could be nice to exploit the provider model also to have automatic injection and switch from one platform to another or maybe even use together.

Leave a Comment

(required) 
(required) 
(optional)
(required) 

This Blog

Syndication