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
分散型タスクスケジューリングシステム
Search
NearMeの技術発表資料です
PRO
November 24, 2023
Programming
130
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
分散型タスクスケジューリングシステム
NearMeの技術発表資料です
PRO
November 24, 2023
More Decks by NearMeの技術発表資料です
See All by NearMeの技術発表資料です
Claude Code × git worktree で並列開発 (続き) -差分のサービスだけを併設する-
nearme_tech
PRO
1
50
Claude Code × git worktree で並列開発 — サブモジュール構成のリポジトリで成立させる —
nearme_tech
PRO
0
54
LLM + 強化学習
nearme_tech
PRO
0
31
PosthogのA/Bテスト機能の紹介
nearme_tech
PRO
1
44
AIフレンドリーなプロダクトに向けて
nearme_tech
PRO
2
65
初めてのLean言語
nearme_tech
PRO
0
100
Apache Airflow Workflow orchestration without turning cron into spaghetti
nearme_tech
PRO
2
37
実務で役立つ幾何学 ボロノイ図の基礎から グラフ・ネットワーク応用まで
nearme_tech
PRO
1
76
SQL/ID抽出タスクから考える 実践的なハルシネーション対策
nearme_tech
PRO
1
88
Other Decks in Programming
See All in Programming
DroidKaigi 2026 「個人開発という実験場: Android エンジニアが手にする4つの自由」
slashnephy
0
220
DynamoDBの基礎を振り返りながらベクトル検索機能を理解する
musan
3
270
GKE アップグレード前に知っておきたい Blue/Green と PDB の関係
stkk
0
160
Seeing Through Serverless: Observability for AWS Lambda with ADOT and CloudWatch Application Signals
seike460
PRO
1
120
【デモ】Kiroで体験する仕様駆動開発|設計からコーディングまでAIと進める開発フロー
cmkudo
0
600
業務時間外もAIに働いてもらう話
colorful12
3
10k
ハーネス設計入門 〜プロンプト、コンテキストの次〜
kinopeee
55
36k
AWS Step Functions 大規模並列の壁を越える / jaws-sonic-2026-niigata-step-functions
kasacchiful
PRO
0
390
数年滞っていたダークモード対応をおよそ2週間で完了させる
chigichan24
0
690
From 6 People Classroom Meetup to 100 People Regional Conference / FOSS4G Hiroshima 2026
furukawayasuto
0
120
Hono + Inertia + React で LP を構築した話
oukayuka
2
220
Vibes Containers 〜AIで変わるコンテナ設計と運用〜
tkikuc
2
500
Featured
See All Featured
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.7k
Music & Morning Musume
bryan
47
7.4k
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2.1k
Unsuck your backbone
ammeep
672
58k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
560
Crafting Experiences
bethany
1
300
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
260
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
250
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
240
Designing for humans not robots
tammielis
254
26k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
Transcript
0 分散型タスクスケジューリングシステム 2023-11-17 第68回NearMe技術勉強会 @yujiosaka
1
2 タスクスケジューリングの悩み 今決まった時間に実行している バッチ処理を、データベースに 登録された値に応じて、動的に タスクの実行間隔を制御したい タスクをスケジューリング するためだけにプロセスを 常駐させるのは面倒だし、 乗り換え作業も大掛かりだわ
3 やりたいことのイメージ const jobInterval = await dokkaKaraMottekuru(); // > “0
* * * *” — cron expression in string // > 1000 * 60 * 60 — milliseconds in number // > { “hours”: 1 } — date-fns Duration object // > etc. await schedule({ jobInterval }, async () => { }); await nannkaOmoiShori();
4 気をつけないといけないこと • どのタイミングでタスクの実⾏間隔(jobInterval)を更新するのか • どこにタスクの実⾏履歴を保存するのか • タスクをスケジューリングするためのプロセスを常駐させるのか → 最初に登録された実⾏間隔でタスクが実⾏され続けないようにしたい
→ プロセスを再起動した時に、実⾏間隔がリセットされないようにしたい → プロセスを常駐させるなら、そのプロセスをモニタリングしないといけない
5 node-cron https://github.com/kelektiv/node-cron 5
6 API cron.schedule(“0 * * * *”, () => {
nannkaOmoiShori(); })
7 やってること (概念的には)Cron Expressionをミリ秒に置き換えてsetIntervalを実⾏しているだけ (実際にはsetIntervalではなくてsetTimeoutを再起的に実⾏している) cron.schedule(“0 * * * *”,
() => { nannkaOmoiShori(); }) setInterval(() => { nannkaOmoiShori(); }, 1000 * 60 * 60);
8 嬉しいこと • 直感的に使えてシンプルなAPI • 依存関係がなく、インストールするだけですぐに使える 困ったこと • プロセスが再起動すると実⾏間隔がリセットされてしまう •
タスクの実⾏間隔(jobInterval)をいつ更新するべきかが悩ましい • プロセスを常駐させるため、タスクが実⾏されていない間のリソースが無駄 • プロセスの⽣存を常にモニタリングしなければならない • Cron型からプロセス常駐型への移⾏に⼿間が少しかかる
9 Agenda https://github.com/agenda/agenda 9
10 API const agenda = new Agenda({ db: { address:
process.env.MONGO_URI } }); await agenda.start(); // worker agenda.define(“nannka omoi shori”, async job => { await nannkaOmoiShori(); }); // scheduler await agenda.every(“0 * * * *”, “nannka omoi shori”);
11 やってること MongoDBをジョブキューとしてタスク処理を分散化 MongoDB Task Task Task Worker Worker Worker
Scheduler Task
12 嬉しいこと • 複数のプロセスでタスク処理を分散することができる • 並列度やタスクの優先度を細かく設定することができる • タスクスケジューリング以外にもキューとして使⽤できる 困ったこと •
スケジューラーが再起動するたびにスケジュールがリセットされてしまう • タスクの実⾏間隔(jobInterval)をいつ更新するべきかが悩ましい • スケジューラーは冗⻑化させることができず、モニタリングは相変わらず必要 • Cron型からキュー型への移⾏はとても⼤変 • MongoDB縛り(同様にbullはRedis縛り)
13 Cronのシンプルさとモダンなタスクスケジューラーの 柔軟性を持ち合わせたライブラリを作れないだろうか
14 Cronyx https://github.com/yujiosaka/Cronyx
15 API await cronyx.requestJobExec( { jobName: "hourly-job", jobInterval: "0 *
* * *", }, async (job) => { await nannkaOmoiShori(); }, );
16 やってること • 実際はタスクスケジューラーというよりタスクガードとして振る舞う • 直前のタスクの実⾏時間を保存し、条件を満たした時にだけタスクが実⾏される • 条件を満たしていない場合タスクを実⾏せず、すぐにPromiseをResolveする Database Lock
Lock Lock ロック要求 ロック要求
17 嬉しいこと • プロセスを常駐させる必要がないため、リソースが無駄にならずモニタリングも簡単 • タスクの実⾏間隔(jobInterval)の更新に強く、常に最新の設定が反映される • プロセスに障害が発⽣しても実⾏間隔がリセットされることがない • Cronを使い続けることができるので、乗り換えが楽
困ったこと • 「タスクスケジューラー」ではなく「タスクガード」なので、少し混乱するかも
18 その他の機能 • 依存関係の解決 • タスクの実⾏に遅延や障害が発⽣しても、⾃動で空⽩期間を埋めて復旧する • MongoDB、Redis、MySQL、Postgresの4つをデータソースとしてサポート • トランザクションやアトミックな処理等を使って安全なタスク分散を実現
• それ以外のデータソースも⾃分でプラグインを作成して利⽤できる
19 依存関係の解決 await cronyx.requestJobExec( { jobName: "child-job", jobInterval: "*/30 *
* * *", }, async (job) => { console.log(job.intervalStartedAt); console.log(job.intervalEndedAt); }, ); await cronyx.requestJobExec( { jobName: "parent-job", jobInterval: "0 * * * *", }, async (job) => { console.log(job.intervalStartedAt); console.log(job.intervalEndedAt); }, ); child-job.ts parent-job.ts requiredJobNames: ["child-job"],
20 その他の機能 • 依存関係の解決 • タスクの実⾏に遅延や障害が発⽣しても、⾃動で空⽩期間を埋めて復旧する • MongoDB、Redis、MySQL、Postgresの4つをデータソースとしてサポート • トランザクションやアトミックな処理等を使って安全なタスク分散を実現
• それ以外のデータソースも⾃分でプラグインを作成して利⽤できる
21 MongoDB export const mongodbJobLockSchema = new Schema({ jobName: {
type: String, required: true }, jobInterval: { type: Number, required: true, default: 0 }, jobIntervalEndedAt: { type: Date, required: true }, isActive: { type: Boolean, required: true, default: true }, createdAt: { type: Date, required: true, default: Date.now }, updatedAt: { type: Date, required: true, default: Date.now }, }).index({ jobName: 1, jobIntervalEndedAt: 1 }, { unique: true })
22 MongoDB try { return await this.#model.findOneAndUpdate( { jobName, jobIntervalEndedAt,
isActive: true }, { jobInterval, updatedAt: new Date() }, { setDefaultsOnInsert: true, new: true }, ); } catch (error) { throw error; } if (error instanceof MongoError && error.code === 11000) { return null; } , upsert: true
23 Cronからの移行手順 23
24 ステップ① cronyx.requestJobExecで元の処理を囲む await nannkaOmoiShori(); await cronyx.requestJobExec({ jobName: "job", jobInterval:
"0 * * * *", }, nannkaOmoisShori);
25 ステップ② Cronの実行時間をjobIntervalよりも短い間隔で設定する。 → こうすることで、遅延が発生しても自動で復旧できるようになる 0 * * * *
./nannka-omoi-shori.ts */10 * * * * ./nannka-omoi-shori.ts
26 これからやること • あらゆるサービスから利⽤できるように、HTTPサーバーを提供する • 様々な⾔語から利⽤できるように、クライアントを提供する やったこと
27 CronyxServer https://github.com/yujiosaka/CronyxServer
28 CronyxClient.js https://github.com/yujiosaka/CronyxClient.js
29 CronyxClient.py https://github.com/yujiosaka/CronyxClient.py
30 おまけ • CronyxServerはBun + Elysiaで実装 • その他のプロジェクトもNodeで動作するがBunファーストで開発(今度発表します)
31 解説記事 https://medium.com/@yujiisobe/cronyx-bridging-the-gap-between-cron-jobs-and-task-scheduling-790b9f709224
32 Thank you