Skip to content

fix: make teardown idempotent and exception-safe#91

Merged
lesnik512 merged 1 commit into
mainfrom
fix/crit-3-idempotent-teardown
May 31, 2026
Merged

fix: make teardown idempotent and exception-safe#91
lesnik512 merged 1 commit into
mainfrom
fix/crit-3-idempotent-teardown

Conversation

@lesnik512
Copy link
Copy Markdown
Member

@lesnik512 lesnik512 commented May 31, 2026

Summary

Three teardown-robustness fixes bundled into one PR:

  1. BaseBootstrapper.teardown() idempotent. Two-line guard at the top — returns immediately when not self.is_bootstrapped. Fixes the Litestar/FastStream case where the bootstrapper registers self.teardown on a framework shutdown hook and gets called manually, otherwise double-tearing-down instruments (many of which aren't idempotent themselves).
  2. OpenTelemetryInstrument.teardown() exception-safe. Wraps self._tracer_provider.shutdown() in try/finally so the cached _tracer_provider resets even when shutdown raises.
  3. LoggingInstrument.teardown() exception-safe. Same try/finally shape around self._logger_factory.close_handlers().

Three regression tests added (one per fix); all fail on main, pass on this branch.

Closes CRIT-3 and TEST-3 from an internal audit. Resolves the follow-up flagged in PR #90's code review.

Test plan

  • just test -- tests/test_free_bootstrap.py -v — all teardown tests pass.
  • just test -- tests/instruments/test_opentelemetry_instrument.py -v — pass.
  • just test -- tests/instruments/test_logging_instrument.py -v — pass.
  • just test — 83/83.
  • just lint — clean.
  • Reviewer: confirm the bootstrapper guard placement (top of teardown, before `is_bootstrapped = False`) — order matters so the second call observes `is_bootstrapped` as `False` from the first call.

Notes for reviewer

  • The two `try/finally` blocks use bare `finally:` with no `except`. The original exception propagates; the reset always runs.
  • A `bootstrap → teardown → bootstrap → teardown` cycle is implicitly supported: the second `bootstrap()` sets `is_bootstrapped = True`, re-enabling the next teardown. Not explicitly tested in this PR; a one-liner extension would close that gap if desired.

🤖 Generated with Claude Code

BaseBootstrapper.teardown() now returns immediately when not bootstrapped.
This fixes Litestar and FastStream, both of which register self.teardown
on a framework shutdown hook while also being callable manually — a user
who explicitly calls teardown() would otherwise re-invoke every instrument's
teardown when the framework shutdown fired second. Most instruments are
not idempotent themselves.

Also wrap the shutdown calls inside OpenTelemetryInstrument and
LoggingInstrument in try/finally so the cached _tracer_provider /
_logger_factory references are reset even when shutdown raises. Without
this, a failed shutdown leaves the instrument in a state where a second
bootstrap reuses a stale reference. The bootstrapper-level guard
prevents the immediate symptom but doesn't help when instruments are
used standalone.

Regression tests:
- test_teardown_is_idempotent: bootstrap, teardown twice, assert each
  instrument's teardown was called exactly once.
- test_opentelemetry_instrument_teardown_resets_tracer_provider_when_shutdown_raises:
  patch shutdown to raise, assert field resets to None.
- test_logging_instrument_teardown_resets_factory_when_close_handlers_raises:
  patch close_handlers to raise, assert field resets to None.

Closes CRIT-3 and TEST-3 from the audit. Resolves the try/finally
follow-up flagged in PR #90's code review.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 31, 2026

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%> (ø)
lite_bootstrap/instruments/logging_instrument.py 100.00% <100.00%> (ø)
..._bootstrap/instruments/opentelemetry_instrument.py 100.00% <100.00%> (ø)
tests/instruments/test_logging_instrument.py 100.00% <100.00%> (ø)
tests/instruments/test_opentelemetry_instrument.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 self-assigned this May 31, 2026
@lesnik512 lesnik512 merged commit 1931c52 into main May 31, 2026
8 checks passed
@lesnik512 lesnik512 deleted the fix/crit-3-idempotent-teardown branch May 31, 2026 21:01
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