The Now Platform® Washington DC release is live. Watch now!
on 10-15-2020 09:18 AM - edited 3 weeks ago
< Previous Article | Next Article > | |
Improving Slow OR and JOIN Queries | Performance Maintenance and Administration |
This guide is written by the ServiceNow Technical Support Performance team (All Articles). We are a global group of experts that help our customers with performance issues. If you have questions about the content of this article we will try to answer them here. However, if you have urgent questions or specific issues, please see the list of resources on our profile page: ServiceNowPerformanceGTS
ServiceNow implementations often need to interact with external systems via an outbound integration using either Simple Object Access Protocol (SOAP) or Representational State Transfer (REST) Web APIs. The ServiceNow platform allows such integrations to be built in a number of different ways, e.g.:
See our official documentation here:
Getting the design of an integration correct can be hugely important. Poor design can impact performance, cause end user frustration, or even lead to service affecting events within an instance. This article discusses the various options available along with the pros and cons of each approach – hopefully this makes deciding how to develop outbound integrations that little bit easier.
All outbound integrations require either a SOAP or REST ‘message’ to be created then corresponding functions/methods defined via the sys_soap_message or sys_rest_message tables (navigate to ‘System Web Services -> Outbound’). The majority of configuration of these ‘messages’ depends on details of the external web service (so is not covered here) however it is worth mentioning MID servers.
Within each function (SOAP) or method (REST) there is a field allowing a MID server (or MID cluster) to be specified, i.e.:
If this field is populated then the integration will run via a MID server (i.e. the MID server will be responsible for physically communicating with the external web service). This can have important ramifications:
Note that the MID server specified in the function or method can be overridden at runtime via use of the ‘setMIDServer()’ method.
Once SOAP/REST messages (and corresponding functions/methods) have been defined they can be specified within APIs (such as ‘RESTMessageV2’) to trigger use of the integration. Both SOAP and REST APIs can be executed in two ways:
Requests to external endpoints are subject to various timeouts:
ECC timeout is influenced by a number of factors:
Note: Waiting for a response after using executeAsync, as described above, is not ideal. It effectively makes the system synchronous again. The initiating thread must wait for the asynchronous thread to complete.
Synchronous requests have some obvious benefits. When triggered, they block the thread waiting for a response from the endpoint. Once this is received, it can be immediately processed. Due to this, they can sometimes be used to populate elements of a user interface with some type of external data. Note, however, that this can be extremely dangerous and should be avoided where possible:
If synchronous requests must be used, ensure that:
The combination of these factors won’t guarantee issues around synchronous requests are avoided. However, they will help safeguard against disaster. As a final point, if a synchronous request has to be used to populate the UI, consider notifying users that they are waiting for an external system (via use of some kind of icon that appears on the form) - at least they will then have some idea why the instance seems slow.
Now that we understand the different options available for outbound web service design, we can step through some graphical examples showing exactly how they operate. The examples below all reference the ‘RESTMessageV2()’ API. However the ‘SOATMessageV2()’ API is functionally identical.
If routed via a MID server, a request using setEccTopic() will fail with the corresponding output ecc_queue record being set to a state of 'error' and the text 'The MID Server code is unable to run this ECC Queue output topic' added by the MID server. The reason for this is that the MID checks that the topic field contains the name of a valid Discovery probe - in the case of an arbitrary topic this will not be the case hence the MID will reject the output record and will not process the request.
Requests which are routed via a MID should use the 'setEccParameter('skip_sensor', 'true')' and 'setEccCorrelator('[arbitrary string]')' methods when they are created. Both of these parameters have an important function:
To process the response to asynchronous REST requests routed via a MID server (and using setEccCorrelator()) an after insert business rule should be created against the ecc_queue table. This should:
current.agent_correlator == "[arbitrary string]" && current.topic == "RESTProbe" && current.queue == "input" && current.state == "ready"
current.state = "processed"; current.processed = new GlideDateTime().getDisplayValue(); current.update();
Note that it is OK to use current.update() in an 'insert' business rule as this will NOT cause the business rule to be recursive - clearly this type of behaviour should be avoided in an 'update' business rule as the business rule will trigger itself and so become cyclical.
NOTE: While the diagram below includes a MID Server, the guidance in this section applies to all use cases of SOAPMessageV2 and RESTMessageV2, regardless of if you use a MID Sever or not. TLDR; waiting for an asynchronous response makes it synchronous. If you don't want that, then use the setEccTopic() or setEccCorrelator() methods described above.
Generally, using asynchronous requests is a preferred approach and increases the likelihood of a consistently performing scalable instance. Be aware, however, that if any of the following methods are used after executeAsync, then you will be effectively making your request synchronous again:
Triggering an asynchronous request, then calling ‘waitForResponse()’ is analogous to using a synchronous request in the first place. If it can be avoided, then it probably should be!
You see, when executeAysnc is used, a separate thread is spawned to handle the actual call to the 3rd party web service. This has the benefit of allowing the initiating thread to proceed without waiting for the response. However, if you then tell your code to wait for a response via getStatusCode, getBody, or waitForResponse, you loose that performance benefit. Your initiating thread will have to wait for completion of the asynchronous thread that it had spawned.
Be aware that if an asynchronous request is triggered, then one of the above methods is called (essentially making the request synchronous) an additional set of timeouts come into play dictating how long the thread will wait for a response (i.e. input record) to appear in the ECC queue (ecc_queue) table:
Both of the above can be overridden at run time via use of the 'waitForResponse([seconds])' method. Their aim is to avoid threads stalling for too long (or potentially indefinitely) waiting for some kind of response from an external web service.
As this document describes, there are many ways in which to design outbound integrations and, whilst asynchronous requests are probably favorable, there is no perfect solution. All designs lead to stalled threads somewhere and there are various pros and cons around handling responses. Ultimately, the most important aspect to consider is where the instance can tolerate delays and the effects this will have at scale. For example, synchronous requests will stall 'front end' components of the instance (i.e. default/API_INT/worker threads). This may be OK in a test environment. But could it cause issues in an instance processing hundreds of thousands of transactions every day? Likewise, asynchronous requests using a MID server will generally stall threads on MID servers - this takes issues away from the instance itself. But how will MID servers cope and could this impact other MID functionality such as Discovery?
Hopefully the discussion and examples in this document will help promote 'balanced' designs where potential pitfalls are understood and can be worked around if necessary.
< Previous Article | Next Article > | |
Improving Slow OR and JOIN Queries | Performance Maintenance and Administration |
Hi!
Thanks for a great article!
If the business rule that handles "setEccCorrelator" is scoped, the following needs to be changed:
From:
current.processed = gs.nowDateTime();
To:
current.processed = new GlideDateTime().getDisplayValue();
Jørgen
Hi,
I noticed there is a discrepancy between this article with similar KB(https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB0694711).
KB states as following:
The ECC timeout value is the amount of time that an asynchronous request will wait for the response to show up in the ECC Queue. This timeout applies to all asynchronous requests, both with a MID Server and without. However, this does not apply to synchronous requests because they do not use the ECC Queue.
On the other hand, this article states that "This timeout only applies to synchronous requests executed via a MID server".
Wow, thanks for the great callout! I'll update both documents with the following corrected description:
ECC timeout is how long getStatusCode, getBody, or waitForResponse will wait for a response when the executeAsync() method has been used. Note that the ECC timeout will NOT impact the timeout of the HTTP web service request itself nor the timeout of the ecc response handling when setEccTopic() or setEccCorrelator() are being used!
ECC timeout is influenced by a number of factors:
Note: Waiting for a response after using executeAsync, as described above, is not ideal. It effectively makes the system synchronous again. The initiating thread must wait for the asynchronous thread to complete.
Best regards, Your Global Technical Support Performance team.
Thanks Jørgen,
We've updated the article.
Hello. Are there any tips regarding the setup an integration Flow to use the Connection Timeout from an alias Connection instead of the global property glide.http.timeout? We seem to have some challenges with it "sticking".