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
6
3.2k
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
740
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
390
できる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
Compose 1.7のTextFieldはPOBox Plusで日本語変換できない
tomoya0x00
0
190
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
120
Better Code Design in PHP
afilina
PRO
0
120
AWS IaCの注目アップデート 2024年10月版
konokenj
3
3.3k
광고 소재 심사 과정에 AI를 도입하여 광고 서비스 생산성 향상시키기
kakao
PRO
0
170
Amazon Qを使ってIaCを触ろう!
maruto
0
400
シールドクラスをはじめよう / Getting Started with Sealed Classes
mackey0225
4
640
CSC509 Lecture 11
javiergs
PRO
0
180
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
330
LLM生成文章の精度評価自動化とプロンプトチューニングの効率化について
layerx
PRO
2
190
Jakarta EE meets AI
ivargrimstad
0
120
レガシーシステムにどう立ち向かうか 複雑さと理想と現実/vs-legacy
suzukihoge
14
2.2k
Featured
See All Featured
Gamification - CAS2011
davidbonilla
80
5k
Designing Experiences People Love
moore
138
23k
The Art of Programming - Codeland 2020
erikaheidi
52
13k
The Power of CSS Pseudo Elements
geoffreycrofte
73
5.3k
Intergalactic Javascript Robots from Outer Space
tanoku
269
27k
Teambox: Starting and Learning
jrom
133
8.8k
Designing on Purpose - Digital PM Summit 2013
jponch
115
7k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
GraphQLとの向き合い方2022年版
quramy
43
13k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
31
2.7k
The World Runs on Bad Software
bkeepers
PRO
65
11k
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