Distributed SQL in 2026: revisit CockroachDB, YugabyteDB and TiDB before you shard or go multi-region
Picture a mutual fund and stockbroking platform in Mumbai (an invented example, not a real client). Its order book and client ledger run on PostgreSQL 16 with Patroni across three VMs in one cloud region, and a rewards service runs on MySQL 8.0. Two things worry the leadership. At market open the PostgreSQL primary runs close to its write limit, and last quarter a long network problem in their cloud region stopped order entry for forty minutes even though the database itself was healthy. Now one architect wants CockroachDB, a second likes YugabyteDB because it reuses PostgreSQL code, and a third says TiDB fits because the rewards team already speaks MySQL.
All three are distributed SQL databases. They split tables into small pieces, keep three or more copies of each piece with the Raft consensus protocol, and let you add nodes instead of buying a bigger primary. They differ in how close they are to PostgreSQL or MySQL, which isolation level you get by default, how long a write pauses when a node dies, how you upgrade them and, since 2024, what licence you accept. This post compares CockroachDB, YugabyteDB and TiDB on those points, and asks the question that should come first: is plain PostgreSQL with good HA still enough? Facts come from release notes, official docs, licence texts and CVE records, checked on 12 October 2026. The drill near the end is my own run.
The short version
- Distributed SQL buys you horizontal write scaling and survival of a node, zone or region without manual failover. It costs you latency on every write (a Raft round trip), more nodes to run, and compatibility gaps. If one well-sized PostgreSQL primary with Patroni meets your write load and your recovery target, keep it.
- CockroachDB v26.3.2 (18 September 2026) is current; v26.3 is an optional Innovation release and v26.2 is the Regular line. Since v24.3 (November 2024) every new binary is under the CockroachDB Software License, which is source available, needs a licence key for production and is free only for businesses under 10 million US dollars of annual revenue.
- YugabyteDB v2026.1.2.0 (22 September 2026) and LTS v2025.2.7.0 (5 October 2026) are current. The database is Apache 2.0, and its YSQL layer now reuses PostgreSQL 15 code (my drill showed PostgreSQL 15.12 in
version()). YugabyteDB Anywhere, the management product, is not open source. - TiDB v8.5.8 (27 August 2026) is the latest LTS patch, with v7.5.8 on 17 September. TiDB, TiKV and PD are Apache 2.0. It speaks the MySQL protocol, but stored procedures, triggers and SERIALIZABLE isolation are not supported.
- Isolation defaults differ, and they matter for money. In my write-skew drill, CockroachDB’s default SERIALIZABLE blocked an overdraft; YugabyteDB’s default Read Committed and TiDB’s default REPEATABLE READ (snapshot isolation) both let it through until I added
SELECT ... FOR UPDATE. - When I killed the node leading a table’s data, writes paused for 3.9 s on CockroachDB, 3.2 s on YugabyteDB and 9.7 s on TiDB with default settings, and no acknowledged write was lost. CockroachDB also returned one “result is ambiguous” error, which your application must handle with idempotency.
Where things stand on 12 October 2026
Dates are the release dates printed on each project’s release notes. GitHub release timestamps, converted to IST, agree within a day.
Project
Latest release
Release type and support
Licence
CockroachDB
v26.3.2 on 18 September 2026 (v26.3.0 GA on 19 August)
Innovation release: optional, shorter support, can be skipped by self-hosted clusters
CockroachDB Software License (source available, licence key required)
CockroachDB
v26.2.7 on 18 September 2026 (v26.2 GA on 27 April)
Regular release; v26.4, the next Regular release, is expected in Q4 2026
CockroachDB Software License
YugabyteDB
v2026.1.2.0 on 22 September 2026 (series released 29 June)
STS: maintenance until 29 June 2027, end of life 29 December 2027
Apache 2.0
YugabyteDB
v2025.2.7.0 on 5 October 2026 (series released 11 December 2025)
LTS: maintenance until 11 December 2027, end of life 11 June 2028
Apache 2.0
YugabyteDB Anywhere
Ships with each release
Self-managed control plane for universes
Polyform Free Trial License 1.0.0
TiDB
v8.5.8 on 27 August 2026
LTS; v8.5 is the latest LTS series
Apache 2.0
TiDB
v7.5.8 on 17 September 2026
Older LTS series still receiving patches
Apache 2.0
TiDB
v9.0.0-beta.1 on 31 March 2025
Beta only; no 9.0 GA on the self-managed release timeline
Apache 2.0
Date
Event
6 February 2014
CockroachDB repository created on GitHub
6 September 2015
TiDB repository created on GitHub
6 October 2017
YugabyteDB repository created on GitHub
15 August 2024
Cockroach Labs announces it will retire the free Core edition and move self-hosted users to a single Enterprise offering
18 November 2024
CockroachDB v24.3 GA; binaries from this date are under the CockroachDB Software License
19 December 2024
TiDB v8.5.0 (LTS)
31 March 2025
TiDB v9.0.0-beta.1
23 July 2025
YugabyteDB v2025.1, the first stable series based on PostgreSQL 15
11 December 2025
YugabyteDB v2025.2 LTS; Read Committed enabled by default for new universes deployed with yugabyted, YugabyteDB Anywhere or Aeon
27 April 2026
CockroachDB v26.2 GA (Regular)
29 June 2026
YugabyteDB v2026.1.0.0 (STS)
19 August 2026
CockroachDB v26.3.0 GA (Innovation): super regions GA, reports PostgreSQL 18 to clients, DOMAIN types
27 August 2026
TiDB v8.5.8
18 September 2026
CockroachDB v26.3.2 and v26.2.7
22 September 2026
YugabyteDB v2026.1.2.0
5 October 2026
YugabyteDB v2025.2.7.0
6 October 2026
Cockroach Labs advisory on two security vulnerabilities (privilege escalation and SSRF)
First question: do you need distributed SQL at all?
Distributed SQL solves two problems: more writes than one primary can take, and the need to survive the loss of a node, zone or region without a failover step. It does not make queries faster. Every write needs a majority of replicas to agree, so a single-row insert costs at least one network round trip between replicas, and across regions that round trip is tens of milliseconds.
My PostgreSQL HA post covers what Patroni and CloudNativePG can do. Plain PostgreSQL is usually enough when:
- the write load fits one primary with headroom, after the usual work on indexes, batching and connection pooling (my PgBouncer post has the pooling side);
- a failover of tens of seconds and, with asynchronous replication, a small loss window are acceptable, or you accept synchronous replication inside one region;
- region loss can be handled by a standby in a second region that you promote, with a written runbook;
- you depend on PostgreSQL extensions, triggers or exact behaviour that a compatible database may not have.
Consider distributed SQL when the write load is growing past a single primary and sharding by hand looks likely, when the business wants writes to continue through a zone or region failure with no human step, or when data must be pinned to particular regions while still being one logical database. For the Mumbai team, the forty-minute stop was a network problem in one region. Distributed SQL across three regions would address that, but so would a tested cross-region standby with a short runbook. Write down which problem you are solving before you pick a database.
How the three are built
All three separate a SQL layer from a replicated, transactional key-value layer. The details decide where latency comes from and what happens in a failure.
CockroachDB
YugabyteDB
TiDB
Processes
One cockroach binary per node, doing SQL and storage
YB-Master (catalog and placement) and YB-TServer (data and the YSQL/YCQL query layers) per node
TiDB servers (stateless SQL), TiKV (storage), PD (placement and timestamps), optional TiFlash (columnar)
Unit of data
Range, 512 MiB maximum by default (range_max_bytes)
Tablet, split automatically as it grows; hash or range sharding chosen per table
Region; region-split-size default 256 MiB since v8.4.0 (96 MiB before)
Replication
One Raft group per range, three replicas by default
One Raft group per tablet; replication factor 3 is typical
One Raft group per Region; PD itself is a Raft group
Who serves reads and writes
The leaseholder, kept equal to the Raft leader by leader leases
The tablet leader
The Region leader on TiKV, reached through a TiDB server
Timestamps
Hybrid logical clocks on each node with a configured maximum clock offset
Hybrid logical clocks on each node
Timestamp Oracle (TSO) served by the PD leader
SQL dialect
PostgreSQL wire protocol and syntax
YSQL reuses the PostgreSQL 15 query layer; YCQL is Cassandra-compatible
MySQL protocol and syntax
Two consequences are worth stating plainly. First, a TiDB transaction gets its start and commit timestamps from PD, so in a multi-region layout the distance to the PD leader matters. CockroachDB and YugabyteDB avoid a central timestamp service but rely on reasonably synchronised clocks, so clock monitoring becomes part of operations. Second, all three move leaders and replicas automatically. That is convenient, but it means “which node holds my data” changes over time, and your capacity planning must assume any node can become hot.
CockroachDB
A CockroachDB node receives SQL, plans it, and sends key-value requests to the leaseholder of each range involved. Writes are proposed to the Raft leader, which is also the leaseholder. Recent versions use leader leases, and the replication layer page says this makes the cluster more robust against network partitions and node liveness failures than the older epoch-based leases. Ranges split and merge automatically, and the cluster rebalances replicas and leases by load.
YugabyteDB
YugabyteDB’s storage layer, DocDB, splits each table into tablets, and each tablet’s peers form a Raft group. YSQL is a modified PostgreSQL that runs inside every YB-TServer, so a lot of PostgreSQL behaviour, error messages and catalog views come along. YB-Master holds the system catalog and decides placement. Since the v2025.1 stable series, YSQL is based on PostgreSQL 15 instead of PostgreSQL 11.
TiDB
TiDB servers hold no data, so you scale them like an application tier. TiKV stores the data in Regions, each a Raft group, and implements transactions on a Percolator-style model with timestamps from PD. TiFlash adds columnar replicas for analytical queries, which is TiDB’s HTAP pitch. PD schedules Region leaders and replicas and serves the TSO.
Compatibility: “PostgreSQL compatible” and “MySQL compatible” need reading
CockroachDB and PostgreSQL
CockroachDB speaks the PostgreSQL wire protocol, and v26.3 reports PostgreSQL version 18 to clients and aligns its system catalogs with PostgreSQL 18, so current drivers and ORMs complain less. It is not PostgreSQL underneath. The v26.3 compatibility page lists features that are not supported, including PostgreSQL range types, dropping a primary key (every table must have one), XML functions, column-level privileges, XA syntax, creating a database from a template, foreign data wrappers and session-scoped advisory locks (transaction-scoped ones work). For dump tools, only schema-only dumps in plain format are supported: no data dumps, no pg_restore, no pg_dumpall. Plan migrations with CockroachDB’s own tools or with an export and IMPORT, not with your usual PostgreSQL backup files.
YugabyteDB and PostgreSQL
Because YSQL reuses PostgreSQL’s query layer, it is the closest of the three to PostgreSQL in syntax and features. It still is not PostgreSQL storage. A table is hash or range sharded on its primary key (declared as HASH or ASC/DESC), and that choice changes how range scans and ORDER BY on the key perform. Some features that depend on PostgreSQL’s single-node storage behave differently. Test the extensions you rely on against the YugabyteDB docs rather than assuming they load.
If you run an older YugabyteDB based on PostgreSQL 11 (anything before v2.25 or the v2025.1 stable series), the move to PostgreSQL 15 is a YSQL major upgrade with extra steps. The docs say it is fully online, with a mixed mode during the upgrade, and that PITR snapshot schedules must be deleted before you start and recreated afterwards.
TiDB and MySQL
TiDB is “highly compatible” with the MySQL protocol and the common features of MySQL 5.7 and 8.0, and MySQL tools work with it. The compatibility page lists what is missing: stored procedures and functions, triggers, events, user-defined functions, FULLTEXT indexes on self-managed TiDB (the syntax parses), and spatial functions and types. A rewards service that leans on triggers will need code changes. TiDB’s REPEATABLE READ is not MySQL’s REPEATABLE READ either, which the next section shows.
Transactions and isolation: the part that decides correctness
This is where teams get hurt, because the same SQL can give different answers. The classic case is write skew: two transactions read the same rows, each decides an action is safe, and each updates a different row, so neither sees a conflict on the row it writes.
CockroachDB
YugabyteDB (YSQL)
TiDB
Levels offered
SERIALIZABLE and READ COMMITTED
Serializable, Snapshot (requested as REPEATABLE READ) and Read Committed
REPEATABLE READ (snapshot isolation) and READ COMMITTED; SERIALIZABLE returns an error
Default
SERIALIZABLE
Read Committed for new universes from v2025.2 deployed with yugabyted, Anywhere or Aeon; otherwise Read Committed falls back to Snapshot
REPEATABLE READ, which is snapshot isolation
Write skew at the default
Prevented
Allowed
Allowed
What the application must do
Retry on SQLSTATE 40001 (serialization_failure)
Lock rows you read for a decision (FOR UPDATE), or use Serializable and retry
Lock rows with FOR UPDATE; pessimistic mode has been the default for new clusters since v3.0.8
A few details from the docs are worth knowing:
- CockroachDB runs everything at SERIALIZABLE unless you ask for READ COMMITTED, which is enabled by the cluster setting
sql.txn.read_committed_isolation.enabled(true by default). Its READ COMMITTED is stronger than PostgreSQL’s, preventing anomalies within a single statement. In my drill on v26.3.2 with default settings, a transaction that asked for REPEATABLE READ reported its level as serializable. - YugabyteDB documents that before v2025.2, or on manually deployed universes,
yb_enable_read_committed_isolationis false, so the default Read Committed actually runs as Snapshot isolation. Check the flag on your universe rather than assuming; upgraded clusters and new clusters can behave differently. - TiDB says plainly that it implements snapshot isolation and calls it REPEATABLE READ for MySQL compatibility, and that this allows write skew. It also differs from MySQL’s REPEATABLE READ, which does not check whether the current row version is visible when updating.
The practical rule is simple: if a business rule spans rows (“total exposure across all accounts of a client must stay under the limit”, “at least one approver must remain”), either run at SERIALIZABLE and retry, or lock every row that the decision reads. Do not rely on the default of a database you have not tested.
Multi-region: three different toolkits
CockroachDB
YugabyteDB
TiDB
Synchronous across regions
Database regions with a survival goal: ZONE (default) or REGION
Replication factor spread across regions, with placement policies per cloud, region and zone
Placement policies in SQL (primary region, regions, follower counts)
Data pinned to a region
Table localities: REGIONAL BY TABLE (default), REGIONAL BY ROW, GLOBAL; super regions for domiciling
Row-level geo-partitioning with tablespaces
Placement policies attached to tables or partitions
Asynchronous copy
Physical cluster replication and changefeeds
xCluster replication, unidirectional or bidirectional; read replicas
TiCDC to another cluster
Cost to know
Surviving a region failure makes writes slower because of cross-region hops
Synchronous writes across regions pay inter-region Raft latency; v2026.1 adds write pipelining (off by default) to cut multi-region write latency
Every transaction needs timestamps from the PD leader’s region
CockroachDB’s model is the most declarative. You add regions to a database, choose a survival goal and set a locality per table. ALTER DATABASE ... SURVIVE REGION FAILURE keeps writes available through a region loss at the cost of slower writes. REGIONAL BY ROW tables keep each row’s leaseholder in the row’s home region, so a client in Mumbai writes Mumbai rows at local latency. In v26.3, super regions with their own survival goals became generally available. The docs warn that defining secondary regions on a database with REGIONAL BY ROW tables is not supported, so read the limitations before mixing features.
YugabyteDB gives you more building blocks. A stretched universe with replication factor 3 across three regions survives a region loss synchronously. xCluster replicates WAL records asynchronously between two universes, one way or both ways, which suits active-passive disaster recovery and some active-active designs if your application avoids conflicting writes. Read replicas serve stale reads near users without voting in Raft.
TiDB uses Placement Rules in SQL to decide where leaders and followers live, and TiCDC for asynchronous replication to a second cluster. The TSO dependency means you place PD’s leader near most of your writes.
For the Mumbai team, the honest picture is this: three Indian cloud regions are not always available from every provider, and a two-region layout cannot survive the loss of either region synchronously with a majority protocol. Two regions plus a third, small “witness” location, or an asynchronous second cluster with a promotion runbook, are the realistic options.
Operations: upgrades, backups and failures
Upgrades
CockroachDB
YugabyteDB
TiDB
Release rhythm
A major version each quarter, alternating Regular and Innovation
Two stable series a year: the first is STS, the second LTS
LTS series with patch releases; DMRs about every two months without patches
Rolling upgrade
Yes, one node at a time
Yes, one node at a time with rebalancing
Yes, TiUP upgrades components in a fixed order
Skipping versions
A Regular release can go to the next Innovation or skip it to the next Regular
Follow the documented upgrade paths per series
Follow the documented path per version
Rollback
Possible until the major upgrade is finalised; auto-finalisation is on by default, so disable it if you want a rollback window
Possible until you finalise; PITR is unavailable during the upgrade and cannot target a time before it after finalising
The docs say TiDB does not support downgrade or rollback after an upgrade
CockroachDB support follows a fixed policy: a Regular release gets 365 days of maintenance support from GA and 180 more days of assistance support, extended once a patch is designated LTS; an Innovation release gets maintenance support only, for a shorter window. The upgrade guide asks you to read the release notes of any skipped major version and to watch for features that only work after finalisation.
TiDB’s rule is the strictest: there is no rollback after an upgrade, so take a BR backup first and rehearse on a copy. YugabyteDB’s upgrade has a monitoring phase before finalisation, which is a real safety net if you remember that PITR is off during it.
Backups
- CockroachDB:
BACKUPandRESTOREin SQL to object storage, with full and incremental backups and schedules. Since the licence change, these features are available to every licensed user, not only paying Enterprise customers. Two technical advisories in the last year are reminders to test restores: one (August 2025) described backups that could report success without backing up all data when object storage had intermittent write failures, and one (February 2026) described AVROIMPORTsilently ignoring storage read errors. - YugabyteDB: distributed snapshots for backup and restore, snapshot schedules for point-in-time recovery, and SQL or CQL export and import.
- TiDB: BR for snapshot backups plus log backup for point-in-time recovery, and Dumpling for logical exports.
Whatever you choose, a backup you have not restored is a hope, not a backup. For a ledger, restore into a scratch cluster every month and compare row counts and balances.
Failures
A node loss is routine for all three, but there is a pause while leadership moves. The drill below measured 3.9 s for CockroachDB, 3.2 s for YugabyteDB and 9.7 s for TiDB. TiDB’s number matches its defaults: TiKV starts a Raft election roughly after raft-base-tick-interval (1 s) times raft-election-timeout-ticks (10). Size client timeouts and retry budgets for these pauses, and expect ambiguous outcomes: CockroachDB returned “result is ambiguous” for one insert whose commit was in flight when the leaseholder died. My idempotency keys post covers how to make such retries safe.
CockroachDB has also published data-correctness advisories this year, for example a race between MVCC garbage collection and range splits that could, under specific and unlikely conditions, delete live data (February 2026, patched in all supported versions), and partial index corruption during a backfill with concurrent updates (April 2026). Subscribe to the technical advisories of whichever database you run; for a distributed database they matter as much as CVEs.
Kubernetes operators
- CockroachDB: Cockroach Labs now recommends its newer CockroachDB operator for Kubernetes, which it says is easier to deploy and supports scaling multi-region clusters, with migration guides from the older Public operator and Helm deployments.
- YugabyteDB: Helm charts for single-zone, multi-zone and multi-cluster layouts, and YugabyteDB Anywhere as a control plane. In v2026.1, Helm deployments run as non-root by default, and existing non-OpenShift deployments that do not pin a user migrate to non-root on upgrade, so check your
podSecurityContextbefore upgrading. - TiDB: TiDB Operator, with stable docs for v1.6; a 2.x line is under development (v2.2.0-alpha.11 on 17 September 2026).
Running a distributed database on Kubernetes adds a second distributed system to debug. If your team is new to both, start on VMs, learn the failure behaviour, and move later.
Licences and terms
Component
Licence
What it means in practice
CockroachDB binaries from 18 November 2024 (v24.3.0 onward, plus later patches of 23.1 to 24.2)
CockroachDB Software License
Source available. Production needs a licence key. Enterprise Free is free for businesses under 10 million US dollars annual revenue, renewed yearly, with telemetry required. Trial is 30 days. Paid Enterprise can disable telemetry. Offering CockroachDB as a service to third parties needs a paid licence.
Older CockroachDB binaries
BSL 1.1, CockroachDB Community License and others
As before; not a route to new patches
YugabyteDB database
Apache 2.0
Permissive, including the core engine and the modified PostgreSQL in src/postgres
YugabyteDB Anywhere
Polyform Free Trial License 1.0.0
Commercial product for production use
TiDB, TiKV, PD
Apache 2.0
Permissive; TiKV is a CNCF graduated project
CockroachDB’s licensing FAQ is specific about enforcement. A cluster with no licence key has a 7-day grace period from initialisation, after which it is throttled; Free and Trial clusters are throttled after 7 days without sending telemetry; an expired Free licence throttles 30 days after expiry. Throttling limits the cluster to 5 concurrent open SQL transactions, which for a production system is an outage. Single-node clusters for development are not throttled, and clusters patch-upgraded from the old Core licence got a 30-day grace period. My drill cluster logged its grace period end exactly 7 days after initialisation.
Two clauses in the licence text deserve a slow read: telemetry sharing is a condition of use unless your key allows it to be disabled, and if you publish a benchmark you must disclose enough to replicate it. For the Mumbai team, a regulated business, the telemetry clause alone is a conversation for the compliance team. None of this makes CockroachDB a bad database; it makes it a commercial dependency, to be budgeted like one. YugabyteDB and TiDB keep the database itself under Apache 2.0, with the vendors earning from management products, cloud services and support.
Security advisories
Every CVE ID below was checked against the CVE.org API (cveawg.mitre.org) on 12 October 2026 and returned a published record. Affected versions are from those records.
CVE
Product
What it is
Affected
CVE-2025-9276
Cockroach Labs cockroach-k8s-request-cert image
Authentication bypass through an empty root password
The latest image tag
CVE-2025-8865
YugabyteDB
Null pointer dereference in YCQL query handling; an authenticated attacker could crash the tablet server
2024.1.0.0 to before 2024.1.3.0, 2024.2.0.0 to before 2024.2.2.5, 2.20.0.0 to before 2.20.9.0
CVE-2025-8862
YugabyteDB
Diagnostics collection could include sensitive gflag configuration
2024.1.0 to before 2024.1.3, 2.20.0.0 to before 2.20.7.0, 2.23.0.0 to before 2.23.1.0
CVE-2025-8863
YugabyteDB
Diagnostic information sent over HTTP
Same ranges as CVE-2025-8862
CVE-2025-8866
YugabyteDB Anywhere
Missing authentication on the /metamaster/universe API exposed network details
2024.x and 2.20.x; 2025.x unaffected
CVE-2026-1966
YugabyteDB Anywhere
LDAP bind passwords set through gflags shown in cleartext in the UI
2025.1.0.0 to before 2025.1.1.0, 2024.2.0.0 to before 2024.2.6.0; 2025.2.0.0 unaffected
CVE-2022-31011
TiDB
Authentication bypass under certain conditions
5.3.0 only (fixed in 5.3.1)
CVE-2022-3023
TiDB
Externally controlled format string
Before 6.4.0 and 6.1.3
CockroachDB publishes security issues as technical advisories. The most recent, dated 6 October 2026, describes two vulnerabilities, privilege escalation and server-side request forgery, that could let authenticated users gain privileges and reach unauthorised data, affecting versions before v26.2.3, v26.1.6, v25.4.12, v25.2.20, v24.3.34, v24.1.31 and v23.2.31. A May 2026 advisory covered two more privilege escalation issues, fixed in v26.1.2, v25.4.7, v25.2.16, v24.3.30, v24.1.27 and v23.2.30. I did not find CVE IDs attached to these advisories in my search, so track the advisories page itself.
For TiDB, there are also CVE records from 2024 describing crashes in v7.5.1 and v8.1.0 reported by third parties; at least one (CVE-2024-41434) carries a “disputed” tag, and they name no fixed version. GitHub’s security advisories pages for CockroachDB, YugabyteDB and TiKV listed no published advisories when I checked; TiDB’s listed one, the 2022 authentication bypass above.
The bigger risk is usually configuration, not CVEs: an insecure-mode CockroachDB cluster, a YugabyteDB universe without authentication enabled on YSQL, or a TiDB root user with no password (the default in a TiUP playground) exposed beyond a laptop.
Migration paths
From PostgreSQL to CockroachDB or YugabyteDB. Start with a schema scan. For CockroachDB, look for range types, triggers that do heavy work, advisory locks and pg_dump based processes. For YugabyteDB, review primary keys, because the hash or range sharding choice changes range-scan behaviour, and test every extension. Move data with each vendor’s migration tooling or a change data capture pipeline, run both side by side, compare results, then cut over during a quiet window.
From MySQL to TiDB. Check for stored procedures, triggers, events and FULLTEXT indexes first; these need application changes. TiDB’s ecosystem tools cover the data copy and incremental sync. Retest every place that relies on MySQL REPEATABLE READ behaviour.
Between versions. CockroachDB: one major at a time, optionally skipping an Innovation release, and keep auto-finalisation off until you are sure. YugabyteDB: follow the series upgrade path, and plan the YSQL major upgrade if you are still on a PostgreSQL 11 based version. TiDB: back up, then upgrade; there is no rollback.
A decision guide
Your situation
A sensible starting point
Watch out for
Write load fits one primary, failover of tens of seconds is fine
PostgreSQL with Patroni or CloudNativePG
A tested cross-region standby and runbook for region loss
PostgreSQL application, need to scale writes and survive zone loss, want an open source licence
YugabyteDB
Sharding choice for primary keys, extension support, isolation flag on older or manually deployed universes
Multi-region with rows pinned to regions, want declarative locality and SERIALIZABLE by default, budget for a licence
CockroachDB
Licence key and telemetry terms, PostgreSQL feature gaps, finalisation and advisories
MySQL application, mixed transactional and analytical queries, want an open source licence
TiDB with TiFlash
No triggers or stored procedures, snapshot isolation by default, no rollback after upgrade, PD placement
Only analytics is slow
A columnar warehouse or replica
Moving OLTP for an analytics problem
Writes are fine but connection counts are not
A connection pooler
Adding a distributed database for a pooling problem
For the Mumbai team, my suggestion has three steps. First, measure: peak writes per second at market open, the latency budget for order entry, and the recovery time and data loss the business can accept for a node, zone and region failure. If the region incident is the main pain, a PostgreSQL standby in a second region with a rehearsed promotion may solve it this quarter. Second, if writes will outgrow one primary within a year, run a proof of concept with YugabyteDB, the closest to their PostgreSQL code under an open source licence, and with CockroachDB if they value SERIALIZABLE by default and declarative multi-region enough to pay for a licence and accept its telemetry terms. Use their real schema and a recorded market-open workload, not a vendor benchmark. Third, leave the rewards service on MySQL unless it has its own scaling problem; if it does, TiDB is the natural candidate, once someone has listed its triggers.
A practical checklist
- Write down the problem: write throughput, zone or region survival, data placement, or something a pooler or replica would fix.
- Measure peak writes, p99 latency budget and acceptable recovery time and loss for each failure type.
- Scan the schema and code for unsupported features: range types and
pg_dumpflows (CockroachDB), extensions and sharding assumptions (YugabyteDB), triggers and stored procedures (TiDB). - Find every business rule that spans rows and decide: SERIALIZABLE with retries, or explicit
FOR UPDATElocks. - Make every write that can be retried idempotent, because ambiguous commit errors will happen.
- Read the licence with your legal team, including telemetry and benchmark clauses for CockroachDB.
- Choose a release line deliberately: Regular or Innovation, STS or LTS, and note its end of maintenance.
- Rehearse an upgrade, including the finalise step and, for TiDB, the fact that there is no rollback.
- Restore a backup into a scratch cluster and compare data, monthly.
- Run a failure drill: kill a node during writes and measure the pause, the errors and the data afterwards.
Common mistakes
- Choosing distributed SQL for a problem it does not solve. Slow analytics, too many connections or a missing index do not need a new database.
- Trusting “compatible” without a schema scan. Each project publishes a list of unsupported features; read it before the proof of concept, not after.
- Assuming the default isolation is safe. In my drill, two of three defaults allowed an overdraft-style write skew.
- Ignoring ambiguous commit errors. Retrying a non-idempotent insert after “result is ambiguous” can create duplicates.
- Treating the licence as a footnote. A CockroachDB cluster without a valid key is throttled to 5 concurrent transactions after its grace period.
- Upgrading without a rollback plan. CockroachDB auto-finalises by default and TiDB cannot roll back at all.
- Testing failover with the gateway only. Killing the node your client is connected to tests reconnection; killing the node that leads your data tests the database. Do both.
Drill: one write-skew test and one node kill on three clusters
On 12 October 2026, from about 6:22 AM to 6:36 AM IST, on a shared Linux VM with 8 vCPUs, 15 GiB of RAM and Debian 13, without root, without containers and on loopback only, I ran three small clusters one after another. CockroachDB v26.3.2 came from the official tarball, whose SHA-256 matched the published .sha256sum. YugabyteDB 2026.1.2.0-b137 came from the official tarball, whose SHA-256 matched the published .sha file. TiDB v8.5.8 came through TiUP 1.17.1 playground (1 PD, 3 TiKV, 1 TiDB, no TiFlash). The client was Python with psycopg 3.3.6 and PyMySQL 1.2.3. Afterwards I stopped every process and deleted the binaries, data directories and downloads.
The cluster commands were:
# CockroachDB, three nodes (n = 1, 2, 3), no licence key, insecure mode for the drill only
cockroach start --insecure --store=c/n$n --listen-addr=127.0.0.1:2625$n \
--http-addr=127.0.0.1:808$n --join=127.0.0.1:26251,127.0.0.1:26252,127.0.0.1:26253 \
--cache=256MiB --max-sql-memory=256MiB --background
# YugabyteDB, three nodes on 127.0.0.1 to 127.0.0.3 (nodes 2 and 3 add --join=127.0.0.1)
yugabyted start --advertise_address=127.0.0.$n --base_dir=y/n$n --callhome=false
# TiDB through TiUP
tiup playground v8.5.8 --kv 3 --db 1 --pd 1 --tiflash 0 --without-monitor --host 127.0.0.1
Two practical notes. YugabyteDB’s initdb failed at first with invalid locale name "en_US.UTF-8" because the VM had no such locale; I compiled one into a user directory with localedef from Debian’s locales package and pointed LOCPATH at it. CockroachDB logged that the unlicensed cluster’s grace period would end exactly 7 days after initialisation, as the FAQ says.
Part 1: write skew. A client has two wallets of 50 each, and the rule is that the total must not go below zero. Two transactions run at the same time; each reads the total, sees 100, and withdraws 80 from a different wallet. Without protection, both commit and the total becomes minus 60. Each transaction ran this, in two threads, with the reads finished before either update:
BEGIN;
SELECT SUM(bal) FROM (SELECT bal FROM wallets WHERE cust = 7) w; -- both see 100
UPDATE wallets SET bal = bal - 80 WHERE id = 1; -- the other uses id = 2
COMMIT;
Database and level
What happened
Final total
CockroachDB, default (SERIALIZABLE)
One transaction failed with “restart transaction: … RETRY_SERIALIZABLE”, the other committed
20
CockroachDB, READ COMMITTED
Both committed
minus 60
CockroachDB, REPEATABLE READ requested
Session reported serializable; one failed with RETRY_SERIALIZABLE
20
YugabyteDB, default (Read Committed, flag on)
Both committed
minus 60
YugabyteDB, REPEATABLE READ (Snapshot)
Both committed
minus 60
YugabyteDB, SERIALIZABLE
One failed with “deadlock detected … Consider using READ COMMITTED isolation level”, the other committed
20
TiDB, default (REPEATABLE READ)
Both committed
minus 60
TiDB, READ COMMITTED
Both committed
minus 60
TiDB, SERIALIZABLE
Error 8048: “The isolation level ‘SERIALIZABLE’ is not supported”
Unchanged
TiDB, default, with FOR UPDATE on the read
Second transaction waited for the first, then saw 20 and did not withdraw
20
On YugabyteDB, yb_get_effective_transaction_isolation_level() returned “read committed” and the TServer flags showed yb_enable_read_committed_isolation=true, as the docs describe for yugabyted on v2025.2 and later.
Part 2: kill the node that leads the data. A writer inserted one row about every 20 ms in autocommit mode and recorded each acknowledged ID. About 10 seconds in, I found the node leading the table’s data and killed it with kill -9, and left it down.
Database
Node killed
Client connection
Longest pause between acknowledged writes
Errors seen by client
Acknowledged rows missing afterwards
CockroachDB
Leaseholder of the table’s only range (node 1)
Node 3, unaffected
3.88 s
1: “result is ambiguous”; that row was not committed
0 of 1,502
YugabyteDB
Leader of the table’s only tablet (node 3), which was also the client’s gateway
Multi-host connection string, reconnected to node 2
3.18 s
1: “server closed the connection unexpectedly”; that row was not committed
0 of 1,699
TiDB
TiKV store leading the table’s Region
TiDB server, unaffected
9.74 s
None; TiDB retried internally
0 of 1,254
The YugabyteDB run tested two things at once, because the leader happened to be on the gateway node. My first YugabyteDB kill attempt was invalid: I killed the database processes on node 1 but not the yugabyted supervisor, which restarted them, and the restarted TServer could not find a master. I discarded that run and repeated it on a fresh cluster. After the CockroachDB kill, I restarted node 1 and it rejoined, with all three nodes live and all 1,502 rows readable.
Smaller observations: each CockroachDB node used about 460 to 580 MiB of resident memory with a 256 MiB cache. Each TiKV in the playground used about 2 GiB with default settings, a reminder that TiKV sizes its caches from host memory, which matters on shared machines. I did not tune any of the three.
Honest limits: one VM, loopback only, so no real network latency or partitions; insecure or default authentication; no licence key for CockroachDB (inside the grace period, for testing only); no multi-region layout; one run per scenario; tiny tables with a single range, tablet or Region; and no load beyond one writer. Pauses depend on timeouts and on which node holds what, so treat them as behaviour, not benchmarks. I did not test upgrades, backups, Kubernetes operators, xCluster, TiCDC or CockroachDB multi-region features. This post contains no performance numbers from any vendor.
What to unlearn and re-learn
- Unlearn “distributed SQL is a faster PostgreSQL or MySQL”. Re-learn that it trades per-write latency and operational weight for scale-out and survival.
- Unlearn “compatible means my app will just work”. Re-learn each project’s list of unsupported features and test your schema against it.
- Unlearn “the default isolation level is safe”. Re-learn which defaults allow write skew, and lock or serialise the rules that span rows.
- Unlearn “a failover means a clear error”. Re-learn that commits can be ambiguous and pauses last seconds, so retries must be idempotent and timeouts sized.
- Unlearn “the licence is the same as it was when we evaluated”. Re-learn the current terms; CockroachDB’s changed in November 2024 and now includes telemetry and throttling rules.
Revisit your database before you shard by hand
The Mumbai team does not need the most talked-about database; it needs a clear statement of what failure it must survive and how many writes it must take, and then the simplest system that does that. Learn how distributed SQL works underneath: ranges, tablets and Regions, Raft leaders, timestamps and isolation. Unlearn the idea that “PostgreSQL compatible” or “MySQL compatible” means identical behaviour, and the habit of trusting default isolation for money. Re-learn the current releases and licences of CockroachDB, YugabyteDB and TiDB from their release notes and licence texts, because all three have changed in the last two years. Practise by running a write-skew test and a node kill on a small cluster, as I did on one VM, and by restoring a backup. Then apply what you learn to one service first, with a real workload and a written rollback plan. Revisit the choice before market volumes double, and whenever your database ships a new major release.
Sources
- CockroachDB: repository, releases overview, v26.3 release notes, v26.2 release notes, release support policy, CockroachDB Software License, licensing FAQs, licensing announcement, 15 August 2024, transactions, READ COMMITTED transactions, replication layer, distribution layer, transaction layer, replication zones, multi-region overview, survival goals, PostgreSQL compatibility, upgrading a cluster, backup and restore overview, Kubernetes overview, technical advisories, security advisories on GitHub
- YugabyteDB: repository, 2026.1.2.0 release, 2025.2.7.0 release, LICENSE.md, releases and support dates, v2026.1 release notes, versioning, architecture, sharding, Raft replication, xCluster replication, transactions overview, isolation levels (architecture), isolation levels (explore), multi-region deployments, upgrading, YSQL major upgrade, backup and restore, Kubernetes deployment, security advisories on GitHub
- TiDB: repository, v8.5.8 release, v7.5.8 release, v9.0.0-beta.1 release, LICENSE, release timeline, 8.5.8 release notes, versioning, release support policy, architecture, TSO, MySQL compatibility, isolation levels, pessimistic transactions, Placement Rules in SQL, Region size tuning, TiKV configuration, upgrading with TiUP, backup and restore overview, TiUP playground, TiDB on Kubernetes, TiDB Operator releases, CNCF: TiKV, security advisories on GitHub, GHSA-4whx-7p29-mq22, TiKV security advisories
- CVE records (cveawg.mitre.org API): CVE-2025-9276, CVE-2025-8865, CVE-2025-8862, CVE-2025-8863, CVE-2025-8866, CVE-2026-1966, CVE-2022-31011, CVE-2022-3023, CVE-2024-41434
Releted Posts
Durable execution in 2026: revisit Temporal, Restate and DBOS before you hand-roll retries, sagas and cron jobs
Picture an online pharmacy and diagnostics booking company in Ahmedabad (an invented example, not a real client). An order goes through five steps: hold the medicine stock, take the UPI payment, book a courier slot, send the customer an SMS, and, if the courier cannot pick up within four hours, release the stock and refund.
Read morePostgreSQL high availability: revisit Patroni and CloudNativePG before your next failover
Picture a lending company in Bengaluru (an invented example, not a real client). Its loan ledger runs on PostgreSQL 16 across three VMs, managed by Patroni with a three-node etcd cluster and HAProxy in front.
Read moreVector search in 2026: revisit pgvector, Qdrant, Milvus, Weaviate before you add a vector database
An e-commerce company in Pune runs customer support on PostgreSQL. The database holds about 2 million help articles, product Q&A threads and resolved tickets, in English and a fair amount of Hinglish.
Read more