fix: various
This commit is contained in:
@ -6,6 +6,7 @@ export abstract class BackgroundLoader<T extends { loaded: number; created: numb
|
|||||||
#system: SystemInterface;
|
#system: SystemInterface;
|
||||||
readonly cache: CachedTable<T>;
|
readonly cache: CachedTable<T>;
|
||||||
#log = debug(this.name());
|
#log = debug(this.name());
|
||||||
|
#blacklist = new Set();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of pubkeys to fetch metadata for
|
* List of pubkeys to fetch metadata for
|
||||||
@ -43,11 +44,6 @@ export abstract class BackgroundLoader<T extends { loaded: number; created: numb
|
|||||||
*/
|
*/
|
||||||
protected abstract buildSub(missing: Array<string>): RequestBuilder;
|
protected abstract buildSub(missing: Array<string>): RequestBuilder;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a placeholder value when no data can be found
|
|
||||||
*/
|
|
||||||
protected abstract makePlaceholder(key: string): T | undefined;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start requesting a set of keys to be loaded
|
* Start requesting a set of keys to be loaded
|
||||||
*/
|
*/
|
||||||
@ -92,7 +88,7 @@ export abstract class BackgroundLoader<T extends { loaded: number; created: numb
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #FetchMetadata() {
|
async #FetchMetadata() {
|
||||||
const loading = [...this.#wantsKeys];
|
const loading = [...this.#wantsKeys].filter(a => !this.#blacklist.has(a));
|
||||||
await this.cache.buffer(loading);
|
await this.cache.buffer(loading);
|
||||||
|
|
||||||
const missing = loading.filter(a => (this.cache.getFromCache(a)?.loaded ?? 0) < this.getExpireCutoff());
|
const missing = loading.filter(a => (this.cache.getFromCache(a)?.loaded ?? 0) < this.getExpireCutoff());
|
||||||
@ -100,12 +96,9 @@ export abstract class BackgroundLoader<T extends { loaded: number; created: numb
|
|||||||
this.#log("Fetching keys: %O", missing);
|
this.#log("Fetching keys: %O", missing);
|
||||||
try {
|
try {
|
||||||
const found = await this.#loadData(missing);
|
const found = await this.#loadData(missing);
|
||||||
const noResult = removeUndefined(
|
const noResult = removeUndefined(missing.filter(a => !found.some(b => a === this.cache.key(b))));
|
||||||
missing.filter(a => !found.some(b => a === this.cache.key(b))).map(a => this.makePlaceholder(a)),
|
|
||||||
);
|
|
||||||
if (noResult.length > 0) {
|
if (noResult.length > 0) {
|
||||||
this.#log("Adding placeholders for %O", noResult);
|
noResult.forEach(a => this.#blacklist.add(a));
|
||||||
await Promise.all(noResult.map(a => this.cache.update(a)));
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.#log("Error: %O", e);
|
this.#log("Error: %O", e);
|
||||||
@ -122,19 +115,11 @@ export abstract class BackgroundLoader<T extends { loaded: number; created: numb
|
|||||||
await Promise.all(results.map(a => this.cache.update(a)));
|
await Promise.all(results.map(a => this.cache.update(a)));
|
||||||
return results;
|
return results;
|
||||||
} else {
|
} else {
|
||||||
const hookHandled = new Set<string>();
|
const v = await this.#system.Fetch(this.buildSub(missing));
|
||||||
const v = await this.#system.Fetch(this.buildSub(missing), async e => {
|
|
||||||
this.#log("Callback handled %o", e);
|
|
||||||
for (const pe of e) {
|
|
||||||
const m = this.onEvent(pe);
|
|
||||||
if (m) {
|
|
||||||
await this.cache.update(m);
|
|
||||||
hookHandled.add(pe.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.#log("Got data", v);
|
this.#log("Got data", v);
|
||||||
return removeUndefined(v.map(this.onEvent));
|
const results = removeUndefined(v.map(this.onEvent));
|
||||||
|
await Promise.all(results.map(a => this.cache.update(a)));
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,9 +121,8 @@ export class DefaultConnectionPool<T extends ConnectionType = Connection>
|
|||||||
if (builder) {
|
if (builder) {
|
||||||
this.#connectionBuilder = builder;
|
this.#connectionBuilder = builder;
|
||||||
} else {
|
} else {
|
||||||
this.#connectionBuilder = async (addr, options, ephemeral) => {
|
this.#connectionBuilder = (addr, options, ephemeral) => {
|
||||||
const c = new Connection(addr, options, ephemeral);
|
return Promise.resolve<T>(new Connection(addr, options, ephemeral) as unknown as T);
|
||||||
return c as unknown as T;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,6 +166,10 @@ export class DefaultConnectionPool<T extends ConnectionType = Connection>
|
|||||||
if (existing.ephemeral && !ephemeral) {
|
if (existing.ephemeral && !ephemeral) {
|
||||||
existing.ephemeral = ephemeral;
|
existing.ephemeral = ephemeral;
|
||||||
}
|
}
|
||||||
|
// re-open if closed
|
||||||
|
if (existing.ephemeral && !existing.isOpen) {
|
||||||
|
await existing.connect();
|
||||||
|
}
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -120,6 +120,9 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
|
|||||||
const wasReconnect = this.Socket !== null;
|
const wasReconnect = this.Socket !== null;
|
||||||
if (this.Socket) {
|
if (this.Socket) {
|
||||||
this.id = uuid();
|
this.id = uuid();
|
||||||
|
if (this.isOpen) {
|
||||||
|
this.Socket.close();
|
||||||
|
}
|
||||||
this.Socket.onopen = null;
|
this.Socket.onopen = null;
|
||||||
this.Socket.onmessage = null;
|
this.Socket.onmessage = null;
|
||||||
this.Socket.onerror = null;
|
this.Socket.onerror = null;
|
||||||
@ -133,10 +136,8 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
|
|||||||
this.Socket.onclose = e => this.#onClose(e);
|
this.Socket.onclose = e => this.#onClose(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(final = true) {
|
close() {
|
||||||
if (final) {
|
this.#closing = true;
|
||||||
this.#closing = true;
|
|
||||||
}
|
|
||||||
this.Socket?.close();
|
this.Socket?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,14 +327,10 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
|
|||||||
* @param cmd The REQ to send to the server
|
* @param cmd The REQ to send to the server
|
||||||
*/
|
*/
|
||||||
request(cmd: ReqCommand | SyncCommand, cbSent?: () => void) {
|
request(cmd: ReqCommand | SyncCommand, cbSent?: () => void) {
|
||||||
const requestKinds = dedupe(
|
const filters = (cmd[0] === "REQ" ? cmd.slice(2) : cmd.slice(3)) as Array<ReqFilter>;
|
||||||
cmd
|
const requestKinds = new Set(filters.flatMap(a => a.kinds ?? []));
|
||||||
.slice(2)
|
|
||||||
.map(a => (a as ReqFilter).kinds ?? [])
|
|
||||||
.flat(),
|
|
||||||
);
|
|
||||||
const ExpectAuth = [EventKind.DirectMessage, EventKind.GiftWrap];
|
const ExpectAuth = [EventKind.DirectMessage, EventKind.GiftWrap];
|
||||||
if (ExpectAuth.some(a => requestKinds.includes(a)) && !this.#expectAuth) {
|
if (ExpectAuth.some(a => requestKinds.has(a)) && !this.#expectAuth) {
|
||||||
this.#expectAuth = true;
|
this.#expectAuth = true;
|
||||||
this.#log("Setting expectAuth flag %o", requestKinds);
|
this.#log("Setting expectAuth flag %o", requestKinds);
|
||||||
}
|
}
|
||||||
@ -518,7 +515,7 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
get #maxSubscriptions() {
|
get #maxSubscriptions() {
|
||||||
return this.info?.limitation?.max_subscriptions ?? 25;
|
return this.info?.limitation?.max_subscriptions ?? 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
#setupEphemeral() {
|
#setupEphemeral() {
|
||||||
@ -529,7 +526,7 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
|
|||||||
if (this.ephemeral) {
|
if (this.ephemeral) {
|
||||||
this.#ephemeralCheck = setInterval(() => {
|
this.#ephemeralCheck = setInterval(() => {
|
||||||
const lastActivity = unixNowMs() - this.#activity;
|
const lastActivity = unixNowMs() - this.#activity;
|
||||||
if (lastActivity > 30_000 && !this.#closing) {
|
if (lastActivity > 10_000 && !this.#closing) {
|
||||||
if (this.#activeRequests.size > 0) {
|
if (this.#activeRequests.size > 0) {
|
||||||
this.#log(
|
this.#log(
|
||||||
"Inactive connection has %d active requests! %O",
|
"Inactive connection has %d active requests! %O",
|
||||||
@ -537,7 +534,7 @@ export class Connection extends EventEmitter<ConnectionTypeEvents> implements Co
|
|||||||
this.#activeRequests,
|
this.#activeRequests,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.close(false);
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 5_000);
|
}, 5_000);
|
||||||
|
@ -21,13 +21,16 @@ export type EventFetcher = {
|
|||||||
|
|
||||||
export function parseRelayTag(tag: Array<string>) {
|
export function parseRelayTag(tag: Array<string>) {
|
||||||
if (tag[0] !== "r") return;
|
if (tag[0] !== "r") return;
|
||||||
return {
|
const url = sanitizeRelayUrl(tag[1]);
|
||||||
url: sanitizeRelayUrl(tag[1]),
|
if (url) {
|
||||||
settings: {
|
return {
|
||||||
read: tag[2] === "read" || tag[2] === undefined,
|
url,
|
||||||
write: tag[2] === "write" || tag[2] === undefined,
|
settings: {
|
||||||
},
|
read: tag[2] === "read" || tag[2] === undefined,
|
||||||
} as FullRelaySettings;
|
write: tag[2] === "write" || tag[2] === undefined,
|
||||||
|
},
|
||||||
|
} as FullRelaySettings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseRelayTags(tag: Array<Array<string>>) {
|
export function parseRelayTags(tag: Array<Array<string>>) {
|
||||||
@ -39,15 +42,19 @@ export function parseRelaysFromKind(ev: NostrEvent) {
|
|||||||
const relaysInContent =
|
const relaysInContent =
|
||||||
ev.content.length > 0 ? (JSON.parse(ev.content) as Record<string, { read: boolean; write: boolean }>) : undefined;
|
ev.content.length > 0 ? (JSON.parse(ev.content) as Record<string, { read: boolean; write: boolean }>) : undefined;
|
||||||
if (relaysInContent) {
|
if (relaysInContent) {
|
||||||
return Object.entries(relaysInContent).map(
|
return removeUndefined(
|
||||||
([k, v]) =>
|
Object.entries(relaysInContent).map(([k, v]) => {
|
||||||
({
|
const url = sanitizeRelayUrl(k);
|
||||||
url: sanitizeRelayUrl(k),
|
if (url) {
|
||||||
settings: {
|
return {
|
||||||
read: v.read,
|
url,
|
||||||
write: v.write,
|
settings: {
|
||||||
},
|
read: v.read,
|
||||||
}) as FullRelaySettings,
|
write: v.write,
|
||||||
|
},
|
||||||
|
} as FullRelaySettings;
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (ev.kind === EventKind.Relays) {
|
} else if (ev.kind === EventKind.Relays) {
|
||||||
|
@ -34,13 +34,4 @@ export class RelayMetadataLoader extends BackgroundLoader<UsersRelays> {
|
|||||||
rb.withFilter().authors(missing).kinds([EventKind.Relays, EventKind.ContactList]);
|
rb.withFilter().authors(missing).kinds([EventKind.Relays, EventKind.ContactList]);
|
||||||
return rb;
|
return rb;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override makePlaceholder(key: string): UsersRelays | undefined {
|
|
||||||
return {
|
|
||||||
relays: [],
|
|
||||||
pubkey: key,
|
|
||||||
created: 0,
|
|
||||||
loaded: this.getExpireCutoff() + 300000,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,4 @@ export class ProfileLoaderService extends BackgroundLoader<CachedMetadata> {
|
|||||||
sub.withFilter().kinds([EventKind.SetMetadata]).authors(missing).relay(MetadataRelays);
|
sub.withFilter().kinds([EventKind.SetMetadata]).authors(missing).relay(MetadataRelays);
|
||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override makePlaceholder(key: string): CachedMetadata | undefined {
|
|
||||||
return {
|
|
||||||
pubkey: key,
|
|
||||||
loaded: unixNowMs() - ProfileCacheExpire + 30_000,
|
|
||||||
created: 0,
|
|
||||||
} as CachedMetadata;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ export class QueryManager extends EventEmitter<QueryManagerEvents> {
|
|||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
qSend.syncFrom = data as Array<TaggedNostrEvent>;
|
qSend.syncFrom = data as Array<TaggedNostrEvent>;
|
||||||
this.#log("Adding from cache: %O", data);
|
this.#log("Adding from cache: %O", data);
|
||||||
q.feed.add(data as Array<TaggedNostrEvent>);
|
q.feed.add(data.map(a => ({ ...a, relays: [] })));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,26 +128,16 @@ export class QueryManager extends EventEmitter<QueryManagerEvents> {
|
|||||||
|
|
||||||
if (qSend.relay) {
|
if (qSend.relay) {
|
||||||
this.#log("Sending query to %s %s %O", qSend.relay, q.id, qSend);
|
this.#log("Sending query to %s %s %O", qSend.relay, q.id, qSend);
|
||||||
const s = this.#system.pool.getConnection(qSend.relay);
|
const nc = await this.#system.pool.connect(qSend.relay, { read: true, write: true }, true);
|
||||||
if (s) {
|
if (nc) {
|
||||||
const qt = q.sendToRelay(s, qSend);
|
const qt = q.sendToRelay(nc, qSend);
|
||||||
if (qt) {
|
if (qt) {
|
||||||
return [qt];
|
return [qt];
|
||||||
} else {
|
} else {
|
||||||
this.#log("Query not sent to %s: %O", qSend.relay, qSend);
|
this.#log("Query not sent to %s: %O", qSend.relay, qSend);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const nc = await this.#system.pool.connect(qSend.relay, { read: true, write: true }, true);
|
console.warn("Failed to connect to new relay for:", qSend.relay, q);
|
||||||
if (nc) {
|
|
||||||
const qt = q.sendToRelay(nc, qSend);
|
|
||||||
if (qt) {
|
|
||||||
return [qt];
|
|
||||||
} else {
|
|
||||||
this.#log("Query not sent to %s: %O", qSend.relay, qSend);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.warn("Failed to connect to new relay for:", qSend.relay, q);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const ret = [];
|
const ret = [];
|
||||||
|
@ -11,7 +11,6 @@ import { LRUCache } from "lru-cache";
|
|||||||
import { ConnectionType } from "./connection-pool";
|
import { ConnectionType } from "./connection-pool";
|
||||||
|
|
||||||
interface QueryTraceEvents {
|
interface QueryTraceEvents {
|
||||||
change: () => void;
|
|
||||||
close: (id: string) => void;
|
close: (id: string) => void;
|
||||||
eose: (id: string, connId: string, wasForced: boolean) => void;
|
eose: (id: string, connId: string, wasForced: boolean) => void;
|
||||||
}
|
}
|
||||||
@ -39,13 +38,11 @@ export class QueryTrace extends EventEmitter<QueryTraceEvents> {
|
|||||||
|
|
||||||
sentToRelay() {
|
sentToRelay() {
|
||||||
this.sent = unixNowMs();
|
this.sent = unixNowMs();
|
||||||
this.emit("change");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gotEose() {
|
gotEose() {
|
||||||
this.eose = unixNowMs();
|
this.eose = unixNowMs();
|
||||||
this.emit("eose", this.id, this.connId, false);
|
this.emit("eose", this.id, this.connId, false);
|
||||||
this.emit("change");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
forceEose() {
|
forceEose() {
|
||||||
@ -59,7 +56,6 @@ export class QueryTrace extends EventEmitter<QueryTraceEvents> {
|
|||||||
sendClose() {
|
sendClose() {
|
||||||
this.close = unixNowMs();
|
this.close = unixNowMs();
|
||||||
this.emit("close", this.id);
|
this.emit("close", this.id);
|
||||||
this.emit("change");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -354,14 +350,6 @@ export class Query extends EventEmitter<QueryEvents> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#onProgress() {
|
|
||||||
const isFinished = this.progress === 1;
|
|
||||||
if (isFinished) {
|
|
||||||
this.#log("%s loading=%s, progress=%d, traces=%O", this.id, !isFinished, this.progress, this.#tracing);
|
|
||||||
this.emit("done");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#stopCheckTraces() {
|
#stopCheckTraces() {
|
||||||
if (this.#checkTrace) {
|
if (this.#checkTrace) {
|
||||||
clearInterval(this.#checkTrace);
|
clearInterval(this.#checkTrace);
|
||||||
@ -411,16 +399,18 @@ export class Query extends EventEmitter<QueryEvents> {
|
|||||||
let filters = q.filters;
|
let filters = q.filters;
|
||||||
const qt = new QueryTrace(c.address, filters, c.id);
|
const qt = new QueryTrace(c.address, filters, c.id);
|
||||||
qt.on("close", x => c.closeRequest(x));
|
qt.on("close", x => c.closeRequest(x));
|
||||||
qt.on("change", () => this.#onProgress());
|
qt.on("eose", (id, connId, forced) => {
|
||||||
qt.on("eose", (id, connId, forced) =>
|
|
||||||
this.emit("trace", {
|
this.emit("trace", {
|
||||||
id,
|
id,
|
||||||
conn: c,
|
conn: c,
|
||||||
wasForced: forced,
|
wasForced: forced,
|
||||||
queued: qt.queued,
|
queued: qt.queued,
|
||||||
responseTime: qt.responseTime,
|
responseTime: qt.responseTime,
|
||||||
} as TraceReport),
|
} as TraceReport);
|
||||||
);
|
if (this.progress === 1) {
|
||||||
|
this.emit("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
const eventHandler = (sub: string, ev: TaggedNostrEvent) => {
|
const eventHandler = (sub: string, ev: TaggedNostrEvent) => {
|
||||||
if (this.request.options?.fillStore ?? true) {
|
if (this.request.options?.fillStore ?? true) {
|
||||||
this.handleEvent(sub, ev);
|
this.handleEvent(sub, ev);
|
||||||
|
Reference in New Issue
Block a user