Skip to content

Commit ce56120

Browse files
committed
Pull incoming messages in 'StreamIngest' in chunks and use batched request calls
1 parent 87e1535 commit ce56120

File tree

4 files changed

+47
-25
lines changed

4 files changed

+47
-25
lines changed

runtime/src/main/scala/fs2/grpc/client/Fs2ClientCall.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Fs2ClientCall[F[_], Request, Response] private[client] (
109109
): Resource[F, Fs2StreamClientCallListener[F, Response]] = {
110110
val prefetchN = options.prefetchN.max(1)
111111
val create = Fs2StreamClientCallListener.create[F, Response](request, signalReadiness, dispatcher, prefetchN)
112-
val acquire = start(create, md) <* request(prefetchN)
112+
val acquire = start(create, md)
113113
val release = handleExitCase(cancelSucceed = true)
114114

115115
Resource.makeCase(acquire)(release)

runtime/src/main/scala/fs2/grpc/client/StreamIngest.scala

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ package grpc
2424
package client
2525

2626
import cats.implicits._
27-
import cats.effect.Concurrent
27+
import cats.effect.{Concurrent, Ref}
2828
import cats.effect.std.Queue
2929

3030
private[grpc] trait StreamIngest[F[_], T] {
@@ -39,41 +39,63 @@ private[grpc] object StreamIngest {
3939
request: Int => F[Unit],
4040
prefetchN: Int
4141
): F[StreamIngest[F, T]] =
42-
Queue
43-
.unbounded[F, Either[Option[Throwable], T]]
44-
.map(q => create[F, T](request, prefetchN, q))
42+
(Ref[F].of(0), Queue.unbounded[F, Either[Option[Throwable], T]])
43+
.mapN((r, q) => create[F, T](request, prefetchN, r, q))
4544

4645
def create[F[_], T](
4746
request: Int => F[Unit],
4847
prefetchN: Int,
48+
requested: Ref[F, Int],
4949
queue: Queue[F, Either[Option[Throwable], T]]
5050
)(implicit F: Concurrent[F]): StreamIngest[F, T] = new StreamIngest[F, T] {
51-
52-
val limit: Int =
53-
math.max(1, prefetchN)
54-
55-
val ensureMessages: F[Unit] =
56-
queue.size.flatMap(qs => request(1).whenA(qs < limit))
51+
private val limit: Int = math.max(1, prefetchN)
52+
private def updateRequests: F[Unit] = {
53+
queue.size.flatMap { queued =>
54+
requested.flatModify { requested =>
55+
val total = queued + requested
56+
val additional = math.max(0, limit - total)
57+
58+
(
59+
requested + additional,
60+
request(additional).whenA(additional > 0)
61+
)
62+
}
63+
}
64+
}
5765

5866
def onMessage(msg: T): F[Unit] =
59-
queue.offer(msg.asRight) *> ensureMessages
67+
queue.offer(msg.asRight) *> requested.update(r => math.max(0, r - 1))
6068

6169
def onClose(error: Option[Throwable]): F[Unit] =
6270
queue.offer(error.asLeft)
6371

6472
val messages: Stream[F, T] = {
65-
66-
val run: F[Option[T]] =
67-
queue.take.flatMap {
68-
case Right(v) => ensureMessages *> v.some.pure[F]
69-
case Left(Some(error)) => F.raiseError(error)
70-
case Left(None) => none[T].pure[F]
73+
type S = Either[Option[Throwable], Chunk[T]]
74+
75+
def zero: S = Chunk.empty.asRight
76+
def loop(state: S): F[Option[(Chunk[T], S)]] =
77+
state match {
78+
case Left(None) => F.pure(none)
79+
case Left(Some(err)) => F.raiseError(err)
80+
case Right(acc) =>
81+
queue.tryTake.flatMap {
82+
case Some(Right(value)) => loop((acc ++ Chunk.singleton(value)).asRight)
83+
case Some(Left(err)) =>
84+
if (acc.isEmpty) loop(err.asLeft)
85+
else F.pure((acc.toIndexedChunk, err.asLeft).some)
86+
case None =>
87+
val await = if (acc.isEmpty) queue.take.flatMap {
88+
case Right(value) => loop(Chunk.singleton(value).asRight)
89+
case Left(err) => loop(err.asLeft)
90+
}
91+
else F.pure((acc.toIndexedChunk, zero).some)
92+
93+
updateRequests *> await
94+
}
7195
}
7296

73-
Stream.repeatEval(run).unNoneTerminate
74-
97+
Stream.unfoldChunkEval(zero)(loop)
7598
}
76-
7799
}
78100

79101
}

runtime/src/test/scala/fs2/grpc/client/StreamIngestSuite.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ class StreamIngestSuite extends CatsEffectSuite with CatsEffectFunFixtures {
4545

4646
run(prefetchN = 1, takeN = 1, expectedReq = 1, expectedCount = 1) *>
4747
run(prefetchN = 2, takeN = 1, expectedReq = 2, expectedCount = 1) *>
48-
run(prefetchN = 2, takeN = 2, expectedReq = 3, expectedCount = 2) *>
49-
run(prefetchN = 1024, takeN = 1024, expectedReq = 2047, expectedCount = 1024) *>
50-
run(prefetchN = 1024, takeN = 1023, expectedReq = 2046, expectedCount = 1023)
48+
run(prefetchN = 2, takeN = 2, expectedReq = 2, expectedCount = 2) *>
49+
run(prefetchN = 1024, takeN = 1024, expectedReq = 1024, expectedCount = 1024) *>
50+
run(prefetchN = 1024, takeN = 1023, expectedReq = 1024, expectedCount = 1023)
5151

5252
}
5353

runtime/src/test/scala/fs2/grpc/server/ServerSuite.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ class ServerSuite extends Fs2GrpcSuite {
360360

361361
tc.tick()
362362

363-
assertEquals(dummy.requested, 1)
363+
assertEquals(dummy.requested, 2)
364364

365365
listener.onMessage("1")
366366
tc.tick()

0 commit comments

Comments
 (0)