Async Process: How to do it?

Hi,

I need to run an async process that will take a given entity A and related entities B,C,D, etc… and perform a data processing task that will store data in a in-cubaschema-table although being this table unplugged from cuba. It’s merely a custom table for other reporting purposes, to speedup reporting.

What I need is that when an instance A is created, launch a separate and async process that won’t interact with CUBA GUI at any point. Only a couple of reports will take natively this data and put it into a JasperReport, but there won’t be any further interaction with CUBA GUI, so it is utterly a CUBA-disconnected table.

My question is, should I better approach this by programming a fully external process that reads the data from the db and populates this disconnected table? or can I trigger somehow this process inside cuba, once this A entity is stored? (entity events?). However it is important to know that the population of this disconnected table must be completely desynchronized from any CUBA GUI events, then there’s no interaction between CUBA GUI and this table data. So in theory it could be built 100% outside CUBA. However if build inside CUBA packages, the whole system will be easier to maintain.

Thanks for your insights.

Carlos.

Hi,

First - you definitely should use an EntityChangedEvent as a point of reaction to the A entity being changed.
https://doc.cuba-platform.com/manual-7.1/entityChangedEvent.html

Then, regarding the async process.
You have two options: passing a task to an asynchronous executor or using a “persistent work queue” pattern.

1 - asynchronous executor.
You can declare a Spring ThreadPoolTaskExecutor in your spring.xml file.
Then inject it to an event listener and pass work tasks to it. All tasks will be executed in a separate thread, avoiding blocking calling thread.

A usage example can be found in the CUBA sources, see com.haulmont.cuba.core.app.Emailer and its mailSendTaskExecutor.

2 - persistent work queue.
In this case you use a separate “WorkQueue” entity that is stored in the database. This entity should store A’s entity ID so that you know which entity instance you need to process later.
In the before-commit event listener you create and persist a WorkQueue entity that references modified A’s ID.
Then actual data processing is performed by a separate scheduled task that loads WorkQueue items from database and processes necessary data processing activities.
https://doc.cuba-platform.com/manual-7.1/scheduled_tasks_cuba.html
Scheduled tasks are executed in background, so no freezing of UI.

You can find a couple of persistent queue examples in the CUBA.
One is com.haulmont.cuba.core.entity.SendingMessage - it is both persisted and then processed by the com.haulmont.cuba.core.app.Emailer class.
Another example is in the FTS addon - com.haulmont.cuba.core.entity.FtsQueue entity.

Thanks Alex for your detailed explanations. Will try to approach the first proposal: asynchronous executor. It looks safer to me. In the second proposal, the work queue if I schedule the job prior to committing the transaction could lead to undesired effects in case the transaction didn’t succeed.

Since pretty much of the job is attained in a sql native manner, the task can be fully disconnected from any entity and merely query tables to find out further task details.

Will post back comments.

Thanks again,

Carlos.

@AlexBudarov in Asynchronous executor there is need for some kind of security validation if i want for example use datamanager? I am getting: SecurityException: No security context bound to the current thread

Take a look to the com.haulmont.cuba.core.app.Emailer class in the platform.

You need to wrap you code inside of the async task with Authentication.begin()/end() calls, because authentication information is bound to the execution thread, and asynchronous thread appears to be unauthenticated.
In the Emailer:

      Authentication authentication = AppBeans.get(Authentication.NAME);
      Emailer emailer = AppBeans.get(EmailerAPI.NAME);

      authentication.begin(emailer.getEmailerLogin());
      try {
           emailer.sendSendingMessage(sendingMessage);
       } finally {
           authentication.end();
       }

See also: System Authentication - CUBA Platform. Developer’s Manual

1 Like

@AlexBudarov and what is sugested way to debug jobs running in Executor?

Well, just use the built-in IDE debugger?
Or print log messages in the code.