When integrating with external systems, one of the common problems is that such external systems can be very slow. While most of the times there is nothing you can do about that slowness, part of the challenge of developing a great application is hiding that slowness from your end-user (or, at least, manage it in a way that it doesn't cause your own application to become slow).
This article helps you understand the type of problems we are discussing, and suggests an effective way of dealing with it. It includes a sample application which you can use for inspiration when solving the problem.
Some of the patterns below can cause extremely slow requests, errors and timeouts:
- Running a slow web-service to show information on a screen;
- Performing a slow query to an external system to reconciliate information with your local data;
- In a BPT flow, call an external system-of-record to close a transaction.
Some of the problems you may find in either of these patterns are:
- The request is very slow, so the screen is left hanging for a long time. Additionally, since OutSystems Platform only allows one concurrent request per end-user session (to avoid corrupting session information), these patterns may lead to the user thinking the server "is down";
- The request times out. You may be tempted to overcome the timeout error by increasing the time the request is allowed to run, but then you end-up with problem 1 again;
- In BPT, since you are not allowed to increase the timeout (it's fixed at 5 minutes), you typically get stuck in a timeout, not knowing what to do.
Dealing with it
Solving this type of problem requires that you think asynchronously. This means pulling the workload to a separate thread, but leaving your current workload in idle wait to avoid starvation (rather than keeping in busy-waiting, which is always bad).
Multiple solutions exist for idle wait in OutSystems, but one that is relatively simple to implement is:
- Implement a queue mechanism with:
- a Database Entity (storing the parameters to be processed, a boolean on whether it's complete, and a column storing the output);
- a timer that processes a "queue" of such requests.
- Whenever you need to process something that is slow:
- put a new request in the queue;
- wake the timer that processes the queue;
- While waiting for the timer to process the queue:
- When the queue is finished processing:
- If it's something on a screen / end-user UI: send the user to the screen when they can proceed (or download, e.g. if it's a report)
- If it's a BPT: the Wait construct ends and skips to the next activity.
For clean-up purposes, don't forget to implement a second timer that runs periodically (e.g. every hour ) and that deletes all queued requests that have been processed some time ago (e.g. more than 30 minutes ago).
An example of this pattern can be found in Forge component Work Queue Sample. It implements the sample above for a screen pattern.