api-database-cockroachdb
CockroachDBは、分散型SQLデータベースとして、トランザクションのリトライ機能や複数地域への対応、オンラインでのスキーマ変更などを実現し、PostgreSQLとの互換性も持つデータベースを構築・運用するSkill。
📜 元の英語説明(参考)
CockroachDB distributed SQL -- transaction retries, multi-region, online schema changes, follower reads, PostgreSQL compatibility gaps
🇯🇵 日本人クリエイター向け解説
CockroachDBは、分散型SQLデータベースとして、トランザクションのリトライ機能や複数地域への対応、オンラインでのスキーマ変更などを実現し、PostgreSQLとの互換性も持つデータベースを構築・運用するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o api-database-cockroachdb.zip https://jpskill.com/download/10231.zip && unzip -o api-database-cockroachdb.zip && rm api-database-cockroachdb.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10231.zip -OutFile "$d\api-database-cockroachdb.zip"; Expand-Archive "$d\api-database-cockroachdb.zip" -DestinationPath $d -Force; ri "$d\api-database-cockroachdb.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
api-database-cockroachdb.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
api-database-cockroachdbフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
CockroachDB のパターン
クイックガイド: CockroachDB は標準の
pgドライバ (PostgreSQL ワイヤプロトコル) を介して接続します。PostgreSQL との最も重要な違いは、トランザクションのリトライが必須であることです。CockroachDB のシリアライザブルな分離レベルは、すべてのトランザクションが SQLSTATE40001で失敗する可能性があることを意味します。アプリケーションはこれをキャッチし、トランザクション全体をリトライする必要があります。主キーにはUUIDとgen_random_uuid()を使用してください (決してSERIALは使用しないでください。連番 ID は分散ホットスポットの原因となります)。DDL はバックグラウンドジョブとしてオンラインスキーマ変更として実行され、明示的なトランザクション内では実行できません。マルチリージョンデプロイメントでのレイテンシを削減するために、フォロワー読み取りにはAS OF SYSTEM TIMEを使用してください。
<critical_requirements>
重要: この Skill を使用する前に
すべてのコードは CLAUDE.md のプロジェクト規約に従う必要があります (kebab-case、名前付きエクスポート、インポート順序、
import type、名前付き定数)
(SQLSTATE 40001 エラーに対するトランザクションリトライロジックを実装する必要があります -- CockroachDB は通常の操作下でもシリアライゼーションエラーを返します。PostgreSQL ではまれですが)
(主キーには UUID と gen_random_uuid() を使用する必要があります -- SERIAL や連番 ID は絶対に使用しないでください。分散書き込みホットスポットの原因となります)
(DDL ステートメントを明示的なトランザクション内に配置しないでください -- ほとんどの DDL はバックグラウンドジョブとして実行され、COMMIT 時に部分的に適用された状態で失敗する可能性があります。CREATE TABLE/CREATE INDEX は例外ですが、最も安全な方法は常に、暗黙的なトランザクションごとに 1 つの DDL ステートメントを使用することです)
(すべてのデータベースアクセスに pg の Pool を使用する必要があります -- PostgreSQL と同じですが、クラスタ内の各ノードが有効な接続ターゲットであることに注意してください)
</critical_requirements>
例
- コアパターン -- プール設定、パラメータ化されたクエリ、トランザクションリトライロジック、エラー処理
- マルチリージョンとパフォーマンス -- 局所性、生存目標、フォロワー読み取り、AS OF SYSTEM TIME
- スキーマと操作 -- オンラインスキーマ変更、IMPORT INTO、CHANGEFEED、cockroach CLI
追加リソース:
- reference.md -- PostgreSQL との互換性のギャップ、エラーコード、型の違い、本番環境チェックリスト
自動検出: CockroachDB, cockroachdb, cockroach, CRDB, crdb, cockroach_restart, SAVEPOINT cockroach_restart, 40001, serialization_failure, retry transaction, restart transaction, gen_random_uuid, unique_rowid, AS OF SYSTEM TIME, follower_read_timestamp, CHANGEFEED, CREATE CHANGEFEED, IMPORT INTO, cockroach sql, cockroach start, multi-region, survival goal, zone survival, region survival, locality, REGIONAL BY ROW
使用するタイミング:
pgドライバを介した CockroachDB に対する直接 SQL クエリ- シリアライザブルな分離を必要とする分散トランザクション
- 局所性を考慮した読み取り/書き込みを行うマルチリージョンデータベースデプロイメント
- PostgreSQL から CockroachDB への移行アプリケーション
- CHANGEFEED を使用した変更データキャプチャ
- IMPORT INTO を使用したバルクデータロード
カバーされる主要なパターン:
- トランザクションリトライロジック (指数バックオフによる SQLSTATE 40001 処理)
- gen_random_uuid() を使用した UUID 主キー (ホットスポット回避)
- フォロワー読み取りおよび履歴クエリのための AS OF SYSTEM TIME
- マルチリージョン構成 (局所性、生存目標、リージョンテーブル)
- オンラインスキーマ変更 (PostgreSQL との DDL 動作の違い)
- PostgreSQL との互換性のギャップ (動作しないもの)
使用しないタイミング:
- ORM またはクエリビルダーが必要な場合 -- 代わりに ORM/クエリビルダーの Skill を使用してください
- CockroachDB なしで標準の PostgreSQL をターゲットにしている場合 -- PostgreSQL の Skill を使用してください
- CockroachDB に欠けている機能 (アドバイザリロック、完全なストアドプロシージャサポート、CREATE DOMAIN) が必要な場合
<philosophy>
哲学
CockroachDB は、PostgreSQL ワイヤプロトコルを使用する 分散 SQL データベース です。コア原則: PostgreSQL 互換の SQL を記述しますが、分散を考慮して設計します。
コア原則:
- すべてをリトライする -- シリアライザブルな分離レベルは、競合を解決するために CockroachDB によってトランザクションが中断される可能性があることを意味します。コードは SQLSTATE
40001を処理し、トランザクション全体をリトライする必要があります。これはエッジケースではありません -- 通常の負荷で発生します。 - 均等に分散する -- 連番の主キー (
SERIAL、自動インクリメント) は、CockroachDB が主キーでデータを範囲にソートするため、書き込みホットスポットを作成します。UUIDとgen_random_uuid()を使用して、クラスタ全体に書き込みを分散します。 - DDL は非同期 -- スキーマ変更はバックグラウンドジョブとして実行されます。明示的なトランザクションでラップすることはできません。それに応じて移行を計画してください -- 本番環境では一度に 1 つの DDL ステートメントを実行します。
- フォロワーから読み取る -- 常にリースホルダーにアクセスする代わりに、
AS OF SYSTEM TIMEを使用して、最も近いレプリカからわずかに古いデータを読み取ります。これは、マルチリージョンデプロイメントにおける最大のレイテンシ最適化です。 - ほぼ PostgreSQL -- CockroachDB はほとんどの PostgreSQL 構文をサポートしており、
pgドライバは直接動作します。ただし、特定の機能が欠落しているか、動作が異なります。本番環境で問題が発生する前に、ギャップを把握してください。
</philosophy>
<patterns>
コアパターン
パターン 1: コネクションプールの設定
CockroachDB は標準の pg ドライバを使用します。プールの設定は PostgreSQL とほぼ同じですが、接続文字列は CockroachDB ノード (またはロードバランサー) を指します。完全な構成については、examples/core.md を参照してください。
// 良い例 - エラー処理付きの CockroachDB プール
import pg from "pg";
const POOL_MAX_CLIENTS = 20;
const IDLE_TIMEOUT_MS = 30_000;
const CONNECTION_TIMEOUT_MS = 5_000;
function createPool(): pg.Pool {
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
// 例: postgresql://user:pass@crdb-lb:26257/mydb?sslmode=verify-full
max: POOL_MAX_CLIENTS,
idleTimeoutMillis: IDLE_TIMEOUT_MS,
connectionTimeoutMillis: CONNECTION_TIMEOUT_MS,
});
pool.on("error", (err) => {
console.error("Unexpected idle client error:", err.message);
});
return pool;
}
export { createPool }; 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
CockroachDB Patterns
Quick Guide: CockroachDB connects via the standard
pgdriver (PostgreSQL wire protocol). The single most important difference from PostgreSQL: transaction retries are mandatory. CockroachDB's serializable isolation means any transaction can fail with SQLSTATE40001-- your application MUST catch this and retry the entire transaction. UseUUIDwithgen_random_uuid()for primary keys (neverSERIAL-- sequential IDs cause distributed hotspots). DDL runs as online schema changes in background jobs and cannot be inside explicit transactions. UseAS OF SYSTEM TIMEfor follower reads to reduce latency in multi-region deployments.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST implement transaction retry logic for SQLSTATE 40001 errors -- CockroachDB WILL return serialization errors under normal operation, unlike PostgreSQL where they are rare)
(You MUST use UUID with gen_random_uuid() for primary keys -- NEVER use SERIAL or sequential IDs, which cause distributed write hotspots)
(You MUST NOT put DDL statements inside explicit transactions -- most DDL runs as background jobs and can fail at COMMIT time with a partially applied state. CREATE TABLE/CREATE INDEX are exceptions but the safest practice is always: one DDL statement per implicit transaction)
(You MUST use Pool from pg for all database access -- same as PostgreSQL, but be aware that each node in the cluster is a valid connection target)
</critical_requirements>
Examples
- Core Patterns -- Pool setup, parameterized queries, transaction retry logic, error handling
- Multi-Region & Performance -- Locality, survival goals, follower reads, AS OF SYSTEM TIME
- Schema & Operations -- Online schema changes, IMPORT INTO, CHANGEFEED, cockroach CLI
Additional resources:
- reference.md -- PostgreSQL compatibility gaps, error codes, type differences, production checklist
Auto-detection: CockroachDB, cockroachdb, cockroach, CRDB, crdb, cockroach_restart, SAVEPOINT cockroach_restart, 40001, serialization_failure, retry transaction, restart transaction, gen_random_uuid, unique_rowid, AS OF SYSTEM TIME, follower_read_timestamp, CHANGEFEED, CREATE CHANGEFEED, IMPORT INTO, cockroach sql, cockroach start, multi-region, survival goal, zone survival, region survival, locality, REGIONAL BY ROW
When to use:
- Direct SQL queries against CockroachDB via the
pgdriver - Distributed transactions requiring serializable isolation
- Multi-region database deployments with locality-aware reads/writes
- Applications migrating from PostgreSQL to CockroachDB
- Change data capture with CHANGEFEED
- Bulk data loading with IMPORT INTO
Key patterns covered:
- Transaction retry logic (SQLSTATE 40001 handling with exponential backoff)
- UUID primary keys with gen_random_uuid() (hotspot avoidance)
- AS OF SYSTEM TIME for follower reads and historical queries
- Multi-region configuration (locality, survival goals, regional tables)
- Online schema changes (DDL behavior differences from PostgreSQL)
- PostgreSQL compatibility gaps (what does NOT work)
When NOT to use:
- You need an ORM or query builder -- use your ORM/query builder skill instead
- You are targeting standard PostgreSQL without CockroachDB -- use the PostgreSQL skill
- You need features CockroachDB lacks (advisory locks, full stored procedure support, CREATE DOMAIN)
<philosophy>
Philosophy
CockroachDB is a distributed SQL database that uses the PostgreSQL wire protocol. The core principle: write PostgreSQL-compatible SQL, but design for distribution.
Core principles:
- Retry everything -- Serializable isolation means any transaction can be aborted by CockroachDB to resolve conflicts. Your code MUST handle SQLSTATE
40001and retry the full transaction. This is not an edge case -- it happens under normal load. - Distribute evenly -- Sequential primary keys (
SERIAL, auto-increment) create write hotspots because CockroachDB sorts data by primary key across ranges. UseUUIDwithgen_random_uuid()to scatter writes across the cluster. - DDL is async -- Schema changes run as background jobs. They cannot be wrapped in explicit transactions. Plan migrations accordingly -- one DDL statement at a time in production.
- Read from followers -- Use
AS OF SYSTEM TIMEto read slightly stale data from the nearest replica instead of always hitting the leaseholder. This is the single biggest latency optimization in multi-region deployments. - PostgreSQL, mostly -- CockroachDB supports most PostgreSQL syntax and the
pgdriver works directly. But certain features are missing or behave differently. Know the gaps before you hit them in production.
</philosophy>
<patterns>
Core Patterns
Pattern 1: Connection Pool Setup
CockroachDB uses the standard pg driver. Pool setup is nearly identical to PostgreSQL, but the connection string points to a CockroachDB node (or load balancer). See examples/core.md for full configuration.
// Good Example - CockroachDB pool with error handling
import pg from "pg";
const POOL_MAX_CLIENTS = 20;
const IDLE_TIMEOUT_MS = 30_000;
const CONNECTION_TIMEOUT_MS = 5_000;
function createPool(): pg.Pool {
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
// Example: postgresql://user:pass@crdb-lb:26257/mydb?sslmode=verify-full
max: POOL_MAX_CLIENTS,
idleTimeoutMillis: IDLE_TIMEOUT_MS,
connectionTimeoutMillis: CONNECTION_TIMEOUT_MS,
});
pool.on("error", (err) => {
console.error("Unexpected idle client error:", err.message);
});
return pool;
}
export { createPool };
Why good: Standard pg Pool works unmodified, named constants, error handler prevents process crash, CockroachDB default port is 26257 (not 5432)
// Bad Example - SERIAL primary key
await pool.query(`
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
)
`);
// SERIAL creates sequential IDs via unique_rowid()
// which causes write hotspots on a single range
Why bad: Sequential IDs from SERIAL/unique_rowid() cluster writes on one range, creating a hotspot that defeats CockroachDB's distributed architecture
Pattern 2: Transaction Retry Logic (MANDATORY)
CockroachDB's serializable isolation means transactions can fail with SQLSTATE 40001 under normal operation. You MUST catch this and retry. See examples/core.md for the full retry helper.
// Good Example - Transaction with retry logic
const CRDB_SERIALIZATION_FAILURE = "40001";
const MAX_RETRIES = 5;
const BASE_DELAY_MS = 50;
async function withRetry<T>(
pool: pg.Pool,
operation: (client: pg.PoolClient) => Promise<T>,
): Promise<T> {
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
const client = await pool.connect();
try {
await client.query("BEGIN");
const result = await operation(client);
await client.query("COMMIT");
return result;
} catch (err) {
await client.query("ROLLBACK");
if (isCrdbRetryError(err) && attempt < MAX_RETRIES) {
const delay =
BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * BASE_DELAY_MS;
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw err;
} finally {
client.release();
}
}
throw new Error("Retry loop exited unexpectedly");
}
Why good: Catches 40001 errors specifically, exponential backoff with jitter prevents thundering herd, fresh client per attempt, bounded retries, releases client in finally
// Bad Example - No retry logic (WILL fail in production)
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query(
"UPDATE accounts SET balance = balance - $1 WHERE id = $2",
[100, fromId],
);
await client.query(
"UPDATE accounts SET balance = balance + $1 WHERE id = $2",
[100, toId],
);
await client.query("COMMIT");
} catch (err) {
await client.query("ROLLBACK");
throw err; // 40001 errors bubble up as application failures!
} finally {
client.release();
}
Why bad: No retry logic -- serialization errors (40001) propagate as unhandled application failures. In CockroachDB, these are EXPECTED under normal concurrent load, not exceptional conditions.
Pattern 3: UUID Primary Keys
CockroachDB distributes data across ranges sorted by primary key. Sequential IDs create hotspots. See examples/core.md for table design patterns.
-- Good Example - UUID primary key with gen_random_uuid()
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Why good: UUIDs distribute writes evenly across all ranges in the cluster, gen_random_uuid() is built-in and generates UUIDv4
-- Bad Example - SERIAL primary key
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- SERIAL uses unique_rowid() which generates time-ordered IDs
-- All recent inserts land on the same range -> hotspot
Why bad: SERIAL/unique_rowid() generates roughly time-ordered values, causing all concurrent inserts to target the same range, which bottlenecks on a single node
Pattern 4: AS OF SYSTEM TIME (Follower Reads)
Read slightly stale data from the nearest replica for dramatically lower latency in multi-region setups. See examples/multi-region.md for full patterns.
// Good Example - Follower read with built-in function
const result = await pool.query<ProductRow>(
"SELECT id, name, price FROM products WHERE category = $1 AS OF SYSTEM TIME follower_read_timestamp()",
[category],
);
Why good: follower_read_timestamp() automatically picks a safe staleness window, query can be served by any replica (nearest to the client), no leaseholder round-trip
When to use: Read-heavy dashboards, product catalogs, search results -- anywhere slightly stale data (at least 4.2 seconds) is acceptable.
When not to use: Reads that must reflect the latest write (e.g., reading immediately after an INSERT to confirm it succeeded).
Pattern 5: Online Schema Changes
CockroachDB DDL runs as background jobs -- NOT inside transactions. See examples/schema-ops.md for migration patterns.
// Good Example - DDL executed as individual statements
await pool.query("ALTER TABLE users ADD COLUMN phone TEXT");
// Runs as a background schema change job
// Table remains fully available for reads and writes during the change
Why good: DDL runs without table locks, no downtime, table available throughout
// Bad Example - DDL inside a transaction
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query("ALTER TABLE users ADD COLUMN phone TEXT");
await client.query("ALTER TABLE users ADD COLUMN address TEXT");
await client.query("COMMIT");
// Most DDL can fail at COMMIT time with a partially applied state
} finally {
client.release();
}
Why bad: Most DDL in explicit transactions can fail at COMMIT time with a partially applied state. CREATE TABLE/CREATE INDEX are exceptions, but the safest practice is always one DDL statement per implicit transaction.
Pattern 6: CHANGEFEED (Change Data Capture)
Stream row-level changes to external sinks. See examples/schema-ops.md for full CHANGEFEED patterns.
-- Good Example - CHANGEFEED to Kafka
CREATE CHANGEFEED FOR TABLE orders
INTO 'kafka://broker:9092'
WITH updated, resolved = '10s';
-- Sinkless changefeed (streams to SQL client)
CREATE CHANGEFEED FOR TABLE orders WITH updated;
Why good: Real-time CDC without polling, supports Kafka/webhook/cloud storage sinks, resolved timestamps enable downstream consumers to know data completeness
When to use: Event-driven architectures, data replication to analytics systems, audit logging, cache invalidation.
</patterns>
<decision_framework>
Decision Framework
Primary Key Strategy
What type of primary key?
+-- Need human-readable IDs? -> UUID with gen_random_uuid() + separate readable slug column
+-- Need globally unique IDs? -> UUID with gen_random_uuid() (recommended default)
+-- Migrating from PostgreSQL SERIAL? -> Switch to UUID, backfill existing data
+-- Need monotonically increasing? -> DO NOT -- use UUID. If you absolutely must, use
| SERIAL but understand the hotspot tradeoff.
Isolation Level Choice
Which isolation level?
+-- Need strongest guarantees? -> SERIALIZABLE (default, recommended)
| +-- Your app handles 40001 retries? -> Yes, use SERIALIZABLE
| +-- Cannot implement retry logic? -> Consider READ COMMITTED
+-- Analytics / read-heavy workload? -> READ COMMITTED (no retry needed)
+-- Background jobs with loose consistency? -> READ COMMITTED
Read Strategy
How fresh must the data be?
+-- Must see latest writes? -> Normal read (hits leaseholder)
+-- Stale by a few seconds is fine? -> AS OF SYSTEM TIME follower_read_timestamp()
+-- Need a specific historical snapshot? -> AS OF SYSTEM TIME '<timestamp>'
+-- Exporting data for analytics? -> AS OF SYSTEM TIME with follower reads
Schema Change Strategy
How to run DDL?
+-- Single column add/drop? -> Run as individual statement (no transaction)
+-- Multiple related changes? -> Run sequentially, one statement at a time
+-- Need to roll back DDL? -> You cannot -- DDL is not transactional. Plan carefully.
+-- Index creation on large table? -> All indexes are created online by default (do NOT use CONCURRENTLY -- it errors)
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- No transaction retry logic for 40001 errors -- CockroachDB WILL return these under normal concurrent load. Without retries, your application randomly fails under traffic.
- Using SERIAL or sequential primary keys -- creates a write hotspot on a single range, bottlenecking the entire cluster on one node.
- DDL inside explicit transactions -- most DDL can fail at COMMIT time with a partially applied state.
CREATE TABLE/CREATE INDEXare exceptions, but the safest practice is one DDL per implicit transaction. - Using advisory locks (
pg_advisory_lock,pg_try_advisory_lock) -- CockroachDB does NOT implement them. They are defined as no-op stubs that silently do nothing.
Medium Priority Issues:
- Not using
AS OF SYSTEM TIMEfor read-heavy workloads in multi-region -- forces all reads to hit the leaseholder, adding cross-region latency. - Running multiple DDL statements simultaneously in production -- each schema change consumes resources. Run them sequentially.
- Assuming PostgreSQL
LISTEN/NOTIFYworks -- CockroachDB does NOT supportLISTEN/NOTIFY. UseCHANGEFEEDfor real-time change streaming. - Using
CREATE DOMAIN-- not supported in CockroachDB. UseCHECKconstraints or application-level validation.
Common Mistakes:
- Connecting to port 5432 instead of 26257 -- CockroachDB default port is 26257.
- Expecting
SERIALto produce gapless sequential IDs -- CockroachDB'sunique_rowid()produces time-ordered but non-sequential values with gaps. - Forgetting that
numeric/decimaltypes return as strings in thepgdriver (same behavior as PostgreSQL). - Wrapping retry logic around individual statements instead of the entire transaction -- you must retry the FULL transaction, not just the failed statement.
- Using
SELECT ... FOR UPDATEwithout understanding it acquires locks across the cluster -- it works but has higher latency than in PostgreSQL.
Gotchas & Edge Cases:
40001errors can occur onCOMMIT, not just on individual statements. Your retry loop must catch errors fromCOMMITtoo.- CockroachDB's
SAVEPOINT cockroach_restartis a special savepoint name that enables the advanced retry protocol. Regular savepoints (SAVEPOINT my_savepoint) work normally for nested rollback. - Temporary tables exist but are experimental (
SET experimental_enable_temp_tables = 'on'). Creating many temp objects degrades DDL performance. READ COMMITTEDisolation is GA and enabled by default (sql.txn.read_committed_isolation.enabled = true), but transactions still default toSERIALIZABLE. Set per-transaction withBEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED, per-session withSET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED, or per-database withALTER DATABASE db SET default_transaction_isolation = 'read committed'.- CockroachDB's
pg_catalogandinformation_schemaare populated but may have differences from PostgreSQL -- some system tables have extra columns, some are missing columns. IMPORT INTOtakes the target table offline during the import. The table cannot serve reads or writes until the import completes.- Changefeed payload is limited. Complex JOINs or aggregations cannot be expressed directly in changefeed queries -- one table per changefeed.
- Float overflow returns
Infinityin CockroachDB (PostgreSQL returns an error). - Bitwise operator precedence differs from PostgreSQL. Use explicit parentheses.
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST implement transaction retry logic for SQLSTATE 40001 errors -- CockroachDB WILL return serialization errors under normal operation, unlike PostgreSQL where they are rare)
(You MUST use UUID with gen_random_uuid() for primary keys -- NEVER use SERIAL or sequential IDs, which cause distributed write hotspots)
(You MUST NOT put DDL statements inside explicit transactions -- most DDL runs as background jobs and can fail at COMMIT time with a partially applied state. CREATE TABLE/CREATE INDEX are exceptions but the safest practice is always: one DDL statement per implicit transaction)
(You MUST use Pool from pg for all database access -- same as PostgreSQL, but be aware that each node in the cluster is a valid connection target)
Failure to follow these rules will cause transaction failures under load, write hotspots that defeat distribution, DDL errors, and application crashes.
</critical_reminders>