web-dev-qa-db-fra.com

Comment annuler une transaction après expiration dans l'application de démarrage de printemps de la même manière que sur weblogic

Donc, dans mon application weblogic, vous utilisez un certain jtaWeblogicTransactionManager. Il existe un délai d'attente par défaut qui peut être remplacé dans l'annotation @Transactional(timeout = 60). J'ai créé une boucle infinie pour lire les données de la base de données qui a correctement dépassé le délai d'attente:

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)

maintenant, je voudrais faire le même comportement dans mon application de démarrage de printemps alors j'ai essayé ceci:

@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);
}

et 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

mais sans succès. Ma boucle à l'infini ne se termine jamais (j'attends environ 15 minutes, puis j'arrête mon application). La seule fois où j'ai vu une restauration, c’est lorsque j’ai essayé Thread.sleep et, après l’interruption du sommeil, que cette transaction a expiré, mais ce n’est pas ce que je voulais. Existe-t-il un moyen d'interrompre le processus après la temporisation (utiliser la temporisation dans les annotations ou utiliser la valeur par défaut) de la même manière que dans l'application weblogic?

METTRE &AGRAVE; JOUR

Je l'ai testé comme ça:

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

Quand j'active le débogage d'atomikos, je peux voir qu'il y a un avertissement pendant init et une minuterie d'atomikos:

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)

c'est peut-être la raison. Comment je peux résoudre ce problème? J'utilise Oracle 12 avec le pilote ojdbc8

MISE À JOUR 3

après avoir corrigé UPDATE2 pour accorder à l’utilisateur la permission de db, je peux voir dans le journal d’avertissement:

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.

le problème est que l'application lit toujours les données de la base de données après ce délai. Pourquoi n'est-il pas annulé?

MISE À JOUR 4

donc j'ai trouvé dans ActiveStateHandler quand timeout se produit il y a du code:

...

        setState ( TxState.ACTIVE );
...

et AtomikosConnectionProxy vérifie le délai d'attente de cette façon 

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");

alors pourquoi timeout est défini état qui ne cause pas exception dans AtomikosConnectionProxy?

MISE À JOUR 5

alors j'ai trouvé cette propriété

com.atomikos.icatch.threaded_2pc

va résoudre mon problème et maintenant il commence à revenir en arrière comme je le veux. Mais je ne comprends toujours pas pourquoi je devrais mettre ceci à vrai parce que maintenant je le teste sur une tâche qui devrait être exécutée en simple thread

9
hudi

définir com.atomikos.icatch.threaded_2pc=true dans jta.properties corrige mon problème. Idk pourquoi cette valeur par défaut était False dans l'application Web. 

 * @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.
2
hudi

Les transactions XA sont horriblement compliquées et vous voulez vraiment avoir une très bonne raison de les utiliser (c'est-à-dire qu'il est littéralement impossible d'ajouter un processus métier qui supprime le besoin de XA), car vous allez avoir des problèmes le sauvage...

Cela dit, j’imagine qu’il s’agit de différences de temporisation entre les phases XA.

Avec XA, il y a 2 délais: un délai pour la 1ère phase, appelé phase de vote (qui est généralement celui défini par l'annotation @Transactional, mais cela dépend du fournisseur JTA), et un autre délai pour la 2ème phase, appelé la phase de validation, qui prend généralement beaucoup plus de temps, car le gestionnaire de transactions a déjà obtenu l'accord de toutes les parties pour lui confirmer que la validation est prête, ce qui lui confère une marge de manœuvre accrue en cas de pannes de réseau transitoires, etc. 

Mon hypothèse est que WebLogic JTA se comporte simplement différemment d'Atomikos en ce qui concerne le traitement des notifications de la 2e phase par les participants, jusqu'à ce que atomikos soit modifié pour utiliser le multithread ack.

Si votre application ne concerne que vous et la base de données, vous pouvez probablement vous en tirer sans XA Transaction Manager. Je m'attendrais à ce que cela se comporte comme vous le souhaitez pour les délais d'attente.

Bonne chance! 

0
stringy05