refactor: reactions grouping and other fixes

This commit is contained in:
2024-01-09 16:40:31 +00:00
parent 4455651d47
commit 80fa5a132b
58 changed files with 344 additions and 501 deletions

View File

@ -4,7 +4,7 @@ import EventEmitter from "eventemitter3";
import { unixNowMs, unwrap } from "@snort/shared";
import { Connection, ReqFilter, Nips, TaggedNostrEvent, SystemInterface } from ".";
import { NoteCollection, NoteStore } from "./note-collection";
import { NoteCollection } from "./note-collection";
import { BuiltRawReqFilter, RequestBuilder } from "./request-builder";
import { eventMatchesFilter } from "./request-matcher";
@ -97,25 +97,26 @@ export interface TraceReport {
responseTime: number;
}
interface QueryEvents {
export interface QueryEvents {
loading: (v: boolean) => void;
trace: (report: TraceReport) => void;
filters: (req: BuiltRawReqFilter) => void;
event: (evs: ReadonlyArray<TaggedNostrEvent>) => void;
event: (evs: Array<TaggedNostrEvent>) => void;
end: () => void;
}
/**
* Active or queued query on the system
*/
export class Query extends EventEmitter<QueryEvents> {
/**
* Unique id of this query
*/
readonly id: string;
get id() {
return this.request.id;
}
/**
* RequestBuilder instance
*/
requests: Array<RequestBuilder> = [];
request: RequestBuilder;
/**
* Nostr system interface
@ -166,8 +167,7 @@ export class Query extends EventEmitter<QueryEvents> {
constructor(system: SystemInterface, req: RequestBuilder) {
super();
this.id = uuid();
this.requests.push(req);
this.request = req;
this.#system = system;
this.#feed = new NoteCollection();
this.#leaveOpen = req.options?.leaveOpen ?? false;
@ -176,32 +176,21 @@ export class Query extends EventEmitter<QueryEvents> {
this.#checkTraces();
this.feed.on("event", evs => this.emit("event", evs));
this.#start();
}
/**
* Adds another request to this one
*/
addRequest(req: RequestBuilder) {
if (this.#groupTimeout) {
clearTimeout(this.#groupTimeout);
this.#groupTimeout = undefined;
}
if (this.requests.some(a => a.instance === req.instance)) {
// already exists, nothing to add
return false;
}
if (this.requests.some(a => a.options?.skipDiff !== req.options?.skipDiff)) {
throw new Error("Mixing skipDiff option is not supported");
}
this.requests.push(req);
if (this.#groupingDelay) {
this.#groupTimeout = setTimeout(() => {
this.#emitFilters();
}, this.#groupingDelay);
} else {
this.#emitFilters();
if (req.instance === this.request.instance) {
// same requst, do nothing
this.#log("Same query %O === %O", req, this.request);
return;
}
this.#log("Add query %O to %s", req, this.id);
this.request.add(req);
this.#start();
return true;
}
@ -228,12 +217,11 @@ export class Query extends EventEmitter<QueryEvents> {
return this.#feed.snapshot;
}
handleEvent(sub: string, e: TaggedNostrEvent) {
#handleEvent(sub: string, e: TaggedNostrEvent) {
for (const t of this.#tracing) {
if (t.id === sub || sub === "*") {
if (t.filters.some(v => eventMatchesFilter(e, v))) {
this.feed.add(e);
return t;
} else {
this.#log("Event did not match filter, rejecting %O %O", e, t);
}
@ -254,7 +242,12 @@ export class Query extends EventEmitter<QueryEvents> {
}
cleanup() {
if (this.#groupTimeout) {
clearTimeout(this.#groupTimeout);
this.#groupTimeout = undefined;
}
this.#stopCheckTraces();
this.emit("end");
}
/**
@ -316,35 +309,46 @@ export class Query extends EventEmitter<QueryEvents> {
return thisProgress;
}
#emitFilters() {
if (this.requests.every(a => !!a.options?.skipDiff)) {
const existing = this.filters;
const rb = new RequestBuilder(this.id);
this.requests.forEach(a => rb.add(a));
const filters = rb.buildDiff(this.#system, existing);
filters.forEach(f => this.emit("filters", f));
this.requests = [];
#start() {
if (this.#groupTimeout) {
clearTimeout(this.#groupTimeout);
this.#groupTimeout = undefined;
}
if (this.#groupingDelay) {
this.#groupTimeout = setTimeout(() => {
this.#emitFilters();
}, this.#groupingDelay);
} else {
// send without diff
const rb = new RequestBuilder(this.id);
this.requests.forEach(a => rb.add(a));
const filters = rb.build(this.#system);
this.#emitFilters();
}
}
#emitFilters() {
this.#log("Starting emit of %s", this.id);
const existing = this.filters;
if (!(this.request.options?.skipDiff ?? false) && existing.length > 0) {
const filters = this.request.buildDiff(this.#system, existing);
this.#log("Build %s %O", this.id, filters);
filters.forEach(f => this.emit("filters", f));
} else {
const filters = this.request.build(this.#system);
this.#log("Build %s %O", this.id, filters);
filters.forEach(f => this.emit("filters", f));
this.requests = [];
}
}
#onProgress() {
const isFinished = this.progress === 1;
if (this.feed.loading !== isFinished) {
this.#log("%s loading=%s, progress=%d, traces=%O", this.id, this.feed.loading, this.progress, this.#tracing);
this.feed.loading = isFinished;
if (isFinished) {
this.#log("%s loading=%s, progress=%d, traces=%O", this.id, !isFinished, this.progress, this.#tracing);
this.emit("loading", !isFinished);
}
}
#stopCheckTraces() {
if (this.#checkTrace) {
clearInterval(this.#checkTrace);
this.#checkTrace = undefined;
}
}
@ -398,6 +402,9 @@ export class Query extends EventEmitter<QueryEvents> {
responseTime: qt.responseTime,
} as TraceReport),
);
const handler = (sub: string, ev: TaggedNostrEvent) => this.#handleEvent(sub, ev);
c.on("event", handler);
this.on("end", () => c.off("event", handler));
this.#tracing.push(qt);
c.QueueReq(["REQ", qt.id, ...qt.filters], () => qt.sentToRelay());
return qt;