I'm using the standard Microsoft provided code to parse through audit records to determine if I can purge anything that we don't need to keep. My audit file has billions of records and CRM seems to be having some trouble with the size of the table. However, we are required by law to keep about 10-years worth of audits. So, I am working on some logic to purge the unneeded audit records to reduce the Audit table size as much as I can. I think I can find a few logical rules to apply such as, if the old and new values are both the same, then we'll purge those. I'm trying to come up with a few dozen rules like this to figure out what I can purge. I'm hoping to find addition logic to identify more to purge. I'm open to suggestions as to what to purge or how to maybe remove some of those from being audited in the first place. For the most part, we turn on field level auditing for fields that we are required by law to audit.
The audits with the same old/new values come from code in CRM plugins, integration services, etc. that pull in a field and then do not change the value, but the field is still there and it gets saved. CRM treats these as updates since it was saved even if the value does not change. I guess it makes sense, since the field was in fact saved. I guess it is just a how you take the save/update meaning. Apparently CRM treats anything that is saved, even NULL and empty values that do not change, as updates. It does make it easy when testing update plugins, just run a SQL update to set a field to it's current value and the update plugins fires. But it's annoying because we end up a billion audit records... literally.
I originally looked at cleaning up all of that code so that we only save what has changed and removing the field from the entity before we update, but that ended up being problematic and we can't entirely remove it in all cases and even when we do, we are still finding that CRM is still creating audits when you wouldn't think it would. So, I will continue working down that path over time, but with 1 billion audit records, I want to try to clean it up as best I can. I will also look at archiving some fo the older data as well. But for this first pass I'm just cleaning up what I can. For other tables/entities like say the PluginTraceLogs, I am just clearing those out, anything over say 30-days old. But with 1 billion valid audit table records, I want to focus on cleaning up those.
To do this, I am using some standard sample code from Microsoft as follows. But no matter what I do, I can't get it to return an AttributeAuditDetail and it only ever returns AuditDetail. Even when I use the RetrieveAtrributeChangeHistoryRequest() vs the RetrieveAuditChangeHistoryRequest(). It always just returns AuditDetail objects and nothing else. However, every example I see online does in fact return the attribute details as you would expect. I can use those examples for other entities and get the attribute details. But for my entity that I am searching for, which is custom, it only returns AuditDetails.
Does anyone have any idea what would cause this? Is there a config setting on the entity that needs to be set? Auditing on this entity works fine and we audit dozens of fields. So the attribute details should be there. Could it be permissions?
Why would a custom entity not have AttributeAuditDetails but some of the build-in ones have those? Any idea what controls this?
Here's the core of the code I am using, which comes from Microsoft samples.
Any suggestions, thoughts, links, or ideas are much appreicated.
Best regards,
Jon Rothlander
public List GetAuditHistory(IOrganizationService svc, string entityLogicalName, Guid entityId, string attributeName) { var auditHistoryForRecord = new List(); var attributeChangeHistoryRequest = new RetrieveAttributeChangeHistoryRequest { Target = new EntityReference(entityLogicalName, entityId), AttributeLogicalName = attributeName, }; var attributeChangeHistoryResponse = (RetrieveAttributeChangeHistoryResponse)svc.Execute(attributeChangeHistoryRequest); foreach (var eachEditRecord in attributeChangeHistoryResponse.AuditDetailCollection.AuditDetails) { var detailType = eachEditRecord.GetType(); if (detailType == typeof(AttributeAuditDetail)) { // Never gets here. There are only AuditDetail types, not attribute types ever. // The problem there, is that only with attribute types can I determine old/new value. } }