LIVESENSE ENGINEER BLOG

リブセンスエンジニアの活動や注目していることを発信しています

転職会議から冪等でないバッチ処理を根絶した話

こんにちは、かたいなかです。

最近転職会議のバッチ処理をすべて冪等にし、処理失敗時に気軽に再実行できるようにすることで運用性を向上させました。 今回の記事ではその取り組みを紹介します。

再実行すると重複送信につながるメール送信バッチ

もともと、転職会議では一部のバッチ処理を除いてほとんどのバッチ処理が冪等に作られていました。しかし、残りの冪等ではないバッチ処理では、失敗するたびに毎回アドホックな対応をする必要があり運用性に課題を抱えていました

残っていたもので一番大きな問題を抱えていたのがメール送信バッチです。これは、以下の図のようなアーキテクチャで動いており、ワーカーにメールを送信するように指示するメッセージをSQSにキューイングする処理を行うものです。

https://cacoo.com/diagrams/TLu2E24DWOhIxye1-A28F4.png

このメール送信バッチのキューイング処理が途中で失敗した際に、雑に再実行してしまうと同一のユーザに重複してメールが送信されてしまう事になってしまいます。 いままではSQSの配信遅延等で問題を緩和したりしていましたが、根本的な解決に至らない状態となっていました。

なにより、現在Kubernetes上でバッチ処理を動かしているのですが、KubernetesのCronJobはドキュメントに記載されているように同じ時刻に2度実行されてしまうことがあります。いままでのところ、転職会議内部で複数回実行されることは観測できていないのですが、他の部署では重複実行されたケースがあったと聞いていました。また、CronJobからArgo Workflowへの全面移行も予定しており、こちらは部分的に運用した結果CronJobよりも重複実行される頻度が高いことがわかっていたため、Argo移行を進める上で何らかの対応を行う必要に迫られていました。

そこで、今回はRedisを使って重複してメールを送信しないようにしてみました。

Redisを使ってロック処理を実装

今回はワーカー側にRedisを使って重複を制御する処理を追加してみました。

https://cacoo.com/diagrams/TLu2E24DWOhIxye1-9D8C9.png

処理は以下のような流れで行います。

f:id:katainaka0503:20210303112229p:plain

ワーカーでそれぞれのユーザにメール送信を行う前にRedisでの重複制御処理を実行します。SETにNXオプションを付与することで、Redisに同じキーの値が登録されていないときのみSETの処理が成功するようになります。 ここでキーとして使用しているメールIDは、送信日・メール種別・メールアドレスで一意になっています。これにより、キューイングバッチが再実行されたケース等で、同じ日に同じメールが同じユーザに重複して送信しようとした際にはRedisへのSETでエラーが発生することになり、重複送信することを防いでいます。

今回のような実装では、ネットワークの問題等でメール送信処理が失敗した際にメールが失われてしまう事がありえますが、送信するメールの内容からメールが失われることよりもメールを重複送信してしまうことのほうが影響度が大きいと考え、現在のようなかたちとなりました。

まとめ

バッチ処理の運用性を考える上で冪等性は極めて重要な概念です。今回はRedisをワーカー側に組み込むことで冪等性を実現しましたが、RDBを使った実装や処理の内容を工夫する等々様々な方法が考えられるかと思います。

特に、Kubernetesでのバッチ処理は同じ時刻に複数回実行される可能性があるため、同じバッチ処理が同じCronJobの時刻に重複して実行されることを想定しておく必要があります。これはArgo等を使用して実行する場合も同様です。

皆さんの関わっているシステムで冪等性について考えるきっかけにしていただけたら幸いです。

参考