Files
upstream-nats-server/server/feature_flags.go
upstream-mirror 709385cbaa
Some checks failed
NATS Server Tests / Lint (push) Failing after 3s
NATS Server Tests / Build (Latest Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2022) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2025) (push) Has been skipped
NATS Server Tests / Test Stores (push) Has been skipped
NATS Server Tests / Test JetStream (push) Has been skipped
NATS Server Tests / Test Raft (push) Has been skipped
NATS Server Tests / Test JetStream Consumers (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 1 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 2 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 3 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 4 (push) Has been skipped
NATS Server Tests / Test JetStream Supercluster (push) Has been skipped
NATS Server Tests / Test No-Race 1 (push) Has been skipped
NATS Server Tests / Test No-Race 2 (push) Has been skipped
NATS Server Tests / Test MQTT (push) Has been skipped
NATS Server Tests / Test Message Tracing (push) Has been skipped
NATS Server Tests / Test JWT (push) Has been skipped
NATS Server Tests / Test Remaining Server Tests (push) Has been skipped
NATS Server Tests / Test Other Packages (push) Has been skipped
Vulnerability Scan / govulncheck (push) Failing after 12m2s
snapshot of https://github.com/nats-io/nats-server.git (main, 2026-09-15; blobs > 131072B omitted)
2026-09-15 20:16:01 +05:00

158 lines
5.3 KiB
Go

// Copyright 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 (
"maps"
"slices"
"strings"
)
const (
FeatureFlagJsAckFormatV2 = "js_ack_fc_v2"
FeatureFlagJsAPIReplyFormatV2 = "js_api_reply_v2"
FeatureFlagJsRaftDeleteRange = "js_raft_delete_range"
FeatureFlagJsSnapshotSources = "js_snapshot_sources"
)
var featureFlags = map[string]bool{
// Use v2 format for `$JS.ACK.>` and `$JS.FC.>`.
// - Introduced: 2.14.0, both v1 and v2 supported, only using v1.
// - Enabled: TBD, both supported, v2 becomes the default.
//
// - v1: $JS.ACK.<stream name>.<consumer name>.<num delivered>.<stream sequence>.<consumer sequence>.<timestamp>.<num pending>
// - v2: $JS.ACK.<domain>.<account hash>.<stream name>.<consumer name>.<num delivered>.<stream sequence>.<consumer sequence>.<timestamp>.<num pending>
// See also: https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-15.md#jsack
FeatureFlagJsAckFormatV2: false,
// Use a scoped v2 format for replies to internally-issued JetStream API
// requests. This permits a leafnode to authorize only the replies for a
// specific remote stream/consumer instead of every $JSC.R reply in an
// account.
//
// - v1: $JSC.R.<uid>
// - v2: $JSC.R.<domain>.<account hash>.<stream name>.<consumer name>.<uid>
//
// The reply subject is opaque to the remote API server, so this is safe to
// enable independently on the server issuing the request.
FeatureFlagJsAPIReplyFormatV2: false,
// Propose delete range gaps as a single `deleteRangeOp` Raft append entry
// instead of one entry per deleted sequence. Dramatically reduces Raft cost
// on mirrors whose origin has a large number of interior deletes.
// - Introduced: 2.14.0, apply-side always supports receiving `deleteRangeOp`.
// - Enabled: 2.15.0, when upgrading all servers should be 2.14.0+
// - To be removed in 2.16.0
//
// WARNING: Only enable once every peer in the cluster is on a version that
// supports receiving `deleteRangeOp`. Older peers panic on apply of an
// unknown stream entry operation.
FeatureFlagJsRaftDeleteRange: true,
// Include the stream's sourcing state in the replicated stream snapshot.
// Unlike other per-message derived state, it outlives the messages it was
// collected from, so a replica that caught up after those messages were
// removed can't derive it locally and would resume sourcing from an earlier
// position once it becomes leader.
// - Introduced: 2.15.0, both encoding versions are accepted, only emitting v1.
// - Enabled: TBD, once all supported versions accept v2.
//
// WARNING: Only enable once every peer in the cluster is on a version that
// accepts the v2 encoding. Older peers reject the snapshot outright.
FeatureFlagJsSnapshotSources: false,
}
// getFeatureFlag is used to retrieve either the default or overwritten value for a feature flag.
// The user's value takes precedence over the system's default. However, if the flag doesn't exist, it's disabled.
// The *Options returned by Server.getOpts() is treated as immutable, mutations go through setOpts,
// so no lock is required on the map read here.
func (o *Options) getFeatureFlag(k string) bool {
defaultValue, ok := featureFlags[k]
if !ok {
return false // Not supported.
}
if userValue, ok := o.FeatureFlags[k]; ok {
return userValue
}
return defaultValue
}
// getMergedFeatureFlags returns a merged map of feature flags, with the user's values taking precedence.
func (o *Options) getMergedFeatureFlags() map[string]bool {
merged := make(map[string]bool)
for k, v := range featureFlags {
merged[k] = v
}
for k, v := range o.FeatureFlags {
if _, ok := featureFlags[k]; !ok {
continue
}
merged[k] = v
}
return merged
}
// printFeatureFlags logs the currently used feature flags on server startup.
func (s *Server) printFeatureFlags(o *Options) {
if len(o.FeatureFlags) == 0 {
return
}
keys := slices.Sorted(maps.Keys(o.FeatureFlags))
var (
configured strings.Builder
unsupported strings.Builder
)
for _, k := range keys {
// Unsupported
defaultValue, ok := featureFlags[k]
if !ok {
if unsupported.Len() > 0 {
unsupported.WriteString(", ")
}
unsupported.WriteString(k)
continue
}
v := o.FeatureFlags[k]
if configured.Len() > 0 {
configured.WriteString(", ")
}
configured.WriteString(k)
configured.WriteString(" (")
if defaultValue {
if v {
configured.WriteString("enabled")
} else {
configured.WriteString("opt-out")
}
} else if v {
configured.WriteString("opt-in")
} else {
configured.WriteString("disabled")
}
configured.WriteString(")")
}
if configured.Len() == 0 {
configured.WriteString("none")
}
s.Noticef(" Feature flags:")
s.Noticef(" Configured: %s", configured.String())
if unsupported.Len() > 0 {
s.Noticef(" Unsupported: %s", unsupported.String())
}
}