@@ -25,6 +25,7 @@ import {
2525 instrumentRedis ,
2626 sqlLabel ,
2727} from '../index' ;
28+ import { observeSql , ObservedSql } from '../drivers' ;
2829
2930function setup ( ) {
3031 const roots : ExecutionRecord [ ] = [ ] ;
@@ -477,3 +478,81 @@ describe('instrumentBetterSqlite3 (database shaped like better-sqlite3: Statemen
477478 expect ( labels ( roots [ 0 ] ) ) . toEqual ( [ ] ) ;
478479 } ) ;
479480} ) ;
481+
482+ describe ( 'pg pools without a server (fake pg)' , ( ) => {
483+ // Shaped like pg: Client.query(text, values, cb) and pg-pool's counters and callback queue
484+ function fakePg ( ) {
485+ class Client {
486+ query ( text : string , values : unknown , cb ?: ( e : unknown , r : unknown ) => void ) {
487+ const done = typeof values === 'function' ? values as typeof cb : cb ;
488+ const result = { rows : [ { 'QUERY PLAN' : [ { Plan : { 'Node Type' : 'Seq Scan' } } ] } ] } ;
489+ if ( done ) { setTimeout ( ( ) => done ( null , result ) , 2 ) ; return undefined ; }
490+ return new Promise ( r => setTimeout ( ( ) => r ( result ) , 2 ) ) ;
491+ }
492+ }
493+ class Pool {
494+ options = { max : 1 } ;
495+ totalCount = 0 ;
496+ idleCount = 0 ;
497+ private queue : Array < ( e : unknown , c : Client , release : ( ) => void ) => void > = [ ] ;
498+ private client = new Client ( ) ;
499+ connect ( cb : ( e : unknown , c : Client , release : ( ) => void ) => void ) {
500+ const release = ( ) => {
501+ const next = this . queue . shift ( ) ;
502+ // The next waiter is called from inside this release: another request's context
503+ if ( next ) next ( null , this . client , release ) ;
504+ else this . idleCount = 1 ;
505+ } ;
506+ if ( this . totalCount < this . options . max ) { this . totalCount ++ ; setTimeout ( ( ) => cb ( null , this . client , release ) , 1 ) ; return ; }
507+ if ( this . idleCount > 0 ) { this . idleCount = 0 ; setTimeout ( ( ) => cb ( null , this . client , release ) , 1 ) ; return ; }
508+ this . queue . push ( cb ) ;
509+ }
510+ query ( text : string , values ?: unknown [ ] ) {
511+ return new Promise ( ( resolve , reject ) => this . connect ( ( e , c , release ) => {
512+ if ( e ) return reject ( e ) ;
513+ ( c . query ( text , values ) as Promise < unknown > ) . then ( r => { release ( ) ; resolve ( r ) ; } , reject ) ;
514+ } ) ) ;
515+ }
516+ }
517+ return { Client, Pool } ;
518+ }
519+
520+ test ( 'a full pool: each request keeps its own queries, and the wait is tracked' , async ( ) => {
521+ const pg = fakePg ( ) ;
522+ const { tp, roots } = setup ( ) ;
523+ const uninstall = instrumentPg ( tp , pg ) ;
524+ const pool = new pg . Pool ( ) ;
525+ try {
526+ await Promise . all ( [ 1 , 2 , 3 ] . map ( i => tp . track ( async ( ) => {
527+ await pool . query ( `SELECT ${ i } ` ) ;
528+ } , { label : `req${ i } ` } ) ) ) ;
529+ } finally {
530+ uninstall ( ) ;
531+ }
532+ const requests = roots . filter ( r => r . label . startsWith ( 'req' ) ) ;
533+ expect ( requests ) . toHaveLength ( 3 ) ;
534+ for ( const r of requests ) expect ( labels ( r ) . filter ( l => l !== 'pg pool wait' ) ) . toEqual ( [ 'pg SELECT' ] ) ;
535+ expect ( requests . filter ( r => labels ( r ) . includes ( 'pg pool wait' ) ) . length ) . toBeGreaterThanOrEqual ( 1 ) ;
536+ } ) ;
537+
538+ test ( 'observeSql sees statements and runs its own on the same pool, untracked' , async ( ) => {
539+ const pg = fakePg ( ) ;
540+ const { tp, roots } = setup ( ) ;
541+ const uninstall = instrumentPg ( tp , pg ) ;
542+ const seen : ObservedSql [ ] = [ ] ;
543+ observeSql ( q => seen . push ( q ) ) ;
544+ const pool = new pg . Pool ( ) ;
545+ try {
546+ await tp . track ( ( ) => pool . query ( 'SELECT * FROM users WHERE id = $1' , [ 7 ] ) , { label : 'job' } ) ;
547+ expect ( seen . map ( q => [ q . text , q . values ] ) ) . toEqual ( [ [ 'SELECT * FROM users WHERE id = $1' , [ 7 ] ] ] ) ;
548+ const rows = await seen [ 0 ] . run ( 'EXPLAIN (FORMAT JSON) SELECT 1' ) ;
549+ expect ( rows [ 0 ] [ 'QUERY PLAN' ] ) . toBeDefined ( ) ;
550+ // The EXPLAIN was neither observed again nor tracked as the app's query
551+ expect ( seen ) . toHaveLength ( 1 ) ;
552+ } finally {
553+ observeSql ( undefined ) ;
554+ uninstall ( ) ;
555+ }
556+ expect ( roots . map ( r => r . label ) ) . toEqual ( [ 'job' ] ) ;
557+ } ) ;
558+ } ) ;
0 commit comments