Upgrade to Pro — share decks privately, control downloads, hide ads and more …

分散型タスクスケジューリングシステム

 分散型タスクスケジューリングシステム

More Decks by NearMeの技術発表資料です

Other Decks in Programming

Transcript

  1. 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();
  3. 8 嬉しいこと • 直感的に使えてシンプルなAPI • 依存関係がなく、インストールするだけですぐに使える 困ったこと • プロセスが再起動すると実⾏間隔がリセットされてしまう •

    タスクの実⾏間隔(jobInterval)をいつ更新するべきかが悩ましい • プロセスを常駐させるため、タスクが実⾏されていない間のリソースが無駄 • プロセスの⽣存を常にモニタリングしなければならない • Cron型からプロセス常駐型への移⾏に⼿間が少しかかる
  4. 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”);
  5. 12 嬉しいこと • 複数のプロセスでタスク処理を分散することができる • 並列度やタスクの優先度を細かく設定することができる • タスクスケジューリング以外にもキューとして使⽤できる 困ったこと •

    スケジューラーが再起動するたびにスケジュールがリセットされてしまう • タスクの実⾏間隔(jobInterval)をいつ更新するべきかが悩ましい • スケジューラーは冗⻑化させることができず、モニタリングは相変わらず必要 • Cron型からキュー型への移⾏はとても⼤変 • MongoDB縛り(同様にbullはRedis縛り)
  6. 15 API await cronyx.requestJobExec( { jobName: "hourly-job", jobInterval: "0 *

    * * *", }, async (job) => { await nannkaOmoiShori(); }, );
  7. 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"],
  8. 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 })
  9. 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