diff --git a/docs/pages/apis/pool.mdx b/docs/pages/apis/pool.mdx index 123bc8ba4..be0aeeb19 100644 --- a/docs/pages/apis/pool.mdx +++ b/docs/pages/apis/pool.mdx @@ -187,6 +187,35 @@ assert(pool.idleCount === 0) assert(pool.totalCount === 0) ``` +`client.destroyOnDispose: boolean` + +`client[Symbol.dispose]() => void` + +Alternatively, pool clients support [Explicit Resource Management](https://tc39.es/proposal-explicit-resource-management/), which means you can use the `using` syntax to release them if your runtime supports it. + +```js + +import { Pool } from 'pg' + +const pool = new Pool() +{ + // check out a single client + using client = await pool.connect() + + // client.release() implicitly called at the end of scope +} + +{ + // check out a single client + using client = await pool.connect() + client.destroyOnDispose = true + + // client.release(true) implicitly called at the end of scope +} +``` + +If you want the client to be destroyed instead of being returned to the pool at the end of the scope, (i.e. calling `client.release(true)` at the end of the scope), set the `destroyOnRelease` property to `true`. +
You must release a client when you are finished with it. diff --git a/packages/pg-pool/index.js b/packages/pg-pool/index.js index 2fbdb78d5..56ae978a7 100644 --- a/packages/pg-pool/index.js +++ b/packages/pg-pool/index.js @@ -341,6 +341,13 @@ class Pool extends EventEmitter { client.release = this._releaseOnce(client, idleListener) + if (Symbol.dispose) { + client.destroyOnDispose = false + client[Symbol.dispose] = function () { + this.release(this.destroyOnDispose) + } + } + client.removeListener('error', idleListener) if (!pendingItem.timedOut) { diff --git a/packages/pg-pool/test/disposable-clients.js b/packages/pg-pool/test/disposable-clients.js new file mode 100644 index 000000000..548ca9ff2 --- /dev/null +++ b/packages/pg-pool/test/disposable-clients.js @@ -0,0 +1,25 @@ +const Pool = require('../') + +const expect = require('expect.js') + +describe('disposable clients', () => { + it('defines a callable [Symbol.dispose]() when symbol is present', async () => { + const pool = new Pool({ max: 1 }) + const client = await pool.connect() + + if (Symbol.dispose) { + expect(client[Symbol.dispose]).to.be.a('function') + } + + // ensure we don't define an `undefined` function when Symbol.dispose + // doesn't exist + expect(client).not.to.have.property('undefined') + + client.release() + await pool.end() + }) + + if (process.version.slice(1).split('.')[0] >= 24) { + require('./disposable-clients/using.js') + } +}) diff --git a/packages/pg-pool/test/disposable-clients/using.js b/packages/pg-pool/test/disposable-clients/using.js new file mode 100644 index 000000000..284335962 --- /dev/null +++ b/packages/pg-pool/test/disposable-clients/using.js @@ -0,0 +1,38 @@ +const Pool = require('../..') + +const expect = require('expect.js') + +it('supports releasing clients via `using`', async () => { + const pool = new Pool({ max: 1 }) + expect(pool.totalCount).to.eql(0) + + { + using client = await pool.connect() + expect(pool.totalCount).to.eql(1) + expect(pool.idleCount).to.eql(0) + await client.query('SELECT NOW()') + } + + expect(pool.totalCount).to.eql(1) + expect(pool.idleCount).to.eql(1) + + await pool.end() +}) + +it('supports destroying clients via `using`', async () => { + const pool = new Pool({ max: 1 }) + expect(pool.totalCount).to.eql(0) + + { + using client = await pool.connect() + client.destroyOnDispose = true + expect(pool.totalCount).to.eql(1) + expect(pool.idleCount).to.eql(0) + await client.query('SELECT NOW()') + } + + expect(pool.totalCount).to.eql(0) + expect(pool.idleCount).to.eql(0) + + await pool.end() +})