From 0bbdc5ae6f00376cd7e9ac1075dd452b73c27190 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sat, 15 Mar 2025 10:55:06 -0500 Subject: [PATCH 1/2] Batch network packets in prepared statements Fixes #3340 Fixes #3325 Fixes #3098 --- packages/pg/bench.js | 16 +++++++++++----- packages/pg/lib/query.js | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/pg/bench.js b/packages/pg/bench.js index 223b10278..8f966c581 100644 --- a/packages/pg/bench.js +++ b/packages/pg/bench.js @@ -47,21 +47,27 @@ const run = async () => { for (let i = 0; i < 4; i++) { let queries = await bench(client, params, seconds * 1000) console.log('') - console.log('little queries:', queries) + console.log('param queries:', queries) console.log('qps', queries / seconds) - console.log('on my laptop best so far seen 733 qps') + console.log('on my laptop best so far seen 987 qps') + + queries = await bench(client, { ...params, name: 'params' }, seconds * 1000) + console.log('') + console.log('named queries:', queries) + console.log('qps', queries / seconds) + console.log('on my laptop best so far seen 937 qps') console.log('') queries = await bench(client, seq, seconds * 1000) console.log('sequence queries:', queries) console.log('qps', queries / seconds) - console.log('on my laptop best so far seen 1309 qps') + console.log('on my laptop best so far seen 2725 qps') console.log('') queries = await bench(client, insert, seconds * 1000) console.log('insert queries:', queries) console.log('qps', queries / seconds) - console.log('on my laptop best so far seen 6445 qps') + console.log('on my laptop best so far seen 27383 qps') console.log('') console.log('Warming up bytea test') @@ -75,7 +81,7 @@ const run = async () => { const time = Date.now() - start console.log('bytea time:', time, 'ms') console.log('bytea length:', results.rows[0].data.byteLength, 'bytes') - console.log('on my laptop best so far seen 1107ms and 104857600 bytes') + console.log('on my laptop best so far seen 1407ms and 104857600 bytes') await new Promise((resolve) => setTimeout(resolve, 250)) } diff --git a/packages/pg/lib/query.js b/packages/pg/lib/query.js index fbef341bf..a7d70d97c 100644 --- a/packages/pg/lib/query.js +++ b/packages/pg/lib/query.js @@ -161,7 +161,21 @@ class Query extends EventEmitter { return new Error('Query values must be an array') } if (this.requiresPreparation()) { - this.prepare(connection) + // If we're using the extended query protocol we fire off several separate commands + // to the backend. On some versions of node & some operating system versions + // the network stack writes each message separately instead of buffering them together + // causing the client & network to send more slowly. Corking & uncorking the stream + // allows node to buffer up the messages internally before sending them all off at once. + // note: we're checking for existence of cork/uncork because on some versions of streams + // might not have this (cloudflare?) + connection.stream.cork && connection.stream.cork() + try { + this.prepare(connection) + } finally { + // while unlikely for this.prepare to throw, if it does & we don't uncork this stream + // this client becomes unresponsive, so put in finally block "just in case" + connection.stream.uncork && connection.stream.uncork() + } } else { connection.query(this.text) } From e67418faad45d94586b1dc2ca589f4a1d5f491b5 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Sat, 15 Mar 2025 11:14:46 -0500 Subject: [PATCH 2/2] Fix type-o but mostly retrigger build for CF pages preview --- packages/pg/lib/query.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pg/lib/query.js b/packages/pg/lib/query.js index a7d70d97c..06b582f6f 100644 --- a/packages/pg/lib/query.js +++ b/packages/pg/lib/query.js @@ -166,7 +166,7 @@ class Query extends EventEmitter { // the network stack writes each message separately instead of buffering them together // causing the client & network to send more slowly. Corking & uncorking the stream // allows node to buffer up the messages internally before sending them all off at once. - // note: we're checking for existence of cork/uncork because on some versions of streams + // note: we're checking for existence of cork/uncork because some versions of streams // might not have this (cloudflare?) connection.stream.cork && connection.stream.cork() try {