capsite/toybox: mount lookups in SQL, not a scan in JS
Each request parsed every mount's labels and searched them in JS. Labels now sit one per row in mount_label, indexed on (key, value): a lookup joins the request's labels (json_each) on the index and keeps the vat whose every label matched. The overlap check at registration is SQL too. mount.labels keeps the whole set as JSON; mounts stored before mount_label get their rows filled in when the schema runs. The SQL lives in mounts-sql.js, tested on node:sqlite, old-shape state included. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
9356f63c64
commit
7fcd251261
4 files changed
+124
-38
No files matched your search
@@ -51,23 +51,3 @@ export const checkUrl = (labels, url) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Do `a` and `b` give some key different values? Flower refuses to register
|
||||
* a target that doesn't conflict with every other: lookups stay unambiguous.
|
||||
*
|
||||
* @param {Record<string, string>} a
|
||||
* @param {Record<string, string>} b
|
||||
*/
|
||||
export const conflict = (a, b) => Object.entries(a).some(([k, v]) => k in b && b[k] !== v);
|
||||
|
||||
/**
|
||||
* Does `request` carry every label of `mount`, with the same values?
|
||||
*
|
||||
* Stricter than Flower's match (no conflict): an ingress client holds no
|
||||
* constrained handle, so a label it omits must not match, or a secret
|
||||
* label would protect nothing.
|
||||
*
|
||||
* @param {Record<string, string>} mount
|
||||
* @param {Record<string, string>} request
|
||||
*/
|
||||
export const carries = (mount, request) => Object.entries(mount).every(([k, v]) => request[k] === v);
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file The ingress's mounts, in SQL: which vat serves which label set.
|
||||
*
|
||||
* Labels sit one per row, so a lookup can use an index; `mount.labels`
|
||||
* keeps the whole set as JSON. Request and mount labels arrive as JSON
|
||||
* objects, split into rows by `json_each`.
|
||||
*/
|
||||
|
||||
export const MOUNT_SCHEMA = [
|
||||
"CREATE TABLE IF NOT EXISTS mount (vat TEXT PRIMARY KEY, labels TEXT)",
|
||||
"CREATE TABLE IF NOT EXISTS mount_label (vat TEXT, key TEXT, value TEXT, PRIMARY KEY (vat, key))",
|
||||
"CREATE INDEX IF NOT EXISTS mount_label_kv ON mount_label (key, value)",
|
||||
// mounts made before mount_label existed
|
||||
`INSERT OR IGNORE INTO mount_label
|
||||
SELECT m.vat, j.key, j.value FROM mount m, json_each(m.labels) j
|
||||
WHERE NOT EXISTS (SELECT 1 FROM mount_label l WHERE l.vat = m.vat)`,
|
||||
];
|
||||
|
||||
/**
|
||||
* Another mount (than ?2's) whose labels conflict with none of ?1's.
|
||||
* Flower's registration rule: every mount must conflict with every other.
|
||||
*/
|
||||
export const OVERLAP = `
|
||||
SELECT 1 FROM mount m WHERE m.vat != ?2 AND NOT EXISTS (
|
||||
SELECT 1 FROM mount_label l JOIN json_each(?1) r ON r.key = l.key
|
||||
WHERE l.vat = m.vat AND l.value != r.value)
|
||||
LIMIT 1`;
|
||||
|
||||
export const MOUNT = "INSERT OR REPLACE INTO mount VALUES (?1, ?2)";
|
||||
export const MOUNT_LABELS = "INSERT INTO mount_label SELECT ?1, key, value FROM json_each(?2)";
|
||||
export const UNMOUNT = "DELETE FROM mount WHERE vat = ?1";
|
||||
export const UNMOUNT_LABELS = "DELETE FROM mount_label WHERE vat = ?1";
|
||||
|
||||
/**
|
||||
* The mount whose every label ?1 (the request's labels) carries, with the
|
||||
* same value: the labels that match, counted per vat, against how many
|
||||
* the vat has. A mount with no labels matches any request.
|
||||
*
|
||||
* Stricter than Flower's match (no conflict): an ingress client holds no
|
||||
* constrained handle, so a label it omits must not match, or a secret
|
||||
* label would protect nothing.
|
||||
*/
|
||||
export const LOOKUP = `
|
||||
SELECT m.vat, m.labels FROM mount m JOIN (
|
||||
SELECT l.vat, count(*) AS hits
|
||||
FROM json_each(?1) r JOIN mount_label l ON l.key = r.key AND l.value = r.value
|
||||
GROUP BY l.vat
|
||||
) h ON h.vat = m.vat
|
||||
WHERE h.hits = (SELECT count(*) FROM mount_label WHERE vat = m.vat)
|
||||
UNION ALL
|
||||
SELECT m.vat, m.labels FROM mount m
|
||||
WHERE NOT EXISTS (SELECT 1 FROM mount_label l WHERE l.vat = m.vat)
|
||||
LIMIT 1`;
|
||||
+18
-18
@@ -4,7 +4,8 @@ import { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
|
||||
import { RpcTarget, newWebSocketRpcSession, newWorkersRpcResponse } from "capnweb";
|
||||
import { E, Far } from "@endo/far";
|
||||
import { makeClient, swissnumFromBytes } from "@endo/ocapn";
|
||||
import { carries, conflict, mergeLabels, urlLabels } from "./labels.js";
|
||||
import { mergeLabels, urlLabels } from "./labels.js";
|
||||
import { LOOKUP, MOUNT, MOUNT_LABELS, MOUNT_SCHEMA, OVERLAP, UNMOUNT, UNMOUNT_LABELS } from "./mounts-sql.js";
|
||||
import { locationOf, makeRpcNetLayer } from "./netlayer.js";
|
||||
import VAT_RUNTIME from "vat-runtime.txt";
|
||||
|
||||
@@ -282,14 +283,7 @@ export class Router extends WorkerEntrypoint {
|
||||
export class Directory extends DurableObject {
|
||||
constructor(ctx, env) {
|
||||
super(ctx, env);
|
||||
ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS mount (vat TEXT PRIMARY KEY, labels TEXT)");
|
||||
}
|
||||
|
||||
#mounts() {
|
||||
return this.ctx.storage.sql
|
||||
.exec("SELECT vat, labels FROM mount")
|
||||
.toArray()
|
||||
.map(({ vat, labels }) => ({ vat, labels: JSON.parse(labels) }));
|
||||
for (const statement of MOUNT_SCHEMA) ctx.storage.sql.exec(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,22 +293,28 @@ export class Directory extends DurableObject {
|
||||
* @param {Record<string, string> | null} labels
|
||||
*/
|
||||
mount(vat, labels) {
|
||||
if (labels) {
|
||||
const overlap = this.#mounts().find((m) => m.vat !== vat && !conflict(m.labels, labels));
|
||||
if (overlap) throw new Error("mount overlaps another: add a label whose value tells them apart");
|
||||
this.ctx.storage.sql.exec("INSERT OR REPLACE INTO mount VALUES (?, ?)", vat, JSON.stringify(labels));
|
||||
} else {
|
||||
this.ctx.storage.sql.exec("DELETE FROM mount WHERE vat = ?", vat);
|
||||
}
|
||||
const { sql } = this.ctx.storage;
|
||||
this.ctx.storage.transactionSync(() => {
|
||||
sql.exec(UNMOUNT_LABELS, vat);
|
||||
if (!labels) return void sql.exec(UNMOUNT, vat);
|
||||
const json = JSON.stringify(labels);
|
||||
if (sql.exec(OVERLAP, json, vat).toArray().length) {
|
||||
throw new Error("mount overlaps another: add a label whose value tells them apart");
|
||||
}
|
||||
sql.exec(MOUNT, vat, json);
|
||||
sql.exec(MOUNT_LABELS, vat, json);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The mount whose labels `request` carries, if any.
|
||||
* The mount whose labels `request` carries, if any. Registration keeps
|
||||
* mounts pairwise conflicting, so at most one can match.
|
||||
*
|
||||
* @param {Record<string, string>} request
|
||||
*/
|
||||
lookup(request) {
|
||||
return this.#mounts().find((m) => carries(m.labels, request)) ?? null;
|
||||
const [row] = this.ctx.storage.sql.exec(LOOKUP, JSON.stringify(request)).toArray();
|
||||
return row ? { vat: row.vat, labels: JSON.parse(row.labels) } : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/** @file The ingress's mount SQL, on node:sqlite: lookup, overlap, and mounts stored before mount_label. */
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { LOOKUP, MOUNT, MOUNT_LABELS, MOUNT_SCHEMA, OVERLAP } from "../mounts-sql.js";
|
||||
|
||||
const makeDb = () => {
|
||||
const db = new DatabaseSync(":memory:");
|
||||
for (const statement of MOUNT_SCHEMA) db.exec(statement);
|
||||
const mount = (vat, labels) => {
|
||||
const json = JSON.stringify(labels);
|
||||
db.prepare(MOUNT).run(vat, json);
|
||||
db.prepare(MOUNT_LABELS).run(vat, json);
|
||||
};
|
||||
const lookup = (request) => db.prepare(LOOKUP).get(JSON.stringify(request))?.vat ?? null;
|
||||
const overlaps = (vat, labels) => !!db.prepare(OVERLAP).get(JSON.stringify(labels), vat);
|
||||
return { db, mount, lookup, overlaps };
|
||||
};
|
||||
|
||||
test("a request reaches the mount whose every label it carries", () => {
|
||||
const { mount, lookup } = makeDb();
|
||||
mount("a", { vat: "alice", key: "s3cret" });
|
||||
mount("b", { vat: "bob" });
|
||||
assert.equal(lookup({ vat: "alice", key: "s3cret", page: "2" }), "a");
|
||||
assert.equal(lookup({ vat: "alice" }), null);
|
||||
assert.equal(lookup({ vat: "alice", key: "wrong" }), null);
|
||||
assert.equal(lookup({ vat: "bob" }), "b");
|
||||
assert.equal(lookup({}), null);
|
||||
});
|
||||
|
||||
test("a mount with no labels matches any request", () => {
|
||||
const { mount, lookup } = makeDb();
|
||||
mount("only", {});
|
||||
assert.equal(lookup({ anything: "x" }), "only");
|
||||
});
|
||||
|
||||
test("overlap: a mount must conflict with every other", () => {
|
||||
const { mount, overlaps } = makeDb();
|
||||
mount("a", { vat: "alice" });
|
||||
assert.equal(overlaps("b", { vat: "alice", n: "2" }), true);
|
||||
assert.equal(overlaps("b", { app: "x" }), true);
|
||||
assert.equal(overlaps("b", { vat: "bob" }), false);
|
||||
assert.equal(overlaps("a", { vat: "alice", n: "2" }), false, "a vat's own mount doesn't count");
|
||||
});
|
||||
|
||||
test("mounts stored before mount_label are filled in when the schema runs", () => {
|
||||
const db = new DatabaseSync(":memory:");
|
||||
db.exec("CREATE TABLE mount (vat TEXT PRIMARY KEY, labels TEXT)");
|
||||
db.prepare("INSERT INTO mount VALUES (?, ?)").run("old", JSON.stringify({ vat: "old" }));
|
||||
for (const statement of MOUNT_SCHEMA) db.exec(statement);
|
||||
assert.equal(db.prepare(LOOKUP).get(JSON.stringify({ vat: "old" }))?.vat, "old");
|
||||
assert.equal(db.prepare(LOOKUP).get(JSON.stringify({ vat: "other" })), undefined);
|
||||
});
|
||||
Reference in new issue
Block a user