List the documented /config and /health keys in the served order - #372
Merged
diolektor merged 1 commit intoSep 20, 2026
Merged
Conversation
Docs: - The `/config` and `/health` samples followed the order the settings are written in the source, while both endpoints serialize through a sorted map and hand back keys ascending. A sample was therefore unusable as the baseline for a diff against a live response, and sent a reader looking for a setting several lines from where it lands. Seven samples across three pages, including the nested plugins object, where the APM plugin's own settings were out of order too. Turning on serde_json's preserve_order was the alternative and was rejected: the plugin config is a HashMap, so that feature would give the plugins subtree the per-process random iteration order and make the same diff worse rather than better. Tests: - Compare each sample's top-level key list against the body its handler actually builds, so a setting added to /config cannot land without the samples following it. The profiler's index.json sample is deliberately out of this net: RunMeta is a derived struct written on serde's text path, which keeps declaration order and never reaches the sorted map. 1863 tests (1717 unit + 146 integration).
diolektor
deleted the
fix/docs-config-samples-list-keys-in-declaration-order
branch
September 20, 2026 07:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
serde_jsonis built withoutpreserve_order, soserde_json::Mapis aBTreeMapand every object/configand/healthreturn goes out with its keys ascending, at every nesting level. The documented samples were written in the order thejson!literals declare the fields — network, workers, timeouts.The keys themselves were right, all thirty-six of them on
/config, and so was every value. Only the position was wrong, which is enough to stop a sample being usable as the baseline for adiffagainst a live response, and to send a reader looking for a setting several lines from where it turns out to be.The scope is wider than it first looks. Seven blocks across three pages:
/config/healthdocs/features/internal-server.mddocs/operations/health-checks.mddocs/operations/configuration.mdplugins/healthis built by the samejson!inbuild_health_jsonand sorts the same way. The nestedpluginsobject inconfiguration.mdwas wrong twice over:otelcame beforeapm, andapm's own settings were not ascending either.Why not
preserve_orderEnabling the feature and leaving the samples alone was the obvious alternative. It was rejected because it breaks the very scenario this fixes.
PluginManager::config_json()serializesconfig_values: HashMap<String, HashMap<String, Value>>throughserde_json::to_value. Today that lands in aBTreeMapand comes out sorted, so thepluginssubtree is stable across restarts. Underpreserve_orderit would land in anIndexMapand inherit theHashMapiteration order, which std randomizes per process viaRandomState—pluginswould reshuffle on every restart, making adiffagainst a live response worse rather than better. Avoiding that would mean convertingplugin/manager.rs,plugin/context.rsand five plugins' config toBTreeMap, plus aCargo.lockchange pullingTHIRD_PARTY_LICENSES.htmlandcargo denybehind it — and it would make the textual order of everyjson!literal in the project an observable contract.Alphabetical is also not the worse order to read: finding a key by name among thirty-six is easiest alphabetically, and the grouped reference already exists as the settings table in
docs/operations/configuration.md. A JSON sample's job is to show what actually arrives.What is deliberately not touched
docs/features/profiling.md'sindex.jsonsample is correct and was left alone.RunMetais a#[derive(Serialize)]struct written withserde_json::to_vec, and serde's text path streams fields in declaration order without ever building aMap. Worth stating precisely, because the premise is narrower than it sounds: this holds forto_string/to_vec/to_writeronly —serde_json::to_value(&struct)does go throughMapand sorts.docs/getting-started/installation.mdalso containslisten_addr, but in a tracing-subscriber log line, not a/configbody.Guard
A unit test compares each sample's top-level key list against the body
build_config_json/build_health_jsonactually builds — set and order in one assertion — and separately checks that nested objects ascend, which is the only reach it has into apluginsblock an emptyPluginManagercannot produce.Six mutants were run to confirm the test earns its place; each reddens on its intended assertion:
plugins.apm/healthsample removedConfig::to_json()The last is the drift that produced this in the first place: a key added to the code with the samples left behind.
Verification
cargo fmt -- --check,cargo clippy --no-default-features --all-targets -- -D warnings— cleancargo test --no-default-featuresand withplugin-apm,plugin-async,plugin-profiler,plugin-shared— 1863 passed, 0 failedscripts/gen-llms-txt.sh --check— up to date (58 pages);llms-full.txtis regenerated, not hand-edited