Skip to content

Commit 5f4568b

Browse files
committed
Add some customization
1 parent 3b821c8 commit 5f4568b

File tree

2 files changed

+40
-8
lines changed

2 files changed

+40
-8
lines changed

packages/cloudflare/src/api/cloudflare-context.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ declare global {
1818
NEXT_CACHE_REVALIDATION_DURABLE_OBJECT?: DurableObjectNamespace<DurableObjectQueueHandler>;
1919
// Asset binding
2020
ASSETS?: Fetcher;
21+
22+
// Below are the potential environment variables that can be set by the user to configure the durable object queue handler
23+
// The max number of revalidations that can be processed by the durable worker at the same time
24+
MAX_REVALIDATION_BY_DURABLE_OBJECT?: string;
25+
// The max time in milliseconds that a revalidation can take before being considered as failed
26+
REVALIDATION_TIMEOUT_MS?: string;
27+
// The amount of time after which a revalidation will be attempted again if it failed
28+
// If it fails again it will exponentially back off until it reaches the max retry interval
29+
REVALIDATION_RETRY_INTERVAL_MS?: string;
30+
// The maximum number of attempts that can be made to revalidate a path
31+
MAX_REVALIDATION_ATTEMPTS?: string;
2132
}
2233
}
2334

packages/cloudflare/src/api/durable-objects/queue.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import {
88
} from "@opennextjs/aws/utils/error.js";
99
import { DurableObject } from "cloudflare:workers";
1010

11-
const MAX_REVALIDATION_BY_DURABLE_OBJECT = 5;
11+
const DEFAULT_MAX_REVALIDATION_BY_DURABLE_OBJECT = 5;
1212
const DEFAULT_REVALIDATION_TIMEOUT_MS = 10_000;
13+
const DEFAULT_REVALIDATION_RETRY_INTERVAL_MS = 2_000;
14+
const DEFAULT_MAX_REVALIDATION_ATTEMPTS = 6;
1315

1416
interface FailedState {
1517
msg: QueueMessage;
@@ -29,8 +31,11 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
2931

3032
service: NonNullable<CloudflareEnv["NEXT_CACHE_REVALIDATION_WORKER"]>;
3133

32-
// TODO: allow this to be configurable - How do we want todo that? env variable? passed down from the queue override ?
33-
maxRevalidations = MAX_REVALIDATION_BY_DURABLE_OBJECT;
34+
// Configurable params
35+
maxRevalidations = DEFAULT_MAX_REVALIDATION_BY_DURABLE_OBJECT;
36+
revalidationTimeout = DEFAULT_REVALIDATION_TIMEOUT_MS;
37+
revalidationRetryInterval = DEFAULT_REVALIDATION_RETRY_INTERVAL_MS;
38+
maxRevalidationAttempts = DEFAULT_MAX_REVALIDATION_ATTEMPTS;
3439

3540
constructor(ctx: DurableObjectState, env: CloudflareEnv) {
3641
super(ctx, env);
@@ -41,6 +46,22 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
4146

4247
// We restore the state
4348
ctx.blockConcurrencyWhile(() => this.initState());
49+
50+
this.maxRevalidations = env.MAX_REVALIDATION_BY_DURABLE_OBJECT
51+
? parseInt(env.MAX_REVALIDATION_BY_DURABLE_OBJECT)
52+
: DEFAULT_MAX_REVALIDATION_BY_DURABLE_OBJECT;
53+
54+
this.revalidationTimeout = env.REVALIDATION_TIMEOUT_MS
55+
? parseInt(env.REVALIDATION_TIMEOUT_MS)
56+
: DEFAULT_REVALIDATION_TIMEOUT_MS;
57+
58+
this.revalidationRetryInterval = env.REVALIDATION_RETRY_INTERVAL_MS
59+
? parseInt(env.REVALIDATION_RETRY_INTERVAL_MS)
60+
: DEFAULT_REVALIDATION_RETRY_INTERVAL_MS;
61+
62+
this.maxRevalidationAttempts = env.MAX_REVALIDATION_ATTEMPTS
63+
? parseInt(env.MAX_REVALIDATION_ATTEMPTS)
64+
: DEFAULT_MAX_REVALIDATION_ATTEMPTS;
4465
}
4566

4667
async revalidate(msg: QueueMessage) {
@@ -50,7 +71,7 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
5071
// The route is already in a failed state, it will be retried later
5172
if (this.routeInFailedState.has(msg.MessageDeduplicationId)) return;
5273

53-
if (this.ongoingRevalidations.size >= MAX_REVALIDATION_BY_DURABLE_OBJECT) {
74+
if (this.ongoingRevalidations.size >= this.maxRevalidations) {
5475
const ongoingRevalidations = this.ongoingRevalidations.values();
5576
// When there is more than the max revalidations, we block concurrency until one of the revalidations finishes
5677
// We still await the promise to ensure the revalidation is completed
@@ -81,7 +102,7 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
81102
"x-prerender-revalidate": process.env.__NEXT_PREVIEW_MODE_ID!,
82103
"x-isr": "1",
83104
},
84-
signal: AbortSignal.timeout(DEFAULT_REVALIDATION_TIMEOUT_MS),
105+
signal: AbortSignal.timeout(this.revalidationTimeout),
85106
});
86107
// Now we need to handle errors from the fetch
87108
if (response.status === 200 && response.headers.get("x-nextjs-cache") !== "REVALIDATED") {
@@ -156,15 +177,15 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
156177
let updatedFailedState: FailedState;
157178

158179
if (existingFailedState) {
159-
if (existingFailedState.retryCount >= 6) {
180+
if (existingFailedState.retryCount >= this.maxRevalidationAttempts) {
160181
// We give up after 6 retries and log the error
161182
error(
162183
`The revalidation for ${msg.MessageBody.host}${msg.MessageBody.url} has failed after 6 retries. It will not be tried again, but subsequent ISR requests will retry.`
163184
);
164185
this.routeInFailedState.delete(msg.MessageDeduplicationId);
165186
return;
166187
}
167-
const nextAlarmMs = Date.now() + Math.pow(2, existingFailedState.retryCount + 1) * 2_000;
188+
const nextAlarmMs = Date.now() + Math.pow(2, existingFailedState.retryCount + 1) * this.revalidationRetryInterval;
168189
updatedFailedState = {
169190
...existingFailedState,
170191
retryCount: existingFailedState.retryCount + 1,
@@ -198,7 +219,7 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
198219
);
199220
if (nextAlarmToSetup < Date.now()) {
200221
// We don't want to set an alarm in the past
201-
nextAlarmToSetup = Date.now() + 2_000;
222+
nextAlarmToSetup = Date.now() + this.revalidationRetryInterval;
202223
}
203224
await this.ctx.storage.setAlarm(nextAlarmToSetup);
204225
}

0 commit comments

Comments
 (0)