0

I need to add a WebSocket-to-TCP proxy to my Play 2.3 application, but while the outgoing TCP connection using Akka I/O supports back-pressure, I don't see anything for the WebSocket. There's clearly no support in the actor-based API, but James Roper says:

Iteratees handle this by design, you can't feed a new element into an iteratee until last future it returns has been redeemed, because you don't have a reference to it until then.

However, I don't see what he's referring to. Iteratee.foreach, as used in the examples, seems too simple. The only futures I see in the iteratee API are for completing the result of the computation. Should I be completing a Future[Unit] for each message or what?

Isvara
  • 3,403
  • 1
  • 28
  • 42

2 Answers2

4

Iteratee.foldM lets to pass a state along to each step, much like the regular fold operation, and return a future. If you do not have such a state you can just pass Unit and it will behave as a foreach that will not accept the next step until the future completes.

Here is an example of a utility function that does exactly that:

 def foreachM[E](f: E => Future[Unit])(implicit ec: ExecutionContext): Iteratee[E, Unit] = 
   Iteratee.foldM[E, Unit](Unit)((_, e) => f(e))
johanandren
  • 11,249
  • 1
  • 25
  • 30
2

Iteratee is not the same as Iterator. An Iteratee does indeed inherently support back-pressure (in fact you'll find yourself with the opposite problem - by default they don't do any buffering (at least within the pipeline - of course async sockets still have receive buffers), so you sometimes have to add an explicit buffering step to an enumerator/iteratee pipeline to get reasonable performance). The examples look simple but that just means the framework is doing what a framework does and making things easy. If you're doing a significant amount of work, or making async calls, in your handlers, then you shouldn't use the simple Iteratee.foreach, but instead use an API that accepts a Future-based handler; if you're blocking within an Iteratee then you block the whole thing, waste your threads, and defeat the point of using them at all.

lmm
  • 17,386
  • 3
  • 26
  • 37
  • "Iteratee is not the same as Iterator" -- it's a typo. Edited. – Isvara Jan 06 '15 at 15:01
  • "use an API that accepts a Future-based handler" -- is there one? Can you give an example? – Isvara Jan 06 '15 at 15:02
  • You can do it "by hand" by returning a `Future[Iteratee[...]]` at each step (using explicit `Cont` rather than `Iteratee.foreach`) and using `Iteratee.flatten`. I don't know the play API well enough to know if there's a nicer variant (I'm used to scalaz iteratees), but heck, you could write one if need be. – lmm Jan 06 '15 at 16:24
  • 2
    Iteratee.foldM lets to pass a state along to each step, much like the regular fold operation, and return a future. If you do not have such a state you can just pass Unit and it will behave as a foreach that will not accept the next step until the future completes. – johanandren Jan 07 '15 at 08:58
  • @johanandren I have that working now. If you put it as an answer, I'll accept it. – Isvara Jan 20 '15 at 07:58
  • @johanandren Do you know how it works in the other direction? If I push to an Enumerator's channel, how do I know when the data has been processed? – Isvara Jan 20 '15 at 08:23
  • http://stackoverflow.com/questions/28041042/is-it-possible-to-get-a-callback-when-data-fed-into-a-play-concurrent-unicast-en – Isvara Jan 20 '15 at 09:18