How are Akka streams implemented on top of Akka actors?

I’m trying to understand how Akka Streams is implemented on top of Akka Actors.

If we take a simple example:

Source.range(0, 990).runWith(Sink.ignore(), mat);

and trace actor messages produced, we see no messages.
If we increase the value from 990 to 1000, we get 4 Resume(GraphInterpreterShell) messages sent to ActorGraphInterpreter, and that seems to repeat (for every additional 1000 entries in a range we get 4 additional messages)

For comparison, when running without auto fusing in 2.4, we see an order of magnitude more messages, including at least one onNext message for every single value in the stream.

In the documentation there is one sentence on fusing saying it reduces the number of actors by merging stages in the pipeline.

But in this case there is only a single stage in the pipeline, and the number and kind of actor messages seems to be fundamentally different with fusing.

Is there any more information on how actors and actor messages work in the current Akka streams implementation?


I’m far from exactly knowing how this works, but:

  • most of the times your streams are materialized to only one actor
  • if you use .async that is fragmanting your graph, and spliting it to multiple actors
  • these actors have an inner runstack with open/close inlet/outlet states