compares plc.directory with other mirrors
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

track cids retroactively

dawn ffdc2ac4 f17c53e5

+52 -9
+20 -8
index.html
··· 423 423 .miss-status-present { color: #5ab05e; } 424 424 .miss-status-missing { color: #b05a5e; } 425 425 .miss-status-error { color: #555; } 426 + .miss-status-late { color: #8a9a44; } 426 427 .miss-section-title { 427 428 font-size: 0.66rem; 428 429 text-transform: uppercase; ··· 434 435 } 435 436 .miss-section-title.missed { color: #7a3022; } 436 437 .miss-section-title.extra { color: #7a5a22; } 438 + .miss-section-title.late { color: #5a6a22; } 437 439 438 440 /* ── mobile ── */ 439 441 @media (max-width: 600px) { ··· 1092 1094 const sm = snap.mirrors[url] ?? {}; 1093 1095 const missed = sm.missedCids ?? {}; 1094 1096 const extra = sm.extraCids ?? {}; 1097 + const late = sm.lateCids ?? {}; 1095 1098 const dt = new Date(snap.ts); 1096 1099 const timeStr = dt.toLocaleString([], { month: 'short', day: 'numeric', 1097 1100 hour: '2-digit', minute: '2-digit' }); ··· 1103 1106 const body = document.getElementById('missBody'); 1104 1107 const missedDids = Object.keys(missed); 1105 1108 const extraDids = Object.keys(extra); 1109 + const lateDids = Object.keys(late); 1106 1110 1107 - const renderDidBlock = (did, cids, withStatus) => { 1108 - const cidRows = cids.map(c => 1109 - withStatus 1110 - ? `<div class="miss-cid-row"><span class="miss-cid" data-cid="${c}">${c}</span><span class="miss-cid-status miss-status-checking">·</span></div>` 1111 - : `<div class="miss-cid-row"><span class="miss-cid">${c}</span></div>` 1112 - ).join(''); 1111 + const renderDidBlock = (did, cids, statusFn) => { 1112 + const cidRows = cids.map(c => { 1113 + const status = statusFn ? statusFn(c) : null; 1114 + const statusHtml = status 1115 + ? `<span class="miss-cid-status ${status.cls}">${status.text}</span>` 1116 + : ''; 1117 + const cidAttr = status?.live ? ` data-cid="${c}"` : ''; 1118 + return `<div class="miss-cid-row"><span class="miss-cid"${cidAttr}>${c}</span>${statusHtml}</div>`; 1119 + }).join(''); 1113 1120 return `<div class="miss-did" data-did="${did}"> 1114 1121 <div class="miss-did-label">${did} <span style="color:#555;font-size:0.68rem">(${cids.length} op${cids.length !== 1 ? 's' : ''})</span> 1115 1122 <a class="miss-audit-link" href="https://plc.directory/${did}/log/audit" target="_blank" rel="noopener">upstream ↗</a> ··· 1122 1129 let html = ''; 1123 1130 if (missedDids.length) { 1124 1131 html += `<div class="miss-section-title missed">not seen by mirror (${missedDids.reduce((n, d) => n + missed[d].length, 0)})</div>`; 1125 - html += missedDids.sort().map(did => renderDidBlock(did, missed[did], true)).join(''); 1132 + html += missedDids.sort().map(did => renderDidBlock(did, missed[did], () => ({ text: '·', cls: 'miss-status-checking', live: true }))).join(''); 1126 1133 for (const did of missedDids) checkMirrorAudit(mirrorBase, did, missed[did], gen); 1127 1134 } 1135 + if (lateDids.length) { 1136 + html += `<div class="miss-section-title late">arrived late (${lateDids.reduce((n, d) => n + late[d].length, 0)})</div>`; 1137 + html += lateDids.sort().map(did => renderDidBlock(did, late[did], () => ({ text: '✓ late', cls: 'miss-status-late', live: false }))).join(''); 1138 + } 1128 1139 if (extraDids.length) { 1129 1140 html += `<div class="miss-section-title extra">not seen by upstream (${extraDids.reduce((n, d) => n + extra[d].length, 0)})</div>`; 1130 - html += extraDids.sort().map(did => renderDidBlock(did, extra[did], false)).join(''); 1141 + html += extraDids.sort().map(did => renderDidBlock(did, extra[did], null)).join(''); 1131 1142 } 1132 1143 if (!html) { 1133 1144 html = '<div class="miss-empty">no discrepancies recorded for this interval</div>'; ··· 1187 1198 mirror: mirrorName, 1188 1199 snapshot: new Date(snap.ts).toISOString(), 1189 1200 missed: sm.missedCids ?? {}, 1201 + late: sm.lateCids ?? {}, 1190 1202 }; 1191 1203 navigator.clipboard.writeText(JSON.stringify(payload, null, 2)).catch(() => { 1192 1204 const btn = document.getElementById('missCopyBtn');
+32 -1
server.ts
··· 37 37 missed: number; 38 38 ops: number; // ops received by mirror in this interval 39 39 primaryOps: number; // ops received by primary in this interval 40 - missedCids: Record<string, string[]>; // did -> [cid, ...] primary got, mirror didn't 40 + missedCids: Record<string, string[]>; // did -> [cid, ...] primary got, mirror didn't (still missing) 41 41 extraCids: Record<string, string[]>; // did -> [cid, ...] mirror got, primary didn't 42 + lateCids: Record<string, string[]>; // did -> [cid, ...] arrived at mirror after snapshot 42 43 } 43 44 44 45 interface Snapshot { ··· 210 211 entry.mirrorRecv.set(url, now); 211 212 if (entry.primaryRecvMs !== null) { 212 213 pushLag(m, now - entry.primaryRecvMs); 214 + retroactivelyMarkReceived(url, cid, entry.did ?? "?", entry.primaryRecvMs); 213 215 } 214 216 } 215 217 } ··· 264 266 if (m.lagSamples.length > LAG_KEEP) m.lagSamples.shift(); 265 267 } 266 268 269 + // When a mirror delivers an op late (after a snapshot was already taken), fix 270 + // the snapshot rather than leaving a permanent false-positive "missed" entry. 271 + function retroactivelyMarkReceived(url: string, cid: string, did: string, primaryRecvMs: number): void { 272 + for (const snap of snapshots) { 273 + const windowStart = snap.ts - SNAP_INTERVAL; 274 + if (primaryRecvMs < windowStart || primaryRecvMs > snap.ts) continue; 275 + const sm = snap.mirrors[url]; 276 + if (!sm) continue; 277 + const cidList = sm.missedCids[did]; 278 + if (!cidList) continue; 279 + const idx = cidList.indexOf(cid); 280 + if (idx === -1) continue; 281 + cidList.splice(idx, 1); 282 + if (cidList.length === 0) delete sm.missedCids[did]; 283 + if (!sm.lateCids) sm.lateCids = {}; 284 + if (!sm.lateCids[did]) sm.lateCids[did] = []; 285 + sm.lateCids[did].push(cid); 286 + sm.ops++; 287 + sm.missed = Math.max(0, sm.missed - 1); 288 + sm.coverage = sm.primaryOps > 0 ? sm.ops / sm.primaryOps : null; 289 + try { 290 + writeFileSync(`${DATA_DIR}/${snap.ts}.json`, JSON.stringify(snap)); 291 + } catch (e) { 292 + console.error("failed to retroactively update snapshot:", e); 293 + } 294 + } 295 + } 296 + 267 297 // ── tracker pruning ─────────────────────────────────────────────────────────── 268 298 269 299 function pruneTracker(): void { ··· 391 421 primaryOps, 392 422 missedCids: mirrorMissed.get(url) ?? {}, 393 423 extraCids: mirrorExtra.get(url) ?? {}, 424 + lateCids: {}, 394 425 }; 395 426 } 396 427 return out;