Repository navigation
Conversation
remove_context_keys popped keys out of the dict held by the THREAD_LOCAL_KEYS ContextVar in place. asyncio tasks and asyncio.to_thread run in a copy of the caller's context, and that copy points at the same dict, so removing a key in one of them removed it for the caller and every other task that copied it. thread_safe_append_keys and thread_safe_clear_keys already set a new dict. thread_safe_remove_keys now does the same.
|
Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need. |
|
Manishram-ai
left a comment
There was a problem hiding this comment.
Thanks for tracking this down. The new test fails on develop (assert None == 'order-1') and passes here, and tests/functional/logger passes (178). I also ran the two snippets from #8771, plus a remove inside contextvars.copy_context().run and a plain thread, against both versions. On develop the to_thread remove drops request_id from the caller's next log line, both tasks lose order_id, and the remove inside ctx.run leaks back to the caller. With this change the caller keeps request_id, the tasks log order-1 and order-2, and the copied context no longer leaks back. Remove now works the same way append and clear already did, by setting a new dict.



Issue number: closes #8771
Summary
Changes
remove_context_keyspopped keys out of the dict stored in theTHREAD_LOCAL_KEYSContextVar in place. asyncio tasks andasyncio.to_threadrun in a copy of the caller's context, and that copy points at the same dict, so athread_safe_remove_keysin one place removed the key for the caller and for every task that copied the context.set_context_keysandclear_context_keysalready set a new dict, so append and clear were fine. Remove now copies the dict before popping, one line inaws_lambda_powertools/logging/formatter.py.Added
test_thread_safe_remove_keys_does_not_affect_tasks_sharing_the_contextnext to the existing thread-safe tests. It appends a key, starts a task, removes the key, and checks the task still logs it while the caller doesn't. Only the publicLoggermethods are used. No existing tests changed.User experience
Before: appending
order_id, spawning a task and then removingorder_iddrops it from the task's logs too. A remove inside a task or ato_threadworker also wipes the key for the caller.After: remove only affects the current context, same as append and clear. Code running in a single context sees no difference.
Verification
Fail before, on
develop:Pass after:
Also ran, all on this branch:
uv run --locked pytest -m "not perf" --ignore tests/e2e: 4054 passed, 73 skipped (Python 3.14.5, env frommake dev-quality-code)uv run --locked pytest tests/performance: 10 passedtests/functional/loggeron Python 3.10.21: 177 passed, 1 skipped, and the new test fails there without the fixruff format --checkandruff check: clean.make mypy: no issues in 949 files. bandit baseline and xenon: cleanorder_idmissing before, present afterDidn't run the 3.11 to 3.13 matrix locally, or the docker based pre-commit hooks (markdownlint, actionlint, cloudformation). No AWS service is involved here, it's in-process contextvars behaviour, so there's no real-service check to do.
used claude code to help with this.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.