I'm fully aware that I'm not even slightly an Apex Developer, and have attempted to cobble this together from other sample code.
Problem Statement:
I'm running a custom flow that is deleting email messages due to storage issues from them, and cloning them back into standard tasks (you'll notice most all EmailMessage records have an ActivityId field, this is what I'm cloning). The "Cloning" process is really to just query the paired activity record that points to the Email message, and re-insert it, since when you delete the email message Salesforce cascade-deletes the task that points to the email message.
Because Tasks get archived, the normal "Get" element in flows fails to return anything querying for that ActivityId on an email message, so I tried an invocable apex method.
This code works correctly on small batch sizes, but anything at the normal 200 transaction size limit fails due to SOQL 101 or 201 in bulk. I'm confident the issue is exclusively tied to this invocable APEX class, because I have no issues doing the same flow in bulk if I skip this apex, and we are extremely clean on SOQL in general in our flow triggers.
I was expecting the flow to iterate the elements prior to the Apex Action and build the set of task Ids to pass into this apex method, and then a single query to be run and return the complete list of tasks to the flow, but it seems that its running the query for each input task Id string.
Apex Class
public class InvocableQueryArchivedTask { @InvocableMethod(label='Get Archived Task' description='Returns Full Task Record' category='Task') public static List<Task> getArchivedTask(List<ID> ids) { List<String> fields = new List<String>(Task.SObjectType.getDescribe().fields.getMap().keySet()); String soql = '' + ' select ' + String.join(fields, ',') + ' from Task' + ' where Id in :ids AND IsDeleted = FALSE ALL ROWS'; List<Task> tasks = database.query(soql); return tasks; } } Flow Pictures
SOLUTION
After some rigorous debugging on the apex, it was clear that it was trying to run in bulk, and doing a single query, however once it left the apex and tried to return the task record(s) to the flow, it essentially then switched to running the query for each email message one at a time. I think this was probably due to a lack of mapping info in returned set of tasks, since I was then referencing back to the original email message to loop through it and re-generate the set of ContentDocumentLinks to re-insert for each task.
I had to figure out how to shift the whole code into the Invocable Apex Class, which I've posted below. At this point the flow trigger exists solely to let me invoke it by dataload of a custom checkbox I added to email messages.
On the plus side, this is doing exactly what I need, and drastically reducing storage impacts from older EmailMessages while not really destroying any old business data that might still have some value.
public class InvocableConvertEmailToTask { @InvocableMethod(label='Convert Email To Task') public static List<Task> emailsToConvert( List<EmailMessage> EmailMessages ) { Map<Id, Id> emailToTaskIdMap = new Map<Id, Id>(); Map<Id, Id> taskToEmailIdMap = new Map<Id, Id>(); List<Id> atIds = new List<Id>(); for( EmailMessage em : EmailMessages ){ emailToTaskIdMap.put( em.Id, em.ActivityId ); taskToEmailIdMap.put( em.ActivityId, em.Id ); atIds.add( em.ActivityId ); } List<String> fields = new List<String>(Task.SObjectType.getDescribe().fields.getMap().keySet()); String soql = '' + ' select ' + String.join(fields, ',') + ' from Task' + ' where Id in :atIds AND IsDeleted = FALSE ALL ROWS'; Map<Id, Task> TaskInsertMap = new Map<Id, Task>(); for( Task at: database.query(soql) ){ TaskInsertMap.put(at.Id, at); TaskInsertMap.get(at.Id).External_Source__c = 'Converted from Email Message'; TaskInsertMap.get(at.Id).External_Id__c = taskToEmailIdMap.get(at.Id); TaskInsertMap.get(at.Id).Id = null; } Insert TaskInsertMap.values(); List <ContentDocumentLink> cdls = [SELECT ContentDocumentId, LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId IN :emailToTaskIdMap.keyset()]; IF( !cdls.isEmpty() ){ List <ContentDocumentLink> cdlInsertSet = new List<ContentDocumentLink>(); for( ContentDocumentLink cdl: cdls ){ ContentDocumentLink cdlnew = new ContentDocumentLink(); cdlnew.LinkedEntityId = TaskInsertMap.get(emailToTaskIdMap.get(cdl.LinkedEntityId)).id; cdlnew.ContentDocumentId = cdl.ContentDocumentId; cdlInsertSet.add(cdlnew); } insert cdlInsertSet; } delete EmailMessages; return TaskInsertMap.values(); } } 
