2
votes

I have a quarkus application with an async endpoint that creates an entity with default properties, starts a new thread within the request method and executes a long running job and then returns the entity as a response for the client to track.

@POST
@Transactional
public Response startJob(@NonNull JsonObject request) {
        // create my entity
        JobsRecord job = new JobsRecord();
        // set default properties
        job.setName(request.getString("name"));

        // make persistent
        jobsRepository.persist(job);
        
        // start the long running job on a different thread
        Executor.execute(() -> longRunning(job));

        return Response.accepted().entity(job).build();
    }

Additionally, the long running job will make updates to the entity as it runs and so it must also be transactional. However, the database entity just doesn't get updated.

These are the issues I am facing:

  1. I get the following warnings:
ARJUNA012094: Commit of action id 0:ffffc0a80065:f2db:5ef4e1c7:0 invoked while multiple threads active within it.
ARJUNA012107: CheckedAction::check - atomic action 0:ffffc0a80065:f2db:5ef4e1c7:0 commiting with 2 threads active!

Seems like something that should be avoided.

  1. I tried using @Transaction(value = TxType.REQUIRES_NEW) to no avail.

  2. I tried using the API Approach instead of the @Transactional approach on longRunning as mentioned in the guide as follows:

@Inject UserTransaction transaction;
.
.
.
try {
    transaction.begin();
    jobsRecord.setStatus("Complete");
    jobsRecord.setCompletedOn(new Timestamp(System.currentTimeMillis()));
    transaction.commit();
} catch (Exception e) {
    e.printStackTrace();
    transaction.rollback();
}

but then I get the errors: ARJUNA016051: thread is already associated with a transaction! and ARJUNA016079: Transaction rollback status is:ActionStatus.COMMITTED

  1. I tried both the declarative and API based methods again this time with context propagation enabled. But still no luck.

  2. Finally, based on the third approach, I thought keeping the @Transactional on the Http request handler and leaving longRunning as is without declarative or API based transaction approaches would work. However the database still does not get updated.

Clearly I am misunderstanding how JTA and context propagation works (among other things).

Is there a way (or even a design pattern) that allows me to update database entities asynchronously in a quarkus web application? Also why wouldn't any of the approaches I took have any effect?

Using quarkus 1.4.1.Final with ext: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, kubernetes-client, mutiny, narayana-jta, rest-client, resteasy, resteasy-jackson, resteasy-mutiny, smallrye-context-propagation, smallrye-health, smallrye-openapi, swagger-ui]

1

1 Answers

1
votes

You should return an async type from your JAX-RS resource method, the transaction context will then be available when the async stage executes. There is some relevant documentation in the quarkus guide on context propagation.

I would start by looking at the one of the reactive examples such as the getting started quickstart. Try annotating each resource endpoint with @Transactional and the async code will run with a transaction context.