Back to all reviewers

Async resource cleanup guarantees

django/django
Based on 3 comments
Python

When implementing async context managers, always ensure proper resource cleanup even during task cancellation. Use try/finally blocks to guarantee that resources are released and context state is properly restored regardless of how execution terminates.

Concurrency Python

Reviewer Prompt

When implementing async context managers, always ensure proper resource cleanup even during task cancellation. Use try/finally blocks to guarantee that resources are released and context state is properly restored regardless of how execution terminates.

Consider this flawed implementation:

async def __aexit__(self, exc_type, exc_value, traceback):
    # This may never complete if a CancelledError is raised
    await self.connection.aclose()
    await self.cleanup_state()

If a task is cancelled while in this context, aclose() might never be called, leading to connection leaks and inconsistent state. Instead, implement:

async def __aexit__(self, exc_type, exc_value, traceback):
    try:
        # Handle normal cleanup first
        if not exc_type:
            await self.connection.acommit()
        else:
            await self.connection.arollback()
    finally:
        # Always execute these operations, even during cancellation
        await self.connection.aclose()
        await self.cleanup_state()

This pattern ensures that critical cleanup operations always execute, preventing resource leaks when tasks are cancelled. When working with resource pools or connection stacks, this approach is essential to maintain system integrity and prevent gradual resource exhaustion.

Additionally, use distinct naming for async methods (like aclose vs close) rather than overloading, to avoid confusing errors and make debugging easier when async/sync boundaries are crossed incorrectly.

3
Comments Analyzed
Python
Primary Language
Concurrency
Category

Source Discussions