Cherry-picks for 2.14.1-RC.1 - #8163
Conversation
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Drop NConcurrent from 100 to 25. With 100 concurrent goroutines hammering AddConsumer, the meta-Raft saturates and individual API requests exceed the 10s default JetStream timeout, surfacing as "context deadline exceeded" failures. At 25 the cluster keeps up. The test still detects the original PR #5600 leak: reverting the clear(c.in.pacache) fix surfaces ~199 MB heap growth, well over the 100 MB threshold. Signed-off-by: Mikael Sundberg <mikael@wingbits.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
… on cleanup Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Ensure snapshot file parsing only accepts `snap.<term>.<index>`. This is to avoid the case where a .tmp snapshot file is selected after a crash. If the crash happend right after the temporary file creation and before the server managed to write out the full snapsho, then a subsequent recovery would fail due to finding a corrupt snapshot. Signed-off-by: Daniele Sciascia <daniele@nats.io>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
…her connection types Signed-off-by: Neil Twigg <neil@nats.io>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Neil Twigg <neil@nats.io>
Signed-off-by: Yongun Seong <nevivurn@nevi.dev>
Signed-off-by: Yongun Seong <nevivurn@nevi.dev>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
… both sub and pub Signed-off-by: Neil Twigg <neil@nats.io>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
hasInterest would ignored emptyResult from the sublist cache. If the cache contains an empty result we can avoid traversing the trie unnecessarily. Signed-off-by: Daniele Sciascia <daniele@nats.io>
Signed-off-by: Neil Twigg <neil@nats.io>
Signed-off-by: Neil Twigg <neil@nats.io>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
updateLeafNodesEx iterates over a list of leafnodes, taking a locking one leafnode connection at every iteration. Change the loop so that the starting point is randomized, avoiding concurrent callers to lock leafs in the same order Signed-off-by: Daniele Sciascia <daniele@nats.io>
Change the client lock to an RWMutex and create a fast path that uses read locks for leafnode permission checks. This reduces contention on the hub side leaf client locks between route/leaf interest propagation and inbound leaf message permission checks. Create canSubscribeInternal for read-lock checks and keep canSubscribe responsible for lazy delivery deny-filter initialization. Signed-off-by: Daniele Sciascia <daniele@nats.io>
canSubscribe only requires a client read lock, however concurrent callers can still contend on the subscribe permission sublist cache. To avoid that, disable the caches on those sublists. Signed-off-by: Daniele Sciascia <daniele@nats.io>
Signed-off-by: Alex Bozhenko <alexbozhenko@gmail.com>
Signed-off-by: Alex Bozhenko <alexbozhenko@gmail.com>
Signed-off-by: Alex Bozhenko <alexbozhenko@gmail.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
|
Claude finished @neilalexander's task in 6m 52s —— View job PR Review: Cherry-picks for 2.14.1-RC.1
Overall AssessmentThis is a high-quality release candidate backport with 37 cherry-picked fixes. The changes are well-tested and accompanied by focused regression tests. No blocking issues found. A few observations noted as inline comments. Critical Bug Fixes — Verified CorrectRaft / NRG
JetStream Consumer
Lock Ordering
Mirror / Source
Filestore
Other Correctness
Security Fixes
Performance / Lock Contention
One Observation (not blocking)The |
|
|
||
| var entries []*Entry | ||
| for seq := start; seq <= end; seq++ { | ||
| entries = append(entries, newEntry(EntryNormal, encodeStreamMsg(_EMPTY_, _EMPTY_, nil, nil, seq-1, 0, false))) |
There was a problem hiding this comment.
In the ProposeMulti loop when batching > 10,000 entries, if the first call succeeds but a subsequent ProposeMulti (or the trailing one after the loop) returns an error, the function returns that error — but some entries have already been proposed to Raft. The caller in processInboundMirrorMsg will call retryMirrorConsumer(), which should recover, but this could leave a short window where the Raft log has partial skip entries. This is a pre-existing structural limitation now made visible by error propagation; worth a comment noting the partial-proposal possibility.
| o.updateDelivered(p.Sequence, seq, dc+1, p.Timestamp) | ||
| o.moveAckFloor(p.Sequence, seq) | ||
| } | ||
| // Ensure redelivered state is set, if not already. |
There was a problem hiding this comment.
The dc+1 here is correct: dc = o.deliveryCount(seq) returns the count before this delivery attempt, so dc+1 is the actual post-delivery count. The companion fix in getNextMsg (below) does NOT add 1 because dc is incremented before that site (dc++ is called earlier in getNextMsg). This asymmetry is intentional but worth a brief code comment to prevent future confusion.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d75dc3b36
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| delete(o.rdc, sseq) | ||
| // Pass 0 as the delivered sequence to only remove the redelivered state. | ||
| o.updateAcks(0, sseq, _EMPTY_) |
There was a problem hiding this comment.
Avoid replicating redelivery cleanup as an ack
In clustered consumers (o.node != nil), this updateAcks(0, sseq, ...) is encoded as a normal updateAcksOp; followers apply it via processReplicatedAck, which always calls mset.ackMsg(o, sseq) for non-LimitsPolicy retention after store.UpdateAcks. So when a deleted stream message only needs its stale redelivery entry cleared, Interest/WorkQueue followers can treat it as a real ack and remove/advance retention state (and AckAll/FlowControl may ack a whole range from the stored ack floor). The dseq == 0 sentinel needs a clustered apply path that only removes Redelivered, or processReplicatedAck must skip retention acking for that sentinel.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| // If allow_responses is not configured, or there is no tracked reply for | ||
| // this subject, the answer is "denied" and we can return it while still |
There was a problem hiding this comment.
There is a TOCTOU window between c.mu.RUnlock() and c.mu.Lock() (a few lines below) where c.replies[replySubject] could be deleted. However, this is safe by design: if the entry disappears between the two lock acquisitions, responseAllowed() under the write lock will return false, which is the correct conservative answer. No security or liveness issue arises from this gap.
| @@ -612,7 +614,7 @@ RESET: | |||
|
|
|||
There was a problem hiding this comment.
Good fix. pm.hdr[:len(pm.hdr):len(pm.hdr)] limits the slice capacity to its current length before append, preventing append from writing the body bytes into the caller's backing array when there is spare capacity. Without this, callers with cap(hdr) > len(hdr) (which is the common case when building headers with append/genHeader) would see their buffer silently mutated after sendInternalAccountMsgWithReply returned.
| @@ -1610,6 +1619,9 @@ func termAndIndexFromSnapFile(sn string) (term, index uint64, err error) { | |||
| if n, err := fmt.Sscanf(fn, snapFileT, &term, &index); err != nil || n != 2 { | |||
There was a problem hiding this comment.
This round-trip validation cleanly rejects any filename that Sscanf parsed but that doesn't reconstruct to the canonical form — e.g. files with a .tmp suffix, leading zeros, or other non-standard characters. Elegant approach.
| @@ -4271,6 +4303,9 @@ func (n *raft) processAppendEntry(ae *appendEntry, sub *subscription) { | |||
| n.Unlock() | |||
There was a problem hiding this comment.
Critical ordering fix: n.pindex/pterm/commit are now set only after installSnapshot succeeds. Previously, setting them before the call and then returning early on failure left the node with inconsistent committed state (the node believed it was at ae.pindex but the WAL hadn't been updated). The snapshot fields now reference ae.pindex/ae.pterm directly, which is equivalent but avoids the pre-mutation issue.
…e clear Signed-off-by: Neil Twigg <neil@nats.io>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: de471aef23
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| delete(o.state.Redelivered, sseq) | ||
|
|
||
| // On restarts the old leader may get a replay from the raft logs that are old. | ||
| if dseq <= o.state.AckFloor.Consumer { | ||
| return nil |
There was a problem hiding this comment.
Flush redelivery-only ack state before returning
When UpdateAcks is used with the new dseq == 0 sentinel from decStreamPending, this deletes state.Redelivered and then immediately returns because 0 <= AckFloor.Consumer, so the file-backed consumer never calls kickFlusher(). In an R1/file-store consumer where a redelivered message is later deleted or evicted while not pending, the in-memory cleanup works until restart, but the stale Redelivered entry is not persisted and is loaded again from disk. Track whether this delete changed state and schedule a flush before either early return.
Useful? React with 👍 / 👎.
Includes the following:
errMQTTUnsupportedCharactersfor control characters on both pub and sub #8112