백엔드 개발을 할때 하나의 DB 가 아닌 여러 DB를 활용해서 개발을 해야 할 경우가 있을 수 있다. 여러 DB에 데이터를 write 할 경우에 데이터 정합성을 위한 transaction 관리가 쉽지 않을텐데 Spring boot 3.0대 최신 버전도 지원하는 atomikos 라이브러리를 활용해보면 여러 DB write의 경우에도 Spring의 @Transactional 어노테이션으로 간단히 트랜잭션 설정을 할 수 가 있다.
atomikos 공식 사이트의 주소는 아래와 같다
https://www.atomikos.com/Main/TransactionsEssentials
위 주소를 방문하면 essential 버전과 extream 버전 2가지가 있는데 essential 버전이 오픈소스 라이센스 free 버전이다. 홈페이지를 잘 읽어보면 maven등에서 라이브러리를 받고 배포판에 적혀있는 서명이 손상되지 않으면 free 라이센스로 활용가능하다고 되어 있다. 물론 extream 버전은 더 많은 기술지원과 자료를 받아볼 수 있지만 유료이다.
Spring boot 설정
pom.xml에 아래 maven 라이브러리를 다운로드 받는다 6.0.0 버전이 spring boot 3.0 이상을 지원하는 버전이다.
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-spring-boot3-starter</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
<version>2.0.1</version>
</dependency>
XaData 설정 Config class를 다음과 같이 작성 한다. JtaTransactionManager가 멀티DB의 commit/rollback을 관장하게 된다.
@Configuration
@EnableTransactionManagement
public class XaDataSourceConfig {
public static final String TRANSACTION_MANAGER_BEAN_NAME = "jtaTransactionManager";
@Bean
public UserTransactionManager userTransactionManager() throws SystemException {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setTransactionTimeout(1000);
userTransactionManager.setForceShutdown(true);
return userTransactionManager;
}
@Bean
public UserTransaction userTransaction() throws SystemException {
UserTransaction userTransaction = new UserTransactionImp();
userTransaction.setTransactionTimeout(60000);
return userTransaction;
}
@Primary
@Bean(name = TRANSACTION_MANAGER_BEAN_NAME)
public JtaTransactionManager jtaTransactionManager(UserTransactionManager userTransactionManager, UserTransaction userTransaction) {
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setTransactionManager(userTransactionManager);
jtaTransactionManager.setUserTransaction(userTransaction);
return jtaTransactionManager;
}
DB 별 DataSource를 작성한다. atomikos 라이브러리를 사용하려면 아래와 같이 DataSource 생성 시 AtomikosDataSourceBean 객체를 활용해 Source를 작성하면 된다.
또한 Vendor별 드라이버 class는 XA spec을 지원하는 아래 class들을 사용하면 된다.
Postgresql -> org.postgresql.xa.PGXADataSource
Oracle -> oracle.jdbc.xa.client.OracleXADataSource
Postgresql 용 DataSourceConfiguration 예시
@Configuration
@MapperScan(annotationClass = Mapper.class, basePackages = {"com"}, sqlSessionFactoryRef = "priSqlSessionFactory")
@EnableTransactionManagement
public class DatasourceConfigurationForPri {
@Value("${spring.mybatis.common.config}")
private String mybatisConfig;
@Value("${spring.mybatis.common.mapper}")
private String mybatisMapper;
@Autowired
ApplicationContext applicationContext;
@Bean
@ConfigurationProperties(prefix = "spring.datasource.primary.hikari")
public DataSourceProperty dataSourcePriProperty() {
return new DataSourceProperty();
}
@Lazy
@Bean(name = "datasourcePri", destroyMethod = "close")
public DataSource dataSourcePri() {
DataSourceProperty dataSourceProperty = dataSourcePriProperty();
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setUniqueResourceName("datasourcePri");
ds.setXaDataSourceClassName("org.postgresql.xa.PGXADataSource");
ds.setMinPoolSize(10);
ds.setMaxPoolSize(dataSourceProperty.getMaximumpoolsize());
if (dataSourceProperty.getBorrowConnectionTimeout() != null) {
ds.setBorrowConnectionTimeout(dataSourceProperty.getBorrowConnectionTimeout());
}
PGXADataSource pgxaDataSource = new PGXADataSource();
pgxaDataSource.setUrl(dataSourceProperty.getJdbcUrl());
pgxaDataSource.setUser(dataSourceProperty.getUsername());
pgxaDataSource.setPassword(dataSourceProperty.getPassword());
ds.setXaDataSource(pgxaDataSource);
return ds;
}
@Bean(name = "priSqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier("datasourcePri") DataSource dataSourcePri) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSourcePri);
sessionFactory.setConfigLocation(applicationContext.getResource(mybatisConfig));
sessionFactory.setMapperLocations(applicationContext.getResources(mybatisMapper));
VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties properties = new Properties();
properties.putAll(ImmutableMap.of(
"PostgresQL", "postgresql"
));
databaseIdProvider.setProperties(properties);
sessionFactory.setDatabaseIdProvider(databaseIdProvider);
return sessionFactory.getObject();
}
@Bean(name = "sqlSessionTemplateForPri")
public SqlSessionTemplate sqlSessionTemplateForPri(SqlSessionFactory sqlSessionFactoryForPri) {
return new SqlSessionTemplate(sqlSessionFactoryForPri);
}
}
Oracle용 DataSourceConfiguration 예시
@Configuration
@MapperScan(value = "com.sung.land.iab.job.*.oraclemapper", sqlSessionFactoryRef = "oracleSqlSessionFactory")
@EnableTransactionManagement
public class DatasourceConfigurationForOracle {
@Value("${spring.mybatis.common.config}")
private String mybatisConfig;
@Value("${spring.mybatis.pops.mapper}")
private String mybatisMapperForPops;
@Autowired
ApplicationContext applicationContext;
@Bean
@ConfigurationProperties(prefix = "spring.datasource.oracle.hikari")
public DataSourceProperty dataSourceOracleProperty() {
return new DataSourceProperty();
}
@Lazy
@Bean(name = "dataSourceForOracle", destroyMethod = "close")
public DataSource dataSourceForOracle() throws SQLException {
DataSourceProperty dataSourceProperty = dataSourceOracleProperty();
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setUniqueResourceName("datasourceOracle");
ds.setXaDataSourceClassName("oracle.jdbc.xa.client.OracleXADataSource");
ds.setMinPoolSize(10);
ds.setMaxPoolSize(dataSourceProperty.getMaximumpoolsize());
if (dataSourceProperty.getBorrowConnectionTimeout() != null) {
ds.setBorrowConnectionTimeout(dataSourceProperty.getBorrowConnectionTimeout());
}
OracleXADataSource oracleXADataSource = new OracleXADataSource();
oracleXADataSource.setURL(dataSourceProperty.getJdbcUrl());
oracleXADataSource.setUser(dataSourceProperty.getUsername());
oracleXADataSource.setPassword(dataSourceProperty.getPassword());
ds.setXaDataSource(oracleXADataSource);
return ds;
}
@Bean(name = "oracleSqlSessionFactory")
public SqlSessionFactory popsSqlSessionFactory(@Qualifier("dataSourceForOracle") DataSource dataSourceForPops) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSourceForPops);
sessionFactory.setConfigLocation(applicationContext.getResource(mybatisConfig));
sessionFactory.setMapperLocations(applicationContext.getResources(mybatisMapperForPops));
VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties properties = new Properties();
properties.putAll(ImmutableMap.of(
"Oracle", "oracle"
));
databaseIdProvider.setProperties(properties);
sessionFactory.setDatabaseIdProvider(databaseIdProvider);
return sessionFactory.getObject();
}
@Bean(name = "sqlSessionTemplateForOracle")
public SqlSessionTemplate sqlSessionTemplateForPops(SqlSessionFactory sqlSessionFactoryForOracle) {
return new SqlSessionTemplate(sqlSessionFactoryForOracle);
}
}
실제로 멀티 DB를 사용하는 메소드에 @Transactional 어노테이션을 추가하여 Transaction 처리가 되도록 한다.
아래 예시는 Quartz 배치에서 로그 insert시 postgresql, oracle 2개 DB에 insert되도록 하였고 이들은 Transactional로 묶이도록 하였다.
Spring Transactional 예시
@Transactional
@Override
public void insertBatchLog(JobExecutionContext context, BatchLog batchLog) {
String schedName = null;
try {
schedName = this.schedulerFactoryBean.getScheduler().getSchedulerName();
} catch (SchedulerException e) {
schedName = "Unknown";
}
String jobName = context.getJobDetail().getKey().getName();
String trigger = context.getTrigger().getKey().getName();
String instance = null;
try {
instance = context.getScheduler().getSchedulerInstanceId();
} catch (SchedulerException e) {
instance = "Unknown";
}
batchLog.setSchedName(schedName);
batchLog.setInstanceId(instance);
batchLog.setJobName(jobName);
batchLog.setTriggerName(trigger);
postgresqlMapper.insertBatchLog(batchLog);
oracleMapper.insertBatchLog(batchLog);
}
DB 별 XA 설정
Atomikos 라이브러리 설정을 마치고 Spring boot transactional 설정을 마치고 실행을 해보면 postgresql의 경우 아래 에러가 발생을 한다.
Caused by: org.postgresql.util.PSQLException: ERROR: prepared transactions are disabled
Hint: Set max_prepared_transactions to a nonzero value.
org.postgresql.xa.PGXAException: Error preparing transaction. prepare xid=XID: 3137322E33302E312E33302E746D313730333033313938303037363030303032:3137322E33302E312E33302E746D33
at org.postgresql.xa.PGXAConnection.prepare(PGXAConnection.java:365)
at com.atomikos.datasource.xa.XAResourceTransaction.prepare(XAResourceTransaction.java:304)
at com.atomikos.icatch.imp.PrepareMessage.send(PrepareMessage.java:40)
at com.atomikos.icatch.imp.PrepareMessage.send(PrepareMessage.java:19)
at com.atomikos.icatch.imp.PropagationMessage.submit(PropagationMessage.java:67)
at com.atomikos.icatch.imp.Propagator$PropagatorThread.run(Propagator.java:63)
at com.atomikos.icatch.imp.Propagator.submitPropagationMessage(Propagator.java:42)
at com.atomikos.icatch.imp.ActiveStateHandler.prepare(ActiveStateHandler.java:172)
at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:522)
at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:681)
at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:279)
at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:178)
at com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:428)
at com.atomikos.icatch.jta.UserTransactionImp.commit(UserTransactionImp.java:86)
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1026)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:660)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:410)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
at com.sung.land.iab.service.impl.BatchService$$SpringCGLIB$$0.insertBatchLog(<generated>)
at com.sung.land.iab.job.test.service.impl.TestService.processTestExecution(TestService.java:28)
at com.sung.land.iab.job.test.TestJob.executeInternal(TestJob.java:16)
at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: org.postgresql.util.PSQLException: ERROR: prepared transactions are disabled
Hint: Set max_prepared_transactions to a nonzero value.
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2676)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2366)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:356)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:496)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:413)
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:333)
at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:319)
at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:295)
at org.postgresql.jdbc.PgStatement.executeUpdate(PgStatement.java:268)
at org.postgresql.xa.PGXAConnection.prepare(PGXAConnection.java:357)
... 28 common frames omitted
Postgresql에서 XA 2phase commit을 사용하기 위해서는 postgresql.conf의 max_prepared_transactions 값을 0 이상으로 올려주저야 한다. 위 hint에서 정확하게 알려주고 있다.
필자는 Synology docker를 활용해 postgresql 15 버전을 사용하고 있다. 위 max_prepared_transactions 를 변경해서 적용하기 위해 아래 docker container 항목에 진입한다.
Details -> Terminal에서 bash 쉘을 create하고 postgresql.conf 설정을 변경한다. docker 이미지에서 conf 위치는 /var/lib/postgresql/data/postgresql.conf 이다.
기본적으로 docker image에 vi가 설치되어 있지 않아서 apt-get으로 다운로드 받는다.
vi 에디터를 설치하고 conf 파일을 열어보면 max_prepared_transactions 항목이 0으로 되어있는것을 볼 수 있다. 값을 0이상으로 올려준다.
DB 설정 후 재기동 한후 DB client tool에서 pg_settings 테이블 값을 읽어보면 변경한 설정이 정상 적용된 것을 볼 수 있다.
Oracle XA 설정
Oracle DB에서도 XA를 사용하기 위해서는 아래 조회 권한이 필요한다.
grant select on pending_trans$ to user;
grant select on dba_2pc_pending to user;
grant select on dba_pending_transactions to user;
필자는 Synology docker를 사용해 oracle을 사용하고 있으므로 docker bash로 들어가서 sysdba권한으로 sqlplus를 실행하고 권한 부여 명령을 수행한다.
사용자별 권한 부여 이력은 아래 테이블을 조회하면 볼 수 있다. 정상적으로 위 수행한 grant가 있음을 볼 수 있다.
Atomoikos transaction 수행 모습
아래는 위 두개의 DB insert 문에 대한 transaction 처리 로그이다. 조금 복잡해 보이긴 하지만 atomikos에서 각각의 commit 내역을 prepared 하고 둘다 문제없이 수행되었을때 최종적으로 commit 하고 종료 하는 모습을 볼 수 있다.
2023-12-20 18:36:28.381 INFO [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.iab.listener.BatchTriggerListener : triggerFired at Wed Dec 20 12:46:38 KST 2023 :: jobKey : Test.TestJob
2023-12-20 18:36:28.381 INFO [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.iab.listener.BatchJobListener : jobToBeExecuted :: jobKey : Test.TestJob
2023-12-20 18:36:28.381 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] org.quartz.core.JobRunShell : Calling execute on job Test.TestJob
2023-12-20 18:36:28.381 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] o.s.t.jta.JtaTransactionManager : Creating new transaction with name [com.sung.land.iab.service.impl.BatchService.insertBatchLog]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2023-12-20 18:36:28.381 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.i.i.CompositeTransactionManagerImp : createCompositeTransaction ( 10000 ): created new ROOT transaction with id 172.30.1.30.tm170306498838100003
2023-12-20 18:36:28.381 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource
2023-12-20 18:36:28.381 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.j.internal.AbstractDataSourceBean : datasourcePri: getConnection()...(생략)
2023-12-20 18:36:28.381 INFO [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.j.internal.AbstractDataSourceBean : datasourcePri: init...
2023-12-20 18:36:28.382 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.i.m.BatchMapper.insertBatchLog : ==> Preparing: INSERT INTO ...
2023-12-20 18:36:28.382 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.icatch.imp.TransactionStateHandler : addParticipant ( XAResourceTransaction: XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D35 ) for transaction 172.30.1.30.tm170306498838100003
2023-12-20 18:36:28.382 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.datasource.xa.XAResourceTransaction : XAResource.start ( XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D35 , XAResource.TMNOFLAGS ) on resource datasourcePri represented by XAResource instance org.postgresql.xa.PGXAConnection@699bea69
2023-12-20 18:36:28.382 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.icatch.imp.CompositeTransactionImp : registerSynchronization ( com.atomikos.jdbc.internal.AtomikosJdbcConnectionProxy$JdbcRequeueSynchronization@8c5fd679 ) for transaction 172.30.1.30.tm170306498838100003
2023-12-20 18:36:28.382 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.i.m.BatchMapper.insertBatchLog : ==> Parameters: schedulerFactoryBean(String), DESKTOP-GH94F8C1703064960417(String), TestJob(String), TestJobTrigger(String), 20231220183628(String), null, EXECUTE(String), null, null, null, null, EXECUTE(String), null, null, null
2023-12-20 18:36:28.384 DEBUG [IAB,TxId : , SpanId : ] 196 --- [SchedulerThread] org.quartz.core.QuartzSchedulerThread : batch acquisition of 0 triggers
2023-12-20 18:36:28.384 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.i.m.BatchMapper.insertBatchLog : <== Updates: 1
2023-12-20 18:36:28.384 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource
2023-12-20 18:36:28.384 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.j.internal.AbstractDataSourceBean : datasourcePops: getConnection()...
2023-12-20 18:36:28.384 INFO [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.j.internal.AbstractDataSourceBean : datasourcePops: init...
2023-12-20 18:36:28.385 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.i.j.t.p.P.insertBatchLog : ==> Preparing: MERGE INTO ... (생략)
2023-12-20 18:36:28.385 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.icatch.imp.TransactionStateHandler : addParticipant ( XAResourceTransaction: XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D36 ) for transaction 172.30.1.30.tm170306498838100003
2023-12-20 18:36:28.385 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.datasource.xa.XAResourceTransaction : XAResource.start ( XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D36 , XAResource.TMNOFLAGS ) on resource datasourcePops represented by XAResource instance oracle.jdbc.driver.T4CXAResource@68c4799d
2023-12-20 18:36:28.386 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.icatch.imp.CompositeTransactionImp : registerSynchronization ( com.atomikos.jdbc.internal.AtomikosJdbcConnectionProxy$JdbcRequeueSynchronization@8c5fd679 ) for transaction 172.30.1.30.tm170306498838100003
2023-12-20 18:36:28.386 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.i.j.t.p.P.insertBatchLog : ==> Parameters: schedulerFactoryBean(String), DESKTOP-GH94F8C1703064960417(String), TestJob(String), TestJobTrigger(String), 20231220183628(String), null, EXECUTE(String), null, null, schedulerFactoryBean(String), DESKTOP-GH94F8C1703064960417(String), TestJob(String), TestJobTrigger(String), 20231220183628(String), null, EXECUTE(String), null, null
2023-12-20 18:36:28.388 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.i.j.t.p.P.insertBatchLog : <== Updates: 1
2023-12-20 18:36:28.388 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.datasource.xa.XAResourceTransaction : XAResource.end ( XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D35 , XAResource.TMSUCCESS ) on resource datasourcePri represented by XAResource instance org.postgresql.xa.PGXAConnection@699bea69
2023-12-20 18:36:28.388 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.datasource.xa.XAResourceTransaction : XAResource.end ( XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D36 , XAResource.TMSUCCESS ) on resource datasourcePops represented by XAResource instance oracle.jdbc.driver.T4CXAResource@68c4799d
2023-12-20 18:36:28.389 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] o.s.t.jta.JtaTransactionManager : Initiating transaction commit
2023-12-20 18:36:28.389 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.icatch.imp.CompositeTransactionImp : commit() done (by application) of transaction 172.30.1.30.tm170306498838100003
2023-12-20 18:36:28.469 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.datasource.xa.XAResourceTransaction : XAResource.prepare ( XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D35 ) returning OK on resource datasourcePri represented by XAResource instance org.postgresql.xa.PGXAConnection@699bea69
2023-12-20 18:36:28.580 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.datasource.xa.XAResourceTransaction : XAResource.prepare ( XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D36 ) returning OK on resource datasourcePops represented by XAResource instance oracle.jdbc.driver.T4CXAResource@68c4799d
2023-12-20 18:36:28.589 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.datasource.xa.XAResourceTransaction : XAResource.commit ( XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D35 , false ) on resource datasourcePri represented by XAResource instance org.postgresql.xa.PGXAConnection@699bea69
2023-12-20 18:36:29.050 DEBUG [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.a.datasource.xa.XAResourceTransaction : XAResource.commit ( XID: 3137322E33302E312E33302E746D313730333036343938383338313030303033:3137322E33302E312E33302E746D36 , false ) on resource datasourcePops represented by XAResource instance oracle.jdbc.driver.T4CXAResource@68c4799d
2023-12-20 18:36:43.482 INFO [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.iab.listener.BatchJobListener : jobWasExecuted :: jobKey : Test.TestJob
2023-12-20 18:36:43.482 INFO [IAB,TxId : , SpanId : ] 196 --- [artzScheduler-2] c.s.e.iab.listener.BatchTriggerListener : triggerComplete at Wed Dec 20 12:46:38 KST 2023 :: jobKey : Test.TestJob
-- The End --
'Backend Development > Spring boot' 카테고리의 다른 글
[Spring Boot] Quartz 배치 DisallowConcurrentExecution 설정 (0) | 2023.12.20 |
---|---|
[Spring boot] Spring Redis Session Clustering 설정하기 (0) | 2023.06.03 |
[Spring boot] RestTemplate 으로 http 통신하기 (0) | 2023.04.29 |
[Spring boot] MultipartFile 파일 업로드 구현 (0) | 2023.04.11 |
[Spring boot] Embedded Tomcat redis session clustering (0) | 2023.02.26 |