Skip to content

Lab: Sharded Postgres (Citus)

Prediction checkpoint

You will run a co-located query and then one that crosses shards. Predict which query stays on one worker and which requires coordination. The distributed plan—not elapsed time alone—shows whether the shard key served the access pattern.

Pairs with: Sharding

A real Citus cluster — 1 coordinator + 3 workers. Distribute a table by shard key, query pg_dist_shard_placement to see where data actually landed, compare a cross-shard query plan against a single-shard one, and reproduce a real hot shard with docker stats showing one container doing all the work.

docker-compose.yml

name: labs-sharding

services:
  coordinator:
    image: citusdata/citus:12.1
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: app_pass
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "appuser"]
      interval: 5s
      timeout: 5s
      retries: 20

  worker1:
    image: citusdata/citus:12.1
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: app_pass
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "appuser"]
      interval: 5s
      timeout: 5s
      retries: 20

  worker2:
    image: citusdata/citus:12.1
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: app_pass
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "appuser"]
      interval: 5s
      timeout: 5s
      retries: 20

  worker3:
    image: citusdata/citus:12.1
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: app_pass
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "appuser"]
      interval: 5s
      timeout: 5s
      retries: 20

  # Runs once: waits for coordinator + all workers, then wires the cluster
  # together with SELECT citus_add_node(...). This is the "shard count"
  # control from the sim brought to life — add/remove services below and
  # re-run this to change the cluster shape.
  cluster-init:
    image: citusdata/citus:12.1
    depends_on:
      coordinator:
        condition: service_healthy
      worker1:
        condition: service_healthy
      worker2:
        condition: service_healthy
      worker3:
        condition: service_healthy
    entrypoint: ["bash", "-c"]
    command:
      - |
        set -e
        for w in worker1 worker2 worker3; do
          psql -h coordinator -U appuser -d appdb -c "SELECT citus_add_node('$$w', 5432);"
        done
        psql -h coordinator -U appuser -d appdb -c "SELECT * FROM citus_get_active_worker_nodes();"
    environment:
      PGPASSWORD: app_pass

Exercises

The full walkthrough (distribute a table, verify real shard balance, feel the cross-shard query cost, simulate a hot key) lives in the lab's README:

labs/sharding-citus/README.md on GitHub

git clone https://github.com/sanketn26/interview-prep
cd interview-prep/labs/sharding-citus
docker compose up -d --wait coordinator worker1 worker2 worker3
docker compose up cluster-init

Apple Silicon / arm64

Citus only publishes linux/amd64 images — Docker runs it under emulation on an arm64 host. Works fine, just slower to start.

← All Labs