turbine-orm 0.7.1 → 0.9.0

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.
@@ -8,25 +8,74 @@
8
8
  * How it works:
9
9
  * 1. Each query method (findUnique, count, etc.) can produce a DeferredQuery descriptor
10
10
  * containing the SQL, params, and a transform function.
11
- * 2. pipeline() collects these descriptors, executes them in a single Postgres
12
- * pipeline/transaction, and maps each result through its transform.
11
+ * 2. pipeline() collects these descriptors, checks whether the underlying pool client
12
+ * supports the extended-query pipeline protocol, and either:
13
+ * (a) executes them via real Postgres pipeline protocol (one TCP flush), or
14
+ * (b) falls back to sequential execution on a single connection.
13
15
  *
14
- * In the production Turbine engine, this would go through the Rust proxy which uses
15
- * actual Postgres pipeline protocol (libpq PQpipelineEnter). For the TS SDK prototype,
16
- * we simulate it by running queries concurrently on a single connection or via
17
- * a multi-statement batch.
16
+ * Real pipeline mode uses `src/pipeline-submittable.ts` which drives the pg Connection's
17
+ * wire-protocol methods (parse/bind/describe/execute/sync) directly with listener-swap.
18
+ *
19
+ * Sequential fallback covers HTTP-based drivers (Neon HTTP, Vercel Postgres, Cloudflare
20
+ * Hyperdrive), mock pools in tests, and any pool that doesn't expose pg internals.
18
21
  */
19
22
  Object.defineProperty(exports, "__esModule", { value: true });
20
23
  exports.executePipeline = executePipeline;
24
+ exports.pipelineSupported = pipelineSupported;
21
25
  const errors_js_1 = require("./errors.js");
26
+ const pipeline_submittable_js_1 = require("./pipeline-submittable.js");
27
+ /**
28
+ * Execute queries sequentially on an already-acquired connection.
29
+ * This is the fallback path for clients that don't support the extended-query
30
+ * pipeline protocol (HTTP drivers, mocks, etc.).
31
+ *
32
+ * The caller is responsible for acquiring the client and releasing it after
33
+ * this function completes (in the finally block).
34
+ */
35
+ async function runSequential(client, queries, options = {}) {
36
+ const { transactional = true } = options;
37
+ try {
38
+ if (transactional) {
39
+ await client.query('BEGIN');
40
+ }
41
+ const results = [];
42
+ for (const q of queries) {
43
+ let raw;
44
+ try {
45
+ raw = await client.query(q.sql, q.params);
46
+ }
47
+ catch (err) {
48
+ throw (0, errors_js_1.wrapPgError)(err);
49
+ }
50
+ results.push(q.transform(raw));
51
+ }
52
+ if (transactional) {
53
+ await client.query('COMMIT');
54
+ }
55
+ return results;
56
+ }
57
+ catch (err) {
58
+ if (transactional) {
59
+ try {
60
+ await client.query('ROLLBACK');
61
+ }
62
+ catch {
63
+ // Best-effort rollback
64
+ }
65
+ }
66
+ throw err;
67
+ }
68
+ }
22
69
  // ---------------------------------------------------------------------------
23
- // Pipeline executor
70
+ // Pipeline executor (public)
24
71
  // ---------------------------------------------------------------------------
25
72
  /**
26
73
  * Execute multiple deferred queries in a single batch.
27
74
  *
28
- * Uses a single connection from the pool and runs all queries within
29
- * a transaction to guarantee consistency and minimize round-trips.
75
+ * On pg.Pool-backed connections with the standard TCP driver, this uses the
76
+ * real Postgres extended-query pipeline protocol for true 1-RTT execution.
77
+ * On HTTP-based drivers (Neon HTTP, Vercel Postgres, etc.) or mock pools,
78
+ * it falls back to sequential execution on a single connection.
30
79
  *
31
80
  * @example
32
81
  * ```ts
@@ -37,42 +86,46 @@ const errors_js_1 = require("./errors.js");
37
86
  * ]);
38
87
  * ```
39
88
  */
40
- async function executePipeline(pool, queries) {
89
+ async function executePipeline(pool, queries, options) {
41
90
  if (queries.length === 0) {
42
91
  return [];
43
92
  }
44
- // Acquire a single connection for the entire batch
93
+ // Acquire a single client — reused for both capability check and execution
45
94
  const client = await pool.connect();
46
95
  try {
47
- // Wrap in a transaction for consistency
48
- await client.query('BEGIN');
49
- // Execute all queries on the same connection — in sequence on a single
50
- // connection this avoids pool checkout overhead, and the Postgres server
51
- // processes them as a tight batch.
52
- //
53
- // Execute queries sequentially on the same connection to avoid the
54
- // "already executing a query" deprecation in pg@8. This is still faster
55
- // than separate pool checkouts because we skip N-1 acquire/release cycles.
56
- // Future: use actual Postgres pipeline protocol for true pipelining.
57
- const results = [];
58
- for (const q of queries) {
59
- let raw;
60
- try {
61
- raw = await client.query(q.sql, q.params);
62
- }
63
- catch (err) {
64
- throw (0, errors_js_1.wrapPgError)(err);
65
- }
66
- results.push(q.transform(raw));
96
+ if ((0, pipeline_submittable_js_1.supportsExtendedPipeline)(client)) {
97
+ // Real pipeline path — uses extended-query protocol wire methods
98
+ const pipelineOptions = {
99
+ transactional: options?.transactional ?? true,
100
+ timeout: options?.timeout,
101
+ };
102
+ const results = await (0, pipeline_submittable_js_1.runPipelined)(client, queries, pipelineOptions);
103
+ return results;
67
104
  }
68
- await client.query('COMMIT');
69
- return results;
70
- }
71
- catch (err) {
72
- await client.query('ROLLBACK');
73
- throw err;
105
+ // Sequential fallback — reuses the same client
106
+ return await runSequential(client, queries, options);
74
107
  }
75
108
  finally {
76
109
  client.release();
77
110
  }
78
111
  }
112
+ /**
113
+ * Check whether a pool supports the real pipeline protocol.
114
+ * Call this to determine at runtime whether pipelines will use the fast path
115
+ * or fall back to sequential execution.
116
+ *
117
+ * Note: This acquires and immediately releases a connection to inspect it.
118
+ */
119
+ async function pipelineSupported(pool) {
120
+ let client;
121
+ try {
122
+ client = await pool.connect();
123
+ return (0, pipeline_submittable_js_1.supportsExtendedPipeline)(client);
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ finally {
129
+ client?.release();
130
+ }
131
+ }