Files
QSfera/Server/vendor/github.com/nats-io/nats-server/v2/server/consumer.go
T
Курнат Андрей 2315f25754 Initial QSfera import
2026-06-07 10:20:04 +03:00

7060 lines
196 KiB
Go

// Copyright 2019-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package server
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"math"
"math/rand"
"os"
"path/filepath"
"reflect"
"regexp"
"slices"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/nats-io/nats-server/v2/server/avl"
"github.com/nats-io/nats-server/v2/server/gsl"
"github.com/nats-io/nuid"
"golang.org/x/time/rate"
)
// Headers sent with Request Timeout
const (
JSPullRequestPendingMsgs = "Nats-Pending-Messages"
JSPullRequestPendingBytes = "Nats-Pending-Bytes"
JSPullRequestNatsPinId = "Nats-Pin-Id"
)
var (
validGroupName = regexp.MustCompile(`^[a-zA-Z0-9/_=-]{1,16}$`)
)
// Headers sent when batch size was completed, but there were remaining bytes.
const JsPullRequestRemainingBytesT = "NATS/1.0 409 Batch Completed\r\n%s: %d\r\n%s: %d\r\n\r\n"
type ConsumerInfo struct {
Stream string `json:"stream_name"`
Name string `json:"name"`
Created time.Time `json:"created"`
Config *ConsumerConfig `json:"config,omitempty"`
Delivered SequenceInfo `json:"delivered"`
AckFloor SequenceInfo `json:"ack_floor"`
NumAckPending int `json:"num_ack_pending"`
NumRedelivered int `json:"num_redelivered"`
NumWaiting int `json:"num_waiting"`
NumPending uint64 `json:"num_pending"`
Cluster *ClusterInfo `json:"cluster,omitempty"`
PushBound bool `json:"push_bound,omitempty"`
Paused bool `json:"paused,omitempty"`
PauseRemaining time.Duration `json:"pause_remaining,omitempty"`
// TimeStamp indicates when the info was gathered
TimeStamp time.Time `json:"ts"`
PriorityGroups []PriorityGroupState `json:"priority_groups,omitempty"`
}
// consumerInfoClusterResponse is a response used in a cluster to communicate the consumer info
// back to the meta leader as part of a consumer list request.
type consumerInfoClusterResponse struct {
ConsumerInfo
OfflineReason string `json:"offline_reason,omitempty"` // Reporting when a consumer is offline.
}
type PriorityGroupState struct {
Group string `json:"group"`
PinnedClientID string `json:"pinned_client_id,omitempty"`
PinnedTS time.Time `json:"pinned_ts,omitempty"`
}
type ConsumerConfig struct {
Durable string `json:"durable_name,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
DeliverPolicy DeliverPolicy `json:"deliver_policy"`
OptStartSeq uint64 `json:"opt_start_seq,omitempty"`
OptStartTime *time.Time `json:"opt_start_time,omitempty"`
AckPolicy AckPolicy `json:"ack_policy"`
AckWait time.Duration `json:"ack_wait,omitempty"`
MaxDeliver int `json:"max_deliver,omitempty"`
BackOff []time.Duration `json:"backoff,omitempty"`
FilterSubject string `json:"filter_subject,omitempty"`
FilterSubjects []string `json:"filter_subjects,omitempty"`
ReplayPolicy ReplayPolicy `json:"replay_policy"`
RateLimit uint64 `json:"rate_limit_bps,omitempty"` // Bits per sec
SampleFrequency string `json:"sample_freq,omitempty"`
MaxWaiting int `json:"max_waiting,omitempty"`
MaxAckPending int `json:"max_ack_pending,omitempty"`
FlowControl bool `json:"flow_control,omitempty"`
HeadersOnly bool `json:"headers_only,omitempty"`
// Pull based options.
MaxRequestBatch int `json:"max_batch,omitempty"`
MaxRequestExpires time.Duration `json:"max_expires,omitempty"`
MaxRequestMaxBytes int `json:"max_bytes,omitempty"`
// Push based consumers.
DeliverSubject string `json:"deliver_subject,omitempty"`
DeliverGroup string `json:"deliver_group,omitempty"`
Heartbeat time.Duration `json:"idle_heartbeat,omitempty"`
// Ephemeral inactivity threshold.
InactiveThreshold time.Duration `json:"inactive_threshold,omitempty"`
// Generally inherited by parent stream and other markers, now can be configured directly.
Replicas int `json:"num_replicas"`
// Force memory storage.
MemoryStorage bool `json:"mem_storage,omitempty"`
// Don't add to general clients.
Direct bool `json:"direct,omitempty"`
Sourcing bool `json:"sourcing,omitempty"`
// Metadata is additional metadata for the Consumer.
Metadata map[string]string `json:"metadata,omitempty"`
// PauseUntil is for suspending the consumer until the deadline.
PauseUntil *time.Time `json:"pause_until,omitempty"`
// Priority groups
PriorityGroups []string `json:"priority_groups,omitempty"`
PriorityPolicy PriorityPolicy `json:"priority_policy,omitempty"`
PinnedTTL time.Duration `json:"priority_timeout,omitempty"`
}
// SequenceInfo has both the consumer and the stream sequence and last activity.
type SequenceInfo struct {
Consumer uint64 `json:"consumer_seq"`
Stream uint64 `json:"stream_seq"`
Last *time.Time `json:"last_active,omitempty"`
}
type CreateConsumerRequest struct {
Stream string `json:"stream_name"`
Config ConsumerConfig `json:"config"`
Action ConsumerAction `json:"action"`
Pedantic bool `json:"pedantic,omitempty"`
}
type ConsumerAction int
const (
ActionCreateOrUpdate ConsumerAction = iota
ActionUpdate
ActionCreate
)
const (
actionUpdateJSONString = `"update"`
actionCreateJSONString = `"create"`
actionCreateOrUpdateJSONString = `""`
)
var (
actionUpdateJSONBytes = []byte(actionUpdateJSONString)
actionCreateJSONBytes = []byte(actionCreateJSONString)
actionCreateOrUpdateJSONBytes = []byte(actionCreateOrUpdateJSONString)
)
func (a ConsumerAction) String() string {
switch a {
case ActionCreateOrUpdate:
return actionCreateOrUpdateJSONString
case ActionCreate:
return actionCreateJSONString
case ActionUpdate:
return actionUpdateJSONString
}
return actionCreateOrUpdateJSONString
}
func (a ConsumerAction) MarshalJSON() ([]byte, error) {
switch a {
case ActionCreate:
return actionCreateJSONBytes, nil
case ActionUpdate:
return actionUpdateJSONBytes, nil
case ActionCreateOrUpdate:
return actionCreateOrUpdateJSONBytes, nil
default:
return nil, fmt.Errorf("can not marshal %v", a)
}
}
func (a *ConsumerAction) UnmarshalJSON(data []byte) error {
switch string(data) {
case actionCreateJSONString:
*a = ActionCreate
case actionUpdateJSONString:
*a = ActionUpdate
case actionCreateOrUpdateJSONString:
*a = ActionCreateOrUpdate
default:
return fmt.Errorf("unknown consumer action: %v", string(data))
}
return nil
}
// ConsumerNakOptions is for optional NAK values, e.g. delay.
type ConsumerNakOptions struct {
Delay time.Duration `json:"delay"`
}
// PriorityPolicy determines policy for selecting messages based on priority.
type PriorityPolicy int
const (
// No priority policy.
PriorityNone PriorityPolicy = iota
// Clients will get the messages only if certain criteria are specified.
PriorityOverflow
// Single client takes over handling of the messages, while others are on standby.
PriorityPinnedClient
// Clients with lowest priority will be selected first.
PriorityPrioritized
)
const (
PriorityNoneJSONString = `"none"`
PriorityOverflowJSONString = `"overflow"`
PriorityPinnedClientJSONString = `"pinned_client"`
PriorityPrioritizedJSONString = `"prioritized"`
)
var (
PriorityNoneJSONBytes = []byte(PriorityNoneJSONString)
PriorityOverflowJSONBytes = []byte(PriorityOverflowJSONString)
PriorityPinnedClientJSONBytes = []byte(PriorityPinnedClientJSONString)
PriorityPrioritizedJSONBytes = []byte(PriorityPrioritizedJSONString)
)
func (pp PriorityPolicy) String() string {
switch pp {
case PriorityOverflow:
return PriorityOverflowJSONString
case PriorityPinnedClient:
return PriorityPinnedClientJSONString
case PriorityPrioritized:
return PriorityPrioritizedJSONString
default:
return PriorityNoneJSONString
}
}
func (pp PriorityPolicy) MarshalJSON() ([]byte, error) {
switch pp {
case PriorityOverflow:
return PriorityOverflowJSONBytes, nil
case PriorityPinnedClient:
return PriorityPinnedClientJSONBytes, nil
case PriorityPrioritized:
return PriorityPrioritizedJSONBytes, nil
case PriorityNone:
return PriorityNoneJSONBytes, nil
default:
return nil, fmt.Errorf("unknown priority policy: %v", pp)
}
}
func (pp *PriorityPolicy) UnmarshalJSON(data []byte) error {
switch string(data) {
case PriorityOverflowJSONString:
*pp = PriorityOverflow
case PriorityPinnedClientJSONString:
*pp = PriorityPinnedClient
case PriorityPrioritizedJSONString:
*pp = PriorityPrioritized
case PriorityNoneJSONString:
*pp = PriorityNone
default:
return fmt.Errorf("unknown priority policy: %v", string(data))
}
return nil
}
// DeliverPolicy determines how the consumer should select the first message to deliver.
type DeliverPolicy int
const (
// DeliverAll will be the default so can be omitted from the request.
DeliverAll DeliverPolicy = iota
// DeliverLast will start the consumer with the last sequence received.
DeliverLast
// DeliverNew will only deliver new messages that are sent after the consumer is created.
DeliverNew
// DeliverByStartSequence will look for a defined starting sequence to start.
DeliverByStartSequence
// DeliverByStartTime will select the first messsage with a timestamp >= to StartTime.
DeliverByStartTime
// DeliverLastPerSubject will start the consumer with the last message for all subjects received.
DeliverLastPerSubject
)
func (dp DeliverPolicy) String() string {
switch dp {
case DeliverAll:
return "all"
case DeliverLast:
return "last"
case DeliverNew:
return "new"
case DeliverByStartSequence:
return "by_start_sequence"
case DeliverByStartTime:
return "by_start_time"
case DeliverLastPerSubject:
return "last_per_subject"
default:
return "undefined"
}
}
// AckPolicy determines how the consumer should acknowledge delivered messages.
type AckPolicy int
const (
// AckNone requires no acks for delivered messages.
AckNone AckPolicy = iota
// AckAll when acking a sequence number, this implicitly acks all sequences below this one as well.
AckAll
// AckExplicit requires ack or nack for all messages.
AckExplicit
// AckFlowControl functions like AckAll, but acks based on responses to flow control.
AckFlowControl
)
func (a AckPolicy) String() string {
switch a {
case AckNone:
return "none"
case AckAll:
return "all"
case AckFlowControl:
return "flow_control"
default:
return "explicit"
}
}
// ReplayPolicy determines how the consumer should replay messages it already has queued in the stream.
type ReplayPolicy int
const (
// ReplayInstant will replay messages as fast as possible.
ReplayInstant ReplayPolicy = iota
// ReplayOriginal will maintain the same timing as the messages were received.
ReplayOriginal
)
func (r ReplayPolicy) String() string {
switch r {
case ReplayInstant:
return replayInstantPolicyJSONString
default:
return replayOriginalPolicyJSONString
}
}
// OK
const OK = "+OK"
// Ack responses. Note that a nil or no payload is same as AckAck
var (
// Ack
AckAck = []byte("+ACK") // nil or no payload to ack subject also means ACK
AckOK = []byte(OK) // deprecated but +OK meant ack as well.
// Nack
AckNak = []byte("-NAK")
// Progress indicator
AckProgress = []byte("+WPI")
// Ack + Deliver the next message(s).
AckNext = []byte("+NXT")
// Terminate delivery of the message.
AckTerm = []byte("+TERM")
)
const (
// reasons to supply when terminating messages using limits
ackTermLimitsReason = "Message deleted by stream limits"
ackTermUnackedLimitsReason = "Unacknowledged message was deleted"
)
// Calculate accurate replicas for the consumer config with the parent stream config.
func (consCfg ConsumerConfig) replicas(strCfg *StreamConfig) int {
if consCfg.Replicas == 0 || consCfg.Replicas > strCfg.Replicas {
if !isDurableConsumer(&consCfg) && strCfg.Retention == LimitsPolicy && consCfg.Replicas == 0 {
// Matches old-school ephemerals only, where the replica count is 0.
return 1
}
return strCfg.Replicas
}
return consCfg.Replicas
}
// Consumer is a jetstream consumer.
type consumer struct {
// Atomic used to notify that we want to process an ack.
// This will be checked in checkPending to abort processing
// and let ack be processed in priority.
awl int64
leader atomic.Bool
mu sync.RWMutex
js *jetStream
mset *stream
acc *Account
srv *Server
client *client
sysc *client
sid int
name string
stream string
sseq uint64 // next stream sequence
subjf subjectFilters // subject filters and their sequences
filters *gsl.SimpleSublist // When we have multiple filters we will use LoadNextMsgMulti and pass this in.
dseq uint64 // delivered consumer sequence
adflr uint64 // ack delivery floor
asflr uint64 // ack store floor
chkflr uint64 // our check floor, interest streams only.
npc int64 // Num Pending Count
npf uint64 // Num Pending Floor Sequence
dsubj string
qgroup string
lss *lastSeqSkipList
rlimit *rate.Limiter
reqSub *subscription
resetSub *subscription
ackSubOld *subscription
ackReplyOldT string
ackSubjOld string
ackSub *subscription
ackReplyT string
ackSubj string
fcPreOld string
fcSubjOld string
fcPre string
fcSubj string
nextMsgSubj string
nextMsgReqs *ipQueue[*nextMsgReq]
resetSubj string
maxp int
pblimit int
maxpb int
pbytes int
fcsz int
fcid string
fcSubOld *subscription
fcSub *subscription
outq *jsOutQ
pending map[uint64]*Pending
ptmr *time.Timer
ptmrEnd time.Time
rdq []uint64
rdqi avl.SequenceSet
rdc map[uint64]uint64
replies map[uint64]string
pendingDeliveries map[uint64]*jsPubMsg // Messages that can be delivered after achieving quorum.
waitingDeliveries map[string]*waitingDelivery // (Optional) request timeout messages that need to wait for replicated deliveries first.
maxdc uint64
waiting *waitQueue
cfg ConsumerConfig
ici *ConsumerInfo
store ConsumerStore
active bool
replay bool
useV2Ack bool
dtmr *time.Timer
uptmr *time.Timer // Unpause timer
gwdtmr *time.Timer
dthresh time.Duration
mch chan struct{} // Message channel
qch chan struct{} // Quit channel
mqch chan struct{} // The monitor's quit channel.
inch chan bool // Interest change channel
sfreq int32
ackEventT string
nakEventT string
deliveryExcEventT string
created time.Time
ldt time.Time
lat time.Time
lwqic time.Time
closed bool
// Clustered.
ca *consumerAssignment
node RaftNode
infoSub *subscription
lqsent time.Time
prm map[string]struct{}
rsm map[string]bool // Reset requests that need to be responded to on the internal sys account (if true).
prOk bool
uch chan struct{}
retention RetentionPolicy
monitorWg sync.WaitGroup
inMonitor bool
// R>1 proposals
pch chan struct{}
phead *proposal
ptail *proposal
// Ack queue
ackMsgs *ipQueue[*jsAckMsg]
// for stream signaling when multiple filters are set.
sigSubs []string
// Priority groups
// Details described in ADR-42.
// currentPinId is the current nuid for the pinned consumer.
// If the Consumer is running in `PriorityPinnedClient` mode, server will
// pick up a new nuid and assign it to first pending pull request.
currentPinId string
/// pinnedTtl is the remaining time before the current PinId expires.
pinnedTtl *time.Timer
pinnedTS time.Time
// If standalone/single-server, the offline reason needs to be stored directly in the consumer.
// Otherwise, if clustered it will be part of the consumer assignment.
offlineReason string
}
// A single subject filter.
type subjectFilter struct {
subject string
tokenizedSubject []string
hasWildcard bool
}
type subjectFilters []*subjectFilter
// subjects is a helper function used for updating consumers.
// It is not used and should not be used in hotpath.
func (s subjectFilters) subjects() []string {
subjects := make([]string, 0, len(s))
for _, filter := range s {
subjects = append(subjects, filter.subject)
}
return subjects
}
type proposal struct {
data []byte
next *proposal
}
const (
// JsAckWaitDefault is the default AckWait, only applicable on explicit ack policy consumers.
JsAckWaitDefault = 30 * time.Second
// JsDeleteWaitTimeDefault is the default amount of time we will wait for non-durable
// consumers to be in an inactive state before deleting them.
JsDeleteWaitTimeDefault = 5 * time.Second
// JsFlowControlMaxPending specifies default pending bytes during flow control that can be outstanding.
JsFlowControlMaxPending = 32 * 1024 * 1024
// JsDefaultMaxAckPending is set for consumers with explicit ack that do not set the max ack pending.
JsDefaultMaxAckPending = 1000
// JsDefaultPinnedTTL is the default grace period for the pinned consumer to send a new request before a new pin
// is picked by a server.
JsDefaultPinnedTTL = 2 * time.Minute
)
// Helper function to set consumer config defaults from above.
func setConsumerConfigDefaults(config *ConsumerConfig, streamCfg *StreamConfig, lim *JSLimitOpts, accLim *JetStreamAccountLimits, pedantic bool) *ApiError {
// Setup default of -1, meaning no limit for MaxDeliver.
if config.MaxDeliver == 0 || config.MaxDeliver < -1 {
if pedantic && config.MaxDeliver < -1 {
return NewJSPedanticError(errors.New("max_deliver must be set to -1"))
}
config.MaxDeliver = -1
}
// Setup zero defaults.
if config.MaxWaiting < 0 {
if pedantic {
return NewJSPedanticError(errors.New("max_waiting must not be negative"))
}
config.MaxWaiting = 0
}
if config.MaxAckPending < -1 {
if pedantic {
return NewJSPedanticError(errors.New("max_ack_pending must be set to -1"))
}
config.MaxAckPending = -1
}
if config.MaxRequestBatch < 0 {
if pedantic {
return NewJSPedanticError(errors.New("max_batch must not be negative"))
}
config.MaxRequestBatch = 0
}
if config.MaxRequestExpires < 0 {
if pedantic {
return NewJSPedanticError(errors.New("max_expires must not be negative"))
}
config.MaxRequestExpires = 0
}
if config.MaxRequestMaxBytes < 0 {
if pedantic {
return NewJSPedanticError(errors.New("max_bytes must not be negative"))
}
config.MaxRequestMaxBytes = 0
}
if config.Heartbeat < 0 {
if pedantic {
return NewJSPedanticError(errors.New("idle_heartbeat must not be negative"))
}
config.Heartbeat = 0
}
if config.InactiveThreshold < 0 {
if pedantic {
return NewJSPedanticError(errors.New("inactive_threshold must not be negative"))
}
config.InactiveThreshold = 0
}
if config.PinnedTTL < 0 {
if pedantic {
return NewJSPedanticError(errors.New("priority_timeout must not be negative"))
}
config.PinnedTTL = 0
}
// Set to default if not specified.
if config.DeliverSubject == _EMPTY_ && config.MaxWaiting == 0 {
config.MaxWaiting = JSWaitQueueDefaultMax
}
// Setup proper default for ack wait if we are in explicit ack mode.
if config.AckWait == 0 && (config.AckPolicy == AckExplicit || config.AckPolicy == AckAll) {
config.AckWait = JsAckWaitDefault
}
// If BackOff was specified that will override the AckWait and the MaxDeliver.
if len(config.BackOff) > 0 {
if pedantic && config.AckWait != config.BackOff[0] {
return NewJSPedanticError(errors.New("first backoff value has to equal batch AckWait"))
}
config.AckWait = config.BackOff[0]
}
if config.MaxAckPending == 0 {
if pedantic && streamCfg.ConsumerLimits.MaxAckPending > 0 {
return NewJSPedanticError(errors.New("max_ack_pending must be set if it's configured in stream limits"))
}
config.MaxAckPending = streamCfg.ConsumerLimits.MaxAckPending
}
if config.InactiveThreshold == 0 {
if pedantic && streamCfg.ConsumerLimits.InactiveThreshold > 0 {
return NewJSPedanticError(errors.New("inactive_threshold must be set if it's configured in stream limits"))
}
config.InactiveThreshold = streamCfg.ConsumerLimits.InactiveThreshold
}
// Set proper default for max ack pending if we are ack explicit and none has been set.
if config.MaxAckPending == 0 && config.AckPolicy != AckNone {
ackPending := JsDefaultMaxAckPending
if lim.MaxAckPending > 0 && lim.MaxAckPending < ackPending {
ackPending = lim.MaxAckPending
}
if accLim.MaxAckPending > 0 && accLim.MaxAckPending < ackPending {
ackPending = accLim.MaxAckPending
}
config.MaxAckPending = ackPending
}
// if applicable set max request batch size
if config.DeliverSubject == _EMPTY_ && config.MaxRequestBatch == 0 && lim.MaxRequestBatch > 0 {
if pedantic {
return NewJSPedanticError(errors.New("max_request_batch must be set if it's JetStream limits are set"))
}
config.MaxRequestBatch = lim.MaxRequestBatch
}
// set the default value only if pinned policy is used.
if config.PriorityPolicy == PriorityPinnedClient && config.PinnedTTL == 0 {
config.PinnedTTL = JsDefaultPinnedTTL
}
// Set default values for flow control policy.
if config.AckPolicy == AckFlowControl && !pedantic {
config.FlowControl = true
config.Heartbeat = sourceHealthHB
}
return nil
}
// Check the consumer config. If we are recovering don't check filter subjects.
func checkConsumerCfg(
config *ConsumerConfig,
srvLim *JSLimitOpts,
cfg *StreamConfig,
_ *Account,
accLim *JetStreamAccountLimits,
isRecovering bool,
) *ApiError {
if config.Name != _EMPTY_ && !isValidAssetName(config.Name) {
return NewJSStreamInvalidConfigError(errors.New("consumer name can not contain '.', '*', '>', '\\', '/'"))
}
if config.Durable != _EMPTY_ && !isValidAssetName(config.Durable) {
return NewJSStreamInvalidConfigError(errors.New("consumer durable name can not contain '.', '*', '>', '\\', '/'"))
}
// Check if replicas is defined but exceeds parent stream.
if config.Replicas > 0 && config.Replicas > cfg.Replicas {
return NewJSConsumerReplicasExceedsStreamError()
}
// Check that it is not negative
if config.Replicas < 0 {
return NewJSReplicasCountCannotBeNegativeError()
}
// If the stream is interest or workqueue retention make sure the replicas
// match that of the stream. This is REQUIRED for now.
if cfg.Retention == InterestPolicy || cfg.Retention == WorkQueuePolicy {
// Only error here if not recovering.
// We handle recovering in a different spot to allow consumer to come up
// if previous version allowed it to be created. We do not want it to not come up.
if !isRecovering && config.Replicas != 0 && config.Replicas != cfg.Replicas {
return NewJSConsumerReplicasShouldMatchStreamError()
}
}
if _, err := config.AckPolicy.MarshalJSON(); err != nil {
return NewJSConsumerAckPolicyInvalidError()
}
if _, err := config.ReplayPolicy.MarshalJSON(); err != nil {
return NewJSConsumerReplayPolicyInvalidError()
}
// Check not negative AckWait/BackOff
for _, backoff := range config.BackOff {
if backoff < 0 {
return NewJSConsumerBackOffNegativeError()
}
}
if config.AckWait < 0 {
return NewJSConsumerAckWaitNegativeError()
}
// Ack Flow Control policy requires push-based flow-controlled consumer.
if config.AckPolicy == AckFlowControl {
if config.DeliverSubject == _EMPTY_ {
return NewJSConsumerAckFCRequiresPushError()
}
if !config.FlowControl {
return NewJSConsumerAckFCRequiresFCError()
}
// We currently limit using heartbeat of 1s, since those are used for ephemeral sourcing consumers as well.
// We could decide to relax this in the future, but need to be careful to not allow a heartbeat larger
// than the stalled source timeout.
if config.Heartbeat != sourceHealthHB {
return NewJSStreamInvalidConfigError(fmt.Errorf("flow control ack policy heartbeat needs to be 1s"))
}
if config.MaxAckPending <= 0 {
return NewJSConsumerAckFCRequiresMaxAckPendingError()
}
if config.AckWait != 0 || len(config.BackOff) > 0 {
return NewJSConsumerAckFCRequiresNoAckWaitError()
}
if config.MaxDeliver > 0 {
return NewJSConsumerAckFCRequiresNoMaxDeliverError()
}
}
// Check if we have a BackOff defined that MaxDeliver is within range etc.
if lbo := len(config.BackOff); lbo > 0 && config.MaxDeliver != -1 && lbo > config.MaxDeliver {
return NewJSConsumerMaxDeliverBackoffError()
}
if len(config.Description) > JSMaxDescriptionLen {
return NewJSConsumerDescriptionTooLongError(JSMaxDescriptionLen)
}
// For now expect a literal subject if its not empty. Empty means work queue mode (pull mode).
if config.DeliverSubject != _EMPTY_ {
if !subjectIsLiteral(config.DeliverSubject) {
return NewJSConsumerDeliverToWildcardsError()
}
if !IsValidSubject(config.DeliverSubject) {
return NewJSConsumerInvalidDeliverSubjectError()
}
if deliveryFormsCycle(cfg, config.DeliverSubject) {
return NewJSConsumerDeliverCycleError()
}
if config.MaxWaiting != 0 {
return NewJSConsumerPushMaxWaitingError()
}
if config.MaxAckPending > 0 && config.AckPolicy == AckNone {
return NewJSConsumerMaxPendingAckPolicyRequiredError()
}
if config.Heartbeat > 0 && config.Heartbeat < 100*time.Millisecond {
return NewJSConsumerSmallHeartbeatError()
}
} else {
// Pull mode with work queue retention from the stream requires an explicit ack.
if config.AckPolicy == AckNone && cfg.Retention == WorkQueuePolicy {
return NewJSConsumerPullRequiresAckError()
}
if config.RateLimit > 0 {
return NewJSConsumerPullWithRateLimitError()
}
if config.MaxWaiting < 0 {
return NewJSConsumerMaxWaitingNegativeError()
}
if config.Heartbeat > 0 {
return NewJSConsumerHBRequiresPushError()
}
if config.FlowControl {
return NewJSConsumerFCRequiresPushError()
}
if config.MaxRequestBatch < 0 {
return NewJSConsumerMaxRequestBatchNegativeError()
}
if config.MaxRequestExpires != 0 && config.MaxRequestExpires < time.Millisecond {
return NewJSConsumerMaxRequestExpiresTooSmallError()
}
if srvLim.MaxRequestBatch > 0 && config.MaxRequestBatch > srvLim.MaxRequestBatch {
return NewJSConsumerMaxRequestBatchExceededError(srvLim.MaxRequestBatch)
}
}
if srvLim.MaxAckPending > 0 && config.MaxAckPending > srvLim.MaxAckPending {
return NewJSConsumerMaxPendingAckExcessError(srvLim.MaxAckPending)
}
if accLim.MaxAckPending > 0 && config.MaxAckPending > accLim.MaxAckPending {
return NewJSConsumerMaxPendingAckExcessError(accLim.MaxAckPending)
}
if cfg.ConsumerLimits.MaxAckPending > 0 && config.MaxAckPending > cfg.ConsumerLimits.MaxAckPending {
return NewJSConsumerMaxPendingAckExcessError(cfg.ConsumerLimits.MaxAckPending)
}
if cfg.ConsumerLimits.InactiveThreshold > 0 && config.InactiveThreshold > cfg.ConsumerLimits.InactiveThreshold {
return NewJSConsumerInactiveThresholdExcessError(cfg.ConsumerLimits.InactiveThreshold)
}
// Direct need to be non-mapped ephemerals.
if config.Direct {
if config.DeliverSubject == _EMPTY_ {
return NewJSConsumerDirectRequiresPushError()
}
if isDurableConsumer(config) {
return NewJSConsumerDirectRequiresEphemeralError()
}
}
// Do not allow specifying both FilterSubject and FilterSubjects,
// as that's probably unintentional without any difference from passing
// all filters in FilterSubjects.
if config.FilterSubject != _EMPTY_ && len(config.FilterSubjects) > 0 {
return NewJSConsumerDuplicateFilterSubjectsError()
}
if config.FilterSubject != _EMPTY_ && !IsValidSubject(config.FilterSubject) {
return NewJSStreamInvalidConfigError(ErrBadSubject)
}
// We treat FilterSubjects: []string{""} as a misconfig, so we validate against it.
for _, filter := range config.FilterSubjects {
if filter == _EMPTY_ {
return NewJSConsumerEmptyFilterError()
}
}
subjectFilters := gatherSubjectFilters(config.FilterSubject, config.FilterSubjects)
// Check subject filters do not overlap.
for outer, subject := range subjectFilters {
if !IsValidSubject(subject) {
return NewJSStreamInvalidConfigError(ErrBadSubject)
}
for inner, ssubject := range subjectFilters {
if inner != outer && subjectIsSubsetMatch(subject, ssubject) {
return NewJSConsumerOverlappingSubjectFiltersError()
}
}
}
// Helper function to formulate similar errors.
badStart := func(dp, start string) error {
return fmt.Errorf("consumer delivery policy is deliver %s, but optional start %s is also set", dp, start)
}
notSet := func(dp, notSet string) error {
return fmt.Errorf("consumer delivery policy is deliver %s, but optional %s is not set", dp, notSet)
}
// Check on start position conflicts.
switch config.DeliverPolicy {
case DeliverAll:
if config.OptStartSeq > 0 {
return NewJSConsumerInvalidPolicyError(badStart("all", "sequence"))
}
if config.OptStartTime != nil {
return NewJSConsumerInvalidPolicyError(badStart("all", "time"))
}
case DeliverLast:
if config.OptStartSeq > 0 {
return NewJSConsumerInvalidPolicyError(badStart("last", "sequence"))
}
if config.OptStartTime != nil {
return NewJSConsumerInvalidPolicyError(badStart("last", "time"))
}
case DeliverLastPerSubject:
if config.OptStartSeq > 0 {
return NewJSConsumerInvalidPolicyError(badStart("last per subject", "sequence"))
}
if config.OptStartTime != nil {
return NewJSConsumerInvalidPolicyError(badStart("last per subject", "time"))
}
if config.FilterSubject == _EMPTY_ && len(config.FilterSubjects) == 0 {
return NewJSConsumerInvalidPolicyError(notSet("last per subject", "filter subject"))
}
case DeliverNew:
if config.OptStartSeq > 0 {
return NewJSConsumerInvalidPolicyError(badStart("new", "sequence"))
}
if config.OptStartTime != nil {
return NewJSConsumerInvalidPolicyError(badStart("new", "time"))
}
case DeliverByStartSequence:
if config.OptStartSeq == 0 {
return NewJSConsumerInvalidPolicyError(notSet("by start sequence", "start sequence"))
}
if config.OptStartTime != nil {
return NewJSConsumerInvalidPolicyError(badStart("by start sequence", "time"))
}
case DeliverByStartTime:
if config.OptStartTime == nil {
return NewJSConsumerInvalidPolicyError(notSet("by start time", "start time"))
}
if config.OptStartSeq != 0 {
return NewJSConsumerInvalidPolicyError(badStart("by start time", "start sequence"))
}
}
if config.SampleFrequency != _EMPTY_ {
s := strings.TrimSuffix(config.SampleFrequency, "%")
if sampleFreq, err := strconv.Atoi(s); err != nil || sampleFreq < 0 {
return NewJSConsumerInvalidSamplingError(err)
}
}
// We reject if flow control is set without heartbeats.
if config.FlowControl && config.Heartbeat == 0 {
return NewJSConsumerWithFlowControlNeedsHeartbeatsError()
}
if config.Durable != _EMPTY_ && config.Name != _EMPTY_ {
if config.Name != config.Durable {
return NewJSConsumerCreateDurableAndNameMismatchError()
}
}
var metadataLen int
for k, v := range config.Metadata {
metadataLen += len(k) + len(v)
}
if metadataLen > JSMaxMetadataLen {
return NewJSConsumerMetadataLengthError(fmt.Sprintf("%dKB", JSMaxMetadataLen/1024))
}
if config.PriorityPolicy != PriorityNone {
if config.DeliverSubject != "" {
return NewJSConsumerPushWithPriorityGroupError()
}
if len(config.PriorityGroups) == 0 {
return NewJSConsumerPriorityPolicyWithoutGroupError()
}
for _, group := range config.PriorityGroups {
if group == _EMPTY_ {
return NewJSConsumerEmptyGroupNameError()
}
if !validGroupName.MatchString(group) {
return NewJSConsumerInvalidGroupNameError()
}
}
} else {
// If PriorityPolicy is None or not set, reject if PriorityGroups or PinnedTTL are set
if len(config.PriorityGroups) > 0 {
return NewJSConsumerPriorityGroupWithPolicyNoneError()
}
if config.PinnedTTL > 0 {
return NewJSConsumerPinnedTTLWithoutPriorityPolicyNoneError()
}
}
// For now don't allow preferred server in placement.
if cfg.Placement != nil && cfg.Placement.Preferred != _EMPTY_ {
return NewJSStreamInvalidConfigError(fmt.Errorf("preferred server not permitted in placement"))
}
return nil
}
func (mset *stream) addConsumerWithAction(config *ConsumerConfig, action ConsumerAction, pedantic bool) (*consumer, error) {
return mset.addConsumerWithAssignment(config, _EMPTY_, nil, false, action, pedantic)
}
func (mset *stream) addConsumer(config *ConsumerConfig) (*consumer, error) {
return mset.addConsumerWithAction(config, ActionCreateOrUpdate, false)
}
func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname string, ca *consumerAssignment, isRecovering bool, action ConsumerAction, pedantic bool) (*consumer, error) {
// Check if this stream has closed.
if mset.closed.Load() {
return nil, NewJSStreamInvalidError()
}
mset.mu.RLock()
s, js, jsa, cfg, acc, lseq := mset.srv, mset.js, mset.jsa, mset.cfg, mset.acc, mset.lseq
mset.mu.RUnlock()
// If we do not have the consumer currently assigned to us in cluster mode we will proceed but warn.
// This can happen on startup with restored state where on meta replay we still do not have
// the assignment. Running in single server mode this always returns true.
if oname != _EMPTY_ && !jsa.consumerAssigned(mset.name(), oname) {
s.Debugf("Consumer %q > %q does not seem to be assigned to this server", mset.name(), oname)
}
if config == nil {
return nil, NewJSConsumerConfigRequiredError()
}
selectedLimits, _, _, _ := acc.selectLimits(config.replicas(&cfg))
if selectedLimits == nil {
return nil, NewJSNoLimitsError()
}
srvLim := &s.getOpts().JetStreamLimits
// Make sure we have sane defaults. Do so with the JS lock, otherwise a
// badly timed meta snapshot can result in a race condition.
mset.js.mu.Lock()
err := setConsumerConfigDefaults(config, &cfg, srvLim, selectedLimits, pedantic)
mset.js.mu.Unlock()
if err != nil {
return nil, err
}
if err := checkConsumerCfg(config, srvLim, &cfg, acc, selectedLimits, isRecovering); err != nil {
return nil, err
}
sampleFreq := 0
if config.SampleFrequency != _EMPTY_ {
// Can't fail as checkConsumerCfg checks correct format
sampleFreq, _ = strconv.Atoi(strings.TrimSuffix(config.SampleFrequency, "%"))
}
// Grab the client, account and server reference.
c := mset.client
if c == nil {
return nil, NewJSStreamInvalidError()
}
var accName string
c.mu.Lock()
s, a := c.srv, c.acc
if a != nil {
accName = a.Name
}
c.mu.Unlock()
// Hold mset lock here.
mset.mu.Lock()
if mset.client == nil || mset.store == nil || mset.consumers == nil {
mset.mu.Unlock()
return nil, NewJSStreamInvalidError()
}
// If this one is durable and already exists, we let that be ok as long as only updating what should be allowed.
var cName string
if isDurableConsumer(config) {
cName = config.Durable
} else if config.Name != _EMPTY_ {
cName = config.Name
}
if cName != _EMPTY_ {
if eo, ok := mset.consumers[cName]; ok {
if action == ActionCreate {
ocfg := eo.config()
copyConsumerMetadata(config, &ocfg)
if !reflect.DeepEqual(config, &ocfg) {
mset.mu.Unlock()
return nil, NewJSConsumerAlreadyExistsError()
}
}
// Check for overlapping subjects if we are a workqueue
if cfg.Retention == WorkQueuePolicy {
subjects := gatherSubjectFilters(config.FilterSubject, config.FilterSubjects)
if !mset.partitionUnique(cName, subjects) {
mset.mu.Unlock()
return nil, NewJSConsumerWQConsumerNotUniqueError()
}
}
mset.mu.Unlock()
err := eo.updateConfig(config)
if err == nil {
return eo, nil
}
return nil, NewJSConsumerCreateError(err, Unless(err))
}
}
if action == ActionUpdate {
mset.mu.Unlock()
return nil, NewJSConsumerDoesNotExistError()
}
standalone := !s.JetStreamIsClustered() && s.standAloneMode()
// If we're clustered we've already done this check, only do this if we're a standalone server.
// But if we're standalone, only enforce if we're not recovering, since the MaxConsumers could've
// been updated while we already had more consumers on disk.
if standalone && !isRecovering {
// Check for any limits, if the config for the consumer sets a limit we check against that
// but if not we use the value from account limits, if account limits is more restrictive
// than stream config we prefer the account limits to handle cases where account limits are
// updated during the lifecycle of the stream
maxc := cfg.MaxConsumers
if maxc <= 0 || (selectedLimits.MaxConsumers > 0 && selectedLimits.MaxConsumers < maxc) {
maxc = selectedLimits.MaxConsumers
}
if maxc > 0 && mset.numLimitableConsumers() >= maxc {
mset.mu.Unlock()
return nil, NewJSMaximumConsumersLimitError()
}
}
// Check on stream type conflicts with WorkQueues.
if cfg.Retention == WorkQueuePolicy && !config.Direct && !config.Sourcing {
// Force explicit acks here.
if config.AckPolicy != AckExplicit && config.AckPolicy != AckFlowControl {
mset.mu.Unlock()
return nil, NewJSConsumerWQRequiresExplicitAckError()
}
if mset.numLimitableConsumers() > 0 {
subjects := gatherSubjectFilters(config.FilterSubject, config.FilterSubjects)
if len(subjects) == 0 {
mset.mu.Unlock()
return nil, NewJSConsumerWQMultipleUnfilteredError()
} else if !mset.partitionUnique(cName, subjects) {
// Prior to v2.9.7, on a stream with WorkQueue policy, the servers
// were not catching the error of having multiple consumers with
// overlapping filter subjects depending on the scope, for instance
// creating "foo.*.bar" and then "foo.>" was not detected, while
// "foo.>" and then "foo.*.bar" would have been. Failing here
// in recovery mode would leave the rejected consumer in a bad state,
// so we will simply warn here, asking the user to remove this
// consumer administratively. Otherwise, if this is the creation
// of a new consumer, we will return the error.
if isRecovering {
s.Warnf("Consumer %q > %q has a filter subject that overlaps "+
"with other consumers, which is not allowed for a stream "+
"with WorkQueue policy, it should be administratively deleted",
cfg.Name, cName)
} else {
// We have a partition but it is not unique amongst the others.
mset.mu.Unlock()
return nil, NewJSConsumerWQConsumerNotUniqueError()
}
}
}
if config.DeliverPolicy != DeliverAll {
mset.mu.Unlock()
return nil, NewJSConsumerWQConsumerNotDeliverAllError()
}
}
// Set name, which will be durable name if set, otherwise we create one at random.
o := &consumer{
mset: mset,
js: s.getJetStream(),
acc: a,
srv: s,
client: s.createInternalJetStreamClient(),
sysc: s.createInternalJetStreamClient(),
cfg: *config,
dsubj: config.DeliverSubject,
outq: mset.outq,
active: true,
qch: make(chan struct{}),
mqch: make(chan struct{}),
uch: make(chan struct{}, 1),
mch: make(chan struct{}, 1),
sfreq: int32(sampleFreq),
maxdc: uint64(max(config.MaxDeliver, 0)), // MaxDeliver is negative (-1) when infinite.
maxp: config.MaxAckPending,
retention: cfg.Retention,
created: time.Now().UTC(),
}
// Add created timestamp used for the store, must match that of the consumer assignment if it exists.
if ca != nil {
js.mu.RLock()
o.created = ca.Created
js.mu.RUnlock()
}
// Bind internal client to the user account.
o.client.registerWithAccount(a)
// Bind to the system account.
o.sysc.registerWithAccount(s.SystemAccount())
if isDurableConsumer(config) {
if len(config.Durable) > JSMaxNameLen {
mset.mu.Unlock()
o.deleteWithoutAdvisory()
return nil, NewJSConsumerNameTooLongError(JSMaxNameLen)
}
o.name = config.Durable
} else if oname != _EMPTY_ {
o.name = oname
} else {
if config.Name != _EMPTY_ {
o.name = config.Name
} else {
// Legacy ephemeral auto-generated.
for {
o.name = createConsumerName()
if _, ok := mset.consumers[o.name]; !ok {
break
}
}
config.Name = o.name
}
}
// Create ackMsgs queue now that we have a consumer name
o.ackMsgs = newIPQueue[*jsAckMsg](s, fmt.Sprintf("[ACC:%s] consumer '%s' on stream '%s' ackMsgs", accName, o.name, cfg.Name))
// Create our request waiting queue.
if o.isPullMode() {
o.waiting = newWaitQueue(config.MaxWaiting)
// Create our internal queue for next msg requests.
o.nextMsgReqs = newIPQueue[*nextMsgReq](s, fmt.Sprintf("[ACC:%s] consumer '%s' on stream '%s' pull requests", accName, o.name, cfg.Name))
}
// already under lock, mset.Name() would deadlock
o.stream = cfg.Name
o.ackEventT = JSMetricConsumerAckPre + "." + o.stream + "." + o.name
o.nakEventT = JSAdvisoryConsumerMsgNakPre + "." + o.stream + "." + o.name
o.deliveryExcEventT = JSAdvisoryConsumerMaxDeliveryExceedPre + "." + o.stream + "." + o.name
if !isValidAssetName(o.name) {
mset.mu.Unlock()
o.deleteWithoutAdvisory()
return nil, NewJSConsumerBadDurableNameError()
}
// Setup our storage if not a direct consumer.
if !config.Direct {
store, err := mset.store.ConsumerStore(o.name, o.created, config)
if err != nil {
mset.mu.Unlock()
o.deleteWithoutAdvisory()
return nil, NewJSConsumerStoreFailedError(err)
}
o.store = store
}
for _, filter := range gatherSubjectFilters(o.cfg.FilterSubject, o.cfg.FilterSubjects) {
sub := &subjectFilter{
subject: filter,
hasWildcard: subjectHasWildcard(filter),
tokenizedSubject: tokenizeSubjectIntoSlice(nil, filter),
}
o.subjf = append(o.subjf, sub)
}
// If we have multiple filter subjects, create a sublist which we will use
// in calling store.LoadNextMsgMulti.
if len(o.subjf) <= 1 {
o.filters = nil
} else {
o.filters = gsl.NewSublist[struct{}]()
for _, filter := range o.subjf {
o.filters.Insert(filter.subject, struct{}{})
}
}
if o.store != nil && o.store.HasState() {
// Restore our saved state.
o.mu.Lock()
o.readStoredState()
replicas := o.cfg.replicas(&mset.cfg)
// Starting sequence represents the next sequence to be delivered, so decrement it
// since that's the minimum amount the stream should have as its last sequence.
sseq := o.sseq
if sseq > 0 {
sseq--
}
o.mu.Unlock()
// A stream observing data loss rolls back in its sequence. Check if we need to reconcile the consumer state
// to ensure new messages aren't skipped.
// Only performed for non-replicated consumers for now.
if replicas == 1 && lseq < sseq && isRecovering {
s.Warnf("JetStream consumer '%s > %s > %s' delivered sequence %d past last stream sequence of %d",
o.acc.Name, o.stream, o.name, sseq, lseq)
o.mu.Lock()
o.reconcileStateWithStream(lseq)
// Save the reconciled state
state := &ConsumerState{
Delivered: SequencePair{
Stream: o.sseq - 1,
Consumer: o.dseq - 1,
},
AckFloor: SequencePair{
Stream: o.asflr,
Consumer: o.adflr,
},
Pending: o.pending,
Redelivered: o.rdc,
}
err := o.store.ForceUpdate(state)
o.mu.Unlock()
if err != nil {
s.Errorf("JetStream consumer '%s > %s > %s' errored while updating state: %v", o.acc.Name, o.stream, o.name, err)
mset.mu.Unlock()
return nil, NewJSConsumerStoreFailedError(err)
}
}
} else if config.Direct || standalone {
// Clustered non-direct consumers defer this to setLeader so the
// expensive store scans don't block the meta apply goroutine.
if err := o.selectStartingSeqNo(); err != nil {
return nil, err
}
}
// Now register with mset and create the ack subscription.
// Check if we already have this one registered.
if eo, ok := mset.consumers[o.name]; ok {
mset.mu.Unlock()
if !o.isDurable() || !o.isPushMode() {
o.name = _EMPTY_ // Prevent removal since same name.
o.deleteWithoutAdvisory()
return nil, NewJSConsumerNameExistError()
}
// If we are here we have already registered this durable. If it is still active that is an error.
if eo.isActive() {
o.name = _EMPTY_ // Prevent removal since same name.
o.deleteWithoutAdvisory()
return nil, NewJSConsumerExistingActiveError()
}
// Since we are here this means we have a potentially new durable so we should update here.
// Check that configs are the same.
if !configsEqualSansDelivery(o.cfg, eo.cfg) {
o.name = _EMPTY_ // Prevent removal since same name.
o.deleteWithoutAdvisory()
return nil, NewJSConsumerReplacementWithDifferentNameError()
}
// Once we are here we have a replacement push-based durable.
eo.updateDeliverSubject(o.cfg.DeliverSubject)
return eo, nil
}
// Set up the ack subscription for this consumer. Will use wildcard for all acks.
// We will remember the template to generate replies with sequence numbers and use
// that to scanf them back in.
// Escape '%' in consumer and stream names, as `pre` is used as a template later
// in consumer.ackReply(), resulting in erroneous formatting of the ack subject.
mn := strings.ReplaceAll(cfg.Name, "%", "%%")
on := strings.ReplaceAll(o.name, "%", "%%")
domain := strings.ReplaceAll(o.srv.getOpts().JetStreamDomain, "%", "%%")
if domain == _EMPTY_ {
domain = "_"
}
accHash := getHash(accName)
o.useV2Ack = s.getOpts().getFeatureFlag(FeatureFlagJsAckFormatV2)
// v1 format: $JS.(ACK|FC).<stream>.<consumer>.etc.
o.fcPreOld = jsFlowControlPre
o.fcSubjOld = fmt.Sprintf(jsFlowControl, cfg.Name, o.name)
preOld := fmt.Sprintf(jsAckT, mn, on)
o.ackReplyOldT = fmt.Sprintf("%s.%%d.%%d.%%d.%%d.%%d", preOld)
o.ackSubjOld = fmt.Sprintf("%s.*.*.*.*.*", preOld)
// v2 format: $JS.(ACK|FC).<domain>.<accHash>.<stream>.<consumer>.etc.
o.fcPre = fmt.Sprintf("%s%s.%s.", jsFlowControlPre, domain, accHash)
o.fcSubj = fmt.Sprintf(jsFlowControlV2, domain, accHash, cfg.Name, o.name)
pre := fmt.Sprintf(jsAckTv2, domain, accHash, mn, on)
o.ackReplyT = fmt.Sprintf("%s.%%d.%%d.%%d.%%d.%%d", pre)
// Subscribe on this ack subject for v2, we require 11 tokens, but allow for more tokens/extension.
o.ackSubj = fmt.Sprintf("%s.*.*.*.*.>", pre)
o.nextMsgSubj = fmt.Sprintf(JSApiRequestNextT, mn, o.name)
o.resetSubj = fmt.Sprintf(JSApiConsumerResetT, mn, o.name)
// Check/update the inactive threshold
o.updateInactiveThreshold(&o.cfg)
if o.isPushMode() {
// Check if we are running only 1 replica and that the delivery subject has interest.
// Check in place here for interest. Will setup properly in setLeader.
if config.replicas(&cfg) == 1 {
interest := o.acc.sl.HasInterest(o.cfg.DeliverSubject)
if !o.hasDeliveryInterest(interest) {
// Let the interest come to us eventually, but setup delete timer.
o.updateDeliveryInterest(false)
}
}
}
// Set our ca.
if ca != nil {
o.setConsumerAssignment(ca)
}
// Check if we have a rate limit set.
if config.RateLimit != 0 {
o.setRateLimit(config.RateLimit)
}
mset.setConsumer(o)
mset.mu.Unlock()
if config.Sourcing && standalone {
o.resetStartingSeq(0, _EMPTY_, false)
}
if config.Direct || standalone {
o.setLeader(true)
}
// This is always true in single server mode.
if o.IsLeader() {
// Send advisory.
var suppress bool
if !s.standAloneMode() && ca == nil {
suppress = true
} else if ca != nil {
suppress = ca.hasResponded()
}
if !suppress {
o.sendCreateAdvisory()
}
}
return o, nil
}
// Updates the consumer `dthresh` delete timer duration and set
// cfg.InactiveThreshold to JsDeleteWaitTimeDefault for ephemerals
// if not explicitly already specified by the user.
// Lock should be held.
func (o *consumer) updateInactiveThreshold(cfg *ConsumerConfig) {
// Ephemerals will always have inactive thresholds.
if !o.isDurable() && cfg.InactiveThreshold <= 0 {
// Add in 1 sec of jitter above and beyond the default of 5s.
o.dthresh = JsDeleteWaitTimeDefault + 100*time.Millisecond + time.Duration(rand.Int63n(900))*time.Millisecond
// Only stamp config with default sans jitter.
cfg.InactiveThreshold = JsDeleteWaitTimeDefault
} else if cfg.InactiveThreshold > 0 {
// Add in up to 1 sec of jitter if pull mode.
if o.isPullMode() {
o.dthresh = cfg.InactiveThreshold + 100*time.Millisecond + time.Duration(rand.Int63n(900))*time.Millisecond
} else {
o.dthresh = cfg.InactiveThreshold
}
} else if cfg.InactiveThreshold <= 0 {
// We accept InactiveThreshold be set to 0 (for durables)
o.dthresh = 0
}
}
// Updates the paused state. If we are the leader and the pause deadline
// hasn't passed yet then we will start a timer to kick the consumer once
// that deadline is reached. Lock should be held.
func (o *consumer) updatePauseState(cfg *ConsumerConfig) {
if o.uptmr != nil {
stopAndClearTimer(&o.uptmr)
}
if !o.isLeader() {
// Only the leader will run the timer as only the leader will run
// loopAndGatherMsgs.
return
}
if cfg.PauseUntil == nil || cfg.PauseUntil.IsZero() || cfg.PauseUntil.Before(time.Now()) {
// Either the PauseUntil is unset (is effectively zero) or the
// deadline has already passed, in which case there is nothing
// to do.
return
}
o.uptmr = time.AfterFunc(time.Until(*cfg.PauseUntil), func() {
o.mu.Lock()
defer o.mu.Unlock()
stopAndClearTimer(&o.uptmr)
o.sendPauseAdvisoryLocked(&o.cfg)
o.signalNewMessages()
})
}
func (o *consumer) consumerAssignment() *consumerAssignment {
o.mu.RLock()
defer o.mu.RUnlock()
return o.ca
}
func (o *consumer) setConsumerAssignment(ca *consumerAssignment) {
o.mu.Lock()
defer o.mu.Unlock()
o.ca = ca
if ca == nil {
return
}
// Set our node.
o.node = ca.Group.node
// Trigger update chan.
select {
case o.uch <- struct{}{}:
default:
}
}
func (o *consumer) monitorQuitC() <-chan struct{} {
if o == nil {
return nil
}
o.mu.Lock()
defer o.mu.Unlock()
// Recreate if a prior monitor routine was stopped.
if o.mqch == nil {
o.mqch = make(chan struct{})
}
return o.mqch
}
// signalMonitorQuit signals to exit the monitor loop. If there's no Raft node,
// this will be the only way to stop the monitor goroutine.
func (o *consumer) signalMonitorQuit() {
o.mu.Lock()
defer o.mu.Unlock()
if o.mqch != nil {
close(o.mqch)
o.mqch = nil
}
}
func (o *consumer) updateC() <-chan struct{} {
o.mu.RLock()
defer o.mu.RUnlock()
return o.uch
}
// checkQueueInterest will check on our interest's queue group status.
// Lock should be held.
func (o *consumer) checkQueueInterest() {
if !o.active || o.cfg.DeliverSubject == _EMPTY_ {
return
}
subj := o.dsubj
if subj == _EMPTY_ {
subj = o.cfg.DeliverSubject
}
if rr := o.acc.sl.Match(subj); len(rr.qsubs) > 0 {
// Just grab first
if qsubs := rr.qsubs[0]; len(qsubs) > 0 {
if sub := rr.qsubs[0][0]; len(sub.queue) > 0 {
o.qgroup = string(sub.queue)
}
}
}
}
// clears our node if we have one. When we scale down to 1.
func (o *consumer) clearNode() {
o.mu.Lock()
defer o.mu.Unlock()
if o.node != nil {
o.node.Delete()
o.node = nil
}
}
// IsLeader will return if we are the current leader.
func (o *consumer) IsLeader() bool {
return o.isLeader()
}
// Lock should be held.
func (o *consumer) isLeader() bool {
return o.leader.Load()
}
func (o *consumer) setLeader(isLeader bool) error {
o.mu.RLock()
mset, closed := o.mset, o.closed
movingToClustered := o.node != nil && o.pch == nil
movingToNonClustered := o.node == nil && o.pch != nil
wasLeader := o.leader.Swap(isLeader)
// For clustered new consumers, starting seq selection was deferred from
// addConsumerWithAssignment so the scan wouldn't block the meta apply
// goroutine, run it here on leader-elect instead.
needsSelect := isLeader && !wasLeader && o.dseq == 0 && (o.store == nil || !o.store.HasState())
o.mu.RUnlock()
// If we are here we have a change in leader status.
if isLeader {
if closed || mset == nil {
return nil
}
if needsSelect {
o.mu.Lock()
if err := o.selectStartingSeqNo(); err != nil {
o.srv.Errorf("JetStream consumer '%s > %s > %s' select starting seq failed: %v",
o.acc.Name, o.stream, o.name, err)
o.leader.Store(false)
node := o.node
o.mu.Unlock()
if node != nil {
_ = node.StepDown()
}
return err
}
o.mu.Unlock()
}
if wasLeader {
// If we detect we are scaling up, make sure to create clustered routines and channels.
if movingToClustered {
o.mu.Lock()
// We are moving from R1 to clustered.
o.pch = make(chan struct{}, 1)
go o.loopAndForwardProposals(o.qch)
if o.phead != nil {
select {
case o.pch <- struct{}{}:
default:
}
}
o.mu.Unlock()
} else if movingToNonClustered {
// We are moving from clustered to non-clustered now.
// Set pch to nil so if we scale back up we will recreate the loopAndForward from above.
o.mu.Lock()
pch := o.pch
o.pch = nil
select {
case pch <- struct{}{}:
default:
}
o.mu.Unlock()
}
return nil
}
mset.mu.RLock()
s, jsa, stream := mset.srv, mset.jsa, mset.getCfgName()
mset.mu.RUnlock()
o.mu.Lock()
o.rdq = nil
o.rdqi.Empty()
// Restore our saved state.
// During non-leader status we just update our underlying store when not clustered.
// If clustered we need to propose our initial (possibly skipped ahead) o.sseq to the group.
if o.node == nil || o.dseq > 1 || (o.store != nil && o.store.HasState()) {
o.readStoredState()
} else if o.node != nil && o.sseq >= 1 {
o.updateSkipped(o.sseq)
}
// Setup initial num pending.
o.streamNumPending()
// Cleanup lss when we take over in clustered mode.
if o.hasSkipListPending() && o.sseq >= o.lss.resume {
o.lss = nil
}
// Do info sub.
if o.infoSub == nil && jsa != nil {
isubj := fmt.Sprintf(clusterConsumerInfoT, jsa.acc(), stream, o.name)
// Note below the way we subscribe here is so that we can send requests to ourselves.
o.infoSub, _ = s.systemSubscribe(isubj, _EMPTY_, false, o.sysc, o.handleClusterConsumerInfoRequest)
}
var err error
if o.cfg.AckPolicy != AckNone && o.cfg.AckPolicy != AckFlowControl {
if o.ackSubOld, err = o.subscribeInternal(o.ackSubjOld, o.pushAck); err != nil {
o.mu.Unlock()
return nil
}
if o.ackSub, err = o.subscribeInternal(o.ackSubj, o.pushAck); err != nil {
o.mu.Unlock()
return nil
}
}
// Setup the internal sub for next message requests regardless.
// Will error if wrong mode to provide feedback to users.
if o.reqSub, err = o.subscribeInternal(o.nextMsgSubj, o.processNextMsgReq); err != nil {
o.mu.Unlock()
return nil
}
if o.resetSub, err = o.subscribeInternal(o.resetSubj, o.processResetReq); err != nil {
o.mu.Unlock()
return nil
}
// Check on flow control settings.
if o.cfg.FlowControl {
o.setMaxPendingBytes(JsFlowControlMaxPending)
if o.fcSubOld, err = o.subscribeInternal(o.fcSubjOld, o.processFlowControl); err != nil {
o.mu.Unlock()
return nil
}
if o.fcSub, err = o.subscribeInternal(o.fcSubj, o.processFlowControl); err != nil {
o.mu.Unlock()
return nil
}
}
// If push mode, register for notifications on interest.
if o.isPushMode() {
o.inch = make(chan bool, 8)
o.acc.sl.registerNotification(o.cfg.DeliverSubject, o.cfg.DeliverGroup, o.inch)
if o.active = <-o.inch; o.active {
o.checkQueueInterest()
}
// Check gateways in case they are enabled.
if s.gateway.enabled {
if !o.active {
o.active = s.hasGatewayInterest(o.acc.Name, o.cfg.DeliverSubject)
}
stopAndClearTimer(&o.gwdtmr)
o.gwdtmr = time.AfterFunc(time.Second, func() { o.watchGWinterest() })
}
}
if o.dthresh > 0 && (o.isPullMode() || !o.active) {
// Pull consumer. We run the dtmr all the time for this one.
stopAndClearTimer(&o.dtmr)
o.dtmr = time.AfterFunc(o.dthresh, o.deleteNotActive)
}
// Update the consumer pause tracking.
o.updatePauseState(&o.cfg)
// If we are not in ReplayInstant mode mark us as in replay state until resolved.
if o.cfg.ReplayPolicy != ReplayInstant {
o.replay = true
}
// Recreate quit channel.
o.qch = make(chan struct{})
qch := o.qch
node := o.node
if node != nil && o.pch == nil {
o.pch = make(chan struct{}, 1)
}
pullMode := o.isPullMode()
o.mu.Unlock()
// Check if there are any pending we might need to clean up etc.
o.checkPending()
// Snapshot initial info.
o.infoWithSnap(true)
// These are the labels we will use to annotate our goroutines.
labels := pprofLabels{
"type": "consumer",
"account": mset.accName(),
"stream": mset.name(),
"consumer": o.name,
}
// Now start up Go routine to deliver msgs.
go func() {
setGoRoutineLabels(labels)
o.loopAndGatherMsgs(qch)
}()
// Now start up Go routine to process acks.
go func() {
setGoRoutineLabels(labels)
o.processInboundAcks(qch)
}()
if pullMode {
// Now start up Go routine to process inbound next message requests.
go func() {
setGoRoutineLabels(labels)
o.processInboundNextMsgReqs(qch)
}()
}
// If we are R>1 spin up our proposal loop.
if node != nil {
// Determine if we can send pending requests info to the group.
// They must be on server versions >= 2.7.1
o.checkAndSetPendingRequestsOk()
o.checkPendingRequests()
go func() {
setGoRoutineLabels(labels)
o.loopAndForwardProposals(qch)
}()
}
} else {
// Shutdown the go routines and the subscriptions.
o.mu.Lock()
if o.qch != nil {
close(o.qch)
o.qch = nil
}
// Stop any inactivity timers. Should only be running on leaders.
stopAndClearTimer(&o.dtmr)
// Stop any unpause timers. Should only be running on leaders.
stopAndClearTimer(&o.uptmr)
// Make sure to clear out any re-deliver queues
o.stopAndClearPtmr()
o.rdq = nil
o.rdqi.Empty()
o.pending = nil
o.rsm = nil
o.resetPendingDeliveries()
// ok if they are nil, we protect inside unsubscribe()
o.unsubscribe(o.ackSubOld)
o.unsubscribe(o.ackSub)
o.unsubscribe(o.reqSub)
o.unsubscribe(o.resetSub)
o.unsubscribe(o.fcSubOld)
o.unsubscribe(o.fcSub)
o.ackSubOld, o.ackSub, o.reqSub, o.resetSub, o.fcSubOld, o.fcSub = nil, nil, nil, nil, nil, nil
if o.infoSub != nil {
o.srv.sysUnsubscribe(o.infoSub)
o.infoSub = nil
}
// Reset waiting if we are in pull mode.
if o.isPullMode() {
o.waiting = newWaitQueue(o.cfg.MaxWaiting)
o.nextMsgReqs.drain()
} else if o.srv.gateway.enabled {
stopAndClearTimer(&o.gwdtmr)
}
o.unassignPinId()
// If we were the leader make sure to drain queued up acks.
if wasLeader {
o.ackMsgs.drain()
// Reset amount of acks that need to be processed.
atomic.StoreInt64(&o.awl, 0)
// Also remove any pending replies since we should not be the one to respond at this point.
o.replies = nil
}
o.mu.Unlock()
}
return nil
}
// This is coming on the wire so do not block here.
func (o *consumer) handleClusterConsumerInfoRequest(sub *subscription, c *client, _ *Account, subject, reply string, msg []byte) {
go o.infoWithSnapAndReply(false, reply)
}
// Lock should be held.
func (o *consumer) subscribeInternal(subject string, cb msgHandler) (*subscription, error) {
c := o.client
if c == nil {
return nil, fmt.Errorf("invalid consumer")
}
if !c.srv.EventsEnabled() {
return nil, ErrNoSysAccount
}
if cb == nil {
return nil, fmt.Errorf("undefined message handler")
}
o.sid++
// Now create the subscription
return c.processSub([]byte(subject), nil, []byte(strconv.Itoa(o.sid)), cb, false)
}
// Unsubscribe from our subscription.
// Lock should be held.
func (o *consumer) unsubscribe(sub *subscription) {
if sub == nil || o.client == nil {
return
}
o.client.processUnsub(sub.sid)
}
// We need to make sure we protect access to the outq.
// Do all advisory sends here.
func (o *consumer) sendAdvisory(subject string, e any) {
if o.acc == nil {
return
}
// If there is no one listening for this advisory then save ourselves the effort
// and don't bother encoding the JSON or sending it.
if sl := o.acc.sl; (sl != nil && !sl.HasInterest(subject)) && !o.srv.hasGatewayInterest(o.acc.Name, subject) {
return
}
j, err := json.Marshal(e)
if err != nil {
return
}
o.outq.sendMsg(subject, j)
}
func (o *consumer) sendDeleteAdvisoryLocked() {
e := JSConsumerActionAdvisory{
TypedEvent: TypedEvent{
Type: JSConsumerActionAdvisoryType,
ID: nuid.Next(),
Time: time.Now().UTC(),
},
Stream: o.stream,
Consumer: o.name,
Action: DeleteEvent,
Domain: o.srv.getOpts().JetStreamDomain,
}
subj := JSAdvisoryConsumerDeletedPre + "." + o.stream + "." + o.name
o.sendAdvisory(subj, e)
}
func (o *consumer) sendPinnedAdvisoryLocked(group string) {
e := JSConsumerGroupPinnedAdvisory{
TypedEvent: TypedEvent{
Type: JSConsumerGroupPinnedAdvisoryType,
ID: nuid.Next(),
Time: time.Now().UTC(),
},
Account: o.acc.Name,
Stream: o.stream,
Consumer: o.name,
Domain: o.srv.getOpts().JetStreamDomain,
PinnedClientId: o.currentPinId,
Group: group,
}
subj := JSAdvisoryConsumerPinnedPre + "." + o.stream + "." + o.name
o.sendAdvisory(subj, e)
}
func (o *consumer) sendUnpinnedAdvisoryLocked(group string, reason string) {
e := JSConsumerGroupUnpinnedAdvisory{
TypedEvent: TypedEvent{
Type: JSConsumerGroupUnpinnedAdvisoryType,
ID: nuid.Next(),
Time: time.Now().UTC(),
},
Account: o.acc.Name,
Stream: o.stream,
Consumer: o.name,
Domain: o.srv.getOpts().JetStreamDomain,
Group: group,
Reason: reason,
}
subj := JSAdvisoryConsumerUnpinnedPre + "." + o.stream + "." + o.name
o.sendAdvisory(subj, e)
}
func (o *consumer) sendCreateAdvisory() {
o.mu.Lock()
defer o.mu.Unlock()
e := JSConsumerActionAdvisory{
TypedEvent: TypedEvent{
Type: JSConsumerActionAdvisoryType,
ID: nuid.Next(),
Time: time.Now().UTC(),
},
Stream: o.stream,
Consumer: o.name,
Action: CreateEvent,
Domain: o.srv.getOpts().JetStreamDomain,
}
subj := JSAdvisoryConsumerCreatedPre + "." + o.stream + "." + o.name
o.sendAdvisory(subj, e)
}
func (o *consumer) sendPauseAdvisoryLocked(cfg *ConsumerConfig) {
e := JSConsumerPauseAdvisory{
TypedEvent: TypedEvent{
Type: JSConsumerPauseAdvisoryType,
ID: nuid.Next(),
Time: time.Now().UTC(),
},
Stream: o.stream,
Consumer: o.name,
Domain: o.srv.getOpts().JetStreamDomain,
}
if cfg.PauseUntil != nil {
e.PauseUntil = *cfg.PauseUntil
e.Paused = time.Now().Before(e.PauseUntil)
}
subj := JSAdvisoryConsumerPausePre + "." + o.stream + "." + o.name
o.sendAdvisory(subj, e)
}
// Created returns created time.
func (o *consumer) createdTime() time.Time {
o.mu.Lock()
created := o.created
o.mu.Unlock()
return created
}
// Internal to allow creation time to be restored.
func (o *consumer) setCreatedTime(created time.Time) {
o.mu.Lock()
o.created = created
o.mu.Unlock()
}
// This will check for extended interest in a subject. If we have local interest we just return
// that, but in the absence of local interest and presence of gateways or service imports we need
// to check those as well.
func (o *consumer) hasDeliveryInterest(localInterest bool) bool {
o.mu.RLock()
mset := o.mset
if mset == nil {
o.mu.RUnlock()
return false
}
acc := o.acc
deliver := o.cfg.DeliverSubject
o.mu.RUnlock()
if localInterest {
return true
}
// If we are here check gateways.
if s := acc.srv; s != nil && s.hasGatewayInterest(acc.Name, deliver) {
return true
}
return false
}
func (s *Server) hasGatewayInterest(account, subject string) bool {
gw := s.gateway
if !gw.enabled {
return false
}
gw.RLock()
defer gw.RUnlock()
for _, gwc := range gw.outo {
psi, qr := gwc.gatewayInterest(account, stringToBytes(subject))
if psi || qr != nil {
return true
}
}
return false
}
// This processes an update to the local interest for a deliver subject.
func (o *consumer) updateDeliveryInterest(localInterest bool) bool {
interest := o.hasDeliveryInterest(localInterest)
o.mu.Lock()
defer o.mu.Unlock()
mset := o.mset
if mset == nil || o.isPullMode() {
return false
}
if interest && !o.active {
o.signalNewMessages()
}
// Update active status, if not active clear any queue group we captured.
if o.active = interest; !o.active {
o.qgroup = _EMPTY_
} else {
o.checkQueueInterest()
}
// If the delete timer has already been set do not clear here and return.
// Note that durable can now have an inactive threshold, so don't check
// for durable status, instead check for dthresh > 0.
if o.dtmr != nil && o.dthresh > 0 && !interest {
return true
}
// Stop and clear the delete timer always.
stopAndClearTimer(&o.dtmr)
// If we do not have interest anymore and have a delete threshold set, then set
// a timer to delete us. We wait for a bit in case of server reconnect.
if !interest && o.dthresh > 0 {
o.dtmr = time.AfterFunc(o.dthresh, o.deleteNotActive)
return true
}
return false
}
const (
defaultConsumerNotActiveStartInterval = 30 * time.Second
defaultConsumerNotActiveMaxInterval = 5 * time.Minute
)
var (
consumerNotActiveStartInterval = defaultConsumerNotActiveStartInterval
consumerNotActiveMaxInterval = defaultConsumerNotActiveMaxInterval
)
// deleteNotActive must only be called from time.AfterFunc or in its own
// goroutine, as it can block on clean-up.
func (o *consumer) deleteNotActive() {
// Take a copy of these when the goroutine starts, mostly it avoids a
// race condition with tests that modify these consts, such as
// TestJetStreamClusterGhostEphemeralsAfterRestart.
cnaMax := consumerNotActiveMaxInterval
cnaStart := consumerNotActiveStartInterval
o.mu.Lock()
if o.mset == nil {
o.mu.Unlock()
return
}
// Push mode just look at active.
if o.isPushMode() {
// If we are active simply return.
if o.active {
o.mu.Unlock()
return
}
} else {
// Pull mode.
elapsed := time.Since(o.waiting.last)
if elapsed < o.dthresh {
// These need to keep firing so reset but use delta.
if o.dtmr != nil {
o.dtmr.Reset(o.dthresh - elapsed)
} else {
o.dtmr = time.AfterFunc(o.dthresh-elapsed, o.deleteNotActive)
}
o.mu.Unlock()
return
}
// Check if we still have valid requests waiting.
if o.checkWaitingForInterest() {
if o.dtmr != nil {
o.dtmr.Reset(o.dthresh)
} else {
o.dtmr = time.AfterFunc(o.dthresh, o.deleteNotActive)
}
o.mu.Unlock()
return
}
// We now know we have no waiting requests, and our last request was long ago.
// However, based on AckWait the consumer could still be actively processing,
// even if we haven't been informed if there were no acks in the meantime.
// We must wait for the message that expires last and start counting down the
// inactive threshold from there.
now := time.Now().UnixNano()
l := len(o.cfg.BackOff)
var delay time.Duration
var ackWait time.Duration
for _, p := range o.pending {
if l == 0 {
ackWait = o.ackWait(0)
} else {
bi := int(o.rdc[p.Sequence])
if bi < 0 {
bi = 0
} else if bi >= l {
bi = l - 1
}
ackWait = o.ackWait(o.cfg.BackOff[bi])
}
if ts := p.Timestamp + ackWait.Nanoseconds() + o.dthresh.Nanoseconds(); ts > now {
delay = max(delay, time.Duration(ts-now))
}
}
// We'll wait for the latest time we expect an ack, plus the inactive threshold.
// Acknowledging a message will reset this back down to just the inactive threshold.
if delay > 0 {
if o.dtmr != nil {
o.dtmr.Reset(delay)
} else {
o.dtmr = time.AfterFunc(delay, o.deleteNotActive)
}
o.mu.Unlock()
return
}
}
s, js := o.mset.srv, o.srv.js.Load()
acc, stream, name, isDirect := o.acc.Name, o.stream, o.name, o.cfg.Direct
var qch, cqch chan struct{}
if o.srv != nil {
qch = o.srv.quitCh
}
oqch := o.qch
o.mu.Unlock()
if js != nil {
cqch = js.clusterQuitC()
}
// Useful for pprof.
setGoRoutineLabels(pprofLabels{
"account": acc,
"stream": stream,
"consumer": name,
})
// We will delete locally regardless.
defer o.delete()
// If we are clustered, check if we still have this consumer assigned.
// If we do forward a proposal to delete ourselves to the metacontroller leader.
if !isDirect && s.JetStreamIsClustered() {
js.mu.RLock()
var (
meta RaftNode
removeEntry []byte
)
ca, cc := js.consumerAssignment(acc, stream, name), js.cluster
if ca != nil && cc != nil {
meta = cc.meta
cca := ca.clone()
cca.Reply = _EMPTY_
removeEntry = encodeDeleteConsumerAssignment(cca)
meta.ForwardProposal(removeEntry)
}
js.mu.RUnlock()
if ca != nil && cc != nil {
// Check to make sure we went away.
// Don't think this needs to be a monitored go routine.
jitter := time.Duration(rand.Int63n(int64(cnaStart)))
interval := cnaStart + jitter
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
case <-qch:
return
case <-cqch:
return
case <-oqch:
// The consumer has stopped already, likely by an earlier delete proposal being applied.
return
}
js.mu.RLock()
if js.shuttingDown {
js.mu.RUnlock()
return
}
nca := js.consumerAssignment(acc, stream, name)
js.mu.RUnlock()
// Make sure this is the same consumer assignment, and not a new consumer with the same name.
if nca != nil && reflect.DeepEqual(nca, ca) {
s.Warnf("Consumer assignment for '%s > %s > %s' not cleaned up, retrying", acc, stream, name)
meta.ForwardProposal(removeEntry)
if interval < cnaMax {
interval *= 2
ticker.Reset(interval)
}
continue
}
// We saw that consumer has been removed, all done.
return
}
}
}
}
func (o *consumer) watchGWinterest() {
pa := o.isActive()
// If there is no local interest...
if o.hasNoLocalInterest() {
o.updateDeliveryInterest(false)
if !pa && o.isActive() {
o.signalNewMessages()
}
}
// We want this to always be running so we can also pick up on interest returning.
o.mu.Lock()
if o.gwdtmr != nil {
o.gwdtmr.Reset(time.Second)
} else {
stopAndClearTimer(&o.gwdtmr)
o.gwdtmr = time.AfterFunc(time.Second, func() { o.watchGWinterest() })
}
o.mu.Unlock()
}
// Config returns the consumer's configuration.
func (o *consumer) config() ConsumerConfig {
o.mu.Lock()
defer o.mu.Unlock()
return o.cfg
}
// Check if we have hit max deliveries. If so do notification and cleanup.
// Return whether or not the max was hit.
// Lock should be held.
func (o *consumer) hasMaxDeliveries(seq uint64) bool {
if o.maxdc == 0 {
return false
}
if dc := o.deliveryCount(seq); dc >= o.maxdc {
// We have hit our max deliveries for this sequence.
// Only send the advisory once.
if dc == o.maxdc {
o.notifyDeliveryExceeded(seq, dc)
}
// Determine if we signal to start flow of messages again.
if o.maxp > 0 && len(o.pending) >= o.maxp {
o.signalNewMessages()
}
// Make sure to remove from pending.
if p, ok := o.pending[seq]; ok && p != nil {
delete(o.pending, seq)
o.updateDelivered(p.Sequence, seq, dc, p.Timestamp)
}
// Ensure redelivered state is set, if not already.
if o.rdc == nil {
o.rdc = make(map[uint64]uint64)
}
o.rdc[seq] = dc
return true
}
return false
}
// Force expiration of all pending.
// Lock should be held.
func (o *consumer) forceExpirePending() {
var expired []uint64
for seq := range o.pending {
if !o.onRedeliverQueue(seq) && !o.hasMaxDeliveries(seq) {
expired = append(expired, seq)
}
}
if len(expired) > 0 {
slices.Sort(expired)
o.addToRedeliverQueue(expired...)
// Now we should update the timestamp here since we are redelivering.
// We will use an incrementing time to preserve order for any other redelivery.
off := time.Now().UnixNano() - o.pending[expired[0]].Timestamp
for _, seq := range expired {
if p, ok := o.pending[seq]; ok && p != nil {
p.Timestamp += off
}
}
o.resetPtmr(o.ackWait(0))
}
o.signalNewMessages()
}
// Acquire proper locks and update rate limit.
// Will use what is in config.
func (o *consumer) setRateLimitNeedsLocks() {
o.mu.RLock()
mset := o.mset
o.mu.RUnlock()
if mset == nil {
return
}
mset.mu.RLock()
o.mu.Lock()
o.setRateLimit(o.cfg.RateLimit)
o.mu.Unlock()
mset.mu.RUnlock()
}
// Set the rate limiter
// Both mset and consumer lock should be held.
func (o *consumer) setRateLimit(bps uint64) {
if bps == 0 {
o.rlimit = nil
return
}
// TODO(dlc) - Make sane values or error if not sane?
// We are configured in bits per sec so adjust to bytes.
rl := rate.Limit(bps / 8)
mset := o.mset
// Burst should be set to maximum msg size for this account, etc.
var burst int
// We don't need to get cfgMu's rlock here since this function
// is already invoked under mset.mu.RLock(), which superseeds cfgMu.
if mset.cfg.MaxMsgSize > 0 {
burst = int(mset.cfg.MaxMsgSize)
} else if mset.jsa.account.limits.mpay > 0 {
burst = int(mset.jsa.account.limits.mpay)
} else {
s := mset.jsa.account.srv
burst = int(s.getOpts().MaxPayload)
}
o.rlimit = rate.NewLimiter(rl, burst)
}
// Check if new consumer config allowed vs old.
func (acc *Account) checkNewConsumerConfig(cfg, ncfg *ConsumerConfig) error {
if reflect.DeepEqual(cfg, ncfg) {
return nil
}
// Something different, so check since we only allow certain things to be updated.
if cfg.DeliverPolicy != ncfg.DeliverPolicy {
return errors.New("deliver policy can not be updated")
}
if cfg.OptStartSeq != ncfg.OptStartSeq {
return errors.New("start sequence can not be updated")
}
if cfg.OptStartTime != nil && ncfg.OptStartTime != nil {
// Both have start times set, compare them directly:
if !cfg.OptStartTime.Equal(*ncfg.OptStartTime) {
return errors.New("start time can not be updated")
}
} else if cfg.OptStartTime != nil || ncfg.OptStartTime != nil {
// At least one start time is set and the other is not
return errors.New("start time can not be updated")
}
if cfg.AckPolicy != ncfg.AckPolicy {
return errors.New("ack policy can not be updated")
}
if cfg.ReplayPolicy != ncfg.ReplayPolicy {
return errors.New("replay policy can not be updated")
}
if cfg.Heartbeat != ncfg.Heartbeat {
return errors.New("heart beats can not be updated")
}
if cfg.FlowControl != ncfg.FlowControl {
return errors.New("flow control can not be updated")
}
// Deliver Subject is conditional on if its bound.
if cfg.DeliverSubject != ncfg.DeliverSubject {
if cfg.DeliverSubject == _EMPTY_ {
return errors.New("can not update pull consumer to push based")
}
if ncfg.DeliverSubject == _EMPTY_ {
return errors.New("can not update push consumer to pull based")
}
if acc.sl.HasInterest(cfg.DeliverSubject) {
return NewJSConsumerNameExistError()
}
}
if cfg.MaxWaiting != ncfg.MaxWaiting {
return errors.New("max waiting can not be updated")
}
// Check if BackOff is defined, MaxDeliver is within range.
if lbo := len(ncfg.BackOff); lbo > 0 && ncfg.MaxDeliver != -1 && lbo > ncfg.MaxDeliver {
return NewJSConsumerMaxDeliverBackoffError()
}
return nil
}
// Update the config based on the new config, or error if update not allowed.
func (o *consumer) updateConfig(cfg *ConsumerConfig) error {
o.mu.Lock()
defer o.mu.Unlock()
if o.closed || o.mset == nil {
return NewJSConsumerDoesNotExistError()
}
if err := o.acc.checkNewConsumerConfig(&o.cfg, cfg); err != nil {
return err
}
// Make sure we always store PauseUntil in UTC.
if cfg.PauseUntil != nil {
utc := (*cfg.PauseUntil).UTC()
cfg.PauseUntil = &utc
}
if o.store != nil {
// Update local state always.
if err := o.store.UpdateConfig(cfg); err != nil {
return err
}
}
// DeliverSubject
if cfg.DeliverSubject != o.cfg.DeliverSubject {
o.updateDeliverSubjectLocked(cfg.DeliverSubject)
}
// MaxAckPending
if cfg.MaxAckPending != o.cfg.MaxAckPending {
o.maxp = cfg.MaxAckPending
o.signalNewMessages()
// If MaxAckPending is lowered, we could have allocated a pending deliveries map of larger size.
// Reset it here, so we can shrink the map.
if cfg.MaxAckPending < o.cfg.MaxAckPending {
o.resetPendingDeliveries()
}
}
// AckWait
if cfg.AckWait != o.cfg.AckWait {
if o.ptmr != nil {
o.resetPtmr(100 * time.Millisecond)
}
}
// Rate Limit
if cfg.RateLimit != o.cfg.RateLimit {
// We need both locks here so do in Go routine.
go o.setRateLimitNeedsLocks()
}
if cfg.SampleFrequency != o.cfg.SampleFrequency {
s := strings.TrimSuffix(cfg.SampleFrequency, "%")
if sampleFreq, err := strconv.ParseInt(s, 10, 32); err == nil {
o.sfreq = int32(sampleFreq)
}
}
// Set MaxDeliver if changed
if cfg.MaxDeliver != o.cfg.MaxDeliver {
// MaxDeliver is negative (-1) when infinite.
o.maxdc = uint64(max(cfg.MaxDeliver, 0))
}
// Set InactiveThreshold if changed.
if val := cfg.InactiveThreshold; val != o.cfg.InactiveThreshold {
o.updateInactiveThreshold(cfg)
stopAndClearTimer(&o.dtmr)
// Restart timer only if we are the leader.
if o.isLeader() && o.dthresh > 0 {
o.dtmr = time.AfterFunc(o.dthresh, o.deleteNotActive)
}
}
// Check whether the pause has changed
{
var old, new time.Time
if o.cfg.PauseUntil != nil {
old = *o.cfg.PauseUntil
}
if cfg.PauseUntil != nil {
new = *cfg.PauseUntil
}
if !old.Equal(new) {
o.updatePauseState(cfg)
if o.isLeader() {
o.sendPauseAdvisoryLocked(cfg)
}
}
}
// Check for Subject Filters update.
newSubjects := gatherSubjectFilters(cfg.FilterSubject, cfg.FilterSubjects)
updatedFilters := !subjectSliceEqual(newSubjects, o.subjf.subjects())
if updatedFilters {
newSubjf := make(subjectFilters, 0, len(newSubjects))
for _, newFilter := range newSubjects {
fs := &subjectFilter{
subject: newFilter,
hasWildcard: subjectHasWildcard(newFilter),
tokenizedSubject: tokenizeSubjectIntoSlice(nil, newFilter),
}
newSubjf = append(newSubjf, fs)
}
// Make sure we have correct signaling setup.
// Consumer lock can not be held.
mset := o.mset
o.mu.Unlock()
mset.swapSigSubs(o, newSubjf.subjects())
o.mu.Lock()
// When we're done with signaling, we can replace the subjects.
// If filters were removed, set `o.subjf` to nil.
if len(newSubjf) == 0 {
o.subjf = nil
o.filters = nil
} else {
o.subjf = newSubjf
if len(o.subjf) == 1 {
o.filters = nil
} else {
o.filters = gsl.NewSublist[struct{}]()
for _, filter := range o.subjf {
o.filters.Insert(filter.subject, struct{}{})
}
}
}
}
// Record new config for others that do not need special handling.
// Allowed but considered no-op, [Description, SampleFrequency, MaxWaiting, HeadersOnly]
o.cfg = *cfg
if cfg.Sourcing && (!o.srv.JetStreamIsClustered() && o.srv.standAloneMode()) {
o.resetStartingSeqLocked(0, _EMPTY_, false)
}
if updatedFilters {
// Cleanup messages that lost interest.
if o.retention == InterestPolicy {
o.mu.Unlock()
o.cleanupNoInterestMessages(o.mset, false)
o.mu.Lock()
}
// Re-calculate num pending on update.
o.streamNumPending()
}
return nil
}
// This is a config change for the delivery subject for a
// push based consumer.
func (o *consumer) updateDeliverSubject(newDeliver string) {
// Update the config and the dsubj
o.mu.Lock()
defer o.mu.Unlock()
o.updateDeliverSubjectLocked(newDeliver)
}
// This is a config change for the delivery subject for a
// push based consumer.
func (o *consumer) updateDeliverSubjectLocked(newDeliver string) {
if o.closed || o.isPullMode() || o.cfg.DeliverSubject == newDeliver {
return
}
// Force redeliver of all pending on change of delivery subject.
if len(o.pending) > 0 {
o.forceExpirePending()
}
o.acc.sl.clearNotification(o.dsubj, o.cfg.DeliverGroup, o.inch)
o.dsubj, o.cfg.DeliverSubject = newDeliver, newDeliver
// When we register new one it will deliver to update state loop.
o.acc.sl.registerNotification(newDeliver, o.cfg.DeliverGroup, o.inch)
}
// Check that configs are equal but allow delivery subjects to be different.
func configsEqualSansDelivery(a, b ConsumerConfig) bool {
// These were copied in so can set Delivery here.
a.DeliverSubject, b.DeliverSubject = _EMPTY_, _EMPTY_
return reflect.DeepEqual(a, b)
}
// Helper to send a reply to an ack.
func (o *consumer) sendAckReply(subj string) {
o.mu.RLock()
defer o.mu.RUnlock()
o.outq.sendMsg(subj, nil)
}
type jsAckMsg struct {
subject string
reply string
hdr int
msg []byte
}
var jsAckMsgPool sync.Pool
func newJSAckMsg(subj, reply string, hdr int, msg []byte) *jsAckMsg {
var m *jsAckMsg
am := jsAckMsgPool.Get()
if am != nil {
m = am.(*jsAckMsg)
} else {
m = &jsAckMsg{}
}
// When getting something from a pool it is critical that all fields are
// initialized. Doing this way guarantees that if someone adds a field to
// the structure, the compiler will fail the build if this line is not updated.
(*m) = jsAckMsg{subj, reply, hdr, msg}
return m
}
func (am *jsAckMsg) returnToPool() {
if am == nil {
return
}
am.subject, am.reply, am.hdr, am.msg = _EMPTY_, _EMPTY_, -1, nil
jsAckMsgPool.Put(am)
}
// Push the ack message to the consumer's ackMsgs queue
func (o *consumer) pushAck(_ *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) {
atomic.AddInt64(&o.awl, 1)
o.ackMsgs.push(newJSAckMsg(subject, reply, c.pa.hdr, copyBytes(rmsg)))
}
// Processes a message for the ack reply subject delivered with a message.
func (o *consumer) processAck(subject, reply string, hdr int, rmsg []byte) {
defer atomic.AddInt64(&o.awl, -1)
var msg []byte
if hdr > 0 {
msg = rmsg[hdr:]
} else {
msg = rmsg
}
sseq, dseq, dc, _, _ := ackReplyInfo(subject)
skipAckReply := sseq == 0
switch {
case len(msg) == 0, bytes.Equal(msg, AckAck), bytes.Equal(msg, AckOK):
if !o.processAckMsg(sseq, dseq, dc, reply, true) {
// We handle replies for acks in updateAcks
skipAckReply = true
}
case bytes.HasPrefix(msg, AckNext):
o.processAckMsg(sseq, dseq, dc, _EMPTY_, true)
o.processNextMsgRequest(reply, msg[len(AckNext):])
skipAckReply = true
case bytes.HasPrefix(msg, AckNak):
o.processNak(sseq, dseq, dc, msg)
case bytes.Equal(msg, AckProgress):
o.progressUpdate(sseq)
case bytes.HasPrefix(msg, AckTerm):
var reason string
if buf := msg[len(AckTerm):]; len(buf) > 0 {
reason = string(bytes.TrimSpace(buf))
}
if !o.processTerm(sseq, dseq, dc, reason, reply) {
// We handle replies for acks in updateAcks
skipAckReply = true
}
}
// Ack the ack if requested.
if len(reply) > 0 && !skipAckReply {
o.sendAckReply(reply)
}
}
// Used to process a working update to delay redelivery.
func (o *consumer) progressUpdate(seq uint64) {
o.mu.Lock()
defer o.mu.Unlock()
if p, ok := o.pending[seq]; ok {
p.Timestamp = time.Now().UnixNano()
// Update store system.
o.updateDelivered(p.Sequence, seq, 1, p.Timestamp)
}
}
// Lock should be held.
func (o *consumer) updateSkipped(seq uint64) {
// Clustered mode and R>1 only.
if o.node == nil || !o.isLeader() {
return
}
var b [1 + 8]byte
b[0] = byte(updateSkipOp)
var le = binary.LittleEndian
le.PutUint64(b[1:], seq)
o.propose(b[:])
}
func (o *consumer) resetStartingSeq(seq uint64, reply string, internal bool) (uint64, bool, error) {
o.mu.Lock()
defer o.mu.Unlock()
return o.resetStartingSeqLocked(seq, reply, internal)
}
// Lock should be held.
func (o *consumer) resetStartingSeqLocked(seq uint64, reply string, internal bool) (uint64, bool, error) {
// Reset to a specific sequence, or back to the ack floor.
if seq == 0 {
seq = o.asflr + 1
} else if o.cfg.DeliverPolicy == DeliverAll {
// Always allowed.
goto VALID
} else if o.cfg.DeliverPolicy == DeliverByStartSequence {
// Only allowed if not going below what's configured.
if seq < o.cfg.OptStartSeq {
return 0, false, errors.New("below start seq")
}
goto VALID
} else if o.cfg.DeliverPolicy == DeliverByStartTime && o.mset != nil {
// Only allowed if not going below what's configured.
nseq := o.mset.store.GetSeqFromTime(*o.cfg.OptStartTime)
if seq < nseq {
return 0, false, errors.New("below start time")
}
goto VALID
} else {
return 0, false, errors.New("not allowed")
}
VALID:
// Must be a minimum of 1.
if seq <= 0 {
seq = 1
}
// The replicated path requires quorum first before the reset actually takes effect.
if o.node != nil {
if !o.isLeader() {
return 0, false, nil
}
b := make([]byte, 1+8+len(reply))
b[0] = byte(resetSeqOp)
var le = binary.LittleEndian
le.PutUint64(b[1:], seq)
copy(b[1+8:], reply)
o.propose(b[:])
if reply != _EMPTY_ {
if o.rsm == nil {
o.rsm = make(map[string]bool, 1)
}
o.rsm[reply] = internal
}
return seq, false, nil
}
o.resetLocalStartingSeq(seq)
if o.store != nil {
o.store.Reset(seq - 1)
// Cleanup messages that lost interest.
if o.retention == InterestPolicy {
if mset := o.mset; mset != nil {
o.mu.Unlock()
ss := mset.state()
o.checkStateForInterestStream(&ss)
o.mu.Lock()
}
}
// Recalculate pending, and re-trigger message delivery.
o.streamNumPending()
o.signalNewMessages()
return seq, true, nil
}
return seq, false, nil
}
// Lock should be held.
func (o *consumer) resetLocalStartingSeq(seq uint64) {
o.pending, o.rdc = nil, nil
o.rdq = nil
o.rdqi.Empty()
o.sseq, o.dseq = seq, 1
o.adflr, o.asflr = o.dseq-1, o.sseq-1
o.ldt, o.lat = time.Time{}, time.Time{}
}
func (o *consumer) loopAndForwardProposals(qch chan struct{}) {
// On exit make sure we nil out pch.
defer func() {
o.mu.Lock()
o.pch = nil
o.mu.Unlock()
}()
o.mu.RLock()
node, pch := o.node, o.pch
o.mu.RUnlock()
if node == nil || pch == nil {
return
}
forwardProposals := func() error {
o.mu.Lock()
if o.node == nil || !o.node.Leader() {
o.mu.Unlock()
return errors.New("no longer leader")
}
proposal := o.phead
o.phead, o.ptail = nil, nil
o.mu.Unlock()
// 256k max for now per batch.
const maxBatch = 256 * 1024
var entries []*Entry
for sz := 0; proposal != nil; proposal = proposal.next {
entries = append(entries, newEntry(EntryNormal, proposal.data))
sz += len(proposal.data)
if sz > maxBatch {
node.ProposeMulti(entries)
// We need to re-create `entries` because there is a reference
// to it in the node's pae map.
sz, entries = 0, nil
}
}
if len(entries) > 0 {
node.ProposeMulti(entries)
}
return nil
}
// In case we have anything pending on entry.
forwardProposals()
for {
select {
case <-qch:
forwardProposals()
return
case <-pch:
if err := forwardProposals(); err != nil {
return
}
}
}
}
// Lock should be held.
func (o *consumer) propose(entry []byte) {
p := &proposal{data: entry}
if o.phead == nil {
o.phead = p
} else {
o.ptail.next = p
}
o.ptail = p
// Kick our looper routine.
select {
case o.pch <- struct{}{}:
default:
}
}
// Lock should be held.
func (o *consumer) updateDelivered(dseq, sseq, dc uint64, ts int64) {
// Clustered mode and R>1.
if o.node != nil {
// Inline for now, use variable compression.
var b [4*binary.MaxVarintLen64 + 1]byte
b[0] = byte(updateDeliveredOp)
n := 1
n += binary.PutUvarint(b[n:], dseq)
n += binary.PutUvarint(b[n:], sseq)
n += binary.PutUvarint(b[n:], dc)
n += binary.PutVarint(b[n:], ts)
o.propose(b[:n])
} else if o.store != nil {
o.store.UpdateDelivered(dseq, sseq, dc, ts)
}
// Update activity.
o.ldt = time.Now()
}
// Used to remember a pending ack reply in a replicated consumer.
// Lock should be held.
func (o *consumer) addAckReply(sseq uint64, reply string) {
if o.replies == nil {
o.replies = make(map[uint64]string)
}
o.replies[sseq] = reply
}
// Used to remember messages that need to be sent for a replicated consumer, after delivered quorum.
// Lock should be held.
func (o *consumer) addReplicatedQueuedMsg(pmsg *jsPubMsg) {
// Is not explicitly limited in size, but will at most hold maximum ack pending.
if o.pendingDeliveries == nil {
o.pendingDeliveries = make(map[uint64]*jsPubMsg)
}
o.pendingDeliveries[pmsg.seq] = pmsg
// Is not explicitly limited in size, but will at most hold maximum waiting requests.
if o.waitingDeliveries == nil {
o.waitingDeliveries = make(map[string]*waitingDelivery)
}
if wd, ok := o.waitingDeliveries[pmsg.dsubj]; ok {
wd.seq = pmsg.seq
} else {
wd := wdPool.Get().(*waitingDelivery)
wd.seq = pmsg.seq
o.waitingDeliveries[pmsg.dsubj] = wd
}
}
// Lock should be held.
func (o *consumer) updateAcks(dseq, sseq uint64, reply string) {
if o.node != nil {
// Inline for now, use variable compression.
var b [2*binary.MaxVarintLen64 + 1]byte
b[0] = byte(updateAcksOp)
n := 1
n += binary.PutUvarint(b[n:], dseq)
n += binary.PutUvarint(b[n:], sseq)
o.propose(b[:n])
if reply != _EMPTY_ {
o.addAckReply(sseq, reply)
}
} else if o.store != nil {
o.store.UpdateAcks(dseq, sseq)
if reply != _EMPTY_ {
// Already locked so send direct.
o.outq.sendMsg(reply, nil)
}
}
// Update activity.
o.lat = time.Now()
}
// Communicate to the cluster an addition of a pending request.
// Lock should be held.
func (o *consumer) addClusterPendingRequest(reply string) {
if o.node == nil || !o.pendingRequestsOk() {
return
}
b := make([]byte, len(reply)+1)
b[0] = byte(addPendingRequest)
copy(b[1:], reply)
o.propose(b)
}
// Communicate to the cluster a removal of a pending request.
// Lock should be held.
func (o *consumer) removeClusterPendingRequest(reply string) {
if o.node == nil || !o.pendingRequestsOk() {
return
}
b := make([]byte, len(reply)+1)
b[0] = byte(removePendingRequest)
copy(b[1:], reply)
o.propose(b)
}
// Set whether or not we can send pending requests to followers.
func (o *consumer) setPendingRequestsOk(ok bool) {
o.mu.Lock()
o.prOk = ok
o.mu.Unlock()
}
// Lock should be held.
func (o *consumer) pendingRequestsOk() bool {
return o.prOk
}
// Set whether or not we can send info about pending pull requests to our group.
// Will require all peers have a minimum version.
func (o *consumer) checkAndSetPendingRequestsOk() {
o.mu.RLock()
s, isValid := o.srv, o.mset != nil
o.mu.RUnlock()
if !isValid {
return
}
if ca := o.consumerAssignment(); ca != nil && len(ca.Group.Peers) > 1 {
for _, pn := range ca.Group.Peers {
if si, ok := s.nodeToInfo.Load(pn); ok {
if !versionAtLeast(si.(nodeInfo).version, 2, 7, 1) {
// We expect all of our peers to eventually be up to date.
// So check again in awhile.
time.AfterFunc(eventsHBInterval, func() { o.checkAndSetPendingRequestsOk() })
o.setPendingRequestsOk(false)
return
}
}
}
}
o.setPendingRequestsOk(true)
}
// On leadership change make sure we alert the pending requests that they are no longer valid.
func (o *consumer) checkPendingRequests() {
o.mu.Lock()
defer o.mu.Unlock()
if o.mset == nil || o.outq == nil {
return
}
hdr := []byte("NATS/1.0 409 Leadership Change\r\n\r\n")
for reply := range o.prm {
o.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
}
o.prm = nil
}
// This will release any pending pull requests if applicable.
// Should be called only by the leader being deleted or stopped.
// Lock should be held.
func (o *consumer) releaseAnyPendingRequests(isAssigned bool) {
if o.mset == nil || o.outq == nil || o.waiting.len() == 0 {
return
}
wq := o.waiting
for wr := wq.head; wr != nil; {
if !isAssigned {
hdr := []byte("NATS/1.0 409 Consumer Deleted\r\n\r\n")
o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
}
next := wr.next
wr.recycle()
wr = next
}
// Nil out old queue.
o.waiting = nil
}
// Process a NAK.
func (o *consumer) processNak(sseq, dseq, dc uint64, nak []byte) {
o.mu.Lock()
defer o.mu.Unlock()
// Check for out of range.
if dseq <= o.adflr || dseq > o.dseq {
return
}
// If we are explicit ack make sure this is still on our pending list.
if _, ok := o.pending[sseq]; !ok {
return
}
// Deliver an advisory
e := JSConsumerDeliveryNakAdvisory{
TypedEvent: TypedEvent{
Type: JSConsumerDeliveryNakAdvisoryType,
ID: nuid.Next(),
Time: time.Now().UTC(),
},
Stream: o.stream,
Consumer: o.name,
ConsumerSeq: dseq,
StreamSeq: sseq,
Deliveries: dc,
Domain: o.srv.getOpts().JetStreamDomain,
}
o.sendAdvisory(o.nakEventT, e)
// Check to see if we have delays attached.
if len(nak) > len(AckNak) {
arg := bytes.TrimSpace(nak[len(AckNak):])
if len(arg) > 0 {
var d time.Duration
var err error
if arg[0] == '{' {
var nd ConsumerNakOptions
if err = json.Unmarshal(arg, &nd); err == nil {
d = nd.Delay
}
} else {
d, err = time.ParseDuration(string(arg))
}
if err != nil {
// Treat this as normal NAK.
o.srv.Warnf("JetStream consumer '%s > %s > %s' bad NAK delay value: %q", o.acc.Name, o.stream, o.name, arg)
} else {
// We have a parsed duration that the user wants us to wait before retrying.
// Make sure we are not on the rdq.
o.removeFromRedeliverQueue(sseq)
if p, ok := o.pending[sseq]; ok {
// now - ackWait is expired now, so offset from there.
p.Timestamp = time.Now().Add(-o.cfg.AckWait).Add(d).UnixNano()
// Update store system which will update followers as well.
o.updateDelivered(p.Sequence, sseq, dc, p.Timestamp)
if o.ptmr != nil {
// Want checkPending to run and figure out the next timer ttl.
// TODO(dlc) - We could optimize this maybe a bit more and track when we expect the timer to fire.
o.resetPtmr(10 * time.Millisecond)
}
}
// Nothing else for use to do now so return.
return
}
}
}
// If already queued up also ignore.
if !o.onRedeliverQueue(sseq) {
o.addToRedeliverQueue(sseq)
}
o.signalNewMessages()
}
// Process a TERM
// Returns `true` if the ack was processed in place and the sender can now respond
// to the client, or `false` if there was an error or the ack is replicated (in which
// case the reply will be sent later).
func (o *consumer) processTerm(sseq, dseq, dc uint64, reason, reply string) bool {
return o.processTermLocked(sseq, dseq, dc, reason, reply, true)
}
func (o *consumer) processTermLocked(sseq, dseq, dc uint64, reason, reply string, needLock bool) bool {
// Treat like an ack to suppress redelivery.
ackedInPlace := o.processAckMsgLocked(sseq, dseq, dc, reply, false, needLock)
if needLock {
o.mu.Lock()
defer o.mu.Unlock()
}
// Deliver an advisory
e := JSConsumerDeliveryTerminatedAdvisory{
TypedEvent: TypedEvent{
Type: JSConsumerDeliveryTerminatedAdvisoryType,
ID: nuid.Next(),
Time: time.Now().UTC(),
},
Stream: o.stream,
Consumer: o.name,
ConsumerSeq: dseq,
StreamSeq: sseq,
Deliveries: dc,
Reason: reason,
Domain: o.srv.getOpts().JetStreamDomain,
}
subj := JSAdvisoryConsumerMsgTerminatedPre + "." + o.stream + "." + o.name
o.sendAdvisory(subj, e)
return ackedInPlace
}
// Introduce a small delay in when timer fires to check pending.
// Allows bursts to be treated in same time frame.
const ackWaitDelay = time.Millisecond
// ackWait returns how long to wait to fire the pending timer.
func (o *consumer) ackWait(next time.Duration) time.Duration {
if next > 0 {
return next + ackWaitDelay
}
return o.cfg.AckWait + ackWaitDelay
}
// Due to bug in calculation of sequences on restoring redelivered let's do quick sanity check.
// Lock should be held.
func (o *consumer) checkRedelivered() {
var shouldUpdateState bool
for sseq := range o.rdc {
if sseq <= o.asflr {
delete(o.rdc, sseq)
o.removeFromRedeliverQueue(sseq)
shouldUpdateState = true
}
}
if shouldUpdateState {
if err := o.writeStoreStateUnlocked(); err != nil && o.srv != nil && o.mset != nil && !o.closed {
s, acc, mset, name := o.srv, o.acc, o.mset, o.name
s.Warnf("Consumer '%s > %s > %s' error on write store state from check redelivered: %v", acc, mset.getCfgName(), name, err)
}
}
}
// This will restore the state from disk.
// Lock should be held.
func (o *consumer) readStoredState() error {
if o.store == nil {
return nil
}
state, err := o.store.State()
if err == nil {
o.applyState(state)
if len(o.rdc) > 0 {
o.checkRedelivered()
}
}
return err
}
// Apply the consumer stored state.
// Lock should be held.
func (o *consumer) applyState(state *ConsumerState) {
if state == nil {
return
}
o.sseq = state.Delivered.Stream + 1
o.dseq = state.Delivered.Consumer + 1
o.adflr = state.AckFloor.Consumer
o.asflr = state.AckFloor.Stream
o.pending = state.Pending
o.rdc = state.Redelivered
// Setup tracking timer if we have restored pending.
if o.isLeader() && len(o.pending) > 0 {
// This is on startup or leader change. We want to check pending
// sooner in case there are inconsistencies etc. Pick between 500ms - 1.5s
delay := 500*time.Millisecond + time.Duration(rand.Int63n(1000))*time.Millisecond
// If normal is lower than this just use that.
if o.cfg.AckWait < delay {
delay = o.ackWait(0)
}
o.resetPtmr(delay)
}
}
// Sets our store state from another source. Used in clustered mode on snapshot restore.
// Lock should be held.
func (o *consumer) setStoreState(state *ConsumerState) error {
if state == nil || o.store == nil {
return nil
}
err := o.store.Update(state)
if err == nil {
o.applyState(state)
} else if err == ErrStoreOldUpdate {
// Our store already has a newer state, which is normal during recovery
// when the consumer was loaded from disk before the meta snapshot state
// was applied.
return nil
}
return err
}
// Update our state to the store.
func (o *consumer) writeStoreState() error {
o.mu.Lock()
defer o.mu.Unlock()
return o.writeStoreStateUnlocked()
}
// Update our state to the store.
// Lock should be held.
func (o *consumer) writeStoreStateUnlocked() error {
if o.store == nil {
return nil
}
state := ConsumerState{
Delivered: SequencePair{
Consumer: o.dseq - 1,
Stream: o.sseq - 1,
},
AckFloor: SequencePair{
Consumer: o.adflr,
Stream: o.asflr,
},
Pending: o.pending,
Redelivered: o.rdc,
}
return o.store.Update(&state)
}
// Returns an initial info. Only applicable for non-clustered consumers.
// We will clear after we return it, so one shot.
func (o *consumer) initialInfo() *ConsumerInfo {
o.mu.Lock()
ici := o.ici
o.ici = nil // gc friendly
o.mu.Unlock()
if ici == nil {
ici = o.info()
}
return ici
}
// Clears our initial info.
// Used when we have a leader change in cluster mode but do not send a response.
func (o *consumer) clearInitialInfo() {
o.mu.Lock()
o.ici = nil // gc friendly
o.mu.Unlock()
}
// Info returns our current consumer state.
func (o *consumer) info() *ConsumerInfo {
return o.infoWithSnap(false)
}
func (o *consumer) infoWithSnap(snap bool) *ConsumerInfo {
return o.infoWithSnapAndReply(snap, _EMPTY_)
}
func (o *consumer) infoWithSnapAndReply(snap bool, reply string) *ConsumerInfo {
o.mu.Lock()
mset := o.mset
if o.closed || mset == nil || mset.srv == nil {
o.mu.Unlock()
return nil
}
js := o.js
if js == nil {
o.mu.Unlock()
return nil
}
// Capture raftGroup.
var rg *raftGroup
if o.ca != nil {
rg = o.ca.Group
}
priorityGroups := []PriorityGroupState{}
// TODO(jrm): when we introduce supporting many priority groups, we need to update assigning `o.currentNuid` for each group.
if len(o.cfg.PriorityGroups) > 0 {
priorityGroups = append(priorityGroups, PriorityGroupState{
Group: o.cfg.PriorityGroups[0],
PinnedClientID: o.currentPinId,
PinnedTS: o.pinnedTS,
})
}
np, err := o.checkNumPending()
if err != nil {
o.mu.Unlock()
return nil
}
dseq, sseq := o.dseq, o.sseq
if dseq <= 0 {
dseq = 1
}
if sseq <= 0 {
sseq = 1
}
cfg := o.cfg
info := &ConsumerInfo{
Stream: o.stream,
Name: o.name,
Created: o.created,
Config: &cfg,
Delivered: SequenceInfo{
Consumer: dseq - 1,
Stream: sseq - 1,
},
AckFloor: SequenceInfo{
Consumer: o.adflr,
Stream: o.asflr,
},
NumAckPending: len(o.pending),
NumRedelivered: len(o.rdc),
NumPending: np,
PushBound: o.isPushMode() && o.active,
TimeStamp: time.Now().UTC(),
PriorityGroups: priorityGroups,
}
// Reset redelivered for MaxDeliver 1. Redeliveries are disabled so must not report it (is confusing otherwise).
// The state does still keep track of these messages.
if o.cfg.MaxDeliver == 1 {
info.NumRedelivered = 0
}
if o.cfg.PauseUntil != nil {
p := *o.cfg.PauseUntil
if info.Paused = time.Now().Before(p); info.Paused {
info.PauseRemaining = time.Until(p)
}
}
// We always need to pull certain data from our store.
if o.store != nil {
state, err := o.store.BorrowState()
if err != nil {
o.mu.Unlock()
return nil
}
// If we are the leader we could have o.sseq that is skipped ahead.
// To maintain consistency in reporting (e.g. jsz) we always take the state for our delivered/ackfloor stream sequence.
// Only use skipped ahead o.sseq if we're a new consumer and have not yet replicated this state yet.
leader := o.isLeader()
if !leader || o.store.HasState() {
info.Delivered.Consumer, info.Delivered.Stream = state.Delivered.Consumer, state.Delivered.Stream
}
info.AckFloor.Consumer, info.AckFloor.Stream = state.AckFloor.Consumer, state.AckFloor.Stream
if !leader {
info.NumAckPending = len(state.Pending)
info.NumRedelivered = len(state.Redelivered)
}
}
// Adjust active based on non-zero etc. Also make UTC here.
if !o.ldt.IsZero() {
ldt := o.ldt.UTC() // This copies as well.
info.Delivered.Last = &ldt
}
if !o.lat.IsZero() {
lat := o.lat.UTC() // This copies as well.
info.AckFloor.Last = &lat
}
// If we are a pull mode consumer, report on number of waiting requests.
if o.isPullMode() {
o.processWaiting(false)
info.NumWaiting = o.waiting.len()
}
// If we were asked to snapshot do so here.
if snap {
o.ici = info
}
sysc := o.sysc
o.mu.Unlock()
// Do cluster.
if rg != nil {
info.Cluster = js.clusterInfo(rg)
}
// If we have a reply subject send the response here.
if reply != _EMPTY_ && sysc != nil {
sysc.sendInternalMsg(reply, _EMPTY_, nil, info)
}
return info
}
// Will signal us that new messages are available. Will break out of waiting.
func (o *consumer) signalNewMessages() {
// Kick our new message channel
select {
case o.mch <- struct{}{}:
default:
}
}
// shouldSample lets us know if we are sampling metrics on acks.
func (o *consumer) shouldSample() bool {
switch {
case o.sfreq <= 0:
return false
case o.sfreq >= 100:
return true
}
// TODO(ripienaar) this is a tad slow so we need to rethink here, however this will only
// hit for those with sampling enabled and its not the default
return rand.Int31n(100) <= o.sfreq
}
func (o *consumer) sampleAck(sseq, dseq, dc uint64) {
if !o.shouldSample() {
return
}
now := time.Now().UTC()
unow := now.UnixNano()
e := JSConsumerAckMetric{
TypedEvent: TypedEvent{
Type: JSConsumerAckMetricType,
ID: nuid.Next(),
Time: now,
},
Stream: o.stream,
Consumer: o.name,
ConsumerSeq: dseq,
StreamSeq: sseq,
Delay: unow - o.pending[sseq].Timestamp,
Deliveries: dc,
Domain: o.srv.getOpts().JetStreamDomain,
}
o.sendAdvisory(o.ackEventT, e)
}
// Process an ACK.
// Returns `true` if the ack was processed in place and the sender can now respond
// to the client, or `false` if there was an error or the ack is replicated (in which
// case the reply will be sent later).
func (o *consumer) processAckMsg(sseq, dseq, dc uint64, reply string, doSample bool) bool {
return o.processAckMsgLocked(sseq, dseq, dc, reply, doSample, true)
}
func (o *consumer) processAckMsgLocked(sseq, dseq, dc uint64, reply string, doSample bool, needLock bool) bool {
lock := func() {
if needLock {
o.mu.Lock()
}
}
unlock := func() {
if needLock {
o.mu.Unlock()
}
}
lock()
if o.closed {
unlock()
return false
}
mset := o.mset
if mset == nil || mset.closed.Load() {
unlock()
return false
}
// Check if this ack is above the current pointer to our next to deliver.
// Ignore if it's a flow-controlled consumer, its state could end up further ahead
// since its state is not replicated before delivery.
if sseq >= o.sseq && !o.cfg.FlowControl {
// Let's make sure this is valid.
// This is only received on the consumer leader, so should never be higher
// than the last stream sequence. But could happen if we've just become
// consumer leader, and we are not up-to-date on the stream yet.
var ss StreamState
mset.store.FastState(&ss)
if sseq > ss.LastSeq {
o.srv.Warnf("JetStream consumer '%s > %s > %s' ACK sequence %d past last stream sequence of %d",
o.acc.Name, o.stream, o.name, sseq, ss.LastSeq)
// FIXME(dlc) - For 2.11 onwards should we return an error here to the caller?
}
// Even though another leader must have delivered a message with this sequence, we must not adjust
// the current pointer. This could otherwise result in a stuck consumer, where messages below this
// sequence can't be redelivered, and we'll have incorrect pending state and ack floors.
unlock()
return false
}
// Let the owning stream know if we are interest or workqueue retention based.
// If this consumer is clustered (o.node != nil) this will be handled by
// processReplicatedAck after the ack has propagated.
// If we're already holding the lock we can't ack in place, since that will
// violate lock ordering with respect to the stream.
ackInPlace := o.node == nil && o.retention != LimitsPolicy && needLock
var sgap, floor uint64
var needSignal bool
switch o.cfg.AckPolicy {
case AckExplicit:
if p, ok := o.pending[sseq]; ok {
if doSample {
o.sampleAck(sseq, dseq, dc)
}
if o.maxp > 0 && len(o.pending) >= o.maxp {
needSignal = true
}
delete(o.pending, sseq)
// Use the original deliver sequence from our pending record.
dseq = p.Sequence
// Only move floors if we matched an existing pending.
if len(o.pending) == 0 {
o.adflr = o.dseq - 1
o.asflr = o.sseq - 1
} else if dseq == o.adflr+1 {
o.adflr, o.asflr = dseq, sseq
for ss := sseq + 1; ss < o.sseq; ss++ {
if p, ok := o.pending[ss]; ok {
if p.Sequence > 0 {
o.adflr, o.asflr = p.Sequence-1, ss-1
}
break
}
}
}
}
delete(o.rdc, sseq)
o.removeFromRedeliverQueue(sseq)
case AckAll, AckFlowControl:
// no-op
if dseq <= o.adflr || sseq <= o.asflr {
unlock()
// Return true to let caller respond back to the client.
return true
}
if o.maxp > 0 && len(o.pending) >= o.maxp {
needSignal = true
}
sgap = sseq - o.asflr
floor = sseq // start at same and set lower as we go.
o.adflr, o.asflr = dseq, sseq
remove := func(seq uint64) {
delete(o.pending, seq)
delete(o.rdc, seq)
o.removeFromRedeliverQueue(seq)
if seq < floor {
floor = seq
}
}
// Determine if smarter to walk all of pending vs the sequence range.
if sgap > uint64(len(o.pending)) {
for seq := range o.pending {
if seq <= sseq {
remove(seq)
}
}
} else {
for seq := sseq; seq > sseq-sgap && len(o.pending) > 0; seq-- {
remove(seq)
}
}
case AckNone:
// FIXME(dlc) - This is error but do we care?
unlock()
return ackInPlace
}
// No ack replication, so we set reply to "" so that updateAcks does not
// send the reply. The caller will.
if ackInPlace {
reply = _EMPTY_
}
// Update underlying store.
o.updateAcks(dseq, sseq, reply)
unlock()
if ackInPlace {
if sgap > 1 {
// FIXME(dlc) - This can very inefficient, will need to fix.
for seq := sseq; seq >= floor; seq-- {
mset.ackMsg(o, seq)
}
} else {
mset.ackMsg(o, sseq)
}
}
// If we had max ack pending set and were at limit we need to unblock ourselves.
if needSignal {
o.signalNewMessages()
}
return ackInPlace
}
// Determine if this is a truly filtered consumer. Modern clients will place filtered subjects
// even if the stream only has a single non-wildcard subject designation.
// Read lock should be held.
func (o *consumer) isFiltered() bool {
if o.subjf == nil {
return false
}
// If we are here we want to check if the filtered subject is
// a direct match for our only listed subject.
mset := o.mset
if mset == nil {
return true
}
// Protect access to mset.cfg with the cfgMu mutex.
mset.cfgMu.RLock()
msetSubjects := mset.cfg.Subjects
mset.cfgMu.RUnlock()
// `isFiltered` need to be performant, so we do
// as any checks as possible to avoid unnecessary work.
// Here we avoid iteration over slices if there is only one subject in stream
// and one filter for the consumer.
if len(msetSubjects) == 1 && len(o.subjf) == 1 {
return msetSubjects[0] != o.subjf[0].subject
}
// if the list is not equal length, we can return early, as this is filtered.
if len(msetSubjects) != len(o.subjf) {
return true
}
// if in rare case scenario that user passed all stream subjects as consumer filters,
// we need to do a more expensive operation.
// reflect.DeepEqual would return false if the filters are the same, but in different order
// so it can't be used here.
cfilters := make(map[string]struct{}, len(o.subjf))
for _, val := range o.subjf {
cfilters[val.subject] = struct{}{}
}
for _, val := range msetSubjects {
if _, ok := cfilters[val]; !ok {
return true
}
}
return false
}
// Check if we need an ack for this store seq.
// This is called for interest based retention streams to remove messages.
func (o *consumer) needAck(sseq uint64, subj string) bool {
var needAck bool
var asflr, osseq uint64
var pending map[uint64]*Pending
var rdc map[uint64]uint64
o.mu.RLock()
defer o.mu.RUnlock()
isFiltered := o.isFiltered()
if isFiltered && o.mset == nil {
return false
}
// Check if we are filtered, and if so check if this is even applicable to us.
if isFiltered {
if subj == _EMPTY_ {
var err error
if subj, err = o.mset.store.SubjectForSeq(sseq); err != nil {
return false
}
}
if !o.isFilteredMatch(subj) {
return false
}
}
if o.isLeader() {
asflr, osseq = o.asflr, o.sseq
pending, rdc = o.pending, o.rdc
} else {
if o.store == nil {
return false
}
state, err := o.store.BorrowState()
if err != nil || state == nil {
// Fall back to what we track internally for now.
return sseq > o.asflr && !o.isFiltered()
}
// If loading state as here, the osseq is +1.
asflr, osseq, pending, rdc = state.AckFloor.Stream, state.Delivered.Stream+1, state.Pending, state.Redelivered
}
switch o.cfg.AckPolicy {
case AckNone, AckAll, AckFlowControl:
needAck = sseq > asflr
case AckExplicit:
if sseq > asflr {
if sseq >= osseq {
needAck = true
} else {
_, needAck = pending[sseq]
}
}
}
// Finally check if redelivery of this message is tracked.
// If the message is not pending, it should be preserved if it reached max delivery.
if !needAck {
_, needAck = rdc[sseq]
}
return needAck
}
type PriorityGroup struct {
Group string `json:"group,omitempty"`
MinPending int64 `json:"min_pending,omitempty"`
MinAckPending int64 `json:"min_ack_pending,omitempty"`
Id string `json:"id,omitempty"`
Priority int `json:"priority,omitempty"`
}
// Used in nextReqFromMsg, since the json.Unmarshal causes the request
// struct to escape to the heap always. This should reduce GC pressure.
var jsGetNextPool = sync.Pool{
New: func() any {
return &JSApiConsumerGetNextRequest{}
},
}
// Helper for the next message requests.
func nextReqFromMsg(msg []byte) (time.Time, int, int, bool, time.Duration, time.Time, *PriorityGroup, error) {
req := bytes.TrimSpace(msg)
switch {
case len(req) == 0:
return time.Time{}, 1, 0, false, 0, time.Time{}, nil, nil
case req[0] == '{':
cr := jsGetNextPool.Get().(*JSApiConsumerGetNextRequest)
defer func() {
*cr = JSApiConsumerGetNextRequest{}
jsGetNextPool.Put(cr)
}()
if err := json.Unmarshal(req, &cr); err != nil {
return time.Time{}, -1, 0, false, 0, time.Time{}, nil, err
}
var hbt time.Time
if cr.Heartbeat > 0 {
if cr.Heartbeat*2 > cr.Expires {
return time.Time{}, 1, 0, false, 0, time.Time{}, nil, errors.New("heartbeat value too large")
}
hbt = time.Now().Add(cr.Heartbeat)
}
priorityGroup := cr.PriorityGroup
if cr.Expires == time.Duration(0) {
return time.Time{}, cr.Batch, cr.MaxBytes, cr.NoWait, cr.Heartbeat, hbt, &priorityGroup, nil
}
return time.Now().Add(cr.Expires), cr.Batch, cr.MaxBytes, cr.NoWait, cr.Heartbeat, hbt, &priorityGroup, nil
default:
if n, err := strconv.Atoi(string(req)); err == nil {
return time.Time{}, n, 0, false, 0, time.Time{}, nil, nil
}
}
return time.Time{}, 1, 0, false, 0, time.Time{}, nil, nil
}
// Represents a request that is on the internal waiting queue
type waitingRequest struct {
next *waitingRequest
acc *Account
interest string
reply string
n int // For batching
d int // num delivered
b int // For max bytes tracking
expires time.Time
received time.Time
hb time.Duration
hbt time.Time
noWait bool
priorityGroup *PriorityGroup
}
// sync.Pool for waiting requests.
var wrPool = sync.Pool{
New: func() any {
return new(waitingRequest)
},
}
// Recycle this request. This request can not be accessed after this call.
func (wr *waitingRequest) recycleIfDone() bool {
if wr != nil && wr.n <= 0 {
wr.recycle()
return true
}
return false
}
// Force a recycle.
func (wr *waitingRequest) recycle() {
if wr != nil {
wr.next, wr.acc, wr.interest, wr.reply = nil, nil, _EMPTY_, _EMPTY_
wrPool.Put(wr)
}
}
// Represents an (optional) request timeout that's sent after waiting for replicated deliveries.
type waitingDelivery struct {
seq uint64
pn int // Pending messages.
pb int // Pending bytes.
}
// sync.Pool for waiting deliveries.
var wdPool = sync.Pool{
New: func() any {
return new(waitingDelivery)
},
}
// Force a recycle.
func (wd *waitingDelivery) recycle() {
if wd != nil {
wd.seq, wd.pn, wd.pb = 0, 0, 0
wdPool.Put(wd)
}
}
// waiting queue for requests that are waiting for new messages to arrive.
type waitQueue struct {
n, max int
last time.Time
head *waitingRequest
tail *waitingRequest
}
// Create a new ring buffer with at most max items.
func newWaitQueue(max int) *waitQueue {
return &waitQueue{max: max}
}
var (
errWaitQueueFull = errors.New("wait queue is full")
errWaitQueueNil = errors.New("wait queue is nil")
)
// insertSorted inserts wr at the correct position based on priority
func (wq *waitQueue) insertSorted(wr *waitingRequest) {
// Handle empty queue
if wq.head == nil {
wq.head = wr
wq.tail = wr
wr.next = nil
return
}
insertAtPosition(wr, wq)
}
func (wq *waitQueue) addPrioritized(wr *waitingRequest) error {
if wq == nil {
return errWaitQueueNil
}
if wq.isFull() {
return errWaitQueueFull
}
wq.insertSorted(wr)
wq.n++
wq.last = wr.received
return nil
}
// Adds in a new request.
func (wq *waitQueue) add(wr *waitingRequest) error {
if wq == nil {
return errWaitQueueNil
}
if wq.isFull() {
return errWaitQueueFull
}
if wq.head == nil {
wq.head = wr
} else {
wq.tail.next = wr
}
// Always set tail.
wq.tail = wr
// Make sure nil
wr.next = nil
// Track last active via when we receive a request.
wq.last = wr.received
wq.n++
return nil
}
func (wq *waitQueue) isFull() bool {
if wq == nil {
return false
}
return wq.n == wq.max
}
func (wq *waitQueue) isEmpty() bool {
if wq == nil {
return true
}
return wq.n == 0
}
func (wq *waitQueue) len() int {
if wq == nil {
return 0
}
return wq.n
}
// Peek will return the next request waiting or nil if empty.
func (wq *waitQueue) peek() *waitingRequest {
if wq == nil {
return nil
}
return wq.head
}
func (wq *waitQueue) cycle() {
wr := wq.peek()
if wr != nil {
// Always remove current now on a pop, and move to end if still valid.
// If we were the only one don't need to remove since this can be a no-op.
wq.removeCurrent()
wq.add(wr)
}
}
func (wq *waitQueue) popOrPopAndRequeue(priority PriorityPolicy) *waitingRequest {
if wq == nil || wq.head == nil {
return nil
}
if priority == PriorityPrioritized {
return wq.popAndRequeue()
}
return wq.pop()
}
// pop will return the next request and move the read cursor.
// This will now place a request that still has pending items at the ends of the list.
func (wq *waitQueue) pop() *waitingRequest {
wr := wq.peek()
if wr != nil {
wr.d++
wr.n--
// Always remove current now on a pop, and move to end if still valid.
// If we were the only one don't need to remove since this can be a no-op.
if wr.n > 0 && wq.n > 1 {
wq.removeCurrent()
wq.add(wr)
} else if wr.n <= 0 {
wq.removeCurrent()
}
}
return wr
}
// popAndRequeue pops the head element and requeues it at the end of its priority group.
// This maintains FIFO order within the same priority level.
func (wq *waitQueue) popAndRequeue() *waitingRequest {
if wq == nil || wq.head == nil {
return nil
}
// Save the head
wr := wq.head
if wr == nil {
return wr
}
wr.d++
wr.n--
if wr.n > 0 && wq.n > 1 {
if wr.next == nil {
return wr
}
wq.head = wq.head.next
wr.next = nil
insertAtPosition(wr, wq)
} else if wr.n <= 0 {
wq.removeCurrent()
return wr
}
return wr
}
func insertAtPosition(wr *waitingRequest, wq *waitQueue) {
priority := math.MaxInt32
if wr.priorityGroup != nil {
priority = wr.priorityGroup.Priority
}
var prev *waitingRequest
current := wq.head
for current != nil {
currentPriority := math.MaxInt32
if current.priorityGroup != nil {
currentPriority = current.priorityGroup.Priority
}
if currentPriority > priority {
break
}
prev = current
current = current.next
}
if prev == nil {
// All remaining elements have higher priority
wr.next = wq.head
wq.head = wr
} else {
wr.next = prev.next
prev.next = wr
if wr.next == nil {
wq.tail = wr
}
}
}
// Removes the current read pointer (head FIFO) entry.
func (wq *waitQueue) removeCurrent() {
wq.remove(nil, wq.head)
}
// Remove the wr element from the wait queue.
func (wq *waitQueue) remove(pre, wr *waitingRequest) {
if wr == nil {
return
}
if pre != nil {
pre.next = wr.next
} else if wr == wq.head {
// We are removing head here.
wq.head = wr.next
}
// Check if wr was our tail.
if wr == wq.tail {
// Check if we need to assign to pre.
if wr.next == nil {
wq.tail = pre
} else {
wq.tail = wr.next
}
}
wq.n--
}
// Return the map of pending requests keyed by the reply subject.
// No-op if push consumer or invalid etc.
func (o *consumer) pendingRequests() map[string]*waitingRequest {
if o.waiting == nil {
return nil
}
wq, m := o.waiting, make(map[string]*waitingRequest)
for wr := wq.head; wr != nil; wr = wr.next {
m[wr.reply] = wr
}
return m
}
func (o *consumer) setPinnedTimer(priorityGroup string) {
if o.pinnedTtl != nil {
o.pinnedTtl.Reset(o.cfg.PinnedTTL)
} else {
o.pinnedTtl = time.AfterFunc(o.cfg.PinnedTTL, func() {
o.mu.Lock()
// Skip if already unset.
if o.currentPinId == _EMPTY_ {
o.mu.Unlock()
return
}
o.unassignPinId()
o.sendUnpinnedAdvisoryLocked(priorityGroup, "timeout")
o.mu.Unlock()
o.signalNewMessages()
})
}
}
// Lock should be held.
func (o *consumer) assignNewPinId(wr *waitingRequest) {
if wr.priorityGroup == nil || wr.priorityGroup.Group == _EMPTY_ {
return
}
o.currentPinId = nuid.Next()
o.pinnedTS = time.Now().UTC()
wr.priorityGroup.Id = o.currentPinId
o.setPinnedTimer(wr.priorityGroup.Group)
o.sendPinnedAdvisoryLocked(wr.priorityGroup.Group)
}
// Lock should be held.
func (o *consumer) unassignPinId() {
o.currentPinId = _EMPTY_
o.pinnedTS = time.Time{}
if o.pinnedTtl != nil {
o.pinnedTtl.Stop()
o.pinnedTtl = nil
}
}
// Return next waiting request. This will check for expirations but not noWait or interest.
// That will be handled by processWaiting.
// Lock should be held.
func (o *consumer) nextWaiting(sz int) *waitingRequest {
if o.waiting == nil || o.waiting.isEmpty() {
return nil
}
// Check if server needs to assign a new pin id.
needNewPin := o.currentPinId == _EMPTY_ && o.cfg.PriorityPolicy == PriorityPinnedClient
numCycled := 0
for wr := o.waiting.peek(); !o.waiting.isEmpty(); wr = o.waiting.peek() {
if wr == nil {
break
}
// Check if we have max bytes set.
if wr.b > 0 {
if sz <= wr.b {
wr.b -= sz
// If we are right now at zero, set batch to 1 to deliver this one but stop after.
if wr.b == 0 {
wr.n = 1
}
} else {
// Since we can't send that message to the requestor, we need to
// notify that we are closing the request.
const maxBytesT = "NATS/1.0 409 Message Size Exceeds MaxBytes\r\n%s: %d\r\n%s: %d\r\n\r\n"
hdr := fmt.Appendf(nil, maxBytesT, JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
// Remove the current one, no longer valid due to max bytes limit.
o.waiting.removeCurrent()
if o.node != nil {
o.removeClusterPendingRequest(wr.reply)
}
wr.recycle()
continue
}
}
if wr.expires.IsZero() || time.Now().Before(wr.expires) {
if needNewPin {
if wr.priorityGroup.Id == _EMPTY_ {
o.assignNewPinId(wr)
} else {
// There is pin id set, but not a matching one. Send a notification to the client and remove the request.
// Probably this is the old pin id.
hdr := fmt.Appendf(nil, "NATS/1.0 423 Nats-Wrong-Pin-Id\r\n%s: %d\r\n%s: %d\r\n\r\n", JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
o.waiting.removeCurrent()
if o.node != nil {
o.removeClusterPendingRequest(wr.reply)
}
wr.recycle()
continue
}
} else if o.currentPinId != _EMPTY_ {
// Check if we have a match on the currentNuid
if wr.priorityGroup != nil && wr.priorityGroup.Id == o.currentPinId {
// If we have a match, we do nothing here and will deliver the message later down the code path.
} else if wr.priorityGroup.Id == _EMPTY_ {
o.waiting.cycle()
numCycled++
if numCycled >= o.waiting.len() {
return nil
}
continue
} else {
// There is pin id set, but not a matching one. Send a notification to the client and remove the request.
hdr := fmt.Appendf(nil, "NATS/1.0 423 Nats-Wrong-Pin-Id\r\n%s: %d\r\n%s: %d\r\n\r\n", JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
o.waiting.removeCurrent()
if o.node != nil {
o.removeClusterPendingRequest(wr.reply)
}
wr.recycle()
continue
}
}
if o.cfg.PriorityPolicy == PriorityOverflow {
if wr.priorityGroup != nil &&
// If both limits are zero we don't cycle and the request will be fulfilled.
(wr.priorityGroup.MinPending > 0 || wr.priorityGroup.MinAckPending > 0) &&
// We need to check o.npc+1, because before calling nextWaiting, we do o.npc--
// If one OR the other limit is exceeded, we want to fulfill the request.
// This is an inverted check. For clarity, we check the positive condition and negate.
!((wr.priorityGroup.MinPending > 0 && wr.priorityGroup.MinPending <= o.npc+1) ||
(wr.priorityGroup.MinAckPending > 0 && wr.priorityGroup.MinAckPending <= int64(len(o.pending)))) {
o.waiting.cycle()
numCycled++
// We're done cycling through the requests.
if numCycled >= o.waiting.len() {
return nil
}
continue
}
}
if wr.acc.sl.HasInterest(wr.interest) {
return o.waiting.popOrPopAndRequeue(o.cfg.PriorityPolicy)
} else if time.Since(wr.received) < defaultGatewayRecentSubExpiration && (o.srv.leafNodeEnabled || o.srv.gateway.enabled) {
return o.waiting.popOrPopAndRequeue(o.cfg.PriorityPolicy)
} else if o.srv.gateway.enabled && o.srv.hasGatewayInterest(wr.acc.Name, wr.interest) {
return o.waiting.popOrPopAndRequeue(o.cfg.PriorityPolicy)
}
} else {
// We do check for expiration in `processWaiting`, but it is possible to hit the expiry here, and not there.
rdWait := o.replicateDeliveries()
if rdWait {
// Check if we need to send the timeout after pending replicated deliveries, or can do so immediately.
if wd, ok := o.waitingDeliveries[wr.reply]; !ok {
rdWait = false
} else {
wd.pn, wd.pb = wr.n, wr.b
}
}
if !rdWait {
hdr := fmt.Appendf(nil, "NATS/1.0 408 Request Timeout\r\n%s: %d\r\n%s: %d\r\n\r\n", JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
}
o.waiting.removeCurrent()
if o.node != nil {
o.removeClusterPendingRequest(wr.reply)
}
wr.recycle()
continue
}
if wr.interest != wr.reply {
const intExpT = "NATS/1.0 408 Interest Expired\r\n%s: %d\r\n%s: %d\r\n\r\n"
hdr := fmt.Appendf(nil, intExpT, JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
}
// Remove the current one, no longer valid.
o.waiting.removeCurrent()
if o.node != nil {
o.removeClusterPendingRequest(wr.reply)
}
wr.recycle()
}
return nil
}
// Next message request.
type nextMsgReq struct {
reply string
msg []byte
}
var nextMsgReqPool sync.Pool
func newNextMsgReq(reply string, msg []byte) *nextMsgReq {
var nmr *nextMsgReq
m := nextMsgReqPool.Get()
if m != nil {
nmr = m.(*nextMsgReq)
} else {
nmr = &nextMsgReq{}
}
// When getting something from a pool it is critical that all fields are
// initialized. Doing this way guarantees that if someone adds a field to
// the structure, the compiler will fail the build if this line is not updated.
(*nmr) = nextMsgReq{reply, msg}
return nmr
}
func (nmr *nextMsgReq) returnToPool() {
if nmr == nil {
return
}
nmr.reply, nmr.msg = _EMPTY_, nil
nextMsgReqPool.Put(nmr)
}
// processNextMsgReq will process a request for the next message available. A nil message payload means deliver
// a single message. If the payload is a formal request or a number parseable with Atoi(), then we will send a
// batch of messages without requiring another request to this endpoint, or an ACK.
func (o *consumer) processNextMsgReq(_ *subscription, c *client, _ *Account, _, reply string, rmsg []byte) {
if reply == _EMPTY_ {
return
}
// Short circuit error here.
if o.nextMsgReqs == nil {
hdr := []byte("NATS/1.0 409 Consumer is push based\r\n\r\n")
o.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
return
}
hdr, msg := c.msgParts(rmsg)
if errorOnRequiredApiLevel(hdr) {
hdr = []byte("NATS/1.0 412 Required Api Level\r\n\r\n")
o.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
return
}
o.nextMsgReqs.push(newNextMsgReq(reply, copyBytes(msg)))
}
// processResetReq will reset a consumer to a new starting sequence.
func (o *consumer) processResetReq(_ *subscription, c *client, a *Account, _, reply string, rmsg []byte) {
if reply == _EMPTY_ {
return
}
s := o.srv
var resp = JSApiConsumerResetResponse{ApiResponse: ApiResponse{Type: JSApiConsumerResetResponseType}}
hdr, msg := c.msgParts(rmsg)
if errorOnRequiredApiLevel(hdr) {
resp.Error = NewJSRequiredApiLevelError()
s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
return
}
// An empty message resets back to the ack floor, otherwise a custom sequence can be used.
var req JSApiConsumerResetRequest
if len(msg) > 0 {
if err := json.Unmarshal(msg, &req); err != nil {
resp.Error = NewJSInvalidJSONError(err)
s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
return
}
}
resetSeq, canRespond, err := o.resetStartingSeq(req.Seq, reply, false)
if err != nil {
resp.Error = NewJSConsumerInvalidResetError(err)
s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
} else if canRespond {
resp.ConsumerInfo = setDynamicConsumerInfoMetadata(o.info())
resp.ResetSeq = resetSeq
s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
}
}
func (o *consumer) processNextMsgRequest(reply string, msg []byte) {
o.mu.Lock()
defer o.mu.Unlock()
mset := o.mset
if mset == nil {
return
}
sendErr := func(status int, description string) {
hdr := fmt.Appendf(nil, "NATS/1.0 %d %s\r\n\r\n", status, description)
o.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
}
if o.isPushMode() || o.waiting == nil {
sendErr(409, "Consumer is push based")
return
}
// Check payload here to see if they sent in batch size or a formal request.
expires, batchSize, maxBytes, noWait, hb, hbt, priorityGroup, err := nextReqFromMsg(msg)
if err != nil {
sendErr(400, fmt.Sprintf("Bad Request - %v", err))
return
}
// Check for request limits
if o.cfg.MaxRequestBatch > 0 && batchSize > o.cfg.MaxRequestBatch {
sendErr(409, fmt.Sprintf("Exceeded MaxRequestBatch of %d", o.cfg.MaxRequestBatch))
return
}
if !expires.IsZero() && o.cfg.MaxRequestExpires > 0 && expires.After(time.Now().Add(o.cfg.MaxRequestExpires)) {
sendErr(409, fmt.Sprintf("Exceeded MaxRequestExpires of %v", o.cfg.MaxRequestExpires))
return
}
if maxBytes > 0 && o.cfg.MaxRequestMaxBytes > 0 && maxBytes > o.cfg.MaxRequestMaxBytes {
sendErr(409, fmt.Sprintf("Exceeded MaxRequestMaxBytes of %v", o.cfg.MaxRequestMaxBytes))
return
}
if priorityGroup != nil {
if (priorityGroup.MinPending != 0 || priorityGroup.MinAckPending != 0) && o.cfg.PriorityPolicy != PriorityOverflow {
sendErr(400, "Bad Request - Not a Overflow Priority consumer")
}
if priorityGroup.Id != _EMPTY_ && o.cfg.PriorityPolicy != PriorityPinnedClient {
sendErr(400, "Bad Request - Not a Pinned Client Priority consumer")
}
if priorityGroup.Priority < 0 || priorityGroup.Priority > 9 {
sendErr(400, "Bad Request - Priority must be between 0 and 9")
return
}
}
if priorityGroup != nil && o.cfg.PriorityPolicy != PriorityNone {
if priorityGroup.Group == _EMPTY_ {
sendErr(400, "Bad Request - Priority Group missing")
return
}
if !slices.Contains(o.cfg.PriorityGroups, priorityGroup.Group) {
sendErr(400, "Bad Request - Invalid Priority Group")
return
}
if o.currentPinId != _EMPTY_ {
if priorityGroup.Id == o.currentPinId {
o.setPinnedTimer(priorityGroup.Group)
} else if priorityGroup.Id != _EMPTY_ {
sendErr(423, "Nats-Pin-Id mismatch")
return
}
}
}
// If we have the max number of requests already pending try to expire.
if o.waiting.isFull() {
// Try to expire some of the requests.
// We do not want to push too hard here so at maximum process once per sec.
if time.Since(o.lwqic) > time.Second {
o.processWaiting(false)
}
}
// If the request is for noWait and we have pending requests already, check if we have room.
if noWait {
msgsPending := o.numPending() + uint64(len(o.rdq))
// If no pending at all, decide what to do with request.
// If no expires was set then fail.
if msgsPending == 0 && expires.IsZero() {
o.waiting.last = time.Now()
sendErr(404, "No Messages")
return
}
if msgsPending > 0 {
_, _, batchPending, _ := o.processWaiting(false)
if msgsPending < uint64(batchPending) {
o.waiting.last = time.Now()
sendErr(408, "Requests Pending")
return
}
}
// If we are here this should be considered a one-shot situation.
// We will wait for expires but will return as soon as we have any messages.
}
// If we receive this request though an account export, we need to track that interest subject and account.
acc, interest := trackDownAccountAndInterest(o.acc, reply)
// Create a waiting request.
wr := wrPool.Get().(*waitingRequest)
wr.acc, wr.interest, wr.reply, wr.n, wr.d, wr.noWait, wr.expires, wr.hb, wr.hbt, wr.priorityGroup = acc, interest, reply, batchSize, 0, noWait, expires, hb, hbt, priorityGroup
wr.b = maxBytes
wr.received = time.Now()
if o.cfg.PriorityPolicy == PriorityPrioritized {
if err := o.waiting.addPrioritized(wr); err != nil {
if hb == 0 {
sendErr(409, "Exceeded MaxWaiting")
}
wr.recycle()
return
}
} else {
if err := o.waiting.add(wr); err != nil {
// If the client has a heartbeat interval set, don't bother responding with a 409,
// otherwise we can end up in a hot loop with the client re-requesting instead of
// waiting for the missing heartbeats instead and retrying.
if hb == 0 {
sendErr(409, "Exceeded MaxWaiting")
}
wr.recycle()
return
}
}
o.signalNewMessages()
// If we are clustered update our followers about this request.
if o.node != nil {
o.addClusterPendingRequest(wr.reply)
}
}
func trackDownAccountAndInterest(acc *Account, interest string) (*Account, string) {
for strings.HasPrefix(interest, replyPrefix) {
oa := acc
oa.mu.RLock()
if oa.exports.responses == nil {
oa.mu.RUnlock()
break
}
si := oa.exports.responses[interest]
if si == nil {
oa.mu.RUnlock()
break
}
acc, interest = si.acc, si.to
oa.mu.RUnlock()
}
return acc, interest
}
// Return current delivery count for a given sequence.
func (o *consumer) deliveryCount(seq uint64) uint64 {
if o.rdc == nil {
return 1
}
if dc := o.rdc[seq]; dc >= 1 {
return dc
}
return 1
}
// Increase the delivery count for this message.
// ONLY used on redelivery semantics.
// Lock should be held.
func (o *consumer) incDeliveryCount(sseq uint64) uint64 {
if o.rdc == nil {
o.rdc = make(map[uint64]uint64)
}
o.rdc[sseq] += 1
return o.rdc[sseq] + 1
}
// Used if we have to adjust on failed delivery or bad lookups.
// Those failed attempts should not increase deliver count.
// Lock should be held.
func (o *consumer) decDeliveryCount(sseq uint64) {
if o.rdc == nil {
o.rdc = make(map[uint64]uint64)
}
o.rdc[sseq] -= 1
}
// send a delivery exceeded advisory.
func (o *consumer) notifyDeliveryExceeded(sseq, dc uint64) {
e := JSConsumerDeliveryExceededAdvisory{
TypedEvent: TypedEvent{
Type: JSConsumerDeliveryExceededAdvisoryType,
ID: nuid.Next(),
Time: time.Now().UTC(),
},
Stream: o.stream,
Consumer: o.name,
StreamSeq: sseq,
Deliveries: dc,
Domain: o.srv.getOpts().JetStreamDomain,
}
o.sendAdvisory(o.deliveryExcEventT, e)
}
// Check if the candidate subject matches a filter if its present.
// Lock should be held.
func (o *consumer) isFilteredMatch(subj string) bool {
// No filter is automatic match.
if o.subjf == nil {
return true
}
for _, filter := range o.subjf {
if !filter.hasWildcard && subj == filter.subject {
return true
}
}
// It's quicker to first check for non-wildcard filters, then
// iterate again to check for subset match.
tsa := [32]string{}
tts := tokenizeSubjectIntoSlice(tsa[:0], subj)
for _, filter := range o.subjf {
if isSubsetMatchTokenized(tts, filter.tokenizedSubject) {
return true
}
}
return false
}
// Check if the candidate filter subject is equal to or a subset match
// of one of the filter subjects.
// Lock should be held.
func (o *consumer) isEqualOrSubsetMatch(subj string) bool {
for _, filter := range o.subjf {
if !filter.hasWildcard && subj == filter.subject {
return true
}
}
tsa := [32]string{}
tts := tokenizeSubjectIntoSlice(tsa[:0], subj)
for _, filter := range o.subjf {
if isSubsetMatchTokenized(filter.tokenizedSubject, tts) {
return true
}
}
return false
}
var (
errMaxAckPending = errors.New("max ack pending reached")
errBadConsumer = errors.New("consumer not valid")
errNoInterest = errors.New("consumer requires interest for delivery subject when ephemeral")
)
// Get next available message from underlying store.
// Is partition aware and redeliver aware.
// Lock should be held.
func (o *consumer) getNextMsg() (*jsPubMsg, uint64, error) {
if o.mset == nil || o.mset.store == nil {
return nil, 0, errBadConsumer
}
// Process redelivered messages before looking at possibly "skip list" (deliver last per subject)
if o.hasRedeliveries() {
var seq, dc uint64
for seq = o.getNextToRedeliver(); seq > 0; seq = o.getNextToRedeliver() {
dc = o.incDeliveryCount(seq)
if o.maxdc > 0 && dc > o.maxdc {
// Only send once
if dc == o.maxdc+1 {
o.notifyDeliveryExceeded(seq, dc-1)
}
// Make sure to remove from pending.
if p, ok := o.pending[seq]; ok && p != nil {
delete(o.pending, seq)
o.updateDelivered(p.Sequence, seq, dc, p.Timestamp)
}
continue
}
pmsg := getJSPubMsgFromPool()
sm, err := o.mset.store.LoadMsg(seq, &pmsg.StoreMsg)
if sm == nil || err != nil {
pmsg.returnToPool()
pmsg = nil
// Adjust back deliver count.
o.decDeliveryCount(seq)
}
// Message was scheduled for redelivery but was removed in the meantime.
if err == ErrStoreMsgNotFound || err == errDeletedMsg {
// This is a race condition where the message is still in o.pending and
// scheduled for redelivery, but it has been removed from the stream.
// o.processTerm is called in a goroutine so would normally run. However,
// if we get here this likely didn't fire, or we are replicated and changed leaders.
// That will correct the pending state and delivery/ack floors, so just skip here.
pmsg.returnToPool()
pmsg = nil
if p, ok := o.pending[seq]; ok {
o.processTermLocked(seq, p.Sequence, dc-1, ackTermUnackedLimitsReason, _EMPTY_, false)
}
continue
}
return pmsg, dc, err
}
}
// Check if we have max pending.
if o.maxp > 0 && len(o.pending) >= o.maxp {
// maxp only set when ack policy != AckNone and user set MaxAckPending
// Stall if we have hit max pending.
return nil, 0, errMaxAckPending
}
if o.hasSkipListPending() {
seq := o.lss.seqs[0]
if len(o.lss.seqs) == 1 {
o.sseq = o.lss.resume
o.lss = nil
o.updateSkipped(o.sseq)
} else {
o.lss.seqs = o.lss.seqs[1:]
o.sseq = seq
}
pmsg := getJSPubMsgFromPool()
sm, err := o.mset.store.LoadMsg(seq, &pmsg.StoreMsg)
if sm == nil || err != nil {
pmsg.returnToPool()
pmsg = nil
}
o.sseq++
return pmsg, 1, err
}
var sseq uint64
var err error
var sm *StoreMsg
var pmsg = getJSPubMsgFromPool()
// Grab next message applicable to us.
filters, subjf, fseq := o.filters, o.subjf, o.sseq
// Check if we are multi-filtered or not.
if filters != nil {
sm, sseq, err = o.mset.store.LoadNextMsgMulti(filters, fseq, &pmsg.StoreMsg)
} else if len(subjf) > 0 { // Means single filtered subject since o.filters means > 1.
filter, wc := subjf[0].subject, subjf[0].hasWildcard
sm, sseq, err = o.mset.store.LoadNextMsg(filter, wc, fseq, &pmsg.StoreMsg)
} else {
// No filter here.
sm, sseq, err = o.mset.store.LoadNextMsg(_EMPTY_, false, fseq, &pmsg.StoreMsg)
}
if sm == nil {
pmsg.returnToPool()
pmsg = nil
}
// Check if we should move our o.sseq.
if sseq >= o.sseq {
// If we are moving step by step then sseq == o.sseq.
// If we have jumped we should update skipped for other replicas.
if sseq != o.sseq && err == ErrStoreEOF {
o.updateSkipped(sseq + 1)
}
o.sseq = sseq + 1
}
return pmsg, 1, err
}
// Will check for expiration and lack of interest on waiting requests.
// Will also do any heartbeats and return the next expiration or HB interval.
func (o *consumer) processWaiting(eos bool) (int, int, int, time.Time) {
var fexp time.Time
if o.srv == nil || o.waiting.isEmpty() {
return 0, 0, 0, fexp
}
// Mark our last check time.
o.lwqic = time.Now()
var expired, brp int
s, now := o.srv, time.Now()
wq := o.waiting
remove := func(pre, wr *waitingRequest) *waitingRequest {
expired++
if o.node != nil {
o.removeClusterPendingRequest(wr.reply)
}
next := wr.next
wq.remove(pre, wr)
wr.recycle()
return next
}
var pre *waitingRequest
for wr := wq.head; wr != nil; {
// Check expiration.
expires := !wr.expires.IsZero() && now.After(wr.expires)
if (eos && wr.noWait) || expires {
rdWait := o.replicateDeliveries()
if rdWait {
// Check if we need to send the timeout after pending replicated deliveries, or can do so immediately.
if wd, ok := o.waitingDeliveries[wr.reply]; !ok {
rdWait = false
} else {
wd.pn, wd.pb = wr.n, wr.b
}
// If we still need to wait for replicated deliveries, remove from waiting list.
if rdWait {
wr = remove(pre, wr)
continue
}
}
// Normally it's a timeout.
if expires {
hdr := fmt.Appendf(nil, "NATS/1.0 408 Request Timeout\r\n%s: %d\r\n%s: %d\r\n\r\n", JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
wr = remove(pre, wr)
continue
} else if wr.expires.IsZero() || wr.d > 0 {
// But if we're NoWait without expiry, we've reached the end of the stream, and we've not delivered any messages.
// Return no messages instead, which is the same as if we'd rejected the pull request initially.
hdr := fmt.Appendf(nil, "NATS/1.0 404 No Messages\r\n\r\n")
o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
wr = remove(pre, wr)
continue
}
}
// Now check interest.
interest := wr.acc.sl.HasInterest(wr.interest)
if !interest && (s.leafNodeEnabled || s.gateway.enabled) {
// If we are here check on gateways and leaf nodes (as they can mask gateways on the other end).
// If we have interest or the request is too young break and do not expire.
if time.Since(wr.received) < defaultGatewayRecentSubExpiration {
interest = true
} else if s.gateway.enabled && s.hasGatewayInterest(wr.acc.Name, wr.interest) {
interest = true
}
}
// Check if we have interest.
if !interest {
// No more interest here so go ahead and remove this one from our list.
wr = remove(pre, wr)
continue
}
// If interest, update batch pending requests counter and update fexp timer.
brp += wr.n
if !wr.hbt.IsZero() {
if now.After(wr.hbt) {
// Fire off a heartbeat here.
o.sendIdleHeartbeat(wr.reply)
// Update next HB.
wr.hbt = now.Add(wr.hb)
}
if fexp.IsZero() || wr.hbt.Before(fexp) {
fexp = wr.hbt
}
}
if !wr.expires.IsZero() && (fexp.IsZero() || wr.expires.Before(fexp)) {
fexp = wr.expires
}
// Update pre and wr here.
pre = wr
wr = wr.next
}
return expired, wq.len(), brp, fexp
}
// Will check to make sure those waiting still have registered interest.
func (o *consumer) checkWaitingForInterest() bool {
o.processWaiting(true)
return o.waiting.len() > 0
}
// Lock should be held.
func (o *consumer) hbTimer() (time.Duration, *time.Timer) {
if o.cfg.Heartbeat == 0 {
return 0, nil
}
return o.cfg.Heartbeat, time.NewTimer(o.cfg.Heartbeat)
}
// Check here for conditions when our ack floor may have drifted below the streams first sequence.
// In general this is accounted for in normal operations, but if the consumer misses the signal from
// the stream it will not clear the message and move the ack state.
// Should only be called from consumer leader.
func (o *consumer) checkAckFloor() {
o.mu.RLock()
mset, closed, asflr, numPending := o.mset, o.closed, o.asflr, len(o.pending)
o.mu.RUnlock()
if asflr == 0 || closed || mset == nil {
return
}
var ss StreamState
mset.store.FastState(&ss)
// If our floor is equal or greater that is normal and nothing for us to do.
if ss.FirstSeq == 0 || asflr >= ss.FirstSeq-1 {
return
}
// Check which linear space is less to walk.
if ss.FirstSeq-asflr-1 < uint64(numPending) {
// Process all messages that no longer exist.
for seq := asflr + 1; seq < ss.FirstSeq; seq++ {
// Check if this message was pending.
o.mu.RLock()
p, isPending := o.pending[seq]
rdc := o.deliveryCount(seq)
o.mu.RUnlock()
// If it was pending for us, get rid of it.
if isPending {
o.processTerm(seq, p.Sequence, rdc, ackTermLimitsReason, _EMPTY_)
}
}
} else if numPending > 0 {
// here it is shorter to walk pending.
// toTerm is seq, dseq, rcd for each entry.
toTerm := make([]uint64, 0, numPending*3)
o.mu.RLock()
for seq, p := range o.pending {
if seq < ss.FirstSeq {
var dseq uint64 = 1
if p != nil {
dseq = p.Sequence
}
rdc := o.deliveryCount(seq)
toTerm = append(toTerm, seq, dseq, rdc)
}
}
o.mu.RUnlock()
for i := 0; i < len(toTerm); i += 3 {
seq, dseq, rdc := toTerm[i], toTerm[i+1], toTerm[i+2]
o.processTerm(seq, dseq, rdc, ackTermLimitsReason, _EMPTY_)
}
}
// Do one final check here.
o.mu.Lock()
defer o.mu.Unlock()
// If we are closed do not change anything and simply return.
if o.closed {
return
}
// If we are here, and this should be rare, we still are off with our ack floor.
// We will make sure we are not doing un-necessary work here if only off by a bit
// since this could be normal for a high activity wq or stream.
// We will set it explicitly to 1 behind our current lowest in pending, or if
// pending is empty, to our current delivered -1.
const minOffThreshold = 50
if ss.FirstSeq >= minOffThreshold && o.asflr < ss.FirstSeq-minOffThreshold {
var psseq, pdseq uint64
for seq, p := range o.pending {
if psseq == 0 || seq < psseq {
psseq, pdseq = seq, p.Sequence
}
}
// If we still have none, set to current delivered -1.
if psseq == 0 {
psseq, pdseq = o.sseq-1, o.dseq-1
// If still not adjusted.
if psseq < ss.FirstSeq-1 {
psseq = ss.FirstSeq - 1
}
} else {
// Since this was set via the pending, we should not include
// it directly but set floors to -1.
psseq, pdseq = psseq-1, pdseq-1
}
o.asflr, o.adflr = psseq, pdseq
}
}
func (o *consumer) processInboundAcks(qch chan struct{}) {
// Grab the server lock to watch for server quit.
o.mu.RLock()
s, mset := o.srv, o.mset
hasInactiveThresh := o.cfg.InactiveThreshold > 0
o.mu.RUnlock()
if s == nil || mset == nil {
return
}
// We will check this on entry and periodically.
o.checkAckFloor()
// How often we will check for ack floor drift.
// Spread these out for large numbers on a server restart.
delta := time.Duration(rand.Int63n(int64(time.Minute)))
ticker := time.NewTicker(time.Minute + delta)
defer ticker.Stop()
for {
select {
case <-o.ackMsgs.ch:
// If we have an inactiveThreshold set, mark our activity.
// Do this before processing acks, otherwise we might race if there are no pending messages
// anymore and the inactivity threshold kicks in before we're able to mark activity.
if hasInactiveThresh {
o.suppressDeletion()
}
acks := o.ackMsgs.pop()
for _, ack := range acks {
o.processAck(ack.subject, ack.reply, ack.hdr, ack.msg)
ack.returnToPool()
}
o.ackMsgs.recycle(&acks)
case <-ticker.C:
o.checkAckFloor()
case <-qch:
return
case <-s.quitCh:
return
}
}
}
// Process inbound next message requests.
func (o *consumer) processInboundNextMsgReqs(qch chan struct{}) {
// Grab the server lock to watch for server quit.
o.mu.RLock()
s := o.srv
o.mu.RUnlock()
for {
select {
case <-o.nextMsgReqs.ch:
reqs := o.nextMsgReqs.pop()
for _, req := range reqs {
o.processNextMsgRequest(req.reply, req.msg)
req.returnToPool()
}
o.nextMsgReqs.recycle(&reqs)
case <-qch:
return
case <-s.quitCh:
return
}
}
}
// Suppress auto cleanup on ack activity of any kind.
func (o *consumer) suppressDeletion() {
o.mu.Lock()
defer o.mu.Unlock()
if o.closed {
return
}
if o.isPushMode() && o.dtmr != nil {
// if dtmr is not nil we have started the countdown, simply reset to threshold.
o.dtmr.Reset(o.dthresh)
} else if o.isPullMode() && o.waiting != nil {
// Pull mode always has timer running, update last on waiting queue.
o.waiting.last = time.Now()
if o.dtmr != nil {
o.dtmr.Reset(o.dthresh)
}
}
}
// loopAndGatherMsgs waits for messages for the consumer. qch is the quit channel,
// upch is the unpause channel which fires when the PauseUntil deadline is reached.
func (o *consumer) loopAndGatherMsgs(qch chan struct{}) {
// On startup check to see if we are in a reply situation where replay policy is not instant.
var (
lts int64 // last time stamp seen, used for replay.
lseq uint64
)
o.mu.RLock()
mset := o.mset
getLSeq := o.replay
o.mu.RUnlock()
// consumer is closed when mset is set to nil.
if mset == nil {
return
}
if getLSeq {
lseq = mset.state().LastSeq
}
o.mu.Lock()
s := o.srv
// need to check again if consumer is closed
if o.mset == nil {
o.mu.Unlock()
return
}
// For idle heartbeat support.
var hbc <-chan time.Time
hbd, hb := o.hbTimer()
if hb != nil {
hbc = hb.C
}
// Interest changes.
inch := o.inch
o.mu.Unlock()
// Grab the stream's retention policy and name
mset.cfgMu.RLock()
stream, rp := mset.cfg.Name, mset.cfg.Retention
mset.cfgMu.RUnlock()
var err error
// Deliver all the msgs we have now, once done or on a condition, we wait for new ones.
for {
var (
pmsg *jsPubMsg
dc uint64
dsubj string
ackReply string
delay time.Duration
sz int
wrn, wrb int
)
o.mu.Lock()
// consumer is closed when mset is set to nil.
if o.closed || o.mset == nil {
o.mu.Unlock()
return
}
// Clear last error.
err = nil
// If the consumer is paused then stop sending.
if o.cfg.PauseUntil != nil && !o.cfg.PauseUntil.IsZero() && time.Now().Before(*o.cfg.PauseUntil) {
// If the consumer is paused and we haven't reached the deadline yet then
// go back to waiting.
goto waitForMsgs
}
// If we are in push mode and not active or under flowcontrol let's stop sending.
if o.isPushMode() {
if !o.active || (o.maxpb > 0 && o.pbytes > o.maxpb) {
goto waitForMsgs
}
} else if o.waiting.isEmpty() {
// If we are in pull mode and no one is waiting already break and wait.
goto waitForMsgs
}
// Grab our next msg.
pmsg, dc, err = o.getNextMsg()
// We can release the lock now under getNextMsg so need to check this condition again here.
if o.closed || o.mset == nil {
o.mu.Unlock()
return
}
// On error either wait or return.
if err != nil || pmsg == nil {
// On EOF we can optionally fast sync num pending state.
if err == ErrStoreEOF {
o.checkNumPendingOnEOF()
}
if err == ErrStoreMsgNotFound || err == errDeletedMsg || err == ErrStoreEOF || err == errMaxAckPending {
goto waitForMsgs
} else {
if pmsg != nil {
s.Errorf("Received an error looking up message with sequence %d for consumer '%s > %s > %s': %v",
pmsg.seq, o.mset.acc, stream, o.cfg.Name, err)
} else {
s.Errorf("Received an error looking up message for consumer '%s > %s > %s': %v",
o.mset.acc, stream, o.cfg.Name, err)
}
goto waitForMsgs
}
}
// Update our cached num pending here first.
if dc == 1 {
o.npc--
}
// Pre-calculate ackReply
ackReply = o.ackReply(pmsg.seq, o.dseq, dc, pmsg.ts, o.numPending())
// If headers only do not send msg payload.
// Add in msg size itself as header.
if o.cfg.HeadersOnly {
convertToHeadersOnly(pmsg)
}
// Calculate payload size. This can be calculated on client side.
// We do not include transport subject here since not generally known on client.
sz = len(pmsg.subj) + len(ackReply) + len(pmsg.hdr) + len(pmsg.msg)
if o.isPushMode() {
dsubj = o.dsubj
} else if wr := o.nextWaiting(sz); wr != nil {
wrn, wrb = wr.n, wr.b
dsubj = wr.reply
if o.cfg.PriorityPolicy == PriorityPinnedClient {
pmsg.hdr = genHeader(pmsg.hdr, JSPullRequestNatsPinId, o.currentPinId)
pmsg.buf = append(pmsg.hdr, pmsg.msg...)
sz = len(pmsg.subj) + len(ackReply) + len(pmsg.hdr) + len(pmsg.msg)
}
if done := wr.recycleIfDone(); done && o.node != nil {
o.removeClusterPendingRequest(dsubj)
} else if !done && wr.hb > 0 {
wr.hbt = time.Now().Add(wr.hb)
}
} else {
// We will redo this one as long as this is not a redelivery.
// Need to also test that this is not going backwards since if
// we fail to deliver we can end up here from rdq but we do not
// want to decrement o.sseq if that is the case.
if dc == 1 && pmsg.seq == o.sseq-1 {
o.sseq--
o.npc++
} else if !o.onRedeliverQueue(pmsg.seq) {
// We are not on the rdq so decrement the delivery count
// and add it back.
o.decDeliveryCount(pmsg.seq)
o.addToRedeliverQueue(pmsg.seq)
}
pmsg.returnToPool()
pmsg = nil
goto waitForMsgs
}
// If we are in a replay scenario and have not caught up check if we need to delay here.
if o.replay && lts > 0 {
if delay = time.Duration(pmsg.ts - lts); delay > time.Millisecond {
o.mu.Unlock()
select {
case <-qch:
pmsg.returnToPool()
pmsg = nil
return
case <-time.After(delay):
}
o.mu.Lock()
}
}
// Track this regardless.
lts = pmsg.ts
// If we have a rate limit set make sure we check that here.
if o.rlimit != nil {
now := time.Now()
r := o.rlimit.ReserveN(now, sz)
delay := r.DelayFrom(now)
if delay > 0 {
o.mu.Unlock()
select {
case <-qch:
pmsg.returnToPool()
pmsg = nil
return
case <-time.After(delay):
}
o.mu.Lock()
}
}
// Do actual delivery.
o.deliverMsg(dsubj, ackReply, pmsg, dc, rp)
// If given request fulfilled batch size, but there are still pending bytes, send information about it.
if wrn <= 0 && wrb > 0 {
msg := fmt.Appendf(nil, JsPullRequestRemainingBytesT, JSPullRequestPendingMsgs, wrn, JSPullRequestPendingBytes, wrb)
o.outq.send(newJSPubMsg(dsubj, _EMPTY_, _EMPTY_, msg, nil, nil, 0))
}
// Reset our idle heartbeat timer if set.
if hb != nil {
hb.Reset(hbd)
}
o.mu.Unlock()
continue
waitForMsgs:
// If we were in a replay state check to see if we are caught up. If so clear.
if o.replay && o.sseq > lseq {
o.replay = false
}
// Make sure to process any expired requests that are pending.
var wrExp <-chan time.Time
if o.isPullMode() {
// Dont expire oneshots if we are here because of max ack pending limit.
_, _, _, fexp := o.processWaiting(err != errMaxAckPending)
if !fexp.IsZero() {
expires := time.Until(fexp)
if expires <= 0 {
expires = time.Millisecond
}
wrExp = time.NewTimer(expires).C
}
}
// We will wait here for new messages to arrive.
mch, odsubj := o.mch, o.cfg.DeliverSubject
o.mu.Unlock()
select {
case <-mch:
// Messages are waiting.
case interest := <-inch:
// inch can be nil on pull-based, but then this will
// just block and not fire.
o.updateDeliveryInterest(interest)
case <-qch:
return
case <-wrExp:
o.mu.Lock()
o.processWaiting(true)
o.mu.Unlock()
case <-hbc:
if o.isActive() {
o.mu.Lock()
o.sendIdleHeartbeat(odsubj)
// Send flow control on EOS if it's used for acknowledgements.
if o.cfg.AckPolicy == AckFlowControl && len(o.pending) > 0 && o.fcid == _EMPTY_ {
o.sendFlowControl()
}
o.mu.Unlock()
}
// Reset our idle heartbeat timer.
hb.Reset(hbd)
}
}
}
// Lock should be held.
func (o *consumer) sendIdleHeartbeat(subj string) {
const t = "NATS/1.0 100 Idle Heartbeat\r\n%s: %d\r\n%s: %d\r\n\r\n"
sseq, dseq := o.sseq-1, o.dseq-1
hdr := fmt.Appendf(nil, t, JSLastConsumerSeq, dseq, JSLastStreamSeq, sseq)
if fcp := o.fcid; fcp != _EMPTY_ {
// Add in that we are stalled on flow control here.
addOn := fmt.Appendf(nil, "%s: %s\r\n\r\n", JSConsumerStalled, fcp)
hdr = append(hdr[:len(hdr)-LEN_CR_LF], []byte(addOn)...)
}
o.outq.send(newJSPubMsg(subj, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
}
func (o *consumer) ackReply(sseq, dseq, dc uint64, ts int64, pending uint64) string {
if o.useV2Ack {
return fmt.Sprintf(o.ackReplyT, dc, sseq, dseq, ts, pending)
}
return fmt.Sprintf(o.ackReplyOldT, dc, sseq, dseq, ts, pending)
}
// Used mostly for testing. Sets max pending bytes for flow control setups.
func (o *consumer) setMaxPendingBytes(limit int) {
o.pblimit = limit
o.maxpb = limit / 16
if o.maxpb == 0 {
o.maxpb = 1
}
}
// Does some sanity checks to see if we should re-calculate.
// Since there is a race when decrementing when there is contention at the beginning of the stream.
// The race is a getNextMsg skips a deleted msg, and then the decStreamPending call fires.
// This does some quick sanity checks to see if we should re-calculate num pending.
// Lock should be held.
func (o *consumer) checkNumPending() (uint64, error) {
if o.mset != nil && o.mset.store != nil {
var state StreamState
o.mset.store.FastState(&state)
npc := o.numPending()
// Make sure we can't report more messages than there are.
// TODO(nat): It's not great that this means consumer info has side effects,
// since we can't know whether anyone will call it or not. The previous num
// pending calculation that this replaces had the same problem though.
if o.sseq > state.LastSeq {
o.npc = 0
} else if npc > 0 {
o.npc = int64(min(npc, state.Msgs, state.LastSeq-o.sseq+1))
}
}
return o.numPending(), nil
}
// Lock should be held.
func (o *consumer) numPending() uint64 {
if o.npc < 0 {
return 0
}
return uint64(o.npc)
}
// This will do a quick sanity check on num pending when we encounter
// and EOF in the loop and gather.
// Lock should be held.
func (o *consumer) checkNumPendingOnEOF() {
if o.mset == nil {
return
}
var state StreamState
o.mset.store.FastState(&state)
if o.sseq > state.LastSeq && o.npc != 0 {
// We know here we can reset our running state for num pending.
o.npc, o.npf = 0, state.LastSeq
}
}
// Call into streamNumPending after acquiring the consumer lock.
func (o *consumer) streamNumPendingLocked() (uint64, error) {
o.mu.Lock()
defer o.mu.Unlock()
return o.streamNumPending()
}
// Will force a set from the stream store of num pending.
// Depends on delivery policy, for last per subject we calculate differently.
// Lock should be held.
func (o *consumer) streamNumPending() (uint64, error) {
if o.mset == nil || o.mset.store == nil {
o.npc, o.npf = 0, 0
return 0, nil
}
npc, npf, err := o.calculateNumPending()
if err != nil {
return 0, err
}
o.npc, o.npf = int64(npc), npf
return o.numPending(), nil
}
// Will calculate num pending but only requires a read lock.
// Depends on delivery policy, for last per subject we calculate differently.
// At least RLock should be held.
func (o *consumer) calculateNumPending() (npc, npf uint64, err error) {
if o.mset == nil || o.mset.store == nil {
return 0, 0, nil
}
isLastPerSubject := o.cfg.DeliverPolicy == DeliverLastPerSubject
filters, subjf := o.filters, o.subjf
if filters != nil {
return o.mset.store.NumPendingMulti(o.sseq, filters, isLastPerSubject)
} else if len(subjf) > 0 {
filter := subjf[0].subject
return o.mset.store.NumPending(o.sseq, filter, isLastPerSubject)
}
return o.mset.store.NumPending(o.sseq, _EMPTY_, isLastPerSubject)
}
func convertToHeadersOnly(pmsg *jsPubMsg) {
// If headers only do not send msg payload.
// Add in msg size itself as header.
hdr, msg := pmsg.hdr, pmsg.msg
var bb bytes.Buffer
if len(hdr) == 0 {
bb.WriteString(hdrLine)
} else {
bb.Write(hdr)
bb.Truncate(len(hdr) - LEN_CR_LF)
}
bb.WriteString(JSMsgSize)
bb.WriteString(": ")
bb.WriteString(strconv.FormatInt(int64(len(msg)), 10))
bb.WriteString(CR_LF)
bb.WriteString(CR_LF)
// Replace underlying buf which we can use directly when we send.
// TODO(dlc) - Probably just use directly when forming bytes.Buffer?
pmsg.buf = pmsg.buf[:0]
pmsg.buf = append(pmsg.buf, bb.Bytes()...)
// Replace with new header.
pmsg.hdr = pmsg.buf
// Cancel msg payload
pmsg.msg = nil
}
// Deliver a msg to the consumer.
// Lock should be held and o.mset validated to be non-nil.
func (o *consumer) deliverMsg(dsubj, ackReply string, pmsg *jsPubMsg, dc uint64, rp RetentionPolicy) {
if o.mset == nil {
pmsg.returnToPool()
return
}
dseq := o.dseq
o.dseq++
pmsg.dsubj, pmsg.reply, pmsg.o = dsubj, ackReply, o
psz := pmsg.size()
if o.maxpb > 0 {
o.pbytes += psz
}
mset := o.mset
ap := o.cfg.AckPolicy
// Cant touch pmsg after this sending so capture what we need.
seq, ts := pmsg.seq, pmsg.ts
// Update delivered first.
o.updateDelivered(dseq, seq, dc, ts)
if ap == AckNone {
o.adflr = dseq
o.asflr = seq
} else {
o.trackPending(seq, dseq)
}
// Send message.
if o.replicateDeliveries() {
o.addReplicatedQueuedMsg(pmsg)
} else {
o.outq.send(pmsg)
}
// Flow control.
if o.maxpb > 0 && o.needFlowControl(psz) {
o.sendFlowControl()
}
// If pull mode and we have inactivity threshold, signaled by dthresh, update last activity.
if o.isPullMode() && o.dthresh > 0 {
o.waiting.last = time.Now()
}
// If we are ack none and mset is interest only we should make sure stream removes interest.
if ap == AckNone && rp != LimitsPolicy {
if mset != nil && mset.ackq != nil && (o.node == nil || o.cfg.Direct) {
mset.ackq.push(seq)
} else {
o.updateAcks(dseq, seq, _EMPTY_)
}
}
}
// replicateDeliveries returns whether deliveries should be replicated before sending them.
// If we're replicated we MUST only send the message AFTER we've got quorum for updating
// delivered state. Otherwise, we could be in an invalid state after a leader change.
// We can send immediately if not replicated, not using acks, or using flow control (incompatible).
// Lock should be held.
func (o *consumer) replicateDeliveries() bool {
return o.node != nil && o.cfg.AckPolicy != AckNone && !o.cfg.FlowControl
}
func (o *consumer) needFlowControl(sz int) bool {
if o.maxpb == 0 {
return false
}
// Decide whether to send a flow control message which we will need the user to respond.
// We send when we are over 50% of our current window limit.
if o.fcid == _EMPTY_ && o.pbytes > o.maxpb/2 {
return true
}
// Or, when acking based on flow control, we need to send it if we've hit the max pending limit earlier.
if o.fcid == _EMPTY_ && o.cfg.AckPolicy == AckFlowControl && o.maxp > 0 && len(o.pending) >= o.maxp {
return true
}
// If we have an existing outstanding FC, check to see if we need to expand the o.fcsz
if o.fcid != _EMPTY_ && (o.pbytes-o.fcsz) >= o.maxpb {
o.fcsz += sz
}
return false
}
func (o *consumer) processFlowControl(_ *subscription, c *client, _ *Account, subj, _ string, rmsg []byte) {
o.mu.Lock()
// Ignore if not the latest we have sent out.
if subj != o.fcid {
o.mu.Unlock()
return
}
// For slow starts and ramping up.
if o.maxpb < o.pblimit {
o.maxpb *= 2
if o.maxpb > o.pblimit {
o.maxpb = o.pblimit
}
}
// Update accounting.
o.pbytes -= o.fcsz
if o.pbytes < 0 {
o.pbytes = 0
}
o.fcid, o.fcsz = _EMPTY_, 0
o.signalNewMessages()
ackFlowControl := o.cfg.AckPolicy == AckFlowControl
o.mu.Unlock()
if !ackFlowControl {
return
}
hdr, _ := c.msgParts(rmsg)
if len(hdr) > 0 {
ldseq := parseInt64(sliceHeader(JSLastConsumerSeq, hdr))
lsseq := parseInt64(sliceHeader(JSLastStreamSeq, hdr))
if lsseq > 0 {
// Delivered sequence is allowed to be zero as a response
// to flow control without any deliveries.
if ldseq <= 0 {
ldseq = 0
}
o.processAckMsg(uint64(lsseq), uint64(ldseq), 1, _EMPTY_, false)
}
}
}
// Lock should be held.
func (o *consumer) fcReply() string {
var sb strings.Builder
if o.useV2Ack {
sb.WriteString(o.fcPre)
} else {
sb.WriteString(o.fcPreOld)
}
sb.WriteString(o.stream)
sb.WriteByte(btsep)
sb.WriteString(o.name)
sb.WriteByte(btsep)
var b [4]byte
rn := rand.Int63()
for i, l := 0, rn; i < len(b); i++ {
b[i] = digits[l%base]
l /= base
}
sb.Write(b[:])
return sb.String()
}
// sendFlowControl will send a flow control packet to the consumer.
// Lock should be held.
func (o *consumer) sendFlowControl() {
if !o.isPushMode() {
return
}
subj, rply := o.cfg.DeliverSubject, o.fcReply()
o.fcsz, o.fcid = o.pbytes, rply
hdr := []byte("NATS/1.0 100 FlowControl Request\r\n\r\n")
o.outq.send(newJSPubMsg(subj, _EMPTY_, rply, hdr, nil, nil, 0))
}
// Tracks our outstanding pending acks. Only applicable to AckExplicit mode.
// Lock should be held.
func (o *consumer) trackPending(sseq, dseq uint64) {
if o.pending == nil {
o.pending = make(map[uint64]*Pending)
}
now := time.Now()
if p, ok := o.pending[sseq]; ok {
// Update timestamp but keep original consumer delivery sequence.
// So do not update p.Sequence.
p.Timestamp = now.UnixNano()
} else {
o.pending[sseq] = &Pending{dseq, now.UnixNano()}
}
// We could have a backoff that set a timer higher than what we need for this message.
// In that case, reset to lowest backoff required for a message redelivery.
minDelay := o.ackWait(0)
if l := len(o.cfg.BackOff); l > 0 {
bi := int(o.rdc[sseq])
if bi < 0 {
bi = 0
} else if bi >= l {
bi = l - 1
}
minDelay = o.ackWait(o.cfg.BackOff[bi])
}
minDeadline := now.Add(minDelay)
if o.ptmr == nil || o.ptmrEnd.After(minDeadline) {
o.resetPtmr(minDelay)
}
}
// Credit back a failed delivery.
// lock should be held.
func (o *consumer) creditWaitingRequest(reply string) {
wq := o.waiting
for wr := wq.head; wr != nil; wr = wr.next {
if wr.reply == reply {
wr.n++
wr.d--
return
}
}
}
// didNotDeliver is called when a delivery for a consumer message failed.
// Depending on our state, we will process the failure.
func (o *consumer) didNotDeliver(seq uint64, subj string) {
o.mu.Lock()
mset := o.mset
if mset == nil {
o.mu.Unlock()
return
}
// Adjust back deliver count.
o.decDeliveryCount(seq)
var checkDeliveryInterest bool
if o.isPushMode() {
o.active = false
checkDeliveryInterest = true
} else if o.pending != nil {
// Good chance we did not deliver because no interest so force a check.
o.processWaiting(false)
// If it is still there credit it back.
o.creditWaitingRequest(subj)
// pull mode and we have pending.
if _, ok := o.pending[seq]; ok {
// We found this messsage on pending, we need
// to queue it up for immediate redelivery since
// we know it was not delivered
if !o.onRedeliverQueue(seq) {
o.addToRedeliverQueue(seq)
if !o.waiting.isEmpty() {
o.signalNewMessages()
}
}
}
}
o.mu.Unlock()
if checkDeliveryInterest {
localInterest := !o.hasNoLocalInterest()
o.updateDeliveryInterest(localInterest)
}
}
// Lock should be held.
func (o *consumer) addToRedeliverQueue(seqs ...uint64) {
o.rdq = append(o.rdq, seqs...)
for _, seq := range seqs {
o.rdqi.Insert(seq)
}
}
// Lock should be held.
func (o *consumer) hasRedeliveries() bool {
return len(o.rdq) > 0
}
func (o *consumer) getNextToRedeliver() uint64 {
if len(o.rdq) == 0 {
return 0
}
seq := o.rdq[0]
if len(o.rdq) == 1 {
o.rdq = nil
o.rdqi.Empty()
} else {
o.rdq = append(o.rdq[:0], o.rdq[1:]...)
o.rdqi.Delete(seq)
}
return seq
}
// This checks if we already have this sequence queued for redelivery.
// FIXME(dlc) - This is O(n) but should be fast with small redeliver size.
// Lock should be held.
func (o *consumer) onRedeliverQueue(seq uint64) bool {
return o.rdqi.Exists(seq)
}
// Remove a sequence from the redelivery queue.
// Lock should be held.
func (o *consumer) removeFromRedeliverQueue(seq uint64) bool {
if !o.onRedeliverQueue(seq) {
return false
}
for i, rseq := range o.rdq {
if rseq == seq {
if len(o.rdq) == 1 {
o.rdq = nil
o.rdqi.Empty()
} else {
o.rdq = append(o.rdq[:i], o.rdq[i+1:]...)
o.rdqi.Delete(seq)
}
return true
}
}
return false
}
// Checks the pending messages.
func (o *consumer) checkPending() {
o.mu.Lock()
defer o.mu.Unlock()
mset := o.mset
ttl := int64(o.cfg.AckWait)
// On stop, mset and timer will be nil.
if o.closed || mset == nil || o.ptmr == nil || ttl == 0 {
o.stopAndClearPtmr()
return
}
var shouldUpdateState bool
var state StreamState
mset.store.FastState(&state)
fseq := state.FirstSeq
now := time.Now().UnixNano()
next := int64(o.ackWait(0))
// However, if there is backoff, initializes with the largest backoff.
// It will be adjusted as needed.
if l := len(o.cfg.BackOff); l > 0 {
next = int64(o.cfg.BackOff[l-1])
}
// Since we can update timestamps, we have to review all pending.
// We will now bail if we see an ack pending inbound to us via o.awl.
var expired []uint64
check := len(o.pending) > 1024
for seq, p := range o.pending {
if check && atomic.LoadInt64(&o.awl) > 0 {
o.resetPtmr(100 * time.Millisecond)
return
}
// Check if these are no longer valid.
if seq < fseq || seq <= o.asflr {
delete(o.pending, seq)
delete(o.rdc, seq)
o.removeFromRedeliverQueue(seq)
shouldUpdateState = true
// Check if we need to move ack floors.
if seq > o.asflr {
o.asflr = seq
}
if p.Sequence > o.adflr {
o.adflr = p.Sequence
}
continue
}
elapsed, deadline := now-p.Timestamp, ttl
if len(o.cfg.BackOff) > 0 {
// This is ok even if o.rdc is nil, we would get dc == 0, which is what we want.
dc := int(o.rdc[seq])
if dc < 0 {
// Prevent consumer backoff from going backwards.
dc = 0
}
// This will be the index for the next backoff, will set to last element if needed.
nbi := dc + 1
if dc+1 >= len(o.cfg.BackOff) {
dc = len(o.cfg.BackOff) - 1
nbi = dc
}
deadline = int64(o.cfg.BackOff[dc])
// Set `next` to the next backoff (if smaller than current `next` value).
if nextBackoff := int64(o.cfg.BackOff[nbi]); nextBackoff < next {
next = nextBackoff
}
}
if elapsed >= deadline {
// We will check if we have hit our max deliveries. Previously we would do this on getNextMsg() which
// worked well for push consumers, but with pull based consumers would require a new pull request to be
// present to process and redelivered could be reported incorrectly.
if !o.onRedeliverQueue(seq) && !o.hasMaxDeliveries(seq) {
expired = append(expired, seq)
}
} else if deadline-elapsed < next {
// Update when we should fire next.
next = deadline - elapsed
}
}
if len(expired) > 0 {
// We need to sort.
slices.Sort(expired)
o.addToRedeliverQueue(expired...)
// Now we should update the timestamp here since we are redelivering.
// We will use an incrementing time to preserve order for any other redelivery.
off := now - o.pending[expired[0]].Timestamp
for _, seq := range expired {
if p, ok := o.pending[seq]; ok {
p.Timestamp += off
}
}
o.signalNewMessages()
}
if len(o.pending) > 0 {
o.resetPtmr(time.Duration(next))
} else {
// Make sure to stop timer and clear out any re delivery queues
o.stopAndClearPtmr()
o.rdq = nil
o.rdqi.Empty()
o.pending = nil
// Mimic behavior in processAckMsg when pending is empty.
o.adflr, o.asflr = o.dseq-1, o.sseq-1
}
// Update our state if needed.
if shouldUpdateState {
if err := o.writeStoreStateUnlocked(); err != nil && o.srv != nil && o.mset != nil && !o.closed {
s, acc, mset, name := o.srv, o.acc, o.mset, o.name
s.Warnf("Consumer '%s > %s > %s' error on write store state from check pending: %v", acc, mset.getCfgName(), name, err)
}
}
}
// SeqFromReply will extract a sequence number from a reply subject.
func (o *consumer) seqFromReply(reply string) uint64 {
_, dseq, _, _, _ := ackReplyInfo(reply)
return dseq
}
// StreamSeqFromReply will extract the stream sequence from the reply subject.
func (o *consumer) streamSeqFromReply(reply string) uint64 {
sseq, _, _, _, _ := ackReplyInfo(reply)
return sseq
}
// Quick parser for positive numbers in ack reply encoding.
func parseAckReplyNum(d string) (n int64) {
if len(d) == 0 {
return -1
}
for _, dec := range d {
if dec < asciiZero || dec > asciiNine {
return -1
}
n = n*10 + (int64(dec) - asciiZero)
}
return n
}
const (
expectedNumReplyTokensV1 = 9
expectedNumReplyTokensV2 = 11
)
// Grab encoded information in the reply subject for a delivered message.
func ackReplyInfo(subject string) (sseq, dseq, dc uint64, ts int64, pending uint64) {
tsa := [expectedNumReplyTokensV2]string{}
start, tokens := 0, tsa[:0]
for i := 0; i < len(subject); i++ {
if subject[i] == btsep {
tokens = append(tokens, subject[start:i])
start = i + 1
}
}
tokens = append(tokens, subject[start:])
if (len(tokens) != expectedNumReplyTokensV1 && len(tokens) < expectedNumReplyTokensV2) || tokens[0] != "$JS" || tokens[1] != "ACK" {
return 0, 0, 0, 0, 0
}
offset := 2
if len(tokens) >= expectedNumReplyTokensV2 {
offset = 4
}
// TODO(dlc) - Should we error if we do not match consumer name?
// stream is tokens[offset], consumer is offset+1.
dc = uint64(parseAckReplyNum(tokens[offset+2]))
sseq, dseq = uint64(parseAckReplyNum(tokens[offset+3])), uint64(parseAckReplyNum(tokens[offset+4]))
ts = parseAckReplyNum(tokens[offset+5])
pending = uint64(parseAckReplyNum(tokens[offset+6]))
return sseq, dseq, dc, ts, pending
}
// NextSeq returns the next delivered sequence number for this consumer.
func (o *consumer) nextSeq() uint64 {
o.mu.RLock()
dseq := o.dseq
o.mu.RUnlock()
return dseq
}
// Used to hold skip list when deliver policy is last per subject.
type lastSeqSkipList struct {
resume uint64
seqs []uint64
}
// Let's us know we have a skip list, which is for deliver last per subject and we are just starting.
// Lock should be held.
func (o *consumer) hasSkipListPending() bool {
return o.lss != nil && len(o.lss.seqs) > 0
}
// reconcileStateWithStream reconciles consumer state when the stream has reverted
// due to data loss (e.g., VM crash). This handles the case where consumer state
// is ahead of the stream's last sequence.
// Lock should be held.
func (o *consumer) reconcileStateWithStream(streamLastSeq uint64) {
// If an ack floor is higher than stream last sequence,
// reset back down but keep the highest known sequences.
if o.asflr > streamLastSeq {
o.asflr = streamLastSeq
// Delivery floor is one below the delivered sequence,
// but if it is zero somehow, ensure we don't underflow.
o.adflr = o.dseq
if o.adflr > 0 {
o.adflr--
}
o.pending = nil
o.rdc = nil
}
// Remove pending entries that are beyond the stream's last sequence
if len(o.pending) > 0 {
for seq := range o.pending {
if seq > streamLastSeq {
delete(o.pending, seq)
}
}
}
// Remove redelivered entries that are beyond the stream's last sequence
if len(o.rdc) > 0 {
for seq := range o.rdc {
if seq > streamLastSeq {
delete(o.rdc, seq)
}
}
}
// Update starting sequence and delivery sequence based on pending state
if len(o.pending) == 0 {
o.sseq = o.asflr + 1
o.dseq = o.adflr + 1
} else {
// Find highest stream sequence in pending
var maxStreamSeq uint64
var maxConsumerSeq uint64
for streamSeq, p := range o.pending {
if streamSeq > maxStreamSeq {
maxStreamSeq = streamSeq
}
if p.Sequence > maxConsumerSeq {
maxConsumerSeq = p.Sequence
}
}
// Set next sequences based on highest pending
o.sseq = maxStreamSeq + 1
o.dseq = maxConsumerSeq + 1
}
}
// Will select the starting sequence.
func (o *consumer) selectStartingSeqNo() error {
if o.mset == nil || o.mset.store == nil {
o.sseq = 1
} else {
var state StreamState
o.mset.store.FastState(&state)
if o.cfg.OptStartSeq == 0 {
if o.cfg.DeliverPolicy == DeliverAll {
o.sseq = state.FirstSeq
} else if o.cfg.DeliverPolicy == DeliverLast {
if o.subjf == nil {
o.sseq = state.LastSeq
} else {
// If we are partitioned here this will be properly set when we become leader.
for _, filter := range o.subjf {
ss, err := o.mset.store.FilteredState(1, filter.subject)
if err != nil {
return err
}
if ss.Last > o.sseq {
o.sseq = ss.Last
}
}
}
} else if o.cfg.DeliverPolicy == DeliverLastPerSubject {
// If our parent stream is set to max msgs per subject of 1 this is just
// a normal consumer at this point. We can avoid any heavy lifting.
o.mset.cfgMu.RLock()
mmp := o.mset.cfg.MaxMsgsPer
o.mset.cfgMu.RUnlock()
if mmp == 1 {
o.sseq = state.FirstSeq
} else {
filters := make([]string, 0, len(o.subjf))
if o.subjf == nil {
filters = append(filters, o.cfg.FilterSubject)
} else {
for _, filter := range o.subjf {
filters = append(filters, filter.subject)
}
}
lss := &lastSeqSkipList{resume: state.LastSeq}
lss.seqs, _ = o.mset.store.MultiLastSeqs(filters, 0, 0)
if len(lss.seqs) == 0 {
o.sseq = state.LastSeq
} else {
o.sseq = lss.seqs[0]
}
// Assign skip list.
o.lss = lss
}
} else if o.cfg.OptStartTime != nil {
// If we are here we are time based.
// TODO(dlc) - Once clustered can't rely on this.
o.sseq = o.mset.store.GetSeqFromTime(*o.cfg.OptStartTime)
// Here we want to see if we are filtered, and if so possibly close the gap
// to the nearest first given our starting sequence from time. This is so we do
// not force the system to do a linear walk between o.sseq and the real first.
if len(o.subjf) > 0 {
nseq := state.LastSeq
for _, filter := range o.subjf {
// Use first sequence since this is more optimized atm.
ss, err := o.mset.store.FilteredState(state.FirstSeq, filter.subject)
if err != nil {
return err
}
if ss.First >= o.sseq && ss.First < nseq {
nseq = ss.First
}
}
// Skip ahead if possible.
if nseq > o.sseq && nseq < state.LastSeq {
o.sseq = nseq
}
}
} else {
// DeliverNew
o.sseq = state.LastSeq + 1
}
} else {
o.sseq = o.cfg.OptStartSeq
}
if state.FirstSeq == 0 && (o.cfg.Direct || o.cfg.OptStartSeq == 0) {
// If the stream is empty, deliver only new.
// But only if mirroring/sourcing, or start seq is unset, otherwise need to respect provided value.
o.sseq = 1
} else if o.sseq > state.LastSeq && (o.cfg.Direct || o.cfg.OptStartSeq == 0) {
// If selected sequence is in the future, clamp back down.
// But only if mirroring/sourcing, or start seq is unset, otherwise need to respect provided value.
o.sseq = state.LastSeq + 1
} else if o.sseq < state.FirstSeq {
// If the first sequence is further ahead than the starting sequence,
// there are no messages there anymore, so move the sequence up.
o.sseq = state.FirstSeq
}
}
// Always set delivery sequence to 1.
o.dseq = 1
// Set ack delivery floor to delivery-1
o.adflr = o.dseq - 1
// Set ack store floor to store-1
o.asflr = o.sseq - 1
// Set our starting sequence state.
// But only if we're not clustered, if clustered we propose upon becoming leader.
if o.store != nil && o.sseq > 0 && o.cfg.replicas(&o.mset.cfg) == 1 {
if err := o.store.SetStarting(o.sseq - 1); err != nil {
return err
}
}
return nil
}
// Test whether a config represents a durable subscriber.
func isDurableConsumer(config *ConsumerConfig) bool {
return config != nil && config.Durable != _EMPTY_
}
func (o *consumer) isDurable() bool {
return o.cfg.Durable != _EMPTY_
}
// Are we in push mode, delivery subject, etc.
func (o *consumer) isPushMode() bool {
return o.cfg.DeliverSubject != _EMPTY_
}
func (o *consumer) isPullMode() bool {
return o.cfg.DeliverSubject == _EMPTY_
}
// Name returns the name of this consumer.
func (o *consumer) String() string {
o.mu.RLock()
n := o.name
o.mu.RUnlock()
return n
}
func createConsumerName() string {
return getHash(nuid.Next())
}
// Lock should be held.
func (mset *stream) createStableConsumerHash() string {
id := fmt.Sprintf("%s %s", mset.cfg.Name, mset.acc.Name)
if domain := mset.srv.getOpts().JetStreamDomain; domain != _EMPTY_ {
id = fmt.Sprintf("%s %s", id, domain)
}
return getHash(id)
}
// Lock should be held.
func (mset *stream) createSourcingConsumerHash(ssi *StreamSource, sources []*StreamSource) string {
id := mset.createStableConsumerHash()
// If the stream sources contain the same stream at least twice, we use a more strict hash of
// an ID that also contains filter subjects etc. If the stream name is only used once, we can
// support the stable identifier above.
var once bool
for _, src := range sources {
if src.Name == ssi.Name {
if once {
if ssi.iname == _EMPTY_ {
ssi.setIndexName()
}
// Append identifying information of the filter subjects, etc. to make it unique
id = fmt.Sprintf("%s %s", id, ssi.iname)
break
} else {
once = true
}
}
}
return getHash(id)
}
// deleteConsumer will delete the consumer from this stream.
func (mset *stream) deleteConsumer(o *consumer) error {
return o.delete()
}
func (o *consumer) getStream() *stream {
o.mu.RLock()
mset := o.mset
o.mu.RUnlock()
return mset
}
func (o *consumer) streamName() string {
o.mu.RLock()
mset := o.mset
o.mu.RUnlock()
if mset != nil {
return mset.name()
}
return _EMPTY_
}
// Active indicates if this consumer is still active.
func (o *consumer) isActive() bool {
o.mu.RLock()
active := o.active && o.mset != nil
o.mu.RUnlock()
return active
}
// hasNoLocalInterest return true if we have no local interest.
func (o *consumer) hasNoLocalInterest() bool {
o.mu.RLock()
interest := o.acc.sl.HasInterest(o.cfg.DeliverSubject)
o.mu.RUnlock()
return !interest
}
// This is when the underlying stream has been purged.
// sseq is the new first seq for the stream after purge.
// Consumer lock should NOT be held but the parent stream
// lock MUST be held.
func (o *consumer) purge(sseq uint64, slseq uint64, isWider bool) {
// Do not update our state unless we know we are the leader.
if !o.isLeader() {
return
}
// Signals all have been purged for this consumer.
if sseq == 0 && !isWider {
sseq = slseq + 1
}
var store StreamStore
if isWider {
o.mu.RLock()
if o.mset != nil {
store = o.mset.store
}
o.mu.RUnlock()
}
o.mu.Lock()
// Do not go backwards
if o.sseq < sseq {
o.sseq = sseq
}
if o.asflr < sseq {
o.asflr = sseq - 1
// We need to remove those no longer relevant from pending.
for seq, p := range o.pending {
if seq <= o.asflr {
if p.Sequence > o.adflr {
o.adflr = p.Sequence
if o.adflr > o.dseq {
o.dseq = o.adflr
}
}
delete(o.pending, seq)
delete(o.rdc, seq)
// rdq handled below.
}
if isWider && store != nil {
// Our filtered subject, which could be all, is wider than the underlying purge.
// We need to check if the pending items left are still valid.
var smv StoreMsg
if _, err := store.LoadMsg(seq, &smv); err == errDeletedMsg || err == ErrStoreMsgNotFound {
if p.Sequence > o.adflr {
o.adflr = p.Sequence
if o.adflr > o.dseq {
o.dseq = o.adflr
}
}
delete(o.pending, seq)
delete(o.rdc, seq)
}
}
}
}
// This means we can reset everything at this point.
if len(o.pending) == 0 {
o.pending, o.rdc = nil, nil
o.adflr, o.asflr = o.dseq-1, o.sseq-1
}
// We need to remove all those being queued for redelivery under o.rdq
if len(o.rdq) > 0 {
rdq := o.rdq
o.rdq = nil
o.rdqi.Empty()
for _, sseq := range rdq {
if sseq >= o.sseq {
o.addToRedeliverQueue(sseq)
}
}
}
// Grab some info in case of error below.
s, acc, mset, name := o.srv, o.acc, o.mset, o.name
o.mu.Unlock()
if err := o.writeStoreState(); err != nil && s != nil && mset != nil {
s.Warnf("Consumer '%s > %s > %s' error on write store state from purge: %v", acc, mset.nameLocked(false), name, err)
}
}
func stopAndClearTimer(tp **time.Timer) {
if *tp == nil {
return
}
// Will get drained in normal course, do not try to
// drain here.
(*tp).Stop()
*tp = nil
}
// Stop will shutdown the consumer for the associated stream.
func (o *consumer) stop() error {
return o.stopWithFlags(false, false, true, false)
}
func (o *consumer) deleteWithoutAdvisory() error {
return o.stopWithFlags(true, false, true, false)
}
// Delete will delete the consumer for the associated stream and send advisories.
func (o *consumer) delete() error {
return o.stopWithFlags(true, false, true, true)
}
// To test for closed state.
func (o *consumer) isClosed() bool {
o.mu.RLock()
defer o.mu.RUnlock()
return o.closed
}
func (o *consumer) stopWithFlags(dflag, sdflag, doSignal, advisory bool) error {
// If dflag is true determine if we are still assigned.
var isAssigned bool
if dflag {
o.mu.RLock()
acc, stream, consumer := o.acc, o.stream, o.name
isClustered := o.js != nil && o.js.isClustered()
o.mu.RUnlock()
if isClustered {
// Grab jsa to check assignment.
var jsa *jsAccount
if acc != nil {
// Need lock here to avoid data race.
acc.mu.RLock()
jsa = acc.js
acc.mu.RUnlock()
}
if jsa != nil {
isAssigned = jsa.consumerAssigned(stream, consumer)
}
}
}
o.mu.Lock()
if o.closed {
o.mu.Unlock()
return nil
}
o.closed = true
// Signal to the monitor loop.
// Can't use only qch here, since that's used when stepping down as a leader.
if o.mqch != nil {
close(o.mqch)
o.mqch = nil
}
// Check if we are the leader and are being deleted (as a node).
if dflag && o.isLeader() {
// If we are clustered and node leader (probable from above), stepdown.
if node := o.node; node != nil {
node.StepDown()
}
// dflag does not necessarily mean that the consumer is being deleted,
// just that the consumer node is being removed from this peer, so we
// send delete advisories only if we are no longer assigned at the meta layer,
// or we are not clustered.
if !isAssigned && advisory {
o.sendDeleteAdvisoryLocked()
}
if o.isPullMode() {
// Release any pending.
o.releaseAnyPendingRequests(isAssigned)
}
}
if o.qch != nil {
close(o.qch)
o.qch = nil
}
a := o.acc
store := o.store
mset := o.mset
o.mset = nil
o.active = false
o.unsubscribe(o.ackSubOld)
o.unsubscribe(o.ackSub)
o.unsubscribe(o.reqSub)
o.unsubscribe(o.resetSub)
o.unsubscribe(o.fcSubOld)
o.unsubscribe(o.fcSub)
o.ackSubOld = nil
o.ackSub = nil
o.reqSub = nil
o.resetSub = nil
o.fcSubOld = nil
o.fcSub = nil
if o.infoSub != nil {
o.srv.sysUnsubscribe(o.infoSub)
o.infoSub = nil
}
c := o.client
o.client = nil
sysc := o.sysc
o.sysc = nil
o.stopAndClearPtmr()
stopAndClearTimer(&o.dtmr)
stopAndClearTimer(&o.gwdtmr)
delivery := o.cfg.DeliverSubject
o.waiting = nil
// Break us out of the readLoop.
if doSignal {
o.signalNewMessages()
}
n := o.node
qgroup := o.cfg.DeliverGroup
o.ackMsgs.unregister()
if o.nextMsgReqs != nil {
o.nextMsgReqs.unregister()
}
// For cleaning up the node assignment.
var ca *consumerAssignment
if dflag {
ca = o.ca
}
js := o.js
o.mu.Unlock()
if c != nil {
c.closeConnection(ClientClosed)
}
if sysc != nil {
sysc.closeConnection(ClientClosed)
}
if delivery != _EMPTY_ {
a.sl.clearNotification(delivery, qgroup, o.inch)
}
var rp RetentionPolicy
if mset != nil {
mset.mu.Lock()
mset.removeConsumer(o)
// No need for cfgMu's lock since mset.mu.Lock superseeds it.
rp = mset.cfg.Retention
mset.mu.Unlock()
}
// Cleanup messages that lost interest.
if dflag && rp == InterestPolicy {
o.cleanupNoInterestMessages(mset, true)
}
// Cluster cleanup.
if n != nil {
if dflag {
n.Delete()
} else {
n.Stop()
}
}
if ca != nil {
js.mu.Lock()
if ca.Group != nil {
ca.Group.node = nil
}
js.mu.Unlock()
}
// Clean up our store.
var err error
if store != nil {
if dflag {
if sdflag {
err = store.StreamDelete()
} else {
err = store.Delete()
}
} else {
err = store.Stop()
}
} else if dflag {
// If there's no store (for example, when it's offline), manually delete the directories.
o.mu.RLock()
stream, consumer := o.stream, o.name
o.mu.RUnlock()
accDir := filepath.Join(js.config.StoreDir, a.GetName())
consumersDir := filepath.Join(accDir, streamsDir, stream, consumerDir)
os.RemoveAll(filepath.Join(consumersDir, consumer))
}
return err
}
// We need to optionally remove all messages since we are interest based retention.
// We will do this consistently on all replicas. Note that if in clustered mode the non-leader
// consumers will need to restore state first.
// ignoreInterest marks whether the consumer should be ignored when determining interest.
// No lock held on entry.
func (o *consumer) cleanupNoInterestMessages(mset *stream, ignoreInterest bool) {
o.mu.Lock()
if !o.isLeader() {
o.readStoredState()
}
start := o.asflr
o.mu.Unlock()
// Make sure we start at worst with first sequence in the stream.
state := mset.state()
if start < state.FirstSeq {
start = state.FirstSeq
}
stop := state.LastSeq
// Consumer's interests are ignored by default. If we should not ignore interest, unset.
co := o
if !ignoreInterest {
co = nil
}
var rmseqs []uint64
mset.mu.RLock()
// If over this amount of messages to check, optimistically call to checkInterestState().
// It will not always do the right thing in removing messages that lost interest, but ensures
// we don't degrade performance by doing a linear scan through the whole stream.
// Messages might need to expire based on limits to be cleaned up.
// TODO(dlc) - Better way?
const bailThresh = 100_000
// Check if we would be spending too much time here and defer to separate go routine.
if len(mset.consumers) == 0 {
mset.mu.RUnlock()
mset.mu.Lock()
defer mset.mu.Unlock()
mset.store.Purge()
var state StreamState
mset.store.FastState(&state)
mset.lseq = state.LastSeq
// Also make sure we clear any pending acks.
mset.clearAllPreAcksBelowFloor(state.FirstSeq)
return
} else if stop-start > bailThresh {
mset.mu.RUnlock()
go mset.checkInterestState()
return
}
mset.mu.RUnlock()
mset.mu.Lock()
for seq := start; seq <= stop; seq++ {
if mset.noInterest(seq, co) {
rmseqs = append(rmseqs, seq)
}
}
mset.mu.Unlock()
// These can be removed.
for _, seq := range rmseqs {
mset.store.RemoveMsg(seq)
}
}
// Check that we do not form a cycle by delivering to a delivery subject
// that is part of the interest group.
func deliveryFormsCycle(cfg *StreamConfig, deliverySubject string) bool {
for _, subject := range cfg.Subjects {
if subjectIsSubsetMatch(deliverySubject, subject) {
return true
}
}
return false
}
// switchToEphemeral is called on startup when recovering ephemerals.
func (o *consumer) switchToEphemeral() {
o.mu.Lock()
o.cfg.Durable = _EMPTY_
store, ok := o.store.(*consumerFileStore)
interest := o.acc.sl.HasInterest(o.cfg.DeliverSubject)
// Setup dthresh.
o.updateInactiveThreshold(&o.cfg)
o.updatePauseState(&o.cfg)
o.mu.Unlock()
// Update interest
o.updateDeliveryInterest(interest)
// Write out new config
if ok {
store.updateConfig(o.cfg)
}
}
// RequestNextMsgSubject returns the subject to request the next message when in pull or worker mode.
// Returns empty otherwise.
func (o *consumer) requestNextMsgSubject() string {
return o.nextMsgSubj
}
func (o *consumer) decStreamPending(sseq uint64, subj string) {
o.mu.Lock()
// Update our cached num pending only if we think deliverMsg has not done so.
if sseq >= o.sseq && o.isFilteredMatch(subj) {
o.npc--
}
// Check if this message was pending.
p, wasPending := o.pending[sseq]
var rdc uint64
if wasPending {
rdc = o.deliveryCount(sseq)
}
o.mu.Unlock()
// If it was pending process it like an ack.
if wasPending {
// We could have the lock for the stream so do this in a go routine.
// TODO(dlc) - We should do this with ipq vs naked go routines.
go o.processTerm(sseq, p.Sequence, rdc, ackTermUnackedLimitsReason, _EMPTY_)
}
}
func (o *consumer) account() *Account {
o.mu.RLock()
a := o.acc
o.mu.RUnlock()
return a
}
// Creates a sublist for consumer.
// All subjects share the same callback.
func (o *consumer) signalSubs() []string {
o.mu.Lock()
defer o.mu.Unlock()
if o.sigSubs != nil {
return o.sigSubs
}
if len(o.subjf) == 0 {
subs := []string{fwcs}
o.sigSubs = subs
return subs
}
subs := make([]string, 0, len(o.subjf))
for _, filter := range o.subjf {
subs = append(subs, filter.subject)
}
o.sigSubs = subs
return subs
}
// This is what will be called when our parent stream wants to kick us regarding a new message.
// We know that this subject matches us by how the parent handles registering us with the signaling sublist,
// but we must check if we are leader.
// We do need the sequence of the message however and we use the msg as the encoded seq.
func (o *consumer) processStreamSignal(seq uint64) {
// We can get called here now when not leader, so bail fast
// and without acquiring any locks.
if !o.leader.Load() {
return
}
o.mu.Lock()
defer o.mu.Unlock()
if o.mset == nil {
return
}
if seq > o.npf {
o.npc++
}
if seq < o.sseq {
return
}
if o.isPushMode() && o.active || o.isPullMode() && !o.waiting.isEmpty() {
o.signalNewMessages()
}
}
// Used to compare if two multiple filtered subject lists are equal.
func subjectSliceEqual(slice1 []string, slice2 []string) bool {
if len(slice1) != len(slice2) {
return false
}
set2 := make(map[string]struct{}, len(slice2))
for _, val := range slice2 {
set2[val] = struct{}{}
}
for _, val := range slice1 {
if _, ok := set2[val]; !ok {
return false
}
}
return true
}
// Utility for simpler if conditions in Consumer config checks.
// In future iteration, we can immediately create `o.subjf` and
// use it to validate things.
func gatherSubjectFilters(filter string, filters []string) []string {
if filter != _EMPTY_ {
filters = append(filters, filter)
}
// list of filters should never contain non-empty filter.
return filters
}
// shouldStartMonitor will return true if we should start a monitor
// goroutine or will return false if one is already running.
func (o *consumer) shouldStartMonitor() bool {
o.mu.Lock()
defer o.mu.Unlock()
if o.inMonitor {
return false
}
o.monitorWg.Add(1)
o.inMonitor = true
return true
}
// Clear the monitor running state. The monitor goroutine should
// call this in a defer to clean up on exit.
func (o *consumer) clearMonitorRunning() {
o.mu.Lock()
defer o.mu.Unlock()
if o.inMonitor {
o.monitorWg.Done()
o.inMonitor = false
}
}
// Test whether we are in the monitor routine.
func (o *consumer) isMonitorRunning() bool {
o.mu.RLock()
defer o.mu.RUnlock()
return o.inMonitor
}
// If we detect that our ackfloor is higher than the stream's last sequence, return this error.
var errAckFloorHigherThanLastSeq = errors.New("consumer ack floor is higher than streams last sequence")
var errAckFloorInvalid = errors.New("consumer ack floor is invalid")
// If we are a consumer of an interest or workqueue policy stream, process that state and make sure consistent.
func (o *consumer) checkStateForInterestStream(ss *StreamState) error {
o.mu.RLock()
// See if we need to process this update if our parent stream is not a limits policy stream.
mset := o.mset
shouldProcessState := mset != nil && o.retention != LimitsPolicy
if o.closed || !shouldProcessState || o.store == nil || ss == nil {
o.mu.RUnlock()
return nil
}
store := mset.store
state, err := o.store.State()
filters, subjf, filter := o.filters, o.subjf, _EMPTY_
var wc bool
if filters == nil && subjf != nil {
filter, wc = subjf[0].subject, subjf[0].hasWildcard
}
chkfloor := o.chkflr
o.mu.RUnlock()
if err != nil {
return err
}
asflr := state.AckFloor.Stream
// Protect ourselves against rolling backwards.
if asflr&(1<<63) != 0 {
return errAckFloorInvalid
}
dflr := asflr
if len(state.Pending) > 0 && state.Delivered.Stream > dflr {
dflr = state.Delivered.Stream
}
// Check if the underlying stream's last sequence is less than our floor.
// This can happen if the stream has been reset and has not caught up yet.
if asflr > ss.LastSeq {
return errAckFloorHigherThanLastSeq
}
var smv StoreMsg
var seq, nseq uint64
// Start at first stream seq or a previous check floor, whichever is higher.
// Note this will really help for interest retention, with WQ the loadNextMsg
// gets us a long way already since it will skip deleted msgs not for our filter.
fseq := ss.FirstSeq
if chkfloor > fseq {
fseq = chkfloor
}
var retryAsflr uint64
for seq = fseq; dflr > 0 && seq <= dflr; seq++ {
if filters != nil {
_, nseq, err = store.LoadNextMsgMulti(filters, seq, &smv)
} else {
_, nseq, err = store.LoadNextMsg(filter, wc, seq, &smv)
}
// if we advanced sequence update our seq. This can be on no error and EOF.
if nseq > seq {
seq = nseq
}
if err == nil {
// Only ack though if no error and seq <= ack floor.
if seq <= asflr {
didRemove := mset.ackMsg(o, seq)
// Removing the message could fail. For example if clustered since we need to propose it.
// Overwrite retry floor (only the first time) to allow us to check next time if the removal was successful.
if didRemove && retryAsflr == 0 {
retryAsflr = seq
}
} else if seq <= dflr {
// Store the first entry above our ack floor, so we don't need to look it up again on retryAsflr=0.
if retryAsflr == 0 {
retryAsflr = seq
}
// If we have pending, we will need to walk through to delivered in case we missed any of those acks as well.
if _, ok := state.Pending[seq]; !ok {
// The filters are already taken into account,
mset.ackMsg(o, seq)
}
}
} else if err == ErrStoreEOF {
break
}
}
// If retry floor was not overwritten, set to ack floor+1, we don't need to account for any retries below it.
// However, our ack floor may be lower than the next message we can receive, so we correct it upward if needed.
if retryAsflr == 0 {
if filters != nil {
_, nseq, err = store.LoadNextMsgMulti(filters, asflr+1, &smv)
} else {
_, nseq, err = store.LoadNextMsg(filter, wc, asflr+1, &smv)
}
if err == nil {
retryAsflr = max(asflr+1, nseq)
} else if err == ErrStoreEOF {
retryAsflr = ss.LastSeq + 1
}
}
o.mu.Lock()
// Update our check floor.
// Check floor must never be greater than ack floor+1, otherwise subsequent calls to this function would skip work.
if retryAsflr > o.chkflr {
o.chkflr = retryAsflr
}
o.mu.Unlock()
return nil
}
func (o *consumer) resetPtmr(delay time.Duration) {
// A delay of zero means it should be stopped.
if delay == 0 {
o.stopAndClearPtmr()
return
}
if o.ptmr == nil {
o.ptmr = time.AfterFunc(delay, o.checkPending)
} else {
o.ptmr.Reset(delay)
}
o.ptmrEnd = time.Now().Add(delay)
}
func (o *consumer) stopAndClearPtmr() {
// If the end time is unset, short-circuit since the timer will already be stopped.
if o.ptmrEnd.IsZero() {
return
}
stopAndClearTimer(&o.ptmr)
o.ptmrEnd = time.Time{}
}
func (o *consumer) resetPendingDeliveries() {
for _, pmsg := range o.pendingDeliveries {
pmsg.returnToPool()
}
o.pendingDeliveries = nil
for _, wd := range o.waitingDeliveries {
wd.recycle()
}
o.waitingDeliveries = nil
}