Commit Graph

3 Commits

Author SHA1 Message Date
Claude 289bc4bd5c perf(quartz): Tier 3 — parallel Schnorr verify in IngestQueue
Closes the last item on the event-ingestion-batching plan: signature
verification no longer serialises on each connection's WebSocket
pump. Instead, IngestQueue takes a `verify: ((Event) -> Boolean)?`
hook and fan-outs the per-batch verify across Dispatchers.Default
(`coroutineScope { events.map { async { verify(it) } }.awaitAll() }`)
before opening the SQLite transaction. Failed verifies pre-mark
Rejected and skip the insert.

Wiring:
- NostrServer takes `parallelVerify: Boolean = false` (opt-in to
  preserve existing behaviour for direct library users).
- geode.Relay forwards a matching flag.
- Main.kt enables it whenever signature checking is on (config
  `[options].parallel_verify = true`, default true), and when so,
  composePolicy is told to skip VerifyPolicy from the chain to
  avoid double-verifying every event.
- New CLI escape hatch `--no-parallel-verify` for the legacy path.

Bench: adds publishGroupCommitSingleClient (sequential publish-and-
confirm; 500 EPS regression floor for the synchronous path) — the
companion to the existing pipelined bench that exercises the
group-commit + parallel-verify wins.

Plan doc updated to describe what shipped (batchInsert + SAVEPOINTs
in Tier 1, IngestQueue mechanics in Tier 2, the verify hook in
Tier 3) and to drop the obsolete `synchronous=NORMAL` confirmation
note — the project ships `synchronous=OFF` and intentionally keeps
that.
2026-05-07 23:00:30 +00:00
Claude 0f7d8696de perf(geode): adaptive outQueue + CIO pool sizing for 10k+ connections
Implements geode/plans/2026-05-07-connection-scaling.md to push the
single-relay connection ceiling past the ~2k floor measured by
LoadBenchmark.connectionsHeldOpen.

- WebSocketSessionPump: switch outQueue to Channel.UNLIMITED bounded
  by an AtomicInteger backlog cap. Idle connections no longer reserve
  an 8 192-slot fixed buffer; lazy-allocated head segments cost only
  a few hundred bytes per idle session. Slow-client policy preserved:
  once outstanding > MAX_OUTGOING_BUFFER, the queue is closed and
  the connection drops, so NIP-01 ordering is never silently
  violated.
- LocalRelayServer / RelayConfig.NetworkSection: expose CIO
  connectionGroupSize / workerGroupSize / callGroupSize so big-VM
  operators can tune Ktor's event-loop pools without forking. Switch
  to the serverConfig {} + configure {} embeddedServer overload so
  CIO tunables can be set.
- LoadBenchmark: add connectionsHeldOpen10k (asserts 10 000 idle
  WebSockets settle under a 1 GiB heap ceiling) and
  connectionsHeldOpenWithFanout (5 000 subscribers, 10 EPS fan-out
  for 10 s, reports p50/p99 last-fanout latency).
- config.example.toml: document the three CIO tunables and the
  ulimit -n requirement for operators targeting >1k connections.
2026-05-07 20:12:50 +00:00
Claude c2f24a5213 refactor: rename quartz-relay module to geode
The relay implementation now stands as its own module. Fitting brand
for a Nostr relay shipped alongside the Quartz library — a geode is a
rock that holds quartz inside.

- Rename module directory quartz-relay/ -> geode/.
- Rename Gradle module path :quartz-relay -> :geode (settings.gradle).
- Rename Kotlin package com.vitorpamplona.quartz.relay -> com.vitorpamplona.geode.
- Rename application name + main class binding to match.
- Update the relay's NIP-11 advertised name to "geode" and the
  software URL to /tree/main/geode. Test asserting the doc updated.
- Refresh comments / Main.kt usage line / config.example.toml header.
- Update consumers: quartz/build.gradle.kts dependency path, and
  quartz NostrClient tests that import the in-process RelayHub.
2026-05-07 13:12:34 +00:00