cdk-nextjs 0.7.6 → 0.7.7
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/caching-guide.md +11 -1
- package/docs/harness-coverage.md +2 -1
- package/lib/adapter/aws-cache-store.d.ts +214 -3
- package/lib/adapter/aws-cache-store.js +470 -35
- package/lib/adapter/aws-cache-store.test.js +675 -3
- package/lib/adapter/cache-handler.mjs +421 -30
- package/lib/adapter/use-cache-common.d.ts +53 -1
- package/lib/adapter/use-cache-common.js +121 -14
- package/lib/adapter/use-cache-common.test.js +138 -1
- package/lib/adapter/use-cache-default-handler.js +36 -6
- package/lib/adapter/use-cache-default-handler.mjs +525 -42
- 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 +516 -40
- 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 +18 -4
- package/lib/runtime/core.test.js +17 -1
- package/lib/runtime/lambda.mjs +16 -3
- package/lib/runtime/server.mjs +16 -3
- package/lib/runtime/tag-manifest.d.ts +15 -0
- package/lib/runtime/tag-manifest.js +24 -0
- package/lib/runtime/tag-manifest.test.d.ts +1 -0
- package/lib/runtime/tag-manifest.test.js +21 -0
- package/package.json +1 -1
|
@@ -74226,6 +74226,7 @@ var import_debug7 = __toESM(require_src());
|
|
|
74226
74226
|
// src/adapter/aws-cache-store.ts
|
|
74227
74227
|
var import_client_dynamodb = __toESM(require_dist_cjs19());
|
|
74228
74228
|
var import_client_s3 = __toESM(require_dist_cjs20());
|
|
74229
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
74229
74230
|
import { join as join6 } from "node:path";
|
|
74230
74231
|
function resolveAwsCacheConfig() {
|
|
74231
74232
|
return {
|
|
@@ -74295,6 +74296,7 @@ function markerOf(item) {
|
|
|
74295
74296
|
}
|
|
74296
74297
|
var BATCH_GET_MAX_KEYS = 100;
|
|
74297
74298
|
var DEFAULT_TAG_REFRESH_MS = 1e3;
|
|
74299
|
+
var TAG_REFRESH_GRACE_MS = 1e3;
|
|
74298
74300
|
var MAX_SORT_KEY_BYTES = 1024;
|
|
74299
74301
|
function hashedTag(tag) {
|
|
74300
74302
|
return `#${sha256Hex(tag)}`;
|
|
@@ -74509,10 +74511,64 @@ var TrackedTagMarkers = class {
|
|
|
74509
74511
|
* result rather than dropped.
|
|
74510
74512
|
*/
|
|
74511
74513
|
pendingRows = /* @__PURE__ */ new Map();
|
|
74514
|
+
/**
|
|
74515
|
+
* Log rows for tags not tracked and not being read, merged by tag, oldest
|
|
74516
|
+
* first: what the log has said about them since {@link completeSince}. Taken
|
|
74517
|
+
* into a tag's marker when it is read or written.
|
|
74518
|
+
*/
|
|
74519
|
+
logOnly = /* @__PURE__ */ new Map();
|
|
74520
|
+
/** See {@link completeSince}. */
|
|
74521
|
+
floor;
|
|
74522
|
+
/**
|
|
74523
|
+
* The future `expiredAt`s of markers dropped since, as `[from, to]`, in
|
|
74524
|
+
* order: once `from` is past, {@link completeSince} moves to `to`, and not
|
|
74525
|
+
* before, since only then does it expire anything. `from` and `to` are the
|
|
74526
|
+
* same until spans are merged for space ({@link laterFloor}).
|
|
74527
|
+
*/
|
|
74528
|
+
laterFloors = [];
|
|
74529
|
+
/** The in-memory entry stores judged by {@link completeSince}. */
|
|
74530
|
+
entryStores = [];
|
|
74531
|
+
/** How many times {@link forget} ran: a read started before one is made again. */
|
|
74532
|
+
forgotten = 0;
|
|
74512
74533
|
/** Tracked tags whose read failed, for the next refresh to read again. */
|
|
74513
74534
|
unread = /* @__PURE__ */ new Set();
|
|
74535
|
+
/**
|
|
74536
|
+
* Set inside a page render {@link catchUp} let go, for that render alone.
|
|
74537
|
+
* See {@link behindInRender}.
|
|
74538
|
+
*/
|
|
74539
|
+
caughtUpRender = new AsyncLocalStorage();
|
|
74540
|
+
/** When `sync` last scanned the entry stores ({@link oldestEntry}). */
|
|
74541
|
+
lastEntryScan = -Infinity;
|
|
74542
|
+
/**
|
|
74543
|
+
* No `logOnly` row is known by it or earlier, so a {@link pruneLogged}
|
|
74544
|
+
* against it or earlier drops nothing: a marker is only ever known by later,
|
|
74545
|
+
* as its `expiredAt` passes.
|
|
74546
|
+
*/
|
|
74547
|
+
prunedAgainst = -Infinity;
|
|
74548
|
+
/** When the last refresh started, or was skipped. */
|
|
74514
74549
|
lastRefresh = -Infinity;
|
|
74550
|
+
/**
|
|
74551
|
+
* The refresh in flight. One left open across a freeze comes back after the
|
|
74552
|
+
* thaw with what it read before, so {@link refresh} waits for it and then
|
|
74553
|
+
* for a fresh one, rather than the runtime waiting for it before the freeze:
|
|
74554
|
+
* that would bill every Lambda invocation for the query's tail.
|
|
74555
|
+
*/
|
|
74515
74556
|
refreshing;
|
|
74557
|
+
/**
|
|
74558
|
+
* When {@link refreshing} last showed the process running: a timer ticks
|
|
74559
|
+
* while it is in flight, and a tick that comes late, or none at all, means
|
|
74560
|
+
* the process was paused - frozen - since it started.
|
|
74561
|
+
*/
|
|
74562
|
+
lastTick = -Infinity;
|
|
74563
|
+
/** Whether a tick came late since {@link refreshing} started. */
|
|
74564
|
+
pausedWhileRefreshing = false;
|
|
74565
|
+
ticker;
|
|
74566
|
+
canFreeze;
|
|
74567
|
+
/**
|
|
74568
|
+
* When the last refresh settled, either way, unless it spanned a pause: what
|
|
74569
|
+
* it knows is from then. See {@link behind}.
|
|
74570
|
+
*/
|
|
74571
|
+
settledAt;
|
|
74516
74572
|
/**
|
|
74517
74573
|
* Where the next log query starts (`clock()`): the last successful one's
|
|
74518
74574
|
* start, less {@link REVALIDATION_LOG_LOOKBACK_MS}.
|
|
@@ -74534,20 +74590,68 @@ var TrackedTagMarkers = class {
|
|
|
74534
74590
|
this.random = options.random ?? Math.random;
|
|
74535
74591
|
this.debug = options.debug ?? (() => {
|
|
74536
74592
|
});
|
|
74593
|
+
this.canFreeze = options.canFreeze ?? false;
|
|
74537
74594
|
const at = this.clock();
|
|
74538
74595
|
this.cursor = at - REVALIDATION_LOG_LOOKBACK_MS;
|
|
74539
74596
|
this.lastLogRead = at;
|
|
74597
|
+
this.settledAt = at;
|
|
74598
|
+
this.floor = markerClock();
|
|
74540
74599
|
}
|
|
74541
74600
|
/** `tag`'s marker as this instance knows it, if it tracks `tag`. */
|
|
74542
74601
|
get(tag) {
|
|
74543
74602
|
return this.tags.get(tag);
|
|
74544
74603
|
}
|
|
74604
|
+
/**
|
|
74605
|
+
* Since when (`markerClock()`) this instance knows of every revalidation of
|
|
74606
|
+
* every tag, tracked or not: tracked ones from their markers and the log,
|
|
74607
|
+
* the others from the log alone (`logOnly`). So an entry created at or after
|
|
74608
|
+
* it can be judged by {@link state} and {@link expiration} without
|
|
74609
|
+
* {@link ensure} reading anything - which matters inside a `'use cache'`
|
|
74610
|
+
* lookup, where Next.js waits for the answer and a read pushes the entry out
|
|
74611
|
+
* of the static stage of a staged render.
|
|
74612
|
+
*
|
|
74613
|
+
* Starts at construction: the log is followed from before it. Moves to now
|
|
74614
|
+
* when the tracked markers are forgotten, and past the revalidations of
|
|
74615
|
+
* whatever else it lets go of - a marker evicted for space, a log row
|
|
74616
|
+
* pruned - which are no longer known: to its `expiredAt` only once that is
|
|
74617
|
+
* past, since a profile's `expire` can be a year out, and moving there at
|
|
74618
|
+
* once would turn the read-free path off for the instance's life.
|
|
74619
|
+
*
|
|
74620
|
+
* A row is pruned only once it is no later than every entry held, so that
|
|
74621
|
+
* costs those entries nothing. An entry stored afterwards with an older
|
|
74622
|
+
* `timestamp` - Next.js stamps the start of its generation, which can
|
|
74623
|
+
* outlast a lookback - is older than this too, and is read first.
|
|
74624
|
+
*
|
|
74625
|
+
* It does not cover a revalidation older than the log's first lookback
|
|
74626
|
+
* whose `expiredAt` is still to come, which would expire an entry created
|
|
74627
|
+
* after it. So the `'use cache'` handler reads an entry's tags in the
|
|
74628
|
+
* background as it stores it.
|
|
74629
|
+
*/
|
|
74630
|
+
get completeSince() {
|
|
74631
|
+
const at = markerClock();
|
|
74632
|
+
while (this.laterFloors.length > 0 && this.laterFloors[0][0] <= at) {
|
|
74633
|
+
this.floor = Math.max(this.floor, this.laterFloors.shift()[1]);
|
|
74634
|
+
}
|
|
74635
|
+
return this.floor;
|
|
74636
|
+
}
|
|
74637
|
+
/**
|
|
74638
|
+
* Register an in-memory store whose entries are judged by
|
|
74639
|
+
* {@link completeSince}, by the `timestamp` of the oldest entry it holds:
|
|
74640
|
+
* `logOnly` keeps the rows that could still apply to one of them, and no
|
|
74641
|
+
* others. With none registered it keeps nothing. Asked with `scan` false,
|
|
74642
|
+
* `oldestTimestamp` may answer early, but never late, without a scan.
|
|
74643
|
+
*/
|
|
74644
|
+
judgeEntriesOf(oldestTimestamp, holdsAny = () => oldestTimestamp(false) < Infinity) {
|
|
74645
|
+
this.entryStores.push({ oldestTimestamp, holdsAny });
|
|
74646
|
+
}
|
|
74545
74647
|
/**
|
|
74546
74648
|
* Track `tag` with `marker`: what this instance just wrote, which it knows
|
|
74547
74649
|
* without reading it back.
|
|
74548
74650
|
*/
|
|
74549
74651
|
set(tag, marker) {
|
|
74550
|
-
this.
|
|
74652
|
+
const logged = this.logOnly.get(tag);
|
|
74653
|
+
this.logOnly.delete(tag);
|
|
74654
|
+
this.remember(tag, mergeMarkers(marker, logged));
|
|
74551
74655
|
this.known(tag, this.clock());
|
|
74552
74656
|
}
|
|
74553
74657
|
/**
|
|
@@ -74600,27 +74704,103 @@ var TrackedTagMarkers = class {
|
|
|
74600
74704
|
const unread = [];
|
|
74601
74705
|
for (const tag of new Set(tags)) {
|
|
74602
74706
|
const inFlight = this.reading.get(tag);
|
|
74603
|
-
if (
|
|
74604
|
-
waits.push(inFlight);
|
|
74605
|
-
} else if (this.tags.has(tag)) {
|
|
74707
|
+
if (this.tags.has(tag)) {
|
|
74606
74708
|
this.touch(tag);
|
|
74709
|
+
} else if (inFlight) {
|
|
74710
|
+
waits.push(inFlight);
|
|
74607
74711
|
} else {
|
|
74608
74712
|
unread.push(tag);
|
|
74609
74713
|
}
|
|
74610
74714
|
}
|
|
74611
74715
|
if (unread.length > 0) {
|
|
74612
|
-
|
|
74613
|
-
for (const tag of unread) {
|
|
74614
|
-
this.reading.delete(tag);
|
|
74615
|
-
}
|
|
74616
|
-
});
|
|
74617
|
-
for (const tag of unread) {
|
|
74618
|
-
this.reading.set(tag, read);
|
|
74619
|
-
}
|
|
74620
|
-
waits.push(read);
|
|
74716
|
+
waits.push(this.startRead(unread));
|
|
74621
74717
|
}
|
|
74622
74718
|
await Promise.all(waits);
|
|
74623
74719
|
}
|
|
74720
|
+
/**
|
|
74721
|
+
* {@link readInto} `tags`, as the read of each that whoever needs one of
|
|
74722
|
+
* them next waits on ({@link reading}). Never rejects: a failed read leaves
|
|
74723
|
+
* the tags tracked.
|
|
74724
|
+
*/
|
|
74725
|
+
startRead(tags) {
|
|
74726
|
+
const read = this.readInto(tags).then(() => {
|
|
74727
|
+
for (const tag of tags) {
|
|
74728
|
+
this.reading.delete(tag);
|
|
74729
|
+
}
|
|
74730
|
+
});
|
|
74731
|
+
for (const tag of tags) {
|
|
74732
|
+
this.reading.set(tag, read);
|
|
74733
|
+
}
|
|
74734
|
+
return read;
|
|
74735
|
+
}
|
|
74736
|
+
/**
|
|
74737
|
+
* Whether the last refresh settled more than `refreshIntervalMs` plus
|
|
74738
|
+
* {@link TAG_REFRESH_GRACE_MS} ago - one that spanned a freeze counts from
|
|
74739
|
+
* when it started: after the instance sat idle or frozen - or always, with
|
|
74740
|
+
* a `refreshIntervalMs` of `0`. Answering from what it knows then could
|
|
74741
|
+
* serve an entry revalidated elsewhere since, however long ago that was, so
|
|
74742
|
+
* `refreshTags` waits for the refresh, and the runtime waits for it before
|
|
74743
|
+
* a page render starts ({@link catchUp}).
|
|
74744
|
+
*
|
|
74745
|
+
* From when it settled, not when it started, so a slow query the runtime
|
|
74746
|
+
* waited for before the render does not leave the instance behind inside
|
|
74747
|
+
* it. A failed one counts too, so one request does not wait for two: the
|
|
74748
|
+
* same "assume the entry is valid" a failed read answers with, until the
|
|
74749
|
+
* next refresh, at most the interval and the grace later.
|
|
74750
|
+
*/
|
|
74751
|
+
get behind() {
|
|
74752
|
+
return this.log !== void 0 && // `0` asks before every check, so every check waits for the answer.
|
|
74753
|
+
(this.refreshIntervalMs === 0 || this.overdue(this.settledAt));
|
|
74754
|
+
}
|
|
74755
|
+
/**
|
|
74756
|
+
* {@link behind}, as `refreshTags` asks it: never inside a page's render
|
|
74757
|
+
* {@link catchUp} caught up, since the runtime settled it for that request
|
|
74758
|
+
* before the render started, and a wait inside a `'use cache'` lookup is
|
|
74759
|
+
* what it settled it to avoid - a render crossing the line partway through
|
|
74760
|
+
* would otherwise wait after all. So a revalidation elsewhere can go unseen
|
|
74761
|
+
* for a render's duration on top of the usual window.
|
|
74762
|
+
*
|
|
74763
|
+
* A route handler, which no catch-up precedes, waits at the plain line.
|
|
74764
|
+
*/
|
|
74765
|
+
get behindInRender() {
|
|
74766
|
+
return this.refreshIntervalMs !== 0 && this.caughtUpRender.getStore() ? false : this.behind;
|
|
74767
|
+
}
|
|
74768
|
+
/**
|
|
74769
|
+
* Whether a registered store ({@link judgeEntriesOf}) holds any entry: one
|
|
74770
|
+
* a refresh could expire. With none there is nothing to wait for even when
|
|
74771
|
+
* the instance is {@link behind}, since whatever is stored from here on is
|
|
74772
|
+
* newer than what the log would say - which covers the first `'use cache'`
|
|
74773
|
+
* call of a process, which creates the handler inside the render.
|
|
74774
|
+
*/
|
|
74775
|
+
get holdsEntries() {
|
|
74776
|
+
return this.entryStores.some((store) => store.holdsAny());
|
|
74777
|
+
}
|
|
74778
|
+
/**
|
|
74779
|
+
* {@link refresh}, waited for when the instance is {@link behind}. The
|
|
74780
|
+
* runtime calls it before handing a page request to Next.js, outside the
|
|
74781
|
+
* staged render whose static stage a wait inside a `'use cache'` lookup
|
|
74782
|
+
* would cut short. Only for the `'use cache'` handler's entries
|
|
74783
|
+
* ({@link judgeEntriesOf}): the other caches wait for the refresh where they
|
|
74784
|
+
* read anyway. Not with a `refreshIntervalMs` of `0` either, where
|
|
74785
|
+
* `refreshTags` asks again regardless. Then `render`, marked as caught up
|
|
74786
|
+
* for ({@link behindInRender}). Rejects only as `render` does.
|
|
74787
|
+
*/
|
|
74788
|
+
async catchUp(render) {
|
|
74789
|
+
const log = (error2) => console.error("Error refreshing cache tags:", error2);
|
|
74790
|
+
if (this.refreshIntervalMs > 0 && this.behind) {
|
|
74791
|
+
if (!this.holdsEntries) {
|
|
74792
|
+
return render();
|
|
74793
|
+
}
|
|
74794
|
+
await this.refresh().catch(log);
|
|
74795
|
+
} else if (this.refreshIntervalMs > 0) {
|
|
74796
|
+
void this.refresh().catch(log);
|
|
74797
|
+
}
|
|
74798
|
+
return this.caughtUpRender.run(true, render);
|
|
74799
|
+
}
|
|
74800
|
+
/** Whether `at` is more than the interval and the grace ago. */
|
|
74801
|
+
overdue(at) {
|
|
74802
|
+
return this.clock() - at > this.refreshIntervalMs + TAG_REFRESH_GRACE_MS;
|
|
74803
|
+
}
|
|
74624
74804
|
/**
|
|
74625
74805
|
* Catch up on revalidations other instances ran, at most once per
|
|
74626
74806
|
* `refreshIntervalMs`. See {@link TrackedTagMarkers} for the cost.
|
|
@@ -74631,6 +74811,9 @@ var TrackedTagMarkers = class {
|
|
|
74631
74811
|
return;
|
|
74632
74812
|
}
|
|
74633
74813
|
if (this.refreshing) {
|
|
74814
|
+
if (this.behind && this.pausedSinceRefreshStarted()) {
|
|
74815
|
+
return this.refreshing.then(() => this.refresh());
|
|
74816
|
+
}
|
|
74634
74817
|
return this.refreshing;
|
|
74635
74818
|
}
|
|
74636
74819
|
const at = this.clock();
|
|
@@ -74638,14 +74821,47 @@ var TrackedTagMarkers = class {
|
|
|
74638
74821
|
return;
|
|
74639
74822
|
}
|
|
74640
74823
|
this.lastRefresh = at;
|
|
74641
|
-
if (this.tags.size === 0) {
|
|
74824
|
+
if (this.tags.size === 0 && this.entryStores.length === 0) {
|
|
74642
74825
|
return;
|
|
74643
74826
|
}
|
|
74827
|
+
this.startTicking();
|
|
74644
74828
|
this.refreshing = this.sync(at, log).finally(() => {
|
|
74829
|
+
this.settledAt = Math.max(
|
|
74830
|
+
this.settledAt,
|
|
74831
|
+
this.pausedSinceRefreshStarted() ? this.lastRefresh : this.clock()
|
|
74832
|
+
);
|
|
74833
|
+
this.stopTicking();
|
|
74645
74834
|
this.refreshing = void 0;
|
|
74646
74835
|
});
|
|
74647
74836
|
return this.refreshing;
|
|
74648
74837
|
}
|
|
74838
|
+
startTicking() {
|
|
74839
|
+
if (!this.canFreeze) {
|
|
74840
|
+
return;
|
|
74841
|
+
}
|
|
74842
|
+
this.lastTick = this.clock();
|
|
74843
|
+
this.pausedWhileRefreshing = false;
|
|
74844
|
+
this.ticker = setInterval(() => {
|
|
74845
|
+
const at = this.clock();
|
|
74846
|
+
if (at - this.lastTick > REFRESH_PAUSE_MS) {
|
|
74847
|
+
this.pausedWhileRefreshing = true;
|
|
74848
|
+
}
|
|
74849
|
+
this.lastTick = at;
|
|
74850
|
+
}, REFRESH_TICK_MS);
|
|
74851
|
+
this.ticker.unref?.();
|
|
74852
|
+
}
|
|
74853
|
+
stopTicking() {
|
|
74854
|
+
clearInterval(this.ticker);
|
|
74855
|
+
this.ticker = void 0;
|
|
74856
|
+
}
|
|
74857
|
+
/**
|
|
74858
|
+
* Whether the process was paused since {@link refreshing} started: a tick
|
|
74859
|
+
* came late, or the next one is overdue (after a thaw, a request can run
|
|
74860
|
+
* before the timer does).
|
|
74861
|
+
*/
|
|
74862
|
+
pausedSinceRefreshStarted() {
|
|
74863
|
+
return this.canFreeze && (this.pausedWhileRefreshing || this.clock() - this.lastTick > REFRESH_PAUSE_MS);
|
|
74864
|
+
}
|
|
74649
74865
|
/**
|
|
74650
74866
|
* Record a revalidation of `tags`: here at once, and in the revalidation
|
|
74651
74867
|
* table for every other instance - the marker row plus a log row per tag.
|
|
@@ -74678,7 +74894,7 @@ var TrackedTagMarkers = class {
|
|
|
74678
74894
|
const at = markerClock();
|
|
74679
74895
|
let state2 = "fresh";
|
|
74680
74896
|
for (const tag of tags) {
|
|
74681
|
-
const marker = this.
|
|
74897
|
+
const marker = this.knownMarker(tag);
|
|
74682
74898
|
if (!marker) {
|
|
74683
74899
|
continue;
|
|
74684
74900
|
}
|
|
@@ -74702,7 +74918,7 @@ var TrackedTagMarkers = class {
|
|
|
74702
74918
|
const at = markerClock();
|
|
74703
74919
|
let latest = 0;
|
|
74704
74920
|
for (const tag of tags) {
|
|
74705
|
-
const marker = this.
|
|
74921
|
+
const marker = this.knownMarker(tag);
|
|
74706
74922
|
if (!marker) {
|
|
74707
74923
|
continue;
|
|
74708
74924
|
}
|
|
@@ -74789,13 +75005,14 @@ var TrackedTagMarkers = class {
|
|
|
74789
75005
|
);
|
|
74790
75006
|
return;
|
|
74791
75007
|
}
|
|
74792
|
-
const
|
|
74793
|
-
|
|
74794
|
-
|
|
74795
|
-
|
|
74796
|
-
|
|
74797
|
-
|
|
74798
|
-
|
|
75008
|
+
const due = this.due(at);
|
|
75009
|
+
if (due.length > 0) {
|
|
75010
|
+
void this.startRead(due);
|
|
75011
|
+
}
|
|
75012
|
+
const result = await log.query(this.cursor).catch((error2) => {
|
|
75013
|
+
console.error("Error reading tag revalidation log:", error2);
|
|
75014
|
+
return void 0;
|
|
75015
|
+
});
|
|
74799
75016
|
if (!result) {
|
|
74800
75017
|
return;
|
|
74801
75018
|
}
|
|
@@ -74806,6 +75023,11 @@ var TrackedTagMarkers = class {
|
|
|
74806
75023
|
);
|
|
74807
75024
|
return;
|
|
74808
75025
|
}
|
|
75026
|
+
const scan = at - this.lastEntryScan > ENTRY_SCAN_INTERVAL_MS;
|
|
75027
|
+
if (scan) {
|
|
75028
|
+
this.lastEntryScan = at;
|
|
75029
|
+
}
|
|
75030
|
+
const oldest = this.oldestEntry(scan);
|
|
74809
75031
|
let applied = 0;
|
|
74810
75032
|
for (const row of result.rows) {
|
|
74811
75033
|
if (this.applied.has(row.sk)) {
|
|
@@ -74820,11 +75042,20 @@ var TrackedTagMarkers = class {
|
|
|
74820
75042
|
mergeMarkers(this.pendingRows.get(row.tag), row.marker)
|
|
74821
75043
|
);
|
|
74822
75044
|
} else {
|
|
75045
|
+
this.rememberLogged(
|
|
75046
|
+
row.tag,
|
|
75047
|
+
mergeMarkers(this.logOnly.get(row.tag), row.marker),
|
|
75048
|
+
oldest
|
|
75049
|
+
);
|
|
74823
75050
|
continue;
|
|
74824
75051
|
}
|
|
74825
75052
|
this.applied.set(row.sk, row.at);
|
|
74826
75053
|
applied++;
|
|
74827
75054
|
}
|
|
75055
|
+
if (oldest > this.prunedAgainst) {
|
|
75056
|
+
this.pruneLogged(oldest);
|
|
75057
|
+
}
|
|
75058
|
+
this.prunedAgainst = oldest;
|
|
74828
75059
|
this.lastLogRead = at;
|
|
74829
75060
|
this.advance(at - REVALIDATION_LOG_LOOKBACK_MS);
|
|
74830
75061
|
this.debug(
|
|
@@ -74840,14 +75071,16 @@ var TrackedTagMarkers = class {
|
|
|
74840
75071
|
const due = [];
|
|
74841
75072
|
for (const tag of this.unread) {
|
|
74842
75073
|
if (due.length >= BATCH_GET_MAX_KEYS) return due;
|
|
74843
|
-
|
|
75074
|
+
if (!this.reading.has(tag)) {
|
|
75075
|
+
due.push(tag);
|
|
75076
|
+
}
|
|
74844
75077
|
}
|
|
74845
75078
|
const jitter = this.resyncIntervalMs * TAG_RESYNC_JITTER;
|
|
74846
75079
|
for (const [tag, dueAt] of this.dueAt) {
|
|
74847
75080
|
if (due.length >= BATCH_GET_MAX_KEYS || dueAt - jitter > at) {
|
|
74848
75081
|
break;
|
|
74849
75082
|
}
|
|
74850
|
-
if (dueAt <= at && !this.unread.has(tag)) {
|
|
75083
|
+
if (dueAt <= at && !this.unread.has(tag) && !this.reading.has(tag)) {
|
|
74851
75084
|
due.push(tag);
|
|
74852
75085
|
}
|
|
74853
75086
|
}
|
|
@@ -74860,10 +75093,19 @@ var TrackedTagMarkers = class {
|
|
|
74860
75093
|
*/
|
|
74861
75094
|
forget(at, why) {
|
|
74862
75095
|
this.debug(`forgetting ${this.tags.size} tracked tags: ${why}`);
|
|
75096
|
+
this.forgotten++;
|
|
75097
|
+
for (const marker of this.tags.values()) {
|
|
75098
|
+
this.raiseFloor(marker);
|
|
75099
|
+
}
|
|
75100
|
+
for (const marker of this.logOnly.values()) {
|
|
75101
|
+
this.raiseFloor(marker);
|
|
75102
|
+
}
|
|
74863
75103
|
this.tags.clear();
|
|
74864
75104
|
this.dueAt.clear();
|
|
74865
75105
|
this.unread.clear();
|
|
74866
75106
|
this.applied.clear();
|
|
75107
|
+
this.logOnly.clear();
|
|
75108
|
+
this.floor = Math.max(this.floor, markerClock());
|
|
74867
75109
|
this.cursor = at - REVALIDATION_LOG_LOOKBACK_MS;
|
|
74868
75110
|
this.lastLogRead = at;
|
|
74869
75111
|
}
|
|
@@ -74889,13 +75131,17 @@ var TrackedTagMarkers = class {
|
|
|
74889
75131
|
* always answered with, rather than a read on every request while DynamoDB
|
|
74890
75132
|
* is unavailable.
|
|
74891
75133
|
*/
|
|
74892
|
-
async readInto(tags) {
|
|
75134
|
+
async readInto(tags, again = false) {
|
|
74893
75135
|
if (tags.length === 0) {
|
|
74894
75136
|
return true;
|
|
74895
75137
|
}
|
|
74896
75138
|
const at = this.clock();
|
|
75139
|
+
const forgotten = this.forgotten;
|
|
74897
75140
|
try {
|
|
74898
75141
|
const read = await this.markers.read(tags);
|
|
75142
|
+
if (this.forgotten !== forgotten && !again) {
|
|
75143
|
+
return await this.readInto(tags, true);
|
|
75144
|
+
}
|
|
74899
75145
|
for (const tag of tags) {
|
|
74900
75146
|
this.unread.delete(tag);
|
|
74901
75147
|
this.remember(
|
|
@@ -74911,6 +75157,9 @@ var TrackedTagMarkers = class {
|
|
|
74911
75157
|
return true;
|
|
74912
75158
|
} catch (error2) {
|
|
74913
75159
|
console.error("Error reading tag markers:", error2);
|
|
75160
|
+
if (this.forgotten !== forgotten && !again) {
|
|
75161
|
+
return this.readInto(tags, true);
|
|
75162
|
+
}
|
|
74914
75163
|
this.trackUnread(tags);
|
|
74915
75164
|
for (const tag of tags) {
|
|
74916
75165
|
this.unread.add(tag);
|
|
@@ -74924,8 +75173,119 @@ var TrackedTagMarkers = class {
|
|
|
74924
75173
|
}
|
|
74925
75174
|
takePendingRows(tag) {
|
|
74926
75175
|
const pending = this.pendingRows.get(tag);
|
|
75176
|
+
const logged = this.logOnly.get(tag);
|
|
74927
75177
|
this.pendingRows.delete(tag);
|
|
74928
|
-
|
|
75178
|
+
this.logOnly.delete(tag);
|
|
75179
|
+
return logged ? mergeMarkers(logged, pending) : pending;
|
|
75180
|
+
}
|
|
75181
|
+
/**
|
|
75182
|
+
* What this instance knows of `tag`: tracked, or from the log alone -
|
|
75183
|
+
* including rows that arrived while its first read is in flight.
|
|
75184
|
+
*/
|
|
75185
|
+
knownMarker(tag) {
|
|
75186
|
+
const tracked = this.tags.get(tag);
|
|
75187
|
+
if (tracked) {
|
|
75188
|
+
return tracked;
|
|
75189
|
+
}
|
|
75190
|
+
const logged = this.logOnly.get(tag);
|
|
75191
|
+
const pending = this.pendingRows.get(tag);
|
|
75192
|
+
return logged || pending ? mergeMarkers(logged, pending) : void 0;
|
|
75193
|
+
}
|
|
75194
|
+
/**
|
|
75195
|
+
* Keep `tag`'s log rows in `logOnly`, unless they cannot apply to an entry
|
|
75196
|
+
* held, `oldest` or newer (see {@link pruneLogged}), as bounded as the
|
|
75197
|
+
* tracked tags. Either way a row let go of takes what it knew with it, so
|
|
75198
|
+
* `completeSince` moves past it.
|
|
75199
|
+
*/
|
|
75200
|
+
rememberLogged(tag, marker, oldest) {
|
|
75201
|
+
this.logOnly.delete(tag);
|
|
75202
|
+
if (knownBy(marker, markerClock()) <= oldest) {
|
|
75203
|
+
this.raiseFloor(marker);
|
|
75204
|
+
return;
|
|
75205
|
+
}
|
|
75206
|
+
this.logOnly.set(tag, marker);
|
|
75207
|
+
for (const [first, dropped] of this.logOnly) {
|
|
75208
|
+
if (this.logOnly.size <= this.maxTrackedTags) {
|
|
75209
|
+
break;
|
|
75210
|
+
}
|
|
75211
|
+
this.logOnly.delete(first);
|
|
75212
|
+
this.raiseFloor(dropped);
|
|
75213
|
+
}
|
|
75214
|
+
}
|
|
75215
|
+
/**
|
|
75216
|
+
* Drop the `logOnly` rows that cannot apply to an entry held, `oldest` or
|
|
75217
|
+
* newer: every revalidation they name so far is no later than it. That
|
|
75218
|
+
* moves `completeSince` no further than `oldest`, which costs the entries
|
|
75219
|
+
* held nothing, and a profile's `expiredAt` still to come waits in
|
|
75220
|
+
* `laterFloors`. Without this, a deployment revalidating many distinct tags
|
|
75221
|
+
* filled `logOnly` with tags no entry here carries, and the evictions past
|
|
75222
|
+
* its bound kept moving `completeSince` up to the present.
|
|
75223
|
+
*/
|
|
75224
|
+
pruneLogged(oldest) {
|
|
75225
|
+
const at = markerClock();
|
|
75226
|
+
for (const [tag, marker] of this.logOnly) {
|
|
75227
|
+
if (knownBy(marker, at) <= oldest) {
|
|
75228
|
+
this.logOnly.delete(tag);
|
|
75229
|
+
this.raiseFloor(marker);
|
|
75230
|
+
}
|
|
75231
|
+
}
|
|
75232
|
+
}
|
|
75233
|
+
/**
|
|
75234
|
+
* The oldest entry timestamp of any registered store, `Infinity` with none:
|
|
75235
|
+
* early, never late, unless `scan`.
|
|
75236
|
+
*/
|
|
75237
|
+
oldestEntry(scan) {
|
|
75238
|
+
let oldest = Infinity;
|
|
75239
|
+
for (const { oldestTimestamp } of this.entryStores) {
|
|
75240
|
+
oldest = Math.min(oldest, oldestTimestamp(scan));
|
|
75241
|
+
}
|
|
75242
|
+
return oldest;
|
|
75243
|
+
}
|
|
75244
|
+
/**
|
|
75245
|
+
* Move `completeSince` past `marker`'s revalidations: what is no longer
|
|
75246
|
+
* known about a tag once its marker is dropped. A future `expiredAt` waits
|
|
75247
|
+
* in `laterFloors` until it is past.
|
|
75248
|
+
*/
|
|
75249
|
+
raiseFloor(marker) {
|
|
75250
|
+
const at = markerClock();
|
|
75251
|
+
this.floor = Math.max(this.floor, knownBy(marker, at));
|
|
75252
|
+
const { expiredAt } = marker;
|
|
75253
|
+
if (expiredAt !== void 0 && expiredAt > Math.max(at, this.floor)) {
|
|
75254
|
+
this.laterFloor(expiredAt);
|
|
75255
|
+
}
|
|
75256
|
+
}
|
|
75257
|
+
/**
|
|
75258
|
+
* Add `time` to `laterFloors`, in order. Past the tracked-tag bound the two
|
|
75259
|
+
* spans closest together are merged: from the first's start,
|
|
75260
|
+
* `completeSince` is the second's end, early - which only costs reads, and
|
|
75261
|
+
* for as short a time as any merge allows. Never later than its time, which
|
|
75262
|
+
* would let an entry expire unseen.
|
|
75263
|
+
*/
|
|
75264
|
+
laterFloor(time2) {
|
|
75265
|
+
const floors = this.laterFloors;
|
|
75266
|
+
let low = 0;
|
|
75267
|
+
let high = floors.length;
|
|
75268
|
+
while (low < high) {
|
|
75269
|
+
const mid = Math.floor((low + high) / 2);
|
|
75270
|
+
if (floors[mid][0] < time2) {
|
|
75271
|
+
low = mid + 1;
|
|
75272
|
+
} else {
|
|
75273
|
+
high = mid;
|
|
75274
|
+
}
|
|
75275
|
+
}
|
|
75276
|
+
if (low > 0 && floors[low - 1][1] >= time2 || floors[low]?.[0] === time2) {
|
|
75277
|
+
return;
|
|
75278
|
+
}
|
|
75279
|
+
floors.splice(low, 0, [time2, time2]);
|
|
75280
|
+
if (floors.length > this.maxTrackedTags) {
|
|
75281
|
+
let merge2 = 0;
|
|
75282
|
+
for (let i5 = 1; i5 < floors.length - 1; i5++) {
|
|
75283
|
+
if (floors[i5 + 1][1] - floors[i5][0] < floors[merge2 + 1][1] - floors[merge2][0]) {
|
|
75284
|
+
merge2 = i5;
|
|
75285
|
+
}
|
|
75286
|
+
}
|
|
75287
|
+
floors.splice(merge2, 2, [floors[merge2][0], floors[merge2 + 1][1]]);
|
|
75288
|
+
}
|
|
74929
75289
|
}
|
|
74930
75290
|
/** Record that `tag`'s marker was known from the table at `at`. */
|
|
74931
75291
|
known(tag, at) {
|
|
@@ -74942,6 +75302,7 @@ var TrackedTagMarkers = class {
|
|
|
74942
75302
|
if (this.tags.size <= this.maxTrackedTags) {
|
|
74943
75303
|
break;
|
|
74944
75304
|
}
|
|
75305
|
+
this.raiseFloor(this.tags.get(oldest));
|
|
74945
75306
|
this.tags.delete(oldest);
|
|
74946
75307
|
this.dueAt.delete(oldest);
|
|
74947
75308
|
this.unread.delete(oldest);
|
|
@@ -74954,7 +75315,18 @@ var TrackedTagMarkers = class {
|
|
|
74954
75315
|
}
|
|
74955
75316
|
}
|
|
74956
75317
|
};
|
|
75318
|
+
function knownBy(marker, at) {
|
|
75319
|
+
const { revalidatedAt, staleAt, expiredAt } = marker;
|
|
75320
|
+
return Math.max(
|
|
75321
|
+
revalidatedAt ?? -Infinity,
|
|
75322
|
+
staleAt ?? -Infinity,
|
|
75323
|
+
expiredAt !== void 0 && expiredAt <= at ? expiredAt : -Infinity
|
|
75324
|
+
);
|
|
75325
|
+
}
|
|
74957
75326
|
var MAX_LOG_PUT_MS = REVALIDATION_LOG_LOOKBACK_MS / 2;
|
|
75327
|
+
var ENTRY_SCAN_INTERVAL_MS = 6e4;
|
|
75328
|
+
var REFRESH_TICK_MS = 250;
|
|
75329
|
+
var REFRESH_PAUSE_MS = 1e3;
|
|
74958
75330
|
function mergeMarkers(local, remote) {
|
|
74959
75331
|
if (!local) {
|
|
74960
75332
|
return remote ?? {};
|
|
@@ -75017,6 +75389,13 @@ var CacheBucket = class {
|
|
|
75017
75389
|
// src/adapter/use-cache-common.ts
|
|
75018
75390
|
var import_client_dynamodb2 = __toESM(require_dist_cjs19());
|
|
75019
75391
|
var import_debug6 = __toESM(require_src());
|
|
75392
|
+
|
|
75393
|
+
// src/runtime/tag-manifest.ts
|
|
75394
|
+
var TAG_MANIFEST_SYMBOL = /* @__PURE__ */ Symbol.for(
|
|
75395
|
+
"cdk-nextjs.use-cache.tag-manifest"
|
|
75396
|
+
);
|
|
75397
|
+
|
|
75398
|
+
// src/adapter/use-cache-common.ts
|
|
75020
75399
|
function isBuildPhase() {
|
|
75021
75400
|
return process.env.NEXT_PHASE === "phase-production-build";
|
|
75022
75401
|
}
|
|
@@ -75026,7 +75405,7 @@ function numberFromEnv(name, fallback2) {
|
|
|
75026
75405
|
return Number.isFinite(value) && value >= 0 ? value : fallback2;
|
|
75027
75406
|
}
|
|
75028
75407
|
var DEFAULT_MEMORY_BYTES = 50 * 1024 * 1024;
|
|
75029
|
-
var
|
|
75408
|
+
var TAG_TABLE_REQUEST_TIMEOUT_MS = 3e3;
|
|
75030
75409
|
function sharedTagManifest() {
|
|
75031
75410
|
const global = globalThis;
|
|
75032
75411
|
if (!global[TAG_MANIFEST_SYMBOL]) {
|
|
@@ -75036,12 +75415,24 @@ function sharedTagManifest() {
|
|
|
75036
75415
|
DEFAULT_TAG_REFRESH_MS
|
|
75037
75416
|
);
|
|
75038
75417
|
if (config.tableName && !isBuildPhase()) {
|
|
75039
|
-
const client = new import_client_dynamodb2.DynamoDBClient({
|
|
75418
|
+
const client = new import_client_dynamodb2.DynamoDBClient({
|
|
75419
|
+
region: config.region,
|
|
75420
|
+
// Without `throwOnRequestTimeout` the timeout only logs a warning. It
|
|
75421
|
+
// ends once the response headers arrive: `socketTimeout`, an
|
|
75422
|
+
// inactivity timer kept until the request closes, covers the body.
|
|
75423
|
+
requestHandler: {
|
|
75424
|
+
requestTimeout: TAG_TABLE_REQUEST_TIMEOUT_MS,
|
|
75425
|
+
throwOnRequestTimeout: true,
|
|
75426
|
+
socketTimeout: TAG_TABLE_REQUEST_TIMEOUT_MS
|
|
75427
|
+
}
|
|
75428
|
+
});
|
|
75040
75429
|
global[TAG_MANIFEST_SYMBOL] = new TrackedTagMarkers({
|
|
75041
75430
|
markers: new TagMarkerTable(client, config.tableName, config.buildId),
|
|
75042
75431
|
log: new RevalidationLog(client, config.tableName, config.buildId),
|
|
75043
75432
|
refreshIntervalMs,
|
|
75044
|
-
debug: (0, import_debug6.default)("cdk-nextjs:cache-handler:tags")
|
|
75433
|
+
debug: (0, import_debug6.default)("cdk-nextjs:cache-handler:tags"),
|
|
75434
|
+
// Set in every Lambda runtime; a container is never frozen.
|
|
75435
|
+
canFreeze: process.env.AWS_LAMBDA_FUNCTION_NAME !== void 0
|
|
75045
75436
|
});
|
|
75046
75437
|
} else {
|
|
75047
75438
|
if (!isBuildPhase()) {
|