1import * as NodeAssert from "node:assert/strict";
2import * as NodeCrypto from "node:crypto";
3import * as NodeModule from "node:module";
4import * as NodeURL from "node:url";
5
6const root = process.argv[2];
7const count = Number(process.argv[3] ?? 4);
8const bytes = Number(process.argv[4] ?? 32768);
9const require = NodeModule.createRequire(root + "/packages/shared/package.json");
10const load = (name) => import(NodeURL.pathToFileURL(require.resolve("effect/" + name)));
11const [Effect, Cache, Tracer] = await Promise.all(["Effect", "Cache", "Tracer"].map(load));
12const { makeLocalFileTracer } = await import(
13 NodeURL.pathToFileURL(root + "/packages/shared/src/observability.ts")
14);
15const refs = [];
16let exported = 0;
17const tracer = await Effect.runPromise(
18 makeLocalFileTracer({
19 filePath: "unused",
20 maxBytes: 1024,
21 maxFiles: 1,
22 batchWindowMs: 10000,
23 sink: {
24 filePath: "unused",
25 push(record) {
26 NodeAssert.deepEqual(record.exit, { _tag: "Success" });
27 exported++;
28 },
29 flush: Effect.void,
30 close: () => Effect.void,
31 },
32 }),
33);
34// Like the SQLite prepared statement cache, lookup fibers remain alive after use.
35const cache = await Effect.runPromise(
36 Cache.make({ capacity: count, timeToLive: "10 minutes", lookup: (key) => Effect.succeed(key) }),
37);
38
39async function read(index) {
40 const result = await Effect.runPromise(
41 Effect.gen(function* () {
42 const projection = JSON.parse(
43 JSON.stringify({
44 turnItems: [{ output: [{ text: NodeCrypto.randomBytes(bytes).toString("hex") }] }],
45 }),
46 );
47 refs.push(new WeakRef(projection));
48 yield* Cache.get(cache, index);
49 return projection;
50 }).pipe(
51 Effect.withSpan("read-thread-projection"),
52 Effect.provideService(Tracer.Tracer, tracer),
53 ),
54 );
55 NodeAssert.equal(result.turnItems[0].output[0].text.length, bytes * 2);
56}
57
58for (let index = 0; index < count; index++) await read(index);
59for (let index = 0; index < 3; index++) {
60 await new Promise(setImmediate);
61 global.gc();
62}
63console.log(
64 JSON.stringify({
65 cacheSize: await Effect.runPromise(Cache.size(cache)),
66 retained: refs.filter((ref) => ref.deref()).length,
67 exported,
68 payloadMiB: (count * bytes * 2) / 1048576,
69 heapMiB: Math.round(process.memoryUsage().heapUsed / 1048576),
70 }),
71);