Skip to content

Commit 08e96cc

Browse files
authored
Merge pull request #18 from cockroachdb/chore/update-cockroachdb-skills
chore: update cockroachdb-skills submodule (1 new commits)
2 parents 31d0cc9 + 19b5c9e commit 08e96cc

6 files changed

Lines changed: 129 additions & 409 deletions

File tree

  • skills
    • application-development/benchmarking-transaction-patterns
    • observability-and-diagnostics
    • query-and-schema-design/cockroachdb-sql
    • security-and-governance/configuring-sso-and-scim
  • submodules

skills/application-development/benchmarking-transaction-patterns/SKILL.md

Lines changed: 51 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,82 +13,43 @@ Guides users through benchmarking, explaining, and comparing two formulations of
1313

1414
**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).
1515

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
23-
- Interpreting benchmark results (throughput, retries, tail latency, failures)
24-
- 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-
3316
## Core Concept
3417

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.
3619

3720
### Explicit Transaction Model
3821

39-
The application orchestrates the workflow as separate SQL statements inside a transaction: read state, apply logic, write changes, commit.
40-
4122
```sql
4223
BEGIN;
43-
4424
SELECT balance FROM accounts WHERE id = $1;
45-
4625
-- Application decides whether transfer is allowed
47-
4826
UPDATE accounts SET balance = balance - $2 WHERE id = $1;
4927
UPDATE accounts SET balance = balance + $2 WHERE id = $3;
50-
5128
INSERT INTO transfers (from_acct, to_acct, amount, created_at)
5229
VALUES ($1, $3, $2, now());
53-
5430
COMMIT;
5531
```
5632

57-
This keeps the transaction open across multiple statements and often includes application-side decision logic between steps.
58-
5933
### CTE Transaction Model
6034

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`.
6236

6337
```sql
64-
WITH debit AS (
38+
WITH funded AS (
39+
SELECT 1 FROM accounts WHERE id = $1 AND balance >= $2
40+
), upd AS (
6541
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 (SELECT 1 FROM debit)
42+
SET balance = CASE WHEN id = $1 THEN balance - $2 ELSE balance + $2 END
43+
WHERE id IN ($1, $3) AND EXISTS (SELECT 1 FROM funded)
7544
RETURNING id
7645
), ins AS (
7746
INSERT INTO transfers (from_acct, to_acct, amount, created_at)
78-
SELECT $1, $3, $2, now()
79-
WHERE EXISTS (SELECT 1 FROM debit)
80-
AND EXISTS (SELECT 1 FROM credit)
47+
SELECT $1, $3, $2, now() WHERE (SELECT count(*) FROM upd) = 2
8148
RETURNING id
8249
)
8350
SELECT id FROM ins;
8451
```
8552

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-
9253
## Steps
9354

9455
### 1. Prepare the Benchmark Environment
@@ -125,7 +86,25 @@ ON CONFLICT (id) DO UPDATE SET balance = 1000.00;
12586

12687
### 3. Run the Explicit Transaction Benchmark
12788

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;
100+
# INSERT INTO transfers (from_acct, to_acct, amount, created_at) VALUES (:from_id, :to_id, :amount, now());
101+
# COMMIT;
102+
103+
pgbench -n -c 64 -j 8 -T 120 -f explicit_transfer.sql \
104+
"postgresql://root@localhost:26257/bankbench?sslmode=disable"
105+
```
106+
107+
Record throughput (tps), retries, p50/p95/p99 latency, max latency, and failures.
129108

130109
### 4. Reset Between Runs for Fair Comparison
131110

@@ -137,7 +116,13 @@ UPDATE accounts SET balance = 1000.00;
137116

138117
### 5. Run the CTE Transaction Benchmark
139118

140-
Execute with the same concurrency, duration, and parameters as the explicit run.
119+
Execute with the same concurrency, duration, and parameters as the explicit run:
120+
121+
```bash
122+
# Create a pgbench script file: cte_transfer.sql containing the CTE query above
123+
pgbench -n -c 64 -j 8 -T 120 -f cte_transfer.sql \
124+
"postgresql://root@localhost:26257/bankbench?sslmode=disable"
125+
```
141126

142127
### 6. Compare Results
143128

@@ -153,6 +138,20 @@ Always compare these metrics side by side:
153138
| Max latency | Outlier behavior |
154139
| Failures | Non-retryable errors |
155140

141+
### 7. Validate Benchmark Integrity
142+
143+
Before interpreting results, verify the benchmark ran cleanly:
144+
145+
```sql
146+
-- Confirm expected transfer volume
147+
SELECT COUNT(*) 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+
156155
## Benchmark Reference Results
157156

158157
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
202201

203202
## Common Misconceptions
204203

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.
210207

211208
## Safety Considerations
212209

skills/observability-and-diagnostics/auditing-table-statistics/SKILL.md

Lines changed: 17 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -13,92 +13,22 @@ Audits optimizer table statistics for staleness, missing column coverage, and ro
1313

1414
**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).
1515

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-
2916
## Prerequisites
3017

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
3419
- Automatic statistics collection enabled (default): `sql.stats.automatic_collection.enabled = true`
3520

36-
**Check automatic collection status:**
37-
```sql
38-
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.
4522

4623
## Core Concepts
4724

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 |
53-
|----------------|-------------|---------------------|
54-
| **row_count** | Total rows in table | Cardinality estimates for full scans |
55-
| **distinct_count** | Unique values per column | Selectivity estimates for WHERE/JOIN predicates |
56-
| **null_count** | NULL values per column | IS NULL / IS NOT NULL predicate costs |
57-
| **histogram** | Value distribution buckets | Range scan selectivity (e.g., `WHERE age BETWEEN 20 AND 30`) |
58-
59-
**Multi-column statistics** capture correlation between columns (e.g., city + state + zip) for more accurate multi-predicate estimates.
60-
61-
### Statistics Lifecycle
62-
63-
**Automatic collection (default):**
64-
- Triggered when row count changes by ~20% since last statistics collection
65-
- Runs as background job (non-blocking, but consumes resources)
66-
- May be delayed for very large tables (>10M rows)
67-
68-
**Manual collection:**
69-
- Explicit `CREATE STATISTICS` command for immediate refresh
70-
- Required for multi-column statistics (automatic collection only creates single-column stats)
71-
- Recommended after bulk data loads or significant schema changes
72-
73-
### Staleness Indicators
25+
**CockroachDB-specific defaults:**
26+
- Automatic collection triggers at ~20% row count change (`sql.stats.automatic_collection.fraction_stale_rows`)
27+
- Auto-collection covers index column groups (when `sql.stats.multi_column_collection.enabled = true`, the default); ad-hoc multi-column stats on non-indexed columns require manual `CREATE STATISTICS`
28+
- Large tables (>10M rows) may have delayed auto-collection
29+
- Staleness thresholds: refresh if >7 days (OLTP) or >30 days (OLAP), or >20-30% row count drift
7430

75-
| Indicator | Definition | Recommended Action |
76-
|-----------|------------|-------------------|
77-
| **Age** | Time since last statistics collection | Refresh if >7 days (OLTP) or >30 days (OLAP) |
78-
| **Row count drift** | Percent difference between current and cached row_count | Refresh if >20-30% drift detected |
79-
| **Missing columns** | Columns without statistics | CREATE STATISTICS for frequently queried columns |
80-
| **Missing histograms** | Columns without distribution data | Automatic collection handles; may need manual refresh |
81-
82-
See [references/statistics-thresholds.md](references/statistics-thresholds.md) for workload-specific threshold guidance.
83-
84-
### When Statistics Are Auto-Collected
85-
86-
**Default trigger:** ~20% row count change (controlled by `sql.stats.automatic_collection.fraction_stale_rows`)
87-
88-
**Collection schedule:**
89-
- Small tables (<10K rows): Immediate
90-
- Medium tables (10K-10M rows): Within minutes to hours
91-
- Large tables (>10M rows): May be delayed hours to avoid resource contention
92-
93-
**Check pending jobs:**
94-
```sql
95-
SELECT job_id, description, status, fraction_completed
96-
FROM [SHOW JOBS]
97-
WHERE job_type = 'AUTO CREATE STATS'
98-
AND status IN ('pending', 'running')
99-
ORDER BY created DESC
100-
LIMIT 20;
101-
```
31+
See [references/statistics-thresholds.md](references/statistics-thresholds.md) for workload-specific guidance.
10232

10333
## Core Diagnostic Queries
10434

@@ -326,9 +256,9 @@ ORDER BY created DESC;
326256
- `column_count`: Number of columns in composite statistic
327257

328258
**Interpretation:**
329-
- **Present**: Manual multi-column statistics exist (automatic collection only creates single-column)
330-
- **Absent**: May need manual creation for correlated columns (e.g., city + state + zip)
331-
- Common use case: Composite index columns that are queried together
259+
- **Present**: Multi-column statistics exist — either auto-collected for an index column group or manually created
260+
- **Absent**: No multi-column stats yet; may need manual creation for correlated non-indexed columns (e.g., city + state + zip)
261+
- Common use case: Manual stats on correlated columns that aren't covered by an index
332262

333263
See [references/create-statistics-examples.md](references/create-statistics-examples.md) for multi-column creation patterns.
334264

@@ -577,110 +507,13 @@ FROM ...;
577507

578508
## Key Considerations
579509

580-
### Automatic vs Manual Collection
581-
582-
**Automatic (default):**
583-
- **Pros:** Zero maintenance, adapts to data changes, covers all single columns
584-
- **Cons:** May lag for very large tables, no multi-column statistics
585-
- **Recommendation:** Keep enabled for baseline coverage
586-
587-
**Manual:**
588-
- **Pros:** Immediate refresh, supports multi-column statistics, controlled timing
589-
- **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'`
513+
- **Staleness tuning:** OLTP: 3-7 days, OLAP: 14-30 days, hybrid: critical tables 3-7 days, archive 30+ days
514+
- **Privilege:** Any table privilege (SELECT, INSERT, etc.) grants statistics visibility
593515

594-
**Default retention:** Controlled by `sql.stats.persisted_rows.max` (default ~10 million rows across cluster)
595-
- Older statistics are pruned automatically
596-
- Typically 7-30 days of history retained depending on statistics volume
597-
598-
**Historical analysis:**
599-
```sql
600-
-- View statistics history for a table
601-
SELECT column_names, row_count, created
602-
FROM [SHOW STATISTICS FOR TABLE table_name]
603-
WHERE column_names = '{}'
604-
ORDER BY created DESC
605-
LIMIT 10; -- Last 10 collections
606-
```
607-
608-
### Histogram Limitations
609-
610-
**What histograms optimize:**
611-
- Range queries: `WHERE age BETWEEN 20 AND 30`
612-
- Inequality predicates: `WHERE price > 100`
613-
- ORDER BY selectivity: `ORDER BY created_at LIMIT 10`
614-
615-
**What histograms don't optimize:**
616-
- Exact equality: `WHERE id = 123` (uses distinct_count instead)
617-
- Multi-column predicates: Requires multi-column statistics
618-
- Very sparse columns: Histograms may be ineffective for highly skewed distributions
619-
620-
### Multi-Column Statistics
621-
622-
**When to create:**
623-
- Columns frequently queried together in WHERE clauses
624-
- Composite index columns with correlated values
625-
- Geographic columns (city + state + zip)
626-
- Time-based partitioning columns (year + month)
627-
628-
**Creation:**
629-
```sql
630-
-- Example: Correlated columns
631-
CREATE STATISTICS city_state_stats ON city, state FROM addresses;
632-
```
633-
634-
**Limitation:** Only manual creation (automatic collection does NOT create multi-column statistics)
635-
636-
See [references/create-statistics-examples.md](references/create-statistics-examples.md) for comprehensive patterns.
637-
638-
### Performance Impact Mitigation
639-
640-
**Large table strategies:**
641-
- Schedule during maintenance windows (nights/weekends)
642-
- Use database-specific batching (one database at a time)
643-
- Monitor cluster metrics: CPU, disk I/O, network saturation
644-
- Consider `AS OF SYSTEM TIME` for historical analysis without impacting live traffic
645-
646-
**Resource monitoring during collection:**
647-
```sql
648-
-- Check running statistics jobs
649-
SELECT job_id, description, status, fraction_completed, running_status
650-
FROM [SHOW JOBS]
651-
WHERE job_type IN ('CREATE STATS', 'AUTO CREATE STATS')
652-
AND status = 'running';
653-
```
654-
655-
### Staleness Threshold Tuning
656-
657-
**OLTP workloads:**
658-
- **Recommended refresh:** 3-7 days
659-
- **Rationale:** Frequent updates, query patterns change rapidly
660-
661-
**OLAP/Analytics workloads:**
662-
- **Recommended refresh:** 14-30 days
663-
- **Rationale:** Batch-oriented, stable query patterns
664-
665-
**Hybrid workloads:**
666-
- Critical tables: 3-7 days
667-
- Archive/historical tables: 30+ days
668-
669-
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-
GRANT SELECT ON 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.
684517

685518
## References
686519

0 commit comments

Comments
 (0)