このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Batch ドキュメント 6.0.6 を使用してください! |
再試行ロジックの構成
ほとんどの場合、スキップまたは Step 障害のいずれかを引き起こす例外が必要です。ただし、すべての例外が決定論的というわけではありません。読み取り中に FlatFileParseException が検出されると、そのレコードに対して常にスローされます。ItemReader をリセットしても解決しません。ただし、他の例外 (別のプロセスがロックを保持しているレコードを現在のプロセスが更新しようとしたことを示す DeadlockLoserDataAccessException など) の場合は、待機して再試行すると成功する可能性があります。
Java
XML
Java では、再試行は次のように構成する必要があります。
@Bean
public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
// retry policy configuration
int retryLimit = 3;
var retrybaleExceptions = Set.of(DeadlockLoserDataAccessException.class);
RetryPolicy retryPolicy = RetryPolicy.builder()
.maxRetries(retryLimit)
.includes(retrybaleExceptions)
.build();
return new StepBuilder("step1", jobRepository)
.<String, String>chunk(2).transactionManager(transactionManager)
.reader(itemReader())
.writer(itemWriter())
.faultTolerant()
.retryPolicy(retryPolicy)
.build();
}XML では、再試行は次のように構成する必要があります。
<step id="step1">
<tasklet>
<chunk reader="itemReader" writer="itemWriter"
commit-interval="2" retry-limit="3">
<retryable-exception-classes>
<include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</retryable-exception-classes>
</chunk>
</tasklet>
</step>Step では、個々の項目を再試行できる回数の制限と、「再試行可能」な例外のリストが許可されます。
再試行して ItemReader
ItemProcessor または ItemWriter 呼び出しが失敗した場合、フォワードオンリーの ItemReader 契約に関しては、再試行は常に安全です。再試行対象のアイテムはすでにステップによって保持されているため、再試行は単に全く同じアイテムで process/write を再呼び出しします。
ItemReader#read() 呼び出しの失敗を再試行するのは、アイテムがまだ存在しないため、状況が異なります。アイテムを生成するのは read() です。再試行で同じ論理アイテムを再試行するか、次のアイテムに静かに進むかは、フレームワークではなく、リーダーの実装に完全に依存します。読み取りが失敗した場合、ChunkOrientedStep は同じリーダーインスタンスで read() を再度呼び出すだけで、それ以上の調整は行いません。リーダーがアイテムを正常に返す直前まで内部位置を進めない限り、これは安全です。組み込みのページングスタイルのリーダー (JdbcPagingItemReader など) はすでにこのルールに従っています。各ページは、新しく自己完結型のクエリによって取得され、現在の位置はクエリが成功した場合にのみ進みます。カーソルベースのリーダー (JdbcCursorItemReader など) は、呼び出し間でアクティブな ResultSet/Connection を保持するため、再試行はカーソルを実際に無効にしない、より狭い一時的な問題にのみ役立ちます。
基となるソースを消費してから例外をスローするかどうかを決定するカスタムリーダーは、リトライセーフではありません。
public class UnsafeItemReader extends ListItemReader<String> {
@Override
public String read() {
String item = super.read(); (1)
validate(item); (2)
return item;
}
}| 1 | 代表者はここで、次に何が起ころうとも、自らの立場を有利にするために尽力します。 |
| 2 | この処理で例外が発生し、読み取りが再試行された場合、次の read() 呼び出しで super.read() が再度呼び出され、別のアイテムが返されます。検証に失敗したアイテムは、再試行もスキップもされず、二度と表示されることもありません。つまり、静かに破棄されます。 |
これに対し、読書が成功した後にのみ順位が上がるリーダーとは対照的です。
public class SafeItemReader implements ItemReader<String> {
private long lastId = 0;
@Override
public String read() {
Row row = fetchNext(lastId); // does not mutate any reader state
if (row == null) {
return null;
}
lastId = row.id(); // only advances on success
return row.value();
}
} ここでは、戻り値を返す前に例外をスローする read() 呼び出しは lastId を移動していないため、再試行するとまったく同じフェッチが再発行され、まったく同じ次のアイテムが返されます。
これは、上記で説明したように、読み取り時の再試行可能な例外を、真に一時的な状態 (接続の切断、タイムアウト、デッドロック) に限定する必要がある理由でもあります。無効な入力の解析中に発生するような決定論的な例外は、毎回同じレコードに対してスローされます。再試行しても、最終的に失敗する (またはスキップポリシーが適用される場合はスキップされる) 前に、設定された再試行回数を消費するだけです。決定論的な読み取り失敗は、RetryPolicy ではなく SkipPolicy にルーティングします。