Files
amethyst/commons
Vitor PamplonaandClaude Opus 5 9fe24be6d6 perf(relays): keep the EOSE cursor lock-free on the per-event path
The previous commit guarded EOSERelayList with a lock, which put the wrong path
under it. addOrUpdate runs on **every live event** — SubscriptionListener.onEvent
calls newEose for each one, hundreds per second across a few hundred relays — so
that was one monitor for every event arriving in the app.

Almost none of those events change the map. MutableTime exists precisely so a
known relay only bumps a Long inside its own entry; the map is structurally
written on the *first* frame from a relay, plus remove() and clear(). A couple of
hundred writes for the lifetime of the process, against millions of reads.

So the map is copy-on-write behind @Volatile: replaced wholesale under the lock,
never mutated in place after publication. Readers take nothing. The per-event
bump takes nothing. Only a relay's first frame pays, and it pays a map copy of a
few hundred entries, once.

The bump itself stays unsynchronized, which is deliberate: two socket threads
racing updateIfNewer can leave the older timestamp, and this value is a floor for
`since`, so losing a millisecond re-asks for a couple of events rather than
skipping any. Documented on the method rather than fixed with an atomic that
would cost a barrier per event.

Verified: iOS, JVM, Android, desktop and the full suite build and pass. Cold start
on emulator-5554 — no fatal exceptions, no ConcurrentModificationException, 0
nos.lol refusals, 182 relays connected and the purpose breakdown populated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:47:50 -04:00
..