ADR 0002: Keep synchronous public APIs over async internals =========================================================== Status ------ Accepted Context ------- The established public API consists of synchronous functions used heavily in scripts, notebooks, pandas workflows, and teaching examples. OGC pagination and chunk fan-out benefit from asynchronous I/O, but exposing only async functions would be a broad breaking change and would complicate common notebook use. Calling ``asyncio.run`` directly is also unsafe when a caller already has a running event loop, as Jupyter commonly does. Decision -------- Keep public service getters synchronous. Async-capable implementations may run inside a short-lived anyio blocking portal and use ``httpx.AsyncClient`` for pagination and bounded fan-out. Internal async functions are implementation details, not a second public API promise. Ambient per-call policy must propagate into the worker context. A resumable OGC call captures the context needed to rebuild its remaining requests after the original getter has returned. Consequences ------------ - Existing scripts and notebooks retain simple blocking call sites. - Concurrent network waits improve large paginated downloads. - Each top-level async-backed call pays worker-thread and portal startup cost. - Cancellation, context propagation, and client ownership need explicit tests. - A future public async API, if justified, should be additive and share the same lower-level contracts rather than duplicate behavior. Compliance ---------- Tests exercise calls inside an already running event loop, ambient context capture on resume, cancellation precedence, bounded in-flight work, and shared client ownership.