티스토리 뷰

spring-batch commit-interval 이 정상적으로 수행되지 않아 삽질했던 경험이 있다.
(혹시 유사한 삽질을 경험하고 있으신 분에게 도움이 되시길...)

담당 시스템에서 spring-batch 를 사용하고 있다.
chunk 단위로 reader-writer 구조를 갖고 있는데, writer 에서 약 150만건의 데이터를 insert 하는 로직이다.
여기서만 2시간 이상 시간이 소요되었다. (때론 3시간이 넘게 걸리는 경우도 있었다.)

왜 이렇게 오래 걸릴까를 생각하다가 한건씩 처리되고 있는 것은 아닐까? 라는 의문이 들었다.
그래서 재현을 진행해 보았다.
 

 <job id=“testJob">
   ...
   <step id=“testJob_step" next=“...">
      <tasklet>
         <chunk reader=“testReader" writer=“testWriter" processor=“testProcess" commit-interval="1000" />
      </tasklet>
   </step>
   ...
</job>

설정만으로는 commit-interval option 이 1000이므로 1000개씩 묶음별로 insert 처리가 되겠구나라고 생각이 된다.
즉, 처음에 가졌던 의문... 건별로 처리가 되는 건 아닐까?라는 질문은 잘못된 생각인 것 같았다.

그래서 정말 묶음별로 commit 처리가 되는지 재현을 해 보았다.

Code 를 아래와 같이 테스트용으로 조금 변경했다. (확인을 쉽게 하기 위해서…)
row 1개를 update 하고 오류가 발생하도록 하였으며, 정상적인 transaction 처리가되면 rollback 처리가 되어야 한다.
 

 public ItemWriter<Map<String, Object>> testWriter(final @Value("#{jobParameters}") Map<String, Object> params) {
    return new ItemWriter<Map<String, Object>>() {
        @Override
        public void write(List<? extends Map<String, Object>> targetList) throws Exception {
            // DB 계정 변경
            for (Map<String, Object> target : targetList) {
                ...

                testDAO.updateInfos(target);
                throw new Exception(“Job 중단...Test용");
            }
        }
    };
}

테스트 결과 예상했던 것처럼 오류가 발생하였다...
그런데 데이터는 생각과 다르게 첫번째 row 는 갱신이 되었다. 즉, 1건씩 commit 처리가 된다는 것을 의미한다.
commit-interval 을 지정하였음에도 정상적으로 처리가 되지 않는 것을 확인할 수 있었다.

왜 안 될까? 만약 DAO에서 사용하고 있는 Datasource 의 autocommit 옵션이 true라면?

아래는 DataTransactionManager 코드의 일부이다.
 
if(con.getAutoCommit()) {
   txObject.setMustRestoreAutoCommit(true);
   if(this.logger.isDebugEnabled()) {
      this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
   }

   con.setAutoCommit(false);
}

즉, autocommit=true 인 경우 false 로 변경하는 것을 알 수 있다. (재현도 해보니 true/false 에 상관없이 결과는 동일)
이 문제도 아닌 것 같다.

spring-batch 문서를 다시 읽어보니...

 5.1.1Configuring a Step

Despite the relatively short list of required dependencies for aStep, it is an extremely complex class that can potentially contain many collaborators. In order to ease configuration, the Spring Batch namespace can be used:

<job id="sampleJob" job-repository="jobRepository">
    <step id="step1">
        <tasklet transaction-manager="transactionManager">
            <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
        </tasklet>
    </step>
</job>

 

The configuration above represents the only required dependencies to create a item-oriented step:

  • reader - TheItemReaderthat provides items for processing.
  • writer - TheItemWriterthat processes the items provided by theItemReader.
  • transaction-manager - Spring'sPlatformTransactionManagerthat will be used to begin and commit transactions during processing.
  • job-repository - TheJobRepositorythat will be used to periodically store theStepExecutionandExecutionContextduring processing (just before committing). For an in-line <step/> (one defined within a <job/>) it is an attribute on the <job/> element; for a standalone step, it is defined as an attribute of the <tasklet/>.
  • commit-interval - The number of items that will be processed before the transaction is committed.
 
It should be noted that, job-repository defaults to "jobRepository" and transaction-manager defaults to "transactionManger". Furthermore, theItemProcessoris optional, not required, since the item could be directly passed from the reader to the writer.

tasklet 에 transaction-manager 옵션을 지정하지 않아도 transactionManager 로 기본 지정된다라고 되어 있는 것을 확인할 수 있다.
이는 spring-batch-2.1.xsd 파일을 열어보면 금방 확인할 수 있다.

 <xsd:attribute name="transaction-managertype="xsd:string" default="transactionManager">

   <xsd:annotation>
      <xsd:documentation source="java:org.springframework.transaction.PlatformTransactionManager"><![CDATA[
      The bean name of the TransactionManager that is to be used. This attribute 
      is not required, and only needs to be specified explicitly
      if the bean name of the desired TransactionManager is not 'transactionManager'.
      ]]></xsd:documentation>
      <xsd:appinfo>
         <tool:annotation kind="ref">
            <tool:expected-type type="org.springframework.transaction.PlatformTransactionManager" />
         </tool:annotation>
      </xsd:appinfo>
   </xsd:annotation>
</xsd:attribute>

또 다른 의문점이 들었다.
테스트 환경에서 보면 코드에 DB 계정 변경 코드가 존재한다. 즉, 계정 Switching 을 하면서 DB 작업을 하게 되는데,
다시 말하면 testDAO 실행시 상황에 따라 DataSource 정보가 다를 수 있다는 의미이다.
Transaction 이슈는 계정 Switching 때문일 수도 있을 것 같다는 생각이 들었다.
 

<job id=“testJob">
   ...
   <step id=“testJob_step" next=“...">
      <tasklet transaction-manager=“”specifiedDbTransactionManager"
         <chunk reader=“testReader" writer=“testWriter" processor=“testProcess" ="1000" />
      </tasklet>
   </step>
   ...
</job>

특정 계정의 transactionManager 로 지정해 주니, 원했던대로 commit-interval 의 역할을 하게 되었습니다. 
150만 건 저장시 느렸던 원인은 건별로 commit 이 되면서 느려진 것이 원인이다.

결론

DataSource 가 변경되는 환경에서는 spring-batch 의 Transaction 설정이 정상적으로 적용되지 않는다.
이는 spring-batch 에 국한된 내용은 아니다. 2page commit 이슈는 모든 곳에 적용되는 이슈이다.

나중에 2 page-commit 은 어떻게 해결할 수 있을지 알아보자.



참고자료


'Programing > Spring-Batch' 카테고리의 다른 글

Transaction 내에서 ExecutorType 변경 불가 오류  (0) 2017.03.15
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함