TOP 8 “Best Practices”on How to Develop a Timer
Timers are asynchronous processes that run a server action that doesn’t depend on other actions to run and execute successfully.
Timers are handled by the OutSystems Scheduler Service. This service checks for Timers that are ready to run and executes their actions. A Timer is ready to run when the current time is greater or equal to the Timer runtime property NextRun.
Possible use cases of a timer
•Execute batch processing to process logic in records;
•Synchronize with external APIs and databases;
•Sending emails at a predetermined time;
•Delete older records from the database;
•Bootstrap an entity;
•Execute logic to configure an application after its deployment;
•Simply define a schedule to do something.
Implementing timers
When implementing timers, there are best practices that you should follow to ensure a proper running of the timer without affecting the app performance and allowing a good audit and debug. In addition, as another type of implementation, the code must be self-explanatory.
1. Prefix the timer action with Timer_<NameofTheTimer>
To easily identify the server action has a timer, prefix it. Avoid using that action in another context than that timer.
2. Dynamically allow/disallow the execution of the timer
You may need to disable a timer of executing due to a business request or some technical issue. The easy way to do it is to avoid changing the code (imagine doing it in production). A site property or a database value can be used to implement this feature.
3. Creating a way out of a cycle
One of the best usages for a timer it’s housekeeping. We can delete some record every day or transfer data to backup entities in order to reduce hot tables. During a huge cycle, we can unintentionally run a timer or generate an infinite cycle. Consequently, it’s a good approach to have a site property that allows us to force the cycle to stop right away.
4. Add logs to audit the execution of the timer
Each time you run a timer, you should audit information about the execution of it for auditing and debugging. Data you definitely need to track is:
•Start date/time;
•End date/time;
•Status (success, completed with no errors, completed with errors, failed);
•Any output or error messages.
•Adapting these logs with a back office screen can help you and the rest of the •team to manage application timers.
It’s important to understand that the log messages order is not assured. Consider numbering the logs.
5. Avoid long-running actions that never times out
Even though OutSystems automatically retries a failed timer, it could end up degrading app performance or changing multiple records many times. At worst, if the code is not ready, it could reprocess the same records multiple times after they were updated.
To prevent a timeout never increase the timeout in minutes. Most of the time it is a design problem and you design expecting possible timeouts.
Timers should take at most 20 minutes and then reschedule themselves to start again immediately to continue the task at hand.
Consider dividing the records being processed in batches and calling the timer to execute while there are records to process. Use control tables and process data in chunks, rescheduling itself at the end of a run.
This is accomplished by adding an explicit timeout inside the timer logic that when reached takes the necessary actions to properly terminate the current processing, store the current status of the process in such a way that when the process starts again it can seamlessly proceed from the point where it stopped. At the end of a run, the timer ends with a wake timer action for itself.
6. Plan, prioritize and monitor the timer schedule to avoid impacting the app performance
Schedule the timer to run when it will not impact the app performance and affect UX. Most of the time, heavy processing timers run at night or weekends when the app less affects users and live systems.
In case of timers running at the same time, prioritize them. Use the Service Center to monitor the timer execution.
7. Place timers in the Core Layer modules
Timers execute actions that should be in the Core Layer modules. Usually, you can find timers in _CS and _BL modules depending on what they are handling. For example, if you have a timer to delete records from a database (for records marked to delete) the timer should be placed in _CS module where that entity is defined; if your timer is executing business logic code it should be placed in a _BL module.
8. Exception Handling
The timer action should be the simplest possible and each record be processed by itself, returning a message record with a flag with success or false and with a message in case of an error. The main action simply uses this information to raise an exception, handle the error or simply continue que logic. Additionally, if you want to guarantee that your record will be always saved, insert a CommitTransaction. Be aware that it has a performance hit, so in some cases, you may be better off only committing every ten or x records.
This is an example of error handling.
Session in Timers
Asynchronous logic, such as Timers and Process Activities, run in a separate session. All session variables will start with their default value when executing an action associated with a timer or process activity.