Compare commits

...

2 Commits

Author SHA1 Message Date
Guido D'Orsi
11f1a9d5ba docs: add docs about worker storage 2025-01-30 13:05:53 +01:00
Guido D'Orsi
91265d62dd feat: add storage peer option to worker 2025-01-30 13:03:32 +01:00
6 changed files with 81 additions and 8 deletions

View File

@@ -0,0 +1,5 @@
---
"jazz-nodejs": patch
---
Add storage peer option

View File

@@ -55,6 +55,22 @@ const { worker } = await startWorker({
- load/subscribe to CoValues: `MyCoValue.subscribe(id, worker, {...})`
- create CoValues & Groups `const val = MyCoValue.create({...}, { owner: worker })`
To make the worker state survive between restarts even on spotty connections, you can pass a `storage` parameter to `startWorker`:
<CodeGroup>
{/* prettier-ignore */}
```ts
import { startWorker } from 'jazz-nodejs';
import { SQLiteStorage } from 'cojson-storage-sqlite';
const { worker } = await startWorker({
AccountSchema: MyWorkerAccount,
syncServer: 'wss://cloud.jazz.tools/?key=you@example.com',
storage: await SQLiteStorage.asPeer({ filename: dbPath }),
});
```
</CodeGroup>
## Using CoValues instead of requests
Just like traditional backend functions, you can use Server Workers to do useful stuff (computations, calls to third-party APIs etc.) and put the results back into CoValues, which subscribed clients automatically get notified about.

View File

@@ -14,6 +14,7 @@
},
"devDependencies": {
"@types/ws": "8.5.10",
"cojson-storage-sqlite": "workspace:*",
"jazz-run": "workspace:*",
"typescript": "~5.6.2"
},

View File

@@ -1,4 +1,4 @@
import { AgentSecret, LocalNode, WasmCrypto } from "cojson";
import { AgentSecret, LocalNode, Peer, WasmCrypto } from "cojson";
import {
Account,
AccountClass,
@@ -14,6 +14,7 @@ type WorkerOptions<Acc extends Account> = {
accountID?: string;
accountSecret?: string;
syncServer?: string;
storage?: Peer;
AccountSchema?: AccountClass<Acc>;
};
@@ -46,6 +47,12 @@ export async function startWorker<Acc extends Account>(
throw new Error("Invalid accountSecret");
}
const peersToLoadFrom = [wsPeer.peer];
if (options.storage) {
peersToLoadFrom.push(options.storage);
}
const context = await createJazzContext({
auth: fixedCredentialsAuth({
accountID: accountID as ID<Acc>,
@@ -54,7 +61,7 @@ export async function startWorker<Acc extends Account>(
AccountSchema,
// TODO: locked sessions similar to browser
sessionProvider: randomSessionProvider,
peersToLoadFrom: [wsPeer.peer],
peersToLoadFrom,
crypto: await WasmCrypto.create(),
});
@@ -69,7 +76,6 @@ export async function startWorker<Acc extends Account>(
async function done() {
await context.account.waitForAllCoValuesSync();
wsPeer.done();
context.done();
}

View File

@@ -1,13 +1,15 @@
import { randomUUID } from "crypto";
import { tmpdir } from "os";
import { join } from "path";
import { SQLiteStorage } from "cojson-storage-sqlite";
import { createWorkerAccount } from "jazz-run/createWorkerAccount";
import { startSyncServer } from "jazz-run/startSyncServer";
import { CoMap, Group, InboxSender, co } from "jazz-tools";
import { CoMap, Group, InboxSender, Peer, co } from "jazz-tools";
import { describe, expect, onTestFinished, test } from "vitest";
import { startWorker } from "../index";
async function setup() {
const { server, port } = await setupSyncServer();
const syncServer = `ws://localhost:${port}`;
const { server, port, syncServer } = await setupSyncServer();
const { worker, done } = await setupWorker(syncServer);
@@ -27,7 +29,9 @@ async function setupSyncServer(defaultPort = "0") {
server.close();
});
return { server, port };
const syncServer = `ws://localhost:${port}`;
return { server, port, syncServer };
}
async function setupWorker(syncServer: string) {
@@ -180,4 +184,42 @@ describe("startWorker integration", () => {
await worker2.done();
newServer.close();
});
test("can use a persistent storage", async () => {
// Create a temporary database file
const dbPath = join(tmpdir(), `test-${randomUUID()}.db`);
const { syncServer, server, port } = await setupSyncServer();
const { accountID, agentSecret } = await createWorkerAccount({
name: "test-worker",
peer: syncServer,
});
const { worker, done } = await startWorker({
accountID: accountID,
accountSecret: agentSecret,
syncServer,
storage: await SQLiteStorage.asPeer({ filename: dbPath }),
});
const map = TestMap.create({ value: "test" }, { owner: worker });
await done();
server.close();
const { worker: worker2, done: done2 } = await startWorker({
accountID: accountID,
accountSecret: agentSecret,
syncServer,
storage: await SQLiteStorage.asPeer({ filename: dbPath }),
});
const mapOnWorker2 = await TestMap.load(map.id, worker2, {});
expect(mapOnWorker2?.value).toBe("test");
await setupSyncServer(port); // Starting a new sync server on the same port so the final waitForSync will work
await done2();
});
});

3
pnpm-lock.yaml generated
View File

@@ -1691,6 +1691,9 @@ importers:
'@types/ws':
specifier: 8.5.10
version: 8.5.10
cojson-storage-sqlite:
specifier: workspace:*
version: link:../cojson-storage-sqlite
jazz-run:
specifier: workspace:*
version: link:../jazz-run