I have a postgresql table fulled with thousands of time-series data each day. I have an app allowing user to retrieve those data. Queries can take up from 200ms to 30 sec depending on the time range, so these must be cancelable to avoid useless load on production.
As there is billions of data, the use of stream to retrieve them is unavoidable.
So I managed to get a working endpoint with data stream like it figures in pg-promise docs, and make it cancelable by closing the cursor within pg-query-stream
.
Here is a sample of what is done within this endpoint (dataStream() is called after having building the query) :
const pgp = require("pg-promise")();
const QueryStream = require("pg-query-stream");
const db = pgp({
host: "1.2.3.4",
port: 5432,
database: "db",
user: "user",
password: "password",
max: 2,
});
// query is an SQL string
dataStream(query, req, res, next) {
const qs = new QueryStream(query);
// "close" event is triggered on client request cancelation
req.on("close", () => {
qs.destroy();
});
return db.stream(qs, s => {
s.pipe(JSONStream.stringify()).pipe(res);
s.on("error", error => handleError(error));
})
.catch(error => handleError(error, query));
}
It works great for a few calls but at that at some point (performing 8 to 10 calls quickly to check cancelability), the app crash with this stack :
\node_modules\pg-promise\node_modules\pg\lib\client.js:346
if (self.activeQuery.name) {
^
TypeError: Cannot read property 'name' of null
at Connection.<anonymous> (\node_modules\pg-promise\node_modules\pg\lib\client.js:346:26)
at Connection.emit (events.js:311:20)
at Socket.<anonymous> (\node_modules\pg-promise\node_modules\pg\lib\connection.js:120:12)
at Socket.emit (events.js:311:20)
at addChunk (_stream_readable.js:294:12)
at readableAddChunk (_stream_readable.js:275:11)
at Socket.Readable.push (_stream_readable.js:209:10)
at TCP.onStreamRead (internal/stream_base_commons.js:186:23)
So I am suspecting that calling qs.destroy() to close the stream is not the right way to do this, even though the cursor is well destroyed on the server side.
Thanks to node-postgres and pg-promise developers for your work.