报告工人的等待到点就摘掉取消监听。本地数据库缓存表结构,并把设置身份合成一条语句,对照通过后默认打开,写成 off 仍走原来的语句。交付、重算和限流的内存表会过期。登录读缓存默认关闭,要开再设 IDENTITY_SESSION_COOKIE_CACHE=on。 自测:tsc 通过,lint 0 error,点名测试 134 通过,test:db 开关关和开都只有已知的 13 条失败,往返 49 降到 32。 Co-Authored-By: Grok (agent loop)
287 lines
10 KiB
TypeScript
287 lines
10 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import type { Pool } from "pg";
|
|
|
|
import {
|
|
clearLocalPostgresMetadataCacheForTests,
|
|
closeLocalPostgresDataPool,
|
|
createLocalPostgresDataClient,
|
|
installLocalPostgresPoolForTests,
|
|
setLocalPostgresMetadataClockForTests,
|
|
type LocalDatabaseRole,
|
|
} from "../src/lib/db/local-postgres-client-core.ts";
|
|
|
|
type QueryCall = { sql: string; params: readonly unknown[] };
|
|
|
|
function tag(sql: string): string {
|
|
const compact = sql.replace(/\s+/g, " ").trim().toLowerCase();
|
|
if (compact === "begin") return "begin";
|
|
if (compact === "commit") return "commit";
|
|
if (compact === "rollback") return "rollback";
|
|
if (compact.startsWith("set local role")) return "set local role";
|
|
if (compact.includes("set_config('role'")) return "set_config(role)";
|
|
if (compact.includes("set_config('request.jwt.claim.sub'")) return "set_config";
|
|
if (compact.includes("information_schema.columns")) return "information_schema";
|
|
if (compact.includes("from pg_index")) return "primary_key";
|
|
if (compact.includes("from pg_proc")) return "pg_proc";
|
|
return "business";
|
|
}
|
|
|
|
function installFakePool(
|
|
url: string,
|
|
options: {
|
|
columns?: ReadonlyArray<{ column_name: string; udt_name: string }> | null;
|
|
primaryKeys?: readonly string[] | null;
|
|
functions?: ReadonlyArray<Record<string, unknown>> | null;
|
|
failBusinessCode?: () => string | null;
|
|
} = {},
|
|
): QueryCall[] {
|
|
const calls: QueryCall[] = [];
|
|
const client = {
|
|
async query(sql: string, params: readonly unknown[] = []) {
|
|
calls.push({ sql, params });
|
|
const label = tag(sql);
|
|
if (label === "information_schema") {
|
|
const rows = options.columns === null
|
|
? []
|
|
: [...(options.columns ?? [{ column_name: "id", udt_name: "uuid" }])];
|
|
return { rows, rowCount: rows.length };
|
|
}
|
|
if (label === "primary_key") {
|
|
const names = options.primaryKeys === null ? [] : [...(options.primaryKeys ?? ["id"])];
|
|
const rows = names.map((column_name) => ({ column_name }));
|
|
return { rows, rowCount: rows.length };
|
|
}
|
|
if (label === "pg_proc") {
|
|
const rows = options.functions === null
|
|
? []
|
|
: [...(options.functions ?? [{
|
|
proretset: false,
|
|
return_type: "text",
|
|
argument_names: ["left_name", "right_name"],
|
|
argument_types: ["text", "text"],
|
|
}])];
|
|
return { rows, rowCount: rows.length };
|
|
}
|
|
if (label === "business") {
|
|
const code = options.failBusinessCode?.() ?? null;
|
|
if (code) {
|
|
const error = new Error("business sql failed") as Error & { code: string };
|
|
error.code = code;
|
|
throw error;
|
|
}
|
|
if (sql.toLowerCase().includes(" as value")) return { rows: [{ value: "ok" }], rowCount: 1 };
|
|
}
|
|
return { rows: [], rowCount: 0 };
|
|
},
|
|
release() {},
|
|
};
|
|
const pool = {
|
|
async connect() {
|
|
return client;
|
|
},
|
|
async end() {},
|
|
};
|
|
installLocalPostgresPoolForTests(url, pool as unknown as Pool);
|
|
return calls;
|
|
}
|
|
|
|
async function withSwitches(
|
|
values: Record<string, string | undefined>,
|
|
run: () => Promise<void>,
|
|
): Promise<void> {
|
|
const previous = new Map<string, string | undefined>();
|
|
for (const [key, value] of Object.entries(values)) {
|
|
previous.set(key, process.env[key]);
|
|
if (value === undefined) delete process.env[key];
|
|
else process.env[key] = value;
|
|
}
|
|
clearLocalPostgresMetadataCacheForTests();
|
|
setLocalPostgresMetadataClockForTests(null);
|
|
try {
|
|
await run();
|
|
} finally {
|
|
for (const [key, value] of previous) {
|
|
if (value === undefined) delete process.env[key];
|
|
else process.env[key] = value;
|
|
}
|
|
clearLocalPostgresMetadataCacheForTests();
|
|
setLocalPostgresMetadataClockForTests(null);
|
|
}
|
|
}
|
|
|
|
const identity = { id: "018f4e6d-7a11-7000-8000-0000000000aa", email: "person@example.com" };
|
|
|
|
function clientFor(url: string, role: LocalDatabaseRole | null = "authenticated") {
|
|
return createLocalPostgresDataClient(url, role === "service_role" ? null : identity, role);
|
|
}
|
|
|
|
test("metadata cache and merged session setup stay off the baseline query sequence", async () => {
|
|
await withSwitches({
|
|
JYOTISHA_PG_METADATA_CACHE: "off",
|
|
JYOTISHA_PG_MERGED_SESSION_SETUP: "off",
|
|
}, async () => {
|
|
const url = "postgresql://roundtrip-off@127.0.0.1:1/jyotisha";
|
|
const calls = installFakePool(url);
|
|
const client = clientFor(url);
|
|
const first = await client.from("profiles").select("id").eq("id", identity.id);
|
|
assert.equal(first.error, null);
|
|
assert.deepEqual(calls.map((call) => tag(call.sql)), [
|
|
"begin",
|
|
"set local role",
|
|
"set_config",
|
|
"information_schema",
|
|
"business",
|
|
"commit",
|
|
]);
|
|
await client.from("profiles").select("id").eq("id", identity.id);
|
|
assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 2);
|
|
});
|
|
});
|
|
|
|
test("cached metadata and merged session setup drop a repeat select and rpc to four roundtrips", async () => {
|
|
await withSwitches({
|
|
JYOTISHA_PG_METADATA_CACHE: "on",
|
|
JYOTISHA_PG_MERGED_SESSION_SETUP: "on",
|
|
}, async () => {
|
|
const url = "postgresql://roundtrip-on@127.0.0.1:1/jyotisha";
|
|
const calls = installFakePool(url);
|
|
const client = clientFor(url);
|
|
await client.from("profiles").select("id").eq("id", identity.id);
|
|
calls.length = 0;
|
|
await client.from("profiles").select("id").eq("id", identity.id);
|
|
assert.deepEqual(calls.map((call) => tag(call.sql)), [
|
|
"begin",
|
|
"set_config(role)",
|
|
"business",
|
|
"commit",
|
|
]);
|
|
|
|
await client.rpc("sample_probe", { left_name: "a", right_name: "b" });
|
|
calls.length = 0;
|
|
const rpc = await client.rpc("sample_probe", { left_name: "a", right_name: "b" });
|
|
assert.equal(rpc.error, null);
|
|
assert.deepEqual(calls.map((call) => tag(call.sql)), [
|
|
"begin",
|
|
"set_config(role)",
|
|
"business",
|
|
"commit",
|
|
]);
|
|
|
|
await client.from("profiles").upsert({ id: identity.id });
|
|
calls.length = 0;
|
|
await client.from("profiles").upsert({ id: identity.id });
|
|
assert.deepEqual(calls.map((call) => tag(call.sql)), [
|
|
"begin",
|
|
"set_config(role)",
|
|
"business",
|
|
"commit",
|
|
]);
|
|
});
|
|
});
|
|
|
|
test("column cache is per role and function cache is per argument names", async () => {
|
|
await withSwitches({
|
|
JYOTISHA_PG_METADATA_CACHE: "on",
|
|
JYOTISHA_PG_MERGED_SESSION_SETUP: "on",
|
|
}, async () => {
|
|
const url = "postgresql://roundtrip-keys@127.0.0.1:1/jyotisha";
|
|
const calls = installFakePool(url);
|
|
const authenticated = clientFor(url, "authenticated");
|
|
const service = clientFor(url, "service_role");
|
|
await authenticated.from("profiles").select("id");
|
|
calls.length = 0;
|
|
await service.from("profiles").select("id");
|
|
assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 1);
|
|
|
|
await authenticated.rpc("sample_probe", { left_name: "a" });
|
|
calls.length = 0;
|
|
await authenticated.rpc("sample_probe", { right_name: "b" });
|
|
assert.equal(calls.filter((call) => tag(call.sql) === "pg_proc").length, 1);
|
|
});
|
|
});
|
|
|
|
test("undefined column, expiry, pool close, and empty metadata all miss the cache", async () => {
|
|
await withSwitches({
|
|
JYOTISHA_PG_METADATA_CACHE: "on",
|
|
JYOTISHA_PG_MERGED_SESSION_SETUP: "off",
|
|
}, async () => {
|
|
const url = "postgresql://roundtrip-invalidate@127.0.0.1:1/jyotisha";
|
|
let failuresLeft = 1;
|
|
const calls = installFakePool(url, {
|
|
failBusinessCode: () => {
|
|
if (failuresLeft <= 0) return null;
|
|
failuresLeft -= 1;
|
|
return "42703";
|
|
},
|
|
});
|
|
const client = clientFor(url);
|
|
const failed = await client.from("profiles").select("id");
|
|
assert.equal(failed.error?.code, "42703");
|
|
calls.length = 0;
|
|
const retried = await client.from("profiles").select("id");
|
|
assert.equal(retried.error, null);
|
|
assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 1);
|
|
});
|
|
});
|
|
|
|
test("metadata older than 300 seconds is queried again", async () => {
|
|
await withSwitches({
|
|
JYOTISHA_PG_METADATA_CACHE: "on",
|
|
JYOTISHA_PG_MERGED_SESSION_SETUP: "off",
|
|
}, async () => {
|
|
const url = "postgresql://roundtrip-ttl@127.0.0.1:1/jyotisha";
|
|
let now = 10_000;
|
|
setLocalPostgresMetadataClockForTests(() => now);
|
|
const calls = installFakePool(url);
|
|
const client = clientFor(url);
|
|
await client.from("profiles").select("id");
|
|
calls.length = 0;
|
|
await client.from("profiles").select("id");
|
|
assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 0);
|
|
now += 300_000;
|
|
calls.length = 0;
|
|
await client.from("profiles").select("id");
|
|
assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 1);
|
|
});
|
|
});
|
|
|
|
test("an empty column list and a missing function are not cached", async () => {
|
|
await withSwitches({
|
|
JYOTISHA_PG_METADATA_CACHE: "on",
|
|
JYOTISHA_PG_MERGED_SESSION_SETUP: "off",
|
|
}, async () => {
|
|
const url = "postgresql://roundtrip-empty@127.0.0.1:1/jyotisha";
|
|
const calls = installFakePool(url, { columns: null, functions: null, primaryKeys: null });
|
|
const client = clientFor(url);
|
|
await client.from("profiles").select("id");
|
|
calls.length = 0;
|
|
await client.from("profiles").select("id");
|
|
assert.equal(calls.filter((call) => tag(call.sql) === "information_schema").length, 1);
|
|
|
|
const missing = await client.rpc("missing_probe", { left_name: "a" });
|
|
assert.match(missing.error?.message ?? "", /not found/);
|
|
calls.length = 0;
|
|
await client.rpc("missing_probe", { left_name: "a" });
|
|
assert.equal(calls.filter((call) => tag(call.sql) === "pg_proc").length, 1);
|
|
});
|
|
});
|
|
|
|
test("closing the pool drops that url from the metadata cache", async () => {
|
|
await withSwitches({
|
|
JYOTISHA_PG_METADATA_CACHE: "on",
|
|
JYOTISHA_PG_MERGED_SESSION_SETUP: "off",
|
|
}, async () => {
|
|
const url = "postgresql://roundtrip-close@127.0.0.1:1/jyotisha";
|
|
const calls = installFakePool(url);
|
|
const client = clientFor(url);
|
|
await client.from("profiles").select("id");
|
|
await closeLocalPostgresDataPool(url);
|
|
const reopenedCalls = installFakePool(url);
|
|
const reopened = clientFor(url);
|
|
await reopened.from("profiles").select("id");
|
|
assert.equal(reopenedCalls.filter((call) => tag(call.sql) === "information_schema").length, 1);
|
|
assert.ok(calls.length > 0);
|
|
});
|
|
});
|