Schedulers
定義された時間間隔で function を実行します。
Schedulers を使用する理由
application に、期限切れ record の cleanup、daily email digest の送信、external API からの data sync、credential の rotation など、繰り返し実行する background task が必要です。scheduler がなければ、cron infrastructure の管理、fault tolerance の確保、deployment coordination の処理を自身で行う必要があります。
scheduler では、function を decorate して deploy するだけです。
// A decorated method that runs on a schedule
@scheduler('cleanupExpiredSessions', CronExpression.EVERY_DAY_AT_MIDNIGHT)
async cleanupExpiredSessions(): Promise<void> {
const sessions = this.squid.collection('sessions');
const expired = await sessions
.query()
.where('expiresAt', '<', new Date())
.dereference()
.snapshot();
for (const session of expired) {
await sessions.doc(session.id).delete();
}
}
cron server は不要です。infrastructure も不要です。時間どおりに実行される function だけで済みます。
概要
Schedulers は、UTC time に基づいて定義された interval で自動的に実行される backend function です。client request による trigger を必要としない、繰り返し実行される background task に最適です。
Scheduler を使用する場合
| ユースケース | 推奨 |
|---|---|
| schedule に従って繰り返し background task を実行する | ✅ Scheduler |
| database の変更に反応する | Triggers を使用 |
| client から function を呼び出す | Executables を使用 |
| external service に HTTP endpoint を公開する | Webhooks を使用 |
仕組み
SquidServiceを拡張する class 内で、method を@scheduler()で decorate します- schedule 用に cron expression または
CronExpressionenum value を指定します - Squid が deploy 時に scheduler を検出して登録します
- function は UTC で、schedule された各 interval に自動実行されます
- log と error は Squid Console で確認できます
クイックスタート
前提条件
squid initで初期化された Squid backend project- NPM からインストールされた
@squidcloud/backendpackage
ステップ 1: Scheduler を作成する
SquidService を拡張する service class を作成し、scheduler function を追加します。
import { CronExpression, SquidService, scheduler } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@scheduler('logHeartbeat', CronExpression.EVERY_MINUTE)
async logHeartbeat(): Promise<void> {
console.log('Scheduler is running:', new Date().toISOString());
}
}
ステップ 2: service を export する
service が service index file から export されていることを確認します。
export * from './example-service';
ステップ 3: backend を開始または deploy する
ローカル開発では、Squid CLI を使用して backend をローカルで実行します。
squid start
cloud に deploy するには、backend の deployを参照してください。
ステップ 4: 検証する
Squid Console log を確認し、scheduler が想定どおりの interval で実行されていることを確認します。
コアコンセプト
Cron expression
@scheduler decorator は、function の実行時刻を定義する cron expression string を受け取ります。すべての時刻は Coordinated Universal Time(UTC)です。expression を定義する際は、希望する local time を UTC に変換してください。
cron expression は次の format に従います。
* * * * * *
| | | | | |
| | | | | day of week
| | | | months
| | | day of month
| | hours
| minutes
seconds (optional)
例:
| Expression | Schedule |
|---|---|
0 0 * * * | 毎日 UTC 0 時 |
0 */6 * * * | 6 時間ごと |
30 9 * * 1-5 | 平日 UTC 午前 9 時 30 分 |
0 0 1 * * | 毎月 1 日の UTC 0 時 |
CronExpression enum
CronExpression enum は predefined interval を提供するため、cron string を手動で記述する必要はありません。
import { CronExpression, scheduler } from '@squidcloud/backend';
@scheduler('everyMinute', CronExpression.EVERY_MINUTE)
async everyMinute(): Promise<void> { /* ... */ }
@scheduler('everyHour', CronExpression.EVERY_HOUR)
async everyHour(): Promise<void> { /* ... */ }
@scheduler('daily', CronExpression.EVERY_DAY_AT_MIDNIGHT)
async daily(): Promise<void> { /* ... */ }
exclusive parameter
@scheduler decorator は、scheduler name、cron expression、optional な exclusive boolean の 3 つの parameter を受け取ります。
exclusive が true(default)の場合、同時に実行される scheduler instance は 1 つだけです。前回の invocation がまだ実行中に新しい invocation が schedule されると、新しい invocation は skip されます。
// Default: exclusive is true, so overlapping runs are skipped
@scheduler('sendEmailReminders', CronExpression.EVERY_MINUTE, true)
async sendEmailReminders(): Promise<void> {
// If this takes longer than 1 minute, the next invocation is skipped
}
exclusive が false の場合、前回の instance が完了しているかどうかにかかわらず、新しい instance は schedule どおりに実行されます。複数の instance が concurrent に実行される可能性があります。
// Non-exclusive: allows concurrent runs
@scheduler('processQueue', CronExpression.EVERY_MINUTE, false)
async processQueue(): Promise<void> {
// Multiple instances may run in parallel
}
Scheduler の管理
scheduler は、プログラムで disable、re-enable、list 化できます。
Disable と enable:
disable された scheduler は、再度 enable されるまで実行されません。disabled state は redeploy 後も保持されますが、undeploy 後の deploy ではすべての scheduler が再度 enable されます。
// Disable a scheduler
await this.squid.schedulers.disable('logHeartbeat');
// Re-enable it later
await this.squid.schedulers.enable('logHeartbeat');
すべての scheduler を list 化する:
現在の state(enabled または disabled)を含む、すべての登録済み scheduler を返します。
const allSchedulers = await this.squid.schedulers.list();
console.log(allSchedulers);
Error Handling
Scheduler が throw した場合
scheduler function が error を throw すると、その error は log に記録され、scheduler は次に schedule された interval で実行を継続します。1 回の failure で scheduler が disable されることはありません。
Logging と debugging
scheduler function 内で console.log と console.error を使用します。output は Squid Console log で確認できます。
@scheduler('syncExternalData', CronExpression.EVERY_HOUR)
async syncExternalData(): Promise<void> {
console.log('Starting external data sync');
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`API returned status ${response.status}`);
}
const data = (await response.json()) as any[];
console.log(`Synced ${data.length} records`);
} catch (error) {
console.error('Sync failed:', error);
}
}
一般的な Error
| Error | 原因 | 解決策 |
|---|---|---|
| Scheduler が実行されない | service/index.ts で service が export されていない | service class が export されていることを確認する |
| Scheduler が実行されない | 変更後に backend が deploy されていない | squid deploy を実行する |
| Overlapping run が skip される | exclusive が true で、前回の run がまだ active | interval を増やすか、exclusive を false に設定する |
| 不正確な timing | cron expression が UTC ではなく local time を使用している | すべての時刻を UTC に変換する |
ベストプラクティス
-
idempotency を考慮して設計する。 retry または redeployment により、scheduler が想定より多く実行されることがあります。複数回実行されても同じ result が得られるよう logic を設計してください。
-
execution time を短く保つ。 長時間実行される scheduler は次の invocation と overlap したり、過剰な resource を消費したりする可能性があります。大きな task は小さな batch に分割してください。
-
exclusiveを適切に使用する。 database cleanup のように overlap すべきでない task では、exclusiveをtrue(default)のままにします。independent queue item の処理のように concurrent execution が安全な場合にのみ、falseに設定してください。 -
scheduler 内で error を処理する。 logic を try/catch block でラップし、error を log に記録します。これにより transient failure が unhandled exception になることを防止します。
-
scheduler を monitor する。 Squid Console を使用して、scheduler が時間どおりに実行されていることを確認します。
this.squid.schedulers.list()を使用して、scheduler state をプログラムで確認します。 -
shared resource を保護する。 scheduler が shared data を変更する場合は、downstream system に過剰な負荷をかけないよう、dependent service で rate and quota limiting を使用してください。
コード例
Database cleanup
daily schedule で期限切れ record を削除します。
import { CronExpression, SquidService, scheduler } from '@squidcloud/backend';
interface Session {
id: string;
userId: string;
expiresAt: Date;
}
export class CleanupService extends SquidService {
@scheduler('cleanupExpiredSessions', CronExpression.EVERY_DAY_AT_MIDNIGHT)
async cleanupExpiredSessions(): Promise<void> {
const sessions = this.squid.collection<Session>('sessions');
const expired = await sessions
.query()
.where('expiresAt', '<', new Date())
.dereference()
.snapshot();
for (const session of expired) {
await sessions.doc(session.id).delete();
}
console.log(`Cleaned up ${expired.length} expired sessions`);
}
}
Email digest の送信
毎週月曜日の UTC 午前 9 時に weekly summary email を送信します。
import { SquidService, scheduler } from '@squidcloud/backend';
interface UserActivity {
userId: string;
email: string;
actionsThisWeek: number;
}
export class NotificationService extends SquidService {
@scheduler('sendWeeklyDigest', '0 9 * * 1') // Monday at 9:00 AM UTC
async sendWeeklyDigest(): Promise<void> {
const users = this.squid.collection<UserActivity>('userActivity');
const activeUsers = await users
.query()
.where('actionsThisWeek', '>', 0)
.dereference()
.snapshot();
for (const user of activeUsers) {
await this.sendDigestEmail(user.email, user.actionsThisWeek);
}
console.log(`Sent digest to ${activeUsers.length} users`);
}
private async sendDigestEmail(email: string, actionCount: number): Promise<void> {
const apiKey = this.secrets['EMAIL_API_KEY'];
await fetch('https://api.email.example.com/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: email,
subject: 'Your Weekly Activity Summary',
body: `You completed ${actionCount} actions this week.`,
}),
});
}
}
External data の sync
external API から毎時 data を取得します。
import { CronExpression, SquidService, scheduler } from '@squidcloud/backend';
interface Product {
id: string;
name: string;
price: number;
}
export class SyncService extends SquidService {
@scheduler('syncProducts', CronExpression.EVERY_HOUR)
async syncProducts(): Promise<void> {
try {
const apiKey = this.secrets['CATALOG_API_KEY'];
const response = await fetch('https://api.catalog.example.com/products', {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!response.ok) {
console.error(`Catalog API error: ${response.status}`);
return;
}
const products = (await response.json()) as Product[];
const collection = this.squid.collection<Product>('products');
for (const product of products) {
await collection.doc(product.id).insert(product);
}
console.log(`Synced ${products.length} products`);
} catch (error) {
console.error('Product sync failed:', error);
}
}
}
関連項目
- Triggers - database の変更に反応する
- Executables - backend function を client に公開する
- Webhooks - external service に HTTP endpoint を公開する
- Rate and quota limiting - backend function を保護する
- Database - data に access し、管理する