← Back to Stormify Documentation

SuspendStormify

Coroutine-aware wrapper around a Stormify instance.

Obtained via Stormify.suspending. Provides a suspending transaction { } that acquires a connection from a SuspendConnectionPool, runs the block on the platform IO dispatcher, wires coroutine cancellation to the underlying DB cancel primitive, and correctly handles nesting via savepoints.

The blocking Stormify instance passed to the constructor continues to work independently — SuspendStormify is purely additive. A single process can use both APIs side-by-side: some modules blocking, others suspending. They share no mutable state; each transaction { } call (blocking or suspend) gets its own connection.

Basic usage

val stormify = Stormify(KdbcDataSource("jdbc:sqlite:app.db"))
val async = stormify.suspending(PoolConfig(maxConnections = 8))

async.transaction {
val user = create(User(email = "a@b.c"))
create(Profile(userId = user.id))
}

Nested transactions

Calling transaction from within another transaction { } block on the same coroutine lineage reuses the outer connection via a savepoint. The outer transaction's final commit/rollback governs overall atomicity; the inner savepoint only bounds the rollback scope if the inner block throws.

Cancellation

When a coroutine running a transaction { } is cancelled, the pool's cancel hook dispatches Connection.cancel — which on Native maps directly to the driver-specific async-cancel primitive (libpq PQcancel, sqlite3_interrupt, mariadb_cancel, dpiConn_breakExecution). This causes the currently-blocking query to return with an error, the transaction rolls back, and the connection is evicted from the pool (on the assumption that its state may be inconsistent after a forced cancel). On JVM the default Connection.cancel is a no-op — cancellation still unwinds the coroutine but does not interrupt an in-flight query.

launch { } inside transaction — strip semantics

If you launch { } a child coroutine inside a transaction { }, the child inherits ConnectionElement via standard coroutine context propagation. Do not perform parallel DB work from that child — the parent's connection is already in use by the parent block, and driver-level APIs forbid sharing a connection across threads. The safe pattern is:

async.transaction {                 // outer tx, connection A
launch {
async.transaction { // nested: opens savepoint on A, NOT parallel work
...
}
}
}

If you truly want parallel DB operations, start a separate top-level transaction from outside any transaction { } block — each gets its own pool connection.

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. Use it when you need the non-suspending API alongside.

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 TransactionContext.() -> R): R

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