@leejpsd/nextjs-cache-handler 0.1.1 → 0.2.0
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/CHANGELOG.md +58 -0
- package/README.md +91 -12
- package/dist/cache-components/index.cjs +62 -6
- package/dist/cache-components/index.cjs.map +1 -1
- package/dist/cache-components/index.d.cts +1 -1
- package/dist/cache-components/index.d.ts +1 -1
- package/dist/cache-components/index.js +62 -6
- package/dist/cache-components/index.js.map +1 -1
- package/dist/incremental/index.d.cts +1 -1
- package/dist/incremental/index.d.ts +1 -1
- package/dist/index.cjs +62 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +62 -6
- package/dist/index.js.map +1 -1
- package/dist/ops/index.d.cts +1 -1
- package/dist/ops/index.d.ts +1 -1
- package/dist/shared/client/adapter-ioredis.d.cts +1 -1
- package/dist/shared/client/adapter-ioredis.d.ts +1 -1
- package/dist/shared/client/adapter-redis.d.cts +1 -1
- package/dist/shared/client/adapter-redis.d.ts +1 -1
- package/dist/{types-CgriT7My.d.cts → types-Dq1q6qXd.d.cts} +23 -1
- package/dist/{types-CgriT7My.d.ts → types-Dq1q6qXd.d.ts} +23 -1
- package/package.json +4 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { CacheComponentsHandler, createCacheComponentsHandler, partitionEntry, shouldServeStale } from './cache-components/index.cjs';
|
|
2
2
|
export { IncrementalCtx, IncrementalRedisCacheHandler, createIncrementalCacheHandler } from './incremental/index.cjs';
|
|
3
3
|
export { getMetricSnapshot } from './ops/index.cjs';
|
|
4
|
-
export { C as CacheComponentsEntry, b as CacheHandlerOptions, F as FallbackStrategy, I as IncrementalCacheData, L as Logger, M as MetricEmitter, c as MetricEvent, d as MetricEventType, a as RedisClientConfig, e as RedisClientFactory, R as RedisClientLike } from './types-
|
|
4
|
+
export { C as CacheComponentsEntry, b as CacheHandlerOptions, F as FallbackStrategy, I as IncrementalCacheData, L as Logger, M as MetricEmitter, c as MetricEvent, d as MetricEventType, a as RedisClientConfig, e as RedisClientFactory, R as RedisClientLike } from './types-Dq1q6qXd.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Timeout-bounded operation wrapper. Surrounds every Redis call so a hung
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { CacheComponentsHandler, createCacheComponentsHandler, partitionEntry, shouldServeStale } from './cache-components/index.js';
|
|
2
2
|
export { IncrementalCtx, IncrementalRedisCacheHandler, createIncrementalCacheHandler } from './incremental/index.js';
|
|
3
3
|
export { getMetricSnapshot } from './ops/index.js';
|
|
4
|
-
export { C as CacheComponentsEntry, b as CacheHandlerOptions, F as FallbackStrategy, I as IncrementalCacheData, L as Logger, M as MetricEmitter, c as MetricEvent, d as MetricEventType, a as RedisClientConfig, e as RedisClientFactory, R as RedisClientLike } from './types-
|
|
4
|
+
export { C as CacheComponentsEntry, b as CacheHandlerOptions, F as FallbackStrategy, I as IncrementalCacheData, L as Logger, M as MetricEmitter, c as MetricEvent, d as MetricEventType, a as RedisClientConfig, e as RedisClientFactory, R as RedisClientLike } from './types-Dq1q6qXd.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Timeout-bounded operation wrapper. Surrounds every Redis call so a hung
|
package/dist/index.js
CHANGED
|
@@ -641,7 +641,9 @@ var DEFAULT_KEY_PREFIX = "next-cache:";
|
|
|
641
641
|
var ENTRY_SUFFIX = "entry:";
|
|
642
642
|
var TAG_SUFFIX = "tag:";
|
|
643
643
|
var TAG_EXP_SUFFIX = "tag-expiration:";
|
|
644
|
+
var LOCK_SUFFIX = "lock:";
|
|
644
645
|
var DEFAULT_ABORT_MS = 1500;
|
|
646
|
+
var DEFAULT_LOCK_TTL_SEC = 10;
|
|
645
647
|
var ONE_YEAR_SEC = 60 * 60 * 24 * 365;
|
|
646
648
|
var FALLBACK_TTL_SEC = 3600;
|
|
647
649
|
var TAG_EXP_MARKER_TTL_SEC = 60 * 60 * 24 * 7;
|
|
@@ -664,6 +666,8 @@ function init(opts) {
|
|
|
664
666
|
staleWhileRevalidate: opts.staleWhileRevalidate ?? true,
|
|
665
667
|
hashTag: opts.hashTag ?? false,
|
|
666
668
|
keyPrefix: opts.keyPrefix ?? DEFAULT_KEY_PREFIX,
|
|
669
|
+
singleFlight: opts.singleFlight ?? false,
|
|
670
|
+
singleFlightLockTtlSec: opts.singleFlightLockTtlSec ?? DEFAULT_LOCK_TTL_SEC,
|
|
667
671
|
rest: opts
|
|
668
672
|
},
|
|
669
673
|
logger,
|
|
@@ -673,7 +677,8 @@ function init(opts) {
|
|
|
673
677
|
memTags: new MemorySetStore(),
|
|
674
678
|
memTagExp: /* @__PURE__ */ new Map(),
|
|
675
679
|
localTagTimestamps: /* @__PURE__ */ new Map(),
|
|
676
|
-
resolveNs: () => resolveBuildNamespace(opts.buildNamespace)
|
|
680
|
+
resolveNs: () => resolveBuildNamespace(opts.buildNamespace),
|
|
681
|
+
instanceId: process.env.HOSTNAME || process.env.ECS_TASK_ID || `pid-${process.pid}`
|
|
677
682
|
};
|
|
678
683
|
}
|
|
679
684
|
function entryKey(state, cacheKey) {
|
|
@@ -700,6 +705,40 @@ function tagExpKey(state, tag) {
|
|
|
700
705
|
state.opts.hashTag
|
|
701
706
|
);
|
|
702
707
|
}
|
|
708
|
+
function lockKey(state, cacheKey) {
|
|
709
|
+
return buildKey(
|
|
710
|
+
`${state.opts.keyPrefix}${LOCK_SUFFIX}`,
|
|
711
|
+
state.resolveNs(),
|
|
712
|
+
cacheKey,
|
|
713
|
+
state.opts.hashTag
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
async function tryAcquireRefreshLock(state, cacheKey) {
|
|
717
|
+
if (!state.opts.singleFlight) return false;
|
|
718
|
+
if (!gateRedis(state)) return false;
|
|
719
|
+
try {
|
|
720
|
+
return await withAbortSignal(
|
|
721
|
+
"cacheHandlers.singleFlight.lock",
|
|
722
|
+
state.opts.abortTimeoutMs,
|
|
723
|
+
async () => {
|
|
724
|
+
const client = await state.conn.getOrConnect();
|
|
725
|
+
if (!client) return false;
|
|
726
|
+
const result = await execLuaScript(
|
|
727
|
+
client,
|
|
728
|
+
"refreshTagLock",
|
|
729
|
+
[lockKey(state, cacheKey)],
|
|
730
|
+
[
|
|
731
|
+
state.instanceId,
|
|
732
|
+
String(state.opts.singleFlightLockTtlSec)
|
|
733
|
+
]
|
|
734
|
+
);
|
|
735
|
+
return result === 1;
|
|
736
|
+
}
|
|
737
|
+
);
|
|
738
|
+
} catch {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
703
742
|
function resolveTtlSeconds(entry) {
|
|
704
743
|
if (Number.isFinite(entry.expire) && entry.expire > 0) {
|
|
705
744
|
return Math.max(60, Math.ceil(entry.expire));
|
|
@@ -794,11 +833,28 @@ async function getImpl(state, cacheKey, softTags) {
|
|
|
794
833
|
return void 0;
|
|
795
834
|
}
|
|
796
835
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
836
|
+
if (partition.freshness === "stale") {
|
|
837
|
+
if (state.opts.singleFlight) {
|
|
838
|
+
const isLeader = await tryAcquireRefreshLock(state, cacheKey);
|
|
839
|
+
state.emit({
|
|
840
|
+
type: isLeader ? "cache.stale.refresh.leader" : "cache.stale.refresh.follower",
|
|
841
|
+
ms: Date.now() - start,
|
|
842
|
+
meta: { freshness: partition.freshness }
|
|
843
|
+
});
|
|
844
|
+
} else {
|
|
845
|
+
state.emit({
|
|
846
|
+
type: "cache.stale",
|
|
847
|
+
ms: Date.now() - start,
|
|
848
|
+
meta: { freshness: partition.freshness }
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
} else {
|
|
852
|
+
state.emit({
|
|
853
|
+
type: "cache.hit",
|
|
854
|
+
ms: Date.now() - start,
|
|
855
|
+
meta: { freshness: partition.freshness }
|
|
856
|
+
});
|
|
857
|
+
}
|
|
802
858
|
return {
|
|
803
859
|
value: bufferToStream(Buffer.from(envelope.value, "base64")),
|
|
804
860
|
tags: envelope.tags,
|