0

I wanted to split a map value which has a size of 2000 into small lists of max size 600 each and publish this smaller lists using platform events. I'm performing an update using platform event triggers. What's the best way of doing it ? Below is my code

if(WrapObjMap.keyset().size() > maxSizePerReq) {
                system.debug('Inside If');
                for(Integer i = 0 ; i < (WrapObjMap.keyset().size() / 600)+1 ; i++){
                    SplitList = new List<platform_Event__e>();
                    SplitFinalList = new List<platform_Event__e>();
                    for(Integer j=(i*600); (j<(i*600)+600) && j<WrapObjMap.keyset().size() ; j++){
                        SplitList.add(new platform_Event__e(long_text_area_field__c =  JSON.serialize(WrapObjMap.values().get(j))));


                    }

                    List<Database.SaveResult> results = EventBus.publish(SplitList);

                }
            }
6
  • May I ask why you chose Platform Events? Publishing and subscribing within the platform like you're doing (Apex pub/sub) may be an anti-pattern depending on your use case. PEs are great when you leverage their capabilities: integration with external systems, broadcast to multiple subscribers, leveraging PUSH technology to build realtime UIs... If you're not doing any of that, you might be better off with some batchable Apex.
    – POZ
    Feb 24, 2020 at 13:10
  • Yes we're integrating with external systems. external system hits salesforce end point which contains opportunities in the request body. optyIdTWrapObjMap contains opportunityId has key and some other fields as pair(in a wrapper class). SFDC validates these opptys and updates them. we're using platform events only when the incoming request body has more number of opportunities
    – Ravi Teja
    Feb 24, 2020 at 17:30
  • I'm sorry but I don't get it. Here's what I understood, please correct me if that's not it. Your external system uses REST or SOAP to call a custom Apex endpoint. The endpoint publishes the PEs (code shown in your question). Then, you use a trigger to catch the PEs and update opportunities?
    – POZ
    Feb 24, 2020 at 21:32
  • That's correct. Here is how the existing logic written. If the incoming opportunities count is 50 the update happens synchronously(no PE). if the count is more than 50 we use trigger to catch the PEs and update opportunities asynchronously. Now for the asynchronous update, if the count is more than 600 let's say 1200 we have to split it into two lists each having 600 opportunities and process two lists 1 at a time using PE. Let me know if this helps you understand or else I can share you the logic.
    – Ravi Teja
    Feb 24, 2020 at 21:46
  • Thanks for the clarification.
    – POZ
    Feb 24, 2020 at 21:59

4 Answers 4

1

As per your comments, I'm afraid that your use of Platform Events is an anti-pattern as you're not using them to integrate directly with your external system. In other words, you're using them to communicate from Salesforce (Apex endpoint) to Salesforce (Apex trigger).

What you need for your use case is either Batchable, Queueable or Schedulable Apex. Check out the documentation for more information on these options: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_async_overview.htm

2
  • thanks for the details. Did you mean we have to replace Platform events with batch apex etc. ? Or may be just use batch apex to split the list by having batch size as 600 ?
    – Ravi Teja
    Feb 24, 2020 at 23:20
  • Yes, I highly recommend not to use PEs for this particular use case and go for Apex. This is probably good for another question but the right tool is likely queuable rather than batchable since your data isn't in the database yet (you're inserting).
    – POZ
    Feb 25, 2020 at 10:28
0

if I didn't misunderstood your requirements then please try to use the below code or else correct me

if(optyIdTWrapObjMap.keyset().size() > maxSizePerReq) {
    for(Integer i = 0 ; i < optyIdTWrapObjMap.keyset(); i++){
        if (i < 600) {
            fillTheFirstList.add(listElement);
        } else if(i < 1200){
            fillThe2ndList.add(listElement);
        } else if(i < 1800){
            fillThe3rdList.add(listElement);
        }
    }
}
2
  • Can you make it more dynamic? FillThe25thList might not be the right way? Feb 24, 2020 at 8:05
  • Size 2000 is not a fixed number. It can be anything. So it has to be dynamic.
    – Ravi Teja
    Feb 24, 2020 at 17:31
0

Since Apex doesn't allow run time new list creation we can use a map to do the trick for us :)

Map<Integer,List<Opportunity_Assignment__e>> opportunityEventMap = new Map<Integer,List<Opportunity_Assignment__e>> ();

if(optyIdTWrapObjMap != null) {

    Integer keyValue = 0;

    //Creating Map
    for(Integer keyInteger: optyIdTWrapObjMap.keyset()) {

        if(opportunityEventMap.containsKey(keyValue) && opportunityEventMap.get(keyValue).size() <= 600)
        opportunityEventMap.get(keyValue).add(Opportunity_Assignment__e(AIR_Opportunties__c =  JSON.serialize(optyIdTWrapObjMap.values().get(keyInteger))));

        else{
            keyValue++;
            opportunityEventMap.put(keyValue,new Opportunity_Assignment__e(AIR_Opportunties__c =  JSON.serialize(optyIdTWrapObjMap.values().get(keyInteger))));
        }
    }
}

//Publish the events to bus
if(opportunityEventMap != null && !opportunityEventMap.keyset().isEmpty()) {
    for(Integer eventKey : opportunityEventMap.keyset()) {

        if(opportunityEventMap.containsKey(eventKey) && !opportunityEventMap.get(eventKey).isEmpty())
        EventBus.publish(opportunityEventMap.get(eventKey));
    }
}

Hope this helps!!

3
  • Can we have event.publish inside a loop ?
    – Ravi Teja
    Feb 24, 2020 at 17:32
  • hey Abhijith, can you please explain your code ?
    – Ravi Teja
    Feb 24, 2020 at 20:08
  • it is a map with lists of 600 records each..... when the first list is full a new list is created and it keeps going for all the records... once the iteration is complete and the map is not empty each list is published to the event bus
    – Abhijith E
    Feb 24, 2020 at 22:47
0

This is a simple pattern to use ... I used Object as a placeholder to avoid dealing with your variable names

Integer MAXCHUNK = 600;
SObject[] chunkRecs = new List<SObject();
for (Object obj : myMap.values()) {
   chunkrecs.add(new MyPE__e (fld = ..., ..));
   if (chunkRecs.size() == MAXCHUNK) {
     EventBus.publish(chunkRecs);
     chunkRecs.clear();
   }
}
if (!chunkRecs.isEmpty()) { // a partial chunk
  EventBus.publish(chunkRecs);
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .