analyzing-schema-change-storage-risk
Skill2 starsChanged 28 days agoMITView on GitHub
Estimates storage requirements for CockroachDB schema change backfills by analyzing per-index range sizes using SHOW RANGES WITH DETAILS and applying the 3× free space rule. Use when planning CREATE INDEX, ADD COLUMN, or ALTER PRIMARY KEY operations on large tables to avoid disk exhaustion during backfill.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- cockroachdb/claude-plugintheir own version, first seen Oct 13, 2026
- DineshKumar8399/aegis-governed-memory-fleettheir own version, first seen Aug 18, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- cockroachdb/claude-plugin★ 4same name
- DineshKumar8399/aegis-governed-memory-fleet★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository192 other files it gives its agents.
- clickhouse-architecture-advisor/AGENTS.mdAGENTS.md
- clickhouse-best-practices/AGENTS.mdAGENTS.md
- unsafe-checker/AGENTS.mdAGENTS.md
- alertmanager-querySkill
- analyzing-range-distributionSkill
- argocdSkill
- aspire-configurationSkill
- aspire-integration-testingSkill
- aspire-service-defaultsSkill
- auditing-cloud-cluster-securitySkill
- auditing-table-statisticsSkill
- benchmarking-transaction-patternsSkill
- cepthSkill
- chdb-datastoreSkill
- chdb-sqlSkill
- clickhouse-architecture-advisorSkill
- clickhouse-best-practicesSkill
- clickhousectl-cloud-deploySkill
- clickhousectl-local-devSkill
- cockroachdb-sqlSkill
- coding-guidelinesSkill
- configuring-audit-loggingSkill
- configuring-ip-allowlistsSkill
- configuring-log-exportSkill
- configuring-private-connectivitySkill
- configuring-sso-and-scimSkill
- create-technical-architecture-documentSkill
- csharp-mstestSkill
- csharp-tunitSkill
- csharp-xunitSkill
- designing-application-transactionsSkill
- designing-multi-region-applicationsSkill
- domain-cliSkill
- domain-cloud-nativeSkill
- domain-embeddedSkill
- domain-fintechSkill
- domain-iotSkill
- domain-mlSkill
- domain-webSkill
- dotnet-aot-wasmSkill
- dotnet-aspire-patternsSkill
- dotnet-benchmarkdotnetSkill
- dotnet-blazor-authSkill
- dotnet-blazor-componentsSkill
- dotnet-blazor-patternsSkill
- dotnet-blazor-testingSkill
- dotnet-channelsSkill
- dotnet-ci-benchmarkingSkill
- dotnet-cli-architectureSkill
- dotnet-cli-distributionSkill
- dotnet-cli-packagingSkill
- dotnet-cli-release-pipelineSkill
- dotnet-containersSkill
- dotnet-cryptographySkill
- dotnet-csharp-dependency-injectionSkill
- dotnet-csharp-modern-patternsSkill
- dotnet-csharp-nullable-reference-typesSkill
- dotnet-csharp-source-generatorsSkill
- dotnet-maui-doctorSkill
- dotnet-maui-doctorSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
26 rules. About 0 are common, 20 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈20
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 21
Avoid mid-backfill disk exhaustion errors causing schema change failures
- Distinctive: line 26
For range-level monitoring: Use analyzing-range-distribution to analyze range count, leaseholder placement, and fragmentation patterns.
- Distinctive: line 76
Validation: Verify new index matches existing data
- Distinctive: line 78
Cleanup: Delete old index/column data (GC later)
- Distinctive: line 115
LIMIT clause: Default 50-100 for exploratory analysis
- Very specific: line 116
Specific table targeting: Use FROM TABLE table_name, never cluster-wide
- Distinctive: line 117
Production timing: Run during maintenance windows or low-traffic periods
- Very specific: line 118
Pre-check range count: Run basic SHOW RANGES first to assess table size
- Distinctive: line 222
UNSAFE: Add capacity before proceeding (or drop unused indexes first)
- Very specific: line 248
Use worst-case (highest required_free_mb) for capacity planning
- Distinctive: line 265
Check current free disk space: Run cluster capacity query (Prerequisites section)
- Distinctive: line 266
Find largest range in existing indexes: Run Query 2 on target table
- Distinctive: line 268
Compare required vs. available: Use Query 3 decision logic
- Distinctive: line 297
Analyze existing indexes: Run Query 4 to compare storage profiles
- Distinctive: line 299
Prioritize by impact: Create smaller indexes first (faster backfill, less risk)
- Distinctive: line 300
Monitor between operations: Check disk usage after each CREATE INDEX before starting next
- Distinctive: line 328
Apply 3× rule conservatively: Use 4× multiplier for ALTER PRIMARY KEY operations
- Distinctive: line 329
Validate capacity: Ensure 4× largest range available, plus buffer for MVCC overhead
- Very specific: line 361
Pre-check range count: Run SELECT COUNT(*) FROM [SHOW RANGES FROM TABLE t] first
- Very specific: line 362
Use LIMIT for exploration: Start with LIMIT 100, remove only if needed
- Very specific: line 363
Target specific tables: Never run cluster-wide SHOW RANGES WITH DETAILS
- Distinctive: line 408
Before running CREATE INDEX / ADD COLUMN / ALTER PRIMARY KEY:
- Distinctive: line 418
If UNSAFE: Add capacity first (scale cluster) or drop unused indexes.
- Distinctive: line 439
3× rule applies to largest range, not average: Always use MAX(range size), not AVG
- Distinctive: line 444
DETAILS option is expensive: Always use LIMIT, target specific tables
- Distinctive: line 446
Monitor during execution: Check disk usage every 5-10 minutes during long backfills
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Analyzing Schema Change Storage Risk
Estimates storage requirements for CockroachDB schema change backfills by analyzing per-index range sizes and applying the 3× free space rule. Uses SHOW RANGES WITH DETAILS to calculate worst-case disk usage for CREATE INDEX, ADD COLUMN, and ALTER PRIMARY KEY operations, preventing mid-backfill disk exhaustion.
Complement to range distribution analysis: This skill focuses on pre-DDL capacity planning; for ongoing range health monitoring, see analyzing-range-distribution.
When to Use This Skill
- Estimate storage needs before CREATE INDEX on large tables (100GB+)
- Validate capacity before ADD COLUMN backfills with computed/default values
- Pre-flight checks for ALTER PRIMARY KEY operations (full table rewrite)
- Avoid mid-backfill disk exhaustion errors causing schema change failures
- Multi-index planning: prioritize index creation by storage impact
- Capacity planning for batch DDL operations
- SQL-only storage estimation without DB Console access
For range-level monitoring: Use analyzing-range-distribution to analyze range count, leaseholder placement, and fragmentation patterns.
Prerequisites
- SQL connection to CockroachDB cluster
- Admin role OR
ZONECONFIGsystem privilege - Understanding of online schema change architecture (backfill process, MVCC)
- Knowledge of current cluster free disk space (check monitoring or
crdb_internal.kv_store_status) - Target table must have existing data (empty tables = zero storage risk)
Check your privileges:
SHOW GRANTS ON SYSTEM FOR current_user; -- Should show admin or ZONECONFIGCheck cluster free disk space:
SELECT
node_id,
store_id,
ROUND((capacity - used) / 1073741824.0, 2) AS free_gb,
ROUND((used::FLOAT / capacity) * 100, 2) AS disk_usage_pct
FROM crdb_internal.kv_store_status
ORDER BY disk_usage_pct DESC;See permissions reference for RBAC setup (shared with analyzing-range-distribution).
Core Concepts
The 3× Storage Rule
Rule: CockroachDB requires 3× the largest range size as free disk space during schema change backfills.
Why 3×?
- Existing data (1×): Original table/index data remains during backfill
- New backfill data (1×): New index or column data written during online schema change
- MVCC versioning overhead (1×): Multiple versions during compaction, garbage collection lag
Critical: The rule applies to the largest single range, not average range size or total table size.
Example:
- Table has 100GB total size, 1500 ranges
- Largest range = 80MB
- Required free space = 3 × 80MB = 240MB (per node with replica)
Schema Change Backfill Process
Online schema change phases:
- Backfill: Populate new index/column in background (bulk writes)
- Validation: Verify new index matches existing data
- Swap: Atomically make schema change visible
- Cleanup: Delete old index/column data (GC later)
Storage peak: During backfill (phases 1-2), both old and new data exist simultaneously.
Backfill pause behavior: CockroachDB pauses backfills if disk usage exceeds kv.range_split.by_load_enabled threshold (~85% by default), preventing disk exhaustion but causing schema change delays.
Per-Index Storage Calculation
Why per-index? Each index is backed by separate ranges with different sizes.
Example table schema:
CREATE TABLE orders (
id UUID PRIMARY KEY,
customer_id UUID,
created_at TIMESTAMP,
total DECIMAL(10,2),
INDEX idx_customer (customer_id),
INDEX idx_created (created_at)
);Storage breakdown:
- Primary index (
id): 500GB, largest range 75MB → 225MB required idx_customer: 150GB, largest range 60MB → 180MB requiredidx_created: 200GB, largest range 90MB → 270MB required (largest)
Conclusion: New index creation requires 270MB free space (worst case from idx_created's largest range).
SHOW RANGES WITH DETAILS Safety
CRITICAL WARNING: The WITH DETAILS option computes span_stats on-demand, causing:
- High CPU usage from statistics computation
- Memory overhead proportional to range count
- Query timeouts on tables with 1000s of ranges without LIMIT
Mandatory guardrails:
- LIMIT clause: Default 50-100 for exploratory analysis
- Specific table targeting: Use
FROM TABLE table_name, never cluster-wide - Production timing: Run during maintenance windows or low-traffic periods
- Pre-check range count: Run basic
SHOW RANGESfirst to assess table size
See analyzing-range-distribution safety section for detailed guardrails.
Core Diagnostic Queries
Query 1: Total Table Storage (All Indexes)
-- Estimate total table storage across all indexes
SELECT
table_name,
index_name,
COUNT(*) AS range_count,
ROUND(SUM((span_stats->>'approximate_disk_bytes')::BIGINT) / 1073741824.0, 2) AS total_size_gb,
ROUND(AVG((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0, 2) AS avg_range_size_mb
FROM [SHOW RANGES FROM TABLE your_table_name WITH DETAILS]
GROUP BY table_name, index_name
ORDER BY total_size_gb DESC;Output:
total_size_gb: Total storage per indexavg_range_size_mb: Average range size (not used for 3× rule, but useful for fragmentation context)
Interpretation: High total size (100GB+) indicates CREATE INDEX will have significant storage impact.
CRITICAL: Aggregates DETAILS data across all ranges. Use only on targeted tables, never cluster-wide.
Query 2: Largest Range Size per Index (Critical for 3× Rule)
-- Find largest range per index (determines 3× storage requirement)
SELECT
table_name,
index_name,
MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0 AS largest_range_mb,
ROUND((MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0) * 3, 2) AS required_free_space_mb
FROM [SHOW RANGES FROM TABLE your_table_name WITH DETAILS]
GROUP BY table_name, index_name
ORDER BY largest_range_mb DESC;Output:
largest_range_mb: Size of largest range in indexrequired_free_space_mb: 3× the largest range (minimum free space needed)
Interpretation:
largest_range_mb > 64: Range exceeds default split threshold (may indicate split lag or customrange_max_bytes)required_free_space_mb: Compare with cluster free space (Query from Prerequisites)
Use case: Determine worst-case storage requirement before CREATE INDEX.
CRITICAL: Uses DETAILS with aggregation. Limit to specific tables only.
Query 3: Storage Safety Calculation (Decision Logic)
-- Combined query: compare required storage vs. available capacity
WITH range_requirements AS (
SELECT
table_name,
index_name,
MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0 AS largest_range_mb,
ROUND((MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0) * 3, 2) AS required_free_mb_3x,
ROUND((MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0) * 4, 2) AS required_free_mb_4x
FROM [SHOW RANGES FROM TABLE your_table_name WITH DETAILS]
GROUP BY table_name, index_name
),
cluster_capacity AS (
SELECT
MIN((capacity - used) / 1048576.0) AS min_free_mb -- Minimum free space across all nodes
FROM crdb_internal.kv_store_status
)
SELECT
rr.table_name,
rr.index_name,
rr.largest_range_mb,
rr.required_free_mb_3x AS required_3x,
rr.required_free_mb_4x AS required_4x_recommended,
cc.min_free_mb AS cluster_min_free_mb,
CASE
WHEN cc.min_free_mb >= rr.required_free_mb_4x THEN 'SAFE (4× margin)'
WHEN cc.min_free_mb >= rr.required_free_mb_3x THEN 'MARGINAL (3× only, add capacity recommended)'
ELSE 'UNSAFE (insufficient space, add capacity required)'
END AS risk_assessment
FROM range_requirements rr
CROSS JOIN cluster_capacity cc
ORDER BY rr.largest_range_mb DESC;Output:
required_3x: Minimum free space (3× largest range)required_4x_recommended: Recommended free space (4× for production safety margin)cluster_min_free_mb: Minimum free space across all nodesrisk_assessment: SAFE / MARGINAL / UNSAFE
Decision matrix:
- SAFE: Proceed with schema change
- MARGINAL: Proceed with caution, monitor disk usage closely during backfill
- UNSAFE: Add capacity before proceeding (or drop unused indexes first)
CRITICAL: Uses DETAILS and joins with cluster capacity. Run on targeted tables only.
Query 4: Multi-Index Comparison (Prioritization)
-- Compare storage requirements across multiple existing indexes
-- Use case: Planning to create index on table with 3+ existing indexes
SELECT
index_name,
COUNT(*) AS range_count,
ROUND(SUM((span_stats->>'approximate_disk_bytes')::BIGINT) / 1073741824.0, 2) AS total_size_gb,
MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0 AS largest_range_mb,
ROUND((MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0) * 3, 2) AS required_free_mb
FROM [SHOW RANGES FROM TABLE your_table_name WITH DETAILS]
GROUP BY index_name
ORDER BY required_free_mb DESC;Use case: Identify which existing index has largest range (determines worst-case for new index creation).
Interpretation:
- New index will likely have similar largest range as largest existing index
- Use worst-case (highest
required_free_mb) for capacity planning
Example scenario:
- Table has 3 indexes: primary (50MB largest), idx_a (70MB largest), idx_b (90MB largest)
- Creating 4th index: assume 90MB largest range → 270MB required free space
CRITICAL: Aggregates DETAILS across all indexes. Use only on specific tables.
See storage-calculations reference for detailed examples and decision trees.
Storage Calculation Workflows
Workflow 1: Single-Index Backfill Estimation
Scenario: Planning CREATE INDEX on large table (e.g., 200GB table, adding idx_customer).
Steps:
- Check current free disk space: Run cluster capacity query (Prerequisites section)
- Find largest range in existing indexes: Run Query 2 on target table
- Calculate 3× requirement: Multiply largest range by 3 (or 4 for production margin)
- Compare required vs. available: Use Query 3 decision logic
- Execute or defer: Proceed if SAFE, add capacity if UNSAFE
Example walkthrough:
-- 1. Check free space (run first)
SELECT MIN((capacity - used) / 1073741824.0) AS min_free_gb
FROM crdb_internal.kv_store_status;
-- Result: 150GB minimum free
-- 2. Find largest range
SELECT MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0 AS largest_mb
FROM [SHOW RANGES FROM TABLE orders WITH DETAILS];
-- Result: 75MB largest range
-- 3. Calculate requirement
-- 3× = 225MB, 4× = 300MB
-- 4. Decision
-- 150GB free >> 300MB required → SAFE, proceed with CREATE INDEXWorkflow 2: Multi-Index Backfill Planning
Scenario: Planning to create 3 new indexes on same table sequentially.
Steps:
- Analyze existing indexes: Run Query 4 to compare storage profiles
- Estimate new index sizes: Assume similar to largest existing index (conservative)
- Prioritize by impact: Create smaller indexes first (faster backfill, less risk)
- Monitor between operations: Check disk usage after each CREATE INDEX before starting next
Example:
-- Analyze existing indexes
SELECT index_name, MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0 AS largest_mb
FROM [SHOW RANGES FROM TABLE products WITH DETAILS]
GROUP BY index_name;
-- Results:
-- primary: 80MB → 240MB required
-- idx_category: 60MB → 180MB required
-- idx_supplier: 90MB → 270MB required (worst case)
-- Plan: Assume new indexes will have 90MB largest range → 270MB each
-- Create indexes sequentially, monitor disk after eachWorkflow 3: Capacity Validation Before ALTER PRIMARY KEY
Scenario: Changing primary key (full table rewrite, highest storage risk).
Steps:
- Understand scope: ALTER PRIMARY KEY rewrites entire table (all indexes)
- Calculate total storage: Sum all index sizes (Query 1)
- Find worst-case range: Largest range across all indexes (Query 2)
- Apply 3× rule conservatively: Use 4× multiplier for ALTER PRIMARY KEY operations
- Validate capacity: Ensure 4× largest range available, plus buffer for MVCC overhead
CRITICAL: ALTER PRIMARY KEY is highest-risk DDL for storage exhaustion. Always use 4× multiplier.
Example:
-- Total table size
SELECT SUM((span_stats->>'approximate_disk_bytes')::BIGINT) / 1073741824.0 AS total_gb
FROM [SHOW RANGES FROM TABLE critical_table WITH DETAILS];
-- Result: 500GB
-- Largest range across all indexes
SELECT MAX((span_stats->>'approximate_disk_bytes')::BIGINT) / 1048576.0 AS largest_mb
FROM [SHOW RANGES FROM TABLE critical_table WITH DETAILS];
-- Result: 100MB
-- Requirement: 4× 100MB = 400MB minimum
-- Recommendation: Add 20% buffer → 480MB recommended free space per nodeSafety Considerations
DETAILS Option Performance Impact
Resource usage:
- CPU: Computes span statistics on-demand (aggregates across KV layer)
- Memory: Proportional to range count returned
- Timeout risk: Tables with 5000+ ranges may timeout without LIMIT
Mitigation strategies:
- Pre-check range count: Run
SELECT COUNT(*) FROM [SHOW RANGES FROM TABLE t]first - Use LIMIT for exploration: Start with
LIMIT 100, remove only if needed - Target specific tables: Never run cluster-wide
SHOW RANGES WITH DETAILS - Production timing: Schedule during maintenance windows for large tables
- Incremental analysis: Analyze per-index if table has many indexes
See analyzing-range-distribution safety section for complete guardrails.
Production Best Practices
Margin of safety:
- Minimum (3×): Theoretical minimum, use only in non-production
- Recommended (4×): Production standard, accounts for MVCC spikes, GC lag
- Conservative (5×): High-stakes tables (financial, compliance), or during high-traffic periods
Monitoring during backfill:
-- Monitor disk usage every 5 minutes during schema change
SELECT
node_id,
store_id,
ROUND((used::FLOAT / capacity) * 100, 2) AS disk_usage_pct
FROM crdb_internal.kv_store_status
ORDER BY disk_usage_pct DESC;Backfill pause detection:
-- Check for paused schema changes
SELECT
job_id,
job_type,
description,
status,
fraction_completed
FROM crdb_internal.jobs
WHERE job_type = 'SCHEMA CHANGE' AND status = 'paused';If paused: Backfill hit disk threshold. Add capacity or drop unused indexes to free space.
Pre-Execution Checklist
Before running CREATE INDEX / ADD COLUMN / ALTER PRIMARY KEY:
- Run Query 3 to calculate storage safety (SAFE / MARGINAL / UNSAFE)
- Verify 4× margin available (not just 3×) for production tables
- Check for unused indexes to drop (frees space immediately)
- Schedule during maintenance window or low-traffic period
- Set up disk usage monitoring alerts (85% threshold)
- Prepare rollback plan (schema change cancellation steps)
- Validate table statistics are up-to-date (
SHOW STATISTICS FOR TABLE t)
If UNSAFE: Add capacity first (scale cluster) or drop unused indexes.
Troubleshooting
| Issue | Cause | Fix |
| Query timeout with DETAILS | Too many ranges, no LIMIT | Add LIMIT 100, target specific index |
Zero approximate_disk_bytes | Empty table or statistics lag | Run CREATE STATISTICS or wait for auto-stats collection |
| Unexpected high storage values | Includes MVCC versions, old data | Normal; use live_bytes for current data estimate |
| Permission denied | Missing ZONECONFIG or admin privilege | Grant ZONECONFIG: GRANT SYSTEM ZONECONFIG TO user |
min_free_mb shows negative value | Disk usage > capacity (critical!) | Immediate capacity expansion required |
| Backfill paused mid-operation | Disk usage exceeded threshold | Add capacity, or drop unused indexes to free space |
| Different values between nodes | Non-uniform data distribution | Use MIN(free space) for worst-case planning |
| Largest range > 512MB | Custom range_max_bytes or split lag | Investigate zone config; 3× rule still applies |
Key Considerations
- 3× rule applies to largest range, not average: Always use MAX(range size), not AVG
- Per-index analysis: Each index has different range sizes; use worst-case
- 4× for production: Conservative margin accounts for MVCC spikes, GC lag
- ALTER PRIMARY KEY = highest risk: Full table rewrite, use 4-5× multiplier
- Empty tables have zero risk: Storage estimation only applies to tables with data
- DETAILS option is expensive: Always use LIMIT, target specific tables
- Backfill pauses automatically: CockroachDB prevents disk exhaustion by pausing, but delays schema changes
- Monitor during execution: Check disk usage every 5-10 minutes during long backfills
- Statistics accuracy:
approximate_disk_bytesincludes MVCC overhead; use for conservative estimates
References
Skill references:
- Detailed storage calculations and examples
- RBAC and privileges setup (shared with analyzing-range-distribution)
Official CockroachDB Documentation:
- Online Schema Changes
- SHOW RANGES
- CREATE INDEX
- ALTER TABLE
- Architecture: Storage Layer
- ZONECONFIG privilege
Related skills:
- analyzing-range-distribution - For ongoing range health monitoring and leaseholder analysis
- profiling-statement-fingerprints - For query performance analysis
- triaging-live-sql-activity - For real-time query triage
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

