Cockroachdb
Build with CockroachDB as a distributed SQL database. Use this skill when the
You are a database specialist who integrates CockroachDB into projects. CockroachDB
is a distributed SQL database that provides PostgreSQL compatibility, serializable
transactions, and automatic horizontal scaling across regions.
## Key Points
- Use UUID primary keys — auto-incrementing IDs cause hotspots in distributed systems
- Implement transaction retry logic for `40001` (serialization) errors
- Use `RETURNING` on INSERT/UPDATE to avoid extra round trips
- Design schemas with CockroachDB-specific types: `STRING` instead of `VARCHAR`
- Use multi-region localities for latency-sensitive data
- Connection pool with `pg.Pool` — don't create connections per request
- Using auto-incrementing integer IDs — creates write hotspots on a single range
- Not retrying on serialization errors (code 40001) — expected in serializable isolation
- Using `SELECT FOR UPDATE` when a simple conditional update suffices
- Interleaving reads and writes in transactions unnecessarily — increases contention
- Not setting appropriate connection pool sizes — too many connections waste resources
- Ignoring region-aware table design in multi-region deployments
## Quick Example
```bash
npm install pg
# Or with Drizzle
npm install drizzle-orm pg
# Or with Prisma
npx prisma init
```
```prisma
datasource db {
provider = "cockroachdb"
url = env("DATABASE_URL")
}
```skilldb get database-services-skills/CockroachdbFull skill: 195 linesCockroachDB Integration
You are a database specialist who integrates CockroachDB into projects. CockroachDB is a distributed SQL database that provides PostgreSQL compatibility, serializable transactions, and automatic horizontal scaling across regions.
Core Philosophy
Distributed SQL
CockroachDB spreads data across multiple nodes and regions automatically. Every node can serve reads and writes — there's no single primary. If a node goes down, the cluster keeps running without intervention.
PostgreSQL compatible
CockroachDB speaks the PostgreSQL wire protocol. Your existing PostgreSQL drivers, ORMs, and tools work with minimal changes. If you know Postgres, you know CockroachDB.
Serializable by default
Every transaction in CockroachDB is serializable — the strongest isolation level. No phantom reads, no write skew, no surprises. You trade some latency for correctness guarantees most databases don't offer by default.
Setup
Install (use any PostgreSQL driver)
npm install pg
# Or with Drizzle
npm install drizzle-orm pg
# Or with Prisma
npx prisma init
Connect
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false },
});
With Drizzle ORM
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from './schema';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool, { schema });
With Prisma
datasource db {
provider = "cockroachdb"
url = env("DATABASE_URL")
}
Key Techniques
Schema (PostgreSQL-compatible)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email STRING NOT NULL UNIQUE,
name STRING,
plan STRING NOT NULL DEFAULT 'free',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
INDEX idx_users_email (email)
);
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title STRING NOT NULL,
content STRING NOT NULL,
status STRING NOT NULL DEFAULT 'draft',
author_id UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
INDEX idx_posts_author (author_id),
INDEX idx_posts_status (status, created_at DESC)
);
CRUD operations
// Insert
const { rows: [user] } = await pool.query(
'INSERT INTO users (email, name) VALUES ($1, $2) RETURNING *',
['alice@example.com', 'Alice']
);
// Select
const { rows: posts } = await pool.query(
'SELECT p.*, u.name as author_name FROM posts p JOIN users u ON p.author_id = u.id WHERE p.status = $1 ORDER BY p.created_at DESC LIMIT $2',
['published', 20]
);
// Update
await pool.query(
'UPDATE posts SET title = $1, updated_at = now() WHERE id = $2',
['Updated', postId]
);
// Upsert
await pool.query(
'INSERT INTO users (email, name) VALUES ($1, $2) ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name, updated_at = now()',
['alice@example.com', 'Alice Updated']
);
// Delete
await pool.query('DELETE FROM posts WHERE id = $1', [postId]);
Transactions with retry
async function runTransaction<T>(fn: (client: any) => Promise<T>): Promise<T> {
const client = await pool.connect();
try {
while (true) {
try {
await client.query('BEGIN');
const result = await fn(client);
await client.query('COMMIT');
return result;
} catch (err: any) {
await client.query('ROLLBACK');
if (err.code === '40001') continue; // Retry on serialization error
throw err;
}
}
} finally {
client.release();
}
}
// Usage
const post = await runTransaction(async (client) => {
const { rows: [post] } = await client.query(
'UPDATE posts SET status = $1 WHERE id = $2 RETURNING *',
['published', postId]
);
await client.query(
'INSERT INTO notifications (user_id, type, entity_id) VALUES ($1, $2, $3)',
[post.author_id, 'published', post.id]
);
return post;
});
Multi-region configuration
-- Set table locality for low-latency regional reads
ALTER TABLE users SET LOCALITY REGIONAL BY ROW;
-- Add a crdb_region column
ALTER TABLE users ADD COLUMN crdb_region crdb_internal_region NOT NULL
DEFAULT gateway_region()::crdb_internal_region;
-- Global tables (replicated to all regions, fast reads everywhere)
ALTER TABLE config SET LOCALITY GLOBAL;
Best Practices
- Use UUID primary keys — auto-incrementing IDs cause hotspots in distributed systems
- Implement transaction retry logic for
40001(serialization) errors - Use
RETURNINGon INSERT/UPDATE to avoid extra round trips - Design schemas with CockroachDB-specific types:
STRINGinstead ofVARCHAR - Use multi-region localities for latency-sensitive data
- Connection pool with
pg.Pool— don't create connections per request
Anti-Patterns
- Using auto-incrementing integer IDs — creates write hotspots on a single range
- Not retrying on serialization errors (code 40001) — expected in serializable isolation
- Using
SELECT FOR UPDATEwhen a simple conditional update suffices - Interleaving reads and writes in transactions unnecessarily — increases contention
- Not setting appropriate connection pool sizes — too many connections waste resources
- Ignoring region-aware table design in multi-region deployments
Install this skill directly: skilldb add database-services-skills
Related Skills
Cassandra
Build with Apache Cassandra for high-availability distributed data. Use this skill
Clickhouse
Build with ClickHouse for real-time analytics and OLAP workloads. Use this skill
Convex
Build with Convex as a reactive backend. Use this skill when the project needs
Drizzle
Use Drizzle ORM for type-safe SQL in TypeScript. Use this skill when the project
Dynamodb
Build with Amazon DynamoDB as a serverless NoSQL database. Use this skill when
Fauna
Build with Fauna as a distributed document-relational database. Use this skill