11

So in my weblogic application we are you using some jtaWeblogicTransactionManager. There is some default timeout which can be override in annotation @Transactional(timeout = 60). I created some infinity loop to read data from db which correctly timeout:

29 Apr 2018 20:44:55,458 WARN  [[ACTIVE] ExecuteThread: '9' for queue: 'weblogic.kernel.Default (self-tuning)'] org.springframework.jdbc.support.SQLErrorCodesFactory : Error while extracting database name - falli
ng back to empty error codes
org.springframework.jdbc.support.MetaDataAccessException: Error while extracting DatabaseMetaData; nested exception is java.sql.SQLException: Unexpected exception while enlisting XAConnection java.sql.SQLExceptio
n: Transaction rolled back: Transaction timed out after 240 seconds 
BEA1-2C705D7476A3E21D0AB1
        at weblogic.jdbc.jta.DataSource.enlist(DataSource.java:1760)
        at weblogic.jdbc.jta.DataSource.refreshXAConnAndEnlist(DataSource.java:1645)
        at weblogic.jdbc.wrapper.JTAConnection.getXAConn(JTAConnection.java:232)
        at weblogic.jdbc.wrapper.JTAConnection.checkConnection(JTAConnection.java:94)
        at weblogic.jdbc.wrapper.JTAConnection.checkConnection(JTAConnection.java:77)
        at weblogic.jdbc.wrapper.Connection.preInvocationHandler(Connection.java:107)
        at weblogic.jdbc.wrapper.Connection.getMetaData(Connection.java:560)
        at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:331)
        at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:366)
        at org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes(SQLErrorCodesFactory.java:212)
        at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.setDataSource(SQLErrorCodeSQLExceptionTranslator.java:134)
        at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.<init>(SQLErrorCodeSQLExceptionTranslator.java:97)
        at org.springframework.jdbc.support.JdbcAccessor.getExceptionTranslator(JdbcAccessor.java:99)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:655)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:690)

now I would like to make same behavior in my spring boot application so I tried this:

@EnableTransactionManagement
.
.
.

@Bean(name = "ds1")
@ConfigurationProperties(prefix = "datasource.ds1")
public DataSource logDataSource() {
    AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
    return ds;
}

@Bean(name = "ds2")
@ConfigurationProperties(prefix = "datasource.ds2")
public DataSource refDataSource() {
    AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
    return ds;
}

tm:

@Bean(name = "userTransaction")
public UserTransaction userTransaction() throws Throwable {
    UserTransactionImp userTransactionImp = new UserTransactionImp();
    userTransactionImp.setTransactionTimeout(120);
    return userTransactionImp;
}

@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable {
    UserTransactionManager userTransactionManager = new UserTransactionManager();
    userTransactionManager.setForceShutdown(false);
    userTransactionManager.setTransactionTimeout(120);
    return userTransactionManager;
}

@Bean(name = "transactionManager")
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
public JtaTransactionManager transactionManager() throws Throwable {
    UserTransaction userTransaction = userTransaction();
    TransactionManager atomikosTransactionManager = atomikosTransactionManager();
    return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}

and application.properties:

datasource.ref.xa-data-source-class-name=oracle.jdbc.xa.client.OracleXADataSource
datasource.ref.unique-resource-name=ref
datasource.ref.xa-properties.URL=jdbc:oracle:thin:@...
datasource.ref.xa-properties.user=...
#datasource.ref.xa-properties.databaseName=...
datasource.ref.password=301d24ae7d0d69614734a499df85f1e2
datasource.ref.test-query=SELECT 1 FROM DUAL
datasource.ref.max-pool-size=5

datasource.log.xa-data-source-class-name=oracle.jdbc.xa.client.OracleXADataSource
datasource.log.unique-resource-name=log
datasource.log.xa-properties.URL=jdbc:oracle:thin:@...
datasource.log.xa-properties.user=...
#datasource.log.xa-properties.databaseName=...
datasource.log.password=e58605c2a0b840b7c6d5b20b3692c5db
datasource.log.test-query=SELECT 1 FROM DUAL
datasource.log.max-pool-size=5

spring.jta.atomikos.properties.log-base-dir=target/transaction-logs/
spring.jta.enabled=true
spring.jta.atomikos.properties.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
spring.jta.atomikos.properties.max-timeout=600000
spring.jta.atomikos.properties.default-jta-timeout=10000
spring.transaction.default-timeout=900

but with no success. My infinity loop never ends (I wait about 15 minutes and then I stop my app). The only time when I saw rollback was when I tried Thread.sleep and after sleep this transaction timeout with rollback but this is not what I want to. So is there some way how to interrupt process after timeout(use timeout in annotation or use default) in same way how in my weblogic application ?

UPDATE

I tested it like this:

public class MyService {

public void customMethod(){

customDao.readSomething();

}

}

public class CustomDao {

@Transactional(timeout = 120)
public void readSomething()

while(true){
 //read data from db. app on weblogic throw timeout, spring boot app in docker did  nothing and after 15 I give it up and kill it
}
}

}

UPDATE2

When I turn on atomikos debug I can see there is warning during init and some atomikos timer:

2018-05-03 14:00:54.833 [main] WARN  c.a.r.xa.XaResourceRecoveryManager - Error while retrieving xids from resource - will retry later...
javax.transaction.xa.XAException: null
    at oracle.jdbc.xa.OracleXAResource.recover(OracleXAResource.java:730)
    at com.atomikos.datasource.xa.RecoveryScan.recoverXids(RecoveryScan.java:32)
    at com.atomikos.recovery.xa.XaResourceRecoveryManager.retrievePreparedXidsFromXaResource(XaResourceRecoveryManager.java:158)
    at com.atomikos.recovery.xa.XaResourceRecoveryManager.recover(XaResourceRecoveryManager.java:67)
    at com.atomikos.datasource.xa.XATransactionalResource.recover(XATransactionalResource.java:449)
    at com.atomikos.datasource.xa.XATransactionalResource.setRecoveryService(XATransactionalResource.java:416)
    at com.atomikos.icatch.config.Configuration.notifyAfterInit(Configuration.java:466)
    at com.atomikos.icatch.config.Configuration.init(Configuration.java:450)
    at com.atomikos.icatch.config.UserTransactionServiceImp.initialize(UserTransactionServiceImp.java:105)
    at com.atomikos.icatch.config.UserTransactionServiceImp.init(UserTransactionServiceImp.java:219)
    at com.atomikos.icatch.jta.UserTransactionImp.checkSetup(UserTransactionImp.java:59)
    at com.atomikos.icatch.jta.UserTransactionImp.setTransactionTimeout(UserTransactionImp.java:127)

maybe this is the reason. How I can fix this ? I am using oracle 12 with ojdbc8 driver

UPDATE 3

after fix UPDATE2 to grant user permission to db I can see in log warning:

2018-05-03 15:16:30.207 [Atomikos:4] WARN  c.a.icatch.imp.ActiveStateHandler - Transaction 127.0.1.1.tm152535336001600001 has timed out and will rollback.

problem is that app is still reading data from db after this timeout. Why it is not rollbacked ?

UPDATE 4

so I found in ActiveStateHandler when timeout occurs there is code:

...

        setState ( TxState.ACTIVE );
...

and AtomikosConnectionProxy is checking timeout this way

if ( ct.getState().equals(TxState.ACTIVE) ) ct.registerSynchronization(new JdbcRequeueSynchronization( this , ct ));
else AtomikosSQLException.throwAtomikosSQLException("The transaction has timed out - try increasing the timeout if needed");

so why timeout is set state which not cause exception in AtomikosConnectionProxy ?

UPDATE 5

so I found that property

com.atomikos.icatch.threaded_2pc

will solve my problem and now it starts rollback how I want. But I still dont understand why I should set this to true because now I am testing it on some task which should run in single thread

hudi
  • 15,555
  • 47
  • 142
  • 246
  • Maybe this https://stackoverflow.com/q/34496105/1140748 can help. – alain.janinm May 02 '18 at 09:24
  • hm I can try parameter oracle.jdbc.ReadTimeout=60000 as default but this still not solve my problem when I want to change timeout of some longer transaction in anotation – hudi May 02 '18 at 09:44
  • The ReadTimeout is for a single statement, it is basically a socket timeout and should always be specified or you will run into problems if there are network problems. Where do you loop? Do you perform a single statement that loops/sleeps in the database or do you perform multiple statements that each complete in reasonable time? – ewramner May 02 '18 at 10:57
  • I tested in some @Transactional method where I had infinity loop of reading data from db. Weblogic throw exception after time out but spring boot app did nothing in 15 minutes – hudi May 02 '18 at 11:11
  • Have you tried the `javax.transaction.Transactional`? According to the (Spring docs)[https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations] it should work. I am not super familiar with the latest Spring but previously it worked for me as I can remember. In case it solves the problem I'll post it as an answer. :) – Hash May 03 '18 at 07:00
  • lol yes all my method are annotated with annotation Transactional. And that is also part of my question. How to use timeout in Transactional anotation – hudi May 03 '18 at 08:15
  • With XA there are 2 timeouts - a timeout for the 1st phase (which is typically the one set by the @Transactional annotation) and another timeout for the 2nd phase (which used to default to 24 hours in WebLogic waaaaaay back in the day). – stringy05 May 09 '18 at 00:09

2 Answers2

3

set com.atomikos.icatch.threaded_2pc=true in jta.properties fixed my problem. Idk why this default value was change to false in web application.

 * @param single_threaded_2pc (!com.atomikos.icatch.threaded_2pc)
 *           If true then commit is done in the same thread as the one that
 *            started the tx.
hudi
  • 15,555
  • 47
  • 142
  • 246
1

XA transactions are horribly complicated and you really want to have a very good reason for using them (ie it's literally impossible to add some business process that removes the need for XA), because you are going to get into trouble out in the wild...

That said, My guess is that it's about timeout discrepancies between XA phases.

With XA there are 2 timeouts - a timeout for the 1st phase, known as the Voting phase (which is typically the one set by the @Transactional annotation, but this depends on the JTA provider) and another timeout for the 2nd phase, known as the commit phase, which is typically a lot longer, because the Transaction Manager has already got the agreement from all parties that the commit is ready to go, and therefore provide greater leeway for things like transient network failures and so on.

My guess is that the WebLogic JTA is simply behaving differently to Atomikos with how it's handling the 2nd phase notifications back from the participants, until atomikos is changed to use the multithreaded ack.

If you application is just you and the database, then you can probably get away without an XA Transaction Manager. I'd expect this would behave the way you want for timeouts.

Good Luck!

stringy05
  • 6,511
  • 32
  • 38