Timers in OutSystems are a powerful tool for triggering actions and processes on a specific schedule. As a developer, you'll frequently encounter them in your applications. However, what distinguishes a great timer from the rest? Let's explore some best practices to ensure efficient and reliable timer usage.
Consider Using Processes
While timers are commonly used for batch processing due to their simplicity, consider leveraging processes for asynchronous processing. Processes are designed to run logic concurrently, making them ideal for large-scale batch processing. Light Processes in particular, which can run up to 20 threads simultaneously. In contrast to timers, which are limited to 3. My rule of thumb is: use processes for record processing and timers for logic that needs to adhere to a schedule.
Timers have a standard timeout of 20 minutes, which can be adjusted, but it's best not to extend it significantly. Endless-running timers can clog up your timer schedule. Implement a timeout prevention mechanism to avoid losing work in progress:
1. Create a local variable named "TimeOut" with the data type "Date Time."
2. Assign the variable with "AddMinutes(CurrDate(), 15)" to leave some error margin to the default timeout.
3. With each record processed, check if the timer is still below its timeout by evaluating "CurrDateTime() < Timeout."
Avoid Repeating Work
Efficiency is compromised when a timer re-processes items. Prevent this by keeping track of completed items. You can achieve this by using attributes like "IsProcessed" or "FinishedAt" depending on the context of your processing. Alternatively, consider using a log entity to track completed items. Also, ensure that external systems do not modify data while the timer is running to prevent potential bugs.
Every timer must eventually come to an end. Be cautious not to create ever-running timers, especially now that you've implemented a timeout prevention mechanism. Validate your query when retrieving items to process, and ensure that completed items are tracked accordingly. Additionally, consider implementing a "KillSwitch" site property to abort timers that run indefinitely, thereby avoiding potential headaches.
Ensure Data Integrity
Errors should not disrupt your timer logic. Expect the unexpected and build proper error handling into your logic. To facilitate error management, extract your record processing logic into a separate action. Introduce an "Exception Handler" node to catch any errors that may occur. If you set the "Abort Transaction" property of this handler to False, the timer will continue to the next record, ensuring that a single error doesn't block the entire processing stack.
Also, consider using "CommitTransaction" to commit changes to the database after processing a sensible set of records (e.g., after every 100 records).
Now we've considered these best practices, here the gist of the whole flow:
To leverage the best of both worlds---timers and processes---you can explore a hybrid timer approach. Use the timer solely to schedule records that need processing. After retrieving the list of data, trigger a batch of processes either through "LaunchProcess" or by creating queue records. Allow the process logic to handle the rest, enabling you to run logic on a specific schedule while benefiting from the concurrency offered by processes.
By incorporating these best practices, you'll be able to harness the full potential of OutSystems timers while ensuring optimal performance and reliability in your applications. Happy coding!