Guides benchmarking and comparing explicit multi-statement transactions versus single-statement CTE transactions in CockroachDB, with fair test methodology, contention analysis, and performance interpretation. Use when comparing transaction formulations, benchmarking CockroachDB workloads under contention, investigating retry pressure, or deciding whether to rewrite multi-step application flows into single SQL statements.
80
75%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./skills/application-development/benchmarking-transaction-patterns/SKILL.mdGuides users through benchmarking, explaining, and comparing two formulations of the same transactional business workflow in CockroachDB: explicit multi-statement transactions versus single-statement CTE transactions. Focuses on performance under contention, fair test methodology, and result interpretation.
Complement to design skills: For general transaction design principles, see designing-application-transactions. For SQL syntax and query patterns, see cockroachdb-sql.
When two implementations perform the same business behavior, the transaction formulation itself can be a primary performance lever under contention.
The application orchestrates the workflow as separate SQL statements inside a transaction: read state, apply logic, write changes, commit.
BEGIN;
SELECT balance FROM accounts WHERE id = $1;
-- Application decides whether transfer is allowed
UPDATE accounts SET balance = balance - $2 WHERE id = $1;
UPDATE accounts SET balance = balance + $2 WHERE id = $3;
INSERT INTO transfers (from_acct, to_acct, amount, created_at)
VALUES ($1, $3, $2, now());
COMMIT;This keeps the transaction open across multiple statements and often includes application-side decision logic between steps.
The same read/decision/write logic is expressed as a single SQL statement, so the database evaluates and applies the business operation atomically without intermediate client orchestration.
WITH debit AS (
UPDATE accounts
SET balance = balance - $2
WHERE id = $1
AND balance >= $2
RETURNING id
), credit AS (
UPDATE accounts
SET balance = balance + $2
WHERE id = $3
AND EXISTS (SELECT 1 FROM debit)
RETURNING id
), ins AS (
INSERT INTO transfers (from_acct, to_acct, amount, created_at)
SELECT $1, $3, $2, now()
WHERE EXISTS (SELECT 1 FROM debit)
AND EXISTS (SELECT 1 FROM credit)
RETURNING id
)
SELECT id FROM ins;The explicit version keeps the transaction open across multiple statements, increasing the time window for write conflicts, timestamp pushes, and retries. Under high concurrency, each retry repeats the read and write work and continues contending for the same hot data.
The CTE version collapses the same business logic into a single atomic statement, reducing transaction duration and sharply narrowing the contention window.
Set up a dedicated test database and schema. Do not mix benchmark workloads with other traffic.
CREATE DATABASE IF NOT EXISTS bankbench;
USE bankbench;
CREATE TABLE accounts (
id INT PRIMARY KEY,
balance DECIMAL(18,2) NOT NULL DEFAULT 0
);
CREATE TABLE transfers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
from_acct INT NOT NULL,
to_acct INT NOT NULL,
amount DECIMAL(18,2) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);Use multi-row UPSERT for efficient seeding. Single-row inserts distort setup cost.
INSERT INTO accounts (id, balance)
SELECT generate_series(1, 10000), 1000.00
ON CONFLICT (id) DO UPDATE SET balance = 1000.00;Execute with realistic concurrency (e.g., 64-128 workers) and a fixed duration or iteration count. Record throughput, retries, p50/p95/p99 latency, max latency, and failures.
For a fair benchmark, reset account balances between explicit and CTE runs so table size, index size, and account state remain comparable.
UPDATE accounts SET balance = 1000.00;Execute with the same concurrency, duration, and parameters as the explicit run.
Always compare these metrics side by side:
| Metric | What to Look For |
|---|---|
| Throughput (txn/s) | Higher is better; CTE typically sustains better under contention |
| Total retries | CTE often reduces to near-zero |
| p50 latency | Median transaction time |
| p95 latency | Tail latency under moderate contention |
| p99 latency | Worst-case tail; explicit model often shows spikes |
| Max latency | Outlier behavior |
| Failures | Non-retryable errors |
In a reported high-contention run comparing the two models:
| Metric | Explicit | CTE | Change |
|---|---|---|---|
| Throughput | 591.1 txn/s | 1,035.1 txn/s | +75.1% |
| Wall time | 216.5s | 123.7s | -42.9% |
| Average latency | 202.2 ms | 111.3 ms | -45.0% |
| Total retries | 2,270,977 | 0 | -100% |
Extended runs preserved the same directional result at higher total volume, with the explicit model continuing to accumulate retries and occasional failures while the CTE model stayed at zero retries and zero failures.
| Dimension | Explicit Multi-Statement | Single-Statement CTE |
|---|---|---|
| Round trips | Multiple client/server interactions | Single request |
| Transaction lifetime | Longer | Shorter |
| Client retry complexity | Higher | Lower |
| Atomic invariant enforcement | Spread across statements/app logic | Contained in SQL |
| Expected throughput | Lower under contention | Higher under contention |
| Client-visible retries | More likely | Often reduced |
--batch-size=1 when you want one business unit of work at a time for clean comparison"CTE always wins in every workload" — No. The claim is narrower: when the same business workflow can be expressed as a single atomic statement and the workload is contention-sensitive, collapsing the transaction shape can materially improve performance and stability.
"SQL Activity showing waiting means CTE failed" — Single-statement CTE execution does not eliminate contention. Statements can still wait on row conflicts, write intents, latches, or scheduling. The right comparison is overall throughput, tail latency, and retry profile.
"Single-statement means no contention" — A CTE can still wait under contention. The benefit is a narrower contention window, not the elimination of contention.
84bc1e4
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.