Optimize admin list publish acceptance

This commit is contained in:
thePR0M3TH3AN committed 2025-10-20 13:05:36 -04:00
1 parent a0f70b9bc0
commit 7703dd13ae
2 files changed
+158 -55

No files matched your search

+82 -49
View File
@@ -558,53 +558,90 @@ function publishListWithFirstAcceptance(pool, relays, event, options = {}) {
: [];
const resultsBuffer = new Array(relayPromises.length);
let resolved = false;
let remaining = relayPromises.length;
const resolveMessage = listKey
? `Failed to publish ${listKey} list to any relay.`
: "Failed to publish admin list to any relay.";
const firstAcceptance = new Promise((resolve, reject) => {
if (!relayPromises.length) {
reject(
createError(
"publish-failed",
listKey
? `Failed to publish ${listKey} list to any relay.`
: "Failed to publish admin list to any relay.",
)
);
return;
const normalizeFailureError = (value) => {
if (value instanceof Error) {
return value;
}
relayPromises.forEach((promise, index) => {
promise
.then((result) => {
resultsBuffer[index] = result;
if (result?.success && !resolved) {
resolved = true;
resolve(result);
}
})
.catch((error) => {
resultsBuffer[index] = {
url: Array.isArray(relays) ? relays[index] : "",
success: false,
error,
};
})
.finally(() => {
remaining -= 1;
if (!resolved && remaining === 0) {
reject(
createError(
"publish-failed",
listKey
? `Failed to publish ${listKey} list to any relay.`
: "Failed to publish admin list to any relay.",
)
);
}
});
if (value && typeof value === "object") {
if (value.error instanceof Error) {
return value.error;
}
if (typeof value.error === "string" && value.error.trim()) {
return new Error(value.error.trim());
}
}
if (typeof value === "string" && value.trim()) {
return new Error(value.trim());
}
if (value && typeof value === "object" && typeof value.reason === "string") {
const trimmed = value.reason.trim();
if (trimmed) {
return new Error(trimmed);
}
}
return new Error("publish failed");
};
const normalizeResult = (result, fallbackUrl = "") => {
if (result && typeof result === "object") {
const url = typeof result.url === "string" ? result.url : fallbackUrl;
const success = !!result.success;
const error = success ? null : normalizeFailureError(result);
return { url, success, error };
}
return {
url: fallbackUrl,
success: false,
error: normalizeFailureError(result),
};
};
const acceptancePromises = relayPromises.map((promise, index) =>
promise
.then((result) => {
const normalized = normalizeResult(
result,
Array.isArray(relays) ? relays[index] : "",
);
resultsBuffer[index] = normalized;
if (normalized.success) {
return normalized;
}
throw normalized;
})
.catch((reason) => {
const normalized = normalizeResult(
reason,
Array.isArray(relays) ? relays[index] : "",
);
if (typeof resultsBuffer[index] === "undefined") {
resultsBuffer[index] = normalized;
}
throw normalized;
})
);
const firstAcceptance = (() => {
if (!relayPromises.length) {
return Promise.reject(createError("publish-failed", resolveMessage));
}
return Promise.any(acceptancePromises).catch((aggregateError) => {
const error = createError("publish-failed", resolveMessage, aggregateError);
throw error;
});
});
})();
const allResults = Promise.allSettled(relayPromises).then((entries) => {
for (let index = 0; index < entries.length; index += 1) {
@@ -612,12 +649,8 @@ function publishListWithFirstAcceptance(pool, relays, event, options = {}) {
const fallbackUrl = Array.isArray(relays) ? relays[index] : "";
const result =
entry?.status === "fulfilled"
? entry.value
: {
url: fallbackUrl,
success: false,
error: entry?.reason || new Error("publish failed"),
};
? normalizeResult(entry.value, fallbackUrl)
: normalizeResult(entry?.reason, fallbackUrl);
if (typeof resultsBuffer[index] === "undefined") {
resultsBuffer[index] = result;
+76 -6
View File
@@ -132,6 +132,7 @@ const relays = [
"wss://relay2.example.com",
];
nostrClient.relays = [...relays];
nostrClient.writeRelays = [...relays];
let publishBehaviors = [];
let publishCallIndex = 0;
@@ -155,14 +156,30 @@ nostrClient.pool = {
}
return {
on(eventName, handler) {
const schedule = (fn) => {
const delay =
typeof behavior.delayMs === "number" && behavior.delayMs >= 0
? behavior.delayMs
: 0;
if (delay > 0) {
setTimeout(fn, delay);
} else {
queueMicrotask(fn);
}
};
if (eventName === "ok" && behavior.success) {
handler();
schedule(() => {
handler();
});
return true;
}
if (eventName === "failed" && !behavior.success) {
handler(
behavior.error || new Error(`relay failure for ${url}`),
);
schedule(() => {
handler(
behavior.error || new Error(`relay failure for ${url}`),
);
});
return true;
}
return false;
@@ -226,6 +243,59 @@ assert.equal(
"should attempt to publish removals to every relay",
);
nostrClient.writeRelays = [relays[1]];
const writerelayBehaviors = [
{ url: relays[1], success: true },
];
setPublishBehaviors(writerelayBehaviors);
await assert.doesNotReject(
() =>
persistAdminState(ADMIN_SUPER_NPUB, {
whitelist: ["npub1writerelaymember"],
}),
"should publish whitelist updates to configured write relays",
);
assert.equal(
publishCallIndex,
writerelayBehaviors.length,
"should target only write relays when available",
);
nostrClient.writeRelays = [];
const slowRelayDelayMs = 75;
const fastAcceptanceBehaviors = [
{ url: relays[0], success: true },
{
url: relays[1],
success: false,
error: new Error("relay2 slow failure"),
delayMs: slowRelayDelayMs,
},
];
setPublishBehaviors(fastAcceptanceBehaviors);
const startTime = Date.now();
await assert.doesNotReject(
() =>
persistAdminState(ADMIN_SUPER_NPUB, {
whitelist: ["npub1fastacceptance"],
}),
"should resolve whitelist updates after the first relay acceptance",
);
const durationMs = Date.now() - startTime;
assert.ok(
durationMs < slowRelayDelayMs,
`should not wait for slower relay failures (duration ${durationMs}ms >= ${slowRelayDelayMs}ms)`,
);
await new Promise((resolve) => setTimeout(resolve, slowRelayDelayMs + 20));
const relayFailureWarnings = capturedWarnings.filter((entry) => {
const [, message] = entry;
return (
@@ -247,8 +317,8 @@ const blacklistFailureCount = failureMessages.filter((message) =>
assert.equal(
relayFailureWarnings.length,
4,
"should warn once per relay failure across additions and removals",
5,
"should warn for each relay failure across scenarios",
);
assert.ok(
whitelistFailureCount >= 2,