You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Hugi Thordarson <hu...@karlmenn.is> on 2017/11/25 16:11:20 UTC

Exception/Transaction problems when performing DB-access in a thread inside a CommitLogListener — but only if using a 3rd party Connection Pool

Hi all.
I've been fighting a bug that's been a pain to replicate. Here's a small self-contained project that demonstrates the issue:

https://github.com/hugith/concurrencytest-simple/

Just run Main.java to see it happen:

https://github.com/hugith/concurrencytest-simple/blob/master/src/main/java/concurrencytest/Main.java

The subject basically says it all: If I touch the DB in a Thread inside a CommitLogListener AND am using a connection pool, it will fail with the Exception/trace shown below. But the real kicker: This only happens if I'm using a 3rd party connection pool (I've tried both HikariCP and c3p0 so I assume it's generic). If I just have Cayenne handle the DB connection for me, everything works fine.

I've been attempting to figure out what the issue is but I'm somewhat at a loss. Any ideas what might be happening?

Cheers,
- hugi

----------------------------------

java.lang.IllegalStateException: Transaction must have 'STATUS_ACTIVE' to add a connection. Current status: STATUS_COMMITTED
	at org.apache.cayenne.tx.BaseTransaction.connectionAdded(BaseTransaction.java:246)
	at org.apache.cayenne.tx.CayenneTransaction.connectionAdded(CayenneTransaction.java:49)
	at org.apache.cayenne.tx.BaseTransaction.addConnection(BaseTransaction.java:231)
	at org.apache.cayenne.tx.BaseTransaction.getOrCreateConnection(BaseTransaction.java:203)
	at org.apache.cayenne.access.DataNode$TransactionDataSource.getConnection(DataNode.java:446)
	at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:273)
	at org.apache.cayenne.access.DataDomainQueryAction.runQuery(DataDomainQueryAction.java:471)
	at org.apache.cayenne.access.DataDomainQueryAction.access$000(DataDomainQueryAction.java:72)
	at org.apache.cayenne.access.DataDomainQueryAction$2.perform(DataDomainQueryAction.java:446)
	at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
	at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:51)
	at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
	at org.apache.cayenne.access.DataDomainQueryAction.runQueryInTransaction(DataDomainQueryAction.java:443)
	at org.apache.cayenne.access.DataDomainQueryAction.execute(DataDomainQueryAction.java:122)
	at org.apache.cayenne.access.DataDomain.onQueryNoFilters(DataDomain.java:564)
	at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
	at org.apache.cayenne.commitlog.CommitLogFilter.onQuery(CommitLogFilter.java:61)
	at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
	at org.apache.cayenne.tx.TransactionFilter.onQuery(TransactionFilter.java:49)
	at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
	at org.apache.cayenne.access.DataDomain.onQuery(DataDomain.java:556)
	at org.apache.cayenne.util.ObjectContextQueryAction.runQuery(ObjectContextQueryAction.java:406)
	at org.apache.cayenne.util.ObjectContextQueryAction.executePostCache(ObjectContextQueryAction.java:107)
	at org.apache.cayenne.util.ObjectContextQueryAction.execute(ObjectContextQueryAction.java:94)
	at org.apache.cayenne.access.DataContext.onQuery(DataContext.java:965)
	at org.apache.cayenne.access.DataContext.performQuery(DataContext.java:954)
	at org.apache.cayenne.BaseContext.select(BaseContext.java:307)
	at org.apache.cayenne.query.FluentSelect.select(FluentSelect.java:157)
	at concurrencytest.Main$AfterUpdateListener.lambda$0(Main.java:61)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "afterUpdateThread" org.apache.cayenne.CayenneRuntimeException: [v.4.1.M1 Oct 06 2017 09:23:31] Global exception.
	at org.apache.cayenne.access.DataDomainQueryAction.nextGlobalException(DataDomainQueryAction.java:619)
	at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:282)
	at org.apache.cayenne.access.DataDomainQueryAction.runQuery(DataDomainQueryAction.java:471)
	at org.apache.cayenne.access.DataDomainQueryAction.access$000(DataDomainQueryAction.java:72)
	at org.apache.cayenne.access.DataDomainQueryAction$2.perform(DataDomainQueryAction.java:446)
	at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
	at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:51)
	at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
	at org.apache.cayenne.access.DataDomainQueryAction.runQueryInTransaction(DataDomainQueryAction.java:443)
	at org.apache.cayenne.access.DataDomainQueryAction.execute(DataDomainQueryAction.java:122)
	at org.apache.cayenne.access.DataDomain.onQueryNoFilters(DataDomain.java:564)
	at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
	at org.apache.cayenne.commitlog.CommitLogFilter.onQuery(CommitLogFilter.java:61)
	at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
	at org.apache.cayenne.tx.TransactionFilter.onQuery(TransactionFilter.java:49)
	at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
	at org.apache.cayenne.access.DataDomain.onQuery(DataDomain.java:556)
	at org.apache.cayenne.util.ObjectContextQueryAction.runQuery(ObjectContextQueryAction.java:406)
	at org.apache.cayenne.util.ObjectContextQueryAction.executePostCache(ObjectContextQueryAction.java:107)
	at org.apache.cayenne.util.ObjectContextQueryAction.execute(ObjectContextQueryAction.java:94)
	at org.apache.cayenne.access.DataContext.onQuery(DataContext.java:965)
	at org.apache.cayenne.access.DataContext.performQuery(DataContext.java:954)
	at org.apache.cayenne.BaseContext.select(BaseContext.java:307)
	at org.apache.cayenne.query.FluentSelect.select(FluentSelect.java:157)
	at concurrencytest.Main$AfterUpdateListener.lambda$0(Main.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Transaction must have 'STATUS_ACTIVE' to add a connection. Current status: STATUS_COMMITTED
	at org.apache.cayenne.tx.BaseTransaction.connectionAdded(BaseTransaction.java:246)
	at org.apache.cayenne.tx.CayenneTransaction.connectionAdded(CayenneTransaction.java:49)
	at org.apache.cayenne.tx.BaseTransaction.addConnection(BaseTransaction.java:231)
	at org.apache.cayenne.tx.BaseTransaction.getOrCreateConnection(BaseTransaction.java:203)
	at org.apache.cayenne.access.DataNode$TransactionDataSource.getConnection(DataNode.java:446)
	at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:273)
	... 24 more


Re: Exception/Transaction problems when performing DB-access in a thread inside a CommitLogListener — but only if using a 3rd party Connection Pool

Posted by Hugi Thordarson <hu...@karlmenn.is>.
Hi Nikita,
Thanks for replying. I just found out that for this specific case, CommitLogFilter has a nice little method called "excludeFromTransaction()" that handles exactly this case, i.e. makes onPostCommit do it's work inside it's own transaction. (boy, do I ever wish I knew this earlier)

But thanks for the code example, quite interesting and might be useful in a later situation!

Cheers,
- hugi




> On 27 Nov 2017, at 14:20, Nikita Timofeev <nt...@objectstyle.com> wrote:
> 
> Hi,
> 
> The problem seems in InheritableThreadLocal that stores current
> transaction [1]. When you start new Thread from the one that already
> have bound transaction it will grab that transaction. In your case
> this leads to exception as transaction already committed (exactly as
> error say).
> Probably we should rethink this behavior.
> Right now this logic is not configurable as it is completely static,
> so it can't be easily changed.
> 
> However you can try to manually start new transaction with a code
> similar to one found in DefaultTransactionManager [2].
> 
> Following method fixes your demo project:
> 
> static <T> T runInNewTransaction(TransactionalOperation<T> operation) {
>    TransactionFactory factory =
> serverRuntime().getInjector().getInstance(TransactionFactory.class);
> 
>    Transaction tx = factory.createTransaction();
>    Transaction oldTransaction = BaseTransaction.getThreadTransaction();
> 
>    BaseTransaction.bindThreadTransaction(tx);
>    try {
>        T result = operation.perform();
>        tx.commit();
>        return result;
>    } catch (CayenneRuntimeException ex) {
>        tx.setRollbackOnly();
>        throw ex;
>    } catch (Exception ex) {
>        tx.setRollbackOnly();
>        throw new CayenneRuntimeException(ex);
>    } finally {
>        BaseTransaction.bindThreadTransaction(oldTransaction);
> 
>        if (tx.isRollbackOnly()) {
>            try {
>                tx.rollback();
>            } catch (Exception e) {
>                //e.printStackTrace();
>            }
>        }
>    }
> }
> 
> ...
> 
> @Override
> public void onPostCommit( ObjectContext originatingContext, ChangeMap
> changes ) {
>    new Thread( () ->
>            runInNewTransaction(() -> {
>                serverRuntime().newContext().select( new
> SelectQuery<>( "Person" ) );
>                return null;
>        }), "afterUpdateThread" ).start();
> }
> 
> 
> [1] https://github.com/apache/cayenne/blob/bd1b109a943307a83078399c7a4d6aa53631a065/cayenne-server/src/main/java/org/apache/cayenne/tx/BaseTransaction.java#L41
> [2] https://github.com/apache/cayenne/blob/bd1b109a943307a83078399c7a4d6aa53631a065/cayenne-server/src/main/java/org/apache/cayenne/tx/DefaultTransactionManager.java#L55
> 
> On Sat, Nov 25, 2017 at 7:11 PM, Hugi Thordarson <hu...@karlmenn.is> wrote:
>> Hi all.
>> I've been fighting a bug that's been a pain to replicate. Here's a small self-contained project that demonstrates the issue:
>> 
>> https://github.com/hugith/concurrencytest-simple/
>> 
>> Just run Main.java to see it happen:
>> 
>> https://github.com/hugith/concurrencytest-simple/blob/master/src/main/java/concurrencytest/Main.java
>> 
>> The subject basically says it all: If I touch the DB in a Thread inside a CommitLogListener AND am using a connection pool, it will fail with the Exception/trace shown below. But the real kicker: This only happens if I'm using a 3rd party connection pool (I've tried both HikariCP and c3p0 so I assume it's generic). If I just have Cayenne handle the DB connection for me, everything works fine.
>> 
>> I've been attempting to figure out what the issue is but I'm somewhat at a loss. Any ideas what might be happening?
>> 
>> Cheers,
>> - hugi
>> 
>> ----------------------------------
>> 
>> java.lang.IllegalStateException: Transaction must have 'STATUS_ACTIVE' to add a connection. Current status: STATUS_COMMITTED
>>        at org.apache.cayenne.tx.BaseTransaction.connectionAdded(BaseTransaction.java:246)
>>        at org.apache.cayenne.tx.CayenneTransaction.connectionAdded(CayenneTransaction.java:49)
>>        at org.apache.cayenne.tx.BaseTransaction.addConnection(BaseTransaction.java:231)
>>        at org.apache.cayenne.tx.BaseTransaction.getOrCreateConnection(BaseTransaction.java:203)
>>        at org.apache.cayenne.access.DataNode$TransactionDataSource.getConnection(DataNode.java:446)
>>        at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:273)
>>        at org.apache.cayenne.access.DataDomainQueryAction.runQuery(DataDomainQueryAction.java:471)
>>        at org.apache.cayenne.access.DataDomainQueryAction.access$000(DataDomainQueryAction.java:72)
>>        at org.apache.cayenne.access.DataDomainQueryAction$2.perform(DataDomainQueryAction.java:446)
>>        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
>>        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:51)
>>        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
>>        at org.apache.cayenne.access.DataDomainQueryAction.runQueryInTransaction(DataDomainQueryAction.java:443)
>>        at org.apache.cayenne.access.DataDomainQueryAction.execute(DataDomainQueryAction.java:122)
>>        at org.apache.cayenne.access.DataDomain.onQueryNoFilters(DataDomain.java:564)
>>        at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>>        at org.apache.cayenne.commitlog.CommitLogFilter.onQuery(CommitLogFilter.java:61)
>>        at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>>        at org.apache.cayenne.tx.TransactionFilter.onQuery(TransactionFilter.java:49)
>>        at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>>        at org.apache.cayenne.access.DataDomain.onQuery(DataDomain.java:556)
>>        at org.apache.cayenne.util.ObjectContextQueryAction.runQuery(ObjectContextQueryAction.java:406)
>>        at org.apache.cayenne.util.ObjectContextQueryAction.executePostCache(ObjectContextQueryAction.java:107)
>>        at org.apache.cayenne.util.ObjectContextQueryAction.execute(ObjectContextQueryAction.java:94)
>>        at org.apache.cayenne.access.DataContext.onQuery(DataContext.java:965)
>>        at org.apache.cayenne.access.DataContext.performQuery(DataContext.java:954)
>>        at org.apache.cayenne.BaseContext.select(BaseContext.java:307)
>>        at org.apache.cayenne.query.FluentSelect.select(FluentSelect.java:157)
>>        at concurrencytest.Main$AfterUpdateListener.lambda$0(Main.java:61)
>>        at java.lang.Thread.run(Thread.java:748)
>> Exception in thread "afterUpdateThread" org.apache.cayenne.CayenneRuntimeException: [v.4.1.M1 Oct 06 2017 09:23:31] Global exception.
>>        at org.apache.cayenne.access.DataDomainQueryAction.nextGlobalException(DataDomainQueryAction.java:619)
>>        at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:282)
>>        at org.apache.cayenne.access.DataDomainQueryAction.runQuery(DataDomainQueryAction.java:471)
>>        at org.apache.cayenne.access.DataDomainQueryAction.access$000(DataDomainQueryAction.java:72)
>>        at org.apache.cayenne.access.DataDomainQueryAction$2.perform(DataDomainQueryAction.java:446)
>>        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
>>        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:51)
>>        at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
>>        at org.apache.cayenne.access.DataDomainQueryAction.runQueryInTransaction(DataDomainQueryAction.java:443)
>>        at org.apache.cayenne.access.DataDomainQueryAction.execute(DataDomainQueryAction.java:122)
>>        at org.apache.cayenne.access.DataDomain.onQueryNoFilters(DataDomain.java:564)
>>        at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>>        at org.apache.cayenne.commitlog.CommitLogFilter.onQuery(CommitLogFilter.java:61)
>>        at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>>        at org.apache.cayenne.tx.TransactionFilter.onQuery(TransactionFilter.java:49)
>>        at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>>        at org.apache.cayenne.access.DataDomain.onQuery(DataDomain.java:556)
>>        at org.apache.cayenne.util.ObjectContextQueryAction.runQuery(ObjectContextQueryAction.java:406)
>>        at org.apache.cayenne.util.ObjectContextQueryAction.executePostCache(ObjectContextQueryAction.java:107)
>>        at org.apache.cayenne.util.ObjectContextQueryAction.execute(ObjectContextQueryAction.java:94)
>>        at org.apache.cayenne.access.DataContext.onQuery(DataContext.java:965)
>>        at org.apache.cayenne.access.DataContext.performQuery(DataContext.java:954)
>>        at org.apache.cayenne.BaseContext.select(BaseContext.java:307)
>>        at org.apache.cayenne.query.FluentSelect.select(FluentSelect.java:157)
>>        at concurrencytest.Main$AfterUpdateListener.lambda$0(Main.java:61)
>>        at java.lang.Thread.run(Thread.java:748)
>> Caused by: java.lang.IllegalStateException: Transaction must have 'STATUS_ACTIVE' to add a connection. Current status: STATUS_COMMITTED
>>        at org.apache.cayenne.tx.BaseTransaction.connectionAdded(BaseTransaction.java:246)
>>        at org.apache.cayenne.tx.CayenneTransaction.connectionAdded(CayenneTransaction.java:49)
>>        at org.apache.cayenne.tx.BaseTransaction.addConnection(BaseTransaction.java:231)
>>        at org.apache.cayenne.tx.BaseTransaction.getOrCreateConnection(BaseTransaction.java:203)
>>        at org.apache.cayenne.access.DataNode$TransactionDataSource.getConnection(DataNode.java:446)
>>        at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:273)
>>        ... 24 more
>> 
> 
> 
> 
> -- 
> Best regards,
> Nikita Timofeev


Re: Exception/Transaction problems when performing DB-access in a thread inside a CommitLogListener — but only if using a 3rd party Connection Pool

Posted by Nikita Timofeev <nt...@objectstyle.com>.
Hi,

The problem seems in InheritableThreadLocal that stores current
transaction [1]. When you start new Thread from the one that already
have bound transaction it will grab that transaction. In your case
this leads to exception as transaction already committed (exactly as
error say).
Probably we should rethink this behavior.
Right now this logic is not configurable as it is completely static,
so it can't be easily changed.

However you can try to manually start new transaction with a code
similar to one found in DefaultTransactionManager [2].

Following method fixes your demo project:

static <T> T runInNewTransaction(TransactionalOperation<T> operation) {
    TransactionFactory factory =
serverRuntime().getInjector().getInstance(TransactionFactory.class);

    Transaction tx = factory.createTransaction();
    Transaction oldTransaction = BaseTransaction.getThreadTransaction();

    BaseTransaction.bindThreadTransaction(tx);
    try {
        T result = operation.perform();
        tx.commit();
        return result;
    } catch (CayenneRuntimeException ex) {
        tx.setRollbackOnly();
        throw ex;
    } catch (Exception ex) {
        tx.setRollbackOnly();
        throw new CayenneRuntimeException(ex);
    } finally {
        BaseTransaction.bindThreadTransaction(oldTransaction);

        if (tx.isRollbackOnly()) {
            try {
                tx.rollback();
            } catch (Exception e) {
                //e.printStackTrace();
            }
        }
    }
}

...

@Override
public void onPostCommit( ObjectContext originatingContext, ChangeMap
changes ) {
    new Thread( () ->
            runInNewTransaction(() -> {
                serverRuntime().newContext().select( new
SelectQuery<>( "Person" ) );
                return null;
        }), "afterUpdateThread" ).start();
}


[1] https://github.com/apache/cayenne/blob/bd1b109a943307a83078399c7a4d6aa53631a065/cayenne-server/src/main/java/org/apache/cayenne/tx/BaseTransaction.java#L41
[2] https://github.com/apache/cayenne/blob/bd1b109a943307a83078399c7a4d6aa53631a065/cayenne-server/src/main/java/org/apache/cayenne/tx/DefaultTransactionManager.java#L55

On Sat, Nov 25, 2017 at 7:11 PM, Hugi Thordarson <hu...@karlmenn.is> wrote:
> Hi all.
> I've been fighting a bug that's been a pain to replicate. Here's a small self-contained project that demonstrates the issue:
>
> https://github.com/hugith/concurrencytest-simple/
>
> Just run Main.java to see it happen:
>
> https://github.com/hugith/concurrencytest-simple/blob/master/src/main/java/concurrencytest/Main.java
>
> The subject basically says it all: If I touch the DB in a Thread inside a CommitLogListener AND am using a connection pool, it will fail with the Exception/trace shown below. But the real kicker: This only happens if I'm using a 3rd party connection pool (I've tried both HikariCP and c3p0 so I assume it's generic). If I just have Cayenne handle the DB connection for me, everything works fine.
>
> I've been attempting to figure out what the issue is but I'm somewhat at a loss. Any ideas what might be happening?
>
> Cheers,
> - hugi
>
> ----------------------------------
>
> java.lang.IllegalStateException: Transaction must have 'STATUS_ACTIVE' to add a connection. Current status: STATUS_COMMITTED
>         at org.apache.cayenne.tx.BaseTransaction.connectionAdded(BaseTransaction.java:246)
>         at org.apache.cayenne.tx.CayenneTransaction.connectionAdded(CayenneTransaction.java:49)
>         at org.apache.cayenne.tx.BaseTransaction.addConnection(BaseTransaction.java:231)
>         at org.apache.cayenne.tx.BaseTransaction.getOrCreateConnection(BaseTransaction.java:203)
>         at org.apache.cayenne.access.DataNode$TransactionDataSource.getConnection(DataNode.java:446)
>         at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:273)
>         at org.apache.cayenne.access.DataDomainQueryAction.runQuery(DataDomainQueryAction.java:471)
>         at org.apache.cayenne.access.DataDomainQueryAction.access$000(DataDomainQueryAction.java:72)
>         at org.apache.cayenne.access.DataDomainQueryAction$2.perform(DataDomainQueryAction.java:446)
>         at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
>         at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:51)
>         at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
>         at org.apache.cayenne.access.DataDomainQueryAction.runQueryInTransaction(DataDomainQueryAction.java:443)
>         at org.apache.cayenne.access.DataDomainQueryAction.execute(DataDomainQueryAction.java:122)
>         at org.apache.cayenne.access.DataDomain.onQueryNoFilters(DataDomain.java:564)
>         at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>         at org.apache.cayenne.commitlog.CommitLogFilter.onQuery(CommitLogFilter.java:61)
>         at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>         at org.apache.cayenne.tx.TransactionFilter.onQuery(TransactionFilter.java:49)
>         at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>         at org.apache.cayenne.access.DataDomain.onQuery(DataDomain.java:556)
>         at org.apache.cayenne.util.ObjectContextQueryAction.runQuery(ObjectContextQueryAction.java:406)
>         at org.apache.cayenne.util.ObjectContextQueryAction.executePostCache(ObjectContextQueryAction.java:107)
>         at org.apache.cayenne.util.ObjectContextQueryAction.execute(ObjectContextQueryAction.java:94)
>         at org.apache.cayenne.access.DataContext.onQuery(DataContext.java:965)
>         at org.apache.cayenne.access.DataContext.performQuery(DataContext.java:954)
>         at org.apache.cayenne.BaseContext.select(BaseContext.java:307)
>         at org.apache.cayenne.query.FluentSelect.select(FluentSelect.java:157)
>         at concurrencytest.Main$AfterUpdateListener.lambda$0(Main.java:61)
>         at java.lang.Thread.run(Thread.java:748)
> Exception in thread "afterUpdateThread" org.apache.cayenne.CayenneRuntimeException: [v.4.1.M1 Oct 06 2017 09:23:31] Global exception.
>         at org.apache.cayenne.access.DataDomainQueryAction.nextGlobalException(DataDomainQueryAction.java:619)
>         at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:282)
>         at org.apache.cayenne.access.DataDomainQueryAction.runQuery(DataDomainQueryAction.java:471)
>         at org.apache.cayenne.access.DataDomainQueryAction.access$000(DataDomainQueryAction.java:72)
>         at org.apache.cayenne.access.DataDomainQueryAction$2.perform(DataDomainQueryAction.java:446)
>         at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:87)
>         at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:51)
>         at org.apache.cayenne.tx.DefaultTransactionManager.performInTransaction(DefaultTransactionManager.java:40)
>         at org.apache.cayenne.access.DataDomainQueryAction.runQueryInTransaction(DataDomainQueryAction.java:443)
>         at org.apache.cayenne.access.DataDomainQueryAction.execute(DataDomainQueryAction.java:122)
>         at org.apache.cayenne.access.DataDomain.onQueryNoFilters(DataDomain.java:564)
>         at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>         at org.apache.cayenne.commitlog.CommitLogFilter.onQuery(CommitLogFilter.java:61)
>         at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>         at org.apache.cayenne.tx.TransactionFilter.onQuery(TransactionFilter.java:49)
>         at org.apache.cayenne.access.DataDomain$DataDomainQueryFilterChain.onQuery(DataDomain.java:748)
>         at org.apache.cayenne.access.DataDomain.onQuery(DataDomain.java:556)
>         at org.apache.cayenne.util.ObjectContextQueryAction.runQuery(ObjectContextQueryAction.java:406)
>         at org.apache.cayenne.util.ObjectContextQueryAction.executePostCache(ObjectContextQueryAction.java:107)
>         at org.apache.cayenne.util.ObjectContextQueryAction.execute(ObjectContextQueryAction.java:94)
>         at org.apache.cayenne.access.DataContext.onQuery(DataContext.java:965)
>         at org.apache.cayenne.access.DataContext.performQuery(DataContext.java:954)
>         at org.apache.cayenne.BaseContext.select(BaseContext.java:307)
>         at org.apache.cayenne.query.FluentSelect.select(FluentSelect.java:157)
>         at concurrencytest.Main$AfterUpdateListener.lambda$0(Main.java:61)
>         at java.lang.Thread.run(Thread.java:748)
> Caused by: java.lang.IllegalStateException: Transaction must have 'STATUS_ACTIVE' to add a connection. Current status: STATUS_COMMITTED
>         at org.apache.cayenne.tx.BaseTransaction.connectionAdded(BaseTransaction.java:246)
>         at org.apache.cayenne.tx.CayenneTransaction.connectionAdded(CayenneTransaction.java:49)
>         at org.apache.cayenne.tx.BaseTransaction.addConnection(BaseTransaction.java:231)
>         at org.apache.cayenne.tx.BaseTransaction.getOrCreateConnection(BaseTransaction.java:203)
>         at org.apache.cayenne.access.DataNode$TransactionDataSource.getConnection(DataNode.java:446)
>         at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:273)
>         ... 24 more
>



-- 
Best regards,
Nikita Timofeev