Skip to content

Commit b0e96bd

Browse files
authored
Allow sending streams and not just receiving them (#121)
1 parent 2254777 commit b0e96bd

File tree

9 files changed

+461
-84
lines changed

9 files changed

+461
-84
lines changed

core/src/main/scala/com/devsisters/shardcake/interfaces/Pods.scala

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,28 @@ trait Pods {
3232
*/
3333
def sendMessage(pod: PodAddress, message: BinaryMessage): Task[Option[Array[Byte]]]
3434

35+
/**
36+
* Send a stream of messages to a pod
37+
*/
38+
def sendStream(
39+
pod: PodAddress,
40+
entityId: String,
41+
messages: ZStream[Any, Throwable, BinaryMessage]
42+
): Task[Option[Array[Byte]]]
43+
3544
/**
3645
* Send a message to a pod and receive a stream of replies
3746
*/
38-
def sendMessageStreaming(pod: PodAddress, message: BinaryMessage): ZStream[Any, Throwable, Array[Byte]]
47+
def sendMessageAndReceiveStream(pod: PodAddress, message: BinaryMessage): ZStream[Any, Throwable, Array[Byte]]
48+
49+
/**
50+
* Send a stream of messages to a pod and receive a stream of replies
51+
*/
52+
def sendStreamAndReceiveStream(
53+
pod: PodAddress,
54+
entityId: String,
55+
messages: ZStream[Any, Throwable, BinaryMessage]
56+
): ZStream[Any, Throwable, Array[Byte]]
3957
}
4058

4159
object Pods {
@@ -46,12 +64,22 @@ object Pods {
4664
*/
4765
val noop: ULayer[Pods] =
4866
ZLayer.succeed(new Pods {
49-
def assignShards(pod: PodAddress, shards: Set[ShardId]): Task[Unit] = ZIO.unit
50-
def unassignShards(pod: PodAddress, shards: Set[ShardId]): Task[Unit] = ZIO.unit
51-
def ping(pod: PodAddress): Task[Unit] = ZIO.unit
52-
def sendMessage(pod: PodAddress, message: BinaryMessage): Task[Option[Array[Byte]]] = ZIO.none
53-
def sendMessageStreaming(pod: PodAddress, message: BinaryMessage): ZStream[Any, Throwable, Array[Byte]] =
67+
def assignShards(pod: PodAddress, shards: Set[ShardId]): Task[Unit] = ZIO.unit
68+
def unassignShards(pod: PodAddress, shards: Set[ShardId]): Task[Unit] = ZIO.unit
69+
def ping(pod: PodAddress): Task[Unit] = ZIO.unit
70+
def sendMessage(pod: PodAddress, message: BinaryMessage): Task[Option[Array[Byte]]] = ZIO.none
71+
def sendStream(
72+
pod: PodAddress,
73+
entityId: String,
74+
messages: ZStream[Any, Throwable, BinaryMessage]
75+
): Task[Option[Array[Byte]]] = ZIO.none
76+
def sendMessageAndReceiveStream(pod: PodAddress, message: BinaryMessage): ZStream[Any, Throwable, Array[Byte]] =
5477
ZStream.empty
78+
def sendStreamAndReceiveStream(
79+
pod: PodAddress,
80+
entityId: String,
81+
messages: ZStream[Any, Throwable, BinaryMessage]
82+
): ZStream[Any, Throwable, Array[Byte]] = ZStream.empty
5583
})
5684

5785
case class BinaryMessage(entityId: String, entityType: String, body: Array[Byte], replyId: Option[String])

entities/src/main/scala/com/devsisters/shardcake/Messenger.scala

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,19 @@ trait Messenger[-Msg] {
2727
* streaming responses. See `sendStreamAutoRestart` for an alternative that will automatically restart the stream
2828
* in case of rebalance.
2929
*/
30-
def sendStream[Res](entityId: String)(msg: StreamReplier[Res] => Msg): Task[ZStream[Any, Throwable, Res]]
30+
def sendAndReceiveStream[Res](entityId: String)(msg: StreamReplier[Res] => Msg): Task[ZStream[Any, Throwable, Res]]
31+
32+
/**
33+
* Send a stream of messages.
34+
*/
35+
def sendStream(entityId: String)(messages: ZStream[Any, Throwable, Msg]): Task[Unit]
36+
37+
/**
38+
* Send a stream of messages and receive a stream of responses of type `Res`.
39+
*/
40+
def sendStreamAndReceiveStream[Res](entityId: String)(
41+
messages: StreamReplier[Res] => ZStream[Any, Throwable, Msg]
42+
): Task[ZStream[Any, Throwable, Res]]
3143

3244
/**
3345
* Send a message and receive a stream of responses of type `Res` while restarting the stream when the remote entity
@@ -38,11 +50,42 @@ trait Messenger[-Msg] {
3850
* cursor from the responses so that when the remote entity is rebalanced, a new message can be sent with the right
3951
* cursor according to what we've seen in the previous stream of responses.
4052
*/
41-
def sendStreamAutoRestart[Cursor, Res](entityId: String, cursor: Cursor)(msg: (Cursor, StreamReplier[Res]) => Msg)(
53+
def sendAndReceiveStreamAutoRestart[Cursor, Res](entityId: String, cursor: Cursor)(
54+
msg: (Cursor, StreamReplier[Res]) => Msg
55+
)(
56+
updateCursor: (Cursor, Res) => Cursor
57+
): ZStream[Any, Throwable, Res] =
58+
ZStream
59+
.unwrap(sendAndReceiveStream[Res](entityId)(msg(cursor, _)))
60+
.either
61+
.mapAccum(cursor) {
62+
case (c, Right(res)) => updateCursor(c, res) -> Right(res)
63+
case (c, Left(err)) => (c, Left(c -> err))
64+
}
65+
.flatMap {
66+
case Right(res) => ZStream.succeed(res)
67+
case Left((lastSeenCursor, StreamCancelled)) =>
68+
ZStream.execute(ZIO.sleep(200.millis)) ++
69+
sendAndReceiveStreamAutoRestart(entityId, lastSeenCursor)(msg)(updateCursor)
70+
case Left((_, err)) => ZStream.fail(err)
71+
}
72+
73+
/**
74+
* Send a stream of messages and receive a stream of responses of type `Res` while restarting the stream when the
75+
* remote entity is rebalanced.
76+
*
77+
* To do so, we need a "cursor" so the stream of responses can be restarted where it ended before the rebalance. That
78+
* is, the first message sent to the remote entity contains the given initial cursor value and we extract an updated
79+
* cursor from the responses so that when the remote entity is rebalanced, a new message can be sent with the right
80+
* cursor according to what we've seen in the previous stream of responses.
81+
*/
82+
def sendStreamAndReceiveStreamAutoRestart[Cursor, Res](entityId: String, cursor: Cursor)(
83+
msg: (Cursor, StreamReplier[Res]) => ZStream[Any, Throwable, Msg]
84+
)(
4285
updateCursor: (Cursor, Res) => Cursor
4386
): ZStream[Any, Throwable, Res] =
4487
ZStream
45-
.unwrap(sendStream[Res](entityId)(msg(cursor, _)))
88+
.unwrap(sendStreamAndReceiveStream[Res](entityId)(msg(cursor, _)))
4689
.either
4790
.mapAccum(cursor) {
4891
case (c, Right(res)) => updateCursor(c, res) -> Right(res)
@@ -52,7 +95,7 @@ trait Messenger[-Msg] {
5295
case Right(res) => ZStream.succeed(res)
5396
case Left((lastSeenCursor, StreamCancelled)) =>
5497
ZStream.execute(ZIO.sleep(200.millis)) ++
55-
sendStreamAutoRestart(entityId, lastSeenCursor)(msg)(updateCursor)
98+
sendStreamAndReceiveStreamAutoRestart(entityId, lastSeenCursor)(msg)(updateCursor)
5699
case Left((_, err)) => ZStream.fail(err)
57100
}
58101
}

0 commit comments

Comments
 (0)