Skip to content

web: reuse the parsed web configuration while the file is unchanged - #443

Open
mrueg wants to merge 1 commit into
prometheus:masterfrom
mrueg:perf/cache-parsed-web-config
Open

web: reuse the parsed web configuration while the file is unchanged#443
mrueg wants to merge 1 commit into
prometheus:masterfrom
mrueg:perf/cache-parsed-web-config

Conversation

@mrueg

@mrueg mrueg commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

The configuration file is consulted on every http request so that changes are picked up without a restart. That read is an os.ReadFile plus a strict YAML unmarshal plus header validation, every time — on the scrape path, and on every TLS handshake through GetConfigForClient.

It is by far the most expensive part of serving a request. Measured against the handler it wraps: the toolkit's handler cost 15.9µs/request where the wrapped handler cost 0.87µs.

Change

Keep the parsed configuration and reuse it while the file's modification time and size are both unchanged, replacing a read and a parse with a stat in the common case.

BenchmarkGetConfig   14743 ns/op   7515 B/op   64 allocs/op   # before
BenchmarkGetConfig    1160 ns/op    515 B/op    3 allocs/op   # after

The tradeoff, stated plainly

An edit is still picked up on the next request, and the existing TestConfigReloading continues to cover that. The limitation worth being explicit about is that a write leaving both the modification time and the size identical is not noticed. On a filesystem whose timestamps have sub-second resolution that needs a rewrite within the same nanosecond at exactly the same length; where timestamps have one-second resolution the window is a second.

I have added a test that pins this behaviour so it is documented rather than discovered later. If you would rather not accept that window at all, this one is easy to drop — the other PRs in this batch do not depend on it.

Details

  • Failures are not cached, so a file that does not parse is reported on every request until fixed, not once.
  • Callers get a copy of the Config struct, so one caller cannot change what the next sees. The maps inside are shared and only ever read.
  • Certificates are still loaded from disk on every handshake. Caching those is a separate change and deliberately not part of this one.

🤖 Generated with Claude Code

The configuration file is consulted on every http request so that changes
are picked up without a restart. That read was an os.ReadFile plus a strict
YAML unmarshal plus header validation, every time, and it happens on the
scrape path as well as on every TLS handshake through GetConfigForClient.
It was by far the most expensive part of serving a request: measured
against the handler it wraps, the toolkit's handler cost 15.9us per request
where the wrapped handler cost 0.87us.

Keep the parsed configuration and reuse it while the file's modification
time and size are both unchanged, replacing a read and a parse with a stat
in the common case.

  BenchmarkGetConfig   14743 ns/op   7515 B/op   64 allocs/op   # before
  BenchmarkGetConfig    1160 ns/op    515 B/op    3 allocs/op   # after

An edit is still picked up on the next request, which the existing
TestConfigReloading continues to cover. The limitation worth being explicit
about is that a write leaving both the modification time and the size
identical is not noticed. On a filesystem whose timestamps have sub-second
resolution that needs a rewrite within the same nanosecond at exactly the
same length; where timestamps have one second resolution the window is a
second. There is a test that pins this behaviour so it is not discovered by
accident later.

Failures are not cached, so a file that does not parse is reported on every
request until it is fixed rather than once. Callers get a copy of the
configuration struct, so one caller cannot change what the next one sees;
the maps inside are shared and are only ever read.

Certificates are still loaded from disk on every handshake. Caching those
is a separate change and is deliberately not part of this one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Manuel Rüger <manuel@rueg.eu>
@mrueg
mrueg force-pushed the perf/cache-parsed-web-config branch from 6d9029f to 6f2be48 Compare September 2, 2026 13:44
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