Why is my Ribbon Button Disabled? (or CRM2011 caching under the covers)

 

Background

Today I had a most frustrating problem where my Ribbon button was visible but disabled no matter what I did to the Command.

This is usually caused by the Command missing or being invalid - but on further inspection of the RibbonLayout.js.aspx being loaded, the Command simply wasn't included in the initialisation objects.

IISRESET just doesn't cut it!

After a performing an IISRESET, the button was then enabled and the command was then included in the RibbonLayout.js.

IISRESET clears the server cache so I guessed the problem was down to caching - but I didn't want to be doing this every time I published. So I investigated further. I soon discovered that the issue was that the reference to to the RibbonLayout.js included two query string parameter rver and mver along the lines of :

/_controls/ribbon/RibbonLayout.js.aspx?
hierarchy=9886ead0-4fcc-4747-9a18-08e7a9a6de71
&id=EntityTemplateTab.lead.NoRelationship.Form.Mscrm.Form.lead.MainTab
&ise=1
&lcid=1033
&mver=830776802
&oc=0
&rver=-1307274159
&ver=-986909320

When I published my customisations, the mver (MetaData version) was incrementing, but the rver (Ribbon Version) was not. 

So for some reason the publish was not incrementing the ribbon version, and so my new command was not being loaded into the browser. The CRM 2011 cache is stored in the standard ASP.NET HTTP Cache - and it was this cache that holds the Ribbon Version. The publish is meant to invalidate this cache so that the new version can be used. Without this cache, every single request would have to hit the database to load customisations xml - so it is a very important part of the framework - but if it does not refresh automatically when changes are made - then the user interface is effectively stuck at a previous version.

Note: I also discovered that the cache was on a sliding 60 minutes refresh - so If I advanced the time by 1 hour, I would also see my new ribbon customisations and the rver number incremented. The rver is not actually an incremental number, but a hash of all of the 'last published' dates of the ribbon customisations. The main thing is that it is a unique key that points to the correct cache item.

So...Why was the cache not being invalidated?

Upon investigating how the CRM2011 cache is managed - there is a Notifications table in the MSCRM_CONFIG database that includes entries that instruct CRM to clear cache items. The Async Server picks these up and notifies the HTTP Worker processes to drop each cache by Key.

You can see what's going on for your server when you publish by using the following SQL:

CREATE TABLE  #eventNames (eventId int, eventName nvarchar(2000))
INSERT INTO #eventNames (eventId, eventName) VALUES (0,'netSecurityUser')
INSERT INTO #eventNames (eventId, eventName) VALUES (1,'netSecurityBusiness')
INSERT INTO #eventNames (eventId, eventName) VALUES (2,'netSecurityOrganization')
INSERT INTO #eventNames (eventId, eventName) VALUES (3,'netMetadata')
INSERT INTO #eventNames (eventId, eventName) VALUES (4,'netMetadataEntity')
INSERT INTO #eventNames (eventId, eventName) VALUES (5,'netSettingsOrganization')
INSERT INTO #eventNames (eventId, eventName) VALUES (6,'netSettingsUser')
INSERT INTO #eventNames (eventId, eventName) VALUES (7,'netSchedulingEngineInvalidateContainerCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (8,'netSchedulingEngineInvalidateResourceExpansionCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (9,'netSchedulingEngineInvalidateScheduleCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (12,'netSqmSessionReset')
INSERT INTO #eventNames (eventId, eventName) VALUES (13,'netSiteMap')
INSERT INTO #eventNames (eventId, eventName) VALUES (14,'netHardExpiry')
INSERT INTO #eventNames (eventId, eventName) VALUES (15,'netInfo')
INSERT INTO #eventNames (eventId, eventName) VALUES (16,'netMessageProcessorInvalidatePipelineCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (17,'netSavedQuery')
INSERT INTO #eventNames (eventId, eventName) VALUES (18,'netPluginTypeInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (19,'netPluginAssemblyInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (20,'netStepDescriptionInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (21,'netStepImageDescriptionInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (22,'netConfigCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (23,'netConfigUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (24,'netConfigDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (25,'netConfigAll')
INSERT INTO #eventNames (eventId, eventName) VALUES (26,'netScaleGroupSetState')
INSERT INTO #eventNames (eventId, eventName) VALUES (27,'netServerSetState')
INSERT INTO #eventNames (eventId, eventName) VALUES (28,'netEncryptionConfigurationChange')
INSERT INTO #eventNames (eventId, eventName) VALUES (29,'netOrganizationCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (30,'netOrganizationSetState')
INSERT INTO #eventNames (eventId, eventName) VALUES (31,'netOrganizationUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (32,'netOrganizationDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (33,'netSubscriptionEntity')
INSERT INTO #eventNames (eventId, eventName) VALUES (34,'netOutlookFilter')
INSERT INTO #eventNames (eventId, eventName) VALUES (35,'netCacheItemRemove')
INSERT INTO #eventNames (eventId, eventName) VALUES (36,'netStepSecureConfigInvalidateCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (37,'netTransactionCurrency')
INSERT INTO #eventNames (eventId, eventName) VALUES (38,'netLocator')
INSERT INTO #eventNames (eventId, eventName) VALUES (39,'netSchedulingEngineMustRunPublishResourceGroupExpansion')
INSERT INTO #eventNames (eventId, eventName) VALUES (40,'netOrganizationMove')
INSERT INTO #eventNames (eventId, eventName) VALUES (41,'netUserInvitationUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (42,'netSolution')
INSERT INTO #eventNames (eventId, eventName) VALUES (43,'netServerCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (44,'netServerMove')
INSERT INTO #eventNames (eventId, eventName) VALUES (45,'netServerDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (46,'netSecurityTeam')
INSERT INTO #eventNames (eventId, eventName) VALUES (47,'netRibbonCustomizationUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (48,'netDependencyNode')
INSERT INTO #eventNames (eventId, eventName) VALUES (50,'netDnsUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (51,'netSystemForm')
INSERT INTO #eventNames (eventId, eventName) VALUES (52,'netSrsExtensionsInstalled')
INSERT INTO #eventNames (eventId, eventName) VALUES (53,'netFederationProviderCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (54,'netFederationProviderUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (55,'netFederationProviderDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (56,'netCertificateCreate')
INSERT INTO #eventNames (eventId, eventName) VALUES (57,'netCertificateUpdate')
INSERT INTO #eventNames (eventId, eventName) VALUES (58,'netCertificateDelete')
INSERT INTO #eventNames (eventId, eventName) VALUES (59,'netWebResource')
INSERT INTO #eventNames (eventId, eventName) VALUES (60,'netSolutionImportFailure')
INSERT INTO #eventNames (eventId, eventName) VALUES (62,'netFlushRecordCountSnapshotCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (63,'netFlushPrincipalObjectAccessReadSnapshotCache')
INSERT INTO #eventNames (eventId, eventName) VALUES (64,'netMaxEventCount')


SELECT n.CreatedOn, l.eventName, n.EventData ,n.EventId, n.OrganizationId
FROM MSCRM_CONFIG.dbo.Notification n LEFT OUTER JOIN #eventNames l ON n.EventId = l.eventId
ORDER BY CreatedOn desc

The reason that my notifications were not getting through was that I had done some testing previously where the server time had been advanced a month. This meant that there were notifications sitting in this table queue that were for a month time. The Async server seems to use the latest date when it first starts to define a time window to query for new notifications. Obviously, in my case, the clear cache notifications (or netRibbonCustomizationUpdate events) were not being processed. I performed an unsupported DELETE from the MSCRM_CONFIG.dbo.Notifications table and all was well again. (phew!)

I hope this has provided you with a bit of an insight into how CRM2011 caching works and how you can work out what's going on if you find things not being reflected when you make changes to customisations.


 

Pingbacks and trackbacks (1)+

Comments are closed