Skip to content

Keep the 1.21 query range and HA failover timeout settings working - #7919

Merged
friedrichg merged 4 commits into
cortexproject:masterfrom
CharlieTLe:restore-deprecated-query-range-and-ha-failover-config
Nov 6, 2026
Merged

friedrichg merged 4 commits into
cortexproject:masterfrom
CharlieTLe:restore-deprecated-query-range-and-ha-failover-config

Conversation

@CharlieTLe

@CharlieTLe CharlieTLe commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

Follows review on #7918. That PR documented that 1.22 breaks 1.21 configs that set the query range or HA failover timeout settings in their old location. @friedrichg asked to keep the old stanzas working instead, since these are long-standing settings. This PR does that. It targets master, and I'll backport it to release-1.22 before v1.22.0.

What breaks on 1.22 today

Change

  • The old YAML fields and the old -querier.* flags come back as hidden, deprecated settings (doc:"hidden"), scheduled for removal in v1.24.0. distributor.ha_tracker.ha_tracker_failover_timeout is YAML-only, because -distributor.ha-tracker.failover-timeout already sets the limit.
  • Config.Validate copies an old value that is set into its limit. This happens before the limits are validated and before they become the runtime-config defaults (SetDefaultLimitsForYAMLUnmarshalling), so tenant overrides inherit it, and the old values go through the same ValidateQueryLimits checks as the new ones.
  • Precedence follows 1.21, where a flag on the command line overrides the YAML file. The old and new names are treated as two spellings of one setting:
    • A flag on the command line wins over the YAML file, whichever name each uses. -distributor.ha-tracker.failover-timeout=30s beats a deprecated distributor.ha_tracker.ha_tracker_failover_timeout: 45s, even though 30s is the default.
    • An old flag on the command line wins over a new key in the YAML file.
    • If the old and the new name are both set in the same place, both in the YAML file or both on the command line, they must agree, otherwise startup fails with limits.query_store_after (6h0m0s) and the deprecated querier.query_store_after (12h0m0s) are both set: remove querier.query_store_after, or the same with the flag names.
    • To know where each value came from, LoadConfig records the YAML keys that are set (SetYAMLKeys, next to the existing SetYAMLRootNodeStates), and main records the flags passed on the command line (SetCommandLineFlags). A Config built in code has neither, and counts a setting as set when it is not at its default value.
  • The deprecated fields are omitempty, so -print.config and /config show them only when they are set.
  • Each old setting that is used logs running with DEPRECATED config querier.query_store_after, use limits.query_store_after instead, it will be removed in v1.24.0 and increments deprecated_flags_inuse_total, as Formally deprecate -blocks-storage.tsdb.max-exemplars #7793 does for -blocks-storage.tsdb.max-exemplars. This happens in cortex.New, not Validate: main calls Validate before util_log.InitLogger, while util_log.Logger is still a no-op logger, so a warning logged there is dropped.
  • The -limits.query-ingesters-within help text said "less than or equal to close-idle-tsdb-timeout", while making query ingesters config per tenant #7323 rejects equal. It now says "less than". config-file-reference.md and the JSON schema are regenerated; that string is their only change.
  • CHANGELOG: the Make query ingester within a per tenant configuration #7160/making query ingesters config per tenant #7323 and feat(ha): Add per-tenant configurable failover timeout #7481 entries say the old names are deprecated rather than removed, and a new entry describes the query_ingesters_within < close_idle_tsdb_timeout startup check, which was approved on Backport #7919 to release-1.22 #7918 as a correctness fix.

Testing

  • TestConfigValidation_DeprecatedLimits covers the old YAML, the old flags, the HA field, old and new equal, old and new different (both kinds), and an old pair that fails ValidateQueryLimits.
  • go test ./pkg/cortex/ ./pkg/ha/ ./pkg/querier/ and go vet pass. Regenerated docs and schema match.
  • TestFlagParsing runs main() with the HA case from review: deprecated YAML field 45s plus -distributor.ha-tracker.failover-timeout=30s now gives 30s. Reverting to the value-based rule fails it, along with the three command-line precedence unit tests.
  • Compared -print.config against a v1.21.0 build, with an old YAML field 45s and the flag unset, =30s or =1m: 45s, 30s, 1m on both. With a YAML querier.query_store_after: 12h and the flag unset, -querier.query-store-after=0 or -limits.query-store-after=0: 12h, 0, 0, matching 1.21.
  • Built ./cmd/cortex and started it with the three 1.21-style configs that fail on v1.22.0-rc.3: each now passes config validation and logs the deprecation warning. The equal close_idle_tsdb_timeout config is still rejected.
  • golangci-lint v2.13.1, the version in build-image/Dockerfile, built with Go 1.27: 0 issues.

cortexproject#7160/cortexproject#7323 moved query_ingesters_within, query_store_after and
shuffle_sharding_ingesters_lookback_period from querier to limits, and
renamed their flags from -querier.* to -limits.*. cortexproject#7481 moved the
ha_tracker_failover_timeout YAML field from distributor.ha_tracker to
limits. Neither left the old names in place, so a 1.21 config that uses
them fails at startup on 1.22.

Restore the old YAML fields and flags as hidden, deprecated settings
that are scheduled for removal in v1.24.0. Config.Validate copies a set
old value into its limit before the limits are validated and before they
become the runtime config defaults, so tenant overrides inherit it. When
the old and the new name are both set to different values, startup fails
rather than picking one. cortex.New logs a warning and increments
deprecated_flags_inuse_total for each one used: Validate runs before the
logger is initialised, so a warning logged there is lost.

Also correct the -limits.query-ingesters-within help text, which said
"less than or equal to" close-idle-tsdb-timeout while cortexproject#7323 rejects equal,
and spell out that new startup check in the CHANGELOG.

Signed-off-by: Charlie Le <charlie_le@apple.com>
@CharlieTLe
CharlieTLe requested a review from a team as a code owner November 5, 2026 22:20
@CharlieTLe
CharlieTLe requested a review from friedrichg November 5, 2026 22:20
Signed-off-by: Charlie Le <charlie_le@apple.com>
staticcheck (SA1019) flags every read of a field whose doc comment starts
with Deprecated:, and cortex.Config reads these to apply them. Other
config fields that Cortex still reads, such as MaxExemplars, do not use
the marker either.

Signed-off-by: Charlie Le <charlie_le@apple.com>
Comment thread CHANGELOG.md Outdated
* [CHANGE] Ingester: Formally deprecate `-blocks-storage.tsdb.max-exemplars`, scheduled for removal in v1.24.0. Use the per-tenant `max_exemplars` limit instead. The flag still works as the global fallback when `max_exemplars` is 0, but setting it now logs a warning and increments `deprecated_flags_inuse_total`. #7793
* [CHANGE] Ingester: Graduate native histogram ingestion (`-blocks-storage.tsdb.enable-native-histograms`) from experimental. #7789
* [CHANGE] Querier: Make query time range configurations per-tenant: `query_ingesters_within`, `query_store_after`, and `shuffle_sharding_ingesters_lookback_period`. Uses `model.Duration` instead of `time.Duration` to support serialization but has minimum unit of 1ms (nanoseconds/microseconds not supported). #7160 #7323
* [CHANGE] Querier: Make query time range configurations per-tenant: `query_ingesters_within`, `query_store_after`, and `shuffle_sharding_ingesters_lookback_period`. Uses `model.Duration` instead of `time.Duration` to support serialization but has minimum unit of 1ms (nanoseconds/microseconds not supported). The settings move from the `querier` block to `limits`, and their flags from `-querier.*` to `-limits.*`. The old YAML fields and flags still work as the defaults for the new limits, but are deprecated and will be removed in v1.24.0: using them logs a warning and increments `deprecated_flags_inuse_total`. Setting an old and a new name to different values fails at startup. #7160 #7323 #7919

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* [CHANGE] Querier: Make query time range configurations per-tenant: `query_ingesters_within`, `query_store_after`, and `shuffle_sharding_ingesters_lookback_period`. Uses `model.Duration` instead of `time.Duration` to support serialization but has minimum unit of 1ms (nanoseconds/microseconds not supported). The settings move from the `querier` block to `limits`, and their flags from `-querier.*` to `-limits.*`. The old YAML fields and flags still work as the defaults for the new limits, but are deprecated and will be removed in v1.24.0: using them logs a warning and increments `deprecated_flags_inuse_total`. Setting an old and a new name to different values fails at startup. #7160 #7323 #7919
* [CHANGE] Querier: Make query time range configurations per-tenant: `query_ingesters_within`, `query_store_after`, and `shuffle_sharding_ingesters_lookback_period`. Uses `model.Duration` instead of `time.Duration` to support serialization but has minimum unit of 1ms (nanoseconds/microseconds not supported). The settings move from the `querier` block to `limits`, and their flags from `-querier.*` to `-limits.*`. The old YAML fields and flags still work as the defaults for the new limits, but are deprecated and will be removed in v1.24.0: using them logs a warning and increments `deprecated_flags_inuse_total`. Setting the old field and a non-default limit to different values fails at startup. #7160 #7323 #7919

Setting an old name and a non-default new limit to different values fails at startup. Isn't it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good catch: as written it only failed when the new limit was at a non-default value. After Friedrich's review the rule changed (7245514), so I reworded the entry instead of taking the suggestion as-is: a command-line flag wins over the YAML file whichever name each uses, and the old and new name have to agree only when both are set in the same place (both in YAML, or both on the command line). The #7481 entry says the same.

@friedrichg friedrichg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not ready yet

Comment thread pkg/cortex/cortex.go Outdated
case *d.limit == d.def:
*d.limit = model.Duration(d.old)
case time.Duration(*d.limit) != d.old:
return fmt.Errorf("%s (%s) and the deprecated %s (%s) are both set: remove %s", d.newName, time.Duration(*d.limit), d.oldName, d.old, d.oldName)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to docs cli flags should take precedence over configuration changes. https://cortexmetrics.io/docs/configuration/configuration-file/#:~:text=Cortex%20can%20be,YAML%20config%20file.

I tested this PR locally and I found that if I test:

distributor:
  ha_tracker:
    ha_tracker_failover_timeout: 45s

with -distributor.ha-tracker.failover-timeout=30s

1.21 uses 30s
while this PR makes it 45s.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for testing it, you're right. The fallback compared values, so it couldn't tell an explicit flag at its default value (30s) from no flag at all. Fixed in 7245514: LoadConfig now records which YAML keys are set and main records which flags were passed, and a command-line flag wins over the YAML file whichever name each uses, as in 1.21. Your case is a TestFlagParsing case now. I also compared -print.config against a v1.21.0 build: with the old field at 45s and the flag unset, =30s or =1m, both give 45s / 30s / 1m. The querier settings match 1.21 too (details in the PR description).

The fallback applied a deprecated value whenever the new limit was at its
default, so it could not tell a flag passed explicitly at its default
value from one left unset. With

    distributor:
      ha_tracker:
        ha_tracker_failover_timeout: 45s

and -distributor.ha-tracker.failover-timeout=30s, 1.21 uses 30s, because
a command-line flag overrides the YAML file, while this PR used 45s. The
same happened to an explicit -limits.query-store-after=0 next to an old
querier.query_store_after.

Record where each setting was set instead of comparing values: the YAML
keys from LoadConfig (SetYAMLKeys, next to SetYAMLRootNodeStates) and the
flags passed on the command line (SetCommandLineFlags, after parsing). A
flag on the command line now wins over the YAML file whichever name each
uses, as in 1.21, and the old and new name must agree only when both are
set in the same place. A config built in code has no recorded sources and
keeps the value-based rule.

The deprecated fields are also omitempty, so -print.config and /config
show them only when they are set.

Each case was checked against a v1.21.0 build with -print.config.

Signed-off-by: Charlie Le <charlie_le@apple.com>
@CharlieTLe
CharlieTLe requested a review from friedrichg November 6, 2026 23:16

@friedrichg friedrichg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the comments

@friedrichg
friedrichg merged commit b6b08e7 into cortexproject:master Nov 6, 2026
40 checks passed
CharlieTLe added a commit to CharlieTLe/cortex that referenced this pull request Nov 6, 2026
…ortexproject#7919)

* Keep the 1.21 query range and HA failover timeout settings working

cortexproject#7160/cortexproject#7323 moved query_ingesters_within, query_store_after and
shuffle_sharding_ingesters_lookback_period from querier to limits, and
renamed their flags from -querier.* to -limits.*. cortexproject#7481 moved the
ha_tracker_failover_timeout YAML field from distributor.ha_tracker to
limits. Neither left the old names in place, so a 1.21 config that uses
them fails at startup on 1.22.

Restore the old YAML fields and flags as hidden, deprecated settings
that are scheduled for removal in v1.24.0. Config.Validate copies a set
old value into its limit before the limits are validated and before they
become the runtime config defaults, so tenant overrides inherit it. When
the old and the new name are both set to different values, startup fails
rather than picking one. cortex.New logs a warning and increments
deprecated_flags_inuse_total for each one used: Validate runs before the
logger is initialised, so a warning logged there is lost.

Also correct the -limits.query-ingesters-within help text, which said
"less than or equal to" close-idle-tsdb-timeout while cortexproject#7323 rejects equal,
and spell out that new startup check in the CHANGELOG.

Signed-off-by: Charlie Le <charlie_le@apple.com>

* CHANGELOG: reference cortexproject#7919

Signed-off-by: Charlie Le <charlie_le@apple.com>

* Drop the Deprecated: marker from the restored config fields

staticcheck (SA1019) flags every read of a field whose doc comment starts
with Deprecated:, and cortex.Config reads these to apply them. Other
config fields that Cortex still reads, such as MaxExemplars, do not use
the marker either.

Signed-off-by: Charlie Le <charlie_le@apple.com>

* Give command-line flags precedence over the deprecated YAML fields

The fallback applied a deprecated value whenever the new limit was at its
default, so it could not tell a flag passed explicitly at its default
value from one left unset. With

    distributor:
      ha_tracker:
        ha_tracker_failover_timeout: 45s

and -distributor.ha-tracker.failover-timeout=30s, 1.21 uses 30s, because
a command-line flag overrides the YAML file, while this PR used 45s. The
same happened to an explicit -limits.query-store-after=0 next to an old
querier.query_store_after.

Record where each setting was set instead of comparing values: the YAML
keys from LoadConfig (SetYAMLKeys, next to SetYAMLRootNodeStates) and the
flags passed on the command line (SetCommandLineFlags, after parsing). A
flag on the command line now wins over the YAML file whichever name each
uses, as in 1.21, and the old and new name must agree only when both are
set in the same place. A config built in code has no recorded sources and
keeps the value-based rule.

The deprecated fields are also omitempty, so -print.config and /config
show them only when they are set.

Each case was checked against a v1.21.0 build with -print.config.

Signed-off-by: Charlie Le <charlie_le@apple.com>

---------

Signed-off-by: Charlie Le <charlie_le@apple.com>
(cherry picked from commit b6b08e7)
Signed-off-by: Charlie Le <charlie_le@apple.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants