Skip to content

Support configuration overrides for troubleshooting - #2810

Open
francoisferrand wants to merge 2 commits into
improvement/BB-808from
improvement/BB-809
Open

Support configuration overrides for troubleshooting#2810
francoisferrand wants to merge 2 commits into
improvement/BB-808from
improvement/BB-809

Conversation

@francoisferrand

@francoisferrand francoisferrand commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Any configuration field should be changeable per process, without a new image or release, so that support can adjust a setting on a running platform. The named settings only cover the knobs a schema field exists for, and cannot reach objects with unconstrained keys, such as the librdkafka producer parameters.

BACKBEAT_CONFIG_OVERRIDES now holds a JSON document applied to the configuration as a JSON Merge Patch, before validation: the merged result is validated as a whole, so a typo or a wrong type fails at startup rather than leaving the setting silently ignored. Each schema is given the fraction of the patch covering its own fields, applied after the environment variables derived from it, so that nothing silently overrides the escape hatch someone reached for precisely because the usual path did not work.

It is applied over the configuration file and any other setting, so that nothing silently overrides the escape hatch someone reached for precisely because the usual path did not work.

This stays an escape hatch: the named settings remain the supported way to configure backbeat.

Issue: BB-809

Any configuration field should be changeable per process, without a new
image or release, so that support can adjust a setting on a running
platform. The named settings only cover the knobs a schema field exists
for, and cannot reach objects with unconstrained keys, such as the
librdkafka producer parameters.

BACKBEAT_CONFIG_OVERRIDES now holds a JSON document applied to the
configuration as a JSON Merge Patch, before validation: the merged
result is validated as a whole, so a typo or a wrong type fails at
startup rather than leaving the setting silently ignored. Each
schema is given the fraction of the patch covering its own fields,
applied after the environment variables derived from it, so that
nothing silently overrides the escape hatch someone reached for
precisely because the usual path did not work.

It is applied over the configuration file and any other setting, so
that nothing silently overrides the escape hatch someone reached for
precisely because the usual path did not work.

This stays an escape hatch: the named settings remain the supported
way to configure backbeat.

Issue: BB-809
Comment thread lib/config/configOverrides.js
JSON.parse sets a `__proto__` key as a plain member, so a
`BACKBEAT_CONFIG_OVERRIDES` document naming one had it merged into
Object.prototype: the configuration field the operator meant to set
stayed untouched, and the override silently corrupted every object in
the process instead.

No configuration field is named that, and a JS object cannot hold such a
member anyway, so the key is now dropped when the document is parsed,
and ignored by the merge whatever its caller passes. Reaching the escape
hatch takes operator access, but neither the parsing nor the merge
should depend on that to stay harmless.

Issue: BB-809
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.80%. Comparing base (06dcc11) to head (96f7010).

Additional details and impacted files

Impacted file tree graph

Files with missing lines Coverage Δ
lib/config/configOverrides.js 100.00% <100.00%> (ø)
lib/config/envOverrides.js 98.96% <100.00%> (-0.06%) ⬇️
lib/config/paths.js 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

Components Coverage Δ
Bucket Notification 80.25% <ø> (ø)
Core Library 81.89% <100.00%> (+0.11%) ⬆️
Ingestion 70.04% <ø> (ø)
Lifecycle 80.45% <ø> (ø)
Oplog Populator 85.80% <ø> (ø)
Replication 62.04% <ø> (ø)
Bucket Scanner 85.76% <ø> (ø)
@@                  Coverage Diff                   @@
##           improvement/BB-808    #2810      +/-   ##
======================================================
+ Coverage               75.73%   75.80%   +0.06%     
======================================================
  Files                     202      204       +2     
  Lines                   14023    14054      +31     
======================================================
+ Hits                    10621    10653      +32     
+ Misses                   3392     3391       -1     
  Partials                   10       10              
Flag Coverage Δ
api:retry 9.47% <23.07%> (+0.04%) ⬆️
api:routes 9.23% <23.07%> (+0.04%) ⬆️
bucket-scanner 85.76% <ø> (ø)
ft_test:queuepopulator 9.55% <23.07%> (-0.02%) ⬇️
ingestion 12.60% <23.07%> (+0.03%) ⬆️
lib 9.18% <23.07%> (+0.04%) ⬆️
lifecycle 19.57% <23.07%> (+0.01%) ⬆️
notification 1.01% <0.00%> (-0.01%) ⬇️
oplogPopulator 0.13% <0.00%> (-0.01%) ⬇️
replication 19.11% <23.07%> (+0.02%) ⬆️
unit 55.38% <100.00%> (+0.10%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread docs/config-overrides.md

```
BACKBEAT_CONFIG_OVERRIDES='{"extensions":{"lifecycle":{"conductor":{"concurrency":20}}}}'
BACKBEAT_CONFIG_OVERRIDES='{"queuePopulator":{"batchMaxRead":null}}'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These two lines assign the same env var — only the second one takes effect, silently dropping the lifecycle override. An operator following this example would lose the concurrency change.

Either combine them into one JSON document (like the example below), or split into two separate code blocks each labeled as a standalone example.

Suggested change
BACKBEAT_CONFIG_OVERRIDES='{"queuePopulator":{"batchMaxRead":null}}'
BACKBEAT_CONFIG_OVERRIDES='{"extensions":{"lifecycle":{"conductor":{"concurrency":20}}},"queuePopulator":{"batchMaxRead":null}}'

Comment on lines +128 to +134
it('should restore the schema default when a field is deleted', () => {
assert.strictEqual(backbeatConfig.kafka.backlogMetrics.intervalS, 60);
process.env[CONFIG_OVERRIDES] = '{"kafka":{"backlogMetrics":{"intervalS":null}}}';
config._parseConfig(testConfig);
// the joi default of the field, not the value of the config file
assert.strictEqual(config.kafka.backlogMetrics.intervalS, 60);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this test passes even with the null delete removed from mergePatch , I checked. The fixture value and the joi default are both 60, so the assertion cannot tell "field deleted, default re-applied" from "nothing happened"...
The delete itself is covered by the required-field test above, it's the restore the default part that isn't. Setting the field to something else first makes the assertion meaningful:

Suggested change
it('should restore the schema default when a field is deleted', () => {
assert.strictEqual(backbeatConfig.kafka.backlogMetrics.intervalS, 60);
process.env[CONFIG_OVERRIDES] = '{"kafka":{"backlogMetrics":{"intervalS":null}}}';
config._parseConfig(testConfig);
// the joi default of the field, not the value of the config file
assert.strictEqual(config.kafka.backlogMetrics.intervalS, 60);
});
it('should restore the schema default when a field is deleted', () => {
testConfig.kafka.backlogMetrics.intervalS = 120;
process.env[CONFIG_OVERRIDES] = '{"kafka":{"backlogMetrics":{"intervalS":null}}}';
config._parseConfig(testConfig);
assert.strictEqual(config.kafka.backlogMetrics.intervalS, 60);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants