1
0
forked from Kieran/snort
snort/packages/worker-relay/src/worker.ts

143 lines
3.9 KiB
TypeScript
Raw Normal View History

2024-01-15 16:57:20 +00:00
/// <reference lib="webworker" />
2024-01-19 11:56:07 +00:00
import { InMemoryRelay } from "./memory-relay";
2024-01-18 11:17:57 +00:00
import { WorkQueueItem, barrierQueue, processWorkQueue } from "./queue";
2024-01-19 19:55:48 +00:00
import { SqliteRelay } from "./sqlite-relay";
2024-01-30 20:32:43 +00:00
import { NostrEvent, RelayHandler, ReqCommand, ReqFilter, WorkerMessage, unixNowMs } from "./types";
2024-01-18 12:26:47 +00:00
2024-01-19 11:56:07 +00:00
let relay: RelayHandler | undefined;
2024-01-15 16:57:20 +00:00
2024-01-23 15:35:28 +00:00
async function reply<T>(id: string, obj?: T) {
globalThis.postMessage({
id,
cmd: "reply",
args: obj,
} as WorkerMessage<T>);
2024-01-15 16:57:20 +00:00
}
2024-01-17 15:47:01 +00:00
// Event inserter queue
let eventWriteQueue: Array<NostrEvent> = [];
async function insertBatch() {
// Only insert event batches when the command queue is empty
// This is to make req's execute first and not block them
if (eventWriteQueue.length > 0 && cmdQueue.length === 0) {
await barrierQueue(cmdQueue, async () => {
2024-01-19 19:55:48 +00:00
const start = unixNowMs();
const timeLimit = 1000;
if (relay) {
2024-01-19 19:55:48 +00:00
while (eventWriteQueue.length > 0) {
if (unixNowMs() - start >= timeLimit) {
2024-01-26 11:47:08 +00:00
//console.debug("Yield insert, queue length: ", eventWriteQueue.length, ", cmds: ", cmdQueue.length);
2024-01-19 19:55:48 +00:00
break;
}
const batch = eventWriteQueue.splice(0, 10);
eventWriteQueue = eventWriteQueue.slice(batch.length);
relay.eventBatch(batch);
}
}
});
2024-01-17 15:47:01 +00:00
}
setTimeout(() => insertBatch(), 100);
}
setTimeout(() => insertBatch(), 100);
2024-01-18 12:26:47 +00:00
const cmdQueue: Array<WorkQueueItem> = [];
2024-01-18 11:17:57 +00:00
processWorkQueue(cmdQueue, 50);
2024-01-19 11:56:07 +00:00
async function tryOpfs() {
try {
await navigator.storage.getDirectory();
return true;
} catch {
// ignore
}
return false;
}
2024-01-17 15:47:01 +00:00
globalThis.onclose = () => {
2024-01-19 11:56:07 +00:00
relay?.close();
2024-01-17 15:47:01 +00:00
};
2024-01-19 11:56:07 +00:00
globalThis.onmessage = async ev => {
2024-01-15 16:57:20 +00:00
const msg = ev.data as WorkerMessage<any>;
2024-01-17 15:47:01 +00:00
try {
switch (msg.cmd) {
case "init": {
2024-01-19 11:56:07 +00:00
await barrierQueue(cmdQueue, async () => {
if ("WebAssembly" in globalThis && (await tryOpfs())) {
2024-01-19 19:55:48 +00:00
relay = new SqliteRelay();
2024-01-19 11:56:07 +00:00
} else {
relay = new InMemoryRelay();
}
await relay.init(msg.args as string);
2024-01-18 11:17:57 +00:00
reply(msg.id, true);
});
2024-01-17 15:47:01 +00:00
break;
}
case "event": {
eventWriteQueue.push(msg.args as NostrEvent);
reply(msg.id, true);
break;
}
2024-01-18 12:26:47 +00:00
case "close": {
reply(msg.id, true);
break;
}
2024-01-17 15:47:01 +00:00
case "req": {
2024-01-19 11:56:07 +00:00
await barrierQueue(cmdQueue, async () => {
2024-01-18 11:17:57 +00:00
const req = msg.args as ReqCommand;
2024-01-23 15:35:28 +00:00
const filters = req.slice(2) as Array<ReqFilter>;
2024-01-25 15:21:42 +00:00
const results: Array<string | NostrEvent> = [];
const ids = new Set<string>();
2024-01-23 15:35:28 +00:00
for (const r of filters) {
2024-01-25 15:21:42 +00:00
const rx = relay!.req(req[1], r);
for (const x of rx) {
if ((typeof x === "string" && ids.has(x)) || ids.has((x as NostrEvent).id)) {
continue;
}
ids.add(typeof x === "string" ? x : (x as NostrEvent).id);
results.push(x);
}
2024-01-18 11:17:57 +00:00
}
2024-01-23 15:35:28 +00:00
reply(msg.id, results);
2024-01-18 11:17:57 +00:00
});
2024-01-17 15:47:01 +00:00
break;
}
case "count": {
2024-01-19 11:56:07 +00:00
await barrierQueue(cmdQueue, async () => {
2024-01-18 11:17:57 +00:00
const req = msg.args as ReqCommand;
let results = 0;
2024-01-23 15:35:28 +00:00
const filters = req.slice(2) as Array<ReqFilter>;
for (const r of filters) {
const c = relay!.count(r);
2024-01-18 11:17:57 +00:00
results += c;
}
reply(msg.id, results);
});
2024-01-17 15:47:01 +00:00
break;
}
case "summary": {
2024-01-19 11:56:07 +00:00
await barrierQueue(cmdQueue, async () => {
const res = relay!.summary();
2024-01-18 11:17:57 +00:00
reply(msg.id, res);
});
2024-01-17 15:47:01 +00:00
break;
}
case "dumpDb": {
2024-01-19 11:56:07 +00:00
await barrierQueue(cmdQueue, async () => {
const res = await relay!.dump();
reply(msg.id, res);
});
break;
}
2024-01-17 15:47:01 +00:00
default: {
reply(msg.id, { error: "Unknown command" });
break;
}
2024-01-15 16:57:20 +00:00
}
2024-01-17 15:47:01 +00:00
} catch (e) {
console.error(e);
2024-01-17 15:47:01 +00:00
reply(msg.id, { error: e });
2024-01-15 16:57:20 +00:00
}
};