fix(nostr): normal shutdown no longer reports relay errors (#111905)

This commit is contained in:
mushuiyu886
2026-07-23 05:59:12 +08:00
committed by GitHub
parent 1650faf6a1
commit e8f0d3198d
2 changed files with 39 additions and 5 deletions

View File

@@ -6,25 +6,36 @@ type RelayHandlers = Parameters<SimplePool["subscribeMany"]>[2];
function createHarness() {
const handlers: RelayHandlers[] = [];
const abortController = new AbortController();
const subscribeMany = vi.fn(
(_relays: string[], _filter: unknown, nextHandlers: RelayHandlers) => {
handlers.push(nextHandlers);
return { close: vi.fn() };
nextHandlers.abort?.addEventListener(
"abort",
() => nextHandlers.onclose?.([String(nextHandlers.abort?.reason ?? "aborted")]),
{ once: true },
);
return {
close: vi.fn(async (reason?: string) => {
nextHandlers.onclose?.([reason ?? "closed by caller"]);
}),
};
},
);
const onBackfillComplete = vi.fn<(relays: string[]) => void>();
const onClose = vi.fn<(relay: string, reasons: string[]) => void>();
const group = createNostrRelaySubscriptionGroup({
pool: { subscribeMany } as unknown as SimplePool,
relays: ["wss://one.example", "wss://two.example"],
filter: { kinds: [4] },
abort: new AbortController().signal,
abort: abortController.signal,
onEvent: (_event: Event) => {},
onBackfillComplete,
onClose: () => {},
onClose,
eoseConfirmDeadlineMs: 10,
});
group.start();
return { group, handlers, onBackfillComplete, subscribeMany };
return { abortController, group, handlers, onBackfillComplete, onClose, subscribeMany };
}
describe("Nostr relay subscriptions", () => {
@@ -66,4 +77,22 @@ describe("Nostr relay subscriptions", () => {
expect(onBackfillComplete).not.toHaveBeenCalled();
await group.close("test complete");
});
it("does not report locally requested closes as relay failures", async () => {
const { abortController, group, onClose } = createHarness();
abortController.abort("closed by caller");
await group.close("closed by caller");
expect(onClose).not.toHaveBeenCalled();
});
it("still reports closes initiated by the relay", async () => {
const { group, handlers, onClose } = createHarness();
handlers[0]?.onclose?.(["relay unavailable"]);
expect(onClose).toHaveBeenCalledWith("wss://one.example", ["relay unavailable"]);
await group.close("test complete");
});
});

View File

@@ -18,6 +18,7 @@ export function createNostrRelaySubscriptionGroup(options: {
const relays = [...new Set(options.relays)];
const subscriptions: Array<ReturnType<SimplePool["subscribeMany"]>> = [];
const deadlineTimers = new Set<ReturnType<typeof setTimeout>>();
let locallyClosing = false;
const backfillStatus = new Map<string, BackfillStatus>(
relays.map((relay): [string, BackfillStatus] => [relay, "pending"]),
);
@@ -74,7 +75,9 @@ export function createNostrRelaySubscriptionGroup(options: {
clearTimeout(deadlineTimer);
deadlineTimers.delete(deadlineTimer);
settleBackfill(relay, "incomplete");
options.onClose(relay, reasons);
if (!locallyClosing && !options.abort.aborted) {
options.onClose(relay, reasons);
}
},
// Own earlier deadline marks synthetic library EOSE as incomplete.
maxWait: confirmDeadlineMs + LIBRARY_EOSE_TIMEOUT_MARGIN_MS,
@@ -84,6 +87,8 @@ export function createNostrRelaySubscriptionGroup(options: {
}
},
close: async (reason: string): Promise<void> => {
// nostr-tools reports caller-requested closes through the same callback as relay failures.
locallyClosing = true;
clearDeadlines();
await Promise.all(subscriptions.map(async (subscription) => subscription.close(reason)));
},