示例#1
0
        public void PartitionHub_must_ensure_that_from_two_different_speed_consumers_the_slower_controls_the_rate()
        {
            this.AssertAllStagesStopped(() =>
            {
                var t = Source.Maybe <int>().ConcatMaterialized(Source.From(Enumerable.Range(1, 19)), Keep.Left)
                        .ToMaterialized(PartitionHub.Sink <int>((size, e) => e % size, 2, 1), Keep.Both)
                        .Run(Materializer);
                var firstElement = t.Item1;
                var source       = t.Item2;

                var f1 = source.Throttle(1, TimeSpan.FromMilliseconds(10), 1, ThrottleMode.Shaping)
                         .RunWith(Sink.Seq <int>(), Materializer);

                // Second cannot be overwhelmed since the first one throttles the overall rate, and second allows a higher rate
                var f2 = source.Throttle(10, TimeSpan.FromMilliseconds(10), 8, ThrottleMode.Enforcing)
                         .RunWith(Sink.Seq <int>(), Materializer);

                // Ensure subscription of Sinks. This is racy but there is no event we can hook into here.
                Thread.Sleep(100);
                // on the jvm Some 0 is used, unfortunately haven't we used Option<T> for the Maybe source
                // and therefore firstElement.SetResult(0) will complete the source without pushing an element
                // since 0 is the default value for int and if you set the result to default(T) it will ignore
                // the element and complete the source. We should probably fix this in the feature.
                firstElement.SetResult(50);

                var expectationF1 = Enumerable.Range(1, 18).Where(v => v % 2 == 0).ToList();
                expectationF1.Insert(0, 50);

                f1.AwaitResult().ShouldAllBeEquivalentTo(expectationF1);
                f2.AwaitResult().ShouldAllBeEquivalentTo(Enumerable.Range(1, 19).Where(v => v % 2 != 0));
            }, Materializer);
        }
示例#2
0
        public void Hubs_must_demonstrate_creating_a_dynamic_partition_hub_routing_to_fastest_consumer()
        {
            #region partition-hub-fastest

            // A simple producer that publishes a new "message-" every second
            Source <int, NotUsed> producer = Source.From(Enumerable.Range(0, 100));

            // Attach a PartitionHub Sink to the producer. This will materialize to a
            // corresponding Source.
            // (We need to use toMat and Keep.right since by default the materialized
            // value to the left is used)
            IRunnableGraph <Source <int, NotUsed> > runnableGraph =
                producer.ToMaterialized(PartitionHub.StatefulSink <int>(
                                            () => ((info, element) => info.ConsumerIds.Min(info.QueueSize)),
                                            startAfterNrOfConsumers: 2, bufferSize: 256), Keep.Right);

            // By running/materializing the producer, we get back a Source, which
            // gives us access to the elements published by the producer.
            Source <int, NotUsed> fromProducer = runnableGraph.Run(Materializer);

            // Print out messages from the producer in two independent consumers
            fromProducer.RunForeach(msg => Console.WriteLine("Consumer1: " + msg), Materializer);
            fromProducer.Throttle(10, TimeSpan.FromMilliseconds(100), 10, ThrottleMode.Shaping)
            .RunForeach(msg => Console.WriteLine("Consumer2: " + msg), Materializer);

            #endregion
        }
示例#3
0
        public void Hubs_must_demonstrate_creating_a_dynamic_partition_hub()
        {
            #region partition-hub

            // A simple producer that publishes a new "message-" every second
            Source <string, NotUsed> producer = Source.Tick(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), "message")
                                                .MapMaterializedValue(_ => NotUsed.Instance)
                                                .ZipWith(Source.From(Enumerable.Range(1, 100)), (msg, i) => $"{msg}-{i}");

            // Attach a PartitionHub Sink to the producer. This will materialize to a
            // corresponding Source.
            // (We need to use toMat and Keep.right since by default the materialized
            // value to the left is used)
            IRunnableGraph <Source <string, NotUsed> > runnableGraph =
                producer.ToMaterialized(PartitionHub.Sink <string>(
                                            (size, element) => Math.Abs(element.GetHashCode()) % size,
                                            startAfterNrOfConsumers: 2, bufferSize: 256), Keep.Right);

            // By running/materializing the producer, we get back a Source, which
            // gives us access to the elements published by the producer.
            Source <string, NotUsed> fromProducer = runnableGraph.Run(Materializer);

            // Print out messages from the producer in two independent consumers
            fromProducer.RunForeach(msg => Console.WriteLine("Consumer1: " + msg), Materializer);
            fromProducer.RunForeach(msg => Console.WriteLine("Consumer2: " + msg), Materializer);

            #endregion
        }
示例#4
0
        public void PartitionHub_must_properly_signal_error_to_consumer()
        {
            this.AssertAllStagesStopped(() =>
            {
                var upstream = this.CreatePublisherProbe <int>();
                var source   = Source.FromPublisher(upstream)
                               .RunWith(PartitionHub.Sink <int>((s, e) => e % s, 2, 8), Materializer);

                var downstream1 = this.CreateSubscriberProbe <int>();
                source.RunWith(Sink.FromSubscriber(downstream1), Materializer);
                var downstream2 = this.CreateSubscriberProbe <int>();
                source.RunWith(Sink.FromSubscriber(downstream2), Materializer);

                downstream1.Request(4);
                downstream2.Request(8);

                Enumerable.Range(0, 16).ForEach(i => upstream.SendNext(i));

                downstream1.ExpectNext(0, 2, 4, 6);
                downstream2.ExpectNext(1, 3, 5, 7, 9, 11, 13, 15);

                downstream1.ExpectNoMsg(TimeSpan.FromMilliseconds(100));
                downstream2.ExpectNoMsg(TimeSpan.FromMilliseconds(100));

                var failure = new TestException("Failed");
                upstream.SendError(failure);

                downstream1.ExpectError().Should().Be(failure);
                downstream2.ExpectError().Should().Be(failure);
            }, Materializer);
        }
示例#5
0
        public void PartitionHub_must_backpressure()
        {
            this.AssertAllStagesStopped(() =>
            {
                var t = this.SourceProbe <int>()
                        .ToMaterialized(PartitionHub.Sink <int>((size, e) => 0, 2, 4), Keep.Both)
                        .Run(Materializer);

                var testSource = t.Item1;
                var hub        = t.Item2;
                var probe0     = hub.RunWith(this.SinkProbe <int>(), Materializer);
                var probe1     = hub.RunWith(this.SinkProbe <int>(), Materializer);

                probe0.Request(10);
                probe1.Request(10);
                testSource.SendNext(0);
                probe0.ExpectNext(0);
                testSource.SendNext(1);
                probe0.ExpectNext(1);
                testSource.SendNext(2);
                probe0.ExpectNext(2);
                testSource.SendNext(3);
                probe0.ExpectNext(3);
                testSource.SendNext(4);
                probe0.ExpectNext(4);

                testSource.SendComplete();
                probe0.ExpectComplete();
                probe1.ExpectComplete();
            }, Materializer);
        }
示例#6
0
 public void PartitionHub_must_work_in_the_happy_case_with_one_stream()
 {
     this.AssertAllStagesStopped(() =>
     {
         var items  = Enumerable.Range(1, 10).ToList();
         var source = Source.From(items)
                      .RunWith(PartitionHub.Sink <int>((size, e) => 0, 0, 8), Materializer);
         var result = source.RunWith(Sink.Seq <int>(), Materializer).AwaitResult();
         result.ShouldAllBeEquivalentTo(items);
     }, Materializer);
 }
示例#7
0
        public void PartitionHub_must_properly_signal_completion_to_consumers_arriving_after_producer_finished()
        {
            this.AssertAllStagesStopped(() =>
            {
                var source = Source.Empty <int>().RunWith(PartitionHub.Sink <int>((s, e) => e % s, 0), Materializer);
                // Wait enough so the Hub gets the completion. This is racy, but this is fine because both
                // cases should work in the end
                Thread.Sleep(50);

                source.RunWith(Sink.Seq <int>(), Materializer).AwaitResult().Should().BeEmpty();
            }, Materializer);
        }
示例#8
0
        public void PartitionHub_must_properly_signal_error_to_consumer_arriving_after_producer_finished()
        {
            this.AssertAllStagesStopped(() =>
            {
                var failure = new TestException("Fail!");
                var source  = Source.Failed <int>(failure).RunWith(PartitionHub.Sink <int>((s, e) => 0, 0), Materializer);
                // Wait enough so the Hub gets the completion. This is racy, but this is fine because both
                // cases should work in the end
                Thread.Sleep(50);

                Action a = () => source.RunWith(Sink.Seq <int>(), Materializer).AwaitResult();
                a.ShouldThrow <TestException>().WithMessage("Fail!");
            }, Materializer);
        }
示例#9
0
 public void PartitionHub_must_work_in_the_happy_case_with_two_streams()
 {
     this.AssertAllStagesStopped(() =>
     {
         var source = Source.From(Enumerable.Range(0, 10))
                      .RunWith(PartitionHub.Sink <int>((size, e) => e % size, 2, 8), Materializer);
         var result1 = source.RunWith(Sink.Seq <int>(), Materializer);
         // it should not start publishing until startAfterNrOfConsumers = 2
         Thread.Sleep(50);
         var result2 = source.RunWith(Sink.Seq <int>(), Materializer);
         result1.AwaitResult().ShouldAllBeEquivalentTo(new[] { 0, 2, 4, 6, 8 });
         result2.AwaitResult().ShouldAllBeEquivalentTo(new[] { 1, 3, 5, 7, 9 });
     }, Materializer);
 }
示例#10
0
        public void PartitionHub_must_be_able_to_use_as_fastest_consumer_router()
        {
            this.AssertAllStagesStopped(() =>
            {
                var items  = Enumerable.Range(0, 999).ToList();
                var source = Source.From(items)
                             .RunWith(
                    PartitionHub.StatefulSink <int>(() => ((info, i) => info.ConsumerIds.Min(info.QueueSize)), 2, 4),
                    Materializer);
                var result1 = source.RunWith(Sink.Seq <int>(), Materializer);
                var result2 = source.Throttle(10, TimeSpan.FromMilliseconds(100), 10, ThrottleMode.Shaping)
                              .RunWith(Sink.Seq <int>(), Materializer);

                result1.AwaitResult().Count.ShouldBeGreaterThan(result2.AwaitResult().Count);
            }, Materializer);
        }
示例#11
0
        public void PartitionHub_must_route_evenly()
        {
            this.AssertAllStagesStopped(() =>
            {
                var t = this.SourceProbe <int>()
                        .ToMaterialized(PartitionHub.Sink <int>((size, e) => e % size, 2, 8), Keep.Both)
                        .Run(Materializer);

                var testSource = t.Item1;
                var hub        = t.Item2;
                var probe0     = hub.RunWith(this.SinkProbe <int>(), Materializer);
                var probe1     = hub.RunWith(this.SinkProbe <int>(), Materializer);

                probe0.Request(3);
                probe1.Request(10);
                testSource.SendNext(0);
                probe0.ExpectNext(0);
                testSource.SendNext(1);
                probe1.ExpectNext(1);

                testSource.SendNext(2);
                testSource.SendNext(3);
                testSource.SendNext(4);
                probe0.ExpectNext(2);
                probe1.ExpectNext(3);
                probe0.ExpectNext(4);

                // probe1 has not requested more
                testSource.SendNext(5);
                testSource.SendNext(6);
                testSource.SendNext(7);
                probe1.ExpectNext(5);
                probe1.ExpectNext(7);
                probe0.ExpectNoMsg(TimeSpan.FromMilliseconds(50));
                probe0.Request(10);
                probe0.ExpectNext(6);

                testSource.SendComplete();
                probe0.ExpectComplete();
                probe1.ExpectComplete();
            }, Materializer);
        }
示例#12
0
        public void PartitionHub_must_remeber_completion_for_materialisations_after_completion()
        {
            var t = this.SourceProbe <NotUsed>().ToMaterialized(PartitionHub.Sink <NotUsed>((s, e) => 0, 0), Keep.Both)
                    .Run(Materializer);
            var sourceProbe = t.Item1;
            var source      = t.Item2;
            var sinkProbe   = source.RunWith(this.SinkProbe <NotUsed>(), Materializer);

            sourceProbe.SendComplete();

            sinkProbe.Request(1);
            sinkProbe.ExpectComplete();

            // Materialize a second time. There was a race here, where we managed to enqueue our Source registration just
            // immediately before the Hub shut down.
            var sink2Probe = source.RunWith(this.SinkProbe <NotUsed>(), Materializer);

            sink2Probe.Request(1);
            sink2Probe.ExpectComplete();
        }
示例#13
0
 public void PartitionHub_must_be_able_to_use_as_rount_robin_router()
 {
     this.AssertAllStagesStopped(() =>
     {
         var source = Source.From(Enumerable.Range(0, 10))
                      .RunWith(PartitionHub.StatefulSink <int>(() =>
         {
             var n = 0L;
             return((info, e) =>
             {
                 n++;
                 return info.ConsumerByIndex((int)n % info.Size);
             });
         }, 2, 8), Materializer);
         var result1 = source.RunWith(Sink.Seq <int>(), Materializer);
         var result2 = source.RunWith(Sink.Seq <int>(), Materializer);
         result1.AwaitResult().ShouldAllBeEquivalentTo(new[] { 1, 3, 5, 7, 9 });
         result2.AwaitResult().ShouldAllBeEquivalentTo(new[] { 0, 2, 4, 6, 8 });
     }, Materializer);
 }
示例#14
0
        public void Hubs_must_demonstrate_creating_a_dynamic_steful_partition_hub()
        {
            #region partition-hub-stateful

            // A simple producer that publishes a new "message-" every second
            Source <string, NotUsed> producer = Source.Tick(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), "message")
                                                .MapMaterializedValue(_ => NotUsed.Instance)
                                                .ZipWith(Source.From(Enumerable.Range(1, 100)), (msg, i) => $"{msg}-{i}");

            // New instance of the partitioner function and its state is created
            // for each materialization of the PartitionHub.
            Func <PartitionHub.IConsumerInfo, string, long> RoundRobbin()
            {
                var i = -1L;

                return((info, element) =>
                {
                    i++;
                    return info.ConsumerByIndex((int)(i % info.Size));
                });
            }

            // Attach a PartitionHub Sink to the producer. This will materialize to a
            // corresponding Source.
            // (We need to use toMat and Keep.right since by default the materialized
            // value to the left is used)
            IRunnableGraph <Source <string, NotUsed> > runnableGraph =
                producer.ToMaterialized(PartitionHub.StatefulSink(RoundRobbin,
                                                                  startAfterNrOfConsumers: 2, bufferSize: 256), Keep.Right);

            // By running/materializing the producer, we get back a Source, which
            // gives us access to the elements published by the producer.
            Source <string, NotUsed> fromProducer = runnableGraph.Run(Materializer);

            // Print out messages from the producer in two independent consumers
            fromProducer.RunForeach(msg => Console.WriteLine("Consumer1: " + msg), Materializer);
            fromProducer.RunForeach(msg => Console.WriteLine("Consumer2: " + msg), Materializer);

            #endregion
        }
示例#15
0
        public void PartitionHub_must_route_unevenly()
        {
            this.AssertAllStagesStopped(() =>
            {
                var t = this.SourceProbe <int>()
                        .ToMaterialized(PartitionHub.Sink <int>((size, e) => (e % 3) % 2, 2, 8), Keep.Both)
                        .Run(Materializer);

                var testSource = t.Item1;
                var hub        = t.Item2;
                var probe0     = hub.RunWith(this.SinkProbe <int>(), Materializer);
                var probe1     = hub.RunWith(this.SinkProbe <int>(), Materializer);

                // (_ % 3) % 2
                // 0 => 0
                // 1 => 1
                // 2 => 0
                // 3 => 0
                // 4 => 1

                probe0.Request(10);
                probe1.Request(10);
                testSource.SendNext(0);
                probe0.ExpectNext(0);
                testSource.SendNext(1);
                probe1.ExpectNext(1);
                testSource.SendNext(2);
                probe0.ExpectNext(2);
                testSource.SendNext(3);
                probe0.ExpectNext(3);
                testSource.SendNext(4);
                probe1.ExpectNext(4);

                testSource.SendComplete();
                probe0.ExpectComplete();
                probe1.ExpectComplete();
            }, Materializer);
        }