Files
prompts/skills/fastapi-async-sqlalchemy-modernization/references/implicit_io.md
T
John Lancaster 9b02007216 more
2026-06-17 21:49:57 -05:00

3.1 KiB

Preventing Implicit ORM I/O (Asyncio)

Source:

Status: adopted Decision level: advisory Applies to: api-runtime, workers, tests Last reviewed: 2026-06-17


Purpose

Minimize unexpected database round-trips caused by attribute access in async ORM code.

In asyncio applications, hidden lazy loads are easy to miss and can produce runtime surprises. This guide defines explicit-loading defaults and progressive enforcement practices.


Scope and Non-Goals

  • In scope: relationship loading strategy, post-commit attribute access, explicit refresh/awaitable access patterns.
  • Out of scope: full ORM performance tuning and domain-specific query architecture.

Rules

  • Prefer explicit eager loading for data required by endpoint/service outputs.
  • Avoid relying on implicit lazy-load behavior in request critical paths.
  • Keep expire_on_commit=False unless strict expiration behavior is intentionally required.
  • Use explicit refresh or awaitable-attribute access when loading deferred state is necessary.

Pattern A: Eager-load what you need

from sqlalchemy import select
from sqlalchemy.orm import selectinload

stmt = select(User).options(selectinload(User.roles))
users = (await session.scalars(stmt)).all()

Pattern B: Explicit refresh of named attributes

user = await session.get(User, user_id)
await session.refresh(user, ["roles"])

Pattern C: Awaitable attribute access where needed

# Requires AsyncAttrs mixin on mapped base or class.
roles = await user.awaitable_attrs.roles

Practical Enforcement Model

Use phased enforcement:

  1. High-traffic and latency-sensitive routes: enforce explicit eager loading.
  2. Background tasks and less critical paths: track and progressively tighten.
  3. Add review checks to prevent newly introduced implicit-load hotspots.

This keeps modernization pragmatic while reducing hidden I/O over time.


Anti-Patterns

  • Returning ORM objects from handlers and triggering lazy loads during serialization.
  • Assuming post-commit attribute access will always be loaded without explicit strategy.
  • Relying on broad expiration + implicit reload behavior in async request flows.
  • Enabling relationship patterns that hide SQL behavior in critical code paths.

Operational Checks

  • Endpoint query blocks define loader options for returned related data.
  • Critical handlers do not depend on incidental lazy loads.
  • Known exceptions are documented with rationale and follow-up items.

Testing Checks

  • Integration tests cover endpoints that return related objects.
  • Tests verify expected data is present without hidden secondary query surprises.
  • Regression tests exist for routes previously affected by implicit-load failures.

Migration Notes

  • Start advisory: target high-risk paths first.
  • As coverage improves, elevate selected rules to mandatory in code review policy.