Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Akka Streams: What does Mat represents in Source[out, Mat]

Tags:

In Akka streams what does Mat in Source[Out, Mat] or Sink[In, Mat] represent. When will it actually be used?

like image 875
Somasundaram Sekar Avatar asked Sep 27 '16 14:09

Somasundaram Sekar


People also ask

Which are the 3 main components in a Akka stream?

Akka streams consist of 3 major components in it – Source, Flow, Sink – and any non-cyclical stream consist of at least 2 components Source, Sink and any number of Flow element. Here we can say Source and Sink are the special cases of Flow. Source – this is the Source of data. It has exactly one output.

What is source in Akka?

The starting point is called Source and can be a collection, an iterator, a block of code which is evaluated repeatedly or a org.reactivestreams.Publisher. A flow with an attached input and open output is also a Source. A flow may also be defined without an attached input or output and that is then a Flow.

How does Akka stream work?

Akka Streams components work with a demand-based protocol. In other words, data flows through the graph as a response to demand from receivers. Producers then comply and send more elements downstream. A second (transparent) protocol kicks in when production of elements is faster than demand.

What is actor Materializer?

Actor Materializer Lifecycle. The Materializer is a component that is responsible for turning the stream blueprint into a running stream and emitting the “materialized value”.


1 Answers

The Mat type parameter represents the type of the materialized value of this stream.

Remember that in Akka Source, Flow, Sink (well, all graphs) are just blueprints - they do not do any processing by themselves, they only describe how the stream should be constructed. The process of turning these blueprints into a working stream with live data is called materialization.

The core method for materializing a stream is called run(), and it is defined in the RunnableGraph class. All other methods to run a stream (e.g. runWith on a Sink or Source) eventually delegate to this method. You can see that this method returns Mat. That is, materializing a stream yields a materialized value.

For example, there is a sink which combines all the values in a stream into a single value, it is constructed with Sink.fold. But how do you get this value? Since the stream is running asynchronously, a natural type for this value would be Future[T], where T is the type of the fold accumulator. Turns out, Sink.fold returns Sink[In, Future[T]], that is, this Future[T] is its materialized value, therefore, when you materialize it, you get an instance of Future[T] which you can then use in your own code for further processing: it will complete with a value if the stream completes correctly and it will complete with a failure if the stream has terminated with an exception.

Each part of the graph you construct by combining sinks, sources and flows (and other kinds of graphs) may potentially have an associated materialized value. For example, materialized value of Source.queue is a queue which you can use to push elements to the stream once it is materialized, and materialized value of Sink.actorSubscriber is an ActorRef which you can use to interact with the actor (which is created by the materializer when the stream is materialized). On the other hand, there is Flow.map which is a flow with no meaningful materialized value (there is nothing you can externally control when you only apply a pure function to a stream), therefore its materialized value is NotUsed, which is essentially Unit.

Naturally, it is possible for different parts of stream contain their own materialized value. For example, nothing prevents you from combining Source.queue and Sink.fold. But RunnableGraph.run() can only return one materialized value. To overcome this, there are usually two variants of combining methods on Sinks, Flows, and other graphs, usually called like method and methodMat, e.g. to and toMat. The second variant allows you to choose how to combine materialized values of the streams you are joining. For example, you can put them into a tuple to get them both:

val (queue, future) = Source.queue[Int](10, OverflowStrategy.fail)
  .map(x => x + 10)
  .toMat(Sink.fold(0)(_ + _))(Keep.both)
  .run()

Default combination methods (without the Mat suffix) usually choose either the left or the right materialized value, depending on what would be the most natural thing to do for this particular kind of stream. The Keep object contains convenience methods which return either left, right or both arguments, specifically for the purpose of using them as the last argument for *Mat methods, but nothing prevents you from writing your own combining function.

like image 139
Vladimir Matveev Avatar answered Oct 19 '22 04:10

Vladimir Matveev