Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Sidekiqで実現する 長時間非同期処理の中断と再開 / Pausing and Resum...
Search
hypermkt
October 25, 2024
Programming
7
3.7k
Sidekiqで実現する 長時間非同期処理の中断と再開 / Pausing and Resuming Long-Running Asynchronous Jobs with Sidekiq
2024.10.25 Fri Kaigi on Rails 2024@有明セントラルタワーホール & カンファレンス(東京)
hypermkt
October 25, 2024
Tweet
Share
More Decks by hypermkt
See All by hypermkt
脆弱性から学ぶ Webセキュリティ Part2/study-web-security-from-vulnerability2
hypermkt
5
3k
脆弱性から学ぶ Webセキュリティ/study-web-security-from-vulnerability1
hypermkt
5
2.3k
モバイルアプリ向けAPI 開発を通じて学んだこと / learned-from-developing-mobile-app-api
hypermkt
3
4k
Passportのパスワードグラントで独自の認証を実装する方法 / how-to-implement-original-authentication-for-passport-password-grant
hypermkt
1
750
Webpackで作る Vueコンポーネント開発環境 / Creating the Vue component development with Webpack.
hypermkt
3
3.9k
あの問題解きました! / solved the code
hypermkt
0
310
Vue.js で作る日報アプリケーション ハンズオン / vue-js-handson-by-nippo
hypermkt
0
400
できるPHP7アップグレード / php7 upgrade
hypermkt
5
7.1k
小さな課題解決から始めるVue.js / Getting started Vue.js for small improvement
hypermkt
6
5.7k
Other Decks in Programming
See All in Programming
これでLambdaが不要に?!Step FunctionsのJSONata対応について
iwatatomoya
2
3.6k
testcontainers のススメ
sgash708
1
120
フロントエンドのディレクトリ構成どうしてる? Feature-Sliced Design 導入体験談
osakatechlab
8
4.1k
短期間での新規プロダクト開発における「コスパの良い」Goのテスト戦略」 / kamakura.go
n3xem
2
170
Criando Commits Incríveis no Git
marcelgsantos
2
170
[JAWS-UG横浜 #76] イケてるアップデートを宇宙いち早く紹介するよ!
maroon1st
0
450
なまけものオバケたち -PHP 8.4 に入った新機能の紹介-
tanakahisateru
1
120
The Efficiency Paradox and How to Save Yourself and the World
hollycummins
1
440
事業成長を爆速で進めてきたプロダクトエンジニアたちの成功談・失敗談
nealle
3
1.4k
モバイルアプリにおける自動テストの導入戦略
ostk0069
0
110
CSC509 Lecture 14
javiergs
PRO
0
130
rails statsで大解剖 🔍 “B/43流” のRailsの育て方を歴史とともに振り返ります
shoheimitani
2
930
Featured
See All Featured
BBQ
matthewcrist
85
9.4k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
32
2.7k
GraphQLとの向き合い方2022年版
quramy
44
13k
Imperfection Machines: The Place of Print at Facebook
scottboms
266
13k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
How STYLIGHT went responsive
nonsquared
95
5.2k
Gamification - CAS2011
davidbonilla
80
5.1k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
The Cost Of JavaScript in 2023
addyosmani
45
7k
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
A better future with KSS
kneath
238
17k
Transcript
Sidekiqで実現する 長時間非同期処理の中断と再開 2024.10.25 Fri Kaigi on Rails 2024@有明セントラルタワーホール & カンファレンス(東京)
@hypermkt SmartHR プロダクトエンジニア
ばーちー 自己紹介 株式会社SmartHR バックエンドエンジニア @hypermkt 2
3
4
SmartHRでは 非同期処理 を活用しています 5
なぜ私は今日発表するのか • SmartHRの本体機能の開発の過程で、一部の非同期処理が抱える課題解決を チームで担当 • 非同期処理の中断と再開に関する情報・事例が社外であまりない • 同じような悩みを抱えている企業やエンジニアに向けて解決方法や設計方針を共有 したい 6
想定する聞き手 • Sidekiqを利用しているエンジニア • 長時間のジョブによりデプロイの影響を受けるシステムを運用している企業 • 中断・再開機能の実装に興味のある方 7
• 長時間実行のジョブが抱える課題 • ジョブの中断・再開の技術 • ユースケース別実装パターン • 中断・再開処理のテスト • Sidekiq
Iterationについて 本日お話すること 8
• インフラ: Google Cloud • バックエンド: Rails ◦ 非同期処理にSidekiqを利用 SmartHRのシステム構成
9
Sidekiqとは 10 • Rubyで動作する非同期処理のライブラリ ◦ https://github.com/sidekiq/sidekiq ◦ ジョブ管理のRedisを利用 • 大量データ処理や時間がかかるタスクを非同期で処理
• 無償版(OSS), 有償版(PRO, ENTERPRISE)があり、プランによって提供される機能に 違いがある ◦ 有償版ではジョブが失われずに再取得できるsuper_fetch機能などがある ◦ SmartHRではENTERPRISE版を利用
長時間実行のジョブが 抱える課題 11
デプロイ担当者が 抱える悩み …… 12
長時間実行中のジョブを 止めるのが怖い …… 😰 13
• データや処理の不整合 ◦ 想定外の箇所でジョブが強制停止されてしまい、再実行時にメー ル通知などが二重送信されてしまうリスクがある • 実行時間の増加 (= ユーザー体験の低下 )
◦ 処理を最初から再実行する必要があるため、実行時間が大幅に 延びることがある ジョブを停止することの懸念点 14
• 以下を目視確認してからSidekiqを手動停止 ◦ 長時間実行されているジョブがない! 👈 ヨシ! ◦ ジョブを停止してもデータ登録に影響がなさそう! 👈 ヨシ!
• イマダ!! Sidekiq停止! デプロイ開始ダ!! ウオー!! 暫定的な回避策 ... 15
いやー辛い …… 😭 16
これを解決するのが 中断・再開処理 17
ジョブの中断・再開処理の 技術 18
中断・再開処理とは • 中断処理 ◦ Sidekiqのプロセス停止を検知して任意のタイミング で 非同期処理を停止させること • 再開処理 ◦
再開した非同期処理を中断した箇所 から継続させること 19
メリット • デプロイ担当者 ◦ 安心かつ安全 に非同期処理を停止できるようになり、デプロイがスムーズに進 められる • 利用ユーザー ◦
実行した非同期処理を最低限の実行時間 で終えることができる 20
長時間非同期処理において 中断・再開処理は 必須の技術 21
中断・再開処理の全体像 Sidekiq プロセス Redis 1 ジョブは進捗を保存しながら 処理を進行 22
中断・再開処理の全体像 Sidekiq プロセス Redis 1 ⚡すべて処理終了! Sidekiq プロセス Redis 2
ジョブは進捗を保存しながら 処理を進行 23
中断・再開処理の全体像 Sidekiq プロセス Redis 1 中断処理 ジョブはSidekiqの処理終了を検知して 任意のタイミングで処理を停止 Sidekiq プロセス
Redis 2 Sidekiq プロセス Redis ジョブは進捗を保存しながら 処理を進行 3 24 ⚡すべて処理終了!
中断・再開処理の全体像 Sidekiq プロセス Redis 1 中断処理 ジョブはSidekiqの処理終了を検知して 任意のタイミングで処理を停止 Sidekiq再起動... Redis
Sidekiq プロセス Redis 2 Sidekiq プロセス Redis ジョブは進捗を保存しながら 処理を進行 3 Sidekiq プロセス 4 25 ⚡すべて処理終了!
中断・再開処理の全体像 Sidekiq プロセス Redis 1 中断処理 ジョブはSidekiqの処理終了を検知して 任意のタイミングで処理を停止 Sidekiq再起動... Redis
Redis 再開処理 ジョブは進捗を取得し処理を継続 Sidekiq プロセス Redis 2 Sidekiq プロセス Redis ジョブは進捗を保存しながら 処理を進行 3 Sidekiq プロセス 4 Sidekiq プロセス 5 26 ⚡すべて処理終了!
中断処理 27
中断処理 28 • 概要 ◦ Sidekiqのプロセス停止を検知して任意のタイミングで非同期処 理を停止させること
Sidekiq設定ファイルにイベントハンドリングを設定 config/initializers/sidekiq.rb Sample 29 • Sidekiqの処理終了(quiet)・停止 (stop)を管理する変数を定義、フラグ として利用する • イベントハンドリングを設定し、
Sidekiqの処理終了と停止を検知 したらフラグをあげる • このフラグで中断を行うかの 判定条件に利用する 参考: https://github.com/sidekiq/sidekiq/wiki/Signals https://github.com/sidekiq/sidekiq/wiki/Deployment
Sidekiqの停止を検知したら例外を送出するメソッドを定義 app/workers/application_worker.rb 30 Sample app/workers/application_worker.rb
中断処理の組み込み 31 • performメソッド内で raise_if_shutting_downメソッドを呼ぶ • Sidekiqプロセスが処理終了シグナルを受 け取った場合、ジョブは例外を発生させて 処理を終了。受け取っていない場合はそ のまま処理を継続します。
Sample DB更新、メール送信などの 様々な処理
• データ登録や各種処理の後、区切 りの良いタイミングで中断処理を入 れる • 例えば、ループ処理の場合はルー プ内の各処理が完了した後に中断 を確認することで、データや処理の 整合性を保つことができる 大事なポイント
: 中断処理を入れるタイミング 32 Sample DB更新、メール送信などの 様々な処理
再開処理 33
• 概要 ◦ 再開した非同期処理を中断した箇所から処理を継続させること 再開処理 34
再開処理の全体像 35 Redis 行番号 1 2 3 4 5 ⚡中断
• 例) CSVで5件のデータをインポートする 1回目の実行 1行目 終わり! 2行目 終わり! 3行目 終わり! 進捗管理 DB データ登録
再開処理の全体像 36 Redis 行番号 1 2 3 4 5 ⚡中断
• 例) CSVで5件のデータをインポートする 1回目の実行 1行目 終わり! 2行目 終わり! 3行目 終わり! 進捗管理 DB データ登録 Redis 行番号 1 2 3 4 5 2回目の実行 1行目 終わり! 2行目 終わり! 3行目 終わり! 4行目 終わり! 5行目 終わり! スキップ 進捗管理 DB データ登録
① ID番号保持方式 ② 行番号保持方式 進捗管理の方式 37
• 用途 ◦ IDなどテーブルの主キーを用いたデータベースへの一括処 理 ▪ 例) データの一括削除 • ポイント
◦ RedisのキーはSidekiqのジョブID + IDの種類名とする ▪ IDの種類名: item_id などデータの種類を示すもの ▪ 1回のジョブで複数データの進捗を管理する 場合もあるため ◦ 処理済みのIDをRedisに追加 ID番号保持方式 38 キー:#{ジョブID}/#{IDの種類名} Redis aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb cccccccc-cccc-cccc-cccc-cccccccccccc …
39 Sample Redisに指定IDの存在確認をする RedisにIDを追加する RedisからIDを削除する 一意なキーを生成する
40 Sample Redisでデータを管理するための一意なキーを生成する
41 Sample 指定IDの存在確認をする
42 Sample RedisにIDを追加して進捗を記録する
43 Sample RedisからIDをすべて削除する
44 Sample 処理済みか判定しスキップをする 処理がすべて完了したらRedisから 進捗データをすべて削除する 進捗を登録する ID番号保持方式の組み込み例
• 用途 ◦ 主にCSVファイルからのデータインポート系 • ポイント ◦ RedisのキーはSidekiqのジョブIDとする ◦ 処理済みの行番号とエラーメッセージ
をRedisに追加 行番号保持方式 45
CSVインポート系の課題: 行ごとに複数個のエラーが発生する可能性がある 46 社員番号 姓 名 部署1 部署 sample-001 須磨
英知 システム部 2行目:登録済みの社員番号です。 2行目 部署1 部署:存在しない部署名です。
種類毎にキーを分けて Redisで進捗管理する 47 キー:#{jid} Redis 1行目 2行目 3行目 処理済みの行番号 失敗した行番号
3行目 … 3行目:登録済みの社員番号です。 3行目:XXXXXは存在しない部署です。 ... 行ごとのエラーメッセージ errors/#{jid} error-logs/#{jid}
48 Redisで種類別の進捗管理するた めのキーを生成する Sample Redisに行番号を追加する
49 Sample Redisで種類別の進捗管理するためのキーを生成する
50 Sample Redisに処理済みの行番号、失敗した行番号、エラーメッセージを追加する
Sample Redisに処理済みの行番号、失敗した行番号、エラーメッセージを追加する 51 処理済みの行番号を保存する
Sample Redisに処理済みの行番号、失敗した行番号、エラーメッセージを追加する 52 失敗した行番号を保存する
Sample Redisに処理済みの行番号、失敗した行番号、エラーメッセージを追加する 53 行に紐づく複数個のエラーメッセージを Redis に登録する
Sample Redisに処理済みの行番号、失敗した行番号、エラーメッセージを追加する 54 エラーメッセージ毎にスコア付けをすることで 順番に並ぶようにする
55 Sample 処理済みか判定しスキップする 行番号とエラーメッセージの 進捗を追加する 行番号保持方式の組み込み例
56 Sample エラーメッセージの進捗を登 録する 行番号保持方式の組み込み例
ユースケース別 実装パターン 57
• 実行時間が短い & ファイル容量が小さいファイルエクスポート • 実行時間が長い & ファイル容量が大きいファイルエクスポート 実際に遭遇した開発例 58
実行時間が 短い & ファイル容量が 小さい ファイルエクスポート 59
• 想定される実行時間 ◦ 数分 • 想定されるファイル容量 ◦ 数MB以下 • 処理内容
◦ DBからデータ抽出したものをファイル書き出しするのみ 実行時間が短い & ファイル容量が小さいファイルエクスポート 60
実行時間が短い & ファイル容量が小さいファイルエクスポート 61 • 実装方針 ◦ 処理が冪等であるため中断処理のみで十分 ◦ 実行時間が数分程度と短く、再実行してもユーザー体験に影響が少ない場合、再開処理は不要
◦ 中断箇所からの再開処理は必ずしも必須ではない Sample
実行時間が 長い & ファイル容量が 大きい ファイルエクスポート 62
• 想定される実行時間 ◦ 数時間以上 • 想定されるファイルサイズ ◦ 数百MB • 処理内容
◦ 従業員の履歴情報のダウンロード 実行時間が長い & ファイル容量が大きいファイルエクスポート 63
運用上の問題 • デプロイに影響 ◦ SmartHRでは毎日デプロイを行っている ◦ 長時間のジョブが実行中の場合は内容に応じてデプロイを スキップする可能性がある ▪ 中断は可能だが最初から再実行に伴いお客様への業務影響が大きい
▪ 重要なリリースがある場合に、終わるまで粘るか、延期するか苦しい判断 をしなくてはならない......😭 64
技術的な問題 • ファイルサイズが大きい ◦ 非同期処理の過程で生成される大きい一時ファイルを中断時にどこに保存 するか🤔 65
ファイルの保存先候補 66 • Redis • オブジェクトストレージ (GCS)
• Redis ◦ 巨大なファイルを保存する際のRedisにかかる負荷影響が不明 ▪ ネット上では負荷がかかるという記事あり ◦ Redisのメモリー容量を超える危険性 ▪ 万が一メモリー上限に達してしまった場合に
Sidekiqの運用に影響がでる ファイルの保存先候補 67
ファイルの保存先候補 68 • オブジェクトストレージ (GCS) ◦ 大容量のファイルの保存が可能 ◦ ライブラリを使って簡単にアップロード可能 ◦
一時ファイルに有効期限を設けて削除することも可能 ◦ セキュリティが強固(暗号化、アクセス制御機能) ◦ GCSへの転送速度も速い 今回の用途としては最適 !!! 😆
中断時に一時ファイルをオブジェクトストレージに保存する 69 Sample 中断が発生したら例外をキャッチし 作成中の一時ファイルを GCSにアップロードする
再開可能かを判 Sample 初回は一時ファイルを新規作成し、再開時は一時ファイルを復元する 70 resumable?: 進捗データが保存されているか && GCSに一時ファイル が保存されているか restore_csv_from_gcs:
GCSから一時ファイルを取得し 指定の場所に配置をする create_csv_file: CSVファイルを新規作成する
中断・再開処理のテスト 71
• 中断処理 ◦ 意図した通りに停止するか ◦ 中断時にデータが適切に保存され、一貫性が保たれているか • 再開処理 ◦ 再開後に正しく途中から処理を再開するか
◦ 処理済みのデータがスキップされ、再度処理されていないか 中断・再開処理のテスト観点 72
中断のみの RSpec実装例 73
74 Sample 中断のみの実装例
75 Sample and_wrap_originalを利用して中断を再現 • and_wrap_original は元のメソッドをそのまま呼び出しつつ、追加の処理を行いたいときに使う RSpecのメソッド • このテストでは中断処理が正しく動くかを確認するために、任意のタイミングで中断が発生することを再現して いる
中断のトリガーとなる例外を 3 件目で発生させる
76 Sample 中断が発生したことで意図した通りの状態になっているかを確認 # 3件目までが処理済み # 3件目以降は未処理
中断 + 再開処理の RSpec実装例 77
78 Sample 中断 + 再開処理の実装例
79 Sample and_wrap_originalを利用して中断を再現 中断のトリガーとなる 例外を3件目で発生させる Sample
80 Sample worker.performを2回呼び 出すことで再開処理を再現 し、期待する挙動になるかを 確認する 再開処理の確認
Sidekiq Iterationについて 81
Sidekiq Iterationとは • 2024/07/03 Sidekiq v7.3.0で Sidekiq Iterationという新機能が導入 ◦ https://github.com/sidekiq/sidekiq/wiki/Iteration
◦ This feature should be considered BETA until the next minor release. • 機能 ◦ 中断時に進行状況(カーソル)を保存し、再開時には保存したポイントから処理を再 開 ◦ on_start, on_resume, on_stop, on_completeなどのコールバック ◦ Active Record, CSV, 配列用にEnumeratorを生成するヘルパーメソッド 82
• Sidekiq Iterationは進捗管理は自動化されており、我々の実装よりも手間を減らし、 簡単に中断・再開が導入できる • コールバックを活用することで、中断・再開時の柔軟な個別処理が実装しやすくなる ◦ 例) 中断時の一時ファイルのGCSへの退避など •
(2024/10/20時点) ベータ状態なので本番導入が注意が必要 • 今後 Sidekiq Iterationの導入を検討する価値がある Sidekiq Iterationの所感 83
まとめ 84
まとめ 85 • 長時間の非同期処理には中断・再開処理が重要で、導入すること で安心・安全な運用が可能になります。 • SmartHRで実際に導入している中断・再開処理の実装・設計方針 を紹介しました • 同様の課題を抱えている場合、参考にしていただけると幸いです
ありがとうございました ! 86