mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-30 01:12:39 +00:00
Optimize admin list publish acceptance
This commit is contained in:
1 parent
a0f70b9bc0
commit
7703dd13ae
2 files changed
+158
-55
No files matched your search
+82
-49
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user