Skip to content

Commit be8afd3

Browse files
committed
Add some customization
1 parent e09dec2 commit be8afd3

File tree

2 files changed

+42
-9
lines changed

2 files changed

+42
-9
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: 31 additions & 9 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);
@@ -42,6 +47,22 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
4247

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

4768
async revalidate(msg: QueueMessage) {
@@ -55,7 +76,7 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
5576
// We don't need to revalidate in this case
5677
if (this.checkSyncTable(msg)) return;
5778

58-
if (this.ongoingRevalidations.size >= MAX_REVALIDATION_BY_DURABLE_OBJECT) {
79+
if (this.ongoingRevalidations.size >= this.maxRevalidations) {
5980
const ongoingRevalidations = this.ongoingRevalidations.values();
6081
// When there is more than the max revalidations, we block concurrency until one of the revalidations finishes
6182
// We still await the promise to ensure the revalidation is completed
@@ -86,7 +107,7 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
86107
"x-prerender-revalidate": process.env.__NEXT_PREVIEW_MODE_ID!,
87108
"x-isr": "1",
88109
},
89-
signal: AbortSignal.timeout(DEFAULT_REVALIDATION_TIMEOUT_MS),
110+
signal: AbortSignal.timeout(this.revalidationTimeout),
90111
});
91112
// Now we need to handle errors from the fetch
92113
if (response.status === 200 && response.headers.get("x-nextjs-cache") !== "REVALIDATED") {
@@ -158,20 +179,21 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
158179

159180
async addToFailedState(msg: QueueMessage) {
160181
const existingFailedState = this.routeInFailedState.get(msg.MessageDeduplicationId);
161-
let nextAlarm = Date.now() + 2_000;
182+
let nextAlarm = Date.now() + this.revalidationRetryInterval;
162183

163184
let updatedFailedState: FailedState;
164185

165186
if (existingFailedState) {
166-
if (existingFailedState.retryCount >= 6) {
187+
if (existingFailedState.retryCount >= this.maxRevalidationAttempts) {
167188
// We give up after 6 retries and log the error
168189
error(
169190
`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.`
170191
);
171192
this.routeInFailedState.delete(msg.MessageDeduplicationId);
172193
return;
173194
}
174-
nextAlarm = Date.now() + Math.pow(2, existingFailedState.retryCount + 1) * 2_000;
195+
nextAlarm =
196+
Date.now() + Math.pow(2, existingFailedState.retryCount + 1) * this.revalidationRetryInterval;
175197
updatedFailedState = {
176198
...existingFailedState,
177199
retryCount: existingFailedState.retryCount + 1,
@@ -206,7 +228,7 @@ export class DurableObjectQueueHandler extends DurableObject<CloudflareEnv> {
206228
);
207229
if (nextAlarmToSetup < Date.now()) {
208230
// We don't want to set an alarm in the past
209-
nextAlarmToSetup = Date.now() + 2_000;
231+
nextAlarmToSetup = Date.now() + this.revalidationRetryInterval;
210232
}
211233
await this.ctx.storage.setAlarm(nextAlarmToSetup);
212234
}

0 commit comments

Comments
 (0)