DidoQ - 時間のかかる処理を、安心して待てるようにする
DidoQは、時間のかかる処理を裏側で確実に実行し、終わったら即座に通知する仕組みです。
たとえば「AIに長文を分析させる」「数千件のデータを集計する」といった処理は、ユーザーが画面で待っているには長すぎます。かといって「あとで見に来てください」では不親切です。
DidoQを使うと、こうした処理をバックグラウンドで確実に実行しながら、リアルタイムで進捗を画面に通知できます。途中でシステムがクラッシュしても、自動的に復旧して処理を続けます。
こんな場面で使えます
- AIによるファクトチェック: ユーザーが入力した主張を、複数の情報源と照らし合わせて検証する(数分かかる)
- レポート生成: 過去1年分のデータを集計してPDFレポートを作成する(数十秒〜数分)
- 画像処理: アップロードされた動画を複数の解像度に変換する(数分〜数時間)
- バッチ通知: 数千人のユーザーにメールやプッシュ通知を送信する
解決する3つの課題
1. 「処理が終わったか、何度も確認しないといけない」問題
従来の方法では、ユーザーが「更新ボタン」を何度も押したり、画面を再読み込みしたりする必要がありました。
DidoQの解決策: WebSocketを使って、処理が終わった瞬間に自動で通知します。更新ボタンは不要です。
2. 「サーバーが落ちたら、処理も消えてしまう」問題
メモリだけで処理を管理していると、サーバーがクラッシュした瞬間にすべての処理状況が失われます。
DidoQの解決策: すべての状態をデータベース(D1)に記録します。サーバーが復旧すると、自動的に処理を再開します。
3. 「処理が固まって、永遠に終わらない」問題
外部APIが応答しなくなったり、処理中に無限ループに陥ったりすると、ジョブが永遠に「処理中」のまま放置されます。
DidoQの解決策: タイムアウト検出機能により、一定時間応答がない処理は自動的に中断され、再試行されます。
仕組み:レストランの注文システムに例えると
DidoQの仕組みは、レストランの注文システムに似ています。
- 注文(ジョブの登録): お客さんが料理を注文します
- キッチン(処理の実行): 裏でシェフが料理を作ります
- 呼び出しベル(リアルタイム通知): 料理ができたら、お客さんの席にベルで知らせます
- 注文伝票(データベース): もしシェフが途中で倒れても、伝票があれば別のシェフが引き継げます
このシステムのおかげで、お客さんは席で安心して待っていられます。キッチンに何度も「まだですか?」と聞きに行く必要はありません。
技術的な特長
データベースが「真実の記録」
DidoQでは、すべてのジョブの状態をCloudflare D1(SQLiteデータベース)に保存します。メモリ上のキャッシュは高速化のためだけに使い、真の状態は常にデータベースから読み取ります。
これにより、システムが再起動しても、処理中だったジョブを正確に復元できます。
自動復旧の仕組み
DidoQは「アラーム」という仕組みを使って、定期的にデータベースをチェックします。もし処理が中断されていたら、そこから自動的に再開します。
人間が手動で復旧操作をする必要はありません。
ハートビートでタイムアウト検出
処理中のジョブは、定期的に「まだ生きてます」という信号(ハートビート)を送ります。この信号が一定時間途絶えると、DidoQは「何かおかしい」と判断し、処理を中断して再試行します。
複数の処理を並列実行(シャーディング)
DidoQはデフォルトで10個の「作業場」(シャード)を用意し、ジョブを分散して処理します。これにより、数千のジョブを同時に処理できます。
使い方の概要
サーバー側:ジョブを登録する
// ジョブをキューに追加
await enqueueJob(env, { tableName: 'jobs' }, {
id: crypto.randomUUID(),
userId: 'user-123',
jobType: 'fact-check',
payload: { claim: 'ユーザーが入力した主張' },
});
サーバー側:ジョブを処理する
export class FactCheckQueue extends DidoQDurableObject {
protected async processJobImpl(job) {
// AIに問い合わせて、ファクトチェックを実行
const result = await checkFactWithAI(job.payload.claim);
return { resultId: result.id };
}
}
クライアント側:進捗をリアルタイムで表示する
function FactCheckPage() {
const { status, isMonitoring } = useJobMonitor(
jobId,
{ apiBase: 'https://api.example.com' },
{
onComplete: (resultId) => {
// 処理完了!結果ページに移動
router.push(`/results/${resultId}`);
},
onError: (error) => {
toast.error('エラーが発生しました');
},
}
);
return (
<div>
<p>処理状況: {status}</p>
{isMonitoring && <Spinner />}
</div>
);
}
信頼性を支える機能
- 最大リトライ回数の制限: 何度も失敗する処理は、無限ループを防ぐため一定回数で停止します
- タイムアウト自動検出: 長時間応答のない処理は自動的に中断され、再試行されます
- ネットワーク切断対応: WebSocketが使えない環境では、自動的にポーリング(定期確認)に切り替わります
- 重複通知の防止: 同じ完了通知を何度も送らないよう、内部で制御しています
- イベント履歴の記録: すべての処理過程を記録し、トラブル時の調査に活用できます
技術スタック
DidoQは、次の技術を組み合わせて構築されています。
- Cloudflare Workers: エッジで動作する高速なサーバーレス環境
- D1: Cloudflare上のSQLiteデータベース(ジョブの状態を永続化)
- Durable Objects: ステートフルな処理とリアルタイム通信を実現
- WebSocket / Long Polling: クライアントへのリアルタイム通知
- React Hooks: フロントエンドでの簡単な状態管理(オプション)
- TypeScript: 型安全な開発体験
まとめ
DidoQは、時間のかかる処理を「確実に実行」し、「リアルタイムで通知」する仕組みです。
従来の「定期的にポーリングして確認する」方式や「メールで通知が来るまで待つ」方式と比べて、ユーザー体験を大きく向上させます。
しかも、サーバーがクラッシュしても自動復旧し、処理が固まっても自動でタイムアウトする信頼性を備えています。
Cloudflare Workersのエッジネットワーク上で動作するため、世界中どこからでも低遅延でアクセスできます。