The headline of this release is fs2.nats.micro — typed NATS micro services
implementing ADR-32.
Alongside it, another round of hot-path work landed across core pub/sub, JetStream,
and Object Store, and 0.4 starts a new binary-compatibility series.
Micro services (fs2.nats.micro)
- An endpoint is described once as a plain
Rpc[P, I, E, O]value — subject pattern
with typed captures, request payload, typed error, response payload — and interpreted
on both sides:rpc.handle(...)inside aNatsService,Micro(client).call(rpc)(...)
from the caller. Definitions can live in a shared module (#57) - Subject patterns are checked at compile time:
pattern["orders.*"].bind[OrderId]
rejects malformed literals, computes the capture arity, and binds captures through
TokenCodecs (String,Int,Long,UUID, or your own) - Payloads via
Payload.empty/bytes/string/json[A](jsoniter-scala),
with optional schema text published in the endpoint INFO metadata - Errors are typed end to end: handlers return
F[Either[E, O]], errors travel as
ADR-32 error headers and come back asLeft(E)at the caller — transport failures
are the only thing raised inF - Full ADR-32 discovery: every instance answers
$SRV.PING/INFO/STATSin all
nine subject forms with the standard response types, sonats microCLI tooling
works out of the box; per-endpoint stats are also available locally via
NatsService#stats - Instances load-balance through a queue group by default; service and endpoint
metadata per ADR-33; request headers are visible to handlers, and response headers
can be set viaReply
The module ships inside the fs2-nats artifact and adds no new dependency
(jsoniter-scala was already on the classpath for JetStream). See
docs/micro.md
for the full tour.
Performance
- Dialled sockets now set
TCP_NODELAY, removing Nagle-induced latency (up to
~40 ms per round trip) from request/reply; socket options are configurable via
the newTransportConfig.socketOptions(#44) JetStream.publishAsync— and Object Storeput/ KVputAsyncon top of it —
no longer starts a supervised fiber per message; correlation is registered up
front and the reply completes on the shared inbox drain fiber (#47)- Request correlation moved from four CAS loops over an immutable-map
Refto a
ConcurrentHashMapplusAtomicLong, so pipelined request/reply scales instead
of contending on one serialization point (#49) - Subscription lookup on the delivery path uses a primitive-keyed
LongMapand a
single volatile read instead of boxing the sid and reading twoRefs per
message (#50) - JetStream API replies are decoded in place in a single jsoniter pass instead of
a payload copy plus two passes per PubAck (#51) - Object Store
putfeeds SHA-256 by wrapping each chunk's backing arrays instead
of flattening them, dropping the extra full copy of all object data on the way
to the wire (#53)
Compatibility
- 0.4 starts a new early-semver series: not binary compatible with 0.3.x,
recompile against 0.4.0. Source-level, typical users need no changes TransportConfiggained asocketOptionsfield (default:TCP_NODELAYon for
sockets the library dials — overridesocketOptionsto opt out). Named-argument
construction is unaffected; only full-arity positional construction needs the
new argument- The
NatsClienttrait gained a package-private abstract method, so hand-written
implementations of the trait outsidefs2.nats(e.g. test stubs) no longer
compile — delegate to a real client instead - Scala 3, cats-effect 3.7, fs2 3.13; JDK 11+
- Dependency refresh: jsoniter-scala 2.38.17 → 2.40.1; munit 1.3.5, sbt 1.12.15,
scalafmt 3.11.5 (test/build-scoped only)
What's Changed
- Minor & major updates by @ThatScalaGuy in #39
- Test library updates by @ThatScalaGuy in #37
- Patch updates by @ThatScalaGuy in #38
- Patch updates by @ThatScalaGuy in #40
- Minor & major updates by @ThatScalaGuy in #41
- Test library updates by @ThatScalaGuy in #43
- Add fs2.nats.micro: typed NATS micro services (ADR-32) by @ThatScalaGuy in #57
Full Changelog: v0.3.1...v0.4.0