Thursday 28 January 2010

Extensible Output Caching with Velocity

This morning Scott Gu posted an article on his blog about the extensibility points now built into output caching in ASP.NET 4.0. Steve Robbins said to me that it looked easy to wire up Velocity, and I was pleased to see that when ASP.NET 4.0 ships there will be a set of samples showing how to use Velocity as an output cache provider, however since Scott said that writing an output cache provider is just a case of inheriting from System.Web.Caching.OutputCacheProvider and overriding four methods, I decided to have a look at it at lunchtime.

And it does seem remarkably simple - here's my code:
Imports Microsoft.Data.Caching
Public Class VelocityOutputCacheProvider
    Inherits System.Web.Caching.OutputCacheProvider
Private Const cacheName As String = "VelocityOutputCache"
Private mCache As DataCache
Public Sub New()
    Dim factory As DataCacheFactory

    Try
        factory = New DataCacheFactory

        mCache = factory.GetCache(cacheName)
    Catch ex As Exception
        Throw
    Finally
        factory = Nothing
    End Try
End Sub

Public Overrides Function Add(ByVal key As String, ByVal entry As Object, ByVal utcExpiry As Date) As Object
    Try
        mCache.Add(key, entry, (utcExpiry - DateTime.UtcNow))
    Catch ex As Exception
        Throw
    Finally
    End Try
End Function

Public Overrides Sub Remove(ByVal key As String)
    Try
        Call mCache.Remove(key)
    Catch ex As Exception
        Throw
    Finally
    End Try
End Sub
Public Overrides Function [Get](ByVal key As String) As Object
Dim cachedObject As Object
    Try
        cachedObject = mCache.Get(key)
        Return cachedObject
    Catch ex As Exception
       Throw
    Finally
    End Try
End Function
Public Overrides Sub [Set](ByVal key As String, ByVal entry As Object, ByVal utcExpiry As Date)
    Try
        Call mCache.Put(key, entry, (utcExpiry - DateTime.UtcNow))
    Catch ex As Exception
        Throw
    Finally
    End Try
    End Sub
End Class

The only thing I'm a little fuzzy on is what object the Add method should return. It's slightly awkward that the Velocity Cache.Add method takes a TimeSpan parameter where the OutputCacheProvider passes in a point in time, so I've calculated a TimeSpan by subtracting the current time from the passed-in Date.
 
Something that's slightly unclear is how this would behave in a server farm. It depends whether each server maintains it's own set of cached pages, or the cached pages are common and available to all servers. If the latter then this would be a truly awesome boost to scalability since for any given web page only the first server that picked up a request for a given URL would need to compile it - other servers in the farm would then be able to pick up the cached copy and return it to the client.

Update: When the Add method is called, you don't need to return anything, just put the object into the cache and ASP.NET takes care of the rest. You get a warning that the function doesn't return anything but this can be ignored.

Wednesday 6 January 2010

Installing AppFabric Caching Beta 1, Part 4

I'm pleased to report that I've (at last) had a measure of success with the AppFabric installer :-)

I mentioned in my last entry that I'd posted on the MSDN AppFabric Caching forum about the problems I'd been having with the installer. I also made two of my installation logs available. I'm indebted to Rahul Kaura from Microsoft, who pointed out that these were AppFabric general installation logs and steered me towards the cache-specific installation logs. These logs are found in the same place as the AppFabric logs i.e. your %TEMP% folder, are called DistributedCacheAppServerConfig(DateTime).log and I've found them invaluable in diagnosing and fixing installation problems.

So what were the problems I was having? From reading the log they seem to be largely around permissions to the Registry:
2010-01-06 11:52:52, Info DCACHE Adding access for NT AUTHORITY\NETWORK SERVICE on registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft Distributed Cache\Version



2010-01-06 11:53:16, Error DCACHE System.InvalidOperationException: This access control list is not in canonical form and therefore cannot be modified.
at System.Security.AccessControl.CommonAcl.ThrowIfNotCanonical()
at System.Security.AccessControl.CommonAcl.AddQualifiedAce(SecurityIdentifier sid, AceQualifier qualifier, Int32 accessMask, AceFlags flags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType)
at System.Security.AccessControl.DiscretionaryAcl.AddAccess(AccessControlType accessType, SecurityIdentifier sid, Int32 accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags)
at System.Security.AccessControl.CommonObjectSecurity.ModifyAccess(AccessControlModification modification, AccessRule rule, Boolean& modified)
at System.Security.AccessControl.CommonObjectSecurity.AddAccessRule(AccessRule rule)
at Microsoft.Data.Caching.InstallConfig.Program.SetResetLocalPermissions(Boolean grant)
at Microsoft.Data.Caching.InstallConfig.Program.PostConfigServiceInstallSteps()
at Microsoft.Data.Caching.InstallConfig.Program.Install()
at Microsoft.Data.Caching.InstallConfig.Program.Main(String[] args)


2010-01-06 11:53:16, Error DCACHE Unexpected Error: This access control list is not in canonical form and therefore cannot be modified.


2010-01-06 11:53:16, Info DCACHE Configuration failed, Rolling back...

which is sort of annoying as I've been running the installer under an account that is a Domain Administrator, and the Domain Admins group is part of the Administrators group on each of my cache servers. The solution was to add the account I'm using to install AppFabric to the ACL for HKEY_LOCAL_MACHINE with full permission.

The installer still reports that it fails to configure AppFabric, but this is because the log includes a Warning that the Windows Firewall is disabled, which I'm reasonably sure I can ignore.

So, I now have an AppFabric caching cluster with three servers. Onward to actually using it!