@aws-blocks/bb-distributed-data

中文文档
0.3.0发布于 2026/11/02Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 6.3万

Serverless SQL database backed by Amazon Aurora DSQL. Zero-ops, instant provisioning, scale-to-zero, and optionally multi-region active-active writes.

原始使用文档
文档目录13 节
  1. Quick Start
  2. Transactions (Optimistic Concurrency Control)
  3. Migrations
  4. PostgreSQL Compatibility
  5. Transaction Constraints
  6. Kysely Query Builder
  7. Error Handling
  8. Testing OCC Conflicts
  9. What It Provisions (AWS)
  10. Local Development
  11. Configuration
  12. Package Export Conditions
  13. Differences from Database (Aurora)

@aws-blocks/bb-distributed-data

Serverless SQL database backed by Amazon Aurora DSQL. Zero-ops, instant provisioning, scale-to-zero, and optionally multi-region active-active writes.

When to use: Serverless apps that need to scale without ops overhead, workloads with zero idle cost requirements, multi-region active-active writes, or any SQL app where you don't need Row Level Security, triggers, procedures, or PL/pgSQL functions.

When NOT to use: If you need Row Level Security, triggers, procedures, or PL/pgSQL functions — use Database (Aurora). Aurora DSQL supports SQL-language functions. If you need transactions that must not fail at commit under contention — use Database. If you're connecting to Supabase — use Database with fromExisting().

Design & mock parity details: DESIGN.md

Quick Start

import { DistributedDatabase, sql } from '@aws-blocks/bb-distributed-data';

const db = new DistributedDatabase(scope, 'main', {
  migrationsPath: './aws-blocks/dsql-migrations',
});

// Same query API as Database — parameterized via sql tagged template
const users = await db.query<{ id: string; name: string }>(
  sql`SELECT * FROM users WHERE active = ${true}`
);

const user = await db.queryOne<{ id: string; name: string }>(
  sql`SELECT * FROM users WHERE id = ${userId}`
);

const { rowCount } = await db.execute(
  sql`INSERT INTO users (id, name, email) VALUES (${id}, ${name}, ${email})`
);

Transactions (Optimistic Concurrency Control)

DSQL uses OCC — transactions may fail at commit if another transaction modified the same rows. The callback executes exactly once unless you opt into retry.

// Default: no retry. Throws SerializationFailureException (HTTP 409 Conflict, retriable) on conflict.
await db.transaction(async (tx) => {
  await tx.execute(sql`UPDATE accounts SET balance = balance - ${100} WHERE id = ${fromId}`);
  await tx.execute(sql`UPDATE accounts SET balance = balance + ${100} WHERE id = ${toId}`);
});

// Opt-in retry: callback may execute multiple times.
// ⚠️ Do NOT include external side effects (HTTP calls, emails) inside.
await db.transaction(async (tx) => {
  await tx.execute(sql`UPDATE accounts SET balance = balance - ${100} WHERE id = ${fromId}`);
  await tx.execute(sql`UPDATE accounts SET balance = balance + ${100} WHERE id = ${toId}`);
}, { retryOnConflict: true, maxRetries: 3 });

The second argument to transaction() accepts:

interface TransactionOptions {
  /** Retry on OCC conflict. Callback may execute multiple times. @default false */
  retryOnConflict?: boolean;
  /** Max retry attempts. Only applies when retryOnConflict is true. @default 3 */
  maxRetries?: number;
}

Migrations

One DDL statement per file. DML in separate files. This matches DSQL's transaction constraints.

aws-blocks/dsql-migrations/
  001_create_users.sql       ← single DDL
  002_create_posts.sql       ← single DDL
  003_create_index.sql       ← single DDL (CREATE INDEX ASYNC)
  004_seed_admin.sql         ← DML only
-- 001_create_users.sql
CREATE TABLE users (
  id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT UNIQUE NOT NULL,
  name TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 003_create_index.sql
CREATE INDEX ASYNC idx_users_email ON users(email);

Migrations are checked with dsql-lint at synth time.

Set migrationsPath to a path relative to your project root (e.g. './aws-blocks/dsql-migrations'); it's resolved at synth from the directory you run cdk / npm run deploy in. That's the simplest reliable pattern.

You don't need fileURLToPath(import.meta.url) for this. Your backend module runs as ESM locally but is bundled to CommonJS in Lambda, where import.meta is empty. AWS Blocks shims import.meta.url / import.meta.dirname in the bundle so it won't crash at load — but at runtime those resolve to the bundled output location, not your source tree. So don't use import.meta.url to read a file relative to your source at request time; inline the data or ship it as a Lambda asset instead.

PostgreSQL Compatibility

Aurora DSQL is PostgreSQL wire-protocol compatible. SQL feature compatibility differs from PostgreSQL; see the Aurora DSQL SQL compatibility documentation for full details.

Not SupportedAlternative
Row Level SecurityWHERE clause filtering in app code
TriggersEvent-driven logic (EventBridge, Lambda)
ProceduresLANGUAGE SQL functions or app logic
PL/pgSQL functionsLANGUAGE SQL functions or app logic
SERIAL / BIGSERIALBIGINT GENERATED BY DEFAULT AS IDENTITY (CACHE 1) or UUIDs (gen_random_uuid()); CACHE is required for identity columns and sequences and must be 1 or >= 65536
TRUNCATEDELETE FROM
Temporary tablesCTEs or subqueries
LISTEN / NOTIFYAppSync Events, EventBridge, or polling
ExtensionsNot available
ADD COLUMN with DEFAULTAdd the column without a default, backfill existing rows, then use ALTER COLUMN ... SET DEFAULT
Index key sort direction (ASC/DESC)Omit it; enforce ordering with ORDER BY in queries (NULLS FIRST/LAST is supported)

Transaction Constraints

ConstraintLimit
Concurrency modelOCC (may conflict at commit)
Isolation levelFixed: Repeatable Read
Max rows mutated per transaction3,000
Max data per transaction10 MiB
Max transaction duration5 minutes
DDL per transaction1 statement max
DDL + DML mixingNot allowed

Kysely Query Builder

import { createKyselyAdapter } from '@aws-blocks/bb-distributed-data';

interface Schema {
  users: { id: string; email: string; name: string };
}

const kysely = createKyselyAdapter<Schema>(db);

const users = await kysely
  .selectFrom('users')
  .where('email', '=', '[email protected]')
  .selectAll()
  .execute();

Do not use Kysely's .addForeignKeyConstraint() — it will fail on DSQL.

Error Handling

import { DistributedDatabaseErrors } from '@aws-blocks/bb-distributed-data';
import { isBlocksError } from '@aws-blocks/core';

try {
  await db.transaction(async (tx) => { /* ... */ });
} catch (e: unknown) {
  if (isBlocksError(e, DistributedDatabaseErrors.SerializationFailure)) {
    // OCC conflict — transaction was NOT committed. Serialized as HTTP 409
    // (Conflict), retriable — safe to retry.
  }
  if (isBlocksError(e, DistributedDatabaseErrors.UniqueConstraintViolation)) {
    // Duplicate key — serialized as HTTP 409 (Conflict), not retriable
    // (a blind retry of the same insert fails identically).
  }
  if (isBlocksError(e, DistributedDatabaseErrors.QueryFailed)) {
    // General query failure
  }
  if (isBlocksError(e, DistributedDatabaseErrors.TransactionRowLimitExceeded)) {
    // More than 3,000 rows mutated in one transaction
  }
  if (isBlocksError(e, DistributedDatabaseErrors.ConnectionFailed)) {
    // Cannot reach the DSQL cluster
  }
  if (isBlocksError(e, DistributedDatabaseErrors.TransactionFailed)) {
    // Transaction could not commit (general failure distinct from serialization/row-limit)
  }
}

Testing OCC Conflicts

The mock provides a simulateConflict() helper for unit testing. It only works against the local mock: in the deployed AWS runtime it throws, and during cdk synth it throws the usual synth-time error.

db.simulateConflict(); // next commit will fail with SerializationFailureException

await expect(db.transaction(fn)).rejects.toThrow('SerializationFailureException');
db.simulateConflict(); // first attempt fails, second succeeds
const result = await db.transaction(fn, { retryOnConflict: true });

What It Provisions (AWS)

  • Aurora DSQL Cluster — Serverless, no VPC required, public endpoint
  • Migration Lambda — Runs .sql files on deploy via CustomResource with retry
  • IAM — dsql:DbConnect (app Lambda, DML only), dsql:DbConnectAdmin (migration Lambda, DDL)
  • Environment variables — BLOCKS_{name}_ENDPOINT, BLOCKS_{name}_REGION

No VPC, no secrets, no security groups, no proxy. DSQL uses IAM token authentication.

Local Development

  • Engine: PGlite (WASM PostgreSQL) wrapped with a DSQL compatibility layer
  • Storage: .bb-data/{fullId}/ — persists across restarts
  • Validation: dsql-lint checks SQL before PGlite executes it
  • Transaction tracking: DDL/DML mixing and 3,000-row limit enforced locally

Migration SQL is also checked with dsql-lint during CDK synthesis, before it is bundled for deployment.

Configuration

interface DistributedDatabaseOptions {
  /** Path to directory containing numbered .sql migration files. */
  migrationsPath?: string;
  /** Removal policy. @default 'retain' */
  removalPolicy?: 'destroy' | 'retain';
  /** Optional logger for internal operations. When omitted, a default Logger at error level is created. */
  logger?: ChildLogger;
}

Package Export Conditions

{
  "exports": {
    ".": {
      "cdk": "./dist/index.cdk.js",
      "aws-runtime": "./dist/index.aws.js",
      "default": "./dist/index.mock.js"
    }
  }
}

Differences from Database (Aurora)

AspectDatabase (bb-data)DistributedDatabase (bb-distributed-data)
EngineAurora Serverless v2 (Data API)Aurora DSQL (pg + IAM)
PostgreSQL compatibilityFullWire protocol compatible; see SQL compatibility details
TransactionsPessimistic (exactly-once)OCC (may conflict at commit)
withRLS()✅❌
fromExisting()✅❌
crud()✅❌
Foreign keys✅✅
Multi-region❌✅ Active-active
VPC requiredYesNo
Deploy time~10 minSeconds
Idle cost$0 (0 ACU)$0

版本与发布记录

@aws-blocks/bb-distributed-data 共发布 15 个版本,首次发布于 2026/06/16;过去 12 个月发布了 15 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.3.0latest2026/11/025 天前
0.2.22026/10/1622 天前
0.0.0-canary.esql-canary.c93c81b.1791235863canary-esql-canary2026/10/061 个月前
0.2.12026/10/051 个月前
0.0.0-canary.esql-canary.f7f39ac.17909891272026/10/031 个月前
0.0.0-canary.ci-publish-canary-npm.f53dfdc.1790988617canary-ci-publish-canary-npm2026/10/031 个月前

关于 @aws-blocks/bb-distributed-data 的常见问题

@aws-blocks/bb-distributed-data 是什么?

Serverless SQL database backed by Amazon Aurora DSQL. Zero-ops, instant provisioning, scale-to-zero, and optionally multi-region active-active writes.

如何安装 @aws-blocks/bb-distributed-data?

在项目目录运行 npm install @aws-blocks/bb-distributed-data,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:kysely(>=0.28.12 <0.30.0)、constructs(^10.6.0)、aws-cdk-lib(>=2.100.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@aws-blocks/bb-distributed-data 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aws-blocks/bb-distributed-data 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@aws-blocks/bb-distributed-data 可以商用吗?

@aws-blocks/bb-distributed-data 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@aws-blocks/bb-distributed-data 的下载量有多少?

根据 npm 官方下载统计,@aws-blocks/bb-distributed-data 在 2026/10/07 – 2026/11/05 这 30 天内被下载 63,343 次,日均约 2,111 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aws-blocks/bb-distributed-data 还在维护吗?

最新版本 0.3.0 发布于 2026/11/02(5 天前);至今共发布 15 个版本,其中过去 12 个月发布了 15 个;首次发布于 2026/06/16。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。