mirror of
https://github.com/stablyai/orca.git
synced 2026-11-01 09:37:33 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
262 lines
9.0 KiB
TypeScript
262 lines
9.0 KiB
TypeScript
import type pg from 'pg'
|
|
|
|
export type PostgresPoolPressureCounts = {
|
|
databasePoolTotal: number
|
|
databasePoolIdle: number
|
|
databasePoolWaiting: number
|
|
databasePoolWaitersMax: number
|
|
databasePoolOldestWaitMs: number
|
|
databasePoolWaitMsMax: number
|
|
}
|
|
|
|
// pg-pool connect timeouts have no SQLSTATE; these messages prove acquisition failed before a statement ran.
|
|
const POOL_CONNECT_TIMEOUT_MESSAGES = [
|
|
// No pooled client came free within connectionTimeoutMillis.
|
|
'timeout exceeded when trying to connect',
|
|
// A new client's own handshake outran connectionTimeoutMillis.
|
|
'Connection terminated due to connection timeout'
|
|
]
|
|
// pg raises this whenever a socket ends early, during the handshake and mid
|
|
// statement alike, so only the acquire boundary can tell the two apart.
|
|
const CONNECTION_TERMINATED_MESSAGE = 'Connection terminated unexpectedly'
|
|
// The socket to Postgres (or its proxy) failed before a client existed, e.g. a Cloud SQL restart.
|
|
const ACQUIRE_SOCKET_ERRNOS = new Set([
|
|
'EPIPE',
|
|
'ECONNRESET',
|
|
'ECONNREFUSED',
|
|
'ETIMEDOUT',
|
|
'EHOSTUNREACH'
|
|
])
|
|
|
|
// Membership is tracked beside the error rather than on it: an error object may
|
|
// be frozen, and a mutated one would leak the marker into logs.
|
|
const poolAcquireFailures = new WeakSet<object>()
|
|
|
|
function errorMessage(error: unknown): string {
|
|
return String((error as { message?: unknown } | null)?.message)
|
|
}
|
|
|
|
function errorCode(error: unknown): string {
|
|
return String((error as { code?: unknown } | null)?.code)
|
|
}
|
|
|
|
export function isPostgresPoolAcquireFailure(error: unknown): boolean {
|
|
return typeof error === 'object' && error !== null && poolAcquireFailures.has(error)
|
|
}
|
|
|
|
// connectionTimeoutMillis firing, either waiting in the queue or dialling.
|
|
export function isPostgresPoolConnectTimeout(error: unknown): boolean {
|
|
const message = errorMessage(error)
|
|
return POOL_CONNECT_TIMEOUT_MESSAGES.some((known) => message.includes(known))
|
|
}
|
|
|
|
// Every way the pool can fail to hand out a usable client. An early-ended
|
|
// socket counts only at the acquire boundary: retrying a statement whose commit
|
|
// outcome is unknown is not safe.
|
|
export function isPostgresPoolConnectFailure(error: unknown): boolean {
|
|
if (isPostgresPoolConnectTimeout(error)) return true
|
|
if (!isPostgresPoolAcquireFailure(error)) return false
|
|
return (
|
|
errorMessage(error).includes(CONNECTION_TERMINATED_MESSAGE) ||
|
|
ACQUIRE_SOCKET_ERRNOS.has(errorCode(error))
|
|
)
|
|
}
|
|
|
|
const emptyCounts = (): PostgresPoolPressureCounts => ({
|
|
databasePoolTotal: 0,
|
|
databasePoolIdle: 0,
|
|
databasePoolWaiting: 0,
|
|
databasePoolWaitersMax: 0,
|
|
databasePoolOldestWaitMs: 0,
|
|
databasePoolWaitMsMax: 0
|
|
})
|
|
|
|
// `priority` work skips the general queue and takes the next connection any
|
|
// caller releases, so a burst of general work cannot make it wait its turn.
|
|
export type PostgresPoolLane = 'general' | 'priority'
|
|
|
|
type GeneralLaneWaiter = {
|
|
grant: (releaseSlot: () => void) => void
|
|
timer: ReturnType<typeof setTimeout> | undefined
|
|
}
|
|
|
|
export class PostgresPoolPressure {
|
|
private readonly waiters = new Map<symbol, number>()
|
|
private waitersMax = 0
|
|
private waitMsMax = 0
|
|
private lastConsumed = emptyCounts()
|
|
// Queue general work here so pg-pool queues only priority; reserving an idle connection would lose Asia drain throughput.
|
|
private readonly generalCapacity: number
|
|
private generalInUse = 0
|
|
private readonly generalQueue: GeneralLaneWaiter[] = []
|
|
|
|
constructor(
|
|
private readonly pool: pg.Pool,
|
|
private readonly now: () => number = Date.now
|
|
) {
|
|
this.generalCapacity = pool.options?.max ?? Infinity
|
|
}
|
|
|
|
async connect(lane: PostgresPoolLane = 'general'): Promise<pg.PoolClient> {
|
|
const startedAt = this.now()
|
|
let waiter: symbol | undefined
|
|
const markWaiting = (): void => {
|
|
if (waiter) return
|
|
waiter = Symbol()
|
|
this.waiters.set(waiter, startedAt)
|
|
this.waitersMax = Math.max(this.waitersMax, this.waiters.size)
|
|
}
|
|
try {
|
|
const slot = lane === 'priority' ? () => {} : this.takeGeneralSlot(markWaiting)
|
|
// Awaited only when queued: a free slot dials in this same turn.
|
|
const releaseSlot = typeof slot === 'function' ? slot : await slot
|
|
try {
|
|
const waitingBefore = this.pool.waitingCount
|
|
const connection = this.pool.connect()
|
|
if (this.pool.waitingCount > waitingBefore) markWaiting()
|
|
return await markedAcquire(connection, releaseSlot)
|
|
} catch (error) {
|
|
releaseSlot()
|
|
throw error
|
|
}
|
|
} finally {
|
|
if (waiter) {
|
|
this.waitMsMax = Math.max(this.waitMsMax, this.now() - startedAt)
|
|
this.waiters.delete(waiter)
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIFO, bounded by the pool's own acquire timeout so a queued caller fails
|
|
// exactly as it would have inside pg-pool, and every classifier still matches.
|
|
private takeGeneralSlot(markWaiting: () => void): (() => void) | Promise<() => void> {
|
|
if (this.generalInUse < this.generalCapacity && this.generalQueue.length === 0) {
|
|
this.generalInUse++
|
|
return this.generalSlotRelease()
|
|
}
|
|
markWaiting()
|
|
return new Promise((resolve, reject) => {
|
|
const queued: GeneralLaneWaiter = { grant: resolve, timer: undefined }
|
|
const timeoutMs = this.pool.options?.connectionTimeoutMillis ?? 0
|
|
if (timeoutMs > 0) {
|
|
queued.timer = setTimeout(() => {
|
|
const index = this.generalQueue.indexOf(queued)
|
|
if (index >= 0) this.generalQueue.splice(index, 1)
|
|
reject(new Error(POOL_CONNECT_TIMEOUT_MESSAGES[0]))
|
|
}, timeoutMs)
|
|
queued.timer.unref?.()
|
|
}
|
|
this.generalQueue.push(queued)
|
|
})
|
|
}
|
|
|
|
private generalSlotRelease(): () => void {
|
|
let released = false
|
|
return () => {
|
|
if (released) return
|
|
released = true
|
|
const next = this.generalQueue.shift()
|
|
if (!next) {
|
|
this.generalInUse--
|
|
return
|
|
}
|
|
// The slot passes straight to the next caller, so generalInUse is unchanged.
|
|
clearTimeout(next.timer)
|
|
next.grant(this.generalSlotRelease())
|
|
}
|
|
}
|
|
|
|
consumeCounts(): PostgresPoolPressureCounts {
|
|
const counts = this.readCounts()
|
|
this.lastConsumed = counts
|
|
this.waitersMax = this.waiters.size
|
|
this.waitMsMax = counts.databasePoolOldestWaitMs
|
|
return counts
|
|
}
|
|
|
|
peekCounts(): PostgresPoolPressureCounts {
|
|
const current = this.readCounts()
|
|
return {
|
|
...current,
|
|
databasePoolWaitersMax: Math.max(
|
|
current.databasePoolWaitersMax,
|
|
this.lastConsumed.databasePoolWaitersMax
|
|
),
|
|
databasePoolOldestWaitMs: Math.max(
|
|
current.databasePoolOldestWaitMs,
|
|
this.lastConsumed.databasePoolOldestWaitMs
|
|
),
|
|
databasePoolWaitMsMax: Math.max(
|
|
current.databasePoolWaitMsMax,
|
|
this.lastConsumed.databasePoolWaitMsMax
|
|
)
|
|
}
|
|
}
|
|
|
|
// Instantaneous, unlike peekCounts, which holds the last interval's maxima.
|
|
oldestWaitMs(): number {
|
|
if (this.waiters.size === 0) return 0
|
|
return Math.max(0, this.now() - Math.min(...this.waiters.values()))
|
|
}
|
|
|
|
private readCounts(): PostgresPoolPressureCounts {
|
|
const now = this.now()
|
|
const oldestWaitMs =
|
|
this.waiters.size === 0 ? 0 : Math.max(0, now - Math.min(...this.waiters.values()))
|
|
return {
|
|
databasePoolTotal: this.pool.totalCount,
|
|
databasePoolIdle: this.pool.idleCount,
|
|
databasePoolWaiting: this.waiters.size,
|
|
databasePoolWaitersMax: Math.max(this.waitersMax, this.waiters.size),
|
|
databasePoolOldestWaitMs: oldestWaitMs,
|
|
databasePoolWaitMsMax: Math.max(this.waitMsMax, oldestWaitMs)
|
|
}
|
|
}
|
|
}
|
|
|
|
async function markedAcquire(
|
|
connection: Promise<pg.PoolClient>,
|
|
onRelease: () => void
|
|
): Promise<pg.PoolClient> {
|
|
let client: pg.PoolClient
|
|
try {
|
|
client = await connection
|
|
} catch (error) {
|
|
if (typeof error === 'object' && error !== null) poolAcquireFailures.add(error)
|
|
throw error
|
|
}
|
|
return guardCheckedOutClient(client, onRelease)
|
|
}
|
|
|
|
// pg-pool removes error listeners on checkout and restores them on release; pool.on(error) covers only idle clients. Guard every checkout or terminated in-flight sessions crash the process.
|
|
function guardCheckedOutClient(client: pg.PoolClient, onRelease: () => void): pg.PoolClient {
|
|
let failure: Error | undefined
|
|
const onError = (error: Error) => {
|
|
failure ??= error
|
|
// Printable unlike the idle path: a checked-out client is past the
|
|
// handshake, so its error carries no connection string.
|
|
console.warn(
|
|
`[orca-relay] checked-out PostgreSQL client failed: ${errorCode(error)} ${errorMessage(error)}`
|
|
)
|
|
}
|
|
client.on('error', onError)
|
|
|
|
// pg-pool assigns a fresh `release` on every acquire, so this never stacks.
|
|
const release = client.release.bind(client)
|
|
client.release = (releaseError?: Error | boolean) => {
|
|
client.removeListener('error', onError)
|
|
// Passing the error makes pg-pool destroy the client instead of returning a
|
|
// dead connection to the pool for the next caller to trip over.
|
|
try {
|
|
release(releaseError ?? failure)
|
|
} finally {
|
|
onRelease()
|
|
}
|
|
}
|
|
return client
|
|
}
|
|
|
|
export function emptyPostgresPoolPressureCounts(): PostgresPoolPressureCounts {
|
|
return emptyCounts()
|
|
}
|