본문 바로가기

Backend Development/Spring boot

[Spring boot] atomikos 라이브러리 활용 XA 멀티 DBMS Transacaction 구현

백엔드 개발을 할때 하나의 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 --