Pub/Sub
Subscribing returns the backend's native stream type. Ox returns Flow, ZIO returns ZStream, Cats Effect returns an fs2 Stream, Kyo returns Stream, and Pekko returns Source. Each message contains its channel and a payload decoded by a ValueCodec. Ending the stream or closing its scope unsubscribes.
Classic channels
subscribe listens on one or more channels; publish sends to a channel. Here we subscribe, publish three messages, then take them back:
val news = client.subscribeScoped[String]("news")
(1 to 3).foreach(i => client.publish("news", s"item-$i"))
val messages = news.take(3).runToList()ZIO.scoped {
for {
stream <- client.subscribeScoped[String]("news")
_ <- ZIO.foreachDiscard(1 to 3) { i =>
client.publish("news", s"item-$i")
}
messages <- stream.take(3).runCollect
} yield messages.map(_.payload).toList
}client.subscribeResource[String]("news").use { stream =>
for {
_ <- (1 to 3).toList.traverse_ { i =>
client.publish("news", s"item-$i")
}
messages <- stream.take(3).compile.toVector
} yield messages.map(_.payload).toList
}for {
stream <- client.subscribeScoped[String]("news")
_ <- Kyo.foreachDiscard(1 to 3) { i =>
client.publish("news", s"item-$i")
}
chunk <- stream.take(3).run
} yield chunk.toList.map(_.payload)// Wait for confirmation before publishing, or the message may be missed.
val (confirmed, collected) =
client.subscribe[String]("news").take(3).toMat(Sink.seq)(Keep.both).run()
val messages =
for {
_ <- confirmed
_ <- Future.traverse(1 to 3)(i => client.publish("news", s"item-$i"))
received <- collected
} yield received.map(_.payload).toListPattern subscriptions return a pattern message that includes the matching glob.
Confirmed subscriptions
The plain subscribe returns the stream immediately and registers the subscription when the stream first requests a message. A message published before registration finishes may be missed. Each backend provides a way to wait for the server's SUBSCRIBE confirmation first (also in p/s forms):
- ZIO / Kyo:
subscribeScoped, a scoped effect. - Cats Effect:
subscribeResource, aResource. - Ox:
subscribeScoped, bound to the enclosing Ox scope. - Pekko: plain
subscribereturns aSourcewhose materializedFuture[Done]completes once registered; await it before publishing.
On a standalone or master-replica server, confirmation guarantees that the subscription is ready. In a cluster, this guarantee is best-effort. With the scoped and resource variants, the subscription remains active until the scope closes. On Pekko, ending the Source unsubscribes.
Connection isolation
All classic subscriptions share one subscription connection, created the first time you subscribe and closed when the last subscription ends. Ordinary commands use a different connection. A slow consumer may delay its own subscriptions, but it does not delay command replies. The subscription connection re-issues every active subscription automatically on reconnect.
Sharded channels (cluster)
In a cluster, a shard channel keeps its traffic within the shard that owns the channel's slot. sSubscribe and sPublish target that node instead of broadcasting across the whole cluster. Shard channels do not support pattern subscriptions, and they deliver ordinary messages.
// ZIO; the shape is the same on every backend
ZIO.scoped {
for {
stream <- client.sSubscribeScoped[String]("orders")
_ <- client.sPublish("orders", "placed")
messages <- stream.take(1).runCollect
} yield messages.map(_.payload).toList
}Sage holds one sharded subscription connection per owning node. When a slot moves or a node fails over, Sage moves the affected subscriptions to the new node.
Introspection
pubsubChannels, pubsubShardChannels, pubsubNumSub, pubsubShardNumSub, and pubsubNumPat report the current subscription state. Each server knows only about its own subscribers. In a cluster, Sage runs these commands on every master and combines the replies.
See Configuration for how to connect to a cluster.