cdk-nextjs 0.7.6 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +3 -3
- package/.jsii.tabl.json +1 -1
- package/README.md +12 -3
- package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +31 -13
- package/docs/adapter-runtime.md +1 -1
- package/docs/caching-guide.md +11 -1
- package/docs/harness-coverage.md +154 -73
- package/lib/adapter/adapter.mjs +12 -3
- package/lib/adapter/aws-cache-store.d.ts +221 -3
- package/lib/adapter/aws-cache-store.js +481 -35
- package/lib/adapter/aws-cache-store.test.js +706 -3
- package/lib/adapter/cache-handler.d.ts +5 -3
- package/lib/adapter/cache-handler.js +16 -10
- package/lib/adapter/cache-handler.mjs +517 -49
- package/lib/adapter/cache-handler.test.js +44 -1
- package/lib/adapter/cache-utils.d.ts +22 -0
- package/lib/adapter/cache-utils.js +44 -1
- package/lib/adapter/function-groups.js +17 -4
- package/lib/adapter/function-groups.test.js +33 -1
- package/lib/adapter/memory-cache-handler.d.ts +5 -1
- package/lib/adapter/memory-cache-handler.js +6 -2
- package/lib/adapter/s3-cache-handler.d.ts +12 -1
- package/lib/adapter/s3-cache-handler.js +30 -8
- package/lib/adapter/s3-cache-handler.test.js +84 -2
- package/lib/adapter/use-cache-common.d.ts +66 -1
- package/lib/adapter/use-cache-common.js +142 -19
- package/lib/adapter/use-cache-common.test.js +174 -1
- package/lib/adapter/use-cache-default-handler.js +36 -6
- package/lib/adapter/use-cache-default-handler.mjs +553 -47
- package/lib/adapter/use-cache-handlers.test.js +247 -7
- package/lib/adapter/use-cache-remote-handler.js +9 -7
- package/lib/adapter/use-cache-remote-handler.mjs +542 -45
- package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +2 -1
- package/lib/lambdas/post-deploy/prune-revalidation-table.js +35 -13
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +19 -1
- package/lib/nextjs-api.js +1 -1
- package/lib/nextjs-build/nextjs-build.js +1 -1
- package/lib/nextjs-cache.js +1 -1
- package/lib/nextjs-compute/nextjs-containers.js +1 -1
- package/lib/nextjs-compute/nextjs-functions.js +1 -1
- package/lib/nextjs-distribution.js +1 -1
- package/lib/nextjs-post-deploy.js +1 -1
- package/lib/nextjs-static-assets.js +1 -1
- package/lib/root-constructs/nextjs-base-construct.js +1 -1
- package/lib/root-constructs/nextjs-global-containers.js +1 -1
- package/lib/root-constructs/nextjs-global-functions.js +1 -1
- package/lib/root-constructs/nextjs-regional-containers.js +1 -1
- package/lib/root-constructs/nextjs-regional-functions.js +1 -1
- package/lib/runtime/core.js +20 -4
- package/lib/runtime/core.test.js +17 -1
- package/lib/runtime/image-utils.d.ts +8 -9
- package/lib/runtime/image-utils.js +16 -1
- package/lib/runtime/image-utils.test.js +13 -1
- package/lib/runtime/image.js +9 -3
- package/lib/runtime/lambda.mjs +66 -18
- package/lib/runtime/server.mjs +66 -18
- package/lib/runtime/tag-manifest.d.ts +40 -0
- package/lib/runtime/tag-manifest.js +59 -0
- package/lib/runtime/tag-manifest.test.d.ts +1 -0
- package/lib/runtime/tag-manifest.test.js +47 -0
- package/package.json +3 -3
- package/scripts/e2e-deploy.sh +7 -2
- package/scripts/e2e-harness/README.md +72 -35
- package/scripts/e2e-harness/screen.mjs +148 -7
- package/scripts/playwright-install-deps.sh +40 -0
|
@@ -34169,6 +34169,7 @@ var import_client_dynamodb2 = __toESM(require_dist_cjs19());
|
|
|
34169
34169
|
var import_debug5 = __toESM(require_src());
|
|
34170
34170
|
|
|
34171
34171
|
// src/adapter/aws-cache-store.ts
|
|
34172
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
34172
34173
|
var import_client_dynamodb = __toESM(require_dist_cjs19());
|
|
34173
34174
|
|
|
34174
34175
|
// src/adapter/cache-utils.ts
|
|
@@ -34177,6 +34178,19 @@ function sha256Hex(value) {
|
|
|
34177
34178
|
return createHash5("sha256").update(value).digest("hex");
|
|
34178
34179
|
}
|
|
34179
34180
|
|
|
34181
|
+
// src/runtime/tag-manifest.ts
|
|
34182
|
+
var OUTSIDE_REQUEST_SYMBOL = /* @__PURE__ */ Symbol.for(
|
|
34183
|
+
"cdk-nextjs.outside-request-context"
|
|
34184
|
+
);
|
|
34185
|
+
function outsideRequest(fn) {
|
|
34186
|
+
const global = globalThis;
|
|
34187
|
+
const run = global[OUTSIDE_REQUEST_SYMBOL];
|
|
34188
|
+
return run ? run(fn) : fn();
|
|
34189
|
+
}
|
|
34190
|
+
var TAG_MANIFEST_SYMBOL = /* @__PURE__ */ Symbol.for(
|
|
34191
|
+
"cdk-nextjs.use-cache.tag-manifest"
|
|
34192
|
+
);
|
|
34193
|
+
|
|
34180
34194
|
// src/adapter/aws-cache-store.ts
|
|
34181
34195
|
function resolveAwsCacheConfig() {
|
|
34182
34196
|
return {
|
|
@@ -34240,6 +34254,7 @@ function markerOf(item) {
|
|
|
34240
34254
|
}
|
|
34241
34255
|
var BATCH_GET_MAX_KEYS = 100;
|
|
34242
34256
|
var DEFAULT_TAG_REFRESH_MS = 1e3;
|
|
34257
|
+
var TAG_REFRESH_GRACE_MS = 1e3;
|
|
34243
34258
|
function hashedTag(tag) {
|
|
34244
34259
|
return `#${sha256Hex(tag)}`;
|
|
34245
34260
|
}
|
|
@@ -34453,10 +34468,64 @@ var TrackedTagMarkers = class {
|
|
|
34453
34468
|
* result rather than dropped.
|
|
34454
34469
|
*/
|
|
34455
34470
|
pendingRows = /* @__PURE__ */ new Map();
|
|
34471
|
+
/**
|
|
34472
|
+
* Log rows for tags not tracked and not being read, merged by tag, oldest
|
|
34473
|
+
* first: what the log has said about them since {@link completeSince}. Taken
|
|
34474
|
+
* into a tag's marker when it is read or written.
|
|
34475
|
+
*/
|
|
34476
|
+
logOnly = /* @__PURE__ */ new Map();
|
|
34477
|
+
/** See {@link completeSince}. */
|
|
34478
|
+
floor;
|
|
34479
|
+
/**
|
|
34480
|
+
* The future `expiredAt`s of markers dropped since, as `[from, to]`, in
|
|
34481
|
+
* order: once `from` is past, {@link completeSince} moves to `to`, and not
|
|
34482
|
+
* before, since only then does it expire anything. `from` and `to` are the
|
|
34483
|
+
* same until spans are merged for space ({@link laterFloor}).
|
|
34484
|
+
*/
|
|
34485
|
+
laterFloors = [];
|
|
34486
|
+
/** The in-memory entry stores judged by {@link completeSince}. */
|
|
34487
|
+
entryStores = [];
|
|
34488
|
+
/** How many times {@link forget} ran: a read started before one is made again. */
|
|
34489
|
+
forgotten = 0;
|
|
34456
34490
|
/** Tracked tags whose read failed, for the next refresh to read again. */
|
|
34457
34491
|
unread = /* @__PURE__ */ new Set();
|
|
34492
|
+
/**
|
|
34493
|
+
* Set inside a page render {@link catchUp} let go, for that render alone.
|
|
34494
|
+
* See {@link behindInRender}.
|
|
34495
|
+
*/
|
|
34496
|
+
caughtUpRender = new AsyncLocalStorage();
|
|
34497
|
+
/** When `sync` last scanned the entry stores ({@link oldestEntry}). */
|
|
34498
|
+
lastEntryScan = -Infinity;
|
|
34499
|
+
/**
|
|
34500
|
+
* No `logOnly` row is known by it or earlier, so a {@link pruneLogged}
|
|
34501
|
+
* against it or earlier drops nothing: a marker is only ever known by later,
|
|
34502
|
+
* as its `expiredAt` passes.
|
|
34503
|
+
*/
|
|
34504
|
+
prunedAgainst = -Infinity;
|
|
34505
|
+
/** When the last refresh started, or was skipped. */
|
|
34458
34506
|
lastRefresh = -Infinity;
|
|
34507
|
+
/**
|
|
34508
|
+
* The refresh in flight. One left open across a freeze comes back after the
|
|
34509
|
+
* thaw with what it read before, so {@link refresh} waits for it and then
|
|
34510
|
+
* for a fresh one, rather than the runtime waiting for it before the freeze:
|
|
34511
|
+
* that would bill every Lambda invocation for the query's tail.
|
|
34512
|
+
*/
|
|
34459
34513
|
refreshing;
|
|
34514
|
+
/**
|
|
34515
|
+
* When {@link refreshing} last showed the process running: a timer ticks
|
|
34516
|
+
* while it is in flight, and a tick that comes late, or none at all, means
|
|
34517
|
+
* the process was paused - frozen - since it started.
|
|
34518
|
+
*/
|
|
34519
|
+
lastTick = -Infinity;
|
|
34520
|
+
/** Whether a tick came late since {@link refreshing} started. */
|
|
34521
|
+
pausedWhileRefreshing = false;
|
|
34522
|
+
ticker;
|
|
34523
|
+
canFreeze;
|
|
34524
|
+
/**
|
|
34525
|
+
* When the last refresh settled, either way, unless it spanned a pause: what
|
|
34526
|
+
* it knows is from then. See {@link behind}.
|
|
34527
|
+
*/
|
|
34528
|
+
settledAt;
|
|
34460
34529
|
/**
|
|
34461
34530
|
* Where the next log query starts (`clock()`): the last successful one's
|
|
34462
34531
|
* start, less {@link REVALIDATION_LOG_LOOKBACK_MS}.
|
|
@@ -34478,20 +34547,68 @@ var TrackedTagMarkers = class {
|
|
|
34478
34547
|
this.random = options.random ?? Math.random;
|
|
34479
34548
|
this.debug = options.debug ?? (() => {
|
|
34480
34549
|
});
|
|
34550
|
+
this.canFreeze = options.canFreeze ?? false;
|
|
34481
34551
|
const at = this.clock();
|
|
34482
34552
|
this.cursor = at - REVALIDATION_LOG_LOOKBACK_MS;
|
|
34483
34553
|
this.lastLogRead = at;
|
|
34554
|
+
this.settledAt = at;
|
|
34555
|
+
this.floor = markerClock();
|
|
34484
34556
|
}
|
|
34485
34557
|
/** `tag`'s marker as this instance knows it, if it tracks `tag`. */
|
|
34486
34558
|
get(tag) {
|
|
34487
34559
|
return this.tags.get(tag);
|
|
34488
34560
|
}
|
|
34561
|
+
/**
|
|
34562
|
+
* Since when (`markerClock()`) this instance knows of every revalidation of
|
|
34563
|
+
* every tag, tracked or not: tracked ones from their markers and the log,
|
|
34564
|
+
* the others from the log alone (`logOnly`). So an entry created at or after
|
|
34565
|
+
* it can be judged by {@link state} and {@link expiration} without
|
|
34566
|
+
* {@link ensure} reading anything - which matters inside a `'use cache'`
|
|
34567
|
+
* lookup, where Next.js waits for the answer and a read pushes the entry out
|
|
34568
|
+
* of the static stage of a staged render.
|
|
34569
|
+
*
|
|
34570
|
+
* Starts at construction: the log is followed from before it. Moves to now
|
|
34571
|
+
* when the tracked markers are forgotten, and past the revalidations of
|
|
34572
|
+
* whatever else it lets go of - a marker evicted for space, a log row
|
|
34573
|
+
* pruned - which are no longer known: to its `expiredAt` only once that is
|
|
34574
|
+
* past, since a profile's `expire` can be a year out, and moving there at
|
|
34575
|
+
* once would turn the read-free path off for the instance's life.
|
|
34576
|
+
*
|
|
34577
|
+
* A row is pruned only once it is no later than every entry held, so that
|
|
34578
|
+
* costs those entries nothing. An entry stored afterwards with an older
|
|
34579
|
+
* `timestamp` - Next.js stamps the start of its generation, which can
|
|
34580
|
+
* outlast a lookback - is older than this too, and is read first.
|
|
34581
|
+
*
|
|
34582
|
+
* It does not cover a revalidation older than the log's first lookback
|
|
34583
|
+
* whose `expiredAt` is still to come, which would expire an entry created
|
|
34584
|
+
* after it. So the `'use cache'` handler reads an entry's tags in the
|
|
34585
|
+
* background as it stores it.
|
|
34586
|
+
*/
|
|
34587
|
+
get completeSince() {
|
|
34588
|
+
const at = markerClock();
|
|
34589
|
+
while (this.laterFloors.length > 0 && this.laterFloors[0][0] <= at) {
|
|
34590
|
+
this.floor = Math.max(this.floor, this.laterFloors.shift()[1]);
|
|
34591
|
+
}
|
|
34592
|
+
return this.floor;
|
|
34593
|
+
}
|
|
34594
|
+
/**
|
|
34595
|
+
* Register an in-memory store whose entries are judged by
|
|
34596
|
+
* {@link completeSince}, by the `timestamp` of the oldest entry it holds:
|
|
34597
|
+
* `logOnly` keeps the rows that could still apply to one of them, and no
|
|
34598
|
+
* others. With none registered it keeps nothing. Asked with `scan` false,
|
|
34599
|
+
* `oldestTimestamp` may answer early, but never late, without a scan.
|
|
34600
|
+
*/
|
|
34601
|
+
judgeEntriesOf(oldestTimestamp, holdsAny = () => oldestTimestamp(false) < Infinity) {
|
|
34602
|
+
this.entryStores.push({ oldestTimestamp, holdsAny });
|
|
34603
|
+
}
|
|
34489
34604
|
/**
|
|
34490
34605
|
* Track `tag` with `marker`: what this instance just wrote, which it knows
|
|
34491
34606
|
* without reading it back.
|
|
34492
34607
|
*/
|
|
34493
34608
|
set(tag, marker) {
|
|
34494
|
-
this.
|
|
34609
|
+
const logged = this.logOnly.get(tag);
|
|
34610
|
+
this.logOnly.delete(tag);
|
|
34611
|
+
this.remember(tag, mergeMarkers(marker, logged));
|
|
34495
34612
|
this.known(tag, this.clock());
|
|
34496
34613
|
}
|
|
34497
34614
|
/**
|
|
@@ -34544,27 +34661,115 @@ var TrackedTagMarkers = class {
|
|
|
34544
34661
|
const unread = [];
|
|
34545
34662
|
for (const tag of new Set(tags)) {
|
|
34546
34663
|
const inFlight = this.reading.get(tag);
|
|
34547
|
-
if (
|
|
34548
|
-
waits.push(inFlight);
|
|
34549
|
-
} else if (this.tags.has(tag)) {
|
|
34664
|
+
if (this.tags.has(tag)) {
|
|
34550
34665
|
this.touch(tag);
|
|
34666
|
+
} else if (inFlight) {
|
|
34667
|
+
waits.push(inFlight);
|
|
34551
34668
|
} else {
|
|
34552
34669
|
unread.push(tag);
|
|
34553
34670
|
}
|
|
34554
34671
|
}
|
|
34555
34672
|
if (unread.length > 0) {
|
|
34556
|
-
|
|
34557
|
-
for (const tag of unread) {
|
|
34558
|
-
this.reading.delete(tag);
|
|
34559
|
-
}
|
|
34560
|
-
});
|
|
34561
|
-
for (const tag of unread) {
|
|
34562
|
-
this.reading.set(tag, read);
|
|
34563
|
-
}
|
|
34564
|
-
waits.push(read);
|
|
34673
|
+
waits.push(this.startRead(unread));
|
|
34565
34674
|
}
|
|
34566
34675
|
await Promise.all(waits);
|
|
34567
34676
|
}
|
|
34677
|
+
/**
|
|
34678
|
+
* {@link readInto} `tags`, as the read of each that whoever needs one of
|
|
34679
|
+
* them next waits on ({@link reading}). Never rejects: a failed read leaves
|
|
34680
|
+
* the tags tracked.
|
|
34681
|
+
*/
|
|
34682
|
+
startRead(tags) {
|
|
34683
|
+
const read = this.readInto(tags).then(() => {
|
|
34684
|
+
for (const tag of tags) {
|
|
34685
|
+
this.reading.delete(tag);
|
|
34686
|
+
}
|
|
34687
|
+
});
|
|
34688
|
+
for (const tag of tags) {
|
|
34689
|
+
this.reading.set(tag, read);
|
|
34690
|
+
}
|
|
34691
|
+
return read;
|
|
34692
|
+
}
|
|
34693
|
+
/**
|
|
34694
|
+
* Whether the last refresh settled more than `refreshIntervalMs` plus
|
|
34695
|
+
* {@link TAG_REFRESH_GRACE_MS} ago - one that spanned a freeze counts from
|
|
34696
|
+
* when it started: after the instance sat idle or frozen - or always, with
|
|
34697
|
+
* a `refreshIntervalMs` of `0`. Answering from what it knows then could
|
|
34698
|
+
* serve an entry revalidated elsewhere since, however long ago that was, so
|
|
34699
|
+
* `refreshTags` waits for the refresh, and the runtime waits for it before
|
|
34700
|
+
* a page render starts ({@link catchUp}).
|
|
34701
|
+
*
|
|
34702
|
+
* From when it settled, not when it started, so a slow query the runtime
|
|
34703
|
+
* waited for before the render does not leave the instance behind inside
|
|
34704
|
+
* it. A failed one counts too, so one request does not wait for two: the
|
|
34705
|
+
* same "assume the entry is valid" a failed read answers with, until the
|
|
34706
|
+
* next refresh, at most the interval and the grace later.
|
|
34707
|
+
*/
|
|
34708
|
+
get behind() {
|
|
34709
|
+
return this.log !== void 0 && // `0` asks before every check, so every check waits for the answer.
|
|
34710
|
+
(this.refreshIntervalMs === 0 || this.overdue(this.settledAt));
|
|
34711
|
+
}
|
|
34712
|
+
/**
|
|
34713
|
+
* {@link behind}, as `refreshTags` asks it: never inside a page's render
|
|
34714
|
+
* {@link catchUp} caught up, since the runtime settled it for that request
|
|
34715
|
+
* before the render started, and a wait inside a `'use cache'` lookup is
|
|
34716
|
+
* what it settled it to avoid - a render crossing the line partway through
|
|
34717
|
+
* would otherwise wait after all. So a revalidation elsewhere can go unseen
|
|
34718
|
+
* for a render's duration on top of the usual window.
|
|
34719
|
+
*
|
|
34720
|
+
* A route handler, which no catch-up precedes, waits at the plain line.
|
|
34721
|
+
*/
|
|
34722
|
+
get behindInRender() {
|
|
34723
|
+
return this.refreshIntervalMs !== 0 && this.caughtUpRender.getStore() ? false : this.behind;
|
|
34724
|
+
}
|
|
34725
|
+
/**
|
|
34726
|
+
* Whether a registered store ({@link judgeEntriesOf}) holds any entry: one
|
|
34727
|
+
* a refresh could expire. With none there is nothing to wait for even when
|
|
34728
|
+
* the instance is {@link behind}, since whatever is stored from here on is
|
|
34729
|
+
* newer than what the log would say - which covers the first `'use cache'`
|
|
34730
|
+
* call of a process, which creates the handler inside the render.
|
|
34731
|
+
*/
|
|
34732
|
+
get holdsEntries() {
|
|
34733
|
+
return this.entryStores.some((store) => store.holdsAny());
|
|
34734
|
+
}
|
|
34735
|
+
/**
|
|
34736
|
+
* {@link refresh}, waited for when the instance is {@link behind}. The
|
|
34737
|
+
* runtime calls it before handing a page request to Next.js, outside the
|
|
34738
|
+
* staged render whose static stage a wait inside a `'use cache'` lookup
|
|
34739
|
+
* would cut short. Only for the `'use cache'` handler's entries
|
|
34740
|
+
* ({@link judgeEntriesOf}): the other caches wait for the refresh where they
|
|
34741
|
+
* read anyway. Not with a `refreshIntervalMs` of `0` either, where
|
|
34742
|
+
* `refreshTags` asks again regardless. Then `render`, marked as caught up
|
|
34743
|
+
* for ({@link behindInRender}). Rejects only as `render` does.
|
|
34744
|
+
*/
|
|
34745
|
+
async catchUp(render) {
|
|
34746
|
+
const log = (error2) => console.error("Error refreshing cache tags:", error2);
|
|
34747
|
+
if (this.refreshIntervalMs > 0 && this.behind) {
|
|
34748
|
+
if (!this.holdsEntries) {
|
|
34749
|
+
return render();
|
|
34750
|
+
}
|
|
34751
|
+
await this.refresh().catch(log);
|
|
34752
|
+
} else if (this.refreshIntervalMs > 0) {
|
|
34753
|
+
void this.refresh().catch(log);
|
|
34754
|
+
}
|
|
34755
|
+
return this.caughtUpRender.run(true, render);
|
|
34756
|
+
}
|
|
34757
|
+
/**
|
|
34758
|
+
* Run `fn` outside the request's context (`outsideRequest`), keeping only
|
|
34759
|
+
* whether the render was {@link catchUp caught up}: a cache handler method
|
|
34760
|
+
* called from inside a staged render, whose clock reads and AWS SDK calls
|
|
34761
|
+
* would otherwise end its static stage.
|
|
34762
|
+
*/
|
|
34763
|
+
outsideRender(fn) {
|
|
34764
|
+
const caughtUp = this.caughtUpRender.getStore();
|
|
34765
|
+
return outsideRequest(
|
|
34766
|
+
() => caughtUp ? this.caughtUpRender.run(caughtUp, fn) : fn()
|
|
34767
|
+
);
|
|
34768
|
+
}
|
|
34769
|
+
/** Whether `at` is more than the interval and the grace ago. */
|
|
34770
|
+
overdue(at) {
|
|
34771
|
+
return this.clock() - at > this.refreshIntervalMs + TAG_REFRESH_GRACE_MS;
|
|
34772
|
+
}
|
|
34568
34773
|
/**
|
|
34569
34774
|
* Catch up on revalidations other instances ran, at most once per
|
|
34570
34775
|
* `refreshIntervalMs`. See {@link TrackedTagMarkers} for the cost.
|
|
@@ -34575,6 +34780,9 @@ var TrackedTagMarkers = class {
|
|
|
34575
34780
|
return;
|
|
34576
34781
|
}
|
|
34577
34782
|
if (this.refreshing) {
|
|
34783
|
+
if (this.behind && this.pausedSinceRefreshStarted()) {
|
|
34784
|
+
return this.refreshing.then(() => this.refresh());
|
|
34785
|
+
}
|
|
34578
34786
|
return this.refreshing;
|
|
34579
34787
|
}
|
|
34580
34788
|
const at = this.clock();
|
|
@@ -34582,14 +34790,47 @@ var TrackedTagMarkers = class {
|
|
|
34582
34790
|
return;
|
|
34583
34791
|
}
|
|
34584
34792
|
this.lastRefresh = at;
|
|
34585
|
-
if (this.tags.size === 0) {
|
|
34793
|
+
if (this.tags.size === 0 && this.entryStores.length === 0) {
|
|
34586
34794
|
return;
|
|
34587
34795
|
}
|
|
34796
|
+
this.startTicking();
|
|
34588
34797
|
this.refreshing = this.sync(at, log).finally(() => {
|
|
34798
|
+
this.settledAt = Math.max(
|
|
34799
|
+
this.settledAt,
|
|
34800
|
+
this.pausedSinceRefreshStarted() ? this.lastRefresh : this.clock()
|
|
34801
|
+
);
|
|
34802
|
+
this.stopTicking();
|
|
34589
34803
|
this.refreshing = void 0;
|
|
34590
34804
|
});
|
|
34591
34805
|
return this.refreshing;
|
|
34592
34806
|
}
|
|
34807
|
+
startTicking() {
|
|
34808
|
+
if (!this.canFreeze) {
|
|
34809
|
+
return;
|
|
34810
|
+
}
|
|
34811
|
+
this.lastTick = this.clock();
|
|
34812
|
+
this.pausedWhileRefreshing = false;
|
|
34813
|
+
this.ticker = setInterval(() => {
|
|
34814
|
+
const at = this.clock();
|
|
34815
|
+
if (at - this.lastTick > REFRESH_PAUSE_MS) {
|
|
34816
|
+
this.pausedWhileRefreshing = true;
|
|
34817
|
+
}
|
|
34818
|
+
this.lastTick = at;
|
|
34819
|
+
}, REFRESH_TICK_MS);
|
|
34820
|
+
this.ticker.unref?.();
|
|
34821
|
+
}
|
|
34822
|
+
stopTicking() {
|
|
34823
|
+
clearInterval(this.ticker);
|
|
34824
|
+
this.ticker = void 0;
|
|
34825
|
+
}
|
|
34826
|
+
/**
|
|
34827
|
+
* Whether the process was paused since {@link refreshing} started: a tick
|
|
34828
|
+
* came late, or the next one is overdue (after a thaw, a request can run
|
|
34829
|
+
* before the timer does).
|
|
34830
|
+
*/
|
|
34831
|
+
pausedSinceRefreshStarted() {
|
|
34832
|
+
return this.canFreeze && (this.pausedWhileRefreshing || this.clock() - this.lastTick > REFRESH_PAUSE_MS);
|
|
34833
|
+
}
|
|
34593
34834
|
/**
|
|
34594
34835
|
* Record a revalidation of `tags`: here at once, and in the revalidation
|
|
34595
34836
|
* table for every other instance - the marker row plus a log row per tag.
|
|
@@ -34622,7 +34863,7 @@ var TrackedTagMarkers = class {
|
|
|
34622
34863
|
const at = markerClock();
|
|
34623
34864
|
let state2 = "fresh";
|
|
34624
34865
|
for (const tag of tags) {
|
|
34625
|
-
const marker = this.
|
|
34866
|
+
const marker = this.knownMarker(tag);
|
|
34626
34867
|
if (!marker) {
|
|
34627
34868
|
continue;
|
|
34628
34869
|
}
|
|
@@ -34646,7 +34887,7 @@ var TrackedTagMarkers = class {
|
|
|
34646
34887
|
const at = markerClock();
|
|
34647
34888
|
let latest = 0;
|
|
34648
34889
|
for (const tag of tags) {
|
|
34649
|
-
const marker = this.
|
|
34890
|
+
const marker = this.knownMarker(tag);
|
|
34650
34891
|
if (!marker) {
|
|
34651
34892
|
continue;
|
|
34652
34893
|
}
|
|
@@ -34733,13 +34974,14 @@ var TrackedTagMarkers = class {
|
|
|
34733
34974
|
);
|
|
34734
34975
|
return;
|
|
34735
34976
|
}
|
|
34736
|
-
const
|
|
34737
|
-
|
|
34738
|
-
|
|
34739
|
-
|
|
34740
|
-
|
|
34741
|
-
|
|
34742
|
-
|
|
34977
|
+
const due = this.due(at);
|
|
34978
|
+
if (due.length > 0) {
|
|
34979
|
+
void this.startRead(due);
|
|
34980
|
+
}
|
|
34981
|
+
const result = await log.query(this.cursor).catch((error2) => {
|
|
34982
|
+
console.error("Error reading tag revalidation log:", error2);
|
|
34983
|
+
return void 0;
|
|
34984
|
+
});
|
|
34743
34985
|
if (!result) {
|
|
34744
34986
|
return;
|
|
34745
34987
|
}
|
|
@@ -34750,6 +34992,11 @@ var TrackedTagMarkers = class {
|
|
|
34750
34992
|
);
|
|
34751
34993
|
return;
|
|
34752
34994
|
}
|
|
34995
|
+
const scan = at - this.lastEntryScan > ENTRY_SCAN_INTERVAL_MS;
|
|
34996
|
+
if (scan) {
|
|
34997
|
+
this.lastEntryScan = at;
|
|
34998
|
+
}
|
|
34999
|
+
const oldest = this.oldestEntry(scan);
|
|
34753
35000
|
let applied = 0;
|
|
34754
35001
|
for (const row of result.rows) {
|
|
34755
35002
|
if (this.applied.has(row.sk)) {
|
|
@@ -34764,11 +35011,20 @@ var TrackedTagMarkers = class {
|
|
|
34764
35011
|
mergeMarkers(this.pendingRows.get(row.tag), row.marker)
|
|
34765
35012
|
);
|
|
34766
35013
|
} else {
|
|
35014
|
+
this.rememberLogged(
|
|
35015
|
+
row.tag,
|
|
35016
|
+
mergeMarkers(this.logOnly.get(row.tag), row.marker),
|
|
35017
|
+
oldest
|
|
35018
|
+
);
|
|
34767
35019
|
continue;
|
|
34768
35020
|
}
|
|
34769
35021
|
this.applied.set(row.sk, row.at);
|
|
34770
35022
|
applied++;
|
|
34771
35023
|
}
|
|
35024
|
+
if (oldest > this.prunedAgainst) {
|
|
35025
|
+
this.pruneLogged(oldest);
|
|
35026
|
+
}
|
|
35027
|
+
this.prunedAgainst = oldest;
|
|
34772
35028
|
this.lastLogRead = at;
|
|
34773
35029
|
this.advance(at - REVALIDATION_LOG_LOOKBACK_MS);
|
|
34774
35030
|
this.debug(
|
|
@@ -34784,14 +35040,16 @@ var TrackedTagMarkers = class {
|
|
|
34784
35040
|
const due = [];
|
|
34785
35041
|
for (const tag of this.unread) {
|
|
34786
35042
|
if (due.length >= BATCH_GET_MAX_KEYS) return due;
|
|
34787
|
-
|
|
35043
|
+
if (!this.reading.has(tag)) {
|
|
35044
|
+
due.push(tag);
|
|
35045
|
+
}
|
|
34788
35046
|
}
|
|
34789
35047
|
const jitter = this.resyncIntervalMs * TAG_RESYNC_JITTER;
|
|
34790
35048
|
for (const [tag, dueAt] of this.dueAt) {
|
|
34791
35049
|
if (due.length >= BATCH_GET_MAX_KEYS || dueAt - jitter > at) {
|
|
34792
35050
|
break;
|
|
34793
35051
|
}
|
|
34794
|
-
if (dueAt <= at && !this.unread.has(tag)) {
|
|
35052
|
+
if (dueAt <= at && !this.unread.has(tag) && !this.reading.has(tag)) {
|
|
34795
35053
|
due.push(tag);
|
|
34796
35054
|
}
|
|
34797
35055
|
}
|
|
@@ -34804,10 +35062,19 @@ var TrackedTagMarkers = class {
|
|
|
34804
35062
|
*/
|
|
34805
35063
|
forget(at, why) {
|
|
34806
35064
|
this.debug(`forgetting ${this.tags.size} tracked tags: ${why}`);
|
|
35065
|
+
this.forgotten++;
|
|
35066
|
+
for (const marker of this.tags.values()) {
|
|
35067
|
+
this.raiseFloor(marker);
|
|
35068
|
+
}
|
|
35069
|
+
for (const marker of this.logOnly.values()) {
|
|
35070
|
+
this.raiseFloor(marker);
|
|
35071
|
+
}
|
|
34807
35072
|
this.tags.clear();
|
|
34808
35073
|
this.dueAt.clear();
|
|
34809
35074
|
this.unread.clear();
|
|
34810
35075
|
this.applied.clear();
|
|
35076
|
+
this.logOnly.clear();
|
|
35077
|
+
this.floor = Math.max(this.floor, markerClock());
|
|
34811
35078
|
this.cursor = at - REVALIDATION_LOG_LOOKBACK_MS;
|
|
34812
35079
|
this.lastLogRead = at;
|
|
34813
35080
|
}
|
|
@@ -34833,13 +35100,17 @@ var TrackedTagMarkers = class {
|
|
|
34833
35100
|
* always answered with, rather than a read on every request while DynamoDB
|
|
34834
35101
|
* is unavailable.
|
|
34835
35102
|
*/
|
|
34836
|
-
async readInto(tags) {
|
|
35103
|
+
async readInto(tags, again = false) {
|
|
34837
35104
|
if (tags.length === 0) {
|
|
34838
35105
|
return true;
|
|
34839
35106
|
}
|
|
34840
35107
|
const at = this.clock();
|
|
35108
|
+
const forgotten = this.forgotten;
|
|
34841
35109
|
try {
|
|
34842
35110
|
const read = await this.markers.read(tags);
|
|
35111
|
+
if (this.forgotten !== forgotten && !again) {
|
|
35112
|
+
return await this.readInto(tags, true);
|
|
35113
|
+
}
|
|
34843
35114
|
for (const tag of tags) {
|
|
34844
35115
|
this.unread.delete(tag);
|
|
34845
35116
|
this.remember(
|
|
@@ -34855,6 +35126,9 @@ var TrackedTagMarkers = class {
|
|
|
34855
35126
|
return true;
|
|
34856
35127
|
} catch (error2) {
|
|
34857
35128
|
console.error("Error reading tag markers:", error2);
|
|
35129
|
+
if (this.forgotten !== forgotten && !again) {
|
|
35130
|
+
return this.readInto(tags, true);
|
|
35131
|
+
}
|
|
34858
35132
|
this.trackUnread(tags);
|
|
34859
35133
|
for (const tag of tags) {
|
|
34860
35134
|
this.unread.add(tag);
|
|
@@ -34868,8 +35142,119 @@ var TrackedTagMarkers = class {
|
|
|
34868
35142
|
}
|
|
34869
35143
|
takePendingRows(tag) {
|
|
34870
35144
|
const pending = this.pendingRows.get(tag);
|
|
35145
|
+
const logged = this.logOnly.get(tag);
|
|
34871
35146
|
this.pendingRows.delete(tag);
|
|
34872
|
-
|
|
35147
|
+
this.logOnly.delete(tag);
|
|
35148
|
+
return logged ? mergeMarkers(logged, pending) : pending;
|
|
35149
|
+
}
|
|
35150
|
+
/**
|
|
35151
|
+
* What this instance knows of `tag`: tracked, or from the log alone -
|
|
35152
|
+
* including rows that arrived while its first read is in flight.
|
|
35153
|
+
*/
|
|
35154
|
+
knownMarker(tag) {
|
|
35155
|
+
const tracked = this.tags.get(tag);
|
|
35156
|
+
if (tracked) {
|
|
35157
|
+
return tracked;
|
|
35158
|
+
}
|
|
35159
|
+
const logged = this.logOnly.get(tag);
|
|
35160
|
+
const pending = this.pendingRows.get(tag);
|
|
35161
|
+
return logged || pending ? mergeMarkers(logged, pending) : void 0;
|
|
35162
|
+
}
|
|
35163
|
+
/**
|
|
35164
|
+
* Keep `tag`'s log rows in `logOnly`, unless they cannot apply to an entry
|
|
35165
|
+
* held, `oldest` or newer (see {@link pruneLogged}), as bounded as the
|
|
35166
|
+
* tracked tags. Either way a row let go of takes what it knew with it, so
|
|
35167
|
+
* `completeSince` moves past it.
|
|
35168
|
+
*/
|
|
35169
|
+
rememberLogged(tag, marker, oldest) {
|
|
35170
|
+
this.logOnly.delete(tag);
|
|
35171
|
+
if (knownBy(marker, markerClock()) <= oldest) {
|
|
35172
|
+
this.raiseFloor(marker);
|
|
35173
|
+
return;
|
|
35174
|
+
}
|
|
35175
|
+
this.logOnly.set(tag, marker);
|
|
35176
|
+
for (const [first, dropped] of this.logOnly) {
|
|
35177
|
+
if (this.logOnly.size <= this.maxTrackedTags) {
|
|
35178
|
+
break;
|
|
35179
|
+
}
|
|
35180
|
+
this.logOnly.delete(first);
|
|
35181
|
+
this.raiseFloor(dropped);
|
|
35182
|
+
}
|
|
35183
|
+
}
|
|
35184
|
+
/**
|
|
35185
|
+
* Drop the `logOnly` rows that cannot apply to an entry held, `oldest` or
|
|
35186
|
+
* newer: every revalidation they name so far is no later than it. That
|
|
35187
|
+
* moves `completeSince` no further than `oldest`, which costs the entries
|
|
35188
|
+
* held nothing, and a profile's `expiredAt` still to come waits in
|
|
35189
|
+
* `laterFloors`. Without this, a deployment revalidating many distinct tags
|
|
35190
|
+
* filled `logOnly` with tags no entry here carries, and the evictions past
|
|
35191
|
+
* its bound kept moving `completeSince` up to the present.
|
|
35192
|
+
*/
|
|
35193
|
+
pruneLogged(oldest) {
|
|
35194
|
+
const at = markerClock();
|
|
35195
|
+
for (const [tag, marker] of this.logOnly) {
|
|
35196
|
+
if (knownBy(marker, at) <= oldest) {
|
|
35197
|
+
this.logOnly.delete(tag);
|
|
35198
|
+
this.raiseFloor(marker);
|
|
35199
|
+
}
|
|
35200
|
+
}
|
|
35201
|
+
}
|
|
35202
|
+
/**
|
|
35203
|
+
* The oldest entry timestamp of any registered store, `Infinity` with none:
|
|
35204
|
+
* early, never late, unless `scan`.
|
|
35205
|
+
*/
|
|
35206
|
+
oldestEntry(scan) {
|
|
35207
|
+
let oldest = Infinity;
|
|
35208
|
+
for (const { oldestTimestamp } of this.entryStores) {
|
|
35209
|
+
oldest = Math.min(oldest, oldestTimestamp(scan));
|
|
35210
|
+
}
|
|
35211
|
+
return oldest;
|
|
35212
|
+
}
|
|
35213
|
+
/**
|
|
35214
|
+
* Move `completeSince` past `marker`'s revalidations: what is no longer
|
|
35215
|
+
* known about a tag once its marker is dropped. A future `expiredAt` waits
|
|
35216
|
+
* in `laterFloors` until it is past.
|
|
35217
|
+
*/
|
|
35218
|
+
raiseFloor(marker) {
|
|
35219
|
+
const at = markerClock();
|
|
35220
|
+
this.floor = Math.max(this.floor, knownBy(marker, at));
|
|
35221
|
+
const { expiredAt } = marker;
|
|
35222
|
+
if (expiredAt !== void 0 && expiredAt > Math.max(at, this.floor)) {
|
|
35223
|
+
this.laterFloor(expiredAt);
|
|
35224
|
+
}
|
|
35225
|
+
}
|
|
35226
|
+
/**
|
|
35227
|
+
* Add `time` to `laterFloors`, in order. Past the tracked-tag bound the two
|
|
35228
|
+
* spans closest together are merged: from the first's start,
|
|
35229
|
+
* `completeSince` is the second's end, early - which only costs reads, and
|
|
35230
|
+
* for as short a time as any merge allows. Never later than its time, which
|
|
35231
|
+
* would let an entry expire unseen.
|
|
35232
|
+
*/
|
|
35233
|
+
laterFloor(time2) {
|
|
35234
|
+
const floors = this.laterFloors;
|
|
35235
|
+
let low = 0;
|
|
35236
|
+
let high = floors.length;
|
|
35237
|
+
while (low < high) {
|
|
35238
|
+
const mid = Math.floor((low + high) / 2);
|
|
35239
|
+
if (floors[mid][0] < time2) {
|
|
35240
|
+
low = mid + 1;
|
|
35241
|
+
} else {
|
|
35242
|
+
high = mid;
|
|
35243
|
+
}
|
|
35244
|
+
}
|
|
35245
|
+
if (low > 0 && floors[low - 1][1] >= time2 || floors[low]?.[0] === time2) {
|
|
35246
|
+
return;
|
|
35247
|
+
}
|
|
35248
|
+
floors.splice(low, 0, [time2, time2]);
|
|
35249
|
+
if (floors.length > this.maxTrackedTags) {
|
|
35250
|
+
let merge2 = 0;
|
|
35251
|
+
for (let i5 = 1; i5 < floors.length - 1; i5++) {
|
|
35252
|
+
if (floors[i5 + 1][1] - floors[i5][0] < floors[merge2 + 1][1] - floors[merge2][0]) {
|
|
35253
|
+
merge2 = i5;
|
|
35254
|
+
}
|
|
35255
|
+
}
|
|
35256
|
+
floors.splice(merge2, 2, [floors[merge2][0], floors[merge2 + 1][1]]);
|
|
35257
|
+
}
|
|
34873
35258
|
}
|
|
34874
35259
|
/** Record that `tag`'s marker was known from the table at `at`. */
|
|
34875
35260
|
known(tag, at) {
|
|
@@ -34886,6 +35271,7 @@ var TrackedTagMarkers = class {
|
|
|
34886
35271
|
if (this.tags.size <= this.maxTrackedTags) {
|
|
34887
35272
|
break;
|
|
34888
35273
|
}
|
|
35274
|
+
this.raiseFloor(this.tags.get(oldest));
|
|
34889
35275
|
this.tags.delete(oldest);
|
|
34890
35276
|
this.dueAt.delete(oldest);
|
|
34891
35277
|
this.unread.delete(oldest);
|
|
@@ -34898,7 +35284,18 @@ var TrackedTagMarkers = class {
|
|
|
34898
35284
|
}
|
|
34899
35285
|
}
|
|
34900
35286
|
};
|
|
35287
|
+
function knownBy(marker, at) {
|
|
35288
|
+
const { revalidatedAt, staleAt, expiredAt } = marker;
|
|
35289
|
+
return Math.max(
|
|
35290
|
+
revalidatedAt ?? -Infinity,
|
|
35291
|
+
staleAt ?? -Infinity,
|
|
35292
|
+
expiredAt !== void 0 && expiredAt <= at ? expiredAt : -Infinity
|
|
35293
|
+
);
|
|
35294
|
+
}
|
|
34901
35295
|
var MAX_LOG_PUT_MS = REVALIDATION_LOG_LOOKBACK_MS / 2;
|
|
35296
|
+
var ENTRY_SCAN_INTERVAL_MS = 6e4;
|
|
35297
|
+
var REFRESH_TICK_MS = 250;
|
|
35298
|
+
var REFRESH_PAUSE_MS = 1e3;
|
|
34902
35299
|
function mergeMarkers(local, remote) {
|
|
34903
35300
|
if (!local) {
|
|
34904
35301
|
return remote ?? {};
|
|
@@ -34999,6 +35396,12 @@ var EntryLru = class {
|
|
|
34999
35396
|
maxBytes;
|
|
35000
35397
|
entries = /* @__PURE__ */ new Map();
|
|
35001
35398
|
bytes = 0;
|
|
35399
|
+
/**
|
|
35400
|
+
* No later than the oldest entry's `timestamp`: the last scan's answer, less
|
|
35401
|
+
* whatever was set since. Entries dropped since only make it early. See
|
|
35402
|
+
* {@link oldestBound}.
|
|
35403
|
+
*/
|
|
35404
|
+
lowest = Infinity;
|
|
35002
35405
|
get(key) {
|
|
35003
35406
|
const entry = this.entries.get(key);
|
|
35004
35407
|
if (entry) {
|
|
@@ -35015,6 +35418,7 @@ var EntryLru = class {
|
|
|
35015
35418
|
}
|
|
35016
35419
|
this.entries.set(key, entry);
|
|
35017
35420
|
this.bytes += size;
|
|
35421
|
+
this.lowest = Math.min(this.lowest, entry.timestamp);
|
|
35018
35422
|
for (const [oldestKey, oldest] of this.entries) {
|
|
35019
35423
|
if (this.bytes <= this.maxBytes) {
|
|
35020
35424
|
break;
|
|
@@ -35028,7 +35432,58 @@ var EntryLru = class {
|
|
|
35028
35432
|
if (existing) {
|
|
35029
35433
|
this.entries.delete(key);
|
|
35030
35434
|
this.bytes -= sizeOf2(key, existing);
|
|
35435
|
+
if (this.entries.size === 0) {
|
|
35436
|
+
this.lowest = Infinity;
|
|
35437
|
+
}
|
|
35438
|
+
}
|
|
35439
|
+
}
|
|
35440
|
+
/**
|
|
35441
|
+
* The `timestamp` of the oldest entry held, `Infinity` with none, dropping
|
|
35442
|
+
* on the way the entries past `revalidate` at `at` (`now()`): a handler
|
|
35443
|
+
* that drops them when next read would otherwise hold them until then, and
|
|
35444
|
+
* they would count here meanwhile. A scan, so asked once in a while: in
|
|
35445
|
+
* between, {@link oldestBound} answers.
|
|
35446
|
+
*/
|
|
35447
|
+
oldestTimestamp(at = -Infinity) {
|
|
35448
|
+
let oldest = Infinity;
|
|
35449
|
+
for (const [key, entry] of this.entries) {
|
|
35450
|
+
if (isPastRevalidate(entry, at)) {
|
|
35451
|
+
this.delete(key);
|
|
35452
|
+
} else {
|
|
35453
|
+
oldest = Math.min(oldest, entry.timestamp);
|
|
35454
|
+
}
|
|
35455
|
+
}
|
|
35456
|
+
this.lowest = oldest;
|
|
35457
|
+
return oldest;
|
|
35458
|
+
}
|
|
35459
|
+
/**
|
|
35460
|
+
* No later than {@link oldestTimestamp}, without its scan: as of the last
|
|
35461
|
+
* one, less the entries set since. `Infinity` with none held. Drops the
|
|
35462
|
+
* entries past `revalidate` at `at` from the least recently used end, up to
|
|
35463
|
+
* the first that isn't, so {@link holdsLive} doesn't walk past them on every
|
|
35464
|
+
* request until the next scan.
|
|
35465
|
+
*/
|
|
35466
|
+
oldestBound(at = -Infinity) {
|
|
35467
|
+
for (const [key, entry] of this.entries) {
|
|
35468
|
+
if (!isPastRevalidate(entry, at)) {
|
|
35469
|
+
break;
|
|
35470
|
+
}
|
|
35471
|
+
this.delete(key);
|
|
35472
|
+
}
|
|
35473
|
+
return this.entries.size === 0 ? Infinity : this.lowest;
|
|
35474
|
+
}
|
|
35475
|
+
/**
|
|
35476
|
+
* Whether an entry not past `revalidate` at `at` is held. Usually the first
|
|
35477
|
+
* entry answers; the expired ones it passes are dropped by the scan
|
|
35478
|
+
* {@link oldestTimestamp} makes, not here, so asking changes nothing.
|
|
35479
|
+
*/
|
|
35480
|
+
holdsLive(at) {
|
|
35481
|
+
for (const entry of this.entries.values()) {
|
|
35482
|
+
if (!isPastRevalidate(entry, at)) {
|
|
35483
|
+
return true;
|
|
35484
|
+
}
|
|
35031
35485
|
}
|
|
35486
|
+
return false;
|
|
35032
35487
|
}
|
|
35033
35488
|
get size() {
|
|
35034
35489
|
return this.entries.size;
|
|
@@ -35037,31 +35492,58 @@ var EntryLru = class {
|
|
|
35037
35492
|
function sizeOf2(key, entry) {
|
|
35038
35493
|
return entry.value.byteLength + key.length;
|
|
35039
35494
|
}
|
|
35495
|
+
function isPastRevalidate(stored, at) {
|
|
35496
|
+
return at > stored.timestamp + stored.revalidate * 1e3;
|
|
35497
|
+
}
|
|
35040
35498
|
var DEFAULT_MEMORY_BYTES = 50 * 1024 * 1024;
|
|
35041
|
-
|
|
35499
|
+
var TAG_TABLE_REQUEST_TIMEOUT_MS = 3e3;
|
|
35500
|
+
async function awaitIf(wait, work, failure) {
|
|
35501
|
+
const logged = work.catch((error2) => {
|
|
35502
|
+
console.error(failure, error2);
|
|
35503
|
+
});
|
|
35504
|
+
if (wait) {
|
|
35505
|
+
await logged;
|
|
35506
|
+
}
|
|
35507
|
+
}
|
|
35508
|
+
function tagMethods(tags, options = {}) {
|
|
35042
35509
|
return {
|
|
35043
|
-
|
|
35044
|
-
|
|
35045
|
-
|
|
35046
|
-
|
|
35047
|
-
|
|
35510
|
+
// Started, not awaited, while the instance keeps up, unless `blocking`.
|
|
35511
|
+
// Next.js awaits `refreshTags` inside the first `'use cache'` lookup of a
|
|
35512
|
+
// request (`use-cache-wrapper`), and a staged render ends its static stage
|
|
35513
|
+
// on a timer: a log `Query` there pushed the entry out of the static
|
|
35514
|
+
// stage, so a cached navigation stored the page segment without it. What
|
|
35515
|
+
// the query finds applies from the next request. Awaited once the instance
|
|
35516
|
+
// is `behind`, which the runtime settles before a page render starts
|
|
35517
|
+
// (`catchUp`): the first request after an idle or frozen spell would
|
|
35518
|
+
// otherwise serve whatever was revalidated elsewhere meanwhile. A page's
|
|
35519
|
+
// render just after `catchUp` gets a grace more (`behindInRender`), so a
|
|
35520
|
+
// request that came in just short of the line doesn't cross it inside
|
|
35521
|
+
// the render. Not even then while the instance holds no entry the query
|
|
35522
|
+
// could expire (`holdsEntries`), with a `refreshIntervalMs` of `0` too.
|
|
35523
|
+
refreshTags: () => awaitIf(
|
|
35524
|
+
options.blocking === true || tags.behindInRender && tags.holdsEntries,
|
|
35525
|
+
tags.refresh(),
|
|
35526
|
+
"Error refreshing cache tags:"
|
|
35527
|
+
),
|
|
35048
35528
|
async updateTags(revalidatedTags, durations) {
|
|
35049
35529
|
await tags.update(revalidatedTags, durations);
|
|
35050
35530
|
}
|
|
35051
35531
|
};
|
|
35052
35532
|
}
|
|
35533
|
+
function outsideRender(fn) {
|
|
35534
|
+
return outsideRequest(sharedTagManifest).outsideRender(fn);
|
|
35535
|
+
}
|
|
35053
35536
|
function lazyHandler(create) {
|
|
35054
35537
|
let handler;
|
|
35055
35538
|
const instance = () => handler ??= create();
|
|
35056
35539
|
return {
|
|
35057
|
-
get: (cacheKey, softTags) => instance().get(cacheKey, softTags),
|
|
35058
|
-
set: (cacheKey, pendingEntry) => instance().set(cacheKey, pendingEntry),
|
|
35059
|
-
refreshTags: () => instance().refreshTags(),
|
|
35060
|
-
getExpiration: (tags) => instance().getExpiration(tags),
|
|
35061
|
-
updateTags: (tags, durations) => instance().updateTags(tags, durations)
|
|
35540
|
+
get: (cacheKey, softTags) => outsideRender(() => instance().get(cacheKey, softTags)),
|
|
35541
|
+
set: (cacheKey, pendingEntry) => outsideRender(() => instance().set(cacheKey, pendingEntry)),
|
|
35542
|
+
refreshTags: () => outsideRender(() => instance().refreshTags()),
|
|
35543
|
+
getExpiration: (tags) => outsideRender(() => instance().getExpiration(tags)),
|
|
35544
|
+
updateTags: (tags, durations) => outsideRender(() => instance().updateTags(tags, durations))
|
|
35062
35545
|
};
|
|
35063
35546
|
}
|
|
35064
|
-
var TAG_MANIFEST_SYMBOL = /* @__PURE__ */ Symbol.for("cdk-nextjs.use-cache.tag-manifest");
|
|
35065
35547
|
function sharedTagManifest() {
|
|
35066
35548
|
const global = globalThis;
|
|
35067
35549
|
if (!global[TAG_MANIFEST_SYMBOL]) {
|
|
@@ -35071,12 +35553,24 @@ function sharedTagManifest() {
|
|
|
35071
35553
|
DEFAULT_TAG_REFRESH_MS
|
|
35072
35554
|
);
|
|
35073
35555
|
if (config.tableName && !isBuildPhase()) {
|
|
35074
|
-
const client = new import_client_dynamodb2.DynamoDBClient({
|
|
35556
|
+
const client = new import_client_dynamodb2.DynamoDBClient({
|
|
35557
|
+
region: config.region,
|
|
35558
|
+
// Without `throwOnRequestTimeout` the timeout only logs a warning. It
|
|
35559
|
+
// ends once the response headers arrive: `socketTimeout`, an
|
|
35560
|
+
// inactivity timer kept until the request closes, covers the body.
|
|
35561
|
+
requestHandler: {
|
|
35562
|
+
requestTimeout: TAG_TABLE_REQUEST_TIMEOUT_MS,
|
|
35563
|
+
throwOnRequestTimeout: true,
|
|
35564
|
+
socketTimeout: TAG_TABLE_REQUEST_TIMEOUT_MS
|
|
35565
|
+
}
|
|
35566
|
+
});
|
|
35075
35567
|
global[TAG_MANIFEST_SYMBOL] = new TrackedTagMarkers({
|
|
35076
35568
|
markers: new TagMarkerTable(client, config.tableName, config.buildId),
|
|
35077
35569
|
log: new RevalidationLog(client, config.tableName, config.buildId),
|
|
35078
35570
|
refreshIntervalMs,
|
|
35079
|
-
debug: (0, import_debug5.default)("cdk-nextjs:cache-handler:tags")
|
|
35571
|
+
debug: (0, import_debug5.default)("cdk-nextjs:cache-handler:tags"),
|
|
35572
|
+
// Set in every Lambda runtime; a container is never frozen.
|
|
35573
|
+
canFreeze: process.env.AWS_LAMBDA_FUNCTION_NAME !== void 0
|
|
35080
35574
|
});
|
|
35081
35575
|
} else {
|
|
35082
35576
|
if (!isBuildPhase()) {
|
|
@@ -35100,21 +35594,29 @@ function createDefaultUseCacheHandler(options = {}) {
|
|
|
35100
35594
|
);
|
|
35101
35595
|
const pending = new PendingSets();
|
|
35102
35596
|
const debug = (0, import_debug6.default)("cdk-nextjs:cache-handler:use-cache:default");
|
|
35597
|
+
tags.judgeEntriesOf(
|
|
35598
|
+
(scan) => scan ? memory.oldestTimestamp(markerClock()) : memory.oldestBound(markerClock()),
|
|
35599
|
+
() => memory.holdsLive(markerClock())
|
|
35600
|
+
);
|
|
35601
|
+
const ensureTags = (needed, tagList) => awaitIf(needed, tags.ensure(tagList), "Error reading cache tags:");
|
|
35103
35602
|
return {
|
|
35104
|
-
async get(cacheKey) {
|
|
35603
|
+
async get(cacheKey, softTags = []) {
|
|
35105
35604
|
await pending.wait(cacheKey);
|
|
35106
35605
|
const stored = memory.get(cacheKey);
|
|
35107
35606
|
if (!stored) {
|
|
35108
35607
|
debug(`MISS ${cacheKey}`);
|
|
35109
35608
|
return void 0;
|
|
35110
35609
|
}
|
|
35111
|
-
if (
|
|
35610
|
+
if (isPastRevalidate(stored, markerClock())) {
|
|
35112
35611
|
debug(`EXPIRED ${cacheKey}`);
|
|
35113
35612
|
memory.delete(cacheKey);
|
|
35114
35613
|
return void 0;
|
|
35115
35614
|
}
|
|
35116
|
-
await
|
|
35117
|
-
|
|
35615
|
+
await ensureTags(stored.timestamp < tags.completeSince, [
|
|
35616
|
+
...stored.tags,
|
|
35617
|
+
...softTags
|
|
35618
|
+
]);
|
|
35619
|
+
const state2 = tags.expiration(softTags) >= stored.timestamp ? "expired" : tags.state(stored.tags, stored.timestamp);
|
|
35118
35620
|
if (state2 === "expired") {
|
|
35119
35621
|
debug(`EXPIRED BY TAG ${cacheKey}`);
|
|
35120
35622
|
memory.delete(cacheKey);
|
|
@@ -35132,7 +35634,7 @@ function createDefaultUseCacheHandler(options = {}) {
|
|
|
35132
35634
|
return;
|
|
35133
35635
|
}
|
|
35134
35636
|
memory.set(cacheKey, stored);
|
|
35135
|
-
|
|
35637
|
+
void ensureTags(false, stored.tags);
|
|
35136
35638
|
debug(`SET ${cacheKey}`);
|
|
35137
35639
|
} catch (error2) {
|
|
35138
35640
|
debug(`SET FAILED ${cacheKey}`, error2);
|
|
@@ -35140,7 +35642,11 @@ function createDefaultUseCacheHandler(options = {}) {
|
|
|
35140
35642
|
done();
|
|
35141
35643
|
}
|
|
35142
35644
|
},
|
|
35143
|
-
...tagMethods(tags)
|
|
35645
|
+
...tagMethods(tags),
|
|
35646
|
+
// `Infinity` has Next.js pass the implicit tags to `get` instead, so they
|
|
35647
|
+
// are judged against the entry read: whether answering needs a marker
|
|
35648
|
+
// read depends on that entry's `timestamp`, which this does not know.
|
|
35649
|
+
getExpiration: async () => Infinity
|
|
35144
35650
|
};
|
|
35145
35651
|
}
|
|
35146
35652
|
var use_cache_default_handler_default = lazyHandler(() => createDefaultUseCacheHandler());
|