Posted at

Spring BatchをOracleで連続Job実行するとORA-08177


現象

以下のコードのように、ほとんど間を空けずにjobをlaunchする。

ExecutorService s = Executors.newFixedThreadPool(2);

long time1 = new Date().getTime();
long time2 = time1 + 111111000;
s.execute(() -> doBatch(time1));
s.execute(() -> doBatch(time2));

void doBatch(long t) {
Map<String, JobParameter> p = new HashMap<String, JobParameter>();
p.put("a", new JobParameter(new Date(t)));
JobParameters jobParameters = new JobParameters(p);
l.run(job, jobParameters)

これを実行すると、以下のようなエラーになる。

Exception in thread "pool-1-thread-1" org.springframework.dao.CannotSerializeTransactionException: PreparedStatementCallback; SQL [INSERT into BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION) values (?, ?, ?, ?)]; ORA-08177: このトランザクションのアクセスをシリアル化できません

; nested exception is java.sql.SQLException: ORA-08177: このトランザクションのアクセスをシリアル化できません

at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:270)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1444)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:632)

これの原因は Spring Batch ORA-08177: can't serialize access for this transaction when running single job, SERIALIZED isolation level とかに書いてある。


解決策

というわけでIsolationLevelREAD_UNCOMMITTEDに変えてしまえばよい。

@Configuration

@EnableBatchProcessing
public class BatchConfiguration1 extends DefaultBatchConfigurer {
@Autowired
DataSource configDataSource;

@Autowired
PlatformTransactionManager configTxManager;

@Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factoryBean = new JobRepositoryFactoryBean();
factoryBean.setDataSource(configDataSource);
factoryBean.setTransactionManager(configTxManager);
factoryBean.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
factoryBean.setTablePrefix("BATCH_");
try {
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
} catch (Exception e) {
throw new BatchConfigurationException(e);
}
}