Skip to content

Commit

Permalink
fix(postgres): invalidate connection after client-side timeout (seque…
Browse files Browse the repository at this point in the history
…lize#15144)

* Invalidate connection after client-side timeout.

The `query_timeout` feature of the `pg` package helps handle stuck TCP
connections more quickly and gracefully by implementing a client-side
timeout:

brianc/node-postgres#1713

Sequelize started passing this dialect-specific option through to `pg` here:

sequelize#13258

I believe we also want to invalidate the connection when a client-side
timeout occurs. We shouldn't try to reuse the stuck connection
because...it's stuck.

This PR updates the error handling code so that the connection is
invalidated if the error matches the one thrown from here:

https://github.com/brianc/node-postgres/blob/5538df6b446f4b4f921947b460fe38acb897e579/packages/pg/lib/client.js#L529

* Add comment with link to PR.

* Add tests, shorten comment.

* Skip tests for postgres-native.

* Update src/dialects/postgres/query.js

Co-authored-by: Rik Smale <13023439+WikiRik@users.noreply.github.com>

Co-authored-by: Rik Smale <13023439+WikiRik@users.noreply.github.com>
  • Loading branch information
harrykao and WikiRik authored Oct 24, 2022
1 parent a10a78c commit ff43e8d
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/dialects/postgres/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export class PostgresQuery extends AbstractQuery {
|| /Unable to set non-blocking to true/i.test(error)
|| /SSL SYSCALL error: EOF detected/i.test(error)
|| /Local: Authentication failure/i.test(error)
// https://github.com/sequelize/sequelize/pull/15144
|| error.message === 'Query read timeout'
) {
connection._invalid = true;
}
Expand Down
46 changes: 45 additions & 1 deletion test/integration/dialects/postgres/query.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const expect = chai.expect;
const Support = require('../../support');

const dialect = Support.getTestDialect();
const { DataTypes } = require('@sequelize/core');
const { DatabaseError, DataTypes } = require('@sequelize/core');

if (dialect.startsWith('postgres')) {
describe('[POSTGRES] Query', () => {
Expand Down Expand Up @@ -221,5 +221,49 @@ if (dialect.startsWith('postgres')) {
order: [['order_0', 'DESC']],
});
});

describe('Connection Invalidation', () => {
if (process.env.DIALECT === 'postgres-native') {
// native driver doesn't support statement_timeout or query_timeout
return;
}

async function setUp(clientQueryTimeoutMs) {
const sequelize = Support.createSequelizeInstance({
dialectOptions: {
statement_timeout: 500, // ms
query_timeout: clientQueryTimeoutMs,
},
pool: {
max: 1, // having only one helps us know whether the connection was invalidated
idle: 60_000,
},
});

return { sequelize, originalPid: await getConnectionPid(sequelize) };
}

async function getConnectionPid(sequelize) {
const connection = await sequelize.connectionManager.getConnection();
const pid = connection.processID;
sequelize.connectionManager.releaseConnection(connection);

return pid;
}

it('reuses connection after statement timeout', async () => {
// client timeout > statement timeout means that the query should fail with a statement timeout
const { sequelize, originalPid } = await setUp(10_000);
await expect(sequelize.query('select pg_sleep(1)')).to.eventually.be.rejectedWith(DatabaseError, 'canceling statement due to statement timeout');
expect(await getConnectionPid(sequelize)).to.equal(originalPid);
});

it('invalidates connection after client-side query timeout', async () => {
// client timeout < statement timeout means that the query should fail with a read timeout
const { sequelize, originalPid } = await setUp(250);
await expect(sequelize.query('select pg_sleep(1)')).to.eventually.be.rejectedWith(DatabaseError, 'Query read timeout');
expect(await getConnectionPid(sequelize)).to.not.equal(originalPid);
});
});
});
}

0 comments on commit ff43e8d

Please sign in to comment.