Transaction Listener captures completed activity instance execution before actual commit?

Looking at examples of transaction listener usage, the pattern seems to be to wait for the transaction to be committed and then perform a execution.getEngineServices…etc.
This seems to perform another DB query?

I am looking to capture the Duration of each activity, which i have working with code such as

        if (ExecutionListener.EVENTNAME_END.equals(execution.getEventName())){
            // Adds a transaction into the current transaction so we can grab the activity duration.
            Context.getCommandContext().getTransactionContext()
                    .addTransactionListener(TransactionState.COMMITTED, commandContext -> {
                    HistoricActivityInstance activity = execution.getProcessEngineServices()
                                .getHistoryService().createHistoricActivityInstanceQuery()
                                .activityInstanceId(activityInstanceId)
                                .processInstanceId(processInstanceId)
                                .singleResult();

                            long duration = activity.getDurationInMillis();
...

Is there a way to avoid the additional lookup, and pull the data from the transaction?

Hi Stephen,

The execution listener + transaction listener approach seems to become rather complicated. Maybe an easier approach is to implement a custom history handler (org.camunda.bpm.engine.impl.history.handler.CompositeDbHistoryEventHandler allows you to add another one on top of the default handler) that receives all activity-instance end events and then uses the duration.

Cheers,
Thorben

At the moment, the reason for the execution listener is because I had planned to implement the execution listener as a configurable option against the bpmn and a parse listener: so that someone can configure at a BPMN level, at a BPmn activity level, or a global engine level (through a yaml config), when to collect durations.

edit: bit more context: Thinking is that when Duration is collected, this is something you do not need for every activity, and only specific activities are relevant for duration collection. Given the BPMN is generally supposed to represent the business process, then the decision of which activities need to have duration monitoring would be defined at a BPMN level.

@thorben with a bit more google fu into the historical google group for camunda and some code docs, i saw that the cache ~“should” have the entity, and with some testing this seems to do the trick.

...
          Context.getCommandContext().getTransactionContext()
                    .addTransactionListener(TransactionState.COMMITTED, commandContext -> {

                        HistoricActivityInstanceEventEntity activity = commandContext.getDbEntityManager()
                                .getCachedEntity(HistoricActivityInstanceEventEntity.class, activityInstanceId);

                        long duration = activity.getDurationInMillis();
...

Thoughts on this @thorben?

Looks fine. Just be aware that internal API is not guaranteed to be stable.

1 Like

@thorben ya of course! All internal API ahahah.

Thanks! I have a similar problem but and this is super helpful and solved my problem, but I have a question. If I fire an event after the transaction has been committed, then what if the something fail in the transaction listener fail? Would it retry? Otherwise if it fails and the transaction is already committed it would be a problem?