Suspend Stormify
Coroutine-aware wrapper around a Stormify instance, backed by a connection pool.
One contract: inside a scope, every database operation uses the scope's connection. The blocking Stormify API (CRUD, top-level extensions, lazy loaders, PagedList, PagedQuery, …) is called unchanged inside the scope and joins the borrowed connection transparently.
Two scopes are available:
withConnection — borrows a pooled connection for the duration of the block. Auto-commit stays on; no transaction is started. Use it for reads and for any work that does not need atomicity. Exceptions propagate unchanged (no
SQLExceptionwrapping) and do not destroy the borrowed connection.transaction — the same borrow, plus BEGIN/COMMIT around the block (rollback on any throwable). Use it only where atomicity is required.
stormify.suspending.withConnection {
val suppliers = findAll<Supplier>("WHERE active = ?", true)
}
stormify.suspending.transaction {
order.update()
Stock.receive(order.warehouse!!, product, qty)
}Nesting. Scopes compose on the same coroutine lineage:
| inner ↓ / outer → | withConnection | transaction |
|---|---|---|
withConnection | reuse the connection | reuse the connection, autoCommit untouched |
transaction | full BEGIN/COMMIT on the ambient connection | savepoint on the ambient connection |
Pooling is suspend-only, by design. Each pooled connection is pinned to a dedicated worker thread (Native drivers and Android's SQLiteSession forbid cross-thread connection use), so a blocking getConnection() could never safely borrow from this pool. If you want pooling, write suspend code. There will be no PooledDataSource for the blocking API.
Closing. close shuts the pool down gracefully. On Native every pooled connection owns a thread — skipping close leaks threads, not just connections.
Double-pooling warning. If your onl.ycode.kdbc.DataSource already pools (e.g. HikariCP on JVM), this pool holds up to PoolConfig.maxConnections of its connections permanently. Keep PoolConfig.maxConnections well below the outer pool's size so other consumers (migrations, health checks, other frameworks) are not starved, and raise or disable the outer pool's leak-detection threshold, which will otherwise flag the long-lived borrows.
Internals
Nesting is detected via a ConnectionElement in the coroutine context. The ambient connection is published to the blocking API through ActiveTxRegistry — on JVM/Android re-published by the coroutine runtime on every dispatcher hop, on Native pinned to the connection's worker thread for the whole block. Coroutine cancellation is wired to the driver's async-cancel primitive (sqlite3_interrupt, PQcancel, …) and always evicts the connection.
The blocking Stormify instance passed to the constructor continues to work independently; the two APIs share no mutable state.
Constructors
Creates a SuspendStormify with its own, independent connection pool.
Properties
Functions
Execute block inside a transaction. Commits on success, rolls back on any throwable.
Borrows a pooled connection for the duration of block, without starting a transaction. The connection stays in auto-commit mode; every statement inside commits independently.