Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

Alarms

最終更新 Markdown で表示Agent セットアップ

背景

Durable Objects のアラームを使うと、将来の時刻に Durable Object を起こすスケジュールを設定できます。アラームの予定時刻になると、alarm() ハンドラーメソッドが呼ばれます。アラームの変更は Storage API で行い、アラーム操作はほかのストレージ操作と同じ規則に従います。

特に次の点に注意してください。

  • 各 Durable Object は、setAlarm() を呼ぶことで、同時に 1 つのアラームだけをスケジュールできます。
  • アラームは少なくとも 1 回(at-least-once)の実行が保証され、alarm() ハンドラーが例外を投げると自動で再試行されます。
  • 再試行は指数バックオフで行われ、最初の失敗から 2 秒の遅延で始まり、最大 6 回まで再試行できます。

アラームは、Durable Objects の上にキューや作業のバッチ処理などの分散プリミティブを作るために使えます。また、受信リクエストに頼らずに Durable Object 内の処理を完了させる仕組みにもなります。完全な例は Alarms API を使う を参照してください。

1 つのアラームで複数イベントをスケジュールする

各 Durable Object が同時に持てるアラームは 1 つだけですが、イベントの予定をストレージに保存し、alarm() ハンドラーで期限到来のイベントを処理してから、次のイベント向けに自分自身を再スケジュールすれば、多数の予定イベントと繰り返しイベントを管理できます。

import { DurableObject } from "cloudflare:workers";

export class AgentServer extends DurableObject {
  // Schedule a one-time or recurring event
  async scheduleEvent(id, runAt, repeatMs = null) {
    await this.ctx.storage.put(`event:${id}`, { id, runAt, repeatMs });
    const currentAlarm = await this.ctx.storage.getAlarm();
    if (!currentAlarm || runAt < currentAlarm) {
      await this.ctx.storage.setAlarm(runAt);
    }
  }

  async alarm() {
    const now = Date.now();
    const events = await this.ctx.storage.list({ prefix: "event:" });
    let nextAlarm = null;

    for (const [key, event] of events) {
      if (event.runAt <= now) {
        await this.processEvent(event);
        if (event.repeatMs) {
          event.runAt = now + event.repeatMs;
          await this.ctx.storage.put(key, event);
        } else {
          await this.ctx.storage.delete(key);
        }
      }
      // Track the next event time
      if (event.runAt > now && (!nextAlarm || event.runAt < nextAlarm)) {
        nextAlarm = event.runAt;
      }
    }

    if (nextAlarm) await this.ctx.storage.setAlarm(nextAlarm);
  }

  async processEvent(event) {
    // Your event handling logic here
  }
}

ストレージメソッド

getAlarm

  • getAlarm(): number | null

    • アラームが設定されている場合は、現在設定されているアラーム時刻を、UNIX epoch からの経過ミリ秒数として返します。それ以外の場合は null を返します。

    • alarm の実行中に getAlarm を呼ぶと、alarm ハンドラーの開始以降に setAlarm も呼んでいない限り、null を返します。

setAlarm

  • setAlarm(scheduledTimeMs number) : void

    • アラームを実行する時刻を設定します。時刻は UNIX epoch からの経過ミリ秒数で指定します。
    • すでにスケジュールがある状態で setAlarm を呼ぶと、既存のアラームを上書きします。

deleteAlarm

  • deleteAlarm(): void

    • 現在アラームが設定されている場合、そのアラームを解除します。

    • alarm() ハンドラー内で deleteAlarm() を呼ぶと、ベストエフォートで再試行を止められることがありますが、保証はありません。

ハンドラーメソッド

alarm

  • alarm(alarmInfo Object): void

    • スケジュールしたアラーム時刻に達したとき、システムから呼ばれます。

    • 任意のパラメーター alarmInfo オブジェクトには、次の 2 つのプロパティがあります。

      • retryCount number: このアラームイベントが再試行された回数です。
      • isRetry boolean: アラームが再試行されたかどうかを示す真偽値です。このアラームイベントが再試行である場合は true です。
    • 1 つの Durable Object インスタンスにつき、同時に実行される alarm() は常に 1 つだけです。

    • alarm() ハンドラーは少なくとも 1 回(at-least-once)の実行が保証され、失敗時は 2 秒の遅延から始まる指数バックオフで、最大 6 回まで再試行されます。これは直近の setAlarm() 呼び出しにだけ適用されます。メソッドがキャッチされない例外で失敗した場合に再試行されます。

    • このメソッドは async にできます。

この例では、Durable Object 内で setAlarm(timestamp) メソッドによるアラーム設定と、alarm() ハンドラーによるアラーム処理の両方を示します。

  • アラームが発火するたびに、alarm() ハンドラーが 1 回呼ばれます。
  • 予期しないエラーで Durable Object が終了した場合、alarm() ハンドラーは別のマシンで再インスタンス化されることがあります。
  • 短い遅延のあと、alarm() ハンドラーは別のマシン上で先頭から実行されます。
import { DurableObject } from "cloudflare:workers";

export default {
	async fetch(request, env) {
		return await env.ALARM_EXAMPLE.getByName("foo").fetch(request);
	},
};

const SECONDS = 1000;

export class AlarmExample extends DurableObject {
	constructor(ctx, env) {
		super(ctx, env);
		this.storage = ctx.storage;
	}
	async fetch(request) {
		// If there is no alarm currently set, set one for 10 seconds from now
		let currentAlarm = await this.storage.getAlarm();
		if (currentAlarm == null) {
			this.storage.setAlarm(Date.now() + 10 * SECONDS);
		}
	}
	async alarm() {
		// The alarm handler will be invoked whenever an alarm fires.
		// You can use this to do work, read from the Storage API, make HTTP calls
		// and set future alarms to run using this.storage.setAlarm() from within this handler.
	}
}
import time

from workers import DurableObject, WorkerEntrypoint

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return await self.env.ALARM_EXAMPLE.getByName("foo").fetch(request)

SECONDS = 1000

class AlarmExample(DurableObject):
    def __init__(self, ctx, env):
        super().__init__(ctx, env)
        self.storage = ctx.storage

    async def fetch(self, request):
        # If there is no alarm currently set, set one for 10 seconds from now
        current_alarm = await self.storage.getAlarm()
        if current_alarm is None:
            self.storage.setAlarm(int(time.time() * 1000) + 10 * SECONDS)

    async def alarm(self):
        # The alarm handler will be invoked whenever an alarm fires.
        # You can use this to do work, read from the Storage API, make HTTP calls
        # and set future alarms to run using self.storage.setAlarm() from within this handler.
        pass

次の例では、alarmInfo プロパティを使い、そのアラームイベントが以前に試行されたかどうかを判別します。

class MyDurableObject extends DurableObject {
	async alarm(alarmInfo) {
		if (alarmInfo?.retryCount != 0) {
			console.log(
				"This alarm event has been attempted ${alarmInfo?.retryCount} times before.",
			);
		}
	}
}
class MyDurableObject(DurableObject):
    async def alarm(self, alarm_info):
        if alarm_info and alarm_info.get('retryCount', 0) != 0:
            print(f"This alarm event has been attempted {alarm_info.get('retryCount')} times before.")

関連リソース

役に立ちましたか?