appfabricdeserializationdata-caching

Appfabric DataCache Deserialization Issue on Get


We are having a strange problem with the Appfabric DataCache. Sometimes, 1 in 1000 perhaps, we get exceptions from the cache Get method cache.Get(key); (public object Get(string key)). The exception is

Object of type 'System.Collections.Generic.List`1[MyNamespace.PersonName]' cannot be converted to type 'MyNamespace.StatusType'.

Why is it trying to do this conversion?? The StatusType Type has nothing to do with the PersonName Type, and its not in its object graph in any way.

This is internal to the DataCache, and as mentioned above, it will run for days with no issues, then sometimes it just starts throwing exceptions like this around. The objects(and object graphs) are all simple enough. And it does work 99% of the time.

Stacktrace:

at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.RtFieldInfo.InternalSetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture, Boolean doVisibilityCheck, Boolean doCheckConsistency)
   at System.Runtime.Serialization.FormatterServices.SerializationSetValue(MemberInfo fi, Object target, Object value)
   at System.Runtime.Serialization.FormatterServices.PopulateObjectMembers(Object obj, MemberInfo[] members, Object[] data)
   at Castle.DynamicProxy.Serialization.ProxyObjectReference.DeserializeProxyState()
   at Castle.DynamicProxy.Serialization.ProxyObjectReference..ctor(SerializationInfo info, StreamingContext context)
   at ReadProxyObjectReferenceFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
   at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
   at ReadPersonFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
   at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, Int32 declaredTypeID, Type declaredType, String name, String ns)
   at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, String name, String ns)
   at System.Runtime.Serialization.NetDataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)
   at System.Runtime.Serialization.XmlObjectSerializer.InternalReadObject(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader reader)
   at Microsoft.ApplicationServer.Caching.Utility.Deserialize(Byte[][] buffers, Boolean checkTypeToLoad)
   at Microsoft.ApplicationServer.Caching.RoutingClient.SendMsgAndWait(RequestBody reqMsg)
   at Microsoft.ApplicationServer.Caching.DataCache.SendReceive(RequestBody reqMsg)
   at Microsoft.ApplicationServer.Caching.DataCache.InternalGet(String key, DataCacheItemVersion& version, String region)
   at Microsoft.ApplicationServer.Caching.DataCache.Get(String key)
   at MyNamespace.CacheManagement.AppFabricCacheProvider.Get(String key)

Solution

  • Tracked this down eventually.

    This issue can occur, if you change the structure of your type, when a cached version of the same type is in the cache, and then try to retrieve a cached instance of the old type, on to your new type.

    In an environment with multiple developers working on the same types, sharing the same global cache, you're bound to run into this.

    Never occurs in production, where the type interfaces would be static.

    Example

    public class Foo // version 1.0
    {
     public string Woof {get;set;}
     public FooBar Meow {get;set;}
    }
    // compile, run,
    // add an instance of Foo to AppFabric Cache, cacheKey = X
    

    // 5 mins later
    public class Foo // version 1.1
    {
     public int Id {get;set;} // change interface of type
     public string Woof {get;set;}
     public FooBar Meow {get;set;}
    }
    
    // compile, run, get an instance of Foo from the cache, cacheKey = X
    // exception, with strange information.