postgres-pool
Version:
Node postgres connection pool implementation for node-pg
276 lines (272 loc) • 11.5 kB
text/typescript
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 };