Skip to content

Isolate retry wrapper statistics per call - #720

Open
HanpuLi wants to merge 1 commit into
jd:mainfrom
HanpuLi:fix/701-isolate-wrapper-statistics
Open

HanpuLi wants to merge 1 commit into
jd:mainfrom
HanpuLi:fix/701-isolate-wrapper-statistics

Conversation

@HanpuLi

@HanpuLi HanpuLi commented Sep 19, 2026 •

Copy link
Copy Markdown

Fixes #701.

Concurrent calls to the same synchronous retry wrapper currently share the wrapper's live statistics dict. A deterministic regression shows thread A at attempt 2 observing its attempt number change to 1 when thread B enters the same wrapper. Re-entrant calls have the same problem.

Each call now keeps its own statistics dict. Changes are mirrored to the stable wrapped_f.statistics object so the behavior fixed by #519, including visibility through outer functools.wraps decorators and during execution, is preserved. A per-thread stack restores the outer call's statistics after a nested call, and publishing to the stable wrapper dict is serialized.

This is intentionally limited to synchronous BaseRetrying.wraps(); async task-local semantics are unchanged.

There is an earlier implementation in #702. The main difference is that #702 changes the existing live-statistics regression to read retry.statistics instead of wrapped statistics. This PR preserves that existing behavior and adds an explicit re-entrant regression, rather than weakening the current wrapper-statistics contract.

Validation: PYTHONPATH=$PWD .venv/bin/poe all passes Ruff, formatting, Mypy, all 186 tests, Sphinx doctests (35/35), and the HTML docs build.

Keep the published wrapper statistics dict stable for decorator compatibility while preventing concurrent and re-entrant calls from sharing their live retry state.

Fixes jd#701.

This branch has not been deployed

No deployments
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.

wraps(): concurrent or reentrant calls share and clear one statistics dict, violating thread-local contract

1 participant