private void RandomDemand(Dictionary <int, SubFlowState> map, RandomDemandProperties props) { while (true) { var nextIndex = ThreadLocalRandom.Current.Next(0, map.Count); var key = map.Keys.ToArray()[nextIndex]; if (!map[key].HasDemand) { var state = map[key]; map[key] = new SubFlowState(state.Probe, true, state.FirstElement); state.Probe.Request(1); // need to verify elements that are first element in subFlow or is in nextElement buffer before // pushing next element from upstream if (state.FirstElement != null) { state.Probe.ExpectNext().ShouldBeEquivalentTo(state.FirstElement); map[key] = new SubFlowState(state.Probe, false, null); } else if (props.BlockingNextElement != null && Math.Abs(props.BlockingNextElement[0] % 100) == key) { state.Probe.ExpectNext().ShouldBeEquivalentTo(props.BlockingNextElement); props.BlockingNextElement = null; map[key] = new SubFlowState(state.Probe, false, null); } else if (props.BlockingNextElement == null) { break; } } } }
public void GroupBy_must_work_with_random_demand() { this.AssertAllStagesStopped(() => { var settings = ActorMaterializerSettings.Create(Sys).WithInputBuffer(1, 1); var materializer = Sys.Materializer(settings); var props = new RandomDemandProperties { Kit = this }; Enumerable.Range(0, 100) .ToList() .ForEach(_ => props.Probes.Add(new TaskCompletionSource <TestSubscriber.Probe <ByteString> >())); var map = new Dictionary <int, SubFlowState>(); var publisherProbe = this.CreateManualPublisherProbe <ByteString>(); var probeShape = new SinkShape <ByteString>(new Inlet <ByteString>("ProbeSink.in")); var probeSink = new ProbeSink(probeShape, props, Attributes.None); Source.FromPublisher(publisherProbe) .GroupBy(100, element => Math.Abs(element[0] % 100)) .To(new Sink <ByteString, TestSubscriber.Probe <ByteString> >(probeSink)) .Run(materializer); var upstreamSubscription = publisherProbe.ExpectSubscription(); for (var i = 1; i <= 400; i++) { var byteString = RandomByteString(10); var index = Math.Abs(byteString[0] % 100); upstreamSubscription.ExpectRequest(); upstreamSubscription.SendNext(byteString); if (map.TryGetValue(index, out var state)) { if (state.FirstElement != null) //first element in subFlow { if (!state.HasDemand) { props.BlockingNextElement = byteString; } RandomDemand(map, props); } else if (state.HasDemand) { if (props.BlockingNextElement == null) { state.Probe.ExpectNext().ShouldBeEquivalentTo(byteString); map[index] = new SubFlowState(state.Probe, false, null); RandomDemand(map, props); } else { true.ShouldBeFalse("INVALID CASE"); } } else { props.BlockingNextElement = byteString; RandomDemand(map, props); } } else { var probe = props.Probes[props.ProbesReaderTop].Task.AwaitResult(); props.ProbesReaderTop++; map[index] = new SubFlowState(probe, false, byteString); //stream automatically requests next element } } upstreamSubscription.SendComplete(); }, Materializer); }