Skip to content

Transactions and persistence threads

A persistence thread batches insert, update and delete operations for one connection.

$thread = $userManager->getConnection()->createPersistThread();

$thread
    ->pushPersist($user)
    ->pushPersist($project)
    ->pushDelete($obsoleteProject)
    ->flush();

flush() executes queued statements without wrapping them in a transaction.

Commit a transactional batch

$thread = $userManager->getConnection()->createPersistThread();

try {
    $thread
        ->pushPersist($user)
        ->pushPersist($project)
        ->commit();
} catch (Throwable $exception) {
    $thread->rollback();
    throw $exception;
}

In Core 3.0, commit() requires a transaction-capable persistence adapter. It then obtains one native driver connection. When that driver exposes PDO-style beginTransaction() and commit() methods, the thread starts a transaction, executes the queued statements on that connection, commits and returns the connection to the pool.

PostgreSQL native-driver behavior

The Core 3.0 PostgreSQL connection uses native PgSql\Connection, which does not expose the PDO transaction methods checked by PersistThread::commit().

In that case Core falls back to flush() rather than providing a native transactional commit. Code that requires strict PostgreSQL transaction semantics must therefore use a path that explicitly guarantees them instead of assuming PersistThread::commit() is atomic for the native PostgreSQL driver.

This distinction is particularly important for higher-level persistence strategies such as Strates: snapshot publication can provide reader-facing version isolation even when individual persistence branches use their own transaction boundaries.

Persistence threads and concurrency

A PersistThread batches work for one connection. It is not itself a scheduler for independent application tasks.

Swoole/OpenSwoole applications can execute independent persistence workflows concurrently when their data dependencies allow it, subject to connection-pool and database capacity. Each workflow can use its own persistence thread and local transaction boundary.

For versioned snapshot workloads, see Strates architecture and parallel persistence.

Entity collections

$users->persist();
$users->delete();

When supported by the connection, collection persistence uses one persistence thread. Small Swoole DB reads the generated identifier after every insert; SQL drivers keep normal batched behavior.

Next chapter: Backups and change detection