3

I'm using Spring-tx-4.0.9 in a multithreaded application and have a problem with "stalled" transaction after the thread is killed by OutOfMemory.

Each thread ask PlatformTransactionManager (implementation org.springframework.jdbc.datasource.DataSourceTransactionManager) to get an transaction via TransactionTemplate Propagation is REQUIRED. This template has a timeout set. State of a transaction is managed by application and after all, work is done, then thread calls commit. This returns a transaction back to the PlatformTransactionManager, and almost every time releases connection to the DB (in DB, the session for this connection is mostly released)

But if the thread is killed by OutOfMemory, then the underlying transaction is returned to internal transaction pool and another thread can acquire this "broken" transaction. This transaction has the timeout set from the first time and cannot be reset even if new thread call TM to get transaction with new TransactionTemplate. So after this timeout expires, every request on transaction throws TransactionTimedOutException.

Only available methods on PlatformTransactionManager are getTransaction, commit and rollback. Method getTransaction can return new or previous transaction. From returned instance of TransactionStatus, I'm able to detect new or stalled transaction, but I'm unable to release it.

Method rollback sets flag isRollbackOnly, but the transaction is still not released and getTransaction returns stalled. If I then call commit as recommended by JavaDoc, then nothing changed and the transaction is still hanged.

If I use propagation REQUIRES_NEW, then "broken" transaction is suspended, which solve a problem with TransactionTimedOutException, but an old transaction is still hanged and holds the connection to the DB (unwanted state).

Is there a way, how to proper release a broken transaction (rollback and release connection)?

EDIT- Current code for getting a TransactionalStatus

public TransactionStatus beginTransaction() {
    // transaction begin
    logger.debug("Create new transaction.");
    TransactionTemplate template = new TransactionTemplate(transactionManager);
    template.setTimeout(txConnectionTimeout);

    TransactionStatus txStatus = transactionManager.getTransaction(template);
    logger.debug(
        "Transaction created. TransactionStatus: isCompleted=" + txStatus.isCompleted() + ", isNewTransaction=" +
        txStatus.isNewTransaction());

    return txStatus;
  }
  • 2
    Why are you managing transactions yourself? If you want that instead of starting/stopping transactions yourself at least use a `TransactionTemplate` instead of this, but the best solution would be to use `@Transactional` to manage your transactions (which has a lot of fallback etc. build in) instead of trying to reinvent all that logic. – M. Deinum May 30 '17 at 11:03
  • There are many reasons why application manages transaction manually, One for all is that user can specify if all thing he posted to the DB was only for "try" and after work every change is rollbacked. Using annotations is also "no-go". Rework of all logic won't be possible in production environment. – Otakar Křížek May 30 '17 at 11:08
  • You have to rework the logic to fix it... So if you cannot rework it how on earth are you going to fix it? Basically every place you are starting/stopping transactions yourself, you have to reimplement the logic as used in the `TransactionTemplate` / `TransactionInterceptor` with proper exception handling etc. \ – M. Deinum May 30 '17 at 11:11
  • Reason why complete rework of transaction logic is not posiblle is simply that there is not time nor money for it. I have to fix a legacy apllication developed by former employee who knows nothing about correct transactional behavior. – Otakar Křížek May 30 '17 at 11:19
  • 1
    What I don't get is you are using `TransactionTemplate` but still are messing around with the `PlatformTransactionManager` yourself... Why? The `TransactionTemplate` should handle it for you, either use `TransactionTemplate` or not, but you are now have competing mechanisms, which imho is the root of your problem. – M. Deinum May 30 '17 at 11:20
  • TransactionTemplate doesn't propagate changes in current transaction. JavaDoc snipet of getTransaction: Return a currently active transaction or create a new one, according to the specified propagation behavior. Note that parameters like isolation level or timeout will only be applied to new transactions, and thus be ignored when participating in active ones. – Otakar Křížek May 30 '17 at 11:27
  • Then you are using it in the wrong way. Post some code because your description isn't making things clearer. As mentioned don't use 2 competing mechanisms as that will complicate things and break things. – M. Deinum May 30 '17 at 11:29
  • Current code for getting transaction is inserted. BTW: Thank you for your time. – Otakar Křížek May 30 '17 at 11:37
  • You aren't really using the `TransactionTemplate` you are using it as a `DefaultTransactionDefinition` (which is what `TransactionTemplate` is extending. I would at least refactor your code to reflect that (and to avoid confusion). Can you show the code for the whole flow (start tx, do stuff, commit/rollback)? – M. Deinum May 30 '17 at 12:18
  • Whole flow cannot be shown due to complexity of current processes. But every transaction is managed by 3 methods: beginTransaction (method in main text), then commit which calls *transactionManager.commit(txStatus)* and rollback which calls *transactionManager.rollback(txStatus)*. – Otakar Křížek May 30 '17 at 14:59
  • Every DB call is through SimpleJdbcCall and main logic of application is in DB PLSQL, but java has to mantain a main transaction and in success scenario after application sends response and response was accepted, then commit can be executed. With "try" simulation java will commit partial data durring execution (reports, validation, logs) and rollback the rest. – Otakar Křížek May 30 '17 at 14:59
  • If i will have enough time to rewrite the logic, i will use autonomous transaction for different operation and logic of transaction will be maitained by DB, but for now i have no choice to only fix current code. – Otakar Křížek May 30 '17 at 15:02
  • For some reason that makes me shudder. I suggest you take a look at the `TransactionTemplate.execute` method, that is more or less what you need to mimic (catching exception and error and rollback that is what I suspect is missing). – M. Deinum May 30 '17 at 15:23

0 Answers0