Skip relays that fail to connect when rendering on the server
This commit is contained in:
parent
018c72021f
commit
9ab145f816
1 changed files with 48 additions and 13 deletions
|
|
@ -12,15 +12,20 @@ import type { Query } from "$lib/forum/query";
|
||||||
// cached briefly to absorb crawler bursts.
|
// cached briefly to absorb crawler bursts.
|
||||||
const QUERY_TIMEOUT = 2500;
|
const QUERY_TIMEOUT = 2500;
|
||||||
const PROFILE_TIMEOUT = 1500;
|
const PROFILE_TIMEOUT = 1500;
|
||||||
|
const CONNECT_TIMEOUT = 1500;
|
||||||
const CACHE_TTL = 30_000;
|
const CACHE_TTL = 30_000;
|
||||||
|
const DEAD_RELAY_TTL = 5 * 60_000;
|
||||||
const CACHE_MAX = 2000;
|
const CACHE_MAX = 2000;
|
||||||
|
|
||||||
const pool = new SimplePool();
|
const pool = new SimplePool();
|
||||||
const cache = new Map<string, { at: number; result: Promise<Event[]> }>();
|
const cache = new Map<string, { at: number; result: Promise<Event[]> }>();
|
||||||
|
// Relays that failed to connect are skipped for a while, so a dead profile
|
||||||
|
// relay doesn't add its connect timeout to every cold page.
|
||||||
|
const deadUntil = new Map<string, number>();
|
||||||
|
|
||||||
// Resolve at EOSE from every relay or at the deadline, whichever comes first,
|
// Ask every reachable relay and resolve as soon as each has answered (EOSE or
|
||||||
// with whatever arrived: a stalled relay must not cost the events the others
|
// closed), or at the deadline, whichever comes first — always with whatever
|
||||||
// already delivered.
|
// arrived, so one stalled relay never costs the events the others delivered.
|
||||||
function boundedQuery(
|
function boundedQuery(
|
||||||
relays: string[],
|
relays: string[],
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
|
|
@ -28,24 +33,54 @@ function boundedQuery(
|
||||||
): Promise<Event[]> {
|
): Promise<Event[]> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const events: Event[] = [];
|
const events: Event[] = [];
|
||||||
|
const subs: { close(): void }[] = [];
|
||||||
let done = false;
|
let done = false;
|
||||||
let sub: { close(): void } | null = null;
|
let pending = 0;
|
||||||
const finish = () => {
|
const finish = () => {
|
||||||
if (done) return;
|
if (done) return;
|
||||||
done = true;
|
done = true;
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
sub?.close();
|
for (const s of subs) {
|
||||||
|
try {
|
||||||
|
s.close();
|
||||||
|
} catch {
|
||||||
|
// Already closed
|
||||||
|
}
|
||||||
|
}
|
||||||
resolve(events);
|
resolve(events);
|
||||||
};
|
};
|
||||||
const timer = setTimeout(finish, maxWait);
|
const timer = setTimeout(finish, maxWait);
|
||||||
try {
|
|
||||||
sub = pool.subscribeMany(relays, filter, {
|
const now = Date.now();
|
||||||
onevent: (e) => events.push(e),
|
const live = relays.filter((url) => (deadUntil.get(url) ?? 0) <= now);
|
||||||
oneose: finish,
|
pending = live.length;
|
||||||
});
|
if (pending === 0) return finish();
|
||||||
if (done) sub.close(); // EOSE fired synchronously
|
|
||||||
} catch {
|
for (const url of live) {
|
||||||
finish();
|
let settled = false;
|
||||||
|
const settle = () => {
|
||||||
|
if (settled) return;
|
||||||
|
settled = true;
|
||||||
|
if (--pending <= 0) finish();
|
||||||
|
};
|
||||||
|
pool.ensureRelay(url, { connectionTimeout: CONNECT_TIMEOUT }).then(
|
||||||
|
(relay) => {
|
||||||
|
if (done) return settle();
|
||||||
|
const sub = relay.subscribe([filter], {
|
||||||
|
onevent: (e) => events.push(e),
|
||||||
|
oneose: () => {
|
||||||
|
sub.close();
|
||||||
|
settle();
|
||||||
|
},
|
||||||
|
onclose: settle,
|
||||||
|
});
|
||||||
|
subs.push(sub);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
deadUntil.set(url, Date.now() + DEAD_RELAY_TTL);
|
||||||
|
settle();
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue