feat: @snort/system CacheRelay
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { NostrEvent, ReqCommand, WorkerMessage, WorkerMessageCommand } from "./types";
|
||||
import { NostrEvent, OkResponse, ReqCommand, ReqFilter, WorkerMessage, WorkerMessageCommand } from "./types";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
export class WorkerRelayInterface {
|
||||
@ -18,35 +18,31 @@ export class WorkerRelayInterface {
|
||||
}
|
||||
|
||||
async init(path: string) {
|
||||
return (await this.#workerRpc<string, boolean>("init", path)).result;
|
||||
return await this.#workerRpc<string, boolean>("init", path);
|
||||
}
|
||||
|
||||
async event(ev: NostrEvent) {
|
||||
return (await this.#workerRpc<NostrEvent, boolean>("event", ev)).result;
|
||||
return await this.#workerRpc<NostrEvent, OkResponse>("event", ev);
|
||||
}
|
||||
|
||||
async req(req: ReqCommand) {
|
||||
async query(req: ReqCommand) {
|
||||
return await this.#workerRpc<ReqCommand, Array<NostrEvent>>("req", req);
|
||||
}
|
||||
|
||||
async count(req: ReqCommand) {
|
||||
return (await this.#workerRpc<ReqCommand, number>("count", req)).result;
|
||||
return await this.#workerRpc<ReqCommand, number>("count", req);
|
||||
}
|
||||
|
||||
async summary() {
|
||||
return (await this.#workerRpc<void, Record<string, number>>("summary")).result;
|
||||
return await this.#workerRpc<void, Record<string, number>>("summary");
|
||||
}
|
||||
|
||||
async close(id: string) {
|
||||
return (await this.#workerRpc<string, boolean>("close", id)).result;
|
||||
return await this.#workerRpc<string, boolean>("close", id);
|
||||
}
|
||||
|
||||
async dump() {
|
||||
return (await this.#workerRpc<void, Uint8Array>("dumpDb")).result;
|
||||
}
|
||||
|
||||
async sql(sql: string, params: Array<string | number>) {
|
||||
return (await this.#workerRpc<object, Array<Array<any>>>("sql", { sql, params })).result;
|
||||
return await this.#workerRpc<void, Uint8Array>("dumpDb");
|
||||
}
|
||||
|
||||
#workerRpc<T, R>(cmd: WorkerMessageCommand, args?: T) {
|
||||
@ -57,16 +53,10 @@ export class WorkerRelayInterface {
|
||||
args,
|
||||
} as WorkerMessage<T>;
|
||||
this.#worker.postMessage(msg);
|
||||
return new Promise<{
|
||||
result: R;
|
||||
port: MessagePort | undefined;
|
||||
}>(resolve => {
|
||||
return new Promise<R>(resolve => {
|
||||
this.#commandQueue.set(id, (v, port) => {
|
||||
const cmdReply = v as WorkerMessage<R>;
|
||||
resolve({
|
||||
result: cmdReply.args,
|
||||
port: port.length > 0 ? port[0] : undefined,
|
||||
});
|
||||
resolve(cmdReply.args);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -69,7 +69,11 @@ export class InMemoryRelay extends EventEmitter<RelayHandlerEvents> implements R
|
||||
const ret = [];
|
||||
for (const [, e] of this.#events) {
|
||||
if (eventMatchesFilter(e, filter)) {
|
||||
ret.push(e);
|
||||
if (filter.ids_only === true) {
|
||||
ret.push(e.id);
|
||||
} else {
|
||||
ret.push(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
@ -184,7 +184,13 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
|
||||
|
||||
const [sql, params] = this.#buildQuery(req);
|
||||
const res = this.#db?.selectArrays(sql, params);
|
||||
const results = res?.map(a => JSON.parse(a[0] as string) as NostrEvent) ?? [];
|
||||
const results =
|
||||
res?.map(a => {
|
||||
if (req.ids_only === true) {
|
||||
return a[0] as string;
|
||||
}
|
||||
return JSON.parse(a[0] as string) as NostrEvent;
|
||||
}) ?? [];
|
||||
const time = unixNowMs() - start;
|
||||
this.#log(`Query ${id} results took ${time.toLocaleString()}ms`);
|
||||
return results;
|
||||
@ -245,7 +251,13 @@ export class SqliteRelay extends EventEmitter<RelayHandlerEvents> implements Rel
|
||||
const conditions: Array<string> = [];
|
||||
const params: Array<any> = [];
|
||||
|
||||
let sql = `select ${count ? "count(json)" : "json"} from events`;
|
||||
let resultType = "json";
|
||||
if (count) {
|
||||
resultType = "count(json)";
|
||||
} else if (req.ids_only === true) {
|
||||
resultType = "id";
|
||||
}
|
||||
let sql = `select ${resultType} from events`;
|
||||
const tags = Object.entries(req).filter(([k]) => k.startsWith("#"));
|
||||
for (const [key, values] of tags) {
|
||||
const vArray = values as Array<string>;
|
||||
|
@ -9,7 +9,7 @@ export type WorkerMessageCommand =
|
||||
| "summary"
|
||||
| "close"
|
||||
| "dumpDb"
|
||||
| "sql";
|
||||
| "emit-event";
|
||||
|
||||
export interface WorkerMessage<T> {
|
||||
id: string;
|
||||
@ -27,11 +27,7 @@ export interface NostrEvent {
|
||||
sig: string;
|
||||
}
|
||||
|
||||
export interface ReqCommand {
|
||||
id: string;
|
||||
filters: Array<ReqFilter>;
|
||||
leaveOpen?: boolean;
|
||||
}
|
||||
export type ReqCommand = ["REQ", id: string, ...filters: Array<ReqFilter>];
|
||||
|
||||
export interface ReqFilter {
|
||||
ids?: string[];
|
||||
@ -41,8 +37,16 @@ export interface ReqFilter {
|
||||
since?: number;
|
||||
until?: number;
|
||||
limit?: number;
|
||||
not?: ReqFilter;
|
||||
[key: string]: Array<string> | Array<number> | string | number | undefined | ReqFilter;
|
||||
ids_only?: boolean;
|
||||
[key: string]: Array<string> | Array<number> | string | number | undefined | boolean;
|
||||
}
|
||||
|
||||
export interface OkResponse {
|
||||
ok: boolean;
|
||||
id: string;
|
||||
relay: string;
|
||||
message?: string;
|
||||
event: NostrEvent;
|
||||
}
|
||||
|
||||
export interface RelayHandler extends EventEmitter<RelayHandlerEvents> {
|
||||
@ -55,7 +59,7 @@ export interface RelayHandler extends EventEmitter<RelayHandlerEvents> {
|
||||
* Run any SQL command
|
||||
*/
|
||||
sql(sql: string, params: Array<string | number>): Array<Array<string | number>>;
|
||||
req(id: string, req: ReqFilter): Array<NostrEvent>;
|
||||
req(id: string, req: ReqFilter): Array<NostrEvent | string>;
|
||||
count(req: ReqFilter): number;
|
||||
summary(): Record<string, number>;
|
||||
dump(): Promise<Uint8Array>;
|
||||
|
@ -15,15 +15,12 @@ const ActiveSubscriptions = new Map<string, PortedFilter>();
|
||||
|
||||
let relay: RelayHandler | undefined;
|
||||
|
||||
async function reply<T>(id: string, obj?: T, transferables?: Transferable[]) {
|
||||
globalThis.postMessage(
|
||||
{
|
||||
id,
|
||||
cmd: "reply",
|
||||
args: obj,
|
||||
} as WorkerMessage<T>,
|
||||
transferables ?? [],
|
||||
);
|
||||
async function reply<T>(id: string, obj?: T) {
|
||||
globalThis.postMessage({
|
||||
id,
|
||||
cmd: "reply",
|
||||
args: obj,
|
||||
} as WorkerMessage<T>);
|
||||
}
|
||||
|
||||
// Event inserter queue
|
||||
@ -108,25 +105,18 @@ globalThis.onmessage = async ev => {
|
||||
break;
|
||||
}
|
||||
case "close": {
|
||||
ActiveSubscriptions.delete(msg.args as string);
|
||||
reply(msg.id, true);
|
||||
break;
|
||||
}
|
||||
case "req": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const req = msg.args as ReqCommand;
|
||||
const chan = new MessageChannel();
|
||||
if (req.leaveOpen) {
|
||||
ActiveSubscriptions.set(req.id, {
|
||||
filters: req.filters,
|
||||
port: chan.port1,
|
||||
});
|
||||
}
|
||||
const filters = req.slice(2) as Array<ReqFilter>;
|
||||
const results = [];
|
||||
for (const r of req.filters) {
|
||||
results.push(...relay!.req(req.id, r as ReqFilter));
|
||||
for (const r of filters) {
|
||||
results.push(...relay!.req(req[1], r));
|
||||
}
|
||||
reply(msg.id, results, req.leaveOpen ? [chan.port2] : undefined);
|
||||
reply(msg.id, results);
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -134,8 +124,9 @@ globalThis.onmessage = async ev => {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const req = msg.args as ReqCommand;
|
||||
let results = 0;
|
||||
for (const r of req.filters) {
|
||||
const c = relay!.count(r as ReqFilter);
|
||||
const filters = req.slice(2) as Array<ReqFilter>;
|
||||
for (const r of filters) {
|
||||
const c = relay!.count(r);
|
||||
results += c;
|
||||
}
|
||||
reply(msg.id, results);
|
||||
@ -156,17 +147,6 @@ globalThis.onmessage = async ev => {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "sql": {
|
||||
await barrierQueue(cmdQueue, async () => {
|
||||
const req = msg.args as {
|
||||
sql: string;
|
||||
params: Array<any>;
|
||||
};
|
||||
const res = relay!.sql(req.sql, req.params);
|
||||
reply(msg.id, res);
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
reply(msg.id, { error: "Unknown command" });
|
||||
break;
|
||||
|
Reference in New Issue
Block a user