@h-ai/reldb 0.1.0-alpha.68 → 0.1.0-alpha.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -588,7 +588,7 @@ Common error codes:
588
588
  | `HaiReldbError.TIMEOUT` | `hai:reldb:009` | MySQL acquisition deadline exceeded |
589
589
  | `HaiReldbError.POOL_EXHAUSTED` | `hai:reldb:011` | MySQL acquisition queue is full |
590
590
 
591
- Custom SQL methods in `BaseReldbCrudRepository` subclasses must check `await this.ensureReady()`. Construction starts table creation; service init must await candidate readiness before publication. PostgreSQL concurrent first `CREATE TABLE IF NOT EXISTS` retries the same DDL once only for `23505/pg_type_typname_nsp_index` catalog contention; other errors remain failures.
591
+ Custom SQL methods in `BaseReldbCrudRepository` subclasses must check `await this.ensureReady()`. Construction starts table creation; service init must await candidate readiness before publication. PostgreSQL retries a single autocommit `CREATE TABLE/INDEX IF NOT EXISTS` once only for `23505/pg_class_relname_nsp_index` catalog contention (also `pg_type_typname_nsp_index` for tables), or a table row-type race reported as `42710` with server routine `heap_create_with_catalog` / `TypeCreate`, consistently through DDL and SQL entry points. A failed retry remains an error. Transactions, business uniqueness errors and other failures are not retried. Initial connection probing attempts temporary DNS `EAI_AGAIN` failures at most three times, with 250/500 ms delays; business SQL is never replayed.
592
592
 
593
593
  ## Production shared state
594
594
 
package/README.ja.md CHANGED
@@ -588,7 +588,7 @@ if (!result.success) {
588
588
  | `HaiReldbError.TIMEOUT` | `hai:reldb:009` | MySQL 接続取得の期限超過 |
589
589
  | `HaiReldbError.POOL_EXHAUSTED` | `hai:reldb:011` | MySQL 接続待機キューが満杯 |
590
590
 
591
- `BaseReldbCrudRepository` の派生クラスで独自 SQL を実行する前に `await this.ensureReady()` を確認します。構築時に建表を開始するだけなので、サービス init は候補の準備完了後に公開します。PostgreSQL の並行初回 `CREATE TABLE IF NOT EXISTS` は `23505/pg_type_typname_nsp_index` の競合だけ同じ DDL を一度再実行し、他のエラーは失敗のままです。
591
+ `BaseReldbCrudRepository` の派生クラスで独自 SQL を実行する前に `await this.ensureReady()` を確認します。構築時に建表を開始するだけなので、サービス init は候補の準備完了後に公開します。PostgreSQL の自動コミットによる単一の `CREATE TABLE/INDEX IF NOT EXISTS` は、`23505/pg_class_relname_nsp_index`(建表は `pg_type_typname_nsp_index` も対象)の競合、または建表時にサーバー routine が `heap_create_with_catalog` / `TypeCreate` である `42710` の行型競合に限り、DDL/SQL の両入口で一度だけ再実行します。再試行も失敗した場合はエラーを返します。トランザクション、業務の一意制約、その他の失敗は再試行しません。初期接続確認の一時 DNS エラー `EAI_AGAIN` は 250/500 ms 間隔で最大三回試行し、業務 SQL は再送しません。
592
592
 
593
593
  ## 本番の共有状態
594
594
 
package/README.md CHANGED
@@ -588,7 +588,7 @@ if (!result.success) {
588
588
  | `HaiReldbError.TIMEOUT` | `hai:reldb:009` | MySQL 连接获取超过截止期限 |
589
589
  | `HaiReldbError.POOL_EXHAUSTED` | `hai:reldb:011` | MySQL 连接等待队列已满 |
590
590
 
591
- 派生 `BaseReldbCrudRepository` 的自定义 SQL 方法先检查 `await this.ensureReady()`;构造函数启动自动建表不等于仓库就绪,服务 init 必须等待候选仓库的就绪入口再发布。PostgreSQL 并发首次 `CREATE TABLE IF NOT EXISTS` 的 `23505/pg_type_typname_nsp_index` 目录冲突仅重执行相同 DDL 一次,其他错误继续失败。
591
+ 派生 `BaseReldbCrudRepository` 的自定义 SQL 方法先检查 `await this.ensureReady()`;构造函数启动自动建表不等于仓库就绪,服务 init 必须等待候选仓库的就绪入口再发布。PostgreSQL 自动提交的单条 `CREATE TABLE/INDEX IF NOT EXISTS` 仅对 `23505/pg_class_relname_nsp_index`(建表还包括 `pg_type_typname_nsp_index`)目录竞争,或建表时 `42710` 且服务端 routine 为 `heap_create_with_catalog` / `TypeCreate` 的行类型竞争重执行一次;重试仍失败则保留错误,DDL 与 SQL 入口一致;事务、业务唯一约束及其他错误不自动重试。初始化连接探测仅对临时 DNS 错误 `EAI_AGAIN` 最多尝试三次,间隔 250/500 ms;不重放业务 SQL。
592
592
 
593
593
  ## 生产共享状态
594
594
 
package/dist/index.js CHANGED
@@ -5,8 +5,8 @@ import process from 'process';
5
5
  import { createRequire } from 'module';
6
6
  import { isIP } from 'net';
7
7
  import { performance as performance$1 } from 'perf_hooks';
8
- import { existsSync } from 'fs';
9
8
  import { setTimeout as setTimeout$1 } from 'timers/promises';
9
+ import { existsSync } from 'fs';
10
10
  import { Worker } from 'worker_threads';
11
11
 
12
12
  // messages/en-US.json
@@ -2436,18 +2436,23 @@ function createPostgresProvider() {
2436
2436
  const result = await queryFn(convertPlaceholders(sql), params);
2437
2437
  return result.rows;
2438
2438
  }
2439
+ async function executeStatement(sql, params) {
2440
+ try {
2441
+ return await pool.query(sql, params);
2442
+ } catch (error) {
2443
+ const kind = /^\s*CREATE\s+(TABLE|(?:UNIQUE\s+)?INDEX)\s+IF\s+NOT\s+EXISTS\s+/i.exec(sql)?.[1]?.toUpperCase();
2444
+ const single = !sql.trim().replace(/;$/, "").includes(";");
2445
+ const catalogConflict = typeof error === "object" && error !== null && "code" in error && (error.code === "23505" && "schema" in error && error.schema === "pg_catalog" && "constraint" in error && (error.constraint === "pg_class_relname_nsp_index" || kind === "TABLE" && error.constraint === "pg_type_typname_nsp_index") || kind === "TABLE" && error.code === "42710" && "routine" in error && (error.routine === "heap_create_with_catalog" || error.routine === "TypeCreate"));
2446
+ if (!kind || !single || !catalogConflict)
2447
+ throw error;
2448
+ return pool.query(sql, params);
2449
+ }
2450
+ }
2439
2451
  const rawDdl = {
2440
2452
  async createTable(name, columns, ifNotExists = true) {
2441
2453
  const quotedTable = quoteIdentifier(name);
2442
2454
  const sql = buildDefaultCreateTableSql(pgBuildColumnSql, quotedTable, columns, ifNotExists);
2443
- try {
2444
- await pool.query(sql);
2445
- } catch (error) {
2446
- if (!ifNotExists || typeof error !== "object" || error === null || !("code" in error) || error.code !== "23505" || !("constraint" in error) || error.constraint !== "pg_type_typname_nsp_index") {
2447
- throw error;
2448
- }
2449
- await pool.query(sql);
2450
- }
2455
+ await executeStatement(sql);
2451
2456
  return ok(void 0);
2452
2457
  },
2453
2458
  async dropTable(name, ifExists) {
@@ -2470,7 +2475,7 @@ function createPostgresProvider() {
2470
2475
  },
2471
2476
  async createIndex(table, index, def) {
2472
2477
  const sql = buildDefaultCreateIndexSql(quoteIdentifier, quoteIdentifier(table), quoteIdentifier(index), def);
2473
- await pool.query(sql);
2478
+ await executeStatement(sql);
2474
2479
  return ok(void 0);
2475
2480
  },
2476
2481
  async dropIndex(index, ifExists = true) {
@@ -2479,7 +2484,7 @@ function createPostgresProvider() {
2479
2484
  return ok(void 0);
2480
2485
  },
2481
2486
  async raw(sql) {
2482
- await pool.query(sql);
2487
+ await executeStatement(sql);
2483
2488
  return ok(void 0);
2484
2489
  }
2485
2490
  };
@@ -2493,7 +2498,7 @@ function createPostgresProvider() {
2493
2498
  return ok(rows[0] ?? null);
2494
2499
  },
2495
2500
  async execute(sql, params) {
2496
- const result = await pool.query(convertPlaceholders(sql), params);
2501
+ const result = await executeStatement(convertPlaceholders(sql), params);
2497
2502
  return ok({ changes: result.rowCount ?? 0 });
2498
2503
  },
2499
2504
  async batch(statements) {
@@ -2627,7 +2632,16 @@ function createPostgresProvider() {
2627
2632
  name: error instanceof Error ? "Error" : "UnknownError"
2628
2633
  });
2629
2634
  });
2630
- await pool.query("SELECT 1");
2635
+ for (let attempt = 0; ; attempt++) {
2636
+ try {
2637
+ await pool.query("SELECT 1");
2638
+ break;
2639
+ } catch (error) {
2640
+ if (attempt >= 2 || typeof error !== "object" || error === null || !("code" in error) || error.code !== "EAI_AGAIN")
2641
+ throw error;
2642
+ await setTimeout$1(250 * (attempt + 1));
2643
+ }
2644
+ }
2631
2645
  ready = true;
2632
2646
  currentConfig2 = config;
2633
2647
  logger3.info("Connected to PostgreSQL", { host: config.host, port: config.port, database: config.database });