メインコンテンツまでスキップ

非同期 Jobs

クライアント間で長時間実行される作業を調整します。job を開始し、結果で解決し、どこからでも結果を待機できます。

Jobs を使用する理由

一部の作業は、単一のリクエストよりも長く続きます。大規模なレポートの生成、アップロードされたデータセットの処理、長時間実行される AI タスクなどです。作業を実行するコードと結果を待つコードは、多くの場合、別々の場所にあります。backend function が処理を行い、browser tab が結果を表示するために待機する、といった形です。

Jobs がない場合は、ステータス変更を確認するために database record をポーリングしたり、カスタム通知チャネルを構築したりする必要があります。Jobs を使用すると、producer は private ID のもとで job を開始し、完了時に解決します。そして、その ID を知っている任意の client が結果を待機できます。

// Producer: start the job, do the work, resolve it
const jobId = crypto.randomUUID();
await squid.job().startJob(jobId);
const result = await doLongRunningWork();
await squid.job().completeJob(jobId, result);
// Consumer: any client that knows the job ID
const result = await squid.job().awaitJob(jobId);
注記

Jobs API は現在、TypeScript Client SDK で利用できます。Python client には jobs API は含まれていません。

概要

Job は、非同期作業の 1 単位を追跡する軽量な record です。in_progresscompletedfailed のいずれかの status を持ち、解決後には result または error message を保持します。Jobs は client-owned です。caller が job ID を生成し、job を開始し、解決する責任を持ちます。Squid は owner が接続されている間 job を保持し、すべての waiter に結果を配信します。

Squid は、CLI agent model asks などの長時間実行される executions に対しても、内部的に同じ job の仕組みを使用します。これらも同じ getJob()awaitJob() 呼び出しで追跡できます。

Jobs を使用する場面

Use CaseRecommendation
Backend function が長時間の作業を実行し、client が待機するBackend で job を開始し、ID を返し、client で awaitJob() します
長時間実行される AI agent ask の進行状況を追跡するjobIdask() に渡すか askAsync() を使用し、その後 job を待機します
進行中の作業の現在の status を確認するgetJob()
UI を database changes と同期させる代わりに real-time queries を使用します
Function を schedule で実行する代わりに scheduler を使用します

仕組み

  1. Producer が一意で private な job ID を生成し、startJob(jobId) を呼び出します
  2. Squid が job を in_progress として mark します。owning client は自動的に job を alive に保ちます。owner が切断された場合、Squid は job を失敗させるため、waiter が待ち続けることはありません
  3. Consumer は awaitJob(jobId) を呼び出して結果を待つか、getJob(jobId) を呼び出して現在の status を poll します
  4. Producer は completeJob(jobId, result) または failJob(jobId, error) で job を解決します
  5. Job がすでに完了した後に subscribe した waiter も含め、すべての waiter が result(または error)を受け取ります

Quick Start

最も一般的な pattern は、backend の executable が長時間実行される作業を開始し、job ID をすぐに返し、client がその job を待機する形です。

Step 1: Backend で job を開始して解決する

Backend code
import { executable, SquidService } from '@squidcloud/backend';

export class ReportService extends SquidService {
@executable()
async startReportGeneration(reportType: string): Promise<string> {
this.assertIsAuthenticated();

const jobId = crypto.randomUUID();
await this.squid.job().startJob(jobId);

// Run the work out-of-band; the executable returns immediately.
this.generateReport(jobId, reportType);

return jobId;
}

private async generateReport(jobId: string, reportType: string): Promise<void> {
try {
const report = await this.buildReport(reportType); // Long-running work
await this.squid.job().completeJob(jobId, report);
} catch (error) {
await this.squid.job().failJob(jobId, String(error));
}
}
}

Step 2: Client から job を待機する

Client code
const jobId = await squid.executeFunction('startReportGeneration', 'quarterly');

// Resolves with the report once the backend completes the job,
// or throws if the backend fails it.
const report = await squid.job().awaitJob(jobId);

Core Concepts

Job ID は private capabilities です

Job ID は job に付与される唯一の credential です。job の ID を知っている人は誰でも、その status と result を読み取ることができます。ID は secret として扱ってください。

  • 推測できない ID を生成します(例: crypto.randomUUID()
  • Job ID を再利用しないでください
  • Result を見るべき client にのみ ID を共有してください

Jobs の開始と解決には API key が必要です。通常は backend(this.squid がすでに authenticated されている場所)または trusted server-side client です。Jobs の読み取りと待機には API key は不要で、ID だけが必要です。

Method reference

Jobs API には squid.job() でアクセスします。

MethodReturnsAPI key requiredDescription
startJob(jobId)Promise<void>Yesこの client が所有する job を開始します。既存の job に対しては idempotent です。
completeJob(jobId, result)Promise<void>Yesこの client が所有する job を、その result で正常に解決します
failJob(jobId, error)Promise<void>Yesこの client が所有する job を、error message とともに failed として解決します
getJob<T>(jobId)Promise<AsyncJob<T> | undefined>NoJob の現在の status と、completed の場合はその result を返します
awaitJob<T>(jobId)Promise<T>NoJob が終了するまで待機します。result で解決するか、throw します

AsyncJob type

getJob()AsyncJob を返します。

FieldTypeDescription
idstringJob ID
createdAtDateJob が開始された時刻
updatedAtDate最後に status が変更された時刻
status'in_progress' | 'completed' | 'failed'Job の現在の状態
resultT (optional)status'completed' の場合に存在する result
errorstring (optional)status'failed' の場合に存在する error message

Ownership と keep-alive

startJob() を呼び出した client が job の owner であり、その job を解決できる唯一の client です。別の client の job を解決しようとすると NOT_JOB_OWNER error で失敗します。Owner が接続を維持している間、SDK は background でその jobs を自動的に alive に保ちます。

Owner が解決前に切断またはクラッシュした場合、Squid は Job failed due to client failover で job を失敗させます。また、gracefully shut-down された client は未解決の jobs を Client shut down で失敗させます。どちらの場合も、waiter は永遠に待機するのではなく、error とともに解放されます。

Job が terminal state に達すると、その状態のままになります。すでに解決済みの job に対する後続の completeJob() または failJob() は無視されます。

Waiting semantics

  • awaitJob()すべての waiter に対して解決します。複数の clients が同じ job を待機でき、全員が result を受け取ります
  • Job がすでに完了した後に subscribe しても、terminal outcome ですぐに解決します
  • Waiter は job の開始前に subscribe できます。まだ job が存在しない ID に対する awaitJob() は単に待機し、job が開始されて完了すると resolve されます。裏を返せば、誤った ID や期限切れの job ID を待機すると無期限に待ち続けます。Error Handling を参照してください

Retention

Completed および failed jobs は、解決後およそ 1 時間 保持され、その後削除されます。結果は速やかに取得し、長期間必要なものは database または storage に永続化してください。In-progress jobs は sweep されません。解決されるか failover されるまで残ります。

AI Agents で Jobs を使用する

Agent asks は optional な job ID を受け取ることができ、これは長時間実行される requests に特に便利です。askAsync() を使用して blocking せずに request を発火し、その後 jobs API で追跡します。

Client code
const jobId = crypto.randomUUID();

// Returns immediately; the ask runs as a job.
await squid.ai().agent('research-assistant').askAsync('Analyze the quarterly report', jobId);

// Await the answer whenever (and wherever) you need it.
// An askAsync job resolves with the agent's answer as a plain string.
const answer = await squid.job().awaitJob<string>(jobId);

ask() または chat() の最後の argument として jobId を渡すことで、blocking call を他の clients から追跡可能にすることもできます。ask() を通じて開始された jobs は、plain string ではなく AiAskResponse object で resolve されます。

CLI agent models(Claude Code や Codex など)を使用する asks は、完了まで数分かかることがあるため、内部的には常に long-running jobs として実行されます。jobId を提供すると、単一の request を開いたままにする代わりに、待機または poll するための handle を得られます。

Error Handling

ErrorCauseSolution
UNAUTHORIZED on startJob/completeJob/failJobClient が API key で authenticated されていませんBackend、または API key で initialized された client から jobs を開始して解決します
NOT_JOB_OWNERClient が、別の client によって開始された job を解決しようとしましたJobs は、それを開始した client からのみ解決してください
awaitJob rejects with the job's errorProducer が failJob を呼び出したか、owner が切断されましたRejection を handle し、原因を調べるために error message を確認します
awaitJob never settlesJob ID が誤っているか、waiter が subscribe する前に job が期限切れになっています(解決後約 1 時間)Job ID を確認し、結果を速やかに待機し、待機処理を独自の timeout でラップしてください
Job fails with Failed to store job resultResult が storage size limit を超えました大きな payload は storage に保存し、reference で job を complete します

Best Practices

  1. 推測できない job IDs を生成してください。 ID は job に対する唯一の access control です。crypto.randomUUID() を使用し、連番や予測可能な値は使用しないでください。
  2. Jobs は必ず解決してください。 作業を try/catch で wrap し、error 時には failJob() を呼び出して、waiter が generic な failover error ではなく意味のある failure を受け取れるようにします。
  3. Backend から開始して解決してください。 Backend code はすでに API key で authenticated されており、作業の duration の間接続を維持します。
  4. Results は小さく保ってください。 Compact な payload または reference で jobs を complete してください。大きな artifacts は storage または database に保存します。
  5. Retention window 内に results を取得してください。 Terminal jobs は解決後およそ 1 時間で削除されます。保持が必要なものは永続化してください。

Next Steps

  • Executables - Jobs を開始して解決できる backend functions
  • AI agents - askAsync() を使用して agent asks を追跡可能な jobs として実行する
  • Database - Retention window を超えて job results を永続化する
  • Distributed locks - Shared resources への concurrent access を調整する