ADR-002: Async-First Design¶
Status: Accepted Date: 2026-02-01 Deciders: adk-secure-sessions maintainers
Context¶
Google ADK is async-first. Its session services use async def methods, its database layer uses aiosqlite for SQLite and async SQLAlchemy for other backends, and its Runner executes agents in an async event loop.
Any session service replacement must be async-compatible to work with ADK's runtime.
Decision¶
All public APIs in adk-secure-sessions are async def.
Rules¶
- All protocol methods are
async def—encrypt,decrypt, and all session service methods - No sync wrappers — we don't provide
encrypt_sync()orcreate_session_sync()convenience methods. Users in sync contexts can useasyncio.run()ornest_asynciothemselves. - Backends must be async — even if the underlying operation is CPU-bound (e.g., Fernet encryption), the method signature is
async deffor interface consistency. CPU-bound backends can useawait asyncio.to_thread()internally if they block. - Database operations use async drivers —
aiosqlitefor SQLite, async SQLAlchemy for others, matching ADK's own approach.
Rationale¶
- ADK's
BaseSessionServicemethods are async — our decorator must match the signature exactly - Encryption backends that call external services (KMS, Vault) are inherently async I/O
- Mixing sync and async creates
RuntimeErrorin event loops and forcesnest_asynciohacks - One consistent model is easier to reason about than sync/async dual APIs
Consequences¶
What becomes easier¶
- ADK integration: Direct drop-in, no async/sync bridging
- KMS backends: Network calls to key management services are naturally async
- Consistency: One calling convention everywhere
What becomes harder¶
- Simple scripts: Users who aren't in an async context need
asyncio.run(). This is standard Python and well-documented. - CPU-bound backends: Fernet encryption is CPU-bound and technically doesn't benefit from async. The overhead of
async defon a CPU-bound operation is negligible (one coroutine frame).
Alternatives Considered¶
Dual Sync/Async API¶
Rejected. Maintaining two versions of every method doubles the API surface, test matrix, and documentation. Libraries like httpx that do this report significant maintenance burden. ADK is async-only, so our users are already in an async context.
Sync-Only with run_in_executor¶
Rejected. Would require wrapping every call site in ADK's async runtime, defeating the purpose.