public void SwitchOnTest() { SourceList <int> one = new SourceList <int>(); SourceList <int> other = new SourceList <int>(); one.AddRange(new[] { 1, 3 }); other.AddRange(new[] { 2, 4 }); using (var switcher = new BehaviorSubject <Unit>(Unit.Default)) using (var aggregate = switcher.Scan(other, (agg, cur) => agg == one ? other : one).Switch().AsAggregator()) { CollectionAssert.AreEqual(new [] { 1, 3 }, aggregate.Data.Items); switcher.OnNext(Unit.Default); CollectionAssert.AreEqual(new [] { 2, 4 }, aggregate.Data.Items); one.Add(5); CollectionAssert.AreEqual(new [] { 2, 4 }, aggregate.Data.Items); switcher.OnNext(Unit.Default); CollectionAssert.AreEqual(new [] { 1, 3, 5 }, aggregate.Data.Items); other.Remove(4); CollectionAssert.AreEqual(new [] { 1, 3, 5 }, aggregate.Data.Items); one.Remove(3); CollectionAssert.AreEqual(new [] { 1, 5 }, aggregate.Data.Items); } }
/// <summary> /// Create a new consumer using the specified configuration. See @ConsumerConfiguration /// </summary> /// <param name="consumerConfig"></param> public Consumer(ConsumerConfiguration consumerConfig) { Configuration = consumerConfig; _cluster = new Cluster(consumerConfig.SeedBrokers); _cluster.OnThreadHang += e => OnMessageArrivedInput.OnError(e); // Low/high watermark implementation FlowControl = _flowControlInput. Scan(1, (last, current) => { if (current < Configuration.LowWatermark) { return(1); } if (current > Configuration.HighWatermark) { return(-1); } return(last); // While in between watermarks, carry over previous on/off state }). DistinctUntilChanged(). Select(i => i > 0). Do(f => FlowControlEnabled = f). Do(f => EtwTrace.Log.ConsumerFlowControl(f ? 1 : 0)); var onMessage = Observable.Create <ReceivedMessage>(observer => { if (Interlocked.CompareExchange(ref _haveSubscriber, 1, 0) == 1) { throw new InvalidOperationException("Only one subscriber is allowed. Use OnMessageArrived.Publish().RefCount()"); } // Relay messages from partition to consumer's output // Ensure that only single subscriber is allowed because it is important to count // outstanding messaged consumed by user OnMessageArrivedInput.Subscribe(observer); // // It is not possible to wait for completion of partition resolution process, so start it asynchronously. // This means that OnMessageArrived.Subscribe() will complete when consumer is not actually connected yet. // Task.Run(async() => { try { await _cluster.ConnectAsync(); await SubscribeClient(); EtwTrace.Log.ConsumerStarted(GetHashCode(), Topic); _connectionComplete.TrySetResult(true); // check that we actually got any partitions subscribed if (_topicPartitions.Count == 0) { OnMessageArrivedInput.OnCompleted(); } } catch (Exception e) { _connectionComplete.TrySetException(e); OnMessageArrivedInput.OnError(e); } }); // upon unsubscribe return(Disposable.Create(() => _partitionsSubscription.Values.ForEach(s => s.Dispose()))); }); if (Configuration.UseFlowControl) { // Increment counter of messages sent for processing onMessage = onMessage.Do(msg => { var count = Interlocked.Increment(ref _outstandingMessageProcessingCount); _flowControlInput.OnNext(count); }); } // handle stop condition onMessage = onMessage.Do(message => { // check if this partition is done per the condition passed in configuration. If so, unsubscribe it. bool partitionDone = (Configuration.StopPosition.IsPartitionConsumingComplete(message)); IDisposable partitionSubscription; if (partitionDone && _partitionsSubscription.TryGetValue(message.Partition, out partitionSubscription)) { _partitionsSubscription.Remove(message.Partition); // calling Dispose here will cause the OnTopicPartitionComplete method to be called when it is completed. partitionSubscription.Dispose(); } }); OnMessageArrived = onMessage. // isolate driver from user code misbehave ObserveOn(Configuration.OutgoingScheduler); // If permanent error within any single partition, fail the whole consumer (intentionally). // Do not try to keep going (failfast principle). _cluster.PartitionStateChanges. Where(s => s.ErrorCode.IsPermanentFailure()). Subscribe(state => OnMessageArrivedInput.OnError(new PartitionFailedException(state.Topic, state.PartitionId, state.ErrorCode))); }
public IObservable <TState> Scan <TState>(TState initialState, IActionReducer <TState> reducer) { return(_actions.Scan(initialState, reducer.Reduce)); }