← Back to Stormify Documentation

SuspendStormify

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 SQLException wrapping) 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 →withConnectiontransaction
withConnectionreuse the connectionreuse the connection, autoCommit untouched
transactionfull BEGIN/COMMIT on the ambient connectionsavepoint 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

Link copied to clipboard
constructor(stormify: Stormify, config: PoolConfig = PoolConfig())

Creates a SuspendStormify with its own, independent connection pool.

Properties

Link copied to clipboard

Returns a snapshot of the pool's counters.

Link copied to clipboard

The blocking Stormify instance this suspend API wraps.

Functions

Link copied to clipboard
suspend fun close()

Gracefully shuts down the underlying connection pool. See SuspendConnectionPool.close for semantics.

Link copied to clipboard
suspend fun <R> transaction(block: suspend () -> R): R

Execute block inside a transaction. Commits on success, rolls back on any throwable.

Link copied to clipboard
suspend fun <R> withConnection(block: suspend () -> R): R

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.