Chunking and global report using JTA


If you have a batch processing records in chunks you probably want to keep track of what succeeds and fails. The issue is the commit is often done after your business code is executed which means you kind of loose track of what happens.

To solve it you can use TransactionSynchronizationRegistry and Synchronizations.

The idea is:

  • To associate one status to the transaction to be able to accumulate things during the transaction
  • To register a Synchronization to be able to know the transaction status

Here what it can look like:

private MyCounter getTxCounter(final MyReportBuilder builder) {
    return ofNullable(registry.getResource(MyCounter .class.getName()))
     .map(MyCounter .class::cast)
     .orElseGet(() -> {
        final MyCounter counter = new MyCounter ();

        // 1
        registry.putResource(MyCounter.class.getName(), counter);

        // 2
        registry.registerInterposedSynchronization(new Synchronization() {
            @Override
            public void beforeCompletion() {
                // no-op
            }

            @Override
            public void afterCompletion(final int status) {
                switch (status) {
                    case Status.STATUS_COMMITTED:
                        builder.success(counter);
                        break;
                    case Status.STATUS_ROLLEDBACK:
                        builder.failed(counter);
                        break;
                    default:
                        throw new IllegalStateException("unsupported status: " + status);
                }
            }
        });

        return counter;
    });
}
  1. we store once a counter which will allow us to accumulate a state in our business logic during one transaction.
  2. we register a Synchronization (transaction listener) and add to our global state (MyReportBuilder) the status of current transaction

Note: we ensure we register a single Synchronization using the counter registration as resource in the transaction registry (if there we don’t re-register a synchronization). It avoids us to get false reports.

Then in business code it looks like:

MyReportBuilder builder = new MyReportBuilder();

// we use a standard loop there but if you read previous post
// you can get it more fancy 😉
for (int i =0; i < 100; i++) {
   safeDoInTransaction(() -> {
       // ...
       // let assume we want to do 5 persists in this call
       getTxCounter(builder).addRecords(5);
   });
}

MyReport report = builder.build();
// N success, N failures...

With such a solution you can track very precisely the actions done or not. In previous sample we track only the number of records we worked on
which means in the report we’ll get the number of persist done and failed but in a real application you would track which one failed most of the time
to be able to give to your users some valuable information about the execution and potentially if you persist multiple kind of records how many suceeded/failed by type (for instance: 5 customers, 8 companies, 3 contracts…).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s