Skip to content

Commit db280fe

Browse files
test: pool wait, context binding and the SQL observer without a database
1 parent 8c66c69 commit db280fe

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

‎src/__tests__/drivers.test.ts‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
instrumentRedis,
2626
sqlLabel,
2727
} from '../index';
28+
import { observeSql, ObservedSql } from '../drivers';
2829

2930
function 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

Comments
 (0)