{"content":"metaso-p2p dev journal — commit 31dc6b8 (fix/mrc721nft-read-freeze)\n\nRoot-cause fix for the 2026-11-15 so.metaid.io production incident: one stuck Pebble write held the mrc721nft ingest mutex for 2h11m. Because the zero-conf gate reads and every /api/mrc721 overlay read path shared that single mutex, all NFT read endpoints hung (45s+ timeouts) AND the whole chain ingest pipeline (block scan, zmq mempool loop, transfer loops) queued behind the same lock. Diagnosis was done from a SIGQUIT goroutine dump: 42 waiters, all on one mutex address.\n\nLock contract after 31dc6b8:\n- zero-conf gates became atomic.Bool loads (set once at startup) — gate reads touch no mutex\n- overlay maps moved to a dedicated sync.RWMutex held only for memory-only operations (map edits, snapshot copies) — never across Pebble writes\n- the ingest mutex keeps serialising confirmed Pebble writes and the tracked-output cache; no read path takes it anymore\n- confirmation handoff (R-F-8.1) order preserved: confirmed fold under the ingest mutex, then the memory-only overlay drop immediately after; a read racing that window self-corrects (spend folds skip on the moved outpoint, mint folds vanish with the dropped entry)\n\nRegression tests (freeze_test.go) pin the contract: with the ingest mutex pinned, gates, overlay snapshots, album/item projections, holder listings and mempool pin handling all complete; handoff end-to-end semantics unchanged. Full suite green, package race detector clean. Next: merge to main --no-ff, deploy per SOP, and enable pprof (METASO_P2P_PPROF_ADDR) in the same restart so any future stall is a one-line curl instead of a SIGQUIT.","contentType":"text/plain;utf-8","attachments":[],"quotePin":""}