You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: skills/application-development/benchmarking-transaction-patterns/SKILL.md
+51-54Lines changed: 51 additions & 54 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,82 +13,43 @@ Guides users through benchmarking, explaining, and comparing two formulations of
13
13
14
14
**Complement to design skills:** For general transaction design principles, see [designing-application-transactions](../designing-application-transactions/SKILL.md). For SQL syntax and query patterns, see [cockroachdb-sql](../../query-and-schema-design/cockroachdb-sql/SKILL.md).
15
15
16
-
## When to Use This Skill
17
-
18
-
- Comparing explicit multi-statement transactions versus CTE-based single-statement transactions
19
-
- Benchmarking CockroachDB workloads under high concurrency or hot-key contention
20
-
- Investigating retry pressure, p95/p99 latency, or throughput differences between transaction formulations
21
-
- Deciding whether to rewrite a multi-step application flow into a single SQL statement
22
-
- Setting up a fair side-by-side benchmark with proper reset discipline
- Explaining why SQL Activity still shows waiting even with CTE transactions
25
-
26
-
## Prerequisites
27
-
28
-
- CockroachDB test cluster (do not benchmark on production)
29
-
- SQL client or JDBC driver for benchmark execution
30
-
- Understanding of CockroachDB SERIALIZABLE isolation and retry behavior
31
-
- Familiarity with basic concurrency testing concepts
32
-
33
16
## Core Concept
34
17
35
-
When two implementations perform the same business behavior, the transaction formulation itself can be a primary performance lever under contention.
18
+
Under contention, the transaction formulation itself is a primary performance lever. The **explicit model** (multi-statement `BEGIN`/`COMMIT`) keeps the transaction open across round trips, widening the contention window. The **CTE model** (single-statement) collapses the same logic into one atomic statement, reducing transaction duration and retries.
36
19
37
20
### Explicit Transaction Model
38
21
39
-
The application orchestrates the workflow as separate SQL statements inside a transaction: read state, apply logic, write changes, commit.
40
-
41
22
```sql
42
23
BEGIN;
43
-
44
24
SELECT balance FROM accounts WHERE id = $1;
45
-
46
25
-- Application decides whether transfer is allowed
47
-
48
26
UPDATE accounts SET balance = balance - $2WHERE id = $1;
49
27
UPDATE accounts SET balance = balance + $2WHERE id = $3;
50
-
51
28
INSERT INTO transfers (from_acct, to_acct, amount, created_at)
52
29
VALUES ($1, $3, $2, now());
53
-
54
30
COMMIT;
55
31
```
56
32
57
-
This keeps the transaction open across multiple statements and often includes application-side decision logic between steps.
58
-
59
33
### CTE Transaction Model
60
34
61
-
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.
35
+
CockroachDB rejects multiple mutations of the same table in a single statement by default (`sql.multiple_modifications_of_table.enabled`), so the debit and credit are folded into one `UPDATE` using `CASE`.
62
36
63
37
```sql
64
-
WITH debit AS (
38
+
WITH funded AS (
39
+
SELECT1FROM accounts WHERE id = $1AND balance >= $2
40
+
), upd AS (
65
41
UPDATE accounts
66
-
SET balance = balance - $2
67
-
WHERE id = $1
68
-
AND balance >= $2
69
-
RETURNING id
70
-
), credit AS (
71
-
UPDATE accounts
72
-
SET balance = balance + $2
73
-
WHERE id = $3
74
-
AND EXISTS (SELECT1FROM debit)
42
+
SET balance = CASE WHEN id = $1 THEN balance - $2 ELSE balance + $2 END
43
+
WHERE id IN ($1, $3) AND EXISTS (SELECT1FROM funded)
75
44
RETURNING id
76
45
), ins AS (
77
46
INSERT INTO transfers (from_acct, to_acct, amount, created_at)
78
-
SELECT $1, $3, $2, now()
79
-
WHERE EXISTS (SELECT1FROM debit)
80
-
AND EXISTS (SELECT1FROM credit)
47
+
SELECT $1, $3, $2, now() WHERE (SELECTcount(*) FROM upd) =2
81
48
RETURNING id
82
49
)
83
50
SELECT id FROM ins;
84
51
```
85
52
86
-
### Why CTE Tends to Win Under Contention
87
-
88
-
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.
89
-
90
-
The CTE version collapses the same business logic into a single atomic statement, reducing transaction duration and sharply narrowing the contention window.
91
-
92
53
## Steps
93
54
94
55
### 1. Prepare the Benchmark Environment
@@ -125,7 +86,25 @@ ON CONFLICT (id) DO UPDATE SET balance = 1000.00;
125
86
126
87
### 3. Run the Explicit Transaction Benchmark
127
88
128
-
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.
89
+
Execute with realistic concurrency. Example using `pgbench` (PostgreSQL-compatible):
90
+
91
+
```bash
92
+
# Create a pgbench script file: explicit_transfer.sql
93
+
# \set from_id random(1, 10000)
94
+
# \set to_id random(1, 10000)
95
+
# \set amount 10.00
96
+
# BEGIN;
97
+
# SELECT balance FROM accounts WHERE id = :from_id;
98
+
# UPDATE accounts SET balance = balance - :amount WHERE id = :from_id;
99
+
# UPDATE accounts SET balance = balance + :amount WHERE id = :to_id;
@@ -153,6 +138,20 @@ Always compare these metrics side by side:
153
138
| Max latency | Outlier behavior |
154
139
| Failures | Non-retryable errors |
155
140
141
+
### 7. Validate Benchmark Integrity
142
+
143
+
Before interpreting results, verify the benchmark ran cleanly:
144
+
145
+
```sql
146
+
-- Confirm expected transfer volume
147
+
SELECTCOUNT(*) AS total_transfers FROM transfers;
148
+
```
149
+
150
+
```bash
151
+
# Check node liveness and start times (no node restarts mid-benchmark)
152
+
cockroach node status --certs-dir=<certs-dir># or --insecure for an insecure cluster
153
+
```
154
+
156
155
## Benchmark Reference Results
157
156
158
157
In a reported high-contention run comparing the two models:
@@ -202,11 +201,9 @@ Extended runs preserved the same directional result at higher total volume, with
202
201
203
202
## Common Misconceptions
204
203
205
-
**"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.
206
-
207
-
**"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.
208
-
209
-
**"Single-statement means no contention"** — A CTE can still wait under contention. The benefit is a narrower contention window, not the elimination of contention.
204
+
-**"CTE always wins"** — Only when the workflow is contention-sensitive and expressible as a single atomic statement.
205
+
-**"SQL Activity showing waiting means CTE failed"** — CTE reduces but does not eliminate contention. Compare throughput, tail latency, and retry profile.
206
+
-**"Single-statement means no contention"** — CTE narrows the contention window but does not eliminate it.
@@ -13,92 +13,22 @@ Audits optimizer table statistics for staleness, missing column coverage, and ro
13
13
14
14
**Complement to profiling-statement-fingerprints:** This skill diagnoses optimizer statistics issues; for identifying historically slow queries, see [profiling-statement-fingerprints](../profiling-statement-fingerprints/SKILL.md).
15
15
16
-
## When to Use This Skill
17
-
18
-
- Query performance degrades after bulk INSERT, UPDATE, or DELETE operations
19
-
- EXPLAIN plans show unexpected full table scans or suboptimal join orders
20
-
- Plan instability: same query produces different execution plans over time
21
-
- After schema changes: ADD COLUMN, DROP COLUMN, or CREATE INDEX operations
22
-
- Tables experience >20-30% row count changes without statistics refresh
23
-
- SQL-only diagnostics needed without DB Console access
24
-
- Validating automatic statistics collection is working correctly
25
-
26
-
**For historical query analysis:** Use [profiling-statement-fingerprints](../profiling-statement-fingerprints/SKILL.md) to identify slow statement patterns.
27
-
**For live query triage:** Use [triaging-live-sql-activity](../triaging-live-sql-activity/SKILL.md) for immediate incident response.
28
-
29
16
## Prerequisites
30
17
31
-
- SQL connection to CockroachDB cluster
32
-
-**Privilege requirement:** Any privilege on target tables (SELECT, INSERT, UPDATE, DELETE, or admin role)
33
-
- Much less restrictive than VIEWACTIVITY: any table access grants statistics visibility for that table
18
+
- SQL connection with any privilege on target tables
SHOW CLUSTER SETTING sql.stats.automatic_collection.enabled; -- Should return: true
39
-
```
40
-
41
-
**Verify table access:**
42
-
```sql
43
-
SHOW GRANTS ON TABLE database_name.table_name;
44
-
```
21
+
**Related skills:**[profiling-statement-fingerprints](../profiling-statement-fingerprints/SKILL.md) for historical query analysis, [triaging-live-sql-activity](../triaging-live-sql-activity/SKILL.md) for live triage.
45
22
46
23
## Core Concepts
47
24
48
-
### What Are Table Statistics?
49
-
50
-
**Table statistics** provide the optimizer with data distribution information to estimate query costs:
51
-
52
-
| Statistic Type | Description | Impact on Optimizer |
-**Cons:** Requires monitoring and operational overhead
590
-
-**Recommendation:** Use for critical tables, correlated columns, post-bulk-load scenarios
591
-
592
-
### Statistics Retention
510
+
-**Auto vs manual:** Keep automatic collection enabled for baseline; use manual `CREATE STATISTICS` for ad-hoc post-bulk-load refresh and critical tables
511
+
-**Multi-column statistics:** Auto-collection covers index column groups; manual `CREATE STATISTICS` is needed for correlated non-indexed columns queried together (e.g., `CREATE STATISTICS city_state_stats ON city, state FROM addresses;`)
512
+
-**Large tables (>10M rows):** Schedule `CREATE STATISTICS` during maintenance windows; monitor with `SHOW JOBS WHERE job_type = 'CREATE STATS'`
See [references/statistics-thresholds.md](references/statistics-thresholds.md) for detailed guidance.
670
-
671
-
### Privilege Requirements
672
-
673
-
**Required:** Any privilege on table (SELECT, INSERT, UPDATE, DELETE, or admin role)
674
-
675
-
**Comparison to other diagnostics:**
676
-
-**Less restrictive than:** VIEWACTIVITY (required for statement_statistics)
677
-
-**More restrictive than:** Public cluster settings
678
-
679
-
**Grant example:**
680
-
```sql
681
-
-- Grant SELECT (least privileged) for statistics visibility
682
-
GRANTSELECTON TABLE database_name.table_name TO diagnostics_user;
683
-
```
516
+
See [references/create-statistics-examples.md](references/create-statistics-examples.md) and [references/statistics-thresholds.md](references/statistics-thresholds.md) for detailed guidance.
0 commit comments