UNPKG

postgres-pool

Version:

Node postgres connection pool implementation for node-pg

276 lines (272 loc) 11.5 kB
import { EventEmitter } from 'node:events'; import { ConnectionOptions } from 'node:tls'; import pg, { Connection, QueryResultRow, QueryResult } from 'pg'; import { StrictEventEmitter } from 'strict-event-emitter-types'; declare class PostgresPoolError extends Error { code: string; constructor(message: string, code: string); } interface SslSettings { /** * TLS options for the underlying socket connection. */ ssl?: ConnectionOptions; } interface SslSettingsOrAwsRdsSsl { /** * TLS options for the underlying socket connection. * NOTE: `aws-rds` sets up strict tls connection details for connecting to AWS RDS instances */ ssl?: ConnectionOptions | 'aws-rds'; } interface PoolOptionsBase { /** * Number of connections to store in the pool */ poolSize: number; /** * Milliseconds until an idle connection is closed and removed from the active connection pool */ idleTimeoutMillis: number; /** * Milliseconds to wait for an available connection before throwing an error that no connection is available */ waitForAvailableConnectionTimeoutMillis: number; /** * Milliseconds to wait to connect to postgres */ connectionTimeoutMillis: number; /** * Number of retries to attempt when there's an error matching `retryConnectionErrorCodes`. A value of 0 * will disable connection retry. */ retryConnectionMaxRetries: number; /** * Milliseconds to wait between retry connection attempts after receiving a connection error with code * that matches `retryConnectionErrorCodes`. A value of 0 will try reconnecting immediately. */ retryConnectionWaitMillis: number; /** * Error codes to trigger a connection retry. Eg. ENOTFOUND, EAI_AGAIN */ retryConnectionErrorCodes: string[]; /** * If connect should be retried when the database throws "the database system is starting up" * NOTE: This typically happens during a fail over scenario when a read-replica is being promoted to master */ reconnectOnDatabaseIsStartingError: boolean; /** * Milliseconds to wait between retry connection attempts while the database is starting up. Allows you to throttle * how many retries should happen until databaseStartupTimeoutMillis expires. A value of 0 will * retry the query immediately. */ waitForDatabaseStartupMillis: number; /** * If connection attempts continually return "the database system is starting up", this is the total number of milliseconds * to wait until an error is thrown. */ databaseStartupTimeoutMillis: number; /** * If the query should be retried when the database throws "cannot execute X in a read-only transaction" * NOTE: This typically happens during a fail over scenario when a read-replica is being promoted to master */ reconnectOnReadOnlyTransactionError: boolean; /** * Milliseconds to wait between retry queries while the connection is marked as read-only. Allows you to throttle * how many retries should happen until readOnlyTransactionReconnectTimeoutMillis expires. A value of 0 will * try reconnecting immediately. */ waitForReconnectReadOnlyTransactionMillis: number; /** * If queries continually return "cannot execute X in a read-only transaction", this is the total number of * milliseconds to wait until an error is thrown. */ readOnlyTransactionReconnectTimeoutMillis: number; /** * If the query should be retried when the database throws "Client has encountered a connection error and is not queryable" * NOTE: This typically happens during a fail-over scenario with the cluster */ reconnectOnConnectionError: boolean; /** * Milliseconds to wait between retry queries after receiving a connection error. Allows you to throttle * how many retries should happen until connectionReconnectTimeoutMillis expires. A value of 0 will * try reconnecting immediately. */ waitForReconnectConnectionMillis: number; /** * If queries continually return "Client has encountered a connection error and is not queryable", this is the total number of * milliseconds to wait until an error is thrown. */ connectionReconnectTimeoutMillis: number; /** * Specifies the regular expression to find named parameters in a query */ namedParameterFindRegExp: RegExp; /** * Returns the regular expression used to replace a named parameter in a query */ getNamedParameterReplaceRegExp: (namedParameter: string) => RegExp; /** * Gets the name of a named parameter without the symbols. This should correspond to the key in the query value object */ getNamedParameterName: (namedParameterWithSymbols: string) => string; /** * Throw an error if a query takes longer than the specified milliseconds */ query_timeout?: number; /** * Abort a query statement if it takes longer than the specified milliseconds */ statement_timeout?: number; } interface PoolOptionsExplicit { host: string; database: string; user?: string; password?: string; port?: number; poolSize?: number; idleTimeoutMillis?: number; waitForAvailableConnectionTimeoutMillis?: number; connectionTimeoutMillis?: number; retryConnectionMaxRetries?: number; retryConnectionWaitMillis?: number; retryConnectionErrorCodes?: string[]; reconnectOnDatabaseIsStartingError?: boolean; waitForDatabaseStartupMillis?: number; databaseStartupTimeoutMillis?: number; reconnectOnReadOnlyTransactionError?: boolean; waitForReconnectReadOnlyTransactionMillis?: number; readOnlyTransactionReconnectTimeoutMillis?: number; reconnectOnConnectionError?: boolean; waitForReconnectConnectionMillis?: number; connectionReconnectTimeoutMillis?: number; namedParameterFindRegExp?: RegExp; getNamedParameterReplaceRegExp?: (namedParameter: string) => RegExp; getNamedParameterName?: (namedParameterWithSymbols: string) => string; query_timeout?: number; statement_timeout?: number; } interface PoolOptionsImplicit { connectionString: string; poolSize?: number; idleTimeoutMillis?: number; waitForAvailableConnectionTimeoutMillis?: number; connectionTimeoutMillis?: number; retryConnectionMaxRetries?: number; retryConnectionWaitMillis?: number; retryConnectionErrorCodes?: string[]; reconnectOnDatabaseIsStartingError?: boolean; waitForDatabaseStartupMillis?: number; databaseStartupTimeoutMillis?: number; reconnectOnReadOnlyTransactionError?: boolean; waitForReconnectReadOnlyTransactionMillis?: number; readOnlyTransactionReconnectTimeoutMillis?: number; reconnectOnConnectionError?: boolean; waitForReconnectConnectionMillis?: number; connectionReconnectTimeoutMillis?: number; namedParameterFindRegExp?: RegExp; getNamedParameterReplaceRegExp?: (namedParameter: string) => RegExp; getNamedParameterName?: (namedParameterWithSymbols: string) => string; query_timeout?: number; statement_timeout?: number; } type PoolClient = pg.Client & { uniqueId: string; idleTimeoutTimer?: NodeJS.Timeout; release: (removeConnection?: boolean) => Promise<void>; errorHandler: (err: Error) => void; }; type PoolClientWithConnection = PoolClient & { connection?: Connection; }; interface ConnectionAddedToPoolParams { connectionId: PoolClient['uniqueId']; retryAttempt: number; startTime: bigint; } interface PoolEvents { connectionRequestQueued: () => void; connectionRequestDequeued: () => void; connectionAddedToPool: (params: ConnectionAddedToPoolParams) => void; connectionRemovedFromPool: () => void; connectionIdle: () => void; connectionRemovedFromIdlePool: () => void; idleConnectionActivated: () => void; queryDeniedForReadOnlyTransaction: () => void; queryDeniedForConnectionError: () => void; waitingForDatabaseToStart: () => void; retryConnectionOnError: () => void; error: (error: Error, client?: PoolClient) => void; } type PoolEmitter = StrictEventEmitter<EventEmitter, PoolEvents>; declare const Pool_base: new () => PoolEmitter; declare class Pool extends Pool_base { /** * Gets the number of queued requests waiting for a database connection * @returns Number of queued requests */ get waitingCount(): number; /** * Gets the number of idle connections * @returns Number of idle connections */ get idleCount(): number; /** * Gets the total number of connections in the pool * @returns Total number of connections */ get totalCount(): number; protected options: PoolOptionsBase & SslSettings & (PoolOptionsExplicit | PoolOptionsImplicit); protected connectionQueueEventEmitter: EventEmitter; protected connections: string[]; protected idleConnections: PoolClient[]; protected connectionQueue: string[]; protected isEnding: boolean; constructor(options: SslSettingsOrAwsRdsSsl & (PoolOptionsExplicit | PoolOptionsImplicit)); /** * Gets a client connection from the pool. * Note: You must call `.release()` when finished with the client connection object. That will release the connection back to the pool to be used by other requests. * @returns Client connection */ connect(): Promise<PoolClient>; /** * Gets a connection to the database and executes the specified query using named parameters. This method will release the connection back to the pool when the query has finished. * @param {string} text * @param {object} values - Keys represent named parameters in the query * @returns Results from query */ query<TRow extends QueryResultRow = any>(text: string, values: Record<string, any>): Promise<QueryResult<TRow>>; /** * Gets a connection to the database and executes the specified query. This method will release the connection back to the pool when the query has finished. * @param {string} text * @param {object[]} values * @returns Results from query */ query<TRow extends QueryResultRow = any>(text: string, values?: any[]): Promise<QueryResult<TRow>>; /** * Drains the pool of all active client connections and prevents additional connections * @returns */ end(): Promise<void>; /** * Drains the pool of all idle client connections. */ drainIdleConnections(): Promise<void>; private _query; /** * Creates a new client connection to add to the pool * @param {string} connectionId * @param {number} [retryAttempt] * @param {bigint} [createConnectionStartTime] - High-resolution time (in nanoseconds) for when the connection was created * @param {[number,number]} [databaseStartupStartTime] - hrtime when the db was first listed as starting up * @returns Client connection */ private _createConnection; /** * Removes the client connection from the pool and tries to gracefully shut it down * @param {PoolClient} client */ private _removeConnection; } export { type ConnectionAddedToPoolParams, Pool, type PoolClient, type PoolClientWithConnection, type PoolOptionsBase, type PoolOptionsExplicit, type PoolOptionsImplicit, PostgresPoolError, type SslSettings, type SslSettingsOrAwsRdsSsl };