GeekFactory

int128.hatenablog.com

エンティティ変換タスクの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:注:勝手に名前を付けました。