Server-side Caching with WCF RIA Services for Silverlight Clients

Nov 2, 2013 at 1:55 PM
Edited Nov 2, 2013 at 2:00 PM
Hello,

I am using WCF RIA Services to download (via Invoke) compressed, encoded byte[] data that is stored on the server file system. I need to achieve the fastest response time possible for recurring downloads of the same items by the client. I have the option of using very large sever memories/many cores.

Is there a way to use the Enterprise Library caching functionality that does not require a relational database backing store (e.g., an in-memory ConcurrentDictionary, etc...)? If so, does the alternate backing store have to be running in a long running Windows process that is accessed serially by the caching system? Or, is there a way to access the cached data via a parallel bus to eliminate the serialization overhead (i.e., some sort of shared memory)?

What I would like is to create an in-memory backing store once, and then access/reuse it across all clients when they call a service. I am using WCF RIA in the per instance mode. Setting the life span of the objects to a couple of hours would work nicely in my particular case, which I see is supported by this caching library.

Thanks,

Warren
Nov 2, 2013 at 4:08 PM
After using NuGet to download/install the Enterprise Library, I managed to get a test case running, but I have some questions.
  1. I benchmarked the results with and without the cache, and the cached results were SLOWER than the non-cached results by as much as 2.5x. Not sure why this is the case, but I have stepped through the code and the cached data is being found and used instead of reading the files from the HD. I should note that SSD are being used for all development/server machines.
  2. Where is the data actually stored, since I did not have to create my own database to hold the cached information? I had read a number of articles that referenced an associated database, so I assumed that one MUST be created and used. Can someone explain what is going on here. Does the instantiation of the cache create its own database somewhere? If so, what and where. Sorry for my ignorance. I do not have a lot of experience in this domain.
  3. What are the following messages and how do I resolve.
Message 1 Could not find schema information for the element 'cachingConfiguration'.
Message 2 Could not find schema information for the attribute 'defaultCacheManager'.
Message 3 Could not find schema information for the element 'cacheManagers'.
Message 4 Could not find schema information for the element 'add'.
Message 5 Could not find schema information for the attribute 'name'.
Message 6 Could not find schema information for the attribute 'type'.
Message 7 Could not find schema information for the attribute 'expirationPollFrequencyInSeconds'.
Message 8 Could not find schema information for the attribute 'maximumElementsInCacheBeforeScavenging'.
Message 9 Could not find schema information for the attribute 'numberToRemoveWhenScavenging'.
Message 10 Could not find schema information for the attribute 'backingStoreName'.
Message 11 Could not find schema information for the element 'backingStores'.
Message 12 Could not find schema information for the element 'add'.
Message 13 Could not find schema information for the attribute 'type'.
Message 14 Could not find schema information for the attribute 'name'.

I instantiated the cache as following in the WCF RIA service:
    ICacheManager cacheManager = null;

    public ItemDownloadService()
    {
        cacheManager = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>("CacheManager");
    }
The web.config file contains:
<configSections>
    <section name="cachingConfiguration"
             type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, 
                             Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
             requirePermission="true" />
</configSections>
<cachingConfiguration defaultCacheManager="CacheManager">
    <cacheManagers>
        <add name="CacheManager"
             type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.505.0,     
                             Culture=neutral, PublicKeyToken=31bf3856ad364e35"
             expirationPollFrequencyInSeconds="60"
             maximumElementsInCacheBeforeScavenging="50000"
             numberToRemoveWhenScavenging="1000"
             backingStoreName="NullBackingStore" />
    </cacheManagers>
    <backingStores>
        <add type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
             name="NullBackingStore" />
    </backingStores>
</cachingConfiguration>
In the services where this caching functionality has been implemented, I am using something similar to the following:
if (cacheManager.Contains(stringKey))
{
    return (byte[])cacheManager.GetData(stringKey);
}
else
{
    byte[] tempByteArray = BinaryWriterReader.ReadBinaryFile(stringKey);
    if (tempByteArray != null)
        cacheManager.Add(stringKey, tempByteArray, CacheItemPriority.Normal, null, new AbsoluteTime(TimeSpan.FromMinutes(120)));
    return tempByteArray;
}
Editor
Nov 2, 2013 at 5:15 PM
Edited Nov 4, 2013 at 1:15 AM
You have a few questions here (which I will address but maybe not all in this post) but let me just start with #3, the configuration. Note that the messages are informational messages only so they shouldn't cause any harm.

If you install Enterprise Library (full install, not from NuGet) then a schema file (EnterpriseLibrary.Configuration.xsd) will be installed in the Visual Studio schemas folder (e.g. C:\Program Files (x86)\Microsoft Visual Studio 10.0\Xml\Schemas\EnterpriseLibrary.Configuration.xsd).

If you have the schema file then you can open the configuration file and then open the Schemas List (Menu: XML->Schemas) and click to Use EnterpriseLibrary.Configuration.xsd. That will make the informational messages disappear and add intellisense support.

Unfortunately, you will now see warning messages. :(

This is because the schema definition has type constraints but those type constraints do not include full type information. That makes sense because you probably don't need a new schema file if you just fix some bugs and bump the version number (that would cause headaches). However, when the configuration tool generates the configuration file full type information is included which causes the warnings. The workaround is to remove the full type information:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="cachingConfiguration"
             type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, 
                             Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
             requirePermission="true" />
  </configSections>
  <cachingConfiguration defaultCacheManager="CacheManager">
    <cacheManagers>
      <add name="CacheManager"
           type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching"
           expirationPollFrequencyInSeconds="60"
           maximumElementsInCacheBeforeScavenging="50000"
           numberToRemoveWhenScavenging="1000"
           backingStoreName="NullBackingStore" />
    </cacheManagers>
    <backingStores>
      <add type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching"
           name="NullBackingStore" />
    </backingStores>
  </cachingConfiguration>
</configuration>
This is a bit of a pain because if you are working with the configuration tool it will add back all of the full type information and cause more warnings. However, with intellisense enabled it makes editing the file quite a bit easier. When I've been on projects, I've usually used the configuration tool to generate the majority of the configuration and then I manually tweaked the settings from then on. Of course, YMMV.

In terms of #2, since you are using the NullBackingStore the cache is only stored in memory and not persisted to a backing store.

Some questions for you:
  • Are you in a web farm?
  • What was the methodology for running the performance test?
~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Nov 2, 2013 at 6:03 PM
Thanks for you assistance. Your feedback/comments make sense for #3.

I am not using a web farm.

Since WCF RIA service has some know startup/latency issues, the client machine sends a given number of item download requests (10 total) and then the process is repeated after a short delay. Only the times for the second/subsequent downloads are measured at regular burst intervals. This is being done on a single client to eliminate any network effect. The tests are currently using the browser to send/receive the data, which is not the best test scenario.

Thanks for explaining about the NullBackingStore. That makes sense. So that should mean that the data in memory is being read over the 64 bit bus, which means that our bus data transfer rate would be about 500,000,000 64 bit transfers/second so it should not be an issue.

I did do some further benchmarks on single file downloads of about 128Kbytes, and the performance was around 3-4 milliseconds/download, which was actually about 50% faster, which is good news. The real issue may still be related to WCF startup.

I should have pointed out that the burst downloads average about 4Kbytes per item (~40K total).

All data is in the form of compressed, encoded data. We have two classes of data sizes. Those that are 3-5Kbytes, and those that are 40-180Kbytes, with the vast majority falling in the lower range. It may be that I can lump the smaller item packets into a single download packet, which would mean only one WCF request per transmission, but then the server must combine the 10 byte streams into one, which will slow the server down. I guess a benchmark of the two approaches is in order.

Thanks again,

Warren
Nov 2, 2013 at 11:51 PM
Hello,

I restructured some of the testing and would like to retract my initial comments about the performance issue. What I think was happening was that some latency/WCF effects were biasing the initial test results. I also had a chance to test the scenario on a server with conventional hard drives ( relatively slow) and the improvement was obviously dramatic.

Regards,

Warren
Editor
Nov 4, 2013 at 1:38 AM
Thanks for posting back with the results. Good to hear it's working as expected.

In terms of the posted approach, I wouldn't use the Contains method. Just get the cached value and if it's null then add the item to the cache. This should work fine as long as you don't expect to cache null values (which from the scenario seems like you wouldn't). For example:
object fileData =cacheManager.GetData(stringKey);

// Not in cache or expired 
if (fileData == null)
{
    fileData = BinaryWriterReader.ReadBinaryFile(stringKey);

    if (fileData != null)
    {
        cacheManager.Add(stringKey, fileData, CacheItemPriority.Normal, null, new AbsoluteTime(TimeSpan.FromMinutes(120)));
    }
}

return (byte[]) fileData;
~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Nov 4, 2013 at 2:44 PM

Hello Randy,

I downloaded the latest Enterprise Library 6.0 and installed it. However, there are no files in the directory mentioned below.

Thanks,

Warren

Editor
Nov 5, 2013 at 12:53 PM
I'm assuming you are talking about the EnterpriseLibrary.Configuration.xsd file? Enterprise Library 6 is a download now and not a Windows install. You would manually need to copy the EnterpriseLibrary.Configuration.xsd file to the schema cache folder or choose to "Add".

Also, if you are installing from NuGet then the schema file should download in the packages\EnterpriseLibrary.Common.6.0.1304.0 directory. It can then be added to Visual Studio schema list.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Nov 5, 2013 at 1:06 PM

Thanks for the clarification, I’ll give it a try.

Warren