Today we are going to talk about a very valuable feature available for Managed Backendless and Backendless Pro users called Low Priority Tasks. In this article, we’ll look at how it works and what is it best used for.
Backendless custom business logic (Cloud Code custom event handlers and custom API services) tasks are put into a single queue and executed by a dedicated service called CodeRunner. In Backendless Cloud, these tasks do not have any kind of priority and are executed according to the task’s position in the queue. But there are cases when the CodeRunner queue is spammed with “heavy” requests which take 10 or even 20 seconds to execute, i.e. getting hundreds or even thousands of records with multiple relations, utility requests to delete thousands of outdated records in a table, etc.
In such a scenario, all subsequent requests with higher priority such as the application’s core logic will be left hanging until all tasks in the queue finish. To resolve these problems, we will introduce you to Low Priority Tasks.
The main purpose of this feature is to keep a particular percentage of available “workers” inactive so tasks with higher priority can be executed while other “workers” are working on low priority tasks. Whenever tasks with higher priority appear in the queue, they are executed right away. Low priority tasks are executed only in the event that there are no tasks in the “main” queue left.
Please keep in mind that this feature works only in the Managed and Pro versions of Backendless. To get started, we’ll need to do the following:
Add the following line to coderunner.json config if you are using JavaScript cloud code:
"workers": { "cache": { "limit": 20 }, "concurrent": 20, "heartbeat": { "interval": 5, "timeout": 10 }, "lowPriorityThreshold": 0.8 },
Here, “limit” and “concurrent”: <VALUE> indicate the number of “workers”; in other words, the number of tasks CodeRunner can simultaneously work on. “lowPriorityThreshold” = 0.8 means that low priority executions can take only 80% of the worker pool. The rest will be reserved ONLY for the main queue. Note that if the queue of tasks includes only high priority ones, 100% of the worker pool can be used.
Add the following line to runner.properties config if you are using Java cloud code:
low_priority_queue.workers_threshold = 0.8 system.pool.core = 20
If you have your CodeRunner running already, you’ll need to restart it after changing the config file (runner.properties or coderunner.json).
That is all we need to do to enable support of low priority tasks on the server-side. Now I’m going to show how CodeRunner differentiates low priority tasks and tasks for the main queue. In order to request a low priority invocation, we need to specify this in a client-side request to a custom service and/or handler.
Here are examples of how it can be done for a custom event handler called ‘myCustomEvent’ :
POST http://<YOUR DOMAIN>/<APP ID>/<REST API Key>/servercode/events/myCustomEvent HTTP header: bl-execution-type: async-low-priority BODY: {"yourArgument":"value"}
HashMap args = new HashMap(); args.put( "weather", "sunny" ); Backendless.Events.dispatch( "myCustomEvent", args, ExecutionType.ASYNC_LOW_PRIORITY );
Backendless.Events.dispatch( "myCustomEvent", args, Backendless.BL.ExecutionTypes.ASYNC_LOW_PRIORITY );
ExecutionType is an enumeration consisting of ASYNC , SYNC and ASYNC_LOW_PRIORITY. ASYNC schedules invocation to the main queue and ASYNC_LOW_PRIORITY will schedule the task to low priority queue. The same will hold true for any service’s method.
Here are examples of how to invoke a method called testMethod in service called testService in low-priority mode:
POST http://<YOUR DOMAIN>/<APP ID>/<REST API Key>/services/testService/testMethod HTTP header: bl-execution-type: async-low-priority BODY: {"yourArgument":"value"}
HashMap args = new HashMap(); args.put( "weather", "sunny" ); Backendless.CustomService.invoke( "testService", "testMethod", args, ExecutionType.ASYNC_LOW_PRIORITY);
Backendless.CustomServices.invoke('testService','testMethod', Backendless.BL.ExecutionTypes.ASYNC_LOW_PRIORITY)
Keeping all these signatures in mind is not necessary because the client code required for invocation of your service can be auto-generated by Backendless for Android (Java), JS, Obj-C, and Swift:
All you’ll need to do is to add an additional argument – ExecutionType.ASYNC or ExecutionType.ASYNC_LOW_PRIORITY to the method’s signature.
Let’s see how this works on an example of a custom event handler. What you’ll need to do is:
Backendless.ServerCode.customEvent('myCustomEvent', async function(req) { console.log('my custom event handler') return new Promise(resolve => setTimeout(resolve, 1000)) });
As you can see it’s very straightforward – just waiting for 1 second before printing a log. This timeout is required to generate a live queue of tasks, so we could demonstrate the difference between how the CodeRunner service approaches low priority tasks and basic ASYNC tasks.
We hope this feature will speed up your application and make you forget about timeouts in business logic! If you have further questions, don’t hesitate to reach out to us on the support forum or our Slack channel.
Useful links:
Cloud Code for Java – https://backendless.com/docs/bl-java/index.html
Cloud Code for JS – https://backendless.com/docs/bl-js/index.html
JS script demonstrating how low priority tasks are handled (don’t forget to replace HOST, AppID and API key with your credentials): https://drive.google.com/file/d/1pfYHUqHgPJXmZTjec6Fn1JcAUXNsar1t/view?usp=sharing