Skip to content

fix: make BaseBootstrapper.bootstrap() idempotent#106

Merged
lesnik512 merged 1 commit into
mainfrom
fix/idempotent-bootstrap
Jun 1, 2026
Merged

fix: make BaseBootstrapper.bootstrap() idempotent#106
lesnik512 merged 1 commit into
mainfrom
fix/idempotent-bootstrap

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Summary

BaseBootstrapper.teardown() short-circuits when is_bootstrapped is False, but bootstrap() had no symmetric guard. Calling bootstrapper.bootstrap() twice would re-invoke every instrument's bootstrap, with user-visible side effects:

  • The FastMCP access-log middleware would mount twice → every MCP request gets logged twice.
  • Re-registering a Prometheus /metrics route via application.custom_route(...) raises.
  • The FastAPI lifespan composition runs twice → _TeardownProvider registered twice on FastMCP.
  • structlog reconfiguration leaks duplicate stream handlers on the root logger.

Add the symmetric guard at the top of bootstrap(): when already bootstrapped, return the prepared application without re-running the instruments.

Test plan

  • just test — full suite (149 tests, +1 new) passes
  • just lint — clean
  • New test test_bootstrap_is_idempotent mirrors the shape of the existing test_teardown_is_idempotent — wires two MagicMock() instruments onto the bootstrapper, calls bootstrap() twice, asserts each instrument's bootstrap was called exactly once and is_bootstrapped is True

Flagged as a follow-up during review of #105 (FastMCP bootstrapper) — the MCP access middleware made the double-mount symptom user-visible.

🤖 Generated with Claude Code

teardown() already short-circuits when is_bootstrapped is False, but
bootstrap() previously didn't guard the symmetric case: calling
bootstrapper.bootstrap() twice would re-invoke every instrument's
bootstrap, causing user-visible side effects (e.g. mounting the FastMCP
access-log middleware twice would double every access record;
re-registering a Prometheus /metrics route raises).

Add the symmetric guard at the top of bootstrap(): when is_bootstrapped,
return the prepared application without re-running the instruments.

Adds test_bootstrap_is_idempotent next to the existing
test_teardown_is_idempotent in test_free_bootstrap.py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 self-assigned this Jun 1, 2026
@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

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

Files with missing lines Coverage Δ
lite_bootstrap/bootstrappers/base.py 100.00% <100.00%> (ø)
tests/test_free_bootstrap.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lesnik512 lesnik512 merged commit 6fe29c0 into main Jun 1, 2026
8 checks passed
@lesnik512 lesnik512 deleted the fix/idempotent-bootstrap branch June 1, 2026 21:37
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