I'm looking for some advice, both on my approach and how to solve my particular issue.
Use Case
When an external integration team updates all particular recordTypes. Every picklist value associated with any available picklists becomes inactive. Which means it became a manual task to update all of these values back to active.
History
My solution was first to store custom Metadata. With each row containing
- Object
- RecordType
- Picklists ('Field_1__c, Field_2__c, Field_3__c')
Then going forwards-
- Integration team call to a WebService Apex class
- That WebService Apex class starts a batch Job
- The batch Job fires an Apex Class
The first two points are self-explanatory. However the Apex class is the issue.
Originally I used a for loop on the Metadata. Processing each Object & RecordType pairing using Tooling API & a Custom Wrapper Class to GET both existing Metadata, process all picklist values to become active then PATCH the RecordType using a queueable. With this approach I got success on miniature data sets, but ultimately we were getting timeouts and record locking issues.
The next approach was using FinancialForce's Apex MetadataService (md-api) code. After days of working with this, Metadata API and bring the MetadataService class up to date with API v50.0. I got more success in our sandbox org. But now I'm getting timeout issues and I'm confident that after nearly a week of back and forth code there can be a fix that I'm unable to discover.
The Problem
When running the batch job with a near-live quantity of data. 9/18 jobs are failing due to WebService time out (120000). I'm not sure whether this is lack of optimisation from me, it being part of the parcel of a large data set, or whether it's unavoidable and we need to contact our reps at Salesforce for further help.
The below is the method that is commonly timing out. Both 'service.ReadMetadata' are in the same method doing callouts. So I'm not sure how to synchronously fix these to retain within 120s.
//List<String> recordType = List of picklist fields available in the recordType
//DescribeSObjectResult objectResult = Current object information
//Set<String> metadataFields = Set of fields to udpate listed in the custom metadata as above
//String recordTypeName = recordTypeName label
public Set<String> processPicklist(List<String> recordType, DescribeSObjectResult objectResult,
Set<String> metadataFields, String recordTypeName ) {
for(String picklistValue : recordType) {
if(metadataFields.contains(picklistValue)){
String ctObjectRecordType = String.valueOf(''+objectResult.getLocalName()+'.'+recordTypeName+'');
String fieldName = String.valueOf(''+objectResult.getLocalName()+'.'+picklistValue+'');
List<MetadataService.PicklistValue> pickVals = new List<MetadataService.PicklistValue>();
// Either of these two lines are timing out when it comes to error logs
MetadataService.CustomField picklistFieldMdApi = (MetadataService.CustomField) service.readMetadata('CustomField', new String[] { fieldName }).getRecords()[0];
MetadataService.GlobalValueSet globalPickListApi = (MetadataService.GlobalValueSet) service.readMetadata('GlobalValueSet', new String[] { picklistFieldMdApi.valueSet.valueSetName }).getRecords()[0];
for(MetadataService.CustomValue existing : globalPickListApi.customValue){
MetadataService.PickListValue newPickVal = new MetadataService.PickListValue();
newPickVal.fullName = existing.fullName;
newPickVal.label = existing.label;
newPickVal.isActive = true;
newPickVal.default_x = existing.default_x;
pickVals.add(newPickVal);
}
MetadataService.Metadata[] mdRecordTypes = service.readMetadata('RecordType', new String[]{ctObjectRecordType}).getRecords();
MetadataService.RecordType[] recordTypes = (MetadataService.RecordType[]) mdRecordTypes;} else {recordTypes = new MetadataService.RecordType[]{};
MetadataService.RecordTypePicklistValue[] rtPickValues = new MetadataService.RecordTypePicklistValue[]{};
MetadataService.RecordTypePicklistValue rtPickValue = new MetadataService.RecordTypePicklistValue();
rtPickValue.picklist = picklistValue;
rtPickValue.values = pickVals;
rtPickValues.add(rtPickValue);
for(MetadataService.RecordType RT : recordTypes){ RT.PicklistValues = rtPickValues;}
List<MetadataService.SaveResult> results = new List<MetadataService.SaveResult>();
results = service.updateMetadata(new MetadataService.Metadata[] { mdRecordTypes[0] });
}
}
return metadataFields;
}
My head is deterioriating so please. Any advice is amazingly appreciated.