I have a spring batch job which uses a SkipListener
to output any errors to an errors file. There are various types of errors that can occur during a step (read, process, write) and the skip listener knows how to handle them. This is working just fine.
However, I have noticed that the errors are being written to the output out of order. Meaning it appears that errors from the processor are written before any errors from the reader.
This causes me great concern because I need the errors to be written out in the same order in which they are read (you will notice that the skip limit is insanely high and the commit-interval is 1 so it's not a matter of chunking). For all practical purposes this is a very mundane job and doesn't do anything that would lead me to believe it's something within the actual reader or processor that is causing this.
This whole thing has me baffled; it's quite odd. I'm hoping someone out there could point me in the right direction on how to resolve this. Even if the answer is "That's the way it is, and you can't change that."
Here is the relevant portions of the job definition:
<job id="devJob" xmlns="http://www.springframework.org/schema/batch">
... some steps ...
<step id="devProcessStep" next="REMOVED_FOR_SO">
<tasklet>
<chunk reader="devFileReader" processor="devItemProcessor" writer="devOutputWriter" commit-interval="1" skip-limit="999999">
<streams>
<stream ref="devInputReaderStream" />
<stream ref="devErrorOutputWriterStream" />
<stream ref="devOutputWriterStream" />
</streams>
<skippable-exception-classes>
<include class="org.springframework.batch.item.ItemReaderException"/>
... others, but removed since it doesn't matter for this question ...
<exclude class="org.springframework.batch.item.NonTransientResourceException"/>
</skippable-exception-classes>
</chunk>
<listeners>
<listener ref="devSkipListener" />
</listeners>
</tasklet>
</step>
... more steps ...
</job>
...
<bean id="devSkipListener" class="you.have.to.guess.DevSkipListener" scope="job">
<property name="writer" ref="devErrorOutputWriterStream" />
... other properties that are based on job parameters ...
</bean>
...
<bean id="devErrorOutputWriter"
class="org.springframework.batch.item.file.FlatFileItemWriter" scope="job">
... a bunch of properties based on job parameters etc.
</bean>
And here is the SkipListener
, with only the relevant parts included (note I am using Groovy not Java, since this is a Grails application)
package you.have.to.guess
// bunch of imports (removed for SO)
class DevSkipListener implements SkipListener {
private static def log = LogFactory.getLog(this)
ItemWriter writer
// some other properties, not important
@Override
void onSkipInRead(Throwable e) {
log.debug("Skip error in read", e)
// some stuff here to figure out the lines
writer.write(lines)
}
@Override
void onSkipInProcess(Object item, Throwable e) {
log.debug("Skip error in process", e)
// some stuff here to figure out the lines
writer.write(lines)
}
@Override
void onSkipInWrite(Object item, Throwable e) {
log.debug("Skip error in write", e)
// some stuff here to figure out the lines
writer.write(lines)
}
}
In short, the
SkipListener
's contract does not guarantee the order of the items to be processed. Only that the methods on the listener will be called once per skip.