Node.js で PostgreSQL にアクセスするために node-postgres を使っている。設定で query_timeout を設定すると、実行時間が長いクエリをタイムアウトさせることができる。 しかし、タイムアウト後に DB 内のプロセスが残ったままになってリソースを圧迫し続けた。
SELECT * FROM pg_stat_activity;
結論
statement_timeout を設定することで、タイムアウト時に DB のプロセスをキャンセルできる。
statement_timeout?: number, // number of milliseconds before a statement in query will time out, default is no timeout
query_timeout?: number, // number of milliseconds before a query call will timeout, default is no timeout
クエリ実行のタイムアウト設定はこの2種類で、説明的にもタイムアウトのタイミングに違いがあるようにしか見えないが、コード見てみると実装が大きく違うっぽい。
DB クライアントのタイムアウト設定はどういう実装かちゃんと確認しておいた方が良さそう。
query_timeout
query_timeout は SQL 実行開始(直前)から一定時間後に例外を出すことで中断している。なので client 側ではタイムアウトになるが DB 上のプロセスは動き続ける。コード上ではこの辺。
readTimeout = config.query_timeout || this.connectionParameters.query_timeout // ... 中略 ... readTimeoutTimer = setTimeout(() => { var error = new Error('Query read timeout') process.nextTick(() => { query.handleError(error, this.connection) }) queryCallback(error) // we already returned an error, // just do nothing if query completes query.callback = () => {} // Remove from queue var index = this.queryQueue.indexOf(query) if (index > -1) { this.queryQueue.splice(index, 1) } this._pulseQueryQueue() }, readTimeout)
https://github.com/brianc/node-postgres/blob/master/packages/pg/lib/client.js#L527-L547
statement_timeout
一方、 statement_timeout は DB との connection 確立時に送信される。コード的にはこの辺り。
// once connection is established send startup message con.on('connect', function () { if (self.ssl) { con.requestSsl() } else { con.startup(self.getStartupConf()) } }) con.on('sslconnect', function () { con.startup(self.getStartupConf()) })
https://github.com/brianc/node-postgres/blob/master/packages/pg/lib/client.js#L115-L126
詳しくコードは読んでいないが、以下と同等にセッションに対して statement_timeout を設定してると思われる。よって、タイムアウトは DB 内で発生するのでプロセスが生き続けることがなくなる。
SET statement_timeout TO 10000;