← Cloudflare Workflows / workflows / build
Uspávání a opakování pokusů
Tento návod popisuje, jak uspat Workflow a/nebo nastavit opakování pro krok Workflow.
Uspání Workflow
Workflow můžete nastavit tak, aby jako explicitní krok přešel do režimu sleep, což se hodí, pokud chcete, aby Workflow počkal, naplánoval práci dopředu nebo se pozastavil, dokud nebude připraven vstup či jiný externí stav.
Uspání na relativní dobu
Použijte step.sleep chcete-li Workflow uspat na relativní dobu:
await step.sleep("sleep for a bit", "1 hour");Druhý argument pro step.sleep přijímá obojí number (milisekundy) nebo ve formátu čitelném pro člověka, jako je "1 minute" nebo "26 hours". Přijímané jednotky pro step.sleep při tomto způsobu použití jsou následující:
| "second"
| "minute"
| "hour"
| "day"
| "week"
| "month"
| "year"Uspání do pevného data
Použijte step.sleepUntil chcete-li Workflow uspat do konkrétního Date: to se může hodit, pokud máte časové razítko z jiného systému nebo chcete práci „naplánovat“ na konkrétní čas (např. neděle, 9:00 UTC).
// sleepUntil accepts a Date object as its second argument
const workflowsLaunchDate = Date.parse("24 Oct 2024 13:00:00 UTC");
await step.sleepUntil("sleep until X times out", workflowsLaunchDate);Můžete také zadat přímo časové razítko UNIX (v milisekundách od počátku epochy UNIX) do sleepUntil.
Opakování kroků
Každé volání step.do v rámci Workflow přijímá volitelný StepConfig, díky kterému můžete definovat chování opakování pro daný krok.
Pokud neuvedete vlastní konfiguraci opakování, Workflows použije následující výchozí hodnoty:
const defaultConfig: WorkflowStepConfig = {
retries: {
limit: 5,
delay: 10000,
backoff: "exponential",
},
timeout: "10 minutes",
};Pokud zadáváte vlastní StepConfig, můžete nakonfigurovat:
- Celkový počet pokusů pro daný krok (omezeno na 10,000 opakování na krok)
- Prodleva mezi pokusy. Použijte pevnou dobu trvání jako
numberv milisekundách nebo jako čitelný řetězec, případně použijte funkci, která vrátí další zpoždění. - Jaký algoritmus backoff použít mezi jednotlivými pokusy: některý z
constant,linear, neboexponential - Po jaké době (timeout) považovat krok za neúspěšný, včetně opakovaných pokusů, protože se timeout nastavuje pro každý pokus zvlášť
Pokud chcete například omezit krok na 10 opakování a nastavit mezi jednotlivými pokusy exponenciální prodlevu (počínaje 10 sekundami), předáte následující konfiguraci jako volitelný objekt funkci step.do:
let someState = await step.do(
"call an API",
{
retries: {
limit: 10, // The total number of attempts
delay: "10 seconds", // Delay between each retry
backoff: "exponential", // Any of "constant" | "linear" | "exponential";
},
timeout: "30 minutes",
},
async () => {
/* Step code goes here */
},
);Nastavení dynamického zpoždění opakování
Funkci pro zpoždění použijte, pokud má délka zpoždění dalšího pokusu záviset na neúspěšném pokusu nebo vyvolané chybě. To vám dává větší kontrolu než pevné zpoždění s constant, linear, nebo exponential backoff. Hodí se pro limity četnosti požadavků, zotavení navazujícího poskytovatele a krátkodobé výpadky sítě.
Funkce delay přijímá objekt s:
ctx- aktuálníWorkflowStepContext, včetněctx.attempt.error- chyba, která způsobila opakování.
Vraťte řetězec s dobou trvání, číslo v milisekundách nebo promise, který se vyhodnotí na jednu z těchto hodnot.
await step.do(
"sync customer",
{
retries: {
limit: 5,
delay: ({ ctx, error }) => {
if (error.message.includes("rate limit")) {
return `${ctx.attempt * 30} seconds`;
}
return "10 seconds";
},
},
},
async () => {
await syncCustomer();
},
);await step.do(
"sync customer",
{
retries: {
limit: 5,
delay: ({ ctx, error }) => {
if (error.message.includes("rate limit")) {
return `${ctx.attempt * 30} seconds`;
}
return "10 seconds";
},
},
},
async () => {
await syncCustomer();
},
);Vynutit selhání instance Workflow
Instanci Workflow můžete také vynutit selhání a ne opakování vyvoláním NonRetryableError uvnitř kroku.
To se hodí ve chvíli, kdy zjistíte trvalou (terminální) chybu z nadřazeného systému (například selhání autentizace) nebo jinou chybu, u které by opakování stejně nepomohlo.
// Import the NonRetryableError definition
import {
WorkflowEntrypoint,
WorkflowStep,
WorkflowEvent,
} from "cloudflare:workers";
import { NonRetryableError } from "cloudflare:workflows";
// In your step code:
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
await step.do("some step", async () => {
if (!event.payload.data) {
throw new NonRetryableError(
"event.payload.data did not contain the expected payload",
);
}
});
}
}Samotná instance Workflow okamžitě selže, žádné další kroky se nespustí a Workflow se nebude opakovat.
Pokud předchozí kroky zaregistrovaly obslužné rutiny rollbacku, tyto rutiny se přesto spustí předtím, než instance dosáhne svého konečného stavu.
Registrace rollback handlerů
Rollback handler můžete připojit k step.do() pro implementaci kompenzace ve stylu Saga. Pokud Workflow později selže, Workflows spustí registrované obslužné rutiny pro rollback v opačném step-start pořadí.
Neúspěšný krok s možnostmi rollbacku se může rollbacku účastnit spolu s libovolnými dokončenými kroky, které mají zaregistrovaný handler rollbacku. Pokud například krok vyvolá NonRetryableError po zaregistrování rollbacku se jeho rollback handler spustí s output nastaveno na undefined.
import { WorkflowEntrypoint } from "cloudflare:workers";
import { NonRetryableError } from "cloudflare:workflows";
export class OrderWorkflow extends WorkflowEntrypoint {
async run(_event, step) {
await step.do(
"reserve inventory",
async () => {
const reservation = await reserveInventory();
return { reservationId: reservation.id };
},
{
rollback: async ({ output }) => {
const { reservationId } = output;
await releaseInventory(reservationId);
},
rollbackConfig: {
retries: { limit: 3, delay: "10 seconds", backoff: "linear" },
timeout: "2 minutes",
},
},
);
await step.do("charge card", async () => {
throw new NonRetryableError("payment processor rejected the charge");
});
}
}import {
WorkflowEntrypoint,
type WorkflowEvent,
type WorkflowStep,
} from "cloudflare:workers";
import { NonRetryableError } from "cloudflare:workflows";
export class OrderWorkflow extends WorkflowEntrypoint<Env> {
async run(_event: WorkflowEvent<unknown>, step: WorkflowStep) {
await step.do(
"reserve inventory",
async () => {
const reservation = await reserveInventory();
return { reservationId: reservation.id };
},
{
rollback: async ({ output }) => {
const { reservationId } = output as { reservationId: string };
await releaseInventory(reservationId);
},
rollbackConfig: {
retries: { limit: 3, delay: "10 seconds", backoff: "linear" },
timeout: "2 minutes",
},
},
);
await step.do("charge card", async () => {
throw new NonRetryableError("payment processor rejected the charge");
});
}
}Rollback handlery přijímají:
error- chyba, kvůli které Workflow selhal.output- hodnota vrácená krokem forward, neboundefinedpokud krok selhal před vrácením
Můžete použít rollbackConfig pro řízení chování opakování obslužné rutiny pro rollback. Vyvolejte NonRetryableError z obslužné rutiny rollbacku, aby se okamžitě zastavilo opakování.
Zachytávání chyb Workflow
Pokud se neošetřená výjimka propaguje až na nejvyšší úroveň nebo kroky dosáhnou limitu počtu opakování, Workflow ukončí provádění ve stavu Errored stav.
Pokud se tomu chcete vyhnout, můžete zachytávat výjimky vyvolané step. To se hodí, pokud potřebujete spustit úklidové úlohy nebo máte podmíněnou logiku, která spouští další kroky.
Aby Workflow mohl pokračovat ve svém provádění, obalte kroky, u kterých je selhání povoleno, do try...catch blok.
...
await step.do('task', async () => {
// work to be done
});
try {
await step.do('non-retryable-task', async () => {
// work not to be retried
throw new NonRetryableError('oh no');
});
} catch (e) {
console.log(`Step failed: ${e.message}`);
await step.do('clean-up-task', async () => {
// Clean up code here
});
}
// the Workflow will not fail and will continue its execution
await step.do('next-task', async() => {
// more work to be done
});
...