cancel
Showing results for 
Search instead for 
Did you mean: 

Event thrown on entity not found.

mhanrahan
Confirmed Champ
Confirmed Champ
Hi,

In our system, we are hooking into the Activiti event system using a ActivitiEventHandler, detecting an ENTITY_CREATED for when a Task is created. We then pass a lightweight message containing the Task ID to a different part of our system (outside Activiti), which then will try to retrieve the full entity via either the REST API or from the TaskService (which amounts to the same thing).

The issue we're facing lies in the fact that once we have received the ENTITY_CREATED event and are trying to retrieve the Task, we get task not found exceptions.

I found this activiti forum topic https://forums.activiti.org/content/problem-using-event-handlers that describes the issue, that the TaskService loads from the database, and the task might not be persisted in the database at the time of the ENTITY_CREATED event.

So my questions are:
1. Am I missing something with regards to querying a task from outside of Activiti to get the 'current' versus the 'database' version of the task?
2. What was the rationale behind firing an event before it could be queried?
3. And is there any chance of changing this behaviour in the future?
4. If none of the above is possible, how would you suggest that I proceed?

Regards,
Michael
1 REPLY 1

gdharley
Elite Collaborator
Elite Collaborator

Hey Michael,

I think Frederik gave you a pretty good technical answer in the forum post you referenced.
It's really a matter of timing. The event is thrown inside the Entity persistence class, so it really depends on the performance of the database and the transaction boundaries as to how long it is before the database record is actually available.
Take the TaskEntity as an example:

TaskEntity.java
...
...
public void
insert(ExecutionEntity execution) {
  CommandContext commandContext = Context.getCommandContext();
  DbSqlSession dbSqlSession = commandContext.getDbSqlSession();
  dbSqlSession.insert(this);
 
  // Inherit tenant id (if applicable)
  if (execution != null && execution.getTenantId() != null) {
   setTenantId(execution.getTenantId());
  }
 
  if(execution != null) {
    execution.addTask(this);
  }
 
  commandContext.getHistoryManager().recordTaskCreated(this, execution);
 
  if(commandContext.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
   commandContext.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
         ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, this));
   commandContext.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
         ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_INITIALIZED, this));
  }
}
...
...

This is typical of the event emitter pattern throughout the engine.
Now, most of the time, the event data will be persisted externally, but from your description, you are simply storing the task ID and relying on a lookup you get the rest of the data. 

For most reporting style operations this shouldnt be a problem because the database transaction will be committed, however if you are making an immediate callback (if so, why not just take the data from the event body?) then you run the risk of a race condition.

Now to your specific questions.

1. The engine is stateless, meaning the state is only stored in the database. All REST calls will access the persistence layer.

2. Rationale - can't speak to that, but by including the content of the entity (in this case a task) in the event body, it would seem un-necessary to tightly couple the event to the DB persistence.

3. The beauty of open source is that everyone gets to have their say. If you want the behavior changed, open a jira and state your case. Better still, create your own implementation and open a pull request. This doesnt guarantee anything, but it gets a discussion started.

4. How to proceed, depends on the scenario. First off, don't assume the event and persistence are tightly coupled. As you have seen, they are not. If you need the task information immediately, then take it from the event body. One thing is for sure, the record will make it into the database, you you can add retry logic if you want (not a good idea if response time is critical).

Perhaps you can describe the interaction and timing so we may offer other options.

Thanks,
Greg