Exemplo n.º 1
0
            public ConsumerConfigurationBuilder(ConsumerConfiguration cc)
            {
                if (cc == null)
                {
                    return;
                }

                _deliverPolicy = cc._deliverPolicy;
                _ackPolicy     = cc._ackPolicy;
                _replayPolicy  = cc._replayPolicy;

                _description     = cc.Description;
                _durable         = cc.Durable;
                _deliverSubject  = cc.DeliverSubject;
                _deliverGroup    = cc.DeliverGroup;
                _filterSubject   = cc.FilterSubject;
                _sampleFrequency = cc.SampleFrequency;

                _startTime         = cc.StartTime;
                _ackWait           = cc.AckWait;
                _idleHeartbeat     = cc.IdleHeartbeat;
                _maxExpires        = cc.MaxExpires;
                _inactiveThreshold = cc.InactiveThreshold;

                _startSeq       = cc._startSeq;
                _maxDeliver     = cc._maxDeliver;
                _rateLimitBps   = cc._rateLimitBps;
                _maxAckPending  = cc._maxAckPending;
                _maxPullWaiting = cc._maxPullWaiting;
                _maxBatch       = cc._maxBatch;
                _maxBytes       = cc._maxBytes;
                _flowControl    = cc._flowControl;
                _headersOnly    = cc._headersOnly;
                _backoff        = new List <Duration>(cc.Backoff);
            }
Exemplo n.º 2
0
 public ConsumerInfo AddOrUpdateConsumer(string streamName, ConsumerConfiguration config)
 {
     Validator.ValidateStreamName(streamName, true);
     Validator.ValidateNotNull(config, nameof(config));
     Validator.ValidateNotNull(config.Durable, nameof(config.Durable)); // durable name is required when creating consumers
     return(AddOrUpdateConsumerInternal(streamName, config));
 }
Exemplo n.º 3
0
 public OrderedMessageManager(JetStream js, string stream, ConsumerConfiguration serverCc, bool syncMode)
 {
     _js                 = js;
     _stream             = stream;
     _serverCc           = serverCc;
     _syncMode           = syncMode;
     lastStreamSeq       = ulong.MaxValue;
     expectedConsumerSeq = 1; // always starts at 1
 }
Exemplo n.º 4
0
        internal ConsumerInfo AddOrUpdateConsumerInternal(string streamName, ConsumerConfiguration config)
        {
            string subj = string.IsNullOrWhiteSpace(config.Durable)
                ? string.Format(JetStreamConstants.JsapiConsumerCreate, streamName)
                : string.Format(JetStreamConstants.JsapiDurableCreate, streamName, config.Durable);

            var ccr = new ConsumerCreateRequest(streamName, config);
            var m   = RequestResponseRequired(subj, ccr.Serialize(), Timeout);

            return(new ConsumerInfo(m, true));
        }
Exemplo n.º 5
0
 private void Init(JSONNode ciNode)
 {
     Stream = ciNode[ApiConstants.StreamName].Value;
     ConsumerConfiguration = new ConsumerConfiguration(ciNode[ApiConstants.Config]);
     Name           = ciNode[ApiConstants.Name].Value;
     Created        = AsDate(ciNode[ApiConstants.Created]);
     Delivered      = new SequenceInfo(ciNode[ApiConstants.Delivered]);
     AckFloor       = new SequenceInfo(ciNode[ApiConstants.AckFloor]);
     NumPending     = ciNode[ApiConstants.NumPending].AsUlong;
     NumWaiting     = ciNode[ApiConstants.NumWaiting].AsLong;
     NumAckPending  = ciNode[ApiConstants.NumAckPending].AsLong;
     NumRedelivered = ciNode[ApiConstants.NumRedelivered].AsLong;
     ClusterInfo    = ClusterInfo.OptionalInstance(ciNode[ApiConstants.Cluster]);
     PushBound      = ciNode[ApiConstants.PushBound].AsBool;
 }
Exemplo n.º 6
0
        internal PushMessageManager(Connection conn, SubscribeOptions so,
                                    ConsumerConfiguration cc, bool queueMode, bool syncMode)
        {
            this.conn       = conn;
            SyncMode        = syncMode;
            QueueMode       = queueMode;
            LastStreamSeq   = 0;
            LastConsumerSeq = 0;
            LastMsgReceived = -1;

            if (queueMode)
            {
                Hb = false;
                Fc = false;
                IdleHeartbeatSetting = 0;
                AlarmPeriodSetting   = 0;
            }
            else
            {
                IdleHeartbeatSetting = cc.IdleHeartbeat?.Millis ?? 0;
                if (IdleHeartbeatSetting <= 0)
                {
                    AlarmPeriodSetting = 0;
                    Hb = false;
                }
                else
                {
                    int mat = so.MessageAlarmTime;
                    if (mat < IdleHeartbeatSetting)
                    {
                        AlarmPeriodSetting = IdleHeartbeatSetting * Threshold;
                    }
                    else
                    {
                        AlarmPeriodSetting = mat;
                    }
                    Hb = true;
                }
                Fc = Hb && cc.FlowControl; // can't have fc w/o heartbeat
            }
        }
Exemplo n.º 7
0
        internal override bool Manage(Msg msg)
        {
            if (msg == null)
            {
                return(false);
            }

            ulong receivedConsumerSeq = msg.MetaData.ConsumerSequence;

            if (expectedConsumerSeq != receivedConsumerSeq)
            {
                try
                {
                    expectedConsumerSeq = 1; // consumer always starts with consumer sequence 1

                    // 1. shutdown the managers, for instance stops heartbeat timers
                    MessageManager[] managers = {};
                    if (Sub is JetStreamAbstractSyncSubscription syncSub)
                    {
                        managers = syncSub.messageManagers;
                    }
                    else if (Sub is JetStreamPushAsyncSubscription asyncSub)
                    {
                        managers = asyncSub.messageManagers;
                    }

                    Shutdown(managers);

                    // 2. re-subscribe. This means kill the sub then make a new one
                    //    New sub needs a new deliver subject
                    string newDeliverSubject = Sub.Connection.NewInbox();
                    Sub.reSubscribe(newDeliverSubject);

                    // 3. make a new consumer using the same deliver subject but
                    //    with a new starting point
                    ConsumerConfiguration userCc = ConsumerConfiguration.Builder(_serverCc)
                                                   .WithDeliverPolicy(DeliverPolicy.ByStartSequence)
                                                   .WithDeliverSubject(newDeliverSubject)
                                                   .WithStartSequence(lastStreamSeq + 1)
                                                   .WithStartTime(DateTime.MinValue) // clear start time in case it was originally set
                                                   .Build();

                    _js.AddOrUpdateConsumerInternal(_stream, userCc);

                    // 4. re start the managers.
                    Startup(Sub, managers);
                }
                catch (Exception e)
                {
                    NATSBadSubscriptionException bad =
                        new NATSBadSubscriptionException("Ordered subscription fatal error: " + e.Message);
                    ((Connection)_js.Conn).ScheduleErrorEvent(this, bad, Sub);
                    if (_syncMode)
                    {
                        throw bad;
                    }
                }

                return(true);
            }

            lastStreamSeq = msg.MetaData.StreamSequence;
            expectedConsumerSeq++;
            return(false);
        }
Exemplo n.º 8
0
 internal ConsumerCreateRequest(string streamName, ConsumerConfiguration config)
 {
     StreamName = streamName;
     Config     = config;
 }
Exemplo n.º 9
0
 public static ConsumerConfigurationBuilder Builder(ConsumerConfiguration cc)
 {
     return(new ConsumerConfigurationBuilder(cc));
 }
Exemplo n.º 10
0
        internal IList <string> GetChanges(ConsumerConfiguration server)
        {
            IList <string> changes = new List <string>();

            if (_deliverPolicy != null && _deliverPolicy != server.DeliverPolicy)
            {
                changes.Add("DeliverPolicy");
            }
            if (_ackPolicy != null && _ackPolicy != server.AckPolicy)
            {
                changes.Add("AckPolicy");
            }
            if (_replayPolicy != null && _replayPolicy != server.ReplayPolicy)
            {
                changes.Add("ReplayPolicy");
            }

            if (_flowControl != null && _flowControl != server.FlowControl)
            {
                changes.Add("FlowControl");
            }
            if (_headersOnly != null && _headersOnly != server.HeadersOnly)
            {
                changes.Add("HeadersOnly");
            }

            if (_startSeq != null && !_startSeq.Equals(server.StartSeq))
            {
                changes.Add("StartSequence");
            }
            if (_rateLimitBps != null && !_rateLimitBps.Equals(server.RateLimitBps))
            {
                changes.Add("RateLimitBps");
            }

            if (_maxDeliver != null && !_maxDeliver.Equals(server.MaxDeliver))
            {
                changes.Add("MaxDeliver");
            }
            if (_maxAckPending != null && !_maxAckPending.Equals(server.MaxAckPending))
            {
                changes.Add("MaxAckPending");
            }
            if (_maxPullWaiting != null && !_maxPullWaiting.Equals(server.MaxPullWaiting))
            {
                changes.Add("MaxPullWaiting");
            }
            if (_maxBatch != null && !_maxBatch.Equals(server.MaxBatch))
            {
                changes.Add("MaxBatch");
            }
            if (_maxBytes != null && !_maxBytes.Equals(server.MaxBytes))
            {
                changes.Add("MaxBytes");
            }

            if (AckWait != null && !AckWait.Equals(server.AckWait))
            {
                changes.Add("AckWait");
            }
            if (IdleHeartbeat != null && !IdleHeartbeat.Equals(server.IdleHeartbeat))
            {
                changes.Add("IdleHeartbeat");
            }
            if (MaxExpires != null && !MaxExpires.Equals(server.MaxExpires))
            {
                changes.Add("MaxExpires");
            }
            if (InactiveThreshold != null && !InactiveThreshold.Equals(server.InactiveThreshold))
            {
                changes.Add("InactiveThreshold");
            }

            RecordWouldBeChange(StartTime, server.StartTime, "StartTime", changes);

            RecordWouldBeChange(FilterSubject, server.FilterSubject, "FilterSubject", changes);
            RecordWouldBeChange(Description, server.Description, "Description", changes);
            RecordWouldBeChange(SampleFrequency, server.SampleFrequency, "SampleFrequency", changes);
            RecordWouldBeChange(DeliverSubject, server.DeliverSubject, "DeliverSubject", changes);
            RecordWouldBeChange(DeliverGroup, server.DeliverGroup, "DeliverGroup", changes);

            if (!Backoff.SequenceEqual(server.Backoff))
            {
                changes.Add("Backoff");
            }
            return(changes);
        }
Exemplo n.º 11
0
        protected SubscribeOptions(ISubscribeOptionsBuilder builder, bool pull, bool ordered, string deliverSubject, string deliverGroup)
        {
            Pull             = pull;
            Bind             = builder.Bind;
            Ordered          = ordered;
            MessageAlarmTime = builder.MessageAlarmTime;

            if (Ordered && Bind)
            {
                throw JsSoOrderedNotAllowedWithBind.Instance();
            }

            Stream = Validator.ValidateStreamName(builder.Stream, builder.Bind);

            string durable = Validator.ValidateMustMatchIfBothSupplied(builder.Durable, builder.Cc?.Durable, JsSoDurableMismatch);

            durable = Validator.ValidateDurable(durable, builder.Bind);

            deliverGroup = Validator.ValidateMustMatchIfBothSupplied(deliverGroup, builder.Cc?.DeliverGroup, JsSoDeliverGroupMismatch);

            deliverSubject = Validator.ValidateMustMatchIfBothSupplied(deliverSubject, builder.Cc?.DeliverSubject, JsSoDeliverSubjectGroupMismatch);

            if (Ordered)
            {
                Validator.ValidateNotSupplied(deliverGroup, JsSoOrderedNotAllowedWithDeliverGroup);
                Validator.ValidateNotSupplied(durable, JsSoOrderedNotAllowedWithDurable);
                Validator.ValidateNotSupplied(deliverSubject, JsSoOrderedNotAllowedWithDeliverSubject);
                long hb = DefaultOrderedHeartbeat;

                if (builder.Cc != null)
                {
                    // want to make sure they didn't set it or they didn't set it to something other than none
                    if (builder.Cc._ackPolicy != null && builder.Cc._ackPolicy != AckPolicy.None)
                    {
                        throw JsSoOrderedRequiresAckPolicyNone.Instance();
                    }
                    if (builder.Cc.MaxDeliver > 1)
                    {
                        throw JsSoOrderedRequiresMaxDeliver.Instance();
                    }

                    Duration ccHb = builder.Cc.IdleHeartbeat;
                    if (ccHb != null && ccHb.Millis > hb)
                    {
                        hb = ccHb.Millis;
                    }
                }
                ConsumerConfiguration = ConsumerConfiguration.Builder(builder.Cc)
                                        .WithAckPolicy(AckPolicy.None)
                                        .WithMaxDeliver(1)
                                        .WithFlowControl(hb)
                                        .WithAckWait(Duration.OfHours(22))
                                        .Build();
            }
            else
            {
                ConsumerConfiguration = ConsumerConfiguration.Builder(builder.Cc)
                                        .WithDurable(durable)
                                        .WithDeliverSubject(deliverSubject)
                                        .WithDeliverGroup(deliverGroup)
                                        .Build();
            }
        }
Exemplo n.º 12
0
 /// <summary>
 /// Set the ConsumerConfiguration
 /// </summary>
 /// <param name="configuration">the ConsumerConfiguration object</param>
 /// <returns>The builder</returns>
 public TB WithConfiguration(ConsumerConfiguration configuration)
 {
     _config = configuration;
     return(GetThis());
 }
Exemplo n.º 13
0
        Subscription CreateSubscription(string subject, string queueName,
                                        EventHandler <MsgHandlerEventArgs> userHandler, bool autoAck,
                                        PushSubscribeOptions pushSubscribeOptions,
                                        PullSubscribeOptions pullSubscribeOptions)
        {
            // 1. Prepare for all the validation
            bool isPullMode = pullSubscribeOptions != null;

            SubscribeOptions      so;
            string                stream;
            string                qgroup;
            ConsumerConfiguration userCC;

            if (isPullMode)
            {
                so     = pullSubscribeOptions; // options must have already been checked to be non null
                stream = pullSubscribeOptions.Stream;

                userCC = so.ConsumerConfiguration;

                qgroup = null; // just to make compiler happy both paths set variable
                ValidateNotSupplied(userCC.DeliverGroup, JsSubPullCantHaveDeliverGroup);
                ValidateNotSupplied(userCC.DeliverSubject, JsSubPullCantHaveDeliverSubject);
            }
            else
            {
                so     = pushSubscribeOptions ?? DefaultPushOpts;
                stream = so.Stream; // might be null, that's ok (see directBind)

                userCC = so.ConsumerConfiguration;

                if (!userCC.MaxPullWaiting.Equals(ConsumerConfiguration.IntUnset))
                {
                    throw JsSubPushCantHaveMaxPullWaiting.Instance();
                }
                if (!userCC.MaxBatch.Equals(ConsumerConfiguration.IntUnset))
                {
                    throw JsSubPushCantHaveMaxBatch.Instance();
                }
                if (!userCC.MaxBytes.Equals(ConsumerConfiguration.IntUnset))
                {
                    throw JsSubPushCantHaveMaxBytes.Instance();
                }

                // figure out the queue name
                qgroup = ValidateMustMatchIfBothSupplied(userCC.DeliverGroup, queueName, JsSubQueueDeliverGroupMismatch);
                if (so.Ordered && qgroup != null)
                {
                    throw JsSubOrderedNotAllowOnQueues.Instance();
                }
            }

            // 2A. Flow Control / heartbeat not always valid
            if (userCC.FlowControl || userCC.IdleHeartbeat != null && userCC.IdleHeartbeat.Millis > 0)
            {
                if (isPullMode)
                {
                    throw JsSubFcHbNotValidPull.Instance();
                }
                if (qgroup != null)
                {
                    throw JsSubFcHbHbNotValidQueue.Instance();
                }
            }

            // 2B. Did they tell me what stream? No? look it up.
            if (string.IsNullOrWhiteSpace(stream))
            {
                stream = LookupStreamBySubject(subject);
                if (stream == null)
                {
                    throw JsSubNoMatchingStreamForSubject.Instance();
                }
            }

            ConsumerConfiguration serverCC = null;
            string consumerName            = userCC.Durable;
            string inboxDeliver            = userCC.DeliverSubject;

            // 3. Does this consumer already exist?
            if (consumerName != null)
            {
                ConsumerInfo serverInfo = LookupConsumerInfo(stream, consumerName);

                if (serverInfo != null)   // the consumer for that durable already exists
                {
                    serverCC = serverInfo.ConsumerConfiguration;

                    // check to see if the user sent a different version than the server has
                    // modifications are not allowed
                    IList <string> changes = userCC.GetChanges(serverCC);
                    if (changes.Count > 0)
                    {
                        throw JsSubExistingConsumerCannotBeModified.Instance($"[{string.Join(",", changes)}]");
                    }

                    if (isPullMode)
                    {
                        if (!string.IsNullOrWhiteSpace(serverCC.DeliverSubject))
                        {
                            throw JsSubConsumerAlreadyConfiguredAsPush.Instance();
                        }
                    }
                    else if (string.IsNullOrWhiteSpace(serverCC.DeliverSubject))
                    {
                        throw JsSubConsumerAlreadyConfiguredAsPull.Instance();
                    }

                    if (string.IsNullOrWhiteSpace(serverCC.DeliverGroup))
                    {
                        // lookedUp was null/empty, means existing consumer is not a queue consumer
                        if (qgroup == null)
                        {
                            // ok fine, no queue requested and the existing consumer is also not a queue consumer
                            // we must check if the consumer is in use though
                            if (serverInfo.PushBound)
                            {
                                throw JsSubConsumerAlreadyBound.Instance();
                            }
                        }
                        else   // else they requested a queue but this durable was not configured as queue
                        {
                            throw JsSubExistingConsumerNotQueue.Instance();
                        }
                    }
                    else if (qgroup == null)
                    {
                        throw JsSubExistingConsumerIsQueue.Instance();
                    }
                    else if (!serverCC.DeliverGroup.Equals(qgroup))
                    {
                        throw JsSubExistingQueueDoesNotMatchRequestedQueue.Instance();
                    }

                    // durable already exists, make sure the filter subject matches
                    if (string.IsNullOrWhiteSpace(subject))
                    {
                        subject = userCC.FilterSubject;
                    }
                    else if (!IsFilterMatch(subject, serverCC.FilterSubject, stream))
                    {
                        throw JsSubSubjectDoesNotMatchFilter.Instance();
                    }

                    inboxDeliver = serverCC.DeliverSubject; // use the deliver subject as the inbox. It may be null, that's ok, we'll fix that later
                }
                else if (so.Bind)
                {
                    throw JsSubConsumerNotFoundRequiredInBind.Instance();
                }
            }

            // 4. If no deliver subject (inbox) provided or found, make an inbox.
            if (string.IsNullOrWhiteSpace(inboxDeliver))
            {
                inboxDeliver = Conn.NewInbox();
            }

            // 5. If consumer does not exist, create and settle on the config. Name will have to wait
            //    If the consumer exists, I know what the settled info is
            if (serverCC == null)
            {
                ConsumerConfiguration.ConsumerConfigurationBuilder ccBuilder = ConsumerConfiguration.Builder(userCC);

                // Pull mode doesn't maintain a deliver subject. It's actually an error if we send it.
                if (!isPullMode)
                {
                    ccBuilder.WithDeliverSubject(inboxDeliver);
                }

                if (string.IsNullOrWhiteSpace(userCC.FilterSubject))
                {
                    ccBuilder.WithFilterSubject(subject);
                }

                if (string.IsNullOrWhiteSpace(userCC.DeliverGroup) && !string.IsNullOrWhiteSpace(qgroup))
                {
                    ccBuilder.WithDeliverGroup(qgroup);
                }

                // createOrUpdateConsumer can fail for security reasons, maybe other reasons?
                serverCC     = ccBuilder.Build();
                consumerName = null;
            }

            // 6. create the subscription
            Subscription sub;

            if (isPullMode)
            {
                MessageManager[] managers = { new PullMessageManager() };
                SyncSubscription CreateSubDelegate(Connection lConn, string lSubject, string lQueueNa)
                {
                    return(new JetStreamPullSubscription(lConn, lSubject, this, stream, consumerName, inboxDeliver, managers));
                }

                sub = ((Connection)Conn).subscribeSync(inboxDeliver, queueName, CreateSubDelegate);
            }
            else
            {
                bool syncMode = userHandler == null;
                PushMessageManager pushMessageManager =
                    PushMessageManagerFactoryImpl((Connection)Conn, so, serverCC, qgroup != null, syncMode);
                MessageManager[] managers;
                if (so.Ordered)
                {
                    managers = new MessageManager[]
                    {
                        new SidCheckManager(),
                        pushMessageManager,
                        new OrderedMessageManager(this, stream, serverCC, syncMode)
                    };
                }
                else
                {
                    managers = new MessageManager[] { pushMessageManager };
                }

                if (syncMode)
                {
                    SyncSubscription CreateSubDelegate(Connection lConn, string lSubject, string lQueue)
                    {
                        return(new JetStreamPushSyncSubscription(lConn, lSubject, lQueue, this, stream, consumerName, inboxDeliver, managers));
                    }

                    sub = ((Connection)Conn).subscribeSync(inboxDeliver, queueName, CreateSubDelegate);
                }
                else
                {
                    EventHandler <MsgHandlerEventArgs> autoAckHandler;
                    if (autoAck && serverCC.AckPolicy != AckPolicy.None)
                    {
                        autoAckHandler = (sender, args) => args.Message.Ack();
                    }
                    else
                    {
                        autoAckHandler = (sender, args) => {};
                    }

                    EventHandler <MsgHandlerEventArgs> handler = (sender, args) =>
                    {
                        foreach (MessageManager mm in managers)
                        {
                            if (mm.Manage(args.Message))
                            {
                                return; // manager handled the message
                            }
                        }

                        userHandler.Invoke(sender, args);
                        autoAckHandler.Invoke(sender, args);
                    };

                    AsyncSubscription CreateAsyncSubDelegate(Connection lConn, string lSubject, string lQueue)
                    {
                        return(new JetStreamPushAsyncSubscription(lConn, lSubject, lQueue, this, stream, consumerName, inboxDeliver, managers));
                    }

                    sub = ((Connection)Conn).subscribeAsync(inboxDeliver, queueName, handler, CreateAsyncSubDelegate);
                }
            }

            // 7. The consumer might need to be created, do it here
            if (consumerName == null)
            {
                try
                {
                    ConsumerInfo ci = AddOrUpdateConsumerInternal(stream, serverCC);
                    if (sub is JetStreamAbstractSyncSubscription syncSub)
                    {
                        syncSub.Consumer = ci.Name;
                    }
                    else if (sub is JetStreamPushAsyncSubscription asyncSub)
                    {
                        asyncSub.Consumer = ci.Name;
                    }
                }
                catch
                {
                    // create consumer can fail, unsubscribe and then throw the exception to the user
                    sub.Unsubscribe();
                    throw;
                }
            }

            return(sub);
        }