Caching large chunks of data

Topics: Building and extending application blocks, Caching Application Block , General discussion
Sep 4, 2007 at 1:07 PM
We have an application that would benefit tremendously from the ability to cache large chunks of data that are otherwise "expensive" to fetch from the source. We've started using the Enterprise Library Caching Application block with a SQL2005 store (shared host won't let us use disk), which appears to work nicely. However, we're getting a sporatic 'System.OutOfMemoryException', which appears to be related to the fetching of an existing Cache item. Here's a snippet of the stack trace when an exception is thrown:

OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.]
System.IO.BinaryReader.ReadBytes(Int32 count) +35
System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadArrayAsBytes(ParseRecord pr) +46
System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadArray(BinaryHeaderEnum binaryHeaderEnum) +975
System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run() +196
System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +266
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +185
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream) +12

We really need to store a few associated properties (content-type, etc.) with the byte[] chunk, and we've created a serializable class to do most of the lifting. However, I fear that the de-serialization of the cached-data to the class (in-memory) is causing the problem.

Any thoughts on a more efficient way to handle this? As I said earlier, simply ensuring that we don't need to go back to the source every time for the data is a tremendous efficiency, but how best to use cache to persist the data & related properties?
Sep 4, 2007 at 1:59 PM
Hi,

Can you post the full stack to know what is triggering the deserialization?
In any case, is it a requirement to persist the information in the database, or would the in memory storage provide enough benefit?

Regards,
Fernando
Sep 4, 2007 at 2:20 PM
Thanks Ferando -- here's the full trace:

OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
System.IO.BinaryReader.ReadBytes(Int32 count) +35
System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadArrayAsBytes(ParseRecord pr) +46
System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadArray(BinaryHeaderEnum binaryHeaderEnum) +975
System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run() +196
System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +266
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +185
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream) +12
Microsoft.Practices.EnterpriseLibrary.Caching.SerializationUtility.ToObject(Byte[] serializedObject) +140
Microsoft.Practices.EnterpriseLibrary.Caching.Database.DataBackingStore.DeserializeValue(DataRow dataToLoad) +152
Microsoft.Practices.EnterpriseLibrary.Caching.Database.DataBackingStore.CreateCacheItem(DataRow dataToLoad) +132
Microsoft.Practices.EnterpriseLibrary.Caching.Database.DataBackingStore.LoadDataFromStore() +298
Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.BaseBackingStore.Load() +28
Microsoft.Practices.EnterpriseLibrary.Caching.Cache..ctor(IBackingStore backingStore, CacheCapacityScavengingPolicy scavengingPolicy, CachingInstrumentationProvider instrumentationProvider) +83
Microsoft.Practices.EnterpriseLibrary.Caching.CacheManagerFactoryHelper.BuildCacheManager(String cacheManagerName, IBackingStore backingStore, Int32 maximumElementsInCacheBeforeScavenging, Int32 numberToRemoveWhenScavenging, Int32 expirationPollFrequencyInSeconds, CachingInstrumentationProvider instrumentationProvider) +113
Microsoft.Practices.EnterpriseLibrary.Caching.CacheManagerCustomFactory.CreateObject(IBuilderContext context, String name, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache) +252
Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder.ConfiguredObjectStrategy.BuildUp(IBuilderContext context, Type t, Object existing, String id) +162
Microsoft.Practices.ObjectBuilder.SingletonStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild) +171
Microsoft.Practices.ObjectBuilder.BuilderStrategy.BuildUp(IBuilderContext context, Type typeToBuild, Object existing, String idToBuild) +38
Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder.ConfigurationNameMappingStrategy.BuildUp(IBuilderContext context, Type t, Object existing, String id) +186
Microsoft.Practices.ObjectBuilder.BuilderBase`1.DoBuildUp(IReadWriteLocator locator, Type typeToBuild, String idToBuild, Object existing, PolicyList[] transientPolicies) +310
Microsoft.Practices.ObjectBuilder.BuilderBase`1.BuildUp(IReadWriteLocator locator, Type typeToBuild, String idToBuild, Object existing, PolicyList[] transientPolicies) +71
Microsoft.Practices.ObjectBuilder.BuilderBase`1.BuildUp(IReadWriteLocator locator, String idToBuild, Object existing, PolicyList[] transientPolicies) +75
Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder.EnterpriseLibraryFactory.BuildUp(IReadWriteLocator locator, IConfigurationSource configurationSource) +255
Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder.LocatorNameTypeFactoryBase`1.CreateDefault() +91
Microsoft.Practices.EnterpriseLibrary.Caching.CacheFactory.GetCacheManager() +87
com.myservice.service.media.doGetSongItemImpl.ProcessRequest(Object req) in C:\inetpub\wwwroot\myservice\BH.Net\BH.Service\com\myservice\service\media\doGetSongItemImpl.cs:222
com.myservice.net.MediaService.ProcessRequest(HttpContext context) in C:\inetpub\wwwroot\myservice\BH.Net\BH.Net\com\myservice\net\MediaService.cs:230
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +405
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +65

Looks like it's failing at the following line:

ms = (MediaStreamType)cacheManager.GetData(mediaKey);

MediaStreamType contains a byte[] -- this is the actual contents of the media "chunk".

In-memory storage would be very expensive -- we could have hundreds of items ranging from 3-10Mb each. We can always re-fectch these from the source, but it becomes a major QoS issue -- maintaining them in Sql Cache is an ideal solution, but only if it works.

Sep 4, 2007 at 3:14 PM
Hi,

Here are a couple of comments on your situation.

First, the stack trace shows an out of memory error while loading the cache during initialization, which should occur before data is retrieved from it (see Microsoft.Practices.EnterpriseLibrary.Caching.Database.DataBackingStore.LoadDataFromStore()).

Second, a few clarifications on the way the caching block works. The cache will always have an in-memory representation of your data, and the backing store will provide persistence for the cached data across application restarts. Actually the stack you posted shows the out of memory error happening while trying to load all the cached information from the database into memory. It is not the case that the individual data items are retrieved from the backing store when requested. Quoting from "Selecting a backing store ms-help://ms.EntLib.2007May/EnterpriseLibrary/html/02-060-Selecting_a_Backing_Store.htm": "Each cache manager can be configured to store data only in memory, which means that it uses the null backing store, or configured to store data both in memory and in persistent storage. The type of persistent storage is specified when you configure the backing store. Backing stores let cached data survive if the application must be restarted.".

Regards,
Fernando
Sep 4, 2007 at 3:37 PM
Hi Fernando:

Okay, that answers a bunch of questions -- I misunderstood the role of the DataBackingStore as a role of persistence rather than source. I really need a mechanism to "cache" large objects, but without the overhead in-memory retention of everything.

Perhaps a Hashtable of cache-keys with stand-alone table of key/properties/image, using a SqlNotification handler to sychronize the external cache with the in-memory representation of available keys. I suppose I could use EntLib to manage the cache-keys, but I'd need a way to kick-off an expiration event on the external table when items in the EntLib cache actually expire.

Thanks.
Sep 4, 2007 at 3:53 PM
Hi,

You can probably use your custom implementation of ICacheItemRefreshAction for this.

Regards,
Fernando
Nov 23, 2009 at 8:31 AM
Edited Nov 23, 2009 at 9:29 AM

HI,

Is it possible to use caching application block only with isolated storage  (I don't want to cache items in memory) Can we do this?