예제 #1
0
        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);
                }
        }
예제 #2
0
        /// <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)));
        }
예제 #3
0
 public IObservable <TState> Scan <TState>(TState initialState, IActionReducer <TState> reducer)
 {
     return(_actions.Scan(initialState, reducer.Reduce));
 }