Configuración de Skip Logic en Spring Batch

1. Introducción

De forma predeterminada, cualquier error encontrado durante el procesamiento de un trabajo de Spring Batch hará que el paso correspondiente falle. Sin embargo, hay muchas situaciones en las que preferimos omitir el artículo procesado actualmente para ciertas excepciones.

En este tutorial, exploraremos dos enfoques para configurar la lógica de omisión en el marco de Spring Batch.

2. Nuestro caso de uso

A modo de ejemplos, reutilizaremos un trabajo simple y orientado a fragmentos presentado ya en nuestro artículo introductorio de Spring Batch.

Este trabajo convierte algunos datos financieros de un formato CSV a XML.

2.1. Los datos de entrada

Primero, agreguemos algunas filas al archivo CSV original:

username, user_id, transaction_date, transaction_amount devendra, 1234, 31/10/2015, 10000 john, 2134, 3/12/2015, 12321 robin, 2134, 2/02/2015, 23411 , 2536, 3/10/2019, 100 mike, 9876, 5/11/2018, -500 , 3425, 10/10/2017, 9999

Como podemos ver, las últimas tres filas contienen algunos datos no válidos: a las filas 5 y 7 les falta el campo de nombre de usuario y el monto de la transacción en la fila 6 es negativo.

En las secciones posteriores, configuraremos nuestro trabajo por lotes para omitir estos registros dañados.

3. Configurar el límite de omisión y las excepciones que se pueden omitir

3.1. Usar skip y skipLimit

Analicemos ahora la primera de dos formas de configurar nuestro trabajo para omitir elementos en caso de falla: los métodos skip y skipLimit :

@Bean public Step skippingStep( ItemProcessor processor, ItemWriter writer) throws ParseException { return stepBuilderFactory .get("skippingStep") .chunk(10) .reader(itemReader(invalidInputCsv)) .processor(processor) .writer(writer) .faultTolerant() .skipLimit(2) .skip(MissingUsernameException.class) .skip(NegativeAmountException.class) .build(); }

En primer lugar, para habilitar la funcionalidad de omisión, necesitamos incluir una llamada a failTolerant () durante el proceso de construcción de pasos.

Dentro de skip () y skipLimit () , definimos las excepciones que queremos omitir y el número máximo de elementos omitidos.

En el ejemplo anterior, si se lanza una MissingUsernameException o NegativeAmountException durante la fase de lectura, proceso o escritura, el elemento procesado actualmente se omitirá y se contará para el límite total de dos.

En consecuencia, si se lanza una excepción por tercera vez, todo el paso fallará .

3.1. Usando noSkip

En el ejemplo anterior, cualquier otra excepción además de MissingUsernameException y NegativeAmountException hace que nuestro paso falle.

En algunas situaciones, sin embargo, puede ser más apropiado identificar excepciones que deberían hacer que nuestro paso falle y omitir cualquier otro.

Veamos cómo podemos configurar esto usando skip , skipLimit y noSkip :

@Bean public Step skippingStep( ItemProcessor processor, ItemWriter writer) throws ParseException { return stepBuilderFactory .get("skippingStep") .chunk(10) .reader(itemReader(invalidInputCsv)) .processor(processor) .writer(writer) .faultTolerant() .skipLimit(2) .skip(Exception.class) .noSkip(SAXException.class) .build(); }

Con la configuración anterior, le indicamos al marco Spring Batch que omita cualquier excepción (dentro de un límite configurado) excepto SAXException . Esto significa que SAXException siempre provoca un error de paso.

El orden de las llamadas skip () y noSkip () no importa.

4. Uso de SkipPolicy personalizado

A veces, es posible que necesitemos un mecanismo de verificación de omisiones más sofisticado. Para ese propósito, el marco Spring Batch proporciona la interfaz SkipPolicy .

Luego, podemos proporcionar nuestra propia implementación de lógica de omisión y conectarla a nuestra definición de paso.

Teniendo en cuenta el ejemplo anterior, imagine que todavía queremos definir un límite de omisión de dos elementos y hacer que solo MissingUsernameException y NegativeAmountException se puedan omitir.

Sin embargo, una restricción adicional es que podemos omitir NegativeAmountException, pero solo si la cantidad no excede un límite definido .

Implementemos nuestra SkipPolicy personalizada :

public class CustomSkipPolicy implements SkipPolicy { private static final int MAX_SKIP_COUNT = 2; private static final int INVALID_TX_AMOUNT_LIMIT = -1000; @Override public boolean shouldSkip(Throwable throwable, int skipCount) throws SkipLimitExceededException { if (throwable instanceof MissingUsernameException && skipCount < MAX_SKIP_COUNT) { return true; } if (throwable instanceof NegativeAmountException && skipCount < MAX_SKIP_COUNT ) { NegativeAmountException ex = (NegativeAmountException) throwable; if(ex.getAmount() < INVALID_TX_AMOUNT_LIMIT) { return false; } else { return true; } } return false; } }

Ahora, podemos usar nuestra política personalizada en una definición de paso:

 @Bean public Step skippingStep( ItemProcessor processor, ItemWriter writer) throws ParseException { return stepBuilderFactory .get("skippingStep") .chunk(10) .reader(itemReader(invalidInputCsv)) .processor(processor) .writer(writer) .faultTolerant() .skipPolicy(new CustomSkipPolicy()) .build(); }

Y, de manera similar a nuestro ejemplo anterior, todavía necesitamos usar failTolerant () para habilitar la funcionalidad de omisión.

Esta vez, sin embargo, no llamamos skip () o noSkip () . En su lugar, usamos el método skipPolicy () para proporcionar nuestra propia implementación de la interfaz SkipPolicy .

Como podemos ver, este enfoque nos da más flexibilidad, por lo que puede ser una buena opción en ciertos casos de uso .

5. Conclusión

En este tutorial, presentamos dos formas de hacer que un trabajo de Spring Batch sea tolerante a fallas.

Aunque el uso de skipLimit () junto con los métodos skip () y noSkip () parece ser más popular, podemos encontrar que implementar una SkipPolicy personalizada es más conveniente en algunas situaciones.

Como de costumbre, todos los ejemplos de código están disponibles en GitHub.