fix query close bug
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Kieran 2023-06-08 06:27:27 +01:00
parent 8e6a1ecbc2
commit 2b80109e3b
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
6 changed files with 33 additions and 44 deletions

View File

@ -30,10 +30,10 @@ function Queries() {
return total; return total;
} }
function queryInfo(q: { id: string; filters: Array<ReqFilter>; closing: boolean; subFilters: Array<ReqFilter> }) { function queryInfo(q: { id: string; filters: Array<ReqFilter>; subFilters: Array<ReqFilter> }) {
return ( return (
<div key={q.id}> <div key={q.id}>
{q.closing ? <s>{q.id}</s> : <>{q.id}</>} {q.id}
<br /> <br />
<span onClick={() => copy(JSON.stringify(q.filters))} className="pointer"> <span onClick={() => copy(JSON.stringify(q.filters))} className="pointer">
&nbsp; Filters: {q.filters.length} ({countElements(q.filters)} elements) &nbsp; Filters: {q.filters.length} ({countElements(q.filters)} elements)

View File

@ -12,6 +12,7 @@ const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TSto
if (rb) { if (rb) {
const q = System.Query<TStore>(type, rb); const q = System.Query<TStore>(type, rb);
const release = q.feed.hook(onChanged); const release = q.feed.hook(onChanged);
q.uncancel();
return () => { return () => {
q.cancel(); q.cancel();
release(); release();

View File

@ -122,7 +122,9 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
Query<T extends NoteStore>(type: { new (): T }, req: RequestBuilder): Query { Query<T extends NoteStore>(type: { new (): T }, req: RequestBuilder): Query {
const existing = this.Queries.get(req.id); const existing = this.Queries.get(req.id);
if (existing) { if (existing) {
const filters = req.buildDiff(this.#relayCache, existing.filters); const filters = !req.options?.skipDiff
? req.buildDiff(this.#relayCache, existing.filters)
: req.build(this.#relayCache);
if (filters.length === 0 && !!req.options?.skipDiff) { if (filters.length === 0 && !!req.options?.skipDiff) {
return existing; return existing;
} else { } else {
@ -138,11 +140,7 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
const store = new type(); const store = new type();
const filters = req.build(this.#relayCache); const filters = req.build(this.#relayCache);
const q = new Query(req.id, store); const q = new Query(req.id, store, req.options?.leaveOpen);
if (req.options?.leaveOpen) {
q.leaveOpen = req.options.leaveOpen;
}
this.Queries.set(req.id, q); this.Queries.set(req.id, q);
for (const subQ of filters) { for (const subQ of filters) {
this.SendQuery(q, subQ).then(qta => this.SendQuery(q, subQ).then(qta =>
@ -222,7 +220,6 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
return { return {
id: a.id, id: a.id,
filters: a.filters, filters: a.filters,
closing: a.closing,
subFilters: [], subFilters: [],
}; };
}), }),
@ -230,12 +227,12 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
} }
#cleanup() { #cleanup() {
const now = unixNowMs();
let changed = false; let changed = false;
for (const [k, v] of this.Queries) { for (const [k, v] of this.Queries) {
if (v.closingAt && v.closingAt < now) { if (v.canRemove()) {
v.sendClose(); v.sendClose();
this.Queries.delete(k); this.Queries.delete(k);
this.#log("Deleted query %s", k);
changed = true; changed = true;
} }
} }

View File

@ -120,12 +120,12 @@ export class Query implements QueryBase {
/** /**
* Leave the query open until its removed * Leave the query open until its removed
*/ */
leaveOpen = false; #leaveOpen = false;
/** /**
* Time when this query can be removed * Time when this query can be removed
*/ */
#cancelTimeout?: number; #cancelAt?: number;
/** /**
* Timer used to track tracing status * Timer used to track tracing status
@ -140,18 +140,15 @@ export class Query implements QueryBase {
#log = debug("Query"); #log = debug("Query");
#allFilters: Array<ReqFilter> = []; #allFilters: Array<ReqFilter> = [];
constructor(id: string, feed: NoteStore) { constructor(id: string, feed: NoteStore, leaveOpen?: boolean) {
this.id = id; this.id = id;
this.#feed = feed; this.#feed = feed;
this.#leaveOpen = leaveOpen ?? false;
this.#checkTraces(); this.#checkTraces();
} }
get closing() { canRemove() {
return this.#cancelTimeout !== undefined; return this.#cancelAt !== undefined && this.#cancelAt < unixNowMs();
}
get closingAt() {
return this.#cancelTimeout;
} }
/** /**
@ -174,8 +171,15 @@ export class Query implements QueryBase {
} }
} }
/**
* This function should be called when this Query object and FeedStore is no longer needed
*/
cancel() { cancel() {
this.#cancelTimeout = unixNowMs() + 5_000; this.#cancelAt = unixNowMs() + 5_000;
}
uncancel() {
this.#cancelAt = undefined;
} }
cleanup() { cleanup() {
@ -203,7 +207,7 @@ export class Query implements QueryBase {
eose(sub: string, conn: Readonly<Connection>) { eose(sub: string, conn: Readonly<Connection>) {
const qt = this.#tracing.find(a => a.id === sub && a.connId === conn.Id); const qt = this.#tracing.find(a => a.id === sub && a.connId === conn.Id);
qt?.gotEose(); qt?.gotEose();
if (!this.leaveOpen) { if (!this.#leaveOpen) {
qt?.sendClose(); qt?.sendClose();
} }
} }
@ -272,6 +276,7 @@ export class Query implements QueryBase {
c.QueueReq(["REQ", qt.id, ...q.filters], () => qt.sentToRelay()); c.QueueReq(["REQ", qt.id, ...q.filters], () => qt.sentToRelay());
return qt; return qt;
} }
#reComputeFilters() { #reComputeFilters() {
console.time("reComputeFilters"); console.time("reComputeFilters");
this.#allFilters = flatMerge(this.#tracing.flatMap(a => a.filters).flatMap(expandFilter)); this.#allFilters = flatMerge(this.#tracing.flatMap(a => a.filters).flatMap(expandFilter));

View File

@ -7,7 +7,7 @@ import { distance } from "./Util";
*/ */
const DiscriminatorKeys = ["since", "until", "limit", "search"]; const DiscriminatorKeys = ["since", "until", "limit", "search"];
export function canMergeFilters(a: FlatReqFilter, b: FlatReqFilter): boolean { export function canMergeFilters(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | ReqFilter): boolean {
const aObj = a as Record<string, string | number | undefined>; const aObj = a as Record<string, string | number | undefined>;
const bObj = b as Record<string, string | number | undefined>; const bObj = b as Record<string, string | number | undefined>;
for (const key of DiscriminatorKeys) { for (const key of DiscriminatorKeys) {
@ -17,34 +17,21 @@ export function canMergeFilters(a: FlatReqFilter, b: FlatReqFilter): boolean {
} }
} }
} }
const keys1 = Object.keys(aObj); return distance(aObj, bObj) <= 1;
const keys2 = Object.keys(bObj);
const maxKeys = keys1.length > keys2.length ? keys1 : keys2;
let distance = 0;
for (const key of maxKeys) {
if (key in aObj && key in bObj) {
if (aObj[key] !== bObj[key]) {
distance++;
}
} else {
return false;
}
}
return distance <= 1;
} }
export function mergeSimilar(filters: Array<ReqFilter>): Array<ReqFilter> { export function mergeSimilar(filters: Array<ReqFilter>): Array<ReqFilter> {
console.time("mergeSimilar"); console.time("mergeSimilar");
const ret = []; const ret = [];
while (filters.length > 0) { const fCopy = [...filters];
const current = filters.shift()!; while (fCopy.length > 0) {
const current = fCopy.shift()!;
const mergeSet = [current]; const mergeSet = [current];
for (let i = 0; i < filters.length; i++) { for (let i = 0; i < fCopy.length; i++) {
const f = filters[i]; const f = fCopy[i];
if (mergeSet.every(v => canMergeFilters(v, f))) { if (mergeSet.every(v => canMergeFilters(v, f))) {
mergeSet.push(filters.splice(i, 1)[0]); mergeSet.push(fCopy.splice(i, 1)[0]);
i--; i--;
} }
} }

View File

@ -38,7 +38,6 @@ export interface SystemSnapshot {
id: string; id: string;
filters: Array<ReqFilter>; filters: Array<ReqFilter>;
subFilters: Array<ReqFilter>; subFilters: Array<ReqFilter>;
closing: boolean;
}>; }>;
} }