Spokes.wiki Search About
Created
Updated

Citus

"Distributed PostgreSQL as an extension." Citus shards a Postgres database across a cluster of Postgres nodes without replacing Postgres — it installs as an extension (shared_preload_libraries = 'citus', then CREATE EXTENSION citus), and the shards it creates are ordinary Postgres tables on ordinary Postgres servers. First-party source: the project README and the GitHub repository metadata (T1).

This is the spoke's first engine that scales out without a proxy in front, and its first OLTP store that also sells itself for analytics — both of which were named as open gaps before it arrived.

The architecture in one line

A coordinator node holds the metadata for distributed and reference tables; worker nodes hold the shards. When you query the coordinator, Citus rewrites your statement into regular SQL against shard tables and sends it to the workers. The README makes the consequence explicit:

"Data in distributed tables is stored in 'shards', which are actually just regular PostgreSQL tables on the worker nodes… That way, all the usual PostgreSQL optimizations and extensions can automatically be used with Citus."

That is the whole design argument. vitess speaks the MySQL wire protocol to a fleet of MySQL servers it does not run inside; Citus runs inside every server in its own fleet, which is why PostGIS, HyperLogLog, JSONB, lateral joins and time partitioning keep working on a distributed table when they would each need a proxy-side reimplementation elsewhere. It is the strongest form of the "push work down to the underlying engine" preference that cross-shard-queries describes from Vitess's side.

Metadata and schema are synced to every node, so any node can serve distributed queries — but "schema changes and cluster administration still need to go through the coordinator." The coordinator is not a read bottleneck; it is still the write path for DDL.

The four table types

  • Distributed tables — SELECT create_distributed_table('events', 'device_id') shards by hash of a distribution column. A WHERE device_id = 1 is routed to one worker; a query with no filter on the column is parallelized across all shards.
  • Reference tables — create_reference_table() replicates a small table to every node, so joins and foreign keys that don't involve the distribution column still work locally. This is the piece database-sharding's proxy model has no equivalent for: replicate the dimension, and the fact table's joins stop being cross-shard.
  • Co-located tables — same distribution column type, so matching values land on the same node. See shard-colocation; this is the mechanism the rest of the feature list rests on.
  • Columnar tables — CREATE TABLE … USING columnar, available on distributed and plain local tables.

There is also create_distributed_table_concurrently(), which converts a live table without blocking DML — the plain create_distributed_table "block[s] writes… until the command is finished." Migrating a table that is already serving traffic is the case a sharding story usually skips, so the two-variant API is a small piece of honesty about what the conversion costs.

Schema-based sharding — the second sharding model

Since Citus 12.0, a whole schema can be the shard: SELECT citus_schema_distribute('user_service'), or SET citus.enable_schema_based_sharding TO ON to make every newly created schema distributed. Then routing follows search_path — a tenant sets SET search_path = tenant_name, public, a microservice connects as a user whose name matches its schema and needs no change at all.

The README's framing is a deployment argument rather than a performance one: it "is an ideal solution for microservices, and for ISVs deploying applications that cannot undergo the changes required to onboard row-based sharding." Row-based sharding demands a distribution column in your data model; schema-based sharding demands only that tenants already be separated by schema. That trade — no query changes, coarser shard granularity — is a different answer to the same problem, and database-sharding had only the row-based one.

Columnar storage, and its price

The README's own numbers: the same 10M rows are 25 MB as USING columnar against 651 MB as a row table — roughly 26×. The restrictions are stated just as plainly, and they are severe:

"you should only load data in batch using COPY or INSERT..SELECT to achieve good compression. Update, delete, and foreign keys are currently unsupported on columnar tables."

So columnar here is not an OLAP engine bolted onto an OLTP one; it is a write-once archival tier. The recommended shape is time partitioning where new partitions are row-based and old ones are compressed — which is a different thing from what clickhouse or duckdb do, and the reason this page sits in the OLTP spoke rather than the analytical sibling. Cross-link, don't duplicate.

The mixed-workload claim

The distributed executor is described as adaptive: single-shard queries are routed whole to one node ("arbitrarily complex queries are supported with minimal routing overhead"), unfiltered queries are fanned out in parallel, and — the actual claim —

"the Citus distributed executor is adaptive and is designed to handle both query types at the same time on the same system under high concurrency, which enables large-scale mixed workloads."

That is the HTAP pitch, and it is the seam this spoke's synthesis.md flagged and had no source for. It is a design claim from the vendor's README, with no benchmark attached here. The SIGMOD '21 paper Citus: Distributed PostgreSQL for Data-Intensive Applications (doi 10.1145/3448016.3457551) is cited as the detailed account and has not been read for this page — the obvious next source.

It does not do its own high availability

A distributed database usually ships a failover story. Citus points at someone else's:

"One of the most popular high availability solutions for PostgreSQL, Patroni 3.0, has first class support for Citus 10.0 and above, additionally since Citus 11.2 ships with improvements for smoother node switchover in Patroni."

Patroni models the cluster as groups — group 0 is the coordinator's replica set, group N is worker shard-group N — each with its own leader and sync standby. So replication and leader election are per-group Postgres streaming replication, not a Citus-wide consensus protocol. Citus distributes the data plane and inherits the control plane from Postgres HA tooling.

Followed up 2026-08-20 in the same batch. patroni is now paged from its own repository and documentation, and it sharpens this negative finding rather than softening it. Patroni implements no consensus either — it holds a TTL'd leader lock in an external store (etcd, Consul, ZooKeeper or the Kubernetes API) and derives the whole failover policy from three integers bound by loop_wait + 2 * retry_timeout <= ttl. So the consensus this cluster depends on is real, and it lives two layers below Citus: not in the extension, not in Postgres, but in the distributed configuration store Patroni leans on.

This is a real answer to the spoke's standing distributed-consistency question, and it is a negative one: there is no Raft or Paxos here. It also means the README says nothing about cross-shard transaction commit (two-phase commit, distributed deadlock detection, snapshot semantics across nodes) — features Citus is known to have, but which this source does not document. Recorded as still open rather than assumed.

Project state (checked 2026-08-20)

  • License: AGPL-3.0. Not the permissive licence most of this spoke's engines carry, and it applies to an extension you load into your own Postgres.
  • 12,712 stars · 785 forks · 1,070 open issues; created 2016-02-01, last push 2026-08-20 — the day of this ingest. Actively developed, not a Microsoft parking lot.
  • Latest release v14.2.0 (2026-08-06), shipped the same day as v13.4.0 and v12.1.14 — three maintained branches in parallel, which is what lets a site pinned to an older Postgres still take security fixes. Citus 14.0 (2026-02-17) brought PostgreSQL 18.1 support.
  • Install: Docker (citusdata/citus), deb/rpm via packagecloud, or the managed service.
  • Managed as Azure Cosmos DB for PostgreSQL — the usual hosting-versus-engine split: the pricing and managed-service angle belongs in ../cloud-wiki (which already pages azure), the architecture here.

Source-quality caveat. The README is stale against its own releases: its banner still points at the Citus 13.0 blog post from February 2025, and the install snippets pin postgresql-17-citus-13.0 and citus130_17 — Postgres 17 and Citus 13 — while 14.2.0 shipped two weeks before this ingest. The prose about architecture is unaffected; the version-specific commands should not be copied. Caught by reading the GitHub releases API against the README rather than trusting either alone.

Who builds it

citus-data, acquired by microsoft; the repo carries "Copyright © Citus Data, Inc." and adopts the Microsoft Open Source Code of Conduct. The same team maintains pg_cron, postgresql-hll, TopN and pg_auto_failover — the Postgres-extension house style is the through-line.

Named users, as claimed

The README's own case-study list, which is marketing but is at least specific: Algolia (real-time dashboards), MixRank (time series — "the biggest Citus clusters store well over a petabyte of time series data and ingest terabytes per day"), Salesloft and ConvertFlow (multi-tenant SaaS), Helsinki Regional Transportation Authority and MobilityDB (geospatial via PostGIS). Unverified here.

postgresql · shard-colocation · database-sharding · cross-shard-queries · vitess · planetscale-768-servers · online-transaction-processing · operational-databases · citus-data · matrixone · analytical-databases · synthesis