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
|
@@ -46265,6 +46265,7 @@ var import_debug6 = __toESM(require_src());
|
|
|
46265
46265
|
// src/adapter/aws-cache-store.ts
|
|
46266
46266
|
var import_client_dynamodb = __toESM(require_dist_cjs20());
|
|
46267
46267
|
var import_client_s3 = __toESM(require_dist_cjs16());
|
|
46268
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
46268
46269
|
import { join as join5 } from "node:path";
|
|
46269
46270
|
|
|
46270
46271
|
// src/adapter/cache-utils.ts
|
|
@@ -46323,6 +46324,19 @@ function sha256Hex(value) {
|
|
|
46323
46324
|
return createHash6("sha256").update(value).digest("hex");
|
|
46324
46325
|
}
|
|
46325
46326
|
|
|
46327
|
+
// src/runtime/tag-manifest.ts
|
|
46328
|
+
var OUTSIDE_REQUEST_SYMBOL = /* @__PURE__ */ Symbol.for(
|
|
46329
|
+
"cdk-nextjs.outside-request-context"
|
|
46330
|
+
);
|
|
46331
|
+
function outsideRequest(fn) {
|
|
46332
|
+
const global = globalThis;
|
|
46333
|
+
const run = global[OUTSIDE_REQUEST_SYMBOL];
|
|
46334
|
+
return run ? run(fn) : fn();
|
|
46335
|
+
}
|
|
46336
|
+
var TAG_MANIFEST_SYMBOL = /* @__PURE__ */ Symbol.for(
|
|
46337
|
+
"cdk-nextjs.use-cache.tag-manifest"
|
|
46338
|
+
);
|
|
46339
|
+
|
|
46326
46340
|
// src/adapter/aws-cache-store.ts
|
|
46327
46341
|
function resolveAwsCacheConfig() {
|
|
46328
46342
|
return {
|
|
@@ -46390,6 +46404,7 @@ function markerOf(item) {
|
|
|
46390
46404
|
}
|
|
46391
46405
|
var BATCH_GET_MAX_KEYS = 100;
|
|
46392
46406
|
var DEFAULT_TAG_REFRESH_MS = 1e3;
|
|
46407
|
+
var TAG_REFRESH_GRACE_MS = 1e3;
|
|
46393
46408
|
function hashedTag(tag) {
|
|
46394
46409
|
return `#${sha256Hex(tag)}`;
|
|
46395
46410
|
}
|
|
@@ -46603,10 +46618,64 @@ var TrackedTagMarkers = class {
|
|
|
46603
46618
|
* result rather than dropped.
|
|
46604
46619
|
*/
|
|
46605
46620
|
pendingRows = /* @__PURE__ */ new Map();
|
|
46621
|
+
/**
|
|
46622
|
+
* Log rows for tags not tracked and not being read, merged by tag, oldest
|
|
46623
|
+
* first: what the log has said about them since {@link completeSince}. Taken
|
|
46624
|
+
* into a tag's marker when it is read or written.
|
|
46625
|
+
*/
|
|
46626
|
+
logOnly = /* @__PURE__ */ new Map();
|
|
46627
|
+
/** See {@link completeSince}. */
|
|
46628
|
+
floor;
|
|
46629
|
+
/**
|
|
46630
|
+
* The future `expiredAt`s of markers dropped since, as `[from, to]`, in
|
|
46631
|
+
* order: once `from` is past, {@link completeSince} moves to `to`, and not
|
|
46632
|
+
* before, since only then does it expire anything. `from` and `to` are the
|
|
46633
|
+
* same until spans are merged for space ({@link laterFloor}).
|
|
46634
|
+
*/
|
|
46635
|
+
laterFloors = [];
|
|
46636
|
+
/** The in-memory entry stores judged by {@link completeSince}. */
|
|
46637
|
+
entryStores = [];
|
|
46638
|
+
/** How many times {@link forget} ran: a read started before one is made again. */
|
|
46639
|
+
forgotten = 0;
|
|
46606
46640
|
/** Tracked tags whose read failed, for the next refresh to read again. */
|
|
46607
46641
|
unread = /* @__PURE__ */ new Set();
|
|
46642
|
+
/**
|
|
46643
|
+
* Set inside a page render {@link catchUp} let go, for that render alone.
|
|
46644
|
+
* See {@link behindInRender}.
|
|
46645
|
+
*/
|
|
46646
|
+
caughtUpRender = new AsyncLocalStorage();
|
|
46647
|
+
/** When `sync` last scanned the entry stores ({@link oldestEntry}). */
|
|
46648
|
+
lastEntryScan = -Infinity;
|
|
46649
|
+
/**
|
|
46650
|
+
* No `logOnly` row is known by it or earlier, so a {@link pruneLogged}
|
|
46651
|
+
* against it or earlier drops nothing: a marker is only ever known by later,
|
|
46652
|
+
* as its `expiredAt` passes.
|
|
46653
|
+
*/
|
|
46654
|
+
prunedAgainst = -Infinity;
|
|
46655
|
+
/** When the last refresh started, or was skipped. */
|
|
46608
46656
|
lastRefresh = -Infinity;
|
|
46657
|
+
/**
|
|
46658
|
+
* The refresh in flight. One left open across a freeze comes back after the
|
|
46659
|
+
* thaw with what it read before, so {@link refresh} waits for it and then
|
|
46660
|
+
* for a fresh one, rather than the runtime waiting for it before the freeze:
|
|
46661
|
+
* that would bill every Lambda invocation for the query's tail.
|
|
46662
|
+
*/
|
|
46609
46663
|
refreshing;
|
|
46664
|
+
/**
|
|
46665
|
+
* When {@link refreshing} last showed the process running: a timer ticks
|
|
46666
|
+
* while it is in flight, and a tick that comes late, or none at all, means
|
|
46667
|
+
* the process was paused - frozen - since it started.
|
|
46668
|
+
*/
|
|
46669
|
+
lastTick = -Infinity;
|
|
46670
|
+
/** Whether a tick came late since {@link refreshing} started. */
|
|
46671
|
+
pausedWhileRefreshing = false;
|
|
46672
|
+
ticker;
|
|
46673
|
+
canFreeze;
|
|
46674
|
+
/**
|
|
46675
|
+
* When the last refresh settled, either way, unless it spanned a pause: what
|
|
46676
|
+
* it knows is from then. See {@link behind}.
|
|
46677
|
+
*/
|
|
46678
|
+
settledAt;
|
|
46610
46679
|
/**
|
|
46611
46680
|
* Where the next log query starts (`clock()`): the last successful one's
|
|
46612
46681
|
* start, less {@link REVALIDATION_LOG_LOOKBACK_MS}.
|
|
@@ -46628,20 +46697,68 @@ var TrackedTagMarkers = class {
|
|
|
46628
46697
|
this.random = options.random ?? Math.random;
|
|
46629
46698
|
this.debug = options.debug ?? (() => {
|
|
46630
46699
|
});
|
|
46700
|
+
this.canFreeze = options.canFreeze ?? false;
|
|
46631
46701
|
const at = this.clock();
|
|
46632
46702
|
this.cursor = at - REVALIDATION_LOG_LOOKBACK_MS;
|
|
46633
46703
|
this.lastLogRead = at;
|
|
46704
|
+
this.settledAt = at;
|
|
46705
|
+
this.floor = markerClock();
|
|
46634
46706
|
}
|
|
46635
46707
|
/** `tag`'s marker as this instance knows it, if it tracks `tag`. */
|
|
46636
46708
|
get(tag) {
|
|
46637
46709
|
return this.tags.get(tag);
|
|
46638
46710
|
}
|
|
46711
|
+
/**
|
|
46712
|
+
* Since when (`markerClock()`) this instance knows of every revalidation of
|
|
46713
|
+
* every tag, tracked or not: tracked ones from their markers and the log,
|
|
46714
|
+
* the others from the log alone (`logOnly`). So an entry created at or after
|
|
46715
|
+
* it can be judged by {@link state} and {@link expiration} without
|
|
46716
|
+
* {@link ensure} reading anything - which matters inside a `'use cache'`
|
|
46717
|
+
* lookup, where Next.js waits for the answer and a read pushes the entry out
|
|
46718
|
+
* of the static stage of a staged render.
|
|
46719
|
+
*
|
|
46720
|
+
* Starts at construction: the log is followed from before it. Moves to now
|
|
46721
|
+
* when the tracked markers are forgotten, and past the revalidations of
|
|
46722
|
+
* whatever else it lets go of - a marker evicted for space, a log row
|
|
46723
|
+
* pruned - which are no longer known: to its `expiredAt` only once that is
|
|
46724
|
+
* past, since a profile's `expire` can be a year out, and moving there at
|
|
46725
|
+
* once would turn the read-free path off for the instance's life.
|
|
46726
|
+
*
|
|
46727
|
+
* A row is pruned only once it is no later than every entry held, so that
|
|
46728
|
+
* costs those entries nothing. An entry stored afterwards with an older
|
|
46729
|
+
* `timestamp` - Next.js stamps the start of its generation, which can
|
|
46730
|
+
* outlast a lookback - is older than this too, and is read first.
|
|
46731
|
+
*
|
|
46732
|
+
* It does not cover a revalidation older than the log's first lookback
|
|
46733
|
+
* whose `expiredAt` is still to come, which would expire an entry created
|
|
46734
|
+
* after it. So the `'use cache'` handler reads an entry's tags in the
|
|
46735
|
+
* background as it stores it.
|
|
46736
|
+
*/
|
|
46737
|
+
get completeSince() {
|
|
46738
|
+
const at = markerClock();
|
|
46739
|
+
while (this.laterFloors.length > 0 && this.laterFloors[0][0] <= at) {
|
|
46740
|
+
this.floor = Math.max(this.floor, this.laterFloors.shift()[1]);
|
|
46741
|
+
}
|
|
46742
|
+
return this.floor;
|
|
46743
|
+
}
|
|
46744
|
+
/**
|
|
46745
|
+
* Register an in-memory store whose entries are judged by
|
|
46746
|
+
* {@link completeSince}, by the `timestamp` of the oldest entry it holds:
|
|
46747
|
+
* `logOnly` keeps the rows that could still apply to one of them, and no
|
|
46748
|
+
* others. With none registered it keeps nothing. Asked with `scan` false,
|
|
46749
|
+
* `oldestTimestamp` may answer early, but never late, without a scan.
|
|
46750
|
+
*/
|
|
46751
|
+
judgeEntriesOf(oldestTimestamp, holdsAny = () => oldestTimestamp(false) < Infinity) {
|
|
46752
|
+
this.entryStores.push({ oldestTimestamp, holdsAny });
|
|
46753
|
+
}
|
|
46639
46754
|
/**
|
|
46640
46755
|
* Track `tag` with `marker`: what this instance just wrote, which it knows
|
|
46641
46756
|
* without reading it back.
|
|
46642
46757
|
*/
|
|
46643
46758
|
set(tag, marker) {
|
|
46644
|
-
this.
|
|
46759
|
+
const logged = this.logOnly.get(tag);
|
|
46760
|
+
this.logOnly.delete(tag);
|
|
46761
|
+
this.remember(tag, mergeMarkers(marker, logged));
|
|
46645
46762
|
this.known(tag, this.clock());
|
|
46646
46763
|
}
|
|
46647
46764
|
/**
|
|
@@ -46694,27 +46811,115 @@ var TrackedTagMarkers = class {
|
|
|
46694
46811
|
const unread = [];
|
|
46695
46812
|
for (const tag of new Set(tags)) {
|
|
46696
46813
|
const inFlight = this.reading.get(tag);
|
|
46697
|
-
if (
|
|
46698
|
-
waits.push(inFlight);
|
|
46699
|
-
} else if (this.tags.has(tag)) {
|
|
46814
|
+
if (this.tags.has(tag)) {
|
|
46700
46815
|
this.touch(tag);
|
|
46816
|
+
} else if (inFlight) {
|
|
46817
|
+
waits.push(inFlight);
|
|
46701
46818
|
} else {
|
|
46702
46819
|
unread.push(tag);
|
|
46703
46820
|
}
|
|
46704
46821
|
}
|
|
46705
46822
|
if (unread.length > 0) {
|
|
46706
|
-
|
|
46707
|
-
for (const tag of unread) {
|
|
46708
|
-
this.reading.delete(tag);
|
|
46709
|
-
}
|
|
46710
|
-
});
|
|
46711
|
-
for (const tag of unread) {
|
|
46712
|
-
this.reading.set(tag, read);
|
|
46713
|
-
}
|
|
46714
|
-
waits.push(read);
|
|
46823
|
+
waits.push(this.startRead(unread));
|
|
46715
46824
|
}
|
|
46716
46825
|
await Promise.all(waits);
|
|
46717
46826
|
}
|
|
46827
|
+
/**
|
|
46828
|
+
* {@link readInto} `tags`, as the read of each that whoever needs one of
|
|
46829
|
+
* them next waits on ({@link reading}). Never rejects: a failed read leaves
|
|
46830
|
+
* the tags tracked.
|
|
46831
|
+
*/
|
|
46832
|
+
startRead(tags) {
|
|
46833
|
+
const read = this.readInto(tags).then(() => {
|
|
46834
|
+
for (const tag of tags) {
|
|
46835
|
+
this.reading.delete(tag);
|
|
46836
|
+
}
|
|
46837
|
+
});
|
|
46838
|
+
for (const tag of tags) {
|
|
46839
|
+
this.reading.set(tag, read);
|
|
46840
|
+
}
|
|
46841
|
+
return read;
|
|
46842
|
+
}
|
|
46843
|
+
/**
|
|
46844
|
+
* Whether the last refresh settled more than `refreshIntervalMs` plus
|
|
46845
|
+
* {@link TAG_REFRESH_GRACE_MS} ago - one that spanned a freeze counts from
|
|
46846
|
+
* when it started: after the instance sat idle or frozen - or always, with
|
|
46847
|
+
* a `refreshIntervalMs` of `0`. Answering from what it knows then could
|
|
46848
|
+
* serve an entry revalidated elsewhere since, however long ago that was, so
|
|
46849
|
+
* `refreshTags` waits for the refresh, and the runtime waits for it before
|
|
46850
|
+
* a page render starts ({@link catchUp}).
|
|
46851
|
+
*
|
|
46852
|
+
* From when it settled, not when it started, so a slow query the runtime
|
|
46853
|
+
* waited for before the render does not leave the instance behind inside
|
|
46854
|
+
* it. A failed one counts too, so one request does not wait for two: the
|
|
46855
|
+
* same "assume the entry is valid" a failed read answers with, until the
|
|
46856
|
+
* next refresh, at most the interval and the grace later.
|
|
46857
|
+
*/
|
|
46858
|
+
get behind() {
|
|
46859
|
+
return this.log !== void 0 && // `0` asks before every check, so every check waits for the answer.
|
|
46860
|
+
(this.refreshIntervalMs === 0 || this.overdue(this.settledAt));
|
|
46861
|
+
}
|
|
46862
|
+
/**
|
|
46863
|
+
* {@link behind}, as `refreshTags` asks it: never inside a page's render
|
|
46864
|
+
* {@link catchUp} caught up, since the runtime settled it for that request
|
|
46865
|
+
* before the render started, and a wait inside a `'use cache'` lookup is
|
|
46866
|
+
* what it settled it to avoid - a render crossing the line partway through
|
|
46867
|
+
* would otherwise wait after all. So a revalidation elsewhere can go unseen
|
|
46868
|
+
* for a render's duration on top of the usual window.
|
|
46869
|
+
*
|
|
46870
|
+
* A route handler, which no catch-up precedes, waits at the plain line.
|
|
46871
|
+
*/
|
|
46872
|
+
get behindInRender() {
|
|
46873
|
+
return this.refreshIntervalMs !== 0 && this.caughtUpRender.getStore() ? false : this.behind;
|
|
46874
|
+
}
|
|
46875
|
+
/**
|
|
46876
|
+
* Whether a registered store ({@link judgeEntriesOf}) holds any entry: one
|
|
46877
|
+
* a refresh could expire. With none there is nothing to wait for even when
|
|
46878
|
+
* the instance is {@link behind}, since whatever is stored from here on is
|
|
46879
|
+
* newer than what the log would say - which covers the first `'use cache'`
|
|
46880
|
+
* call of a process, which creates the handler inside the render.
|
|
46881
|
+
*/
|
|
46882
|
+
get holdsEntries() {
|
|
46883
|
+
return this.entryStores.some((store) => store.holdsAny());
|
|
46884
|
+
}
|
|
46885
|
+
/**
|
|
46886
|
+
* {@link refresh}, waited for when the instance is {@link behind}. The
|
|
46887
|
+
* runtime calls it before handing a page request to Next.js, outside the
|
|
46888
|
+
* staged render whose static stage a wait inside a `'use cache'` lookup
|
|
46889
|
+
* would cut short. Only for the `'use cache'` handler's entries
|
|
46890
|
+
* ({@link judgeEntriesOf}): the other caches wait for the refresh where they
|
|
46891
|
+
* read anyway. Not with a `refreshIntervalMs` of `0` either, where
|
|
46892
|
+
* `refreshTags` asks again regardless. Then `render`, marked as caught up
|
|
46893
|
+
* for ({@link behindInRender}). Rejects only as `render` does.
|
|
46894
|
+
*/
|
|
46895
|
+
async catchUp(render) {
|
|
46896
|
+
const log = (error2) => console.error("Error refreshing cache tags:", error2);
|
|
46897
|
+
if (this.refreshIntervalMs > 0 && this.behind) {
|
|
46898
|
+
if (!this.holdsEntries) {
|
|
46899
|
+
return render();
|
|
46900
|
+
}
|
|
46901
|
+
await this.refresh().catch(log);
|
|
46902
|
+
} else if (this.refreshIntervalMs > 0) {
|
|
46903
|
+
void this.refresh().catch(log);
|
|
46904
|
+
}
|
|
46905
|
+
return this.caughtUpRender.run(true, render);
|
|
46906
|
+
}
|
|
46907
|
+
/**
|
|
46908
|
+
* Run `fn` outside the request's context (`outsideRequest`), keeping only
|
|
46909
|
+
* whether the render was {@link catchUp caught up}: a cache handler method
|
|
46910
|
+
* called from inside a staged render, whose clock reads and AWS SDK calls
|
|
46911
|
+
* would otherwise end its static stage.
|
|
46912
|
+
*/
|
|
46913
|
+
outsideRender(fn) {
|
|
46914
|
+
const caughtUp = this.caughtUpRender.getStore();
|
|
46915
|
+
return outsideRequest(
|
|
46916
|
+
() => caughtUp ? this.caughtUpRender.run(caughtUp, fn) : fn()
|
|
46917
|
+
);
|
|
46918
|
+
}
|
|
46919
|
+
/** Whether `at` is more than the interval and the grace ago. */
|
|
46920
|
+
overdue(at) {
|
|
46921
|
+
return this.clock() - at > this.refreshIntervalMs + TAG_REFRESH_GRACE_MS;
|
|
46922
|
+
}
|
|
46718
46923
|
/**
|
|
46719
46924
|
* Catch up on revalidations other instances ran, at most once per
|
|
46720
46925
|
* `refreshIntervalMs`. See {@link TrackedTagMarkers} for the cost.
|
|
@@ -46725,6 +46930,9 @@ var TrackedTagMarkers = class {
|
|
|
46725
46930
|
return;
|
|
46726
46931
|
}
|
|
46727
46932
|
if (this.refreshing) {
|
|
46933
|
+
if (this.behind && this.pausedSinceRefreshStarted()) {
|
|
46934
|
+
return this.refreshing.then(() => this.refresh());
|
|
46935
|
+
}
|
|
46728
46936
|
return this.refreshing;
|
|
46729
46937
|
}
|
|
46730
46938
|
const at = this.clock();
|
|
@@ -46732,14 +46940,47 @@ var TrackedTagMarkers = class {
|
|
|
46732
46940
|
return;
|
|
46733
46941
|
}
|
|
46734
46942
|
this.lastRefresh = at;
|
|
46735
|
-
if (this.tags.size === 0) {
|
|
46943
|
+
if (this.tags.size === 0 && this.entryStores.length === 0) {
|
|
46736
46944
|
return;
|
|
46737
46945
|
}
|
|
46946
|
+
this.startTicking();
|
|
46738
46947
|
this.refreshing = this.sync(at, log).finally(() => {
|
|
46948
|
+
this.settledAt = Math.max(
|
|
46949
|
+
this.settledAt,
|
|
46950
|
+
this.pausedSinceRefreshStarted() ? this.lastRefresh : this.clock()
|
|
46951
|
+
);
|
|
46952
|
+
this.stopTicking();
|
|
46739
46953
|
this.refreshing = void 0;
|
|
46740
46954
|
});
|
|
46741
46955
|
return this.refreshing;
|
|
46742
46956
|
}
|
|
46957
|
+
startTicking() {
|
|
46958
|
+
if (!this.canFreeze) {
|
|
46959
|
+
return;
|
|
46960
|
+
}
|
|
46961
|
+
this.lastTick = this.clock();
|
|
46962
|
+
this.pausedWhileRefreshing = false;
|
|
46963
|
+
this.ticker = setInterval(() => {
|
|
46964
|
+
const at = this.clock();
|
|
46965
|
+
if (at - this.lastTick > REFRESH_PAUSE_MS) {
|
|
46966
|
+
this.pausedWhileRefreshing = true;
|
|
46967
|
+
}
|
|
46968
|
+
this.lastTick = at;
|
|
46969
|
+
}, REFRESH_TICK_MS);
|
|
46970
|
+
this.ticker.unref?.();
|
|
46971
|
+
}
|
|
46972
|
+
stopTicking() {
|
|
46973
|
+
clearInterval(this.ticker);
|
|
46974
|
+
this.ticker = void 0;
|
|
46975
|
+
}
|
|
46976
|
+
/**
|
|
46977
|
+
* Whether the process was paused since {@link refreshing} started: a tick
|
|
46978
|
+
* came late, or the next one is overdue (after a thaw, a request can run
|
|
46979
|
+
* before the timer does).
|
|
46980
|
+
*/
|
|
46981
|
+
pausedSinceRefreshStarted() {
|
|
46982
|
+
return this.canFreeze && (this.pausedWhileRefreshing || this.clock() - this.lastTick > REFRESH_PAUSE_MS);
|
|
46983
|
+
}
|
|
46743
46984
|
/**
|
|
46744
46985
|
* Record a revalidation of `tags`: here at once, and in the revalidation
|
|
46745
46986
|
* table for every other instance - the marker row plus a log row per tag.
|
|
@@ -46772,7 +47013,7 @@ var TrackedTagMarkers = class {
|
|
|
46772
47013
|
const at = markerClock();
|
|
46773
47014
|
let state2 = "fresh";
|
|
46774
47015
|
for (const tag of tags) {
|
|
46775
|
-
const marker = this.
|
|
47016
|
+
const marker = this.knownMarker(tag);
|
|
46776
47017
|
if (!marker) {
|
|
46777
47018
|
continue;
|
|
46778
47019
|
}
|
|
@@ -46796,7 +47037,7 @@ var TrackedTagMarkers = class {
|
|
|
46796
47037
|
const at = markerClock();
|
|
46797
47038
|
let latest = 0;
|
|
46798
47039
|
for (const tag of tags) {
|
|
46799
|
-
const marker = this.
|
|
47040
|
+
const marker = this.knownMarker(tag);
|
|
46800
47041
|
if (!marker) {
|
|
46801
47042
|
continue;
|
|
46802
47043
|
}
|
|
@@ -46883,13 +47124,14 @@ var TrackedTagMarkers = class {
|
|
|
46883
47124
|
);
|
|
46884
47125
|
return;
|
|
46885
47126
|
}
|
|
46886
|
-
const
|
|
46887
|
-
|
|
46888
|
-
|
|
46889
|
-
|
|
46890
|
-
|
|
46891
|
-
|
|
46892
|
-
|
|
47127
|
+
const due = this.due(at);
|
|
47128
|
+
if (due.length > 0) {
|
|
47129
|
+
void this.startRead(due);
|
|
47130
|
+
}
|
|
47131
|
+
const result = await log.query(this.cursor).catch((error2) => {
|
|
47132
|
+
console.error("Error reading tag revalidation log:", error2);
|
|
47133
|
+
return void 0;
|
|
47134
|
+
});
|
|
46893
47135
|
if (!result) {
|
|
46894
47136
|
return;
|
|
46895
47137
|
}
|
|
@@ -46900,6 +47142,11 @@ var TrackedTagMarkers = class {
|
|
|
46900
47142
|
);
|
|
46901
47143
|
return;
|
|
46902
47144
|
}
|
|
47145
|
+
const scan = at - this.lastEntryScan > ENTRY_SCAN_INTERVAL_MS;
|
|
47146
|
+
if (scan) {
|
|
47147
|
+
this.lastEntryScan = at;
|
|
47148
|
+
}
|
|
47149
|
+
const oldest = this.oldestEntry(scan);
|
|
46903
47150
|
let applied = 0;
|
|
46904
47151
|
for (const row of result.rows) {
|
|
46905
47152
|
if (this.applied.has(row.sk)) {
|
|
@@ -46914,11 +47161,20 @@ var TrackedTagMarkers = class {
|
|
|
46914
47161
|
mergeMarkers(this.pendingRows.get(row.tag), row.marker)
|
|
46915
47162
|
);
|
|
46916
47163
|
} else {
|
|
47164
|
+
this.rememberLogged(
|
|
47165
|
+
row.tag,
|
|
47166
|
+
mergeMarkers(this.logOnly.get(row.tag), row.marker),
|
|
47167
|
+
oldest
|
|
47168
|
+
);
|
|
46917
47169
|
continue;
|
|
46918
47170
|
}
|
|
46919
47171
|
this.applied.set(row.sk, row.at);
|
|
46920
47172
|
applied++;
|
|
46921
47173
|
}
|
|
47174
|
+
if (oldest > this.prunedAgainst) {
|
|
47175
|
+
this.pruneLogged(oldest);
|
|
47176
|
+
}
|
|
47177
|
+
this.prunedAgainst = oldest;
|
|
46922
47178
|
this.lastLogRead = at;
|
|
46923
47179
|
this.advance(at - REVALIDATION_LOG_LOOKBACK_MS);
|
|
46924
47180
|
this.debug(
|
|
@@ -46934,14 +47190,16 @@ var TrackedTagMarkers = class {
|
|
|
46934
47190
|
const due = [];
|
|
46935
47191
|
for (const tag of this.unread) {
|
|
46936
47192
|
if (due.length >= BATCH_GET_MAX_KEYS) return due;
|
|
46937
|
-
|
|
47193
|
+
if (!this.reading.has(tag)) {
|
|
47194
|
+
due.push(tag);
|
|
47195
|
+
}
|
|
46938
47196
|
}
|
|
46939
47197
|
const jitter = this.resyncIntervalMs * TAG_RESYNC_JITTER;
|
|
46940
47198
|
for (const [tag, dueAt] of this.dueAt) {
|
|
46941
47199
|
if (due.length >= BATCH_GET_MAX_KEYS || dueAt - jitter > at) {
|
|
46942
47200
|
break;
|
|
46943
47201
|
}
|
|
46944
|
-
if (dueAt <= at && !this.unread.has(tag)) {
|
|
47202
|
+
if (dueAt <= at && !this.unread.has(tag) && !this.reading.has(tag)) {
|
|
46945
47203
|
due.push(tag);
|
|
46946
47204
|
}
|
|
46947
47205
|
}
|
|
@@ -46954,10 +47212,19 @@ var TrackedTagMarkers = class {
|
|
|
46954
47212
|
*/
|
|
46955
47213
|
forget(at, why) {
|
|
46956
47214
|
this.debug(`forgetting ${this.tags.size} tracked tags: ${why}`);
|
|
47215
|
+
this.forgotten++;
|
|
47216
|
+
for (const marker of this.tags.values()) {
|
|
47217
|
+
this.raiseFloor(marker);
|
|
47218
|
+
}
|
|
47219
|
+
for (const marker of this.logOnly.values()) {
|
|
47220
|
+
this.raiseFloor(marker);
|
|
47221
|
+
}
|
|
46957
47222
|
this.tags.clear();
|
|
46958
47223
|
this.dueAt.clear();
|
|
46959
47224
|
this.unread.clear();
|
|
46960
47225
|
this.applied.clear();
|
|
47226
|
+
this.logOnly.clear();
|
|
47227
|
+
this.floor = Math.max(this.floor, markerClock());
|
|
46961
47228
|
this.cursor = at - REVALIDATION_LOG_LOOKBACK_MS;
|
|
46962
47229
|
this.lastLogRead = at;
|
|
46963
47230
|
}
|
|
@@ -46983,13 +47250,17 @@ var TrackedTagMarkers = class {
|
|
|
46983
47250
|
* always answered with, rather than a read on every request while DynamoDB
|
|
46984
47251
|
* is unavailable.
|
|
46985
47252
|
*/
|
|
46986
|
-
async readInto(tags) {
|
|
47253
|
+
async readInto(tags, again = false) {
|
|
46987
47254
|
if (tags.length === 0) {
|
|
46988
47255
|
return true;
|
|
46989
47256
|
}
|
|
46990
47257
|
const at = this.clock();
|
|
47258
|
+
const forgotten = this.forgotten;
|
|
46991
47259
|
try {
|
|
46992
47260
|
const read = await this.markers.read(tags);
|
|
47261
|
+
if (this.forgotten !== forgotten && !again) {
|
|
47262
|
+
return await this.readInto(tags, true);
|
|
47263
|
+
}
|
|
46993
47264
|
for (const tag of tags) {
|
|
46994
47265
|
this.unread.delete(tag);
|
|
46995
47266
|
this.remember(
|
|
@@ -47005,6 +47276,9 @@ var TrackedTagMarkers = class {
|
|
|
47005
47276
|
return true;
|
|
47006
47277
|
} catch (error2) {
|
|
47007
47278
|
console.error("Error reading tag markers:", error2);
|
|
47279
|
+
if (this.forgotten !== forgotten && !again) {
|
|
47280
|
+
return this.readInto(tags, true);
|
|
47281
|
+
}
|
|
47008
47282
|
this.trackUnread(tags);
|
|
47009
47283
|
for (const tag of tags) {
|
|
47010
47284
|
this.unread.add(tag);
|
|
@@ -47018,8 +47292,119 @@ var TrackedTagMarkers = class {
|
|
|
47018
47292
|
}
|
|
47019
47293
|
takePendingRows(tag) {
|
|
47020
47294
|
const pending = this.pendingRows.get(tag);
|
|
47295
|
+
const logged = this.logOnly.get(tag);
|
|
47021
47296
|
this.pendingRows.delete(tag);
|
|
47022
|
-
|
|
47297
|
+
this.logOnly.delete(tag);
|
|
47298
|
+
return logged ? mergeMarkers(logged, pending) : pending;
|
|
47299
|
+
}
|
|
47300
|
+
/**
|
|
47301
|
+
* What this instance knows of `tag`: tracked, or from the log alone -
|
|
47302
|
+
* including rows that arrived while its first read is in flight.
|
|
47303
|
+
*/
|
|
47304
|
+
knownMarker(tag) {
|
|
47305
|
+
const tracked = this.tags.get(tag);
|
|
47306
|
+
if (tracked) {
|
|
47307
|
+
return tracked;
|
|
47308
|
+
}
|
|
47309
|
+
const logged = this.logOnly.get(tag);
|
|
47310
|
+
const pending = this.pendingRows.get(tag);
|
|
47311
|
+
return logged || pending ? mergeMarkers(logged, pending) : void 0;
|
|
47312
|
+
}
|
|
47313
|
+
/**
|
|
47314
|
+
* Keep `tag`'s log rows in `logOnly`, unless they cannot apply to an entry
|
|
47315
|
+
* held, `oldest` or newer (see {@link pruneLogged}), as bounded as the
|
|
47316
|
+
* tracked tags. Either way a row let go of takes what it knew with it, so
|
|
47317
|
+
* `completeSince` moves past it.
|
|
47318
|
+
*/
|
|
47319
|
+
rememberLogged(tag, marker, oldest) {
|
|
47320
|
+
this.logOnly.delete(tag);
|
|
47321
|
+
if (knownBy(marker, markerClock()) <= oldest) {
|
|
47322
|
+
this.raiseFloor(marker);
|
|
47323
|
+
return;
|
|
47324
|
+
}
|
|
47325
|
+
this.logOnly.set(tag, marker);
|
|
47326
|
+
for (const [first, dropped] of this.logOnly) {
|
|
47327
|
+
if (this.logOnly.size <= this.maxTrackedTags) {
|
|
47328
|
+
break;
|
|
47329
|
+
}
|
|
47330
|
+
this.logOnly.delete(first);
|
|
47331
|
+
this.raiseFloor(dropped);
|
|
47332
|
+
}
|
|
47333
|
+
}
|
|
47334
|
+
/**
|
|
47335
|
+
* Drop the `logOnly` rows that cannot apply to an entry held, `oldest` or
|
|
47336
|
+
* newer: every revalidation they name so far is no later than it. That
|
|
47337
|
+
* moves `completeSince` no further than `oldest`, which costs the entries
|
|
47338
|
+
* held nothing, and a profile's `expiredAt` still to come waits in
|
|
47339
|
+
* `laterFloors`. Without this, a deployment revalidating many distinct tags
|
|
47340
|
+
* filled `logOnly` with tags no entry here carries, and the evictions past
|
|
47341
|
+
* its bound kept moving `completeSince` up to the present.
|
|
47342
|
+
*/
|
|
47343
|
+
pruneLogged(oldest) {
|
|
47344
|
+
const at = markerClock();
|
|
47345
|
+
for (const [tag, marker] of this.logOnly) {
|
|
47346
|
+
if (knownBy(marker, at) <= oldest) {
|
|
47347
|
+
this.logOnly.delete(tag);
|
|
47348
|
+
this.raiseFloor(marker);
|
|
47349
|
+
}
|
|
47350
|
+
}
|
|
47351
|
+
}
|
|
47352
|
+
/**
|
|
47353
|
+
* The oldest entry timestamp of any registered store, `Infinity` with none:
|
|
47354
|
+
* early, never late, unless `scan`.
|
|
47355
|
+
*/
|
|
47356
|
+
oldestEntry(scan) {
|
|
47357
|
+
let oldest = Infinity;
|
|
47358
|
+
for (const { oldestTimestamp } of this.entryStores) {
|
|
47359
|
+
oldest = Math.min(oldest, oldestTimestamp(scan));
|
|
47360
|
+
}
|
|
47361
|
+
return oldest;
|
|
47362
|
+
}
|
|
47363
|
+
/**
|
|
47364
|
+
* Move `completeSince` past `marker`'s revalidations: what is no longer
|
|
47365
|
+
* known about a tag once its marker is dropped. A future `expiredAt` waits
|
|
47366
|
+
* in `laterFloors` until it is past.
|
|
47367
|
+
*/
|
|
47368
|
+
raiseFloor(marker) {
|
|
47369
|
+
const at = markerClock();
|
|
47370
|
+
this.floor = Math.max(this.floor, knownBy(marker, at));
|
|
47371
|
+
const { expiredAt } = marker;
|
|
47372
|
+
if (expiredAt !== void 0 && expiredAt > Math.max(at, this.floor)) {
|
|
47373
|
+
this.laterFloor(expiredAt);
|
|
47374
|
+
}
|
|
47375
|
+
}
|
|
47376
|
+
/**
|
|
47377
|
+
* Add `time` to `laterFloors`, in order. Past the tracked-tag bound the two
|
|
47378
|
+
* spans closest together are merged: from the first's start,
|
|
47379
|
+
* `completeSince` is the second's end, early - which only costs reads, and
|
|
47380
|
+
* for as short a time as any merge allows. Never later than its time, which
|
|
47381
|
+
* would let an entry expire unseen.
|
|
47382
|
+
*/
|
|
47383
|
+
laterFloor(time2) {
|
|
47384
|
+
const floors = this.laterFloors;
|
|
47385
|
+
let low = 0;
|
|
47386
|
+
let high = floors.length;
|
|
47387
|
+
while (low < high) {
|
|
47388
|
+
const mid = Math.floor((low + high) / 2);
|
|
47389
|
+
if (floors[mid][0] < time2) {
|
|
47390
|
+
low = mid + 1;
|
|
47391
|
+
} else {
|
|
47392
|
+
high = mid;
|
|
47393
|
+
}
|
|
47394
|
+
}
|
|
47395
|
+
if (low > 0 && floors[low - 1][1] >= time2 || floors[low]?.[0] === time2) {
|
|
47396
|
+
return;
|
|
47397
|
+
}
|
|
47398
|
+
floors.splice(low, 0, [time2, time2]);
|
|
47399
|
+
if (floors.length > this.maxTrackedTags) {
|
|
47400
|
+
let merge2 = 0;
|
|
47401
|
+
for (let i5 = 1; i5 < floors.length - 1; i5++) {
|
|
47402
|
+
if (floors[i5 + 1][1] - floors[i5][0] < floors[merge2 + 1][1] - floors[merge2][0]) {
|
|
47403
|
+
merge2 = i5;
|
|
47404
|
+
}
|
|
47405
|
+
}
|
|
47406
|
+
floors.splice(merge2, 2, [floors[merge2][0], floors[merge2 + 1][1]]);
|
|
47407
|
+
}
|
|
47023
47408
|
}
|
|
47024
47409
|
/** Record that `tag`'s marker was known from the table at `at`. */
|
|
47025
47410
|
known(tag, at) {
|
|
@@ -47036,6 +47421,7 @@ var TrackedTagMarkers = class {
|
|
|
47036
47421
|
if (this.tags.size <= this.maxTrackedTags) {
|
|
47037
47422
|
break;
|
|
47038
47423
|
}
|
|
47424
|
+
this.raiseFloor(this.tags.get(oldest));
|
|
47039
47425
|
this.tags.delete(oldest);
|
|
47040
47426
|
this.dueAt.delete(oldest);
|
|
47041
47427
|
this.unread.delete(oldest);
|
|
@@ -47048,7 +47434,18 @@ var TrackedTagMarkers = class {
|
|
|
47048
47434
|
}
|
|
47049
47435
|
}
|
|
47050
47436
|
};
|
|
47437
|
+
function knownBy(marker, at) {
|
|
47438
|
+
const { revalidatedAt, staleAt, expiredAt } = marker;
|
|
47439
|
+
return Math.max(
|
|
47440
|
+
revalidatedAt ?? -Infinity,
|
|
47441
|
+
staleAt ?? -Infinity,
|
|
47442
|
+
expiredAt !== void 0 && expiredAt <= at ? expiredAt : -Infinity
|
|
47443
|
+
);
|
|
47444
|
+
}
|
|
47051
47445
|
var MAX_LOG_PUT_MS = REVALIDATION_LOG_LOOKBACK_MS / 2;
|
|
47446
|
+
var ENTRY_SCAN_INTERVAL_MS = 6e4;
|
|
47447
|
+
var REFRESH_TICK_MS = 250;
|
|
47448
|
+
var REFRESH_PAUSE_MS = 1e3;
|
|
47052
47449
|
function mergeMarkers(local, remote) {
|
|
47053
47450
|
if (!local) {
|
|
47054
47451
|
return remote ?? {};
|
|
@@ -47191,6 +47588,12 @@ var EntryLru = class {
|
|
|
47191
47588
|
maxBytes;
|
|
47192
47589
|
entries = /* @__PURE__ */ new Map();
|
|
47193
47590
|
bytes = 0;
|
|
47591
|
+
/**
|
|
47592
|
+
* No later than the oldest entry's `timestamp`: the last scan's answer, less
|
|
47593
|
+
* whatever was set since. Entries dropped since only make it early. See
|
|
47594
|
+
* {@link oldestBound}.
|
|
47595
|
+
*/
|
|
47596
|
+
lowest = Infinity;
|
|
47194
47597
|
get(key) {
|
|
47195
47598
|
const entry = this.entries.get(key);
|
|
47196
47599
|
if (entry) {
|
|
@@ -47207,6 +47610,7 @@ var EntryLru = class {
|
|
|
47207
47610
|
}
|
|
47208
47611
|
this.entries.set(key, entry);
|
|
47209
47612
|
this.bytes += size;
|
|
47613
|
+
this.lowest = Math.min(this.lowest, entry.timestamp);
|
|
47210
47614
|
for (const [oldestKey, oldest] of this.entries) {
|
|
47211
47615
|
if (this.bytes <= this.maxBytes) {
|
|
47212
47616
|
break;
|
|
@@ -47220,7 +47624,58 @@ var EntryLru = class {
|
|
|
47220
47624
|
if (existing) {
|
|
47221
47625
|
this.entries.delete(key);
|
|
47222
47626
|
this.bytes -= sizeOf2(key, existing);
|
|
47627
|
+
if (this.entries.size === 0) {
|
|
47628
|
+
this.lowest = Infinity;
|
|
47629
|
+
}
|
|
47630
|
+
}
|
|
47631
|
+
}
|
|
47632
|
+
/**
|
|
47633
|
+
* The `timestamp` of the oldest entry held, `Infinity` with none, dropping
|
|
47634
|
+
* on the way the entries past `revalidate` at `at` (`now()`): a handler
|
|
47635
|
+
* that drops them when next read would otherwise hold them until then, and
|
|
47636
|
+
* they would count here meanwhile. A scan, so asked once in a while: in
|
|
47637
|
+
* between, {@link oldestBound} answers.
|
|
47638
|
+
*/
|
|
47639
|
+
oldestTimestamp(at = -Infinity) {
|
|
47640
|
+
let oldest = Infinity;
|
|
47641
|
+
for (const [key, entry] of this.entries) {
|
|
47642
|
+
if (isPastRevalidate(entry, at)) {
|
|
47643
|
+
this.delete(key);
|
|
47644
|
+
} else {
|
|
47645
|
+
oldest = Math.min(oldest, entry.timestamp);
|
|
47646
|
+
}
|
|
47647
|
+
}
|
|
47648
|
+
this.lowest = oldest;
|
|
47649
|
+
return oldest;
|
|
47650
|
+
}
|
|
47651
|
+
/**
|
|
47652
|
+
* No later than {@link oldestTimestamp}, without its scan: as of the last
|
|
47653
|
+
* one, less the entries set since. `Infinity` with none held. Drops the
|
|
47654
|
+
* entries past `revalidate` at `at` from the least recently used end, up to
|
|
47655
|
+
* the first that isn't, so {@link holdsLive} doesn't walk past them on every
|
|
47656
|
+
* request until the next scan.
|
|
47657
|
+
*/
|
|
47658
|
+
oldestBound(at = -Infinity) {
|
|
47659
|
+
for (const [key, entry] of this.entries) {
|
|
47660
|
+
if (!isPastRevalidate(entry, at)) {
|
|
47661
|
+
break;
|
|
47662
|
+
}
|
|
47663
|
+
this.delete(key);
|
|
47223
47664
|
}
|
|
47665
|
+
return this.entries.size === 0 ? Infinity : this.lowest;
|
|
47666
|
+
}
|
|
47667
|
+
/**
|
|
47668
|
+
* Whether an entry not past `revalidate` at `at` is held. Usually the first
|
|
47669
|
+
* entry answers; the expired ones it passes are dropped by the scan
|
|
47670
|
+
* {@link oldestTimestamp} makes, not here, so asking changes nothing.
|
|
47671
|
+
*/
|
|
47672
|
+
holdsLive(at) {
|
|
47673
|
+
for (const entry of this.entries.values()) {
|
|
47674
|
+
if (!isPastRevalidate(entry, at)) {
|
|
47675
|
+
return true;
|
|
47676
|
+
}
|
|
47677
|
+
}
|
|
47678
|
+
return false;
|
|
47224
47679
|
}
|
|
47225
47680
|
get size() {
|
|
47226
47681
|
return this.entries.size;
|
|
@@ -47229,31 +47684,58 @@ var EntryLru = class {
|
|
|
47229
47684
|
function sizeOf2(key, entry) {
|
|
47230
47685
|
return entry.value.byteLength + key.length;
|
|
47231
47686
|
}
|
|
47687
|
+
function isPastRevalidate(stored, at) {
|
|
47688
|
+
return at > stored.timestamp + stored.revalidate * 1e3;
|
|
47689
|
+
}
|
|
47232
47690
|
var DEFAULT_MEMORY_BYTES = 50 * 1024 * 1024;
|
|
47233
|
-
|
|
47691
|
+
var TAG_TABLE_REQUEST_TIMEOUT_MS = 3e3;
|
|
47692
|
+
async function awaitIf(wait, work, failure) {
|
|
47693
|
+
const logged = work.catch((error2) => {
|
|
47694
|
+
console.error(failure, error2);
|
|
47695
|
+
});
|
|
47696
|
+
if (wait) {
|
|
47697
|
+
await logged;
|
|
47698
|
+
}
|
|
47699
|
+
}
|
|
47700
|
+
function tagMethods(tags, options = {}) {
|
|
47234
47701
|
return {
|
|
47235
|
-
|
|
47236
|
-
|
|
47237
|
-
|
|
47238
|
-
|
|
47239
|
-
|
|
47702
|
+
// Started, not awaited, while the instance keeps up, unless `blocking`.
|
|
47703
|
+
// Next.js awaits `refreshTags` inside the first `'use cache'` lookup of a
|
|
47704
|
+
// request (`use-cache-wrapper`), and a staged render ends its static stage
|
|
47705
|
+
// on a timer: a log `Query` there pushed the entry out of the static
|
|
47706
|
+
// stage, so a cached navigation stored the page segment without it. What
|
|
47707
|
+
// the query finds applies from the next request. Awaited once the instance
|
|
47708
|
+
// is `behind`, which the runtime settles before a page render starts
|
|
47709
|
+
// (`catchUp`): the first request after an idle or frozen spell would
|
|
47710
|
+
// otherwise serve whatever was revalidated elsewhere meanwhile. A page's
|
|
47711
|
+
// render just after `catchUp` gets a grace more (`behindInRender`), so a
|
|
47712
|
+
// request that came in just short of the line doesn't cross it inside
|
|
47713
|
+
// the render. Not even then while the instance holds no entry the query
|
|
47714
|
+
// could expire (`holdsEntries`), with a `refreshIntervalMs` of `0` too.
|
|
47715
|
+
refreshTags: () => awaitIf(
|
|
47716
|
+
options.blocking === true || tags.behindInRender && tags.holdsEntries,
|
|
47717
|
+
tags.refresh(),
|
|
47718
|
+
"Error refreshing cache tags:"
|
|
47719
|
+
),
|
|
47240
47720
|
async updateTags(revalidatedTags, durations) {
|
|
47241
47721
|
await tags.update(revalidatedTags, durations);
|
|
47242
47722
|
}
|
|
47243
47723
|
};
|
|
47244
47724
|
}
|
|
47725
|
+
function outsideRender(fn) {
|
|
47726
|
+
return outsideRequest(sharedTagManifest).outsideRender(fn);
|
|
47727
|
+
}
|
|
47245
47728
|
function lazyHandler(create) {
|
|
47246
47729
|
let handler;
|
|
47247
47730
|
const instance = () => handler ??= create();
|
|
47248
47731
|
return {
|
|
47249
|
-
get: (cacheKey, softTags) => instance().get(cacheKey, softTags),
|
|
47250
|
-
set: (cacheKey, pendingEntry) => instance().set(cacheKey, pendingEntry),
|
|
47251
|
-
refreshTags: () => instance().refreshTags(),
|
|
47252
|
-
getExpiration: (tags) => instance().getExpiration(tags),
|
|
47253
|
-
updateTags: (tags, durations) => instance().updateTags(tags, durations)
|
|
47732
|
+
get: (cacheKey, softTags) => outsideRender(() => instance().get(cacheKey, softTags)),
|
|
47733
|
+
set: (cacheKey, pendingEntry) => outsideRender(() => instance().set(cacheKey, pendingEntry)),
|
|
47734
|
+
refreshTags: () => outsideRender(() => instance().refreshTags()),
|
|
47735
|
+
getExpiration: (tags) => outsideRender(() => instance().getExpiration(tags)),
|
|
47736
|
+
updateTags: (tags, durations) => outsideRender(() => instance().updateTags(tags, durations))
|
|
47254
47737
|
};
|
|
47255
47738
|
}
|
|
47256
|
-
var TAG_MANIFEST_SYMBOL = /* @__PURE__ */ Symbol.for("cdk-nextjs.use-cache.tag-manifest");
|
|
47257
47739
|
function sharedTagManifest() {
|
|
47258
47740
|
const global = globalThis;
|
|
47259
47741
|
if (!global[TAG_MANIFEST_SYMBOL]) {
|
|
@@ -47263,12 +47745,24 @@ function sharedTagManifest() {
|
|
|
47263
47745
|
DEFAULT_TAG_REFRESH_MS
|
|
47264
47746
|
);
|
|
47265
47747
|
if (config.tableName && !isBuildPhase()) {
|
|
47266
|
-
const client = new import_client_dynamodb2.DynamoDBClient({
|
|
47748
|
+
const client = new import_client_dynamodb2.DynamoDBClient({
|
|
47749
|
+
region: config.region,
|
|
47750
|
+
// Without `throwOnRequestTimeout` the timeout only logs a warning. It
|
|
47751
|
+
// ends once the response headers arrive: `socketTimeout`, an
|
|
47752
|
+
// inactivity timer kept until the request closes, covers the body.
|
|
47753
|
+
requestHandler: {
|
|
47754
|
+
requestTimeout: TAG_TABLE_REQUEST_TIMEOUT_MS,
|
|
47755
|
+
throwOnRequestTimeout: true,
|
|
47756
|
+
socketTimeout: TAG_TABLE_REQUEST_TIMEOUT_MS
|
|
47757
|
+
}
|
|
47758
|
+
});
|
|
47267
47759
|
global[TAG_MANIFEST_SYMBOL] = new TrackedTagMarkers({
|
|
47268
47760
|
markers: new TagMarkerTable(client, config.tableName, config.buildId),
|
|
47269
47761
|
log: new RevalidationLog(client, config.tableName, config.buildId),
|
|
47270
47762
|
refreshIntervalMs,
|
|
47271
|
-
debug: (0, import_debug5.default)("cdk-nextjs:cache-handler:tags")
|
|
47763
|
+
debug: (0, import_debug5.default)("cdk-nextjs:cache-handler:tags"),
|
|
47764
|
+
// Set in every Lambda runtime; a container is never frozen.
|
|
47765
|
+
canFreeze: process.env.AWS_LAMBDA_FUNCTION_NAME !== void 0
|
|
47272
47766
|
});
|
|
47273
47767
|
} else {
|
|
47274
47768
|
if (!isBuildPhase()) {
|
|
@@ -47298,9 +47792,6 @@ function bucketFromEnv() {
|
|
|
47298
47792
|
}
|
|
47299
47793
|
return new CacheBucket(new import_client_s32.S3Client({ region }), bucketName);
|
|
47300
47794
|
}
|
|
47301
|
-
function isPastRevalidate(stored, at) {
|
|
47302
|
-
return at > stored.timestamp + stored.revalidate * 1e3;
|
|
47303
|
-
}
|
|
47304
47795
|
function isPastExpire(stored, at) {
|
|
47305
47796
|
return at > stored.timestamp + stored.expire * 1e3;
|
|
47306
47797
|
}
|
|
@@ -47420,7 +47911,13 @@ function createRemoteUseCacheHandler(options = {}) {
|
|
|
47420
47911
|
done();
|
|
47421
47912
|
await writeToS3(cacheKey, stored);
|
|
47422
47913
|
},
|
|
47423
|
-
|
|
47914
|
+
// Waits for the log query on every request: an entry read from S3 may
|
|
47915
|
+
// predate this instance, so what the query says applies to it at once.
|
|
47916
|
+
...tagMethods(tags, { blocking: true }),
|
|
47917
|
+
async getExpiration(implicitTags) {
|
|
47918
|
+
await tags.ensure(implicitTags);
|
|
47919
|
+
return tags.expiration(implicitTags);
|
|
47920
|
+
}
|
|
47424
47921
|
};
|
|
47425
47922
|
}
|
|
47426
47923
|
var use_cache_remote_handler_default = lazyHandler(() => createRemoteUseCacheHandler());
|