Integrations and APIs for Service

Get Involved. Join the Conversation.

Topic

    Anuj Behl
    Async CPM can't access latest field (Contact)...
    Topic posted May 4, 2017 by Anuj BehlBlack Diamond: 60,000+ Points, last edited May 4, 2017 by Anuj BehlBlue Ribbon: 750+ Points 
    501 Views, 24 Comments
    Title:
    Async CPM can't access latest field (Contact) value
    Content:

    Hi,

    I have an Incident update async CPM which executes through a business rule. The CPM is required to get the latest value of certain Incident fields and send them over to an external system via CURL. One of the field updates that I need to send is associated Contact details.

    When an incident field such as Status, Thread etc. is updated and Incident is saved, I can access the latest updated value of these fields and send them to the external system. However, when I update the contact and save the Incident, the Incident record in CPM just cannot get the latest true value of associated contact.

    Suppose, a contact A was associated to Incident and I changed it to B. The Incident record in CPM will still give me the value of A. Now, if I change the contact again to C, I will see either A or B. I keep getting random contact ID associated to the Incident, sometimes it's the contact I added a few saves before.

    Now, I have tried $incident->PrimaryContact with

    • the object instance available to CPM in apply method
    • Fetch() the incident record and then access the contact
    • Use ROQL to fetch the incident and then access the contact

    No matter how I try to get the Contact value, I always get wrong result.

    Note: There is no issue when I do NOT run the CPM asynchronously. Since I want to make a call to an external system, I need to run it async.

    Did anyone encounter this before? Is it a known issue or a product bug that I am not aware of?

    If anyone has any solution to this, please share.

    Thanks,

    Anuj

    Version:
    Service Cloud Feb 2016, Connect v1.2

    Comment

    • Scott Harwell

      So, let's explore that a little more.  When you add usleep, you're not actually adding anything to your code that "fixes" an issue.  You're simply pushing the execution of that script 1000 microseconds.  You're not guaranteeing any system or data state, or alter the business logic of your code.

      What is likely happening in your case, too, is that the fetch query is hitting the replication DB, which is slightly behind the operational DB (less than 1000 microseconds), or business rules and other process that are triggered by a separate save operation have completed.  Therefore, when the process executes, in most cases the state of the data is as you expect due to that delay.  And, therefore you have the impression that simply adding a delay to the script run time has resolved the issue.  But, likely, you've just pushed the edge cases in which data state is not in sync with the report DB or some business rule is altering your record as the CPM is running.  The delay could also deal with rules or other processes running that impact data, in the same way that I have mentioned is likely happening to Anuj; but, again, that offset in run time is a likely a solution based on coincidence rather than anything else.  If you want to absolutely confirm that your data is coming from the operational DB at query time, then the best way is to use the "USE <DB>" clause and run a ROQL query to get the data.  And, if that data comes back in a different state than you expect, then something else has altered the data between your save event (or business rule trigger) and the execution of the CPM.  (Note: any time you run a query against the operational DB, you are impacting the performance on your instance.  So, you should only hit that DB when you use case requires recently operated data.  Otherwise, the reporting DB is the best place to fetch data.)

      Keep in mind that the CPM execution is not the only variable in play for this issue.  You have to consider the code in the CPM (what it does and if it, too, alters the data and saves again), the business rules of the site (if they are altering data and triggering other processes, like other CPMs, that might be editing the data too), db sync, API calls from other customizations or external extensions, system load, and more.  The issue can manifest differently from site-to-site based on the conditions that exist in that environment.  The cause can be one or any combination of things, and the consistency of the issue is usually your key for pinpointed where the issue happens and how to resolve it successfully.

      The best method to troubleshoot is to start with what you know.  There is a CPM that is querying for data and getting unexpected results.  So, start with the code first.  Is it querying for the data as expected?  Does it alter the data?  Does it cause a recursive loop anywhere that might re-trigger the CPM (i.e. an unsuppressed save)?  Then, once all the variables are worked out from the CPM, check out the business rules.  Are they being hit when the record is saved?  Does the order of events of business rules and CPMs risk an incident update occurring in this fashion?  Does the audit trail of the record provide any indication of multiple saves in the same immediate timeframe?  Are add-ins or other customizations in play that could be altering your record, too?  With that information, you can usually find the cause of the delay, which is typically a rule or save that was unnoticed or not considered during implementation.

      In the end, there are a lot of variable that are in play; not just what's being executed at the time of the CPM execution.  And, if you do believe that there is a system issue causing a problem, then it should be reported to customer care.  But, there usually an explanation for the source of these issues in the mix of the variables that I mentioned before.

    • Rajan Davis

      I see what you are saying with the various possibilities impacting the CPM Scott, but I don't think this is issue is as complicated as you're making it (or at least that is my impression).

      If you are concerned with external events affecting the CPM, then I would say check the audit/rule log for the incidents affected by this process. It would lessen the guess work of where the issues you had mentioned may potentially come from because the system records what API's and business rules make these changes..

      If there are processes in the background affecting the CPM, you need to address those things first, that is the source of your problems. I think we both can agree on that.

      If not, you can try to:

      • Add a usleep to defer some time to pass for background processes to do what they are supposed to before making changes to the incident (the naive, Gordian knot solution). This does not factor in that the fetched values may not be in sync with the operational database, but it solved my problem which was occurring in a synchronous context. It would be nice to have some sort of Promises/Futures feature where I could reliably commit a save to the database and then fetch the results on a callback, but I do not have this luxury with PHP.
      • Offload the logic to a integration script and use language that has better semantics for handling asynchronous processes (like Node) with the REST API (which is inherently stateless) for your incident interactions and external service update. The CPM becomes solely responsible for passing values to the integration script based on the incident update event and handling the integration script success or failure.
    • Dietrik

      Although I understand that there might be some possible issues by other or more complex customizations, the issue reported was pretty clear:

      Does this site have as many customizations as suggested or can this behavior be reproduced by a more simple setup ?
    • Bastiaan van der Kooij

      Coincidentally I just ran a very simple test with a forwarding script async on a vanila site, since this issue fascinated me :) The CPM simply forwarded the subject and the ID of the contact on every save. As test I changed the contact, pasted the new contact ID in the subject, saved it and repeated that a couple of times..

      Exact same issue Anuj is describing, subject always nicely in line of the actual edit, the contact ID all over the place. Screenshot below is the forwarded mails , first part is the subject where I pasted the ID of the actual contact, second part the ID of the contact retrieved by the script;

      Async issues ok, but having the CPM showing values from different states from the same object is a bug IMHO.. 

      It looks like the Async process caches related objects for each run in a completely unpredictable way.. Although it could be a lot of things, as long as we don't know the exact process it's all guessing to no avail..

    • Scott Harwell

      Can you post your script?  Are you comparing the $obj param passed to the CPM against a query for the incident in the DB at the same time?  Those aren't guaranteed to be the same.

    • Bastiaan van der Kooij

      Sure thing, see attached. I've tried using the $object directly and fetching the incident using the $object->ID, no difference. The subject and the primary contact ID is changed in the same object, the same edit to the same value, so they should match, regardless of where its coming from.

    • Scott Harwell

      You've been working on this script for a while!  December 4 2016! smiley

      I think that you may have trimmed some code that might provide context, but your query for contact to get the contact ID appears redundant and I seem to be missing where the ID comparison is happening in your logic...I have been hasty in a review though.

      A couple of things that we can try...ensure that you're using the same version of CPHP that you're using the the flowerbox.

      /**
       * CPMObjectEventHandler: incident_forward
       * Package: RN
       * Objects: Incident
       * Actions: Create, Update
       * Version: 1.3
       * Purpose:
       */
      
      use \RightNow\Connect\v1_3 as RNCPHP;
      

      Then, let's get the state of the incident through ROQL using the operational database as opposed to the fetch operation.

      // fetch objects
      $queryString = sprintf(“USE OPERATIONAL; SELECT id, subject, , udpatedtime, PrimaryContact.ID FROM Incident WHERE ID = %d;”, $object->ID);
      $incident = RNCPHP\ROQL::query($queryString);
      //no need for a contact query; we basically queried for the contact to just use it's ID, which you already have from the ID in the incident.
      

      I think that your subject line from your email is what your screenshot displays, but your code example seems to replace the subject, rather than append to it as you loop through your fields array like the other fields do.  Did you remove other data from the fields array that resulted in the output from your screenshot?  The array only gets one pair on line 46, but the loop implies other data.

      ... 
      else if (strtolower($label) == 'subject') { 
         $message['subject'] = $value; 
      } 
      ...
      

      So, if the subject has changed to the expected value at the time of the query to the operational DB, and the primary contact is different on the incident than the one that was set in the same transaction (incident save via API or console) that updated the subject, then I can see if there is a delayed DB transaction that might change the contact ID at a later time on the incident.

    • Bastiaan van der Kooij

      Yeah good scripts take a while :)

      However I have the feeling we're getting off track here. Ofcourse this CPM is not specifically created for this so it has some bloated functions (looping through report fields) and shortcuts, which are not used for this test. Couple of things;

      1. The fetch was some testing to see if it would make a difference, which it did not
      2. The subject is set after the generateMail function so the mail subject = incident subject
      3. The idea is that in the console you change the contact and paste the new contact ID in the subject line, then save. You would always expect the ID in the subject to be the same as the ID of the Contact, which (as can be seen in my previous post) is not

      I attached this less than ideal script as a quick test, the objective here is not to get it working for me, but to provide an easy way to reproduce the issue.

      Edit: I replaced the script with only the essentials, to avoid further confusion, which is probably what I should have done in the first place :)

    • Scott Harwell

      Coming back to this after a while.  I missed this in discussions in the earlier posts and the code review, but wanted to point it out after seeing a similar issue recently.  Using object queries in your CPMs (or anywhere), such as the queryObject() method (and therefor RNObject methods first, fetch, etc.), are served from a cache that you cannot override.  USE OPERATIONAL is only available on query (or QueryCSV in the SOAP API), which will override that cache and should always return the data as it sits in the DB at the time of the query from the CPM.  See if this works in your script and gives you consistent results...

      $results = RNCPHP\ROQL::query("SELECT subject FROM Incident WHERE ID = " . $obj->ID)->next()->next(); 
      ... 
      $mm->Subject = $results['subject'];