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); }
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)); }
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 }
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)); }
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; }
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 } }
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); }
internal ConsumerCreateRequest(string streamName, ConsumerConfiguration config) { StreamName = streamName; Config = config; }
public static ConsumerConfigurationBuilder Builder(ConsumerConfiguration cc) { return(new ConsumerConfigurationBuilder(cc)); }
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); }
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(); } }
/// <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()); }
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); }