Async Concurrency Practices

When working in async-capable code, ensure correctness and concurrency safety: 1) Keep async code truly async (no accidental sync calls) - If a branch is meant for async transformation, it must call/await async functions (e.g., use `await _async_transform_recursive(...)`, not the synchronous equivalent).

copy reviewer prompt

Prompt

Reviewer Prompt

When working in async-capable code, ensure correctness and concurrency safety:

1) Keep async code truly async (no accidental sync calls)

  • If a branch is meant for async transformation, it must call/await async functions (e.g., use await _async_transform_recursive(...), not the synchronous equivalent).

2) Make shared caches/thread-safety explicit

  • If you add cached dispatch/lookup tables (or any shared mutable state) that can be hit concurrently, ensure synchronization is in place (or use immutable data / thread-safe initialization patterns).

3) Prevent event-loop blocking from blocking IO

  • If code uses libraries that perform blocking IO during “async” operations (e.g., reading AWS config via boto3), either:
    • perform it only once during initialization and reuse results, and/or
    • cache the expensive objects (e.g., a session) so it’s not recreated repeatedly.
  • Document the behavior so async users don’t instantiate clients inside hot loops.

Example pattern (bounded blocking + caching):

import os
from functools import lru_cache

@lru_cache(maxsize=1)
def _infer_region() -> str:
    aws_region = os.environ.get("AWS_REGION")
    if aws_region:
        return aws_region

    import boto3  # may do blocking config/FS access
    return boto3.Session().region_name

Also ensure parity between sync/async clients: if an async client exposes the same configuration surface as sync, apply the same changes and keep behavior consistent.

Source discussions