Database / CockroachDB Interview questions
Last updated
1. What is CockroachDB?
CockroachDB is a distributed SQL database built to scale horizontally and keep running when machines, zones, or even whole regions fail. You talk to it with standard SQL over the PostgreSQL wire protocol, but underneath, data is split into pieces and replicated across many nodes.
It gives you ACID transactions with serializable isolation by default, so you do not have to trade consistency for scale the way many NoSQL stores force you to.
Typical uses are financial systems, order management, SaaS back ends, and anything that needs a relational model with strong correctness and multi-region reach.
Take quiz
An in-memory cache for key lookups
A document store with eventual consistency
A graph database for relationship queries
A distributed SQL database with ACID transactions
The PostgreSQL wire protocol
The Redis RESP protocol
The MongoDB wire protocol
The Cassandra native protocol
2. What are the key features of CockroachDB?
The headline features are scale, survivability, and strong consistency, all behind a familiar SQL interface.
- Horizontal scaling: add nodes and data rebalances automatically.
- Replication and self-healing: three copies of every range by default, using Raft.
- Serializable ACID transactions across nodes.
- PostgreSQL compatibility for drivers and ORMs.
- Multi-region support with locality-aware tables and survival goals.
- Online schema changes without blocking reads or writes.
- Change data capture through changefeeds.
In interviews, tie each feature to a problem it solves: Raft replication answers node failure, range splitting answers growth, and serializable isolation answers correctness under concurrency.
Take quiz
Manual table partitioning scripts
Replacing the cluster with a bigger single server
Horizontal scaling with automatic rebalancing
Shutting the cluster down to reshard
Gossip-based eventual sync
The Raft consensus protocol
Nightly rsync jobs
Two-phase locking on a master node
3. Is CockroachDB compatible with PostgreSQL?
Largely yes. CockroachDB speaks the PostgreSQL wire protocol and supports most of the PostgreSQL SQL dialect, so drivers such as pgx, psycopg, and the PostgreSQL JDBC driver, plus many ORMs, work with small or no changes.
It is not a drop-in clone though. Some PostgreSQL features and extensions are missing or behave differently, and the default isolation level is SERIALIZABLE, which means your application must be ready to retry transactions that hit conflicts.
Always check the docs for your version before migrating a feature-heavy PostgreSQL schema.
Take quiz
CockroachDB does not support SQL
Serializable isolation can require transaction retries
It needs a proprietary client library only
It cannot store integers
PostgreSQL-compatible drivers
Only ODBC drivers for SQL Server
Only Oracle OCI drivers
Only drivers shipped by Cockroach Labs for Go
4. What are the main components of a CockroachDB cluster?
A cluster is made of nodes, each running the same cockroach binary. Every node holds one or more stores (disks) and can serve SQL.
Data is divided into ranges, and each range has several replicas placed on different nodes. One replica holds the lease (the leaseholder) and coordinates reads and writes for that range.
The node a client connects to is the gateway node. Nodes also share cluster state using a gossip protocol.
The five terms to remember: node, range, replica, leaseholder, gateway.
Take quiz
Certificate
Gateway
Range
Gossip packet
The gateway node
The load balancer
The decommissioned node
The node with the largest disk
5. What is a range in CockroachDB?
A range is a contiguous slice of the sorted key-value space that stores table and index data. CockroachDB starts with few ranges and splits a range in two once it grows past the maximum size, which defaults to 512 MiB.
Each range is replicated, three times by default, and the replicas form their own Raft group. That is what makes ranges the unit of replication, rebalancing, and failover.
You can inspect them with:
SHOW RANGES FROM TABLE orders;
Ranges also split under heavy traffic (load-based splitting) and merge when they shrink, so the range count tracks both data size and load.
Take quiz
64 KiB
1 TiB
10 GiB
512 MiB
All tables in one database
Every SQL session
The replicas of one range
All nodes in the cluster for all data
6. What is a leaseholder?
The leaseholder is the replica of a range that holds the range lease. It serves all reads for the range and proposes writes through Raft, so reads do not need a quorum round trip.
The lease is time-bound and can move to another replica when a node fails or when the system wants it closer to the traffic. The leaseholder is usually, but not always, the same replica as the Raft leader.
When you hear "hot leaseholder", it means one node is serving a disproportionate share of a range's traffic.
Take quiz
Any random replica every time
The newest replica
The gateway node only
The leaseholder
The cluster restarts
Queries wait until the node returns
The range is deleted
It expires and another replica acquires it
7. How do you start a local CockroachDB cluster?
For quick experiments, a single insecure node is enough:
cockroach start-single-node --insecure --listen-addr=localhost:26257
For a real multi-node cluster, start each node with --join pointing at the others, then initialize once:
cockroach start --insecure --store=node1 --listen-addr=localhost:26257 --http-addr=localhost:8080 --join=localhost:26257,localhost:26258,localhost:26259 cockroach init --insecure --host=localhost:26257
Production clusters should use TLS certificates instead of --insecure. Also, cockroach demo spins up a temporary in-memory cluster with a SQL shell.
After starting, connect with cockroach sql --insecure and open the DB Console at http://localhost:8080 to confirm all nodes are live.
Take quiz
cockroach merge
cockroach seed
cockroach format
cockroach init
Which table to join first
Which SQL dialect to use
Which addresses to contact to find the cluster
Which backup file to load
8. What isolation level does CockroachDB use by default?
The default is SERIALIZABLE, the strongest standard SQL isolation level. Concurrent transactions behave as if they ran one at a time, which rules out anomalies like write skew.
The price is that conflicting transactions may be aborted with a retryable error (SQLSTATE 40001), so clients must retry.
Newer versions also offer READ COMMITTED when enabled through a cluster setting, which cuts retries for workloads that can tolerate weaker guarantees.
This default is stricter than PostgreSQL, which uses READ COMMITTED. It is a common point of surprise when porting applications.
Take quiz
Eventual consistency
SNAPSHOT with no conflict checks
READ UNCOMMITTED
SERIALIZABLE
40001
23505
08006
42P01
9. What is the default replication factor?
The default is 3, so every range is stored on three nodes. A range stays available as long as a majority, two of the three, can talk to each other.
You can change it per database, table, or index through zone configurations:
ALTER TABLE payments CONFIGURE ZONE USING num_replicas = 5;
Five replicas tolerate two simultaneous failures, at the cost of more storage and slightly more write work.
Odd numbers are the sensible choice. Four replicas still need three for quorum, so they tolerate one failure, the same as three.
Take quiz
Three
None
One
Two
Add a CHECK constraint
Edit its zone configuration
Drop and recreate the cluster
Rename the table
10. What is MVCC in CockroachDB?
MVCC (multi-version concurrency control) means CockroachDB never overwrites a value in place. Each write adds a new version tagged with a timestamp, and readers see the version valid at their own timestamp.
The effect is that reads do not block writes and writes do not block reads. It also powers AS OF SYSTEM TIME queries and consistent backups.
Old versions pile up, so a background garbage collector removes versions older than the gc.ttlseconds setting, 4 hours by default.
Deletes work the same way: a delete writes a tombstone version, and the old data stays readable by older timestamps until garbage collection removes it.
Take quiz
Share one global lock
Skip timestamps entirely
Proceed without blocking each other in most cases
Bypass Raft
4 seconds
30 days
Forever, no GC
4 hours
11. How do you create a table in CockroachDB?
The syntax is standard SQL. A good habit is to pick a primary key that spreads writes, such as a UUID:
CREATE TABLE orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), customer_id UUID NOT NULL, total DECIMAL(10,2) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), INDEX idx_customer (customer_id) );
Every table has a primary key. If you do not declare one, CockroachDB adds a hidden rowid column and uses it.
You can add secondary indexes inline as shown, or later with CREATE INDEX. Check the result with SHOW CREATE TABLE orders;.
Take quiz
The table becomes read-only
The table creation fails
A hidden rowid column is used
Rows are stored unordered with no key
uuid_sequential_only()
rand_int8()
nextval_uuid_seq()
gen_random_uuid()
12. What is a zone configuration?
A zone configuration controls how ranges are stored: replica count, placement constraints, lease preferences, range size limits, and the GC TTL. You can set one for the cluster, a database, a table, an index, or a partition, and the most specific one wins.
ALTER TABLE audit_log CONFIGURE ZONE USING num_replicas = 5, gc.ttlseconds = 86400, constraints = '[+region=us-east1]';
In multi-region databases, many of these settings are managed for you through high-level SQL, so hand-written zone configs are usually reserved for special cases.
Take quiz
sql.defaults.tls
gc.ttlseconds
num_voters_only
range_min_ms
The cluster default always
The table's own config
Whichever was created first, regardless of scope
The database config always
13. What is AS OF SYSTEM TIME?
AS OF SYSTEM TIME is a clause that runs a read at a timestamp in the past. Since CockroachDB keeps old MVCC versions, it can show the data as it looked then, as long as that time is within the GC TTL.
SELECT * FROM orders AS OF SYSTEM TIME '-10m' WHERE customer_id = $1;
It is useful for undoing mistakes, auditing, long reports that should not block writers, and follower reads, where slightly stale data is read from the nearest replica for lower latency.
Take quiz
Read data as it existed at an earlier time
Change the server clock
Disable MVCC
Write to a past timestamp
The GC TTL of the data
The number of columns
The client driver version
The size of the primary key
14. What is a changefeed?
A changefeed streams row-level changes from tables to an external sink, so downstream systems can react to inserts, updates, and deletes without polling. It is CockroachDB's change data capture (CDC) feature.
CREATE CHANGEFEED FOR TABLE orders INTO 'kafka://broker:9092' WITH updated, resolved = '10s';
Supported sinks include Kafka, webhooks, cloud storage, and Google Pub/Sub. The resolved option emits timestamps that tell consumers everything before that time has been delivered.
Changefeeds deliver changes at least once, so consumers should be idempotent. They also resume from a checkpoint after node failures instead of starting over.
Take quiz
TLS certificates
Query plans only
Row-level changes from tables
Raft heartbeats
Timestamps guaranteeing earlier changes were delivered
The table schema DDL
Node IDs
Backup file names
15. What are the types of indexes in CockroachDB?
CockroachDB supports several index types, each with a clear job:
- Primary index: stores the table rows themselves, ordered by primary key.
- Secondary index: speeds lookups on other columns; can be
UNIQUE. - Covering index: uses
STORINGto include extra columns and avoid an index join. - Partial index: indexes only rows matching a
WHEREpredicate. - Expression index: indexes the result of an expression such as
lower(email). - Inverted index: for JSONB, arrays, and spatial data.
- Hash-sharded index: spreads sequential keys to avoid hotspots.
Take quiz
Hash-sharded index only
Primary index only
Bloom index
Inverted index
Indexes only rows that satisfy a predicate
Indexes half of each column
Skips NULL types entirely
Stores the index on a single node
16. How does CockroachDB achieve high availability?
High availability comes from replication plus consensus. Every range has multiple replicas on different nodes, and Raft lets the survivors elect a new leader and keep serving as long as a majority is reachable.
When a node fails, its leases expire and move to surviving replicas within seconds. If the node stays gone for about five minutes by default, the cluster re-creates its replicas elsewhere to restore the replication factor.
You choose the failure domain you want to survive with locality flags and survival goals: node, zone, or region.
Take quiz
A DNS update by the client
A standby read-only mirror
A Raft majority electing and serving
A manual failover script
5 hours
5 minutes
5 days
5 milliseconds
17. How does Raft consensus work in CockroachDB?
Each range is its own Raft group. One replica is the leader and receives proposed writes. It appends them to its log, sends them to followers, and once a majority has persisted the entry, the write is committed and applied to the storage engine.
If the leader becomes unreachable, followers hold an election, and a replica with an up-to-date log wins. Terms prevent two leaders from acting at once.
sequenceDiagram
participant L as Leader replica
participant F1 as Follower 1
participant F2 as Follower 2
L->>F1: AppendEntries (write)
L->>F2: AppendEntries (write)
F1-->>L: Ack
Note over L: Majority reached, commit
L-->>F2: Commit index update
Because thousands of ranges run independent Raft groups, the load spreads across the cluster rather than funnelling through one leader.
Take quiz
When the leader flushes its cache
When a majority of replicas have persisted it
When every replica has applied it
When the client closes the connection
A single global mutex file
Raft terms and majority votes
A cron job
SQL isolation levels
18. Explain the architecture layers of CockroachDB?
CockroachDB is built as a stack of layers, each with one responsibility:
- SQL layer: parses and optimizes SQL, then turns it into key-value operations.
- Transactional layer: provides ACID guarantees using MVCC, write intents, and timestamps.
- Distribution layer: maps keys to ranges and routes requests to the right leaseholder.
- Replication layer: keeps replicas in sync through Raft.
- Storage layer: persists data on disk using the Pebble LSM engine.
flowchart TD
A["SQL layer"] --> B["Transactional layer"]
B --> C["Distribution layer"]
C --> D["Replication layer - Raft"]
D --> E["Storage layer - Pebble"]
The key idea is that tables become sorted key-value pairs, and everything below the SQL layer only sees keys and values.
Take quiz
SQL layer
Gateway layer
Distribution layer
Storage layer using Pebble
Distribution layer
Replication log compactor
Storage layer
Parser
19. Explain the lifecycle of a write in CockroachDB?
Take INSERT INTO orders ... sent to some gateway node. The gateway plans the statement and converts the row into key-value writes. It then sends the write to the leaseholder of the target range.
The leaseholder checks for conflicts, assigns a timestamp, writes a provisional write intent, and proposes the change through Raft. Once a majority of replicas have it in their log, it is durable.
On commit, intents are resolved into regular committed values.
sequenceDiagram
participant C as Client
participant G as Gateway
participant LH as Leaseholder
participant R as Other replicas
C->>G: INSERT
G->>LH: KV write batch
LH->>R: Raft proposal
R-->>LH: Majority ack
LH-->>G: Write done
G-->>C: Success
If the data spans several ranges, each range follows the same path and the transaction protocol ties them together.
Take quiz
A write intent
A DNS record
A final committed value only
A TLS session ticket
At the node with the oldest clock
At the leaseholder of the range
At a separate master server
At the client driver
20. Explain the execution flow of a read query?
The client sends a SELECT to a gateway node. The gateway parses it, the cost-based optimizer picks a plan using table statistics, and the plan is turned into key-value reads.
Requests go to the leaseholders of the ranges involved. Each leaseholder reads from Pebble at the transaction's timestamp, checks for conflicting write intents, and handles the uncertainty interval caused by possible clock offset.
flowchart LR
A[Client] --> B["Gateway: parse and optimize"]
B --> C["Leaseholders of needed ranges"]
C --> D["Pebble read at timestamp"]
D --> B
B --> A
For large scans or aggregations, DistSQL pushes filtering and partial aggregation to the nodes holding the data so less crosses the network.
Take quiz
Table statistics and a cost model
Random selection
Only the table name
The client's IP address
It runs filters and aggregations near the data
It skips MVCC
It disables the optimizer
It copies all rows to the gateway first
21. How does CockroachDB handle distributed transactions?
It uses an MVCC-based protocol built on write intents and a transaction record. As a transaction writes, each change is stored as a provisional intent pointing at the transaction record, which lives on the range of the first write.
On commit, the record is marked committed. After that, the intents are resolved into normal values, often asynchronously. Any reader or writer that runs into an intent checks the record to learn whether to wait, push, or ignore it.
No global lock manager or central coordinator is involved, which is why this scales across ranges and regions.
Isolation comes from timestamps. If a transaction's timestamp has to be pushed forward, it first tries a read refresh and only restarts if the refresh finds a newer conflicting write.
Take quiz
On the client machine
On a dedicated global coordinator
In the DB Console
On the range of the transaction's first write
Deletes the intent immediately
Checks the transaction record to decide how to proceed
Restarts the node
Ignores all intents blindly
22. What are parallel commits?
Parallel commits is an optimization that cuts commit latency from two consensus round trips to roughly one. The transaction record is first written in a STAGING state at the same time as the final batch of writes, instead of waiting for those writes to finish.
The transaction is implicitly committed as soon as all its writes succeed, so the client can be told "committed" right away. The record is flipped to COMMITTED afterwards, in the background.
If the coordinator dies mid-way, other transactions can check whether all intents landed and finish the job.
The trade-off is a little more bookkeeping during recovery, but the cost is paid only when a coordinator fails, while every commit gets the faster path.
Take quiz
Bigger range sizes
Cheaper TLS handshakes
Lower commit latency
Automatic index creation
RESOLVED_ONLY
DROPPED
STAGING
ARCHIVED
23. What is the difference between CockroachDB and PostgreSQL?
Both speak SQL and the PostgreSQL wire protocol, but their designs target different problems.
| Aspect | PostgreSQL | CockroachDB |
| Architecture | Single primary with optional replicas | Multi-active, shared-nothing cluster |
| Scaling | Mostly vertical; sharding by hand | Horizontal, automatic rebalancing |
| Default isolation | READ COMMITTED | SERIALIZABLE |
| Failover | Needs tooling or manual steps | Built in via Raft |
| Sequences | SERIAL uses a sequence | SERIAL defaults to unique_rowid() |
| Extensions | Very large ecosystem | Smaller; some features built in |
Pick PostgreSQL for a single-node, extension-heavy workload. Pick CockroachDB when you need scale-out writes, automatic failover, or multi-region survival.
Migration effort is mostly in the details: retry logic, key design, and any extension or stored-procedure features your schema depends on.
Take quiz
CockroachDB
Neither supports SQL isolation levels
PostgreSQL
Both identically with READ COMMITTED
PostgreSQL with no extra tooling
CockroachDB
Neither
Only a standby cron job
24. What is the difference between CockroachDB and Google Spanner?
Both are globally distributed SQL databases inspired by similar ideas, but they differ in how they get time and where they run.
| Aspect | Spanner | CockroachDB |
| Clock source | TrueTime (GPS and atomic clocks) | Hybrid logical clocks on commodity hardware |
| Where it runs | Google Cloud only | Any cloud, on-premises, or self-hosted |
| Wire compatibility | Own APIs, PostgreSQL interface available | PostgreSQL wire protocol |
| Consistency | External consistency | Serializable, with uncertainty-interval handling |
The deployment portability of CockroachDB is often the deciding factor, while Spanner's TrueTime gives it tighter timing guarantees.
Neither is simply better. Teams already committed to Google Cloud may prefer Spanner, while multi-cloud or on-prem needs point toward CockroachDB.
Take quiz
Client-supplied timestamps
TrueTime hardware
A single global atomic clock
Hybrid logical clocks
Only inside Google data centers
Only on managed Kubernetes in one cloud
On any cloud or on-premises
Only on mainframes
25. Why does CockroachDB use hybrid logical clocks?
A hybrid logical clock (HLC) combines a physical wall-clock time with a logical counter. It gives timestamps that are close to real time, but still preserve causality: if event A caused B, B always gets a larger timestamp.
That matters because CockroachDB cannot assume perfectly synced clocks across nodes. HLCs let nodes adjust when they see a message with a later timestamp, and the transaction layer handles the small remaining uncertainty with uncertainty intervals and read refreshes.
The result is ordered MVCC versions without specialized time hardware.
When a read encounters a value with a timestamp inside its uncertainty window, it cannot tell if that write happened before or after, so the transaction restarts at a later timestamp to stay correct.
Take quiz
Epoch and TLS serial
Physical time and a logical counter
Date and username
Node ID and table ID
Compressing SSTables
Ordering events without perfectly synchronized clocks
Encrypting inter-node traffic
Choosing replication factor
26. What happens when clock offset exceeds the allowed limit?
The default maximum clock offset is 500 ms. Each node compares its clock with its peers, and if it detects an offset of more than 80% of that limit (400 ms) against at least half of the other nodes, it shuts itself down.
This is deliberate. Running with badly drifted clocks could break the consistency guarantees, so the node removes itself instead of risking stale or out-of-order reads.
The fix is to keep clocks synchronized with NTP or a cloud time service such as chrony, and to monitor offset in the DB Console.
On cloud VMs, prefer the provider's built-in time source, which typically stays far inside the limit, and alert well before offsets approach 400 ms.
Take quiz
50 minutes
5 seconds
5 ms
500 ms
It terminates itself to protect consistency
It keeps serving and logs a warning only
It becomes the Raft leader for all ranges
It rewrites its timestamps to zero
27. Why do transactions fail with error 40001 and how do you handle it?
Error 40001 (restart transaction) means CockroachDB could not keep a transaction serializable, usually due to a read-write conflict with another transaction or because its timestamp had to move forward.
It is not a bug. It is an instruction to retry the whole transaction. Simple single statements are retried automatically, but multi-statement transactions need client logic.
for attempt in range(max_retries): try: with conn.cursor() as cur: cur.execute("BEGIN") # ... statements ... cur.execute("COMMIT") break except SerializationFailure: # SQLSTATE 40001 conn.rollback() time.sleep(backoff(attempt))
Keep transactions short, avoid user think-time inside them, and use SELECT ... FOR UPDATE on hot rows to reduce conflicts.
Take quiz
Commit anyway
Retry the entire transaction
Ignore it and continue
Drop the table
Disabling indexes
Holding transactions open for user input
Keeping transactions short
Using longer transactions with more statements
28. Why should you avoid sequential primary keys in CockroachDB?
Data is stored sorted by key, so a steadily increasing key makes every new insert land on the last range. That range's leaseholder becomes a hotspot, while the rest of the cluster sits idle, and throughput stops scaling with node count.
Better choices are random UUID keys, composite keys that start with a well-distributed column (such as tenant or customer ID), or a hash-sharded primary key when you must keep time-ordered access.
Timestamp-leading indexes cause the same trouble and often need USING HASH.
Symptoms include one node with high CPU while others idle, and write latency that stays flat when you add nodes. The Hot Ranges page makes this easy to spot.
Take quiz
All inserts hit the same range, creating a hotspot
They break SQL parsing
They prevent replication
They disable MVCC
An ever-increasing counter
Random UUIDs
Current timestamp as the first column
An alphabetically sorted name
29. What is the difference between UUID and SERIAL primary keys?
The two behave differently under load, even though both are unique.
| Aspect | UUID | SERIAL |
| Generation | gen_random_uuid(), random | unique_rowid() by default, timestamp-based |
| Write distribution | Even across ranges | Tends to cluster, can create hotspots |
| Size | 16 bytes | 8 bytes (INT8) |
| Readability | Hard to read | Compact numbers |
| Gaps and order | No order | Not contiguous, roughly time-ordered |
Use UUID for write-heavy distributed tables. If you need small numeric IDs, SERIAL is acceptable at modest write rates, or use a cached sequence for lower per-ID cost.
If you need ordering by creation time, store a separate timestamp column and index it, preferably with hash sharding, rather than relying on the key.
Take quiz
A date-only key
UUID
Auto-increment starting at 1
SERIAL with unique_rowid()
A hash of the row data
A strict gap-free counter
unique_rowid(), not a contiguous sequence
A random UUID
30. How do hash-sharded indexes prevent hotspots?
A hash-sharded index adds a hidden shard column computed from a hash of the indexed columns, and puts it first in the index key. Rows with adjacent values, such as consecutive timestamps, end up in different buckets and therefore different ranges.
CREATE INDEX idx_events_ts ON events (created_at) USING HASH; CREATE TABLE logs ( ts TIMESTAMPTZ NOT NULL, msg STRING, PRIMARY KEY (ts) USING HASH );
The trade-off is that range scans over the original column now fan out across buckets, so use it when write throughput matters more than ordered scans.
The bucket count is configurable. More buckets spread writes further but make ordered range scans touch more shards, so size it to your write rate rather than maximizing it.
Take quiz
A TLS certificate
A second copy of the table
A hidden shard column computed from a hash
A foreign key
Reads stop working
Range scans touch several buckets
Writes become single-node only
Uniqueness is dropped
31. How do you troubleshoot a hot range?
Start by confirming it. In the DB Console, open the Hot Ranges page to see which ranges, and which nodes, have the highest queries per second or CPU use.
- Map the range to a table or index with
SHOW RANGES FROM TABLE t WITH DETAILS; - Check the access pattern: sequential keys, a single popular row, or a timestamp-leading index.
- Fix the cause: switch to UUIDs, add
USING HASH, reorder composite key columns, or cache hot reads in the application. - For a known one-time load, pre-split with
ALTER TABLE ... SPLIT AT.
Load-based splitting already divides busy ranges, but it cannot help when the load targets a single key.
Take quiz
The Licence page
The Hot Ranges page in DB Console
The CREATE TABLE docs
The Certificates page
A single key cannot be split into smaller ranges
It is disabled by default forever
Splits only occur on Sundays
Raft prohibits splits
32. How do you troubleshoot slow queries in CockroachDB?
Work from evidence, not guesses:
- Find the culprit in SQL Activity > Statements, sorted by latency or total time.
- Run
EXPLAIN ANALYZEto see actual rows, time per operator, and network hops. - Look for full table scans, index joins that could be covered, and large sorts.
- Add or adjust indexes, using
STORINGwhere it removes an index join. - Check statistics are fresh. Stale stats lead to bad plans;
CREATE STATISTICSrefreshes them.
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = '...';
Also check the Insights page, which flags slow executions and suggests indexes.
Read the plan from the bottom up. Check estimated versus actual row counts, since a large gap usually points to bad statistics.
Take quiz
DROP STATISTICS
SET TRACING OFF
SHOW TABLES
EXPLAIN ANALYZE
Stale table statistics
Too many comments in SQL
Using uppercase keywords
A short table name
33. How do you troubleshoot transaction contention?
Contention appears as slow transactions, frequent 40001 retries, or latency that grows with concurrency on a few rows.
- Open the Insights page and check for high-contention events, which name the blocking and waiting statements.
- Query
crdb_internal.transaction_contention_eventsto find the contended table, index, and key. - Shorten transactions and remove work that does not need to be inside them.
- Use
SELECT ... FOR UPDATEto take locks early on rows you will modify. - Redesign the hot spot: split a counter row into several rows, or use append-only inserts.
Confirm the fix by watching retry counts and p99 latency afterwards. If they do not drop, contention is probably elsewhere.
Take quiz
SHOW LOCKS ALL OFF
TRUNCATE
SELECT DISTINCT
SELECT ... FOR UPDATE
Make it a TEXT column
Store it in a JSON file
Increase the clock offset
Split it across several rows
34. When should you use follower reads?
Use follower reads when slightly stale data is acceptable and you want low latency. Normally reads go to the leaseholder, which may be in a distant region. A follower read lets the nearest replica answer from a timestamp old enough to be safe.
SELECT * FROM products AS OF SYSTEM TIME follower_read_timestamp() WHERE id = $1;
The timestamp is roughly 4.8 seconds in the past by default. For a staleness bound instead of a fixed offset, use with_max_staleness('10s').
Good fits are catalogs, dashboards, and reference data. Avoid them for read-your-own-write flows such as checking a balance right after a payment.
Take quiz
Higher latency for fresher data
Fewer replicas
Slightly stale data for lower latency
No data at all
Reading a balance immediately after updating it
An analytics dashboard
A product catalog page
Reference lookup tables
35. How does multi-region work in CockroachDB?
You declare regions on the database, and CockroachDB places data accordingly.
ALTER DATABASE app PRIMARY REGION "us-east1"; ALTER DATABASE app ADD REGION "europe-west1"; ALTER DATABASE app ADD REGION "asia-southeast1"; ALTER DATABASE app SURVIVE REGION FAILURE;
Nodes must be started with a matching --locality flag, such as region=us-east1,zone=a. Tables then get a locality: REGIONAL BY TABLE, REGIONAL BY ROW, or GLOBAL. The system adjusts replica and lease placement for you, so local users get low latency and the chosen survival goal is respected.
Once regions are defined, you can inspect placement with SHOW REGIONS FROM DATABASE app; and verify survival settings with SHOW SURVIVAL GOAL FROM DATABASE app;.
Take quiz
--locality
--cloud-vendor
--replica-count
--region-lock
SHARED BY CLOUD and LOCAL ONLY
REPLICATED BY PAGE
ZONAL BY KEY and DEFAULT-NONE
REGIONAL BY ROW and GLOBAL
36. What is the difference between ZONE and REGION survival goals?
A survival goal states which failure the database must tolerate without losing availability.
| Aspect | SURVIVE ZONE FAILURE | SURVIVE REGION FAILURE |
| Tolerates | Loss of one availability zone | Loss of an entire region |
| Minimum regions | 1 or more | 3 or more |
| Write latency | Lower, quorum stays in one region | Higher, quorum spans regions |
| Default | Yes | No, must be set explicitly |
Choose region survival for systems that cannot go down with a regional outage, accepting the cost of cross-region round trips on writes. Zone survival is cheaper and faster for most apps.
Region survival typically places replicas so that a majority sits outside any single region, which is why writes need to cross a region boundary and take longer.
Take quiz
Two
Seven
Three
One
SURVIVE NODE-PAIR
There is no default
SURVIVE REGION FAILURE
SURVIVE ZONE FAILURE
37. What is the difference between REGIONAL BY ROW and GLOBAL tables?
They solve opposite problems.
| Aspect | REGIONAL BY ROW | GLOBAL |
| Data placement | Each row homed in its own region | Replicated for reads in every region |
| Reads | Fast in the row's home region | Fast everywhere |
| Writes | Fast in the row's home region | Slower, coordinated across regions |
| Best for | User or tenant data tied to a location | Mostly-read reference data |
A regional-by-row table uses a hidden crdb_region column to decide each row's home. GLOBAL tables suit configuration, currency rates, and lookup tables that are read much more than written.
You can mix both in one database. A common layout is regional-by-row for users and orders, and GLOBAL for countries, plans, and feature flags.
Take quiz
Unlogged table
REGIONAL BY ROW
GLOBAL
Temporary table
rowid_region_key
node_locality_id
crdb_region
geo_hash_bucket
38. How does CockroachDB rebalance data automatically?
Rebalancing is driven by three background mechanisms working together:
- Splits: a range larger than the max size, or one under heavy load, is split in two.
- Merges: small adjacent ranges are merged to avoid too many tiny ranges.
- Replica and lease rebalancing: an allocator moves replicas and leases toward balanced disk use, replica counts, and query load, while honoring zone constraints.
When you add a node, replicas start moving to it gradually, throttled to protect foreground traffic. You do not run a manual reshard.
You can watch progress through the replica and range graphs in the DB Console. A drop in under-replicated ranges to zero means the cluster is settled.
Take quiz
It is split into two ranges
It is moved to cold storage only
It blocks all writes permanently
It is deleted
Time of day
Client driver version
Balance of load, storage, and zone constraints
Alphabetical node names
39. What happens when a node fails in CockroachDB?
Clients on that node see connection errors and reconnect, usually through a load balancer, to another node. For ranges where the failed node held the lease, the lease expires and another replica takes over within seconds, as long as a Raft majority remains.
Reads and writes then continue. If the node does not come back within roughly 5 minutes (server.time_until_store_dead), the cluster considers it dead and copies its replicas to other nodes to restore the replication factor.
If the node returns sooner, it simply catches up via Raft logs or snapshots.
Applications should use connection pools with retry on connect errors and a load balancer health check on /health?ready=1, so traffic avoids a node that is down or draining.
Take quiz
The data is restored from a tape
The client connects to a read-only mirror
The range waits for a human operator
Another replica acquires the lease
Instantly at the first missed heartbeat
Never, it is manual
After the node is considered dead, about 5 minutes
Only at midnight
40. How do you add or remove nodes safely?
Adding is simple: start a new node with --join pointing at existing nodes and the right --locality. Replicas flow to it automatically.
Removing should be done by decommissioning, not just stopping the process:
cockroach node decommission 4 --certs-dir=certs --host=node1:26257 cockroach node status --decommission --certs-dir=certs --host=node1:26257
Decommissioning moves all replicas off the node first. Wait until its replica count reaches zero, then stop it. Simply killing a node works too but leaves ranges under-replicated until re-replication completes, which raises risk.
Make sure the remaining nodes have enough disk and CPU to absorb the moved replicas. Decommissioning below the replication factor will stall.
Take quiz
cockroach merge --force
cockroach node vanish
cockroach drop node
cockroach node decommission
The next calendar day
A new TLS certificate
Its replica count to reach zero
The DB Console to restart
41. How do you perform a rolling upgrade in CockroachDB?
A rolling upgrade replaces the binary on one node at a time so the cluster stays online.
- Optionally set
cluster.preserve_downgrade_optionto the current version, so you can roll back if needed. - For each node:
cockroach node drain, stop it, replace the binary, restart it, and wait for it to rejoin and be healthy. - Once every node runs the new version, finalize: reset the setting or let auto-finalization run.
RESET CLUSTER SETTING cluster.preserve_downgrade_option;
Until finalization, the cluster still operates in the old feature set, which is what makes rollback possible.
Upgrade one major version at a time and pause between nodes. Draining first lets in-flight queries finish and leases move away cleanly.
Take quiz
Only the leader
All at once
One at a time
Exactly half
All data is duplicated in a second cluster
Nodes lose their stores
The cluster still runs the old feature version
Backups are auto-restored
42. How do you back up and restore a CockroachDB cluster?
Use the built-in BACKUP and RESTORE statements, writing to cloud storage such as S3, GCS, or Azure Blob.
BACKUP INTO 's3://bucket/crdb?AUTH=implicit' AS OF SYSTEM TIME '-10s' WITH revision_history; RESTORE DATABASE app FROM LATEST IN 's3://bucket/crdb?AUTH=implicit'; CREATE SCHEDULE daily_backup FOR BACKUP INTO 's3://bucket/crdb?AUTH=implicit' RECURRING '@daily' FULL BACKUP '@weekly';
Backups are distributed, so every node writes its own part. Using revision_history lets you restore to a point in time within the retained window. Schedules combine periodic full backups with incrementals.
A backup you have never restored is only a hope. Schedule regular restore tests into a scratch cluster and check row counts.
Take quiz
encrypt_none
revision_history
single_node_dump
skip_replicas
Only the system clipboard
Cloud object storage such as S3 or GCS
Only a USB stick
Only the same node's RAM
43. How do you migrate from PostgreSQL to CockroachDB?
Plan it in three stages.
- Convert the schema. Review types, sequences, extensions, and unsupported features. Cockroach Labs' MOLT tooling can assess and convert schemas.
- Load the data. Use
IMPORT INTO,COPY, or MOLT Fetch for bulk loads, and verify counts and checksums afterwards. - Cut over. Optionally keep systems in sync with replication, test application behavior, then switch traffic.
Before cutover, add retry logic for 40001 errors and replace sequential keys with distribution-friendly ones. These two items cause most migration surprises.
Run a trial migration into a staging cluster first, replay representative traffic, and compare query plans and latencies before committing to a date.
Take quiz
Removing all indexes
Rewriting in assembly
Transaction retry logic and distribution-friendly keys
Switching to a new SQL keyword set only
COPY ROWS TO NULL
ALTER IMPORT
IMPORT INTO
LOAD GALAXY
44. When would you choose a STORING index?
Choose STORING when a query filters on one set of columns but also reads a few others. Without it, CockroachDB finds rows in the secondary index and then does an index join back to the primary index for the extra columns, which is an additional lookup that can be on another node.
CREATE INDEX idx_orders_customer ON orders (customer_id) STORING (total, created_at);
The index now covers the query. Reads get faster, but writes cost more and storage grows because those columns are duplicated. Add only the columns that hot queries actually read.
Check EXPLAIN afterwards. If the plan no longer shows an index join for that query, the index is covering and doing its job.
Take quiz
An index join back to the primary index
Raft elections
TLS negotiation
Parsing the SQL
Slower DNS
Fewer replicas
More storage and slower writes
Lower isolation level
45. How does CockroachDB support schema changes without downtime?
Schema changes run as background jobs that move through intermediate states instead of locking the table. For example, adding an index goes through states like delete-only, write-only, and backfill, then becomes public when it is complete and consistent.
At every step, other nodes may be on a version of the schema that differs by at most one state, which keeps concurrent reads and writes correct.
You can watch progress with SHOW JOBS or the Jobs page. Large backfills are throttled to protect traffic. One caution: mixing several schema changes with other statements inside one explicit transaction has limits, so run them separately where possible.
Take quiz
They stop all nodes briefly
They run as background jobs through intermediate states
They lock the table exclusively
They copy the database offline
The Certificates page
SHOW JOBS or the Jobs page
SHOW COLORS
SELECT version()
46. How do you secure a CockroachDB cluster?
Security has several layers, all of which should be on in production.
- TLS everywhere: create a CA and node/client certificates (
cockroach cert) and start nodes without--insecure. - Authentication: use passwords, client certificates, or SSO/JWT where supported.
- Authorization: create roles and grant least-privilege access with
GRANT. - Network controls: restrict the SQL and HTTP ports using firewalls and private networking.
- Encryption at rest and audit logging for compliance needs.
CREATE ROLE app_rw; GRANT SELECT, INSERT, UPDATE ON TABLE orders TO app_rw;
Never run production with --insecure; it disables both encryption and authentication.
Rotate certificates before they expire and monitor expiry dates, as an expired node certificate will stop nodes from talking to each other.
Take quiz
--join
--store
--insecure
--locality
Everyone is admin
Grant ALL to PUBLIC
Share one root password
Least privilege
47. How does the cost-based optimizer work in CockroachDB?
The optimizer takes the parsed query and generates many logically equivalent plans, such as different join orders, join algorithms, and index choices. It estimates the cost of each from table statistics (row counts, distinct values, histograms) and picks the cheapest.
Locality matters too: in multi-region clusters, the cost model accounts for cross-region latency, so it prefers plans that read local data.
Statistics are collected automatically, but after large loads you can refresh them with CREATE STATISTICS. If a plan looks wrong, EXPLAIN shows it, and index hints can serve as a last resort.
Plans are cached for prepared statements, but they are re-optimized when statistics, schema, or placeholders change enough to matter.
Take quiz
The client IP
Table statistics
Node uptime only
The query's comment text
Drop the primary key
Refresh statistics
Reduce replication to one
Restart every client
48. How do you monitor a CockroachDB cluster in production?
Combine the built-in tools with an external stack.
- DB Console: node liveness, replica counts, SQL latency, and capacity.
- Prometheus and Grafana: scrape
/_status/varsfrom each node and build alerts. - Logs and events: use structured logging and send them to your log platform.
- Health endpoints:
/healthand/health?ready=1for load balancer checks.
The signals worth alerting on are node liveness, under-replicated ranges, clock offset, disk usage, and SQL p99 latency.
Alert on trends too: a steady rise in disk use or p99 latency is a capacity warning long before it becomes an outage. Keep dashboards per node and per region.
Review the Insights and Statements pages regularly, not just during incidents, to catch regressions after a deploy.
Take quiz
/index.php
/swagger.json
/_status/vars
/robots.txt only
Under-replicated ranges
Number of table comments
Count of colors in a UI
Size of the README
49. What are the limitations of CockroachDB?
No database fits every job, and CockroachDB has real trade-offs to weigh.
- Write latency: every commit needs a Raft majority, so single-row writes are slower than on a lone PostgreSQL node, especially across regions.
- Retries: serializable isolation means clients must handle
40001. - Compatibility gaps: some PostgreSQL features and extensions are unsupported or differ by version.
- Hotspots: poor key design can cap scaling.
- Operational overhead: clock sync, certificates, and capacity planning still matter.
For a small app that fits comfortably on one server, a single PostgreSQL instance is often simpler and cheaper.
Most of these limits can be managed with good key design, batching, retry logic, and the right locality settings, but they should be weighed honestly during design.
Take quiz
SQL is not supported
Writes are always asynchronous
There is no storage engine
Commits require consensus from a replica majority
A cluster needing automatic failover
A small app that fits on one server
A multi-region active-active system
A global app needing region survival
50. How would you design a CockroachDB deployment for a global application?
Start from the requirements: which failures to survive and where users are.
- Regions: deploy nodes in at least three regions with correct
--localityflags. - Survival goal: set
SURVIVE REGION FAILUREif the app must live through a regional outage. - Table localities:
REGIONAL BY ROWfor user data,GLOBALfor reference data. - Reads: use follower reads for stale-tolerant paths.
- Keys: UUIDs or tenant-prefixed keys to avoid hotspots.
- Operations: NTP, scheduled backups, monitoring, and tested restores.
flowchart LR
U1["Users US"] --> R1["Region us-east"]
U2["Users EU"] --> R2["Region europe-west"]
U3["Users APAC"] --> R3["Region asia-southeast"]
R1 <--> R2
R2 <--> R3
R1 <--> R3
Test failure scenarios before launch, by taking a region offline in staging and measuring recovery.