JdbcEventStorageEngine.appendEvents will commit transaction instead of letting UnitOfWork do it

Hi,
I noticed that JdbcEventStorageEngine.appendEvents will commit transaction itself (by calling this.transactionManager.executeInTransaction).
Is there any reason it has to commit transaction itself instead of letting UnitOfWork commit transaction just once?

Hi,

we need to commit, because the JdbcEventStorageEngine cannot guarantee the UnitOfWork has a registered transaction.
However, if you provide a ‘UnitOfWorkConnectionProviderWrapper’ (name might be slightly different. Unable to check right now), it will prevent a commit and delay it until the unit of work is committed.

Hope this helps.

Cheers,

Allard

Hi,
Do you mean UnitOfWorkAwareConnectionProviderWrapper?
It seem’s it will reuse the connection from uow, but will still commit twice in my test.
JdbcEventStorageEngine will call transactionManager.executeInTransaction when inserting events, so it’ll always make the transaction manager committing the transaction if I understand this correctly.

Thanks.

@Override
public Connection getConnection() throws SQLException {
    if (!CurrentUnitOfWork.isStarted() || CurrentUnitOfWork.get().phase().isAfter(UnitOfWork.Phase.PREPARE_COMMIT)) {
        return delegate.getConnection();
    }

    UnitOfWork<?> uow = CurrentUnitOfWork.get();
    Connection connection = uow.root().getResource(CONNECTION_RESOURCE_NAME);
    if (connection == null || connection.isClosed()) {
        final Connection delegateConnection = delegate.getConnection();
        connection = ConnectionWrapperFactory.wrap(delegateConnection,
                                                   UoWAttachedConnection.class,
                                                   new UoWAttachedConnectionImpl(delegateConnection),
                                                   new ConnectionWrapperFactory.NoOpCloseHandler());
        uow.root().resources().put(CONNECTION_RESOURCE_NAME, connection);
        uow.onCommit(u -> {
            Connection cx = u.root().getResource(CONNECTION_RESOURCE_NAME);
            try {
                if (!cx.isClosed() && !cx.getAutoCommit()) {
                    cx.commit();
                }
            } catch (SQLException e) {
                throw new JdbcException("Unable to commit transaction", e);
            }
        });

Yes, that’s the one.

How did you observe the double commit?
The wrapper returns a version of a connection that wraps the actual connection and basically ignores requests to commit or close. When the Unit of Work completes, the commit and close actually happen.

Allard

We found this because when uow committing after command handling, it’ll get a “java.sql.SQLException: Could not commit with auto-commit set on” exception.
And it seems the NoOpCloseHandler passed to ConnectionWrapperFactory will still invoke the connection.commit()

Hi Tian,

I will attempt to reproduce this as soon as I get the chance.
Can you confirm that you’re using a SpringTransactionManager, and that the ConnectionProvidet are wrapped in a UnitOfWorkAware… and a SpringAwareConnectionProviderWrapper? (Can’t confirm the exact names of the classes, as I mid-air at the moment, sorry).

Cheers,

Allard

No, we are trying to use axon in a legacy project that doesn’t use spring.
I think project use spring won’t get this exception, but the reason is SpringTransactionManager.commitTransaction will check the TransactionStatus. The transaction got from TransactionManager was committed twice.

protected void commitTransaction(TransactionStatus status) {
if (status.isNewTransaction() && !status.isCompleted()) {
transactionManager.commit(status);
}
}

Thanks

Hi Tian,

my responses were misleading, sorry. I mistakenly assumed a commit was invoked on the connection.
What transaction manager are you using?
I assume you’ve built your own. In that case, you would want to check if a transaction is already started on a connection. If so, return a status that reflects this, so that you can skip the actual commit.

Hope this helps.
Cheers,
Allard

Hi Allard,
Right now we implement transaction manager this way, it works.
But I’m wondering if JdbcTokenStore aware of UOW, and UOW also know if it has a transaction manager is better. then the transaction manager is much simpler to implement and also less error-prone.
More importantly project can choose not to suppport nested transaction if they want to.

Thanks.
Best regards,