INTEGRITY Dokumentace

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:

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:

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í:

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
});
...