public void A_UniqueKillSwitch_must_work_if_used_multiple_times_in_a_flow() { var t = this.SourceProbe <int>() .ViaMaterialized(KillSwitches.Single <int>(), Keep.Both) //ex is a AggregateException from the Task .Recover(ex => ex.InnerException is TestException ? -1 : Option <int> .None) .ViaMaterialized(KillSwitches.Single <int>(), Keep.Both) .ToMaterialized(this.SinkProbe <int>(), Keep.Both) .Run(Materializer); var upstream = t.Item1.Item1.Item1; var killSwitch1 = t.Item1.Item1.Item2; var killSwitch2 = t.Item1.Item2; var downstream = t.Item2; downstream.Request(1); upstream.SendNext(1); downstream.ExpectNext(1); var testException = new TestException("Abort"); killSwitch1.Abort(testException); upstream.ExpectCancellation(); downstream.RequestNext(-1); killSwitch2.Shutdown(); downstream.ExpectComplete(); }
public void Restart_stages_should_demonstrate_a_restart_with_backoff_source() { #region restart-with-backoff-source var httpClient = new HttpClient(); var restartSource = RestartSource.WithBackoff(() => { // Create a source from a task return(Source.FromTask( httpClient.GetAsync("http://example.com/eventstream") // Make a single request ) .Select(c => c.Content.ReadAsStringAsync()) .Select(c => c.Result)); }, minBackoff: TimeSpan.FromSeconds(3), maxBackoff: TimeSpan.FromSeconds(30), randomFactor: 0.2 // adds 20% "noise" to vary the intervals slightly ); #endregion #region with-kill-switch var killSwitch = restartSource .ViaMaterialized(KillSwitches.Single <string>(), Keep.Right) .ToMaterialized(Sink.ForEach <string>(evt => Console.WriteLine($"Got event: {evt}")), Keep.Left) .Run(Materializer); DoSomethingElse(); killSwitch.Shutdown(); #endregion }
public void RetryConcat_tolerate_killswitch_terminations_inside_the_flow_after_start() { var innerFlow = RetryFlow <int>().ViaMaterialized(KillSwitches.Single <Tuple <Result <int>, int> >(), Keep.Right); var t = this.SourceProbe <int>() .Select(i => Tuple.Create(i, i * i)) .ViaMaterialized(Retry.Concat(100, innerFlow, x => { if (x % 4 == 0) { return new[] { Tuple.Create(x / 2, x / 4) } } ; var sqrt = (int)Math.Sqrt(x); return(new[] { Tuple.Create(sqrt, x) }); }), Keep.Both) .ToMaterialized(this.SinkProbe <Tuple <Result <int>, int> >(), Keep.Both) .Run(Sys.Materializer()); var source = t.Item1.Item1; var killSwitch = t.Item1.Item2; var sink = t.Item2; sink.Request(99); source.SendNext(1); sink.ExpectNext().Item1.Value.Should().Be(2); source.SendNext(2); sink.ExpectNext().Item1.Value.Should().Be(2); killSwitch.Abort(FailedElement.Exception); sink.ExpectError().InnerException.Should().Be(FailedElement.Exception); }
public IDisposable CreateSubscription(long?lastProcessedCheckpoint, Action <string, Exception> errorHandler) { _errorHandler = errorHandler; var source = CreateSource(Offset.Sequence(lastProcessedCheckpoint ?? 0)) .Select(ee => (ee.Event as IDomainEvent, ee.Offset)) .Where(de => de.Item1 != null) .Batch(20, de => ImmutableList <(IDomainEvent, Offset)> .Empty.Add(de !), (list, evt) => list.Add(evt !)) .Select(de => { var(domainEvent, offset) = de.Last(); return(new Transaction { Checkpoint = ((Sequence)offset).Value, Id = EventId.New.Value, StreamId = domainEvent.GetIdentity().Value, TimeStampUtc = domainEvent.Timestamp.DateTime, Events = new List <LiquidProjections.EventEnvelope>( de .Select(pair => { var(evt, _) = pair; return new LiquidProjections.EventEnvelope { Body = evt, Headers = evt .Metadata .Select(p => Tuple.Create <string, object>(p.Key, p.Value)) .ToDictionary(t => t.Item1, t => t.Item2) }; })) }); }) .Batch(5, t => ImmutableList <Transaction> .Empty.Add(t), (list, transaction) => list.Add(transaction)) .AlsoTo(Sink.OnComplete <ImmutableList <Transaction> >( () => _isCancel.GetAndSet(true), e => { _isCancel.GetAndSet(true); errorHandler(_exceptionInfo, e); })) .ViaMaterialized(KillSwitches.Single <ImmutableList <Transaction> >(), (_, kill) => kill) .PreMaterialize(_materializer); _cancelable = source.Item1; var sinkQueue = source.Item2.RunWith(Sink.Queue <ImmutableList <Transaction> >() .WithAttributes(new Attributes(new Attributes.InputBuffer(2, 2))), _materializer); _runner = Run(sinkQueue); return(this); }
public IAsyncEnumerator <T> GetAsyncEnumerator(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return(new SinkQueueAsyncEnumerator <T>(_source .Via(cancellationToken.AsFlow <T>(cancelGracefully: true)) .ViaMaterialized(KillSwitches.Single <T>(), Keep.Right) .ToMaterialized(thisSinkQueue, Keep.Both) .Run(_materializer), cancellationToken)); }
public void RetryConcat_tolerate_killswitch_terminations_inside_the_flow_on_start() { var t = this.SourceProbe <int>() .ViaMaterialized(KillSwitches.Single <int>(), Keep.Right) .Select(i => Tuple.Create(i, i)) .Via(Retry.Concat(100, RetryFlow <int>(), x => new[] { Tuple.Create(x, x + 1) })) .ToMaterialized(this.SinkProbe <Tuple <Result <int>, int> >(), Keep.Both) .Run(Sys.Materializer()); var killSwitch = t.Item1; var sink = t.Item2; sink.Request(99); killSwitch.Abort(FailedElement.Exception); sink.ExpectError().InnerException.Should().Be(FailedElement.Exception); }
public void Unique_kill_switch_must_control_graph_completion_with_abort() { #region unique-abort var countingSrc = Source.From(Enumerable.Range(1, int.MaxValue)).Delay(1.Seconds(), DelayOverflowStrategy.Backpressure); var lastSink = Sink.Last <int>(); var(killSwitch, last) = countingSrc .ViaMaterialized(KillSwitches.Single <int>(), Keep.Right) .ToMaterialized(lastSink, Keep.Both) .Run(Materializer); var error = new Exception("boom"); killSwitch.Abort(error); last.Wait(1.Seconds()); last.Exception.Should().Be(error); #endregion }
public void Unique_kill_switch_must_control_graph_completion_with_shutdown() { #region unique-shutdown var countingSrc = Source.From(Enumerable.Range(1, int.MaxValue)).Delay(1.Seconds(), DelayOverflowStrategy.Backpressure); var lastSink = Sink.Last <int>(); var(killSwitch, last) = countingSrc .ViaMaterialized(KillSwitches.Single <int>(), Keep.Right) .ToMaterialized(lastSink, Keep.Both) .Run(Materializer); DoSomethingElse(); killSwitch.Shutdown(); AwaitCondition(() => last.IsCompleted); #endregion }
public void A_UniqueKillSwitch_must_stop_a_stream_if_requested() { var t = this.SourceProbe <int>() .ViaMaterialized(KillSwitches.Single <int>(), Keep.Both) .ToMaterialized(this.SinkProbe <int>(), Keep.Both) .Run(Materializer); var upstream = t.Item1.Item1; var killSwitch = t.Item1.Item2; var downstream = t.Item2; downstream.Request(1); upstream.SendNext(1); downstream.ExpectNext(1); killSwitch.Shutdown(); upstream.ExpectCancellation(); downstream.ExpectComplete(); }
public void A_UniqueKillSwitch_must_ignore_completion_after_already_completed() { var t = this.SourceProbe <int>() .ViaMaterialized(KillSwitches.Single <int>(), Keep.Both) .ToMaterialized(this.SinkProbe <int>(), Keep.Both) .Run(Materializer); var upstream = t.Item1.Item1; var killSwitch = t.Item1.Item2; var downstream = t.Item2; upstream.EnsureSubscription(); downstream.EnsureSubscription(); killSwitch.Shutdown(); upstream.ExpectCancellation(); downstream.ExpectComplete(); killSwitch.Abort(new TestException("Won't happen")); upstream.ExpectNoMsg(TimeSpan.FromMilliseconds(100)); downstream.ExpectNoMsg(TimeSpan.FromMilliseconds(100)); }
public void A_UniqueKillSwitch_must_fail_a_stream_if_requested() { var t = this.SourceProbe <int>() .ViaMaterialized(KillSwitches.Single <int>(), Keep.Both) .ToMaterialized(this.SinkProbe <int>(), Keep.Both) .Run(Materializer); var upstream = t.Item1.Item1; var killSwitch = t.Item1.Item2; var downstream = t.Item2; downstream.Request(1); upstream.SendNext(1); downstream.ExpectNext(1); var testException = new TestException("Abort"); killSwitch.Abort(testException); upstream.ExpectCancellation(); //is a AggregateException from the Task downstream.ExpectError().InnerException.Should().Be(testException); }
static Task <int> Stream() { // 1秒ごとに下流に各要素を放出する Source Source <int, NotUsed> source = Source.From(Enumerable.Range(1, 100)); // 上流から流れてきた要素を足し合わせる Sink Sink <int, Task <int> > sink = Sink.Aggregate <int, int>(0, (l, r) => l + r); // Stream を正常(もしくはキャンセル扱いで)に停止させるための KillSwitch Flow <int, int, UniqueKillSwitch> killSwitch = Flow.Create <int>().ViaMaterialized(KillSwitches.Single <int>(), Keep.Right); // Stream(の特定の部分)を通過する要素の流量を制御するための Throttle Flow <int, int, NotUsed> throttle = Flow.Create <int>().Throttle(1, TimeSpan.FromSeconds(1), 1, ThrottleMode.Shaping); // Stream を動作させる Actor をホストする ActorSystem ActorSystem system = ActorSystem.Create("akkanet"); // ActorSystem を使用して Stream をマテリアル化するマテリアライザ ActorMaterializer materializer = ActorMaterializer.Create(system); // Source、Sink、Throttle、KillSwitch を使用して RunnableGraph(実行可能なグラフ)を組み立てる IRunnableGraph <Tuple <UniqueKillSwitch, Task <int> > > runnable = source .Via(throttle) .ViaMaterialized(killSwitch, Keep.Right) .ToMaterialized(sink, Keep.Both); // RunnableGraph をマテリアライズして Stream を作動させる var(ks, mat) = runnable.Run(materializer); // 10秒後に KillSwitch を使用して Stream を途中で停止させる(完了扱い) ICancelable canceller = materializer.ScheduleOnce(TimeSpan.FromSeconds(10), () => { Console.WriteLine("Stream is cancelled"); ks.Shutdown(); }); // Stream 完了後に ActorSystem と ActorMaterializer を破棄する return(mat.ContinueWith(prev => { canceller.Cancel(); materializer.Dispose(); system.Dispose(); return prev.Result; })); }