Migrate config service to ref-backed Effect runtime

This commit is contained in:
elpresidank 2026-06-02 00:40:44 -05:00
parent b4ee2b691f
commit 88db18fbda
4 changed files with 907 additions and 755 deletions

View file

@ -1,7 +1,9 @@
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Effect, SynchronizedRef } from "effect";
import { describe, expect, it } from "vitest";
import { topics } from "@trustgraph/base";
import {
ConfigServiceError,
makeConfigService,
@ -16,9 +18,15 @@ import type {
} from "@trustgraph/base";
class NoopPubSub implements PubSubBackend {
async createProducer<T>(_options: CreateProducerOptions): Promise<BackendProducer<T>> {
readonly sentByTopic = new Map<string, Array<unknown>>();
async createProducer<T>(options: CreateProducerOptions<T>): Promise<BackendProducer<T>> {
return {
send: async () => undefined,
send: async (message) => {
const sent = this.sentByTopic.get(options.topic) ?? [];
sent.push(message);
this.sentByTopic.set(options.topic, sent);
},
flush: async () => undefined,
close: async () => undefined,
};
@ -48,10 +56,12 @@ const makeService = (persistPath?: string) =>
describe("ConfigService operations", () => {
it("uses tagged errors for invalid mutations", async () => {
const service = makeService();
const putRequest: ConfigRequest = { operation: "put" };
const deleteRequest: ConfigRequest = { operation: "delete" };
const putError = await service.handlePut({ operation: "put" } as ConfigRequest)
const putError = await service.handlePut(putRequest)
.catch((caught: unknown) => caught);
const deleteError = await service.handleDelete({ operation: "delete" } as ConfigRequest)
const deleteError = await service.handleDelete(deleteRequest)
.catch((caught: unknown) => caught);
expect(putError).toBeInstanceOf(ConfigServiceError);
@ -64,13 +74,14 @@ describe("ConfigService operations", () => {
const dir = await mkdtemp(join(tmpdir(), "trustgraph-config-service-"));
const persistPath = join(dir, "config.json");
const service = makeService(persistPath);
await service.handlePut({
const putRequest: ConfigRequest = {
operation: "put",
values: [
{ workspace: "alpha", type: "prompt", key: "system", value: "hello" },
],
} as ConfigRequest);
};
await service.handlePut(putRequest);
const persisted = await Bun.file(persistPath).json();
await rm(dir, { recursive: true, force: true });
@ -97,10 +108,11 @@ describe("ConfigService operations", () => {
const service = makeService(persistPath);
await service.loadFromDisk();
const response = service.handleGet({
const getRequest: ConfigRequest = {
operation: "get",
keys: ["prompt", "system"],
} as ConfigRequest);
};
const response = service.handleGet(getRequest);
await rm(dir, { recursive: true, force: true });
expect(response).toEqual({
@ -110,4 +122,58 @@ describe("ConfigService operations", () => {
},
});
});
it("serializes concurrent mutations through ref-backed state", async () => {
const service = makeService();
const requests: Array<ConfigRequest> = [
{ operation: "put", values: [{ type: "prompt", key: "a", value: "one" }] },
{ operation: "put", values: [{ type: "prompt", key: "b", value: "two" }] },
{ operation: "put", values: [{ workspace: "beta", type: "prompt", key: "c", value: "three" }] },
];
await Promise.all(requests.map((request) => service.handlePut(request)));
expect(service.handleGet({ operation: "get", keys: ["prompt"] })).toEqual({
version: 3,
values: {
a: "one",
b: "two",
},
});
expect(service.handleGetValuesAllWorkspaces({ operation: "getvalues-all-ws", keys: ["prompt"] }).values).toEqual([
{ workspace: "default", type: "prompt", key: "a", value: "one" },
{ workspace: "default", type: "prompt", key: "b", value: "two" },
{ workspace: "beta", type: "prompt", key: "c", value: "three" },
]);
});
it("pushes config from the stored producer handle", async () => {
const backend = new NoopPubSub();
const service = makeConfigService({
id: "config-test",
manageProcessSignals: false,
pubsub: backend,
});
const pushProducer = await backend.createProducer<{
readonly version: number;
readonly config: Record<string, unknown>;
}>({ topic: topics.configPush });
await Effect.runPromise(
SynchronizedRef.update(service.state, (state) => ({
...state,
pushProducer,
})),
);
await service.pushConfig();
await service.handlePut({
operation: "put",
values: [{ type: "prompt", key: "system", value: "hello" }],
});
expect(backend.sentByTopic.get(topics.configPush)).toEqual([
{ version: 0, config: {} },
{ version: 1, config: { prompt: { system: "hello" } } },
]);
});
});

File diff suppressed because it is too large Load diff