From 27a71009d718f9e33688376967e332dcac7790d7 Mon Sep 17 00:00:00 2001 From: Malthe Borch Date: Wed, 19 Jun 2024 07:52:14 +0200 Subject: [PATCH] Add back 'end' event and fix graceful shutdown error handling --- CHANGES.md | 5 +++++ src/client.ts | 30 ++++++++++++++++++------------ test/client.test.ts | 3 +++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cb3f7ef..c713964 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ In next release ... +- Added "end" event which was removed in v1.8.0. + +- The `end` method now correctly throws an error if a network error + occurred during the resulting protocol traffic. + ## v2.0.3 (2024-06-15) - Fix issue where larger results would sometimes have duplicates (#122). diff --git a/src/client.ts b/src/client.ts index 78e164b..41e8b23 100644 --- a/src/client.ts +++ b/src/client.ts @@ -142,15 +142,17 @@ interface PreFlightQueue { const DEFAULTS = new Defaults(env as Record); -export type EventMap< - T = { - error: DatabaseError; - notice: ClientNotice; - notification: Notification; - }, -> = { - [K in keyof T]: [T[K]]; -}; + +export interface EventMap { + /** The connection has ended, possibly due to a network error. */ + end: [NodeJS.ErrnoException | null]; + /** A database error has occurred. */ + error: [DatabaseError]; + /** A client notice (typically a warning) has been received. */ + notice: [ClientNotice]; + /** A client notification has been received. */ + notification: [Notification]; +} type Resolve = (value?: T) => void; @@ -209,6 +211,7 @@ export class ClientImpl { this.stream.on('close', () => { this.closed = true; + this.events.emit('end', null); this.ending?.(); }); @@ -233,10 +236,12 @@ export class ClientImpl { } else { // Don't raise ECONNRESET errors - they can & should be // ignored during disconnect. + if (error.errno === constants.errno.ECONNRESET) return; + if (this.ending) { - if (error.errno === constants.errno.ECONNRESET) return; - this.ending(); + this.ending(error); } + this.events.emit('end', error); } }); @@ -286,7 +291,8 @@ export class ClientImpl { const abort = (error: Error) => { this.handleError(error); - this.connecting?.(error); + if (!this.connecting) throw error; + this.connecting(error); }; const startup = (stream?: Socket) => { diff --git a/test/client.test.ts b/test/client.test.ts index 78d9e7d..cec55fa 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -287,9 +287,12 @@ describe('Query', () => { idleInTransactionSessionTimeout: 500, }); const errors: string[] = []; + let error: NodeJS.ErrnoException | null = null; + client.on('end', (isError) => error = isError); client.on('error', (error) => errors.push(error.code)); await new Promise((resolve) => setTimeout(resolve, 625)); equal(client.closed, true); + equal(error, null); deepEqual(errors, ['57P05']); });