!str #16565 Make Flexi* limitations explicit
* remove isDemandAvailable * hide emit from CompletionHandler context * throw if more than one emit in response to an input * had to remove the OrderedMerge test/sample because emitting from CompletionHandler is currently not supported * FlexiRoute and FlexiMerge might become more capable later, see issue 16753
This commit is contained in:
parent
8095ebb3cc
commit
2740d67c61
14 changed files with 206 additions and 303 deletions
|
|
@ -312,44 +312,4 @@ class FlexiDocSpec extends AkkaSpec {
|
|||
}.run()
|
||||
}
|
||||
|
||||
"flexi route completion handling emitting element upstream completion" in {
|
||||
class ElementsAndStatus[A] extends FlexiRoute[A] {
|
||||
import FlexiRoute._
|
||||
val out = createOutputPort[A]()
|
||||
|
||||
override def createRouteLogic() = new RouteLogic[A] {
|
||||
override def outputHandles(outputCount: Int) = Vector(out)
|
||||
|
||||
// format: OFF
|
||||
//#flexiroute-completion-upstream-completed-signalling
|
||||
var buffer: List[A]
|
||||
//#flexiroute-completion-upstream-completed-signalling
|
||||
= List[A]()
|
||||
// format: ON
|
||||
|
||||
//#flexiroute-completion-upstream-completed-signalling
|
||||
|
||||
def drainBuffer(ctx: RouteLogicContext[Any]): Unit =
|
||||
while (ctx.isDemandAvailable(out) && buffer.nonEmpty) {
|
||||
ctx.emit(out, buffer.head)
|
||||
buffer = buffer.tail
|
||||
}
|
||||
|
||||
val signalStatusOnTermination = CompletionHandling(
|
||||
onUpstreamFinish = ctx => drainBuffer(ctx),
|
||||
onUpstreamFailure = (ctx, cause) => drainBuffer(ctx),
|
||||
onDownstreamFinish = (_, _) => SameState)
|
||||
//#flexiroute-completion-upstream-completed-signalling
|
||||
|
||||
override def initialCompletionHandling = signalStatusOnTermination
|
||||
|
||||
override def initialState = State[A](DemandFromAny(out)) {
|
||||
(ctx, output, element) =>
|
||||
ctx.emit(output, element)
|
||||
SameState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,6 +203,9 @@ completion or errors to the merges downstream stage.
|
|||
The state function must always return the next behaviour to be used when an element should be pulled from its upstreams,
|
||||
we use the special :class:`SameState` object which signals :class:`FlexiMerge` that no state transition is needed.
|
||||
|
||||
.. note::
|
||||
As response to an input element it is allowed to emit at most one output element.
|
||||
|
||||
Implementing Zip-like merges
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
More complex fan-in junctions may require not only multiple States but also sharing state between those states.
|
||||
|
|
@ -254,6 +257,9 @@ to this stages downstream effectively shutting down the stream.
|
|||
|
||||
In case you want to change back to the default completion handling, it is available as ``MergeLogic#defaultCompletionHandling``.
|
||||
|
||||
It is not possible to emit elements from the completion handling, since completion
|
||||
handlers may be invoked at any time (without regard to downstream demand being available).
|
||||
|
||||
Using FlexiRoute
|
||||
----------------
|
||||
Similarily to using :class:`FlexiMerge`, implementing custom fan-out stages requires extending the :class:`FlexiRoute` class
|
||||
|
|
@ -284,12 +290,15 @@ of the tuple to the ``right`` stream. Notice that since we are emitting values o
|
|||
the type parameter of this ``State[_]`` must be set to ``Any``. This type can be utilised more efficiently when a junction
|
||||
is emitting the same type of element to its downstreams e.g. in all *strictly routing* stages.
|
||||
|
||||
The state function must always return the next behaviour to be used when an element should be emited,
|
||||
The state function must always return the next behaviour to be used when an element should be emitted,
|
||||
we use the special :class:`SameState` object which signals :class:`FlexiRoute` that no state transition is needed.
|
||||
|
||||
.. warning::
|
||||
While a :class:`RouteLogic` instance *may* be stateful, the :class:`FlexiRoute` instance
|
||||
*must not* hold any mutable state, since it may be shared across several materialized ``FlowGraph`` instances.
|
||||
|
||||
.. note::
|
||||
It is only allowed to `emit` at most one element to each output in response to `onInput`, `IllegalStateException` is thrown.
|
||||
|
||||
Completion handling
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -313,14 +322,6 @@ Notice that State changes are only allowed in reaction to downstream cancellatio
|
|||
cases. This is because since there is only one upstream, there is nothing else to do than possibly flush buffered elements
|
||||
and continue with shutting down the entire stream.
|
||||
|
||||
Sometimes you may want to emit buffered or additional elements from the completion handler when the stream is shutting down.
|
||||
However calling ``ctx.emit`` is only legal when the stream we emit to *has demand available*. In normal operation,
|
||||
this is guaranteed by properly using demand conditions, however as completion handlers may be invokead at any time (without
|
||||
regard to downstream demand being available) we must explicitly check that the downstream has demand available before signalling it.
|
||||
It is not possible to emit elements from the completion handling, since completion
|
||||
handlers may be invoked at any time (without regard to downstream demand being available).
|
||||
|
||||
The completion strategy below assumes that we have implemented some kind of :class:`FlexiRoute` which buffers elements,
|
||||
yet when its upstream completes it should drain as much as possible to its downstream ``out`` output port. We use the
|
||||
``ctx.isDemandAvailable(outputHandle)`` method to make sure calling emit with the buffered elements is valid and
|
||||
complete this flushing once all demand (or the buffer) is drained:
|
||||
|
||||
.. includecode:: code/docs/stream/FlexiDocSpec.scala#flexiroute-completion-upstream-completed-signalling
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ Akka Streams currently provide these junctions:
|
|||
- ``ZipWith[A,B,...,Out]`` – (n inputs (defined upfront), 1 output), which takes a function of n inputs that, given all inputs are signalled, transforms and emits 1 output,
|
||||
- ``Zip[A,B]`` – (2 inputs, 1 output), which is a :class:`ZipWith` specialised to zipping input streams of ``A`` and ``B`` into an ``(A,B)`` tuple stream,
|
||||
- ``Concat[A]`` – (2 inputs, 1 output), which enables to concatenate streams (first consume one, then the second one), thus the order of which stream is ``first`` and which ``second`` matters,
|
||||
- ``FlexiMerge[Out]`` – (n inputs, 1 output), which enables writing custom fan out elements using a simple DSL.
|
||||
- ``FlexiMerge[Out]`` – (n inputs, 1 output), which enables writing custom fan-in elements using a simple DSL.
|
||||
|
||||
One of the goals of the FlowGraph DSL is to look similar to how one would draw a graph on a whiteboard, so that it is
|
||||
simple to translate a design from whiteboard to code and be able to relate those two. Let's illustrate this by translating
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue