Files
Baradb/clients/nim-allographer/.cursor/rules/branch/320-allographerpostgresql-非同期待機改善-設計書.mdc
T
dimgigov 6bfc5b3a3c
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
feat(clients): add nim-allographer client with BaraDB driver support
- Pure Nim wire protocol client (async + sync, no C FFI)
- Query Builder: fluent API, SQL generator, execution, transactions
- Schema Builder: create/alter/drop tables, column operations
- Connection pool with aging and timeout handling
- ORM integration via compile-time DB_BARADB switch
- Tests: test_open, test_query
- Documentation: README.md, PLAN_BARADB.md
2026-05-21 15:49:11 +03:00

157 lines
8.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: 320-allographerpostgresql-非同期待機改善-設計書ブランチでの開発時に読み込む
alwaysApply: false
---
allographer(PostgreSQL) 非同期待機改善 ブランチルール
===
このブランチで実装することは以下の通りです。
- allographer の PostgreSQL 実装に残っている `sleepAsync(10)` ベースの非同期待機を、libpq ソケット readiness ベースの待機へ置き換える。
- `query*`, `queryPlain*`, `exec*`, `getColumns*` など、同一問題を持つ待機経路を共通化して修正する。
- `dbOpen` 時に PostgreSQL 接続を non-blocking 化し、libpq 非同期 API の前提を明示的に保証する。
- 接続プール空き待機を `sleepAsync(10)` ポーリングから通知ベースへ移行し、プール枯渇時の待機遅延を抑制する。
- 公開 API 互換を維持したまま、httpbeast/httpx 利用時の DB 完了後の回収遅延を縮小する。
## 進捗
- [x] `src/allographer/query_builder/libs/postgres/postgres_impl.nim` の待機ループを調査し、`sleepAsync(10)` 依存箇所を一覧化する
- [x] `src/allographer/query_builder/libs/postgres/postgres_rdb.nim` を確認し、既存宣言で再利用できる libpq API と不足宣言を整理する(追加宣言不要。`pqsocket` / `pqflush` / `pqsetnonblocking` 等は既存)
- [x] PostgreSQL 待機ヘルパ `waitPgReadable` / `waitPgWritable` / `cancelQuery` の責務と配置を決める(`postgres_impl.nim` 内の非公開プロシージャ)
- [x] `PQsocket` / `PQconsumeInput` / `PQisBusy` / `PQgetResult` / `PQflush` に基づく共通クエリ待機ループを設計する(`pgFlushOutgoing` / `pgNextResult` / `pgEnsureIdle`
- [x] `query*`, `queryPlain*`, `exec*`, `getColumns*` の各経路を共通待機ロジックへ寄せる
- [x] タイムアウト時の `cancelQuery` 実行とエラーメッセージ方針を統一する(タイムアウトは `cancelQuery` 後に `DbError`
- [x] `src/allographer/query_builder/models/postgres/postgres_open.nim` で `pqsetnonblocking(conn, 1)` と検証処理を追加する
- [x] `src/allographer/query_builder/models/postgres/postgres_types.nim` に接続プール waiter キューを追加する(`waiters*: seq[Future[void]]`
- [x] `src/allographer/query_builder/models/postgres/postgres_exec.nim` の `getFreeConn` / `returnConn` を通知ベースへ置換する
- [x] PostgreSQL の単体・結合・回帰テストを追加または更新する(`tests/postgres/test_pool_wait.nim` 追加、既存 `test*.nim` 回帰)
- [x] `sleepAsync(10)` が PostgreSQL クエリ待機とプール空き待機から除去されたことを確認する
- [ ] README / documents / ルール文書に仕様差分が必要か確認し、必要分を同期する(利用者向け公開 API 変更なしのため未着手。必要なら追記)
- [x] MariaDB: `mariadb_rdb.nim` に `mysql_read_query_result``cdecl`)を追加
- [x] MariaDB: `mariadb_impl.nim` は接続ブロッキング前提で `mysql_real_query` / `mysql_read_query_result` / `mysql_fetch_row` / `free_result` を同期利用(`mysql_read_query_result_*` / `fetch_row_*` の async ループは環境でタイムアウト・デッドロックのため不使用)
- [x] MariaDB: `mariadb_open.nim` は `MYSQL_OPT_NONBLOCK` を付与しない(同上)
- [x] MariaDB: `mariadb_types` / `mariadb_exec` のプール空き待ちは通知ベース(`tests/mariadb/test_pool_wait.nim`
## 参考資料
- allographer の PostgreSQL 実装: `src/allographer/query_builder/libs/postgres/postgres_impl.nim`
- allographer の PostgreSQL 実行管理: `src/allographer/query_builder/models/postgres/postgres_exec.nim`
- allographer の PostgreSQL 接続初期化: `src/allographer/query_builder/models/postgres/postgres_open.nim`
- allographer の PostgreSQL 型定義: `src/allographer/query_builder/models/postgres/postgres_types.nim`
- libpq 非同期 API: `PQsocket`, `PQconsumeInput`, `PQisBusy`, `PQgetResult`, `PQsetnonblocking`, `PQflush`, `PQgetCancel`, `PQcancel`, `PQfreeCancel`
- Nim `asyncdispatch` のイベントループ仕様
- httpbeast / httpx の POSIX selector ループ実装
## 調査結果・設計まとめ
### 背景
`httpbeast` / `httpx` 利用時のみ、DB クエリ待機が `asynchttpserver` より大きく悪化する事象がある。
現状の allographer PostgreSQL 実装は、libpq 非同期 API を使いながらも、実際の待機を `await sleepAsync(10)` に依存している。
### 問題の本質
- 現状の PostgreSQL 待機は、ソケット readiness 待ちではなく 10ms 刻みのポーリングである
- Nim の `asyncdispatch` は `poll` / `waitFor` / `runForever` が回らない限りタイマーも進まない
- httpbeast / httpx の POSIX 経路は `selectInto(-1, ...)` による無期限ブロックを中心としており、`asyncdispatch.poll(0)` は限定的にしか回らない
- そのため `sleepAsync(10)` は実時間 10ms で復帰せず、「次に HTTP 側ループがたまたま dispatcher を回すまで」遅延する
- 結果として、DB は完了済みでも allographer 側の結果回収が遅れ、体感レイテンシが悪化する
### 設計目標
1. PostgreSQL クエリ待機から `sleepAsync` ポーリングを除去する
2. libpq ソケットの可読・可書込イベントで待機する
3. 接続プール空き待ちも通知ベースへ移行し、タイマー依存を減らす
4. `dbOpen`, `table`, `select`, `find`, `update` などの公開 API 互換を維持する
### 非目標
1. Query Builder API の破壊的変更
2. MySQL / SQLite への同時展開(MariaDB は PostgreSQL と同方針で非同期待機を実装済み)
3. 初回対応で全 OS 最適化を完了させること
### 実装方針
#### 1. libpq 待機を FD イベント待機へ変更する
変更対象:
- `src/allographer/query_builder/libs/postgres/postgres_impl.nim`
- `src/allographer/query_builder/libs/postgres/postgres_rdb.nim`
追加する内部ヘルパ:
- `waitPgReadable(db: PPGconn, timeoutMs: int): Future[bool]`
- `waitPgWritable(db: PPGconn, timeoutMs: int): Future[bool]`
- `cancelQuery(db: PPGconn)`
要点:
- `pqsocket(db)` で FD を得る
- 必要に応じて dispatcher に `AsyncFD` を登録する
- read / write readiness を Future 完了で待つ
- タイムアウト管理を行う
- 登録リークや未解除ハンドラを残さない
#### 2. クエリ実行ループを共通化する
送信フェーズ:
1. `pqsendQueryParams(...)` を呼ぶ
2. `pqflush == 1` の間は `waitPgWritable` で待機する
受信フェーズ:
1. `pqconsumeInput`
2. `pqisBusy == 1` なら `waitPgReadable` で待機する
3. ready 後に `pqgetResult` を drain する
タイムアウト時は `cancelQuery` を実行し、エラーを返す。
#### 3. `dbOpen` で non-blocking を保証する
変更対象:
- `src/allographer/query_builder/models/postgres/postgres_open.nim`
仕様:
1. `pqsetnonblocking(conn, 1)` を実行する
2. 失敗時は `DbError`
3. `pqisnonblocking(conn) == 1` を確認し、保証できない場合は例外化する
#### 4. 接続プール空き待ちを通知ベースに変更する
変更対象:
- `src/allographer/query_builder/models/postgres/postgres_types.nim`
- `src/allographer/query_builder/models/postgres/postgres_exec.nim`
方針:
- `Connections` に `waiters*: Deque[Future[void]]` を追加する
- `getFreeConn` は空きがなければ waiter を登録して待機する
- `returnConn` は `isBusy = false` にした後、未完了 waiter を 1 つだけ起こす
- 余剰 waiter や完了済み waiter は掃除する
### 失敗時挙動
1. `pqsocket < 0` は `DbError`
2. タイムアウト時は `cancelQuery` 実行後に `DbError`
3. `cancelQuery` 失敗時は追加情報付きで例外化
4. dispatcher 登録・解除エラーは握り潰さず上位へ伝播する
### テスト方針
単体テスト:
1. `waitPgReadable` が read event で復帰する
2. timeout 時に `false` を返す
3. `cancelQuery` が後始末まで行う
4. `getFreeConn` が通知で復帰する
結合テスト:
1. httpbeast / httpx + Postgres のレイテンシ回帰を確認する
2. `asynchttpserver` と比較して極端な乖離がないことを確認する
3. プール枯渇時の待機が 10ms 刻みポーリングに依存しないことを確認する
回帰テスト:
1. 既存 CRUD クエリの結果互換
2. transaction 系 API の動作
3. 例外時メッセージが意味を保っていること
### 受け入れ基準
1. PostgreSQL クエリ待機ループから `await sleepAsync(10)` が除去されている
2. httpbeast / httpx で DB 完了後の回収遅延が顕著に増幅しない
3. プール空き待機で `sleepAsync` ポーリングを使わない
4. 公開 API 変更なしで既存テストと追加テストが成立する