Estamos en el proceso de pasar a Azure SQL Server desde Oracle DB para nuestra aplicación Spring Batch.
Recibo el siguiente error al intentar ejecutar dos trabajos diferentes al mismo tiempo que actualiza tablas diferentes, pero usa las mismas tablas BATCH_ comunes
Causado por: org.springframework.dao.DataAccessResourceFailureException: no se pudo incrementar la identidad; La excepción anidada es com.microsoft.sqlserver.jdbc.SQLServerException: la transacción (ID de proceso 167) se interbloqueó en los recursos de bloqueo con otro proceso y se eligió como víctima del interbloqueo. Vuelva a ejecutar la transacción. en org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer.getNextKey(SqlServerMaxValueIncrementer.java:124) ~[bat-applybatch-jobs-2.2.12-SNAPSHOT.jar:?] en org.springframework.jdbc.support.incrementer. AbstractDataFieldMaxValueIncrementer.nextLongValue(AbstractDataFieldMaxValueIncrementer.java:125)
Configuración de mi repositorio de trabajos
<job-repository id="jobRepository" isolation-level-for-create="READ_COMMITED" />interbloqueo de la base de datos
<deadlock> <victim-list> <victimProcess id="process2a41675a4e8" /> </victim-list> <process-list> <process id="process2a41675a4e8" taskpriority="0" logused="280" waitresource="RID: 6:9:24682488:29" waittime="4984" ownerId="696000712" transactionname="implicit_transaction" lasttranstarted="2021-12-29T12:18:30.153" XDES="0x29a22bc4428" lockMode="U" schedulerid="4" kpid="52760" status="suspended" spid="173" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-12-29T12:18:30.157" lastbatchcompleted="2021-12-29T12:18:30.153" lastattention="1900-01-01T00:00:00.153" clientapp="Microsoft JDBC Driver for SQL Server" hostname="ServerName" hostpid="0" loginname="LoginName" isolationlevel="read committed (2)" xactid="696000712" currentdb="6" currentdbname="Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058"> <executionStack> <frame procname="unknown" queryhash="0xadc42a7474869694" queryplanhash="0x238c4f9df8a5d6cc" line="1" stmtstart="26" stmtend="146" sqlhandle="0x020000007654041849f4ffe980c136b592ccbe8260983e220000000000000000000000000000000000000000"> unknown </frame> <frame procname="unknown" queryhash="0xadc42a7474869694" queryplanhash="0x238c4f9df8a5d6cc" line="1" stmtend="126" sqlhandle="0x0200000045a2af306ade799ae9ffa65edc0f722c526e26330000000000000000000000000000000000000000"> unknown </frame> </executionStack> <inputbuf> delete from LoginName.BATCH_STEP_EXECUTION_SEQ where ID < 10899 </inputbuf> </process> <process id="process2a42d680ca8" taskpriority="0" logused="420" waitresource="RID: 6:9:24682490:8" waittime="4984" ownerId="696000707" transactionname="implicit_transaction" lasttranstarted="2021-12-29T12:18:30.153" XDES="0x2a41ae18428" lockMode="U" schedulerid="7" kpid="53280" status="suspended" spid="129" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-12-29T12:18:30.153" lastbatchcompleted="2021-12-29T12:18:30.153" lastattention="1900-01-01T00:00:00.153" clientapp="Microsoft JDBC Driver for SQL Server" hostname="ServerName" hostpid="0" loginname="LoginName" isolationlevel="read committed (2)" xactid="696000707" currentdb="6" currentdbname="Database" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058"> <executionStack> <frame procname="unknown" queryhash="0xadc42a7474869694" queryplanhash="0x238c4f9df8a5d6cc" line="1" stmtstart="26" stmtend="146" sqlhandle="0x020000007654041849f4ffe980c136b592ccbe8260983e220000000000000000000000000000000000000000"> unknown </frame> <frame procname="unknown" queryhash="0xadc42a7474869694" queryplanhash="0x238c4f9df8a5d6cc" line="1" stmtend="126" sqlhandle="0x02000000a0f1f51de77e1eefa19367c42fc9d1938c2075020000000000000000000000000000000000000000"> unknown </frame> </executionStack> <inputbuf> delete from LoginName.BATCH_STEP_EXECUTION_SEQ where ID < 10898 </inputbuf> </process> </process-list> <resource-list> <ridlock fileid="9" pageid="24682488" dbid="6" objectname="162589bb-bc36-4834-8bdc-e58a2deca742.LoginName.BATCH_STEP_EXECUTION_SEQ" id="lock2a043bbcc00" mode="X" associatedObjectId="72057594071547904"> <owner-list> <owner id="process2a42d680ca8" mode="X" /> </owner-list> <waiter-list> <waiter id="process2a41675a4e8" mode="U" requestType="wait" /> </waiter-list> </ridlock> <ridlock fileid="9" pageid="24682490" dbid="6" objectname="162589bb-bc36-4834-8bdc-e58a2deca742.LoginName.BATCH_STEP_EXECUTION_SEQ" id="lock29f5f1b7f00" mode="X" associatedObjectId="72057594071547904"> <owner-list> <owner id="process2a41675a4e8" mode="X" /> </owner-list> <waiter-list> <waiter id="process2a42d680ca8" mode="U" requestType="wait" /> </waiter-list> </ridlock> </resource-list> </deadlock>intentado:
<job-repository id="jobRepository" isolation-level-for-create="READ_UNCOMMITED" /> <job-repository id="jobRepository" isolation-level-for-create="ISOLATION_REPEATABLE_READ" /> <job-repository id="jobRepository" isolation-level-for-create="SERIALIZABLE" />He creado las tablas como se destaca a continuación.
CREATE TABLE BATCH_STEP_EXECUTION_SEQ ( ID BIGINT IDENTITY(<last Oracle sequence value>, 1) ); CREATE TABLE BATCH_JOB_EXECUTION_SEQ ( ID BIGINT IDENTITY(<last Oracle sequence value>, 1) ); CREATE TABLE BATCH_JOB_SEQ ( ID BIGINT IDENTITY(<last Oracle sequence value>, 1) );¿cual es el problema? ¿Cómo puedo solucionar esto?
Actualización: definición de trabajo
<bean id="simpleStep" class="org.springframework.batch.core.step.factory.SimpleStepFactoryBean" abstract="true"> <property name="transactionManager" ref="transactionManager" /> <property name="jobRepository" ref="jobRepository" /> <property name="startLimit" value="100" /> <property name="commitInterval" value="1" /> </bean>Actualización # 2: ¿Puedo probar algo como esto?
<bean id="informixIncrementer" class="com.bah.batch.informixsupport.InformixMaxValueIncrementerFactory"><property name="dataSource" ref="dataSource" /> <bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean" isolation-level-for-create="READ_COMMITTED" table-prefix="BATCH_"> <property name="incrementerFactory" ref="informixIncrementer"/> </bean>Me temo que este es un error conocido en Spring Batch 4.x que actualmente solo está planeado para resolverse con Spring Batch 5, que no se espera hasta dentro de un par de meses: https://github.com/spring- proyectos/lote de primavera/problemas/3927
Puede emular la corrección haciendo ajustes locales como en la confirmación de la corrección: https://github.com/spring-projects/spring-batch/commit/fe911c8456bb49a69b1c84c78c0a0e0fdf224803 , es decir
DataFieldMaxValueIncrementerFactory que se usa para construir el JobRepository .No creo que esto último sea factible con una configuración XML pura. La discusión sobre este tema contiene algunos consejos sobre cómo se puede hacer en Java: https://github.com/spring-projects/spring-batch/issues/1448
La personalización concreta depende de las personalizaciones que ya tenga, pero debería funcionar al menos aproximadamente de la siguiente manera. Puede aplicar la fábrica de incrementadores con un BatchConfigurer :
@Bean public BatchConfigurer batchConfigurer(DataSource dataSource) { return new DefaultBatchConfigurer(dataSource) { @Override protected JobRepository createJobRepository() throws Exception { JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean(); factory.setDataSource(getDataSource()); factory.setTransactionManager(getTransactionManager()); factory.setIncrementerFactory(new MyIncrementerFactory(getDataSource())); factory.afterPropertiesSet(); return factory.getObject(); } }; }donde
public class MyIncrementerFactory implements DataFieldMaxValueIncrementerFactory { private final DataSource dataSource; public MyIncrementerFactory(DataSource dataSource) { this.dataSource = dataSource; } @Override public DataFieldMaxValueIncrementer getIncrementer(String databaseType, String incrementerName) { return new SqlServerSequenceMaxValueIncrementer(dataSource, incrementerName); } @Override public boolean isSupportedIncrementerType(String databaseType) { return true; } @Override public String[] getSupportedIncrementerTypes() { return null; // method should not get called anyway } } y SqlServerSequenceMaxValueIncrementer debe ser el incrementador de la confirmación.