Skip to content

v0.3.0

Choose a tag to compare

@ThatScalaGuy ThatScalaGuy released this 10 Jun 09:08
· 52 commits to main since this release
f9d8953

This release is almost entirely about performance. The client was benchmarked
head-to-head against the official Java client (jnats) across core pub/sub,
JetStream, and Object Store workloads, and the hot paths were rebuilt based on
what the profiles showed. There are no public API changes — everything
below is internal.

Performance

Core pub/sub

  • The protocol parser keeps a single flat array carry instead of an immutable
    chunk queue, fixing an O(n²) scan under streaming load — 2.4× parser
    throughput and −82% allocations in JMH, ~+40% end-to-end receive (#18)
  • Publishes enqueue a small write descriptor instead of building a combined
    header+payload array per message; the single writer fiber assembles batches
    into one reused buffer and flushes them in one socket write — per-publish
    allocation is now constant at 112 B regardless of payload size (#20, #21, #22)
  • The receive path builds the user-facing message once, directly in the parser,
    instead of materializing an intermediate frame that gets re-wrapped (#23)
  • Sends take a lock-free fast path while the connection is online (#19)
  • Sockets are read in 64 KiB requests instead of fs2-io's 8 KiB default,
    cutting async read round-trips 8× on large-payload streams (#26)
  • Internal bounded queues stay on cats-effect's lock-free implementation at
    any capacity. Queue.bounded silently degrades to a CAS-retry queue at
    capacities ≥ 65534, which cost >30% of client CPU under sustained load with
    large queueCapacity settings — high-capacity configs are now ~2× faster
    under CPU pressure (#24)

JetStream

  • consume processes inbox messages chunk-at-a-time instead of one effect per
    message: 2.1× consume throughput (#25)

Object Store

  • GET benefits from the larger socket reads and now hashes chunks without
    copying them — streaming GET reaches parity with jnats at steady state
    (~1.3 GB/s in our benchmarks) (#26)

Other changes

Compatibility

  • Binary compatible with 0.2.x (MiMa-checked); no source changes required
  • Scala 3, cats-effect 3.6, fs2 3.13; JDK 11+

What's Changed

  • docs: add sbt-typelevel-site documentation site by @ThatScalaGuy in #17
  • Test library updates by @ThatScalaGuy in #16
  • perf(parser): flat-array incremental decoder (fixes O(n²) control-line scan) by @ThatScalaGuy in #18
  • perf(client): skip the connRef.modify CAS on the Online publish path by @ThatScalaGuy in #19
  • perf(transport): reuse the writer coalescing buffer across drains by @ThatScalaGuy in #20
  • perf(transport): drop the per-publish Some box from the write queue by @ThatScalaGuy in #21
  • perf(publish): enqueue a write descriptor instead of a per-publish combined array by @ThatScalaGuy in #22
  • perf(receive): build the user NatsMessage once on the MSG/HMSG data path by @ThatScalaGuy in #23
  • perf(queues): keep hot-path queues on cats-effect's lock-free implementations at any capacity by @ThatScalaGuy in #24
  • perf(jetstream): process pull-consume deliveries a chunk at a time by @ThatScalaGuy in #25
  • perf(receive): 64 KiB socket reads + copy-free Object Store GET digest by @ThatScalaGuy in #26

Full Changelog: v0.2.0...v0.3.0