@voltro/cache 0.85.0 → 0.86.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 +666 -0
- package/THIRD-PARTY-NOTICES.md +1 -1
- package/dist/Cache.js +89 -0
- package/dist/CacheStore.js +5 -0
- package/dist/_virtual/_rolldown/runtime.js +11 -0
- package/dist/backendMemory.js +114 -0
- package/dist/backendRedis.js +56 -0
- package/dist/errors.js +9 -0
- package/dist/index.js +9 -406
- package/dist/invalidationBus.js +14 -0
- package/dist/layer.js +25 -0
- package/dist/semantic.js +131 -0
- package/package.json +2 -2
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Third-Party Notices — @voltro/cache
|
|
2
2
|
|
|
3
|
-
This package
|
|
3
|
+
This package uses the open-source software listed below. Each is the
|
|
4
4
|
property of its respective copyright holders and is used under the terms of
|
|
5
5
|
its license. This file is provided for attribution; it grants no rights in
|
|
6
6
|
@voltro/cache itself, which is proprietary (see LICENSE).
|
package/dist/Cache.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CacheStore as e } from "./CacheStore.js";
|
|
2
|
+
import { Clock as t, Context as n, Deferred as r, Effect as i, HashMap as a, Layer as o, Metric as s, Option as c, SynchronizedRef as l } from "effect";
|
|
3
|
+
//#region src/Cache.ts
|
|
4
|
+
var u = (e) => typeof e == "object" && !!e && e._voltroSwr === 1, d = class d extends n.Service()("@voltro/Cache", { make: i.gen(function* () {
|
|
5
|
+
let n = yield* e, o = yield* l.make(a.empty()), d = s.counter("voltro_cache_hits"), f = s.counter("voltro_cache_misses"), p = (e, r, a) => {
|
|
6
|
+
let o = a.tags === void 0 ? {} : { tags: a.tags }, s = a.swrMs;
|
|
7
|
+
if (s !== void 0) {
|
|
8
|
+
let c = a.ttlMs ?? 0;
|
|
9
|
+
return t.currentTimeMillis.pipe(i.flatMap((t) => n.setWithTags(e, {
|
|
10
|
+
_voltroSwr: 1,
|
|
11
|
+
v: r,
|
|
12
|
+
freshUntil: t + c
|
|
13
|
+
}, {
|
|
14
|
+
ttlMs: c + s,
|
|
15
|
+
...o
|
|
16
|
+
})));
|
|
17
|
+
}
|
|
18
|
+
let c = a.ttlMs === void 0 ? {} : { ttlMs: a.ttlMs };
|
|
19
|
+
return n.setWithTags(e, r, {
|
|
20
|
+
...c,
|
|
21
|
+
...o
|
|
22
|
+
});
|
|
23
|
+
}, m = (e, t, n) => n.pipe(i.tap((n) => p(e, n, t))), h = (e, t) => l.modifyEffect(o, (t) => {
|
|
24
|
+
let n = a.get(t, e);
|
|
25
|
+
return c.isSome(n) ? i.succeed([{
|
|
26
|
+
kind: "await",
|
|
27
|
+
deferred: n.value
|
|
28
|
+
}, t]) : r.make().pipe(i.map((n) => [{
|
|
29
|
+
kind: "own",
|
|
30
|
+
deferred: n
|
|
31
|
+
}, a.set(t, e, n)]));
|
|
32
|
+
}).pipe(i.flatMap((n) => {
|
|
33
|
+
if (n.kind === "await") return r.await(n.deferred);
|
|
34
|
+
let s = n.deferred;
|
|
35
|
+
return t.pipe(i.onExit((t) => r.done(s, t).pipe(i.andThen(l.update(o, (t) => a.remove(t, e))))));
|
|
36
|
+
})), g = (e, r, a) => n.get(e).pipe(i.flatMap((n) => {
|
|
37
|
+
if (c.isSome(n)) {
|
|
38
|
+
let o = n.value;
|
|
39
|
+
return u(o) ? s.update(d, 1).pipe(i.andThen(t.currentTimeMillis), i.flatMap((t) => {
|
|
40
|
+
if (t < o.freshUntil) return i.succeed(o.v);
|
|
41
|
+
let n = h(e, m(e, r, a)).pipe(i.catchCause(() => i.void));
|
|
42
|
+
return i.forkDetach(n).pipe(i.as(o.v));
|
|
43
|
+
})) : s.update(d, 1).pipe(i.as(o));
|
|
44
|
+
}
|
|
45
|
+
return s.update(f, 1).pipe(i.andThen(h(e, m(e, r, a))));
|
|
46
|
+
}));
|
|
47
|
+
return {
|
|
48
|
+
get: (e) => n.get(e),
|
|
49
|
+
set: (e, t, r) => n.set(e, t, r),
|
|
50
|
+
setWithTags: (e, t, r) => n.setWithTags(e, t, r),
|
|
51
|
+
put: (e, t, n) => p(e, n, t),
|
|
52
|
+
remove: n.remove,
|
|
53
|
+
has: n.has,
|
|
54
|
+
clear: n.clear,
|
|
55
|
+
invalidateTag: n.invalidateTag,
|
|
56
|
+
wrap: g,
|
|
57
|
+
partition: (e, t = {}) => {
|
|
58
|
+
let r = (t) => `${e}:${t}`, i = (e) => ({
|
|
59
|
+
...t.ttlMs === void 0 ? {} : { ttlMs: t.ttlMs },
|
|
60
|
+
...t.swrMs === void 0 ? {} : { swrMs: t.swrMs },
|
|
61
|
+
...t.tags === void 0 ? {} : { tags: t.tags },
|
|
62
|
+
...e
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
get: (e) => n.get(r(e)),
|
|
66
|
+
set: (e, i, a) => n.set(r(e), i, a ?? (t.ttlMs === void 0 ? void 0 : { ttlMs: t.ttlMs })),
|
|
67
|
+
has: (e) => n.has(r(e)),
|
|
68
|
+
remove: (e) => n.remove(r(e)),
|
|
69
|
+
wrap: (e, t, n) => g(r(e), i(t), n),
|
|
70
|
+
invalidateTag: (e) => n.invalidateTag(e)
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
stats: () => i.all({
|
|
74
|
+
hits: s.value(d),
|
|
75
|
+
misses: s.value(f)
|
|
76
|
+
}).pipe(i.map(({ hits: e, misses: t }) => {
|
|
77
|
+
let n = Number(e.count), r = Number(t.count), i = n + r;
|
|
78
|
+
return {
|
|
79
|
+
hits: n,
|
|
80
|
+
misses: r,
|
|
81
|
+
hitRate: i === 0 ? 0 : n / i
|
|
82
|
+
};
|
|
83
|
+
}))
|
|
84
|
+
};
|
|
85
|
+
}) }) {
|
|
86
|
+
static layer = o.effect(d, d.make);
|
|
87
|
+
};
|
|
88
|
+
//#endregion
|
|
89
|
+
export { d as Cache };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var e = Object.defineProperty, t = (t, n) => {
|
|
3
|
+
let r = {};
|
|
4
|
+
for (var i in t) e(r, i, {
|
|
5
|
+
get: t[i],
|
|
6
|
+
enumerable: !0
|
|
7
|
+
});
|
|
8
|
+
return n || e(r, Symbol.toStringTag, { value: "Module" }), r;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { t as __exportAll };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { CacheStore as e } from "./CacheStore.js";
|
|
2
|
+
import { Clock as t, Effect as n, HashMap as r, HashSet as i, Layer as a, Option as o, Ref as s } from "effect";
|
|
3
|
+
//#region src/backendMemory.ts
|
|
4
|
+
var c = {
|
|
5
|
+
entries: r.empty(),
|
|
6
|
+
tagIndex: r.empty(),
|
|
7
|
+
seq: 0,
|
|
8
|
+
oldest: void 0,
|
|
9
|
+
newest: void 0
|
|
10
|
+
}, l = (e, t, n) => {
|
|
11
|
+
let a = e;
|
|
12
|
+
for (let e of n) {
|
|
13
|
+
let n = r.get(a, e);
|
|
14
|
+
if (o.isNone(n)) continue;
|
|
15
|
+
let s = i.remove(n.value, t);
|
|
16
|
+
a = i.size(s) === 0 ? r.remove(a, e) : r.set(a, e, s);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
}, u = (e, t) => {
|
|
20
|
+
let n = r.get(e.entries, t);
|
|
21
|
+
if (o.isNone(n)) return e;
|
|
22
|
+
let { older: i, newer: a } = n.value, s = r.remove(e.entries, t);
|
|
23
|
+
if (i !== void 0) {
|
|
24
|
+
let e = r.get(s, i);
|
|
25
|
+
o.isSome(e) && (s = r.set(s, i, {
|
|
26
|
+
...e.value,
|
|
27
|
+
newer: a
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
if (a !== void 0) {
|
|
31
|
+
let e = r.get(s, a);
|
|
32
|
+
o.isSome(e) && (s = r.set(s, a, {
|
|
33
|
+
...e.value,
|
|
34
|
+
older: i
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
entries: s,
|
|
39
|
+
tagIndex: l(e.tagIndex, t, n.value.tags),
|
|
40
|
+
seq: e.seq,
|
|
41
|
+
oldest: i === void 0 ? a : e.oldest,
|
|
42
|
+
newest: a === void 0 ? i : e.newest
|
|
43
|
+
};
|
|
44
|
+
}, d = (e, t, n) => {
|
|
45
|
+
let a = r.get(e, t), s = o.isSome(a) ? i.add(a.value, n) : i.make(n);
|
|
46
|
+
return r.set(e, t, s);
|
|
47
|
+
}, f = (e, t) => {
|
|
48
|
+
let n = r.size(e.entries) - t;
|
|
49
|
+
if (n <= 0) return e;
|
|
50
|
+
let i = e;
|
|
51
|
+
for (let e = 0; e < n; e++) {
|
|
52
|
+
let e = i.oldest;
|
|
53
|
+
if (e === void 0) break;
|
|
54
|
+
i = u(i, e);
|
|
55
|
+
}
|
|
56
|
+
return i;
|
|
57
|
+
}, p = (e, t, n, i, a, s) => {
|
|
58
|
+
let c = u(e, t), l = c.seq + 1, p = c.tagIndex;
|
|
59
|
+
for (let e of a) p = d(p, e, t);
|
|
60
|
+
let m = r.set(c.entries, t, {
|
|
61
|
+
value: n,
|
|
62
|
+
expiresAt: i,
|
|
63
|
+
tags: a,
|
|
64
|
+
seq: l,
|
|
65
|
+
older: c.newest,
|
|
66
|
+
newer: void 0
|
|
67
|
+
});
|
|
68
|
+
if (c.newest !== void 0) {
|
|
69
|
+
let e = r.get(m, c.newest);
|
|
70
|
+
o.isSome(e) && (m = r.set(m, c.newest, {
|
|
71
|
+
...e.value,
|
|
72
|
+
newer: t
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
let h = {
|
|
76
|
+
entries: m,
|
|
77
|
+
tagIndex: p,
|
|
78
|
+
seq: l,
|
|
79
|
+
oldest: c.oldest ?? t,
|
|
80
|
+
newest: t
|
|
81
|
+
};
|
|
82
|
+
return s === void 0 ? h : f(h, s);
|
|
83
|
+
}, m = (e, t) => t === void 0 ? Infinity : e + t, h = (e) => n.gen(function* () {
|
|
84
|
+
let i = yield* s.make(c), a = e.maxEntries, l = e.now === void 0 ? t.currentTimeMillis : n.sync(e.now);
|
|
85
|
+
return {
|
|
86
|
+
get: (e) => l.pipe(n.flatMap((t) => s.modify(i, (n) => {
|
|
87
|
+
let i = r.get(n.entries, e);
|
|
88
|
+
return o.isNone(i) ? [o.none(), n] : i.value.expiresAt <= t ? [o.none(), u(n, e)] : [o.some(i.value.value), n];
|
|
89
|
+
}))),
|
|
90
|
+
set: (e, t, r) => l.pipe(n.flatMap((n) => s.update(i, (i) => p(i, e, t, m(n, r?.ttlMs), [], a)))),
|
|
91
|
+
setWithTags: (e, t, r) => l.pipe(n.flatMap((n) => s.update(i, (i) => p(i, e, t, m(n, r.ttlMs), r.tags ?? [], a)))),
|
|
92
|
+
remove: (e) => l.pipe(n.flatMap((t) => s.modify(i, (n) => {
|
|
93
|
+
let i = r.get(n.entries, e);
|
|
94
|
+
return o.isNone(i) ? [!1, n] : [i.value.expiresAt > t, u(n, e)];
|
|
95
|
+
}))),
|
|
96
|
+
has: (e) => l.pipe(n.flatMap((t) => s.modify(i, (n) => {
|
|
97
|
+
let i = r.get(n.entries, e);
|
|
98
|
+
return o.isNone(i) ? [!1, n] : i.value.expiresAt <= t ? [!1, u(n, e)] : [!0, n];
|
|
99
|
+
}))),
|
|
100
|
+
clear: () => s.set(i, c),
|
|
101
|
+
invalidateTag: (e) => s.modify(i, (t) => {
|
|
102
|
+
let n = r.get(t.tagIndex, e);
|
|
103
|
+
if (o.isNone(n)) return [0, t];
|
|
104
|
+
let i = t, a = 0;
|
|
105
|
+
for (let e of n.value) i = u(i, e), a += 1;
|
|
106
|
+
return [a, {
|
|
107
|
+
...i,
|
|
108
|
+
tagIndex: r.remove(i.tagIndex, e)
|
|
109
|
+
}];
|
|
110
|
+
})
|
|
111
|
+
};
|
|
112
|
+
}), g = (t = {}) => a.effect(e, h(t));
|
|
113
|
+
//#endregion
|
|
114
|
+
export { g as layerMemory };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { CacheError as e } from "./errors.js";
|
|
2
|
+
import { CacheStore as t } from "./CacheStore.js";
|
|
3
|
+
import { Clock as n, Effect as r, Layer as i, Option as a } from "effect";
|
|
4
|
+
import { commandRetry as o, connect as s } from "@voltro/kv/connection";
|
|
5
|
+
//#region src/backendRedis.ts
|
|
6
|
+
var c = (t) => s({
|
|
7
|
+
url: t.url,
|
|
8
|
+
driver: t.driver,
|
|
9
|
+
...t.token === void 0 ? {} : { token: t.token }
|
|
10
|
+
}).pipe(r.mapError((t) => new e({
|
|
11
|
+
operation: "connect",
|
|
12
|
+
key: "",
|
|
13
|
+
cause: String(t)
|
|
14
|
+
}))), l = "\nlocal NOEXP = 9000000000000000\nlocal ttl = tonumber(ARGV[1])\nlocal now = tonumber(ARGV[2])\nlocal score\nif ttl < 0 then score = NOEXP else score = now + ttl end\nredis.call('ZADD', KEYS[1], score, ARGV[3])\nlocal top = redis.call('ZREVRANGE', KEYS[1], 0, 0, 'WITHSCORES')\nlocal maxScore = tonumber(top[2])\nif maxScore >= NOEXP then\n redis.call('PERSIST', KEYS[1])\nelse\n local rem = maxScore - now\n if rem < 1 then rem = 1 end\n redis.call('PEXPIRE', KEYS[1], rem)\nend\nreturn 1\n", u = (t) => r.gen(function* () {
|
|
15
|
+
let i = yield* r.acquireRelease(c(t), (e) => r.promise(() => e.close().catch(() => void 0))), s = t.keyPrefix ?? "voltro:cache", u = (e) => `${s}:v:${e}`, d = (e) => `${s}:t:${e}`, f = (t, n, a) => r.tryPromise({
|
|
16
|
+
try: () => a(i),
|
|
17
|
+
catch: (r) => new e({
|
|
18
|
+
operation: t,
|
|
19
|
+
key: n,
|
|
20
|
+
cause: String(r)
|
|
21
|
+
})
|
|
22
|
+
}).pipe(r.retry(o)), p = (t, n) => r.try({
|
|
23
|
+
try: () => JSON.stringify(n),
|
|
24
|
+
catch: (n) => new e({
|
|
25
|
+
operation: "serialize",
|
|
26
|
+
key: t,
|
|
27
|
+
cause: String(n)
|
|
28
|
+
})
|
|
29
|
+
}), m = (t, n) => r.try({
|
|
30
|
+
try: () => a.some(JSON.parse(n)),
|
|
31
|
+
catch: (n) => new e({
|
|
32
|
+
operation: "deserialize",
|
|
33
|
+
key: t,
|
|
34
|
+
cause: String(n)
|
|
35
|
+
})
|
|
36
|
+
}), h = (e) => f("clear", "", (t) => t.scan(e, `${s}:*`, 200)).pipe(r.flatMap(({ cursor: e, keys: t }) => (t.length > 0 ? f("clear", "", (e) => e.del(t)) : r.succeed(0)).pipe(r.flatMap(() => e === "0" ? r.void : h(e))))), g = (e, t, n) => p(e, t).pipe(r.flatMap((t) => n !== void 0 && n > 0 ? f("set", e, (r) => r.psetex(u(e), Math.ceil(n), t)) : f("set", e, (n) => n.set(u(e), t))), r.asVoid);
|
|
37
|
+
return {
|
|
38
|
+
get: (e) => f("get", e, (t) => t.get(u(e))).pipe(r.flatMap((t) => t === null ? r.succeed(a.none()) : m(e, t))),
|
|
39
|
+
set: (e, t, n) => g(e, t, n?.ttlMs),
|
|
40
|
+
setWithTags: (e, t, i) => g(e, t, i.ttlMs).pipe(r.flatMap(() => {
|
|
41
|
+
let t = i.tags ?? [];
|
|
42
|
+
if (t.length === 0) return r.void;
|
|
43
|
+
let a = i.ttlMs !== void 0 && i.ttlMs > 0 ? Math.ceil(i.ttlMs) : -1;
|
|
44
|
+
return n.currentTimeMillis.pipe(r.flatMap((n) => r.forEach(t, (t) => f("setWithTags", e, (r) => r.eval(l, 1, d(t), a, n, e)), { discard: !0 })));
|
|
45
|
+
})),
|
|
46
|
+
remove: (e) => f("remove", e, (t) => t.del([u(e)])).pipe(r.map((e) => e > 0)),
|
|
47
|
+
has: (e) => f("has", e, (t) => t.exists(u(e))).pipe(r.map((e) => e > 0)),
|
|
48
|
+
clear: () => h("0"),
|
|
49
|
+
invalidateTag: (e) => f("invalidateTag", e, (t) => t.zrange(d(e))).pipe(r.flatMap((t) => f("invalidateTag", e, async (n) => {
|
|
50
|
+
let r = t.map(u), i = r.length > 0 ? await n.del(r) : 0;
|
|
51
|
+
return await n.del([d(e)]), i;
|
|
52
|
+
})))
|
|
53
|
+
};
|
|
54
|
+
}), d = (e) => i.effect(t, u(e));
|
|
55
|
+
//#endregion
|
|
56
|
+
export { d as layerRedis };
|