github centrifugal/centrifugo v6.9.7

2 hours ago

Centrifugo is an open-source scalable real-time messaging server. It instantly delivers messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE), GRPC, WebTransport). Centrifugo is built around channel subscriptions – so it's a user-facing PUB/SUB server.

Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, AI streaming responses, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.

Official client SDKs are available for JavaScript (browser, Node.js, React Native), Dart/Flutter, Swift, Java, Python, Go, and .NET. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.

For details, go to the Centrifugo documentation site. For runnable demos see centrifugal/examples.

What's changed

Improvements

  • Publications into a channel are no longer delayed by clients subscribing to it with recovery or positioning (see also the fix below). In a benchmark which publishes into a channel with 100 subscribers while other clients connect with it and a second channel whose history read takes 1ms, publish latency dropped from 249µs to 28µs on average and from 1240µs to 46µs at p99. Broadcasting to a client which is not subscribing to anything now costs an atomic load instead of a mutex and a map lookup (a broadcast to 100 subscribers went from 27.4µs to 23.9µs).
  • Faster fossil delta encoding – Centrifugo now uses centrifugal/fdelta package instead of shadowspore/fossil-delta. Creating a delta is 3.5-4.2x faster on JSON payloads and allocates once instead of five times (128 B/op at 16 KiB against 19 KB). centrifugal/centrifuge#638.
  • Less memory per connection. Client command handlers copied the whole configuration (about 10 KB) onto the stack of the connection's goroutine, so the stack grew on the first commands. They now read it by reference. With 20k connections which connect and subscribe, stack memory is 35% lower and process RSS about 20% lower – about 10 KB less per connection (#1244).
  • Faster channel options lookup. Options are now resolved for every namespace once – on start and on every config reload – so a lookup is a map read without allocation. Before, Centrifugo cached the options of 1024 channels, and with more channels in use (a broadcast into thousands of channels, many personal channels) lookups kept missing the cache and allocated a copy of the options each time. A broadcast into 1000 channels is 27% faster and allocates about half the memory (#1245).
  • Less work per broadcast when delta compression is used by JSON clients. A JSON client receives a delta as a JSON string, so a delta may only be built when the previous payload is valid UTF-8. Now a payload is scanned at most once per broadcast, and not at all when no JSON client takes deltas. It only matters for payloads carrying non-ASCII text (utf8.Valid has an ASCII fast path) – on such payloads the broadcast is 12-21% faster depending on the number of encoding combinations in the channel (centrifugal/centrifuge#637).

Fixes

  • Fix a possible node deadlock when a client subscribes to a channel with recovery or positioning enabled. Such a subscription kept its recovery buffer locked until the subscribe result was written, while a publication into the channel waited for that buffer holding the hub shard lock – so publications, subscribes, unsubscribes, disconnects and graceful shutdown for all channels of that shard could block. Publications no longer wait for a subscribing client (centrifugal/centrifuge#640). Reported in GHSA-xcch-wm46-g65g by @EJ-Research
  • Delta compression and publication tags filters are documented as mutually exclusive, but Centrifugo still negotiated delta for a subscription with a tags filter. To keep the chain of deltas intact, publications filtered out for such a subscriber were sent to it anyway – so the filter was not applied at all. Delta is no longer negotiated for subscriptions with a tags filter (centrifugal/centrifuge#634).
  • Map channels: when a channel's stream expired while its top offset and epoch survived, a read since an older offset returned an empty result with no error – recovery reported recovered: true while silently skipping the lost publications. Such a read now results in an unrecoverable position error, so clients resync from the full state (centrifugal/centrifuge#636).
  • Publications buffered while a client subscribes with recovery or positioning are now limited by client.queue_max_size per connection. Before, each subscription got the whole limit for itself, so a client subscribing to many channels at once – for example with server-side subscriptions on connect – could hold many times that limit in memory (centrifugal/centrifuge#641).
  • Redis Cluster: a channel whose name starts with } made the keys of that channel land in different slots, which could crash the node – for example when a client subscribed to such a channel with recovery. Such channels are now refused with a bad request error (centrifugal/centrifuge#645).
  • The unsubscribe server API method now returns a bad request error when channel is empty, as documented. Before, it unsubscribed nothing but still sent the targeted connections an unsubscribe push for an empty channel (#1238).

Miscellaneous

  • This release is built with Go 1.26.8.
  • Dependency updates.
  • See also the corresponding Centrifugo PRO release.

Don't miss a new centrifugo release

NewReleases is sending notifications on new releases.