|
|
|
|
@@ -6210,8 +6210,14 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
const publishSessionId = `pub_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
|
|
|
const publishedSoFar = new Set();
|
|
|
|
|
const failedSoFar = new Set(); // explicit rejections (auth-required, blocked, etc.)
|
|
|
|
|
const timedOutSoFar = new Set(); // timeouts — NOT skip-marked (could be slow connect)
|
|
|
|
|
const timedOutSoFar = new Set(); // timeouts
|
|
|
|
|
const totalTarget = isBroadcast ? allBroadcastUrls.length : 0;
|
|
|
|
|
// Track current batch for progress display. Updated by the batch loop.
|
|
|
|
|
let currentBatch = 0;
|
|
|
|
|
let totalBatches = 0;
|
|
|
|
|
// Debug: track publish start time for timing analysis (defined in outer
|
|
|
|
|
// scope so the batch loop and silent-failure detection can access it).
|
|
|
|
|
const publishStartTime = Date.now();
|
|
|
|
|
|
|
|
|
|
if (isBroadcast && totalTarget > 0) {
|
|
|
|
|
console.log(`[Worker] Broadcasting to ${allBroadcastUrls.length} relays ` +
|
|
|
|
|
@@ -6233,8 +6239,10 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
ndkEvent.on('relay:published', (relay) => {
|
|
|
|
|
const url = relay?.url || String(relay || '');
|
|
|
|
|
if (!url) return;
|
|
|
|
|
const elapsed = ((Date.now() - publishStartTime) / 1000).toFixed(1);
|
|
|
|
|
publishedSoFar.add(url);
|
|
|
|
|
trackRelayWrite(url);
|
|
|
|
|
console.log(`[Worker][broadcast] ✅ ${url} published (${elapsed}s) — batch ${currentBatch}/${totalBatches}, ${publishedSoFar.size}/${totalTarget} total`);
|
|
|
|
|
broadcast({
|
|
|
|
|
type: 'broadcastProgress',
|
|
|
|
|
sessionId: publishSessionId,
|
|
|
|
|
@@ -6243,6 +6251,8 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
successful: publishedSoFar.size,
|
|
|
|
|
failed: failedSoFar.size + timedOutSoFar.size,
|
|
|
|
|
latestRelay: url,
|
|
|
|
|
batchCurrent: currentBatch,
|
|
|
|
|
batchTotal: totalBatches,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@@ -6250,11 +6260,14 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
const url = relay?.url || String(relay || '');
|
|
|
|
|
if (!url) return;
|
|
|
|
|
const errMsg = err?.message || String(err) || '';
|
|
|
|
|
const elapsed = ((Date.now() - publishStartTime) / 1000).toFixed(1);
|
|
|
|
|
const isTimeout = /timeout/i.test(errMsg);
|
|
|
|
|
if (isTimeout) {
|
|
|
|
|
timedOutSoFar.add(url);
|
|
|
|
|
console.log(`[Worker][broadcast] ⏱️ ${url} TIMEOUT (${elapsed}s) — batch ${currentBatch}/${totalBatches}`);
|
|
|
|
|
} else {
|
|
|
|
|
failedSoFar.add(url);
|
|
|
|
|
console.log(`[Worker][broadcast] ❌ ${url} REJECTED (${elapsed}s): ${errMsg} — batch ${currentBatch}/${totalBatches}`);
|
|
|
|
|
}
|
|
|
|
|
broadcast({
|
|
|
|
|
type: 'broadcastProgress',
|
|
|
|
|
@@ -6265,6 +6278,8 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
failed: failedSoFar.size + timedOutSoFar.size,
|
|
|
|
|
latestRelay: url,
|
|
|
|
|
latestError: errMsg,
|
|
|
|
|
batchCurrent: currentBatch,
|
|
|
|
|
batchTotal: totalBatches,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@@ -6322,10 +6337,27 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
for (let i = 0; i < broadcastOnlyUrls.length; i += BATCH_SIZE) {
|
|
|
|
|
batches.push(broadcastOnlyUrls.slice(i, i + BATCH_SIZE));
|
|
|
|
|
}
|
|
|
|
|
console.log(`[Worker] Phase 2: Broadcasting to ${broadcastOnlyUrls.length} relays in ${batches.length} batches of ${BATCH_SIZE}`);
|
|
|
|
|
totalBatches = batches.length;
|
|
|
|
|
console.log(`[Worker] Phase 2: Broadcasting to ${broadcastOnlyUrls.length} relays in ${totalBatches} batches of ${BATCH_SIZE}`);
|
|
|
|
|
|
|
|
|
|
// Emit a progress event showing batch info before starting batches.
|
|
|
|
|
broadcast({
|
|
|
|
|
type: 'broadcastProgress',
|
|
|
|
|
sessionId: publishSessionId,
|
|
|
|
|
phase: 'progress',
|
|
|
|
|
total: totalTarget,
|
|
|
|
|
successful: publishedSoFar.size,
|
|
|
|
|
failed: failedSoFar.size + timedOutSoFar.size,
|
|
|
|
|
latestRelay: null,
|
|
|
|
|
batchCurrent: 0,
|
|
|
|
|
batchTotal: totalBatches,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (let bi = 0; bi < batches.length; bi++) {
|
|
|
|
|
currentBatch = bi + 1; // update before publish so per-relay events include it
|
|
|
|
|
const batch = batches[bi];
|
|
|
|
|
const batchStartTime = Date.now();
|
|
|
|
|
console.log(`[Worker][broadcast] Batch ${bi + 1}/${totalBatches} starting — ${batch.length} relays: ${batch.slice(0, 5).join(', ')}${batch.length > 5 ? '...' : ''}`);
|
|
|
|
|
const batchSet = NDKRelaySet.fromRelayUrls(batch, ndk);
|
|
|
|
|
if (batchSet?.relays) {
|
|
|
|
|
for (const relay of batchSet.relays) {
|
|
|
|
|
@@ -6341,7 +6373,33 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
} catch (batchErr) {
|
|
|
|
|
// Expected — some relays in this batch may fail.
|
|
|
|
|
}
|
|
|
|
|
console.log(`[Worker] Batch ${bi + 1}/${batches.length} complete — ` +
|
|
|
|
|
// --- Detect silent failures ---
|
|
|
|
|
// NDK's relay.publish() can resolve false without emitting
|
|
|
|
|
// relay:publish:failed (e.g., DNS errors, connection refused,
|
|
|
|
|
// HTTP handshake failures). These relays are not in any of
|
|
|
|
|
// our tracked sets. Mark them as timed out so they get
|
|
|
|
|
// skip-marked and don't waste time on the next publish.
|
|
|
|
|
for (const url of batch) {
|
|
|
|
|
if (!publishedSoFar.has(url) && !failedSoFar.has(url) && !timedOutSoFar.has(url)) {
|
|
|
|
|
timedOutSoFar.add(url);
|
|
|
|
|
const elapsed = ((Date.now() - publishStartTime) / 1000).toFixed(1);
|
|
|
|
|
console.log(`[Worker][broadcast] ⏱️ ${url} SILENT TIMEOUT (${elapsed}s) — batch ${currentBatch}/${totalBatches} (no response, likely connection failure)`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Emit a progress event with batch info after each batch.
|
|
|
|
|
broadcast({
|
|
|
|
|
type: 'broadcastProgress',
|
|
|
|
|
sessionId: publishSessionId,
|
|
|
|
|
phase: 'progress',
|
|
|
|
|
total: totalTarget,
|
|
|
|
|
successful: publishedSoFar.size,
|
|
|
|
|
failed: failedSoFar.size + timedOutSoFar.size,
|
|
|
|
|
latestRelay: null,
|
|
|
|
|
batchCurrent: bi + 1,
|
|
|
|
|
batchTotal: totalBatches,
|
|
|
|
|
});
|
|
|
|
|
const batchElapsed = ((Date.now() - batchStartTime) / 1000).toFixed(1);
|
|
|
|
|
console.log(`[Worker] Batch ${bi + 1}/${totalBatches} complete (${batchElapsed}s) — ` +
|
|
|
|
|
`${publishedSoFar.size}/${totalTarget} total succeeded, ` +
|
|
|
|
|
`${failedSoFar.size + timedOutSoFar.size} failed/timeout so far`);
|
|
|
|
|
}
|
|
|
|
|
@@ -6367,18 +6425,25 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
} catch (_cleanupErr) { /* ignore */ }
|
|
|
|
|
|
|
|
|
|
// --- Failure marking (background, after all batches) ---
|
|
|
|
|
const newSkips = Array.from(failedSoFar)
|
|
|
|
|
// Skip-mark both explicit rejections AND timeouts. A relay that
|
|
|
|
|
// times out is either down, too slow, or unreachable — skip it
|
|
|
|
|
// next time to avoid wasting time on it.
|
|
|
|
|
const allFailed = [...Array.from(failedSoFar), ...Array.from(timedOutSoFar)];
|
|
|
|
|
const newSkips = allFailed
|
|
|
|
|
.filter(url => broadcastRelayUrls.has(url) && !skippedRelayUrls.has(url));
|
|
|
|
|
if (newSkips.length > 0) {
|
|
|
|
|
for (const url of newSkips) {
|
|
|
|
|
const isTimeout = timedOutSoFar.has(url);
|
|
|
|
|
skippedRelayUrls.set(url, {
|
|
|
|
|
reason: 'publish-rejected',
|
|
|
|
|
reason: isTimeout ? 'publish-timeout' : 'publish-rejected',
|
|
|
|
|
timestamp: Math.floor(Date.now() / 1000),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
scheduleSkipMarkSave();
|
|
|
|
|
console.log(`[Worker] Skip-marked ${newSkips.length} rejected broadcast relays ` +
|
|
|
|
|
`(${timedOutSoFar.size} timeouts excluded)`);
|
|
|
|
|
const timeoutCount = newSkips.filter(u => timedOutSoFar.has(u)).length;
|
|
|
|
|
const rejectCount = newSkips.length - timeoutCount;
|
|
|
|
|
console.log(`[Worker] Skip-marked ${newSkips.length} failed broadcast relays ` +
|
|
|
|
|
`(${rejectCount} rejected, ${timeoutCount} timed out)`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return early — we already sent the response above. The rest of
|
|
|
|
|
@@ -6454,27 +6519,25 @@ async function handlePublish(requestId, event, port) {
|
|
|
|
|
console.log('[Worker] ❌ Failed relays:', relayResults.failed.length);
|
|
|
|
|
|
|
|
|
|
// --- Failure marking for broadcast relays ---------------------------------
|
|
|
|
|
// Persist skip-marks ONLY for broadcast relays that EXPLICITLY rejected
|
|
|
|
|
// the publish (auth-required, blocked, invalid, etc.). We do NOT skip-mark
|
|
|
|
|
// relays that timed out — with hundreds of relays, a timeout often means
|
|
|
|
|
// the relay was slow to connect, not that it's broken. The relay:publish:failed
|
|
|
|
|
// listener above separates timeouts (timedOutSoFar) from explicit rejections
|
|
|
|
|
// (failedSoFar) by checking the error message for "timeout".
|
|
|
|
|
// Skip-mark both explicit rejections AND timeouts. A relay that times
|
|
|
|
|
// out is either down, too slow, or unreachable — skip it next time.
|
|
|
|
|
if (isBroadcast) {
|
|
|
|
|
const newSkips = Array.from(failedSoFar)
|
|
|
|
|
const allFailed = [...Array.from(failedSoFar), ...Array.from(timedOutSoFar)];
|
|
|
|
|
const newSkips = allFailed
|
|
|
|
|
.filter(url => broadcastRelayUrls.has(url) && !skippedRelayUrls.has(url));
|
|
|
|
|
if (newSkips.length > 0) {
|
|
|
|
|
for (const url of newSkips) {
|
|
|
|
|
const isTimeout = timedOutSoFar.has(url);
|
|
|
|
|
skippedRelayUrls.set(url, {
|
|
|
|
|
reason: 'publish-rejected',
|
|
|
|
|
reason: isTimeout ? 'publish-timeout' : 'publish-rejected',
|
|
|
|
|
timestamp: Math.floor(Date.now() / 1000),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
scheduleSkipMarkSave();
|
|
|
|
|
console.log(`[Worker] Skip-marked ${newSkips.length} rejected broadcast relays ` +
|
|
|
|
|
`(${timedOutSoFar.size} timeouts excluded from skip-marking)`);
|
|
|
|
|
} else if (timedOutSoFar.size > 0) {
|
|
|
|
|
console.log(`[Worker] ${timedOutSoFar.size} broadcast relays timed out (not skip-marked — will retry next publish)`);
|
|
|
|
|
const timeoutCount2 = newSkips.filter(u => timedOutSoFar.has(u)).length;
|
|
|
|
|
const rejectCount2 = newSkips.length - timeoutCount2;
|
|
|
|
|
console.log(`[Worker] Skip-marked ${newSkips.length} failed broadcast relays ` +
|
|
|
|
|
`(${rejectCount2} rejected, ${timeoutCount2} timed out)`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|