function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
cmarkcmark 

Governor Limits - breaking up a trigger into multiple triggers?

Hello.  I am writing a trigger which does the following: when Account ownership is changed, query all associated Notes, Open Activities, Contacts (and related Notes and Open Activities), and Open Opportunities (and related Notes and Open Activities) - and change the ownership of all of those records.  I'm concerned that in some rare cases I may be updating more than 100 records as a result of this.

So my question: If i divide this functionality into three triggers - one which does Account Notes and Open Activities, a second trigger which does related Contacts (with Notes and Activities), and a third which does Opportunities (with Notes and Activities) - will this help?   Or will the platform calculate the sum of the records being processed by all three triggers?

Thanks
Chris
BoxBox
Nice try but I'm afraid the Limits are for the life of the thread not the individual triggers.

If you need larger Limits look into Asynchronous Apex (Summer 08) and moving your logic out of the trigger and into a class.  Async APEX isn't GA as yet but can be enabled by support if you have a valid business case.

BoxBox

By GA I mean in Production environments sorry, if you are in a Dev org its there waiting for you.

cmarkcmark
Okay - sorry for the dumb question.  You mention async apex and moving logic into classes.  Can you explain how this helps?  I'm familiar with moving logic into classes, of course, but i'm guessing that doesn't help me by itself?  How does async apex help?
thanks so much for your time
chris
BoxBox
If you code and execute APEX asynchronously you get the larger set of governor limits however your business requirements must accept that this code will not execute straight away.  Below is a really simple example which i think gets the idea across well.

Code:
trigger myTrigger on MyObject (after Update)
{
// lets create a list of records IDs that i need to process with the larger limits
for(MyObject : trigger.new)
{
if(i need to process this one)
{
liIDForFutureProcessing.add(MyObject.Id);
}
}

// any other processing
...

// Now lets make my call
FutureMethods.ProcessList(liIDForFutureProcessing);

}

 and my class would look something like:

Code:
global class FutureMethods 
{
@future
public static void ProcessList(List<Id> thisIDList)
{
try
{
// what you need to do
}
catch(Exception e)
{
System.Debug('Exception - ' + e);
}
finally
{
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

String[] toAddresses = new String[] {'admin@ABC-corp.com'};
mail.setToAddresses(toAddresses);
mail.setReplyTo('captain.future@superhero.com.au');
mail.setSenderDisplayName('Captain Future!');
mail.setSubject('Future Method are easy!');

mail.setPlainTextBody('As if you couldn\'t guess..... this was fired from a future method =O)');

Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}
}

 The mail message can be over kill but can be directed to the user who initiated the original call, you could put in some exception handling logic that e-mails admin and the user if something terrible happened but that about the gist of it.

Let me know if you need any more pointers,


cmarkcmark
Very nice - this would not cause a problem with my requirements.  A few final (hopefully) questions though - you mention that this would give me higher limits - but I don't see that in the documentation.  Where do you see that?  The documentation for the "future" annotation mentions some limits, but doesn't specifically mention DML limits.

In the infamous governor table, I see the following note:

***** Salesforce also imposes an organization-wide limit of 200 method calls with the future annotation per 24 hours.

I assume that means that the trigger that calls my "future" method can only fire 200 times / day?

Thanks again
Chris
Box @ HomeBox @ Home

In the APEX documentation the gov limit table has three columns, trigger, test class and web service invocation limits, future methods get the larger web service limits.

You would be limited to 200 method calls per day I'm afraid, you could get around this issue but creating checkbox on the MyObject record that indicates if updates should be processed with future methods or the trigger.  If the MyObject entity causes errors on the trigger limits, set the flag and only then will it get processed by the future call.  You would then need to create and maintain two classes but it give you the better scailablity.

cmarkcmark
Okay - I wasn't aware that the future annotation got the higher numbers in that table - excellent.

I see what you're saying about only using the "future" annotation when necessary.  So tell me if this works:

1) i write two Classes - one which uses @future and one which does not. (per your recommendation)
2) in the trigger, I query all of the records that must be updated
3) if there are 100 or fewer records, call the method which doesn't use @future.  if there are more than 100 records, call the method which uses @future.

I actually don't expect that I'll be bumping against the 200 call limit, but I'd like to code for it anyway.

Thanks so much for your time - I really appreciate it.
chris

BoxBox

Ah, I might have taken you down a solution that is move sophisticated that you require (or its early in the morning and I haven’t finished my coffee yet and I have miss understood  :smileyhappy:). 

So each trigger can fire 20 DML statements, and a maximum of up to 100 SObject records per trigger member.  Where as a web service or future can process 100 DML statements with a maximum of 10,000 records. 

- if your trigger fires for 1 record and you need to update 100 MyObjects then triggers are good.
- if your trigger fires for 1 record and you need to update 50 MyObjects and 50 MyOtherObjects then triggers are also good.
- if your trigger fires for 2 records and you need to update 150 MyObjects then triggers are also good.
- if your trigger fires for 2 record and you need to update 150 MyObjects and 50 MyOtherObjects then triggers are also good.

- if your trigger fires for 1 record and you need to update 101 MyObjects then you must also use future/web service
- if you needed to update 21 different types of SObject then you must use future/web service

Now you can also just fire a web service from an SControl button or a bit of visual force if you don’t want to go down the future route, I guess this is why I love the platform, there are some many options available to solve a problem so you really can design and build the best solution for your requirements. 

I’m a big fan of the future method functionality and when combining it with VF pages you can get some awesome behavior happening.


And no worries about the time, that’s what we’re here for, any more questions just send them over.



Message Edited by Box on 08-01-2008 09:55 AM
cmarkcmark
Hmmm, okay, I think I understand.  I'll describe my situation.

The owner of an Account is updated via the API.  This will cause my Trigger to fire.  My trigger does the following:

1) grab all associated Notes
2) grab all associated Open Activities
3) grab all associated Contacts (and Notes and Open Activities)
4) grab all associated Opportunities (and Notes and Open Activities)
5) change the ownership of all of the records returned in 1-4 above.

So that's the scope.  Generally speaking, this functionality will be called approx 20 times per day, so volume isn't huge.  Additionally, a majority of the Accounts will have fewer than 100 records returned by 1-4 above.  But occasionally, let's suppose that an Account has 400 records (between Notes, Activities, Contacts, Opps, etc).  So this is where I need the @future functionality.  (Or using your examples, 1 records is updated and I need to update a total of 400 records.)

So my plan is to query 1-4 above and get a count.  If the count is <=100, call the sync method.  If it's greater than 100, then call the async method.  From what you're saying, it sounds like async will do the job.  I'm trying to avoid the web service approach because i'd love to keep all of the functionality inside of sfdc (as would the client, of course).

Is this correct?  Sounds like it should work with @future...

Thanks
Chris
BoxBox

it sounds good to me!  Let me know how you go, I always enjoy hearing how our grand ideas pan out in the real world.



sparktestsparktest

I am hoping you can shed some light on some confusion.  I am trying to put together a trigger that will respond to an insert on a master object, and then insert any number of related records on another object depending on some calcs done on the values in the master object.  If the number of records that needs to be inserted is greater than 100, it sends it to a '@future' class to do the insert, otherwise the trigger takes care of it itself.

I am not well versed yet on how to pass the list from the trigger to the class, at least I believe that is where I am falling short.  Here is the trigger and the class so far.....

Any help?

Thanks,

 

error on last line

expecting a semi-colon, found '' SiteLeaseCumCostUpdate/src/triggers SiteLeaseFncTblTrigger.trigger line 55

Code:
trigger SiteLeaseFncTblTrigger on Site_Lease_Contract__c (after update) {
  
 for (Site_Lease_Contract__c SLC : trigger.new) 
 {
  List<SiteFnc__c> followuprecords = new List<SiteFnc__c>();
  Double Months = SLC.Lease_Term_In_Months__c;
     Double RMonths = SLC.Renew_Term__c;
     Double RTerms = SLC.Renew_Addl_Terms__c;
     Double j = Months + (RMonths * RTerms);
     Boolean GT = SLC.Generate_Table__c;
     String LeaseID = SLC.id;
     Date MonthBegin = SLC.Start_Date__c;
     Double escamt = (SLC.Escalation_Amount__c/100)+1;
     Double escmth = SLC.Escalation_Month__c;
     Double RentToPay = SLC.Initial_Rent__c;
     Double SLD = SLC.Straight_Line_Depr__c;
       
  if (GT == True)
  {

      Integer Count = 1;
      Integer CycleInsert = 1;
      
      for (Integer k = 0; k < j; k++) {
          
       if (escmth == k+1)
       {
        RentToPay = RentToPay * escamt;
       }
       if (k+1 > escmth)
       {
        Count = Count + 1;
       }
       if (Count == 12)
       {
        Count = 1;
        RentToPay = RentToPay * escamt;
       }
       
       SiteFnc__c SiteFnc = new SiteFnc__c(
       Site_Lease__c = LeaseID,
    Date__c = MonthBegin.addmonths(k),
    Cumulative_Def_Rent_Obl__c = SLD * (k+1),
    Rent__c = RentToPay);
     
    followuprecords.add(SiteFnc);
      }
  }
     if (j < 101)
     {
      insert followuprecords;
     }
     else
     {
   SiteLeaseFncTblFClass.ProcessList(List : followuprecords);
     }
    } 
}


 error on 'insert followup records;' is

DML requires SObject or SObject list type: LIST:Id SiteLeaseCumCostUpdate/src/classes SiteLeaseFncTblFClass.cls line 8

Code:
global class SiteLeaseFncTblFClass 
{
 @future
 public static void ProcessList(List<Id> followuprecords)
 {
  try
  {
   insert followuprecords;
  }
  catch(Exception e)
  {
   System.Debug('Exception - ' + e);
  }

 }
}

 


Message Edited by sparktest on 12-24-2008 10:12 AM
JeremyKraybillJeremyKraybill
Not sure if you solved this already, but you have a syntax issue in your code.

Code:
SiteLeaseFncTblFClass.ProcessList(List : followuprecords);
 should be
Code:
SiteLeaseFncTblFClass.ProcessList(followuprecords);

Let me know if you fixed this, and if it did get around your governor limits, we are looking at solutions for a somewhat similar case.

Jeremy Kraybill
Austin, TX
sparktestsparktest

Thanks! 

Will let you know after I get a chance to test........

sparktestsparktest

I just looked and I had already made that change to the code.  So, at this point I still have not been able to get it to work.  Will keep plugging away at it though....

Thanks Jeremy