DBIO

 view release on metacpan or  search on metacpan

docs/adr/0030-async-explicit-per-connection-mode.md  view on Meta::CPAN

## Consequences

- **Removed:** `async_fallback` and its auto-chain (0029); `async_backend` as a
  per-class auto-default (0028); the always-on silent degrade as the default
  behaviour of `*_async` (0014) — it survives only as the explicit `immediate`
  mode.
- **Kept:** the universal `*_async` API surface (0014); the `DBIO::Storage::Async`
  contract and `DBIO::Future` duck-type; `PoolBase`; the embedded-backend concept
  (0028). `future_class` still comes from the live backend.
- **Core changes:** a mode registry + per-connection resolver on
  `DBIO::Storage::DBI` (reading `{ async => ... }`); `DBIO::Storage::Async` made
  concrete with seam hooks; `PoolBase` readiness-gating default. No event-loop
  dependency added to core. Fully mock-testable (`DBIO::Test::Storage` extends
  `DBIO::Storage::DBI`): registry resolution, three modes side by side under mock,
  unavailable-mode croak, `*_async`-on-sync croak.
- **Driver/add-on changes (thin):** `dbio-async` provides only the abstract
  Future::IO base `DBIO::Async::Storage` — `future_io` is convention-resolved per
  driver (`DBIO::X::Storage::Async`), not registered generically (karr #65);
  `dbio-postgresql-ev`/`dbio-mysql-ev`
  keep only EV transport seams + DB-specifics + register `ev`;
  `dbio-postgresql`/`dbio-mysql` register the `ev` mapping (no auto-default);
  `dbio-forked` registers `forked`. The MySQL-EV `connection()`/`storage_type`
  hijack is removed (it contradicts the inert-component model).
- **Extension storage classes** (schema components that set `storage_type` to a
  driver-storage subclass: AGE, PostGIS) run `future_io` through the nearest
  parent adapter found by the convention's MRO walk (karr #67). Extension-
  *specific* async API (e.g. AGE `cypher_async`) lives in the extension dist as
  a conventionally-named adapter subclass and additionally requires connection
  actions (`on_connect_do`/`on_connect_call`) to fire on pool connections —
  a core seam tracked as karr #68; until it lands, pool connections silently
  skip session setup, which is also a sync/async divergence for ordinary
  users of `on_connect_*`.
- **`dbio-ev` is deliberately NOT created** (YAGNI). After the core lift, too
  little shared EV code remains to justify its own dist; revisit if that changes.
- **Migration:** `async_backend('DBIO::Foo::Async::Storage')` on a storage class →
  `connect(..., { async => '<mode>' })` at the call site. Driver-specific
  async-only features (`listen`/`notify`/`pipeline`/`copy_in`) remain reached via
  `$schema->storage->async->...` on the chosen instance.
- **Scope boundary — storage-level only (this increment).** Only the six
  *storage-level* `$storage->*_async` are made explicit (croak on a sync
  instance). The *ResultSet/Row* `*_async` (`all_async`/`first_async`/
  `single_async`/`count_async`/`create_async`) are NOT touched: they still run the
  sync op and wrap it in `future_class`, degrading silently even on a sync
  instance. So after this increment `$storage->select_async` croaks on a sync
  instance while `$rs->all_async` does not — a deliberate, temporary asymmetry
  that the RS/Row real-async work (below) closes by routing RS `*_async` through
  the storage backend.

## Future architecture work (tracked cross-repo, not here)

- **dbio-async / dbio-postgresql-ev / dbio-mysql-ev**: collapse to transport-only
  over the core orchestration; register their mode. karr tickets on their boards.
- **dbio-postgresql / dbio-mysql**: register the `ev` mode mapping; drop the
  MySQL-EV `connection()` hijack; reach parity (`deploy_async`, pool readiness).
- **Naming cleanup**: stale `::Async` / `DBIO::EV::Pg` names in core POD and
  `DBIO::Manual::Heritage`; `Changes`/README staleness in the add-ons.
- **ResultSet/Row real async** (carried over from the earlier async ADRs): route RS `*_async`
  through the storage backend with async row inflation and prefetch/collapse
  parity. Its own ADR when taken up.
- **dbio-postgresql-age**: `DBIO::PostgreSQL::Age::Storage::Async` with
  `cypher_async` (age board #5) — blocked on core #68 (pool on_connect seam).

## Relationship to other ADRs

- **0028 (class-declared embedded backend) and 0029 (forked auto-fallback) —
  removed.** Both are fully replaced by this ADR and have been deleted from
  `docs/adr/`; their still-relevant rationale is folded into the Context and
  Decision above (why an auto-fallback is a footgun → §Context.1; why the embedded
  backend belongs in the storage instance, now chosen per connection rather than
  per class → §Decision.1). Git history preserves the originals.
- **0014 — binding/degrade superseded, API kept.** `*_async` stays universal and
  the contract/`PoolBase` tier stand; the always-on silent degrade becomes the
  explicit `immediate` mode, and `*_async` on a sync instance now croaks instead
  of degrading silently. The `DBIO::Test::Future` production-default smell is now
  doubly resolved: the class is renamed to `DBIO::Future::Immediate`, and it is
  reached only via the named `immediate` mode. 0014's Status is
  updated to "superseded in part by 0030".



( run in 1.721 second using v1.01-cache-2.11-cpan-5e09290becf )