@@ -29,9 +29,9 @@ describe('Pg database foundation', () => {
2929 }
3030 }
3131
32- async function createConnection ( ) {
32+ async function createConnection ( overrides ?: Partial < TenantConnectionOptions > ) {
3333 const superUser = await getServiceKeyUser ( tenantId )
34- const settings = createConnectionSettings ( superUser )
34+ const settings = createConnectionSettings ( superUser , overrides )
3535
3636 const pool = new PgPoolStrategy ( settings )
3737 const connection = new PgTenantConnection ( pool , settings )
@@ -206,6 +206,68 @@ describe('Pg database foundation', () => {
206206 }
207207 } )
208208
209+ it ( 'releases the pending client unused when acquisition is aborted on an exhausted pool' , async ( ) => {
210+ const { pool } = await createConnection ( { maxConnections : 1 } )
211+
212+ try {
213+ const holder = await pool . acquire ( ) . beginTransaction ( )
214+
215+ try {
216+ const controller = new AbortController ( )
217+ const pending = pool . acquire ( ) . beginTransaction ( { signal : controller . signal } )
218+ controller . abort ( )
219+
220+ await expect ( pending ) . rejects . toMatchObject ( {
221+ name : 'AbortError' ,
222+ code : 'ABORT_ERR' ,
223+ message : 'Query was aborted' ,
224+ } )
225+
226+ expect ( pool . getPoolStats ( ) ) . toEqual ( { total : 1 , used : 1 } )
227+ } finally {
228+ await holder . rollback ( )
229+ }
230+
231+ await new Promise ( ( resolve ) => setImmediate ( resolve ) )
232+ expect ( pool . getPoolStats ( ) ) . toEqual ( { total : 1 , used : 0 } )
233+
234+ const result = await pool . acquire ( ) . query < { n : number } > ( 'SELECT 1 AS n' )
235+ expect ( result . rows [ 0 ] . n ) . toBe ( 1 )
236+ expect ( pool . getPoolStats ( ) ) . toEqual ( { total : 1 , used : 0 } )
237+ } finally {
238+ await pool . destroy ( )
239+ }
240+ } )
241+
242+ it ( 'destroys the client instead of returning it to the pool when a query is aborted mid-flight' , async ( ) => {
243+ const { pool } = await createConnection ( )
244+ const controller = new AbortController ( )
245+
246+ try {
247+ const query = pool . acquire ( ) . query ( 'SELECT pg_sleep(10)' , {
248+ signal : controller . signal ,
249+ } )
250+ const abortTimeout = setTimeout ( ( ) => controller . abort ( ) , 50 )
251+
252+ try {
253+ await expect ( query ) . rejects . toMatchObject ( {
254+ name : 'AbortError' ,
255+ code : 'ABORT_ERR' ,
256+ } )
257+ } finally {
258+ clearTimeout ( abortTimeout )
259+ }
260+
261+ expect ( pool . getPoolStats ( ) ) . toEqual ( { total : 0 , used : 0 } )
262+
263+ const result = await pool . acquire ( ) . query < { n : number } > ( 'SELECT 1 AS n' )
264+ expect ( result . rows [ 0 ] . n ) . toBe ( 1 )
265+ expect ( pool . getPoolStats ( ) ) . toEqual ( { total : 1 , used : 0 } )
266+ } finally {
267+ await pool . destroy ( )
268+ }
269+ } )
270+
209271 it ( 'reuses cached pg tenant pools for cacheable settings' , async ( ) => {
210272 const superUser = await getServiceKeyUser ( tenantId )
211273 const settings = createConnectionSettings ( superUser , {
0 commit comments