Skip to content
2 changes: 1 addition & 1 deletion packages/memory/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
Reference,
Signer,
} from "./interface.ts";
import { refer } from "merkle-reference";
import { refer } from "./reference.ts";
import { unauthorized } from "./error.ts";
import { type DID } from "@commontools/identity";
import { fromDID } from "./util.ts";
Expand Down
4 changes: 4 additions & 0 deletions packages/memory/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
"migrate": {
"description": "Performs database migration",
"command": "deno run -A ./migrate.ts"
},
"bench": {
"description": "Run benchmarks for fact operations",
"command": "deno bench --allow-read --allow-write --allow-net --allow-ffi --allow-env --no-check test/benchmark.ts"
}
},
"test": {
Expand Down
2 changes: 1 addition & 1 deletion packages/memory/entity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fromJSON, refer } from "merkle-reference";
import { fromJSON, refer } from "./reference.ts";

export interface Entity<T extends null | NonNullable<unknown>> {
"@": ToString<Entity<T>>;
Expand Down
2 changes: 1 addition & 1 deletion packages/memory/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
TransactionError,
} from "./interface.ts";
import { MemorySpace } from "./interface.ts";
import { refer } from "merkle-reference";
import { refer } from "./reference.ts";

export const unauthorized = (
message: string,
Expand Down
7 changes: 1 addition & 6 deletions packages/memory/fact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ import {
State,
Unclaimed,
} from "./interface.ts";
import {
fromJSON,
fromString,
is as isReference,
refer,
} from "merkle-reference";
import { fromJSON, fromString, is as isReference, refer } from "./reference.ts";

/**
* Creates an unclaimed fact.
Expand Down
42 changes: 41 additions & 1 deletion packages/memory/reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,44 @@ export * from "merkle-reference";
// workaround it like this.
export const fromString = Reference.fromString as (
source: string,
) => Reference.Reference;
) => Reference.View;

/**
* Bounded LRU cache for memoizing refer() results.
* refer() is a pure function (same input → same output), so caching is safe.
* We use JSON.stringify as the cache key since it's ~25x faster than refer().
*/
const CACHE_MAX_SIZE = 1000;
const referCache = new Map<string, Reference.View>();

/**
* Memoized version of refer() that caches results.
* Provides significant speedup for repeated references to the same objects,
* which is common in transaction processing where the same payload is
* referenced multiple times (datum, assertion, commit log).
*/
export const refer = <T>(source: T): Reference.View<T> => {
const key = JSON.stringify(source);

let ref = referCache.get(key);
if (ref !== undefined) {
// Move to end (most recently used) by re-inserting
referCache.delete(key);
referCache.set(key, ref);
return ref as Reference.View<T>;
}

// Compute new reference
ref = Reference.refer(source);

// Evict oldest entry if at capacity
if (referCache.size >= CACHE_MAX_SIZE) {
const oldest = referCache.keys().next().value;
if (oldest !== undefined) {
referCache.delete(oldest);
}
}

referCache.set(key, ref);
return ref as Reference.View<T>;
};
Loading