Skip to content

Reload only the config sections a reload file declares - #663

Merged
coopernetes merged 1 commit into
mainfrom
fix/hot-reload-contention
Sep 14, 2026
Merged

coopernetes merged 1 commit into
mainfrom
fix/hot-reload-contention

Conversation

@coopernetes

@coopernetes coopernetes commented Sep 14, 2026

Copy link
Copy Markdown
Member

ConfigHotReloadE2ETest flaked in CI in both directions, and both traced to one root cause: a reload always applied every section of the composed config. A reload file is composed over the base config, so a section the file omits comes back populated with base defaults — an override that sets only secret-scan: silently reset rules: to the base (deny) on the next Section.ALL reload the file-watch fired. Depending on which reload won a shared guard, a push was then rejected for the wrong reason ("not allowed to push" instead of the check under test), or a relaxed rule never took hold.

Fix

A reload now applies only the sections the source document actually declares. A section it is silent about keeps its current live value instead of reverting to a default — a partial reload file patches the sections it names and leaves the rest alone. Declared sections are read from the file's top-level YAML keys.

The reload guard is also fixed. It was an AtomicBoolean that let an explicit reload (POST /api/config/reload, and the e2e harness) be silently dropped when a background reload was in flight — while reload() still returned "Reloaded ...". It's now a ReentrantLock: an explicit reload blocks and always applies, returning the true outcome; background file-watch/git-poll tryLock and skip when busy, since the next poll catches up.

With both fixed the tests are deterministic, so the retry loops (awaitReload/awaitPush) that papered over the drop are gone.

Docs

docs/configuration/hot-reload.md now documents partial reload semantics and the "must still be structurally valid on its own" requirement (a provider referenced by the file must exist and be enabled in the base config).

Verified

ConfigHotReloadE2ETest run locally against Testcontainers Gitea — 5/5 green, repeated.

🤖 Generated with Claude Code

ConfigHotReloadE2ETest flaked in CI, and both failure directions traced to
the same root: a reload always applied every section of the composed config.
A reload file is composed over the base config, so a section the file omits
came back populated with base defaults — an override that set only
`secret-scan:` silently reset `rules:` to the base (deny) on the next
Section.ALL reload the file-watch fired. Depending on which reload won a
shared guard, a push was then rejected for the wrong reason (unauthorized,
not the check under test) or a relaxed rule never took hold.

A reload now applies only the sections the source document actually declares;
a section it is silent about keeps its current live value instead of
reverting. Section keys are read from the file's top-level YAML keys.

The guard is also fixed: it was an AtomicBoolean that let an explicit reload
be silently dropped when a background reload was in flight while reload()
still reported success. It is now a ReentrantLock — an explicit reload blocks
and always applies, returning the true outcome; background polls tryLock and
skip, since the next poll catches up.

With both fixed the tests are deterministic, so the retry loops that papered
over the drop are gone. Documents partial reload semantics in
docs/configuration/hot-reload.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coopernetes
coopernetes force-pushed the fix/hot-reload-contention branch from 711d65f to 19406a2 Compare September 14, 2026 16:24
@coopernetes coopernetes changed the title Stop hot-reload from silently dropping an explicit reload under contention Reload only the config sections a reload file declares Sep 14, 2026
@coopernetes
coopernetes merged commit 523934e into main Sep 14, 2026
25 checks passed
@coopernetes
coopernetes deleted the fix/hot-reload-contention branch September 14, 2026 16:30
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.

1 participant