エンティティ変換タスクのRespawnパターン
長時間にわたる処理は、App Engineの最大実行時間である30秒ごとに分割する必要があります。大量のエンティティを変換する処理もその一つです。
いろいろ試してみた結果、Respawnパターン*1が使いやすいと思います。
Respawnパターンは、Task QueueがDeadlineExceededExceptionで終了するとリトライされる性質を利用します。EG内の読み込み、書き込みは必ずトランザクション内で行うようにします。
未処理エンティティを確実に抽出できるクエリが必要です。処理済みのエンティティと区別が付かない場合は適用できません。
// 未処理エンティティを抽出するクエリ List<E> target = Datastore.query(m)...(); TaskResult taskResult = new TaskResult(name, target.size()); try { for(List<E> piece : ListUtil.split(target, 1000)) { for(E entity : piece) { // エンティティを変換 entity...(); } Datastore.put(piece); taskResult.incrementActualCount(piece.size()); } } catch(DeadlineExceededException e) { // リトライが走る throw e; } finally { // タスクの処理実績を報告 logger.info(taskResult.toString()); } return null;
手元のデータストアに適用した結果、3回のタスクで収束しました。
run: Task 5913770154700435815-2: actual=2225, planned=2225 run: Task 5913770154700435815-1: actual=2000, planned=4725 run: Task 5913770154700435815-0: actual=3000, planned=8225
こういうデザインパターンはすでにまとめ記事があるのでしょうか。えろい人、教えてください。
*1:注:勝手に名前を付けました。