Skip to content

Commit 70c38c0

Browse files
authored
Merge pull request #946 from supabase/fix/query-timeout-connection-closing
fix(query): ensure that open connection are killed after timeout
2 parents 76cd87a + 5434754 commit 70c38c0

File tree

4 files changed

+39
-2
lines changed

4 files changed

+39
-2
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
"test": "run-s db:clean db:run test:run db:clean",
3131
"db:clean": "cd test/db && docker compose down",
3232
"db:run": "cd test/db && docker compose up --detach --wait",
33-
"test:run": "PG_META_MAX_RESULT_SIZE_MB=20 vitest run --coverage",
34-
"test:update": "run-s db:clean db:run && PG_META_MAX_RESULT_SIZE_MB=20 vitest run --update && run-s db:clean"
33+
"test:run": "PG_META_MAX_RESULT_SIZE_MB=20 PG_QUERY_TIMEOUT_SECS=3 PG_CONN_TIMEOUT_SECS=30 vitest run --coverage",
34+
"test:update": "run-s db:clean db:run && PG_META_MAX_RESULT_SIZE_MB=20 PG_QUERY_TIMEOUT_SECS=3 PG_CONN_TIMEOUT_SECS=30 vitest run --update && run-s db:clean"
3535
},
3636
"engines": {
3737
"node": ">=20",

src/server/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ export const PG_META_MAX_RESULT_SIZE = process.env.PG_META_MAX_RESULT_SIZE_MB
5959
export const DEFAULT_POOL_CONFIG: PoolConfig = {
6060
max: 1,
6161
connectionTimeoutMillis: PG_CONN_TIMEOUT_SECS * 1000,
62+
// node-postgrest need a statement_timeout to kill the connection when timeout is reached
63+
// otherwise the query will keep running on the database even if query timeout was reached
64+
statement_timeout: (PG_QUERY_TIMEOUT_SECS + 1) * 1000,
6265
query_timeout: PG_QUERY_TIMEOUT_SECS * 1000,
6366
ssl: PG_META_DB_SSL_ROOT_CERT ? { ca: PG_META_DB_SSL_ROOT_CERT } : undefined,
6467
application_name: `postgres-meta ${pkg.version}`,

test/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ import './server/ssl'
2323
import './server/table-privileges'
2424
import './server/typegen'
2525
import './server/result-size-limit'
26+
import './server/query-timeout'

test/server/query-timeout.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { expect, test, describe } from 'vitest'
2+
import { app } from './utils'
3+
import { pgMeta } from '../lib/utils'
4+
5+
describe('test query timeout', () => {
6+
test('query timeout after 3s and connection cleanup', async () => {
7+
const query = `SELECT pg_sleep(10);`
8+
// Execute a query that will sleep for 10 seconds
9+
const res = await app.inject({
10+
method: 'POST',
11+
path: '/query',
12+
payload: {
13+
query,
14+
},
15+
})
16+
17+
// Check that we get the proper timeout error response
18+
expect(res.statusCode).toBe(408) // Request Timeout
19+
expect(res.json()).toMatchObject({
20+
error: expect.stringContaining('Query read timeout'),
21+
})
22+
// wait one second for the statement timeout to take effect
23+
await new Promise((resolve) => setTimeout(resolve, 1000))
24+
25+
// Verify that the connection has been cleaned up by checking active connections
26+
const connectionsRes = await pgMeta.query(`
27+
SELECT * FROM pg_stat_activity where application_name = 'postgres-meta 0.0.0-automated' and query ILIKE '%${query}%';
28+
`)
29+
30+
// Should have no active connections except for our current query
31+
expect(connectionsRes.data).toHaveLength(0)
32+
}, 5000)
33+
})

0 commit comments

Comments
 (0)