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
Brian12345Brian12345 

Best Practices - Future Calls, Triggers and Apex Governor Limits

We have what we feel is a very common use case scenario - the need to create "matches" between the attributes of records stored in two objects - think of any online "dating" service, such as FriendFinder.  For the sake of example, I will refer to the objects as Searcher and Creator.  When the attributes of a Searcher record and Creator record match, the result is stored in the match object.  Both the Searcher and Creator have a lookup relationship to this match.

Most of the time, a Searcher will match several hundred Creators and the matches are generated almost immediately (< 1 sec). However, if the Searcher has very wide criteria they will match more than 1000 Creators, which causes problems with the Apex governor limits.  As a result, we use simultaneous future calls in addition to the immediate call - in this scenario it can take 2-4 minutes to return 1300 matches.  While this delay is far from ideal, it was sufficient enough.  The user reviews the matches that are returned immediately and as they use the application the page refreshes and they can see the matches created from the completion of the future calls.  This implementation was stable.

We are now having at least three issues with this implementation:

1) Several months ago we added triggers that count the # of matches created and insert the totals into fields in the Searcher and Creator records.  These triggers are fired off at the same time that the matching occurs.  At this point, we began to receive "invalid query locator" developer exceptions related to the class that is counting the matches.  The net impact of this error is that the query was not completed, resulting in matches not being created for the Searcher.

2) In the event a Searcher edits their record and a match no longer exists with a particular Creator record, that match will be deleted.   We are now receiving errors that seem to indicate that two future calls are attempting to delete the same match.  When this error occurs, the wrong matches will be recorded for our Searchers. 

3) Another issue we are having right now is: as sometimes future calls are running simultaneously and triggers in different threads/transactions are populating the same record (Creator or Searcher), that has caused deadlocks which are unavoidable with concurrent trigger executions.

To solve for these problems, our developer changed the future calls from being simultaneous to sequential.  We implemented sequential future calls through workflow, after one future call is finished, it triggers the workflow which triggers another future call. The sequential future calls are taking 25 minutes to return all matches in the sandbox.  This is unacceptable to our users.  

As of right now we are at a standstill.  Future calls do not seem to be a good foundation for generating matches, particularly if we have additional classes / triggers tied to the execution of the code that generates the matching.  Sequential Calls create an awful user experience.  We expect matches to be returned in seconds, not 25 minutes.

We are hoping that the user community can help us vet out these issues and provide feedback as to best practices related to our business requirement.  Ultimately, we need to make a decision if Force.com is capable of supporting this requirement, or if we will need to move the logic that generates the matching to ec2, for example..

We are happy to provide code samples that will enable the community to help us solve our problems, but did not want to do so on this first post as we have several thousand lines of code and simply providing a few lines of code won't make sense.

Thanks!

B.T.
Ron HessRon Hess

A complex problem indeed. 

 

I don't have a good answer but the one idea i have is to use Batch Apex to create the matches.  You may not need to do this since the implementation was stable just using future calls, ok.

 

1) for the trigger that counts the match, you may have to use the SOQL keyword " for update " to lock a record that is in common use by multiple threads.

 

2) if this error occurs you may be able to catch it and ignore it ( since one delete is as good as two deletes)

 

3) can you use "for update" to detect locking and then retry that update using a future call ?  

 

 

 

 

Ron HessRon Hess

Since a match is directly related to a search, could this be made into a master-detail ?

 

with the resulting benifit that you can have a counter that is built using a Rollup Sumary field.

 

This would eliminate the trigger that counts up the matches per searcher.

 

The other trigger ( couning the number of records that are looked up to a creator) would appear to be a devilishly hard problem to properly test.  You could use for update to avoid deadlock in the trigger, and retry from a future call if the record was locked, i've never tried this.

MyGodItsColdMyGodItsCold

I'm not sure I understand correctly - are Searcher & Creator different objects? We have something similar to that. We looked at creating a match object & maintaining it. However, the governors challenged us. We went with embedding knowledge on a field on one of the objects.

 

We have mail lists: m1, m2, etc. One mail list can have many contacts & one contact can be on many lists. On the contact, we have a field: mailListMemberships - a semicolon delimited list of which mail lists that contact is on.

 

Since a contact manager can have thousands on a list, we use VF pages to present enhanced lists.

 

Further, there are all kinds of rules as to viewability & changeability based on account team membership, etc. We use fields on the User record to store last mail list used, and last view ID accessed, and things look pretty good.

 

Granted, we have the semicolon delimited list in a text area field - we know it's enough to hold usual & customary, but ideally, they should go into a large text field, but then you have the problem of they're not being indexed.

 

Hope this helps, but a year ago, we traveled down a number of roads & finally settled here - and given our constraints - it works.

 

However, I agree, this is a common use case & should not be hard. My manager keeps on telling me he could do this system in a day in Access ...