@vrplatform/kysely 1.3.45-stage.119 → 1.3.45-stage.1247
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/.data.tar +2 -2
- package/build/main/control-plane/index.d.ts +28 -6
- package/build/main/control-plane/index.js.map +1 -1
- package/build/main/index.d.ts +38 -2
- package/build/main/index.js +271 -55
- package/build/main/index.js.map +1 -1
- package/build/main/local/index.js +115 -8
- package/build/main/local/index.js.map +1 -1
- package/build/main/postgres-error.d.ts +3 -3
- package/build/main/postgres-error.js +4 -3
- package/build/main/postgres-error.js.map +1 -1
- package/build/main/query/listing/status.js +6 -2
- package/build/main/query/listing/status.js.map +1 -1
- package/build/main/query/tenant/status.d.ts +1 -1
- package/build/main/query/tenant/status.js +5 -2
- package/build/main/query/tenant/status.js.map +1 -1
- package/build/main/regional.d.ts +1 -0
- package/build/main/regional.js +1 -0
- package/build/main/regional.js.map +1 -1
- package/build/main/test/index.d.ts +25 -2
- package/build/main/test/index.js +40 -8
- package/build/main/test/index.js.map +1 -1
- package/build/main/v1.generated.d.ts +195 -19
- package/build/main/v1.generated.js.map +1 -1
- package/build/migrations-v2/0000000000033-plaid-connect-session-telemetry.ts +31 -0
- package/build/migrations-v2/0000000000033-reservation-issue-seed-bucket-index.ts +19 -0
- package/build/migrations-v2/0000000000034-retire-recent-classification-indexes.ts +29 -0
- package/build/migrations-v2/0000000000035-core-change-autovacuum.ts +24 -0
- package/build/migrations-v2/0000000000036-journal-entry-inactive-reason-constraint.ts +33 -0
- package/build/migrations-v2/0000000000037-journal-entry-inactive-reasons.ts +19 -0
- package/build/migrations-v2/0000000000038-journal-entry-group-inactive-reason.ts +14 -0
- package/build/migrations-v2/0000000000039-reservation-guest-breakdown.ts +19 -0
- package/build/migrations-v2/0000000000040-csv-import-source-identity-index.ts +23 -0
- package/build/migrations-v2/0000000000041-payment-returns.ts +38 -0
- package/build/migrations-v2/0000000000042-operation-resource-types.ts +609 -0
- package/build/migrations-v2/0000000000043-reservation-journal-freshness.ts +149 -0
- package/build/migrations-v2/0000000000044-reservation-journal-freshness-backfill.ts +254 -0
- package/build/migrations-v2/0000000000045-reservation-journal-freshness-revision-repair.ts +83 -0
- package/build/migrations-v2/0000000000046-materialized-reservation-journal-operations.ts +709 -0
- package/build/migrations-v2/0000000000047-public-audit-completion.ts +77 -0
- package/build/migrations-v2/0000000000048-reservation-journal-full-refresh-repair.ts +136 -0
- package/build/migrations-v2/0000000000049-queued-journal-operation-failures.ts +60 -0
- package/build/migrations-v2/0000000000050-connection-oauth-state.ts +43 -0
- package/build/migrations-v2/0000000000051-owner-statement-email-delivery.ts +134 -0
- package/build/migrations-v2/0000000000052-fee-journal-lock-skip-operations.ts +59 -0
- package/build/migrations-v2/0000000000053-fee-journal-lock-skip-null-attempt.ts +44 -0
- package/build/migrations-v2/0000000000054-journal-refresh-issue-codes.ts +16 -0
- package/build/migrations-v2/0000000000055-tenant-billing-subscription.ts +8 -0
- package/build/migrations-v2/0000000000056-journal-operation-failure-precedence.ts +306 -0
- package/build/migrations-v2/0000000000057-journal-invalidation-failure-code.ts +28 -0
- package/build/migrations-v2/0000000000058-journal-invalidation-locked-failure.ts +16 -0
- package/build/migrations-v2/0000000000059-billing-active-listing-cross-region-partner.ts +135 -0
- package/build/migrations-v2/0000000000060-accounting-integrity-change-driven.ts +99 -0
- package/build/migrations-v2/0000000000061-tenant-pending-onboarding-status.ts +37 -0
- package/build/migrations-v2/0000000000062-statement-payout-operations.ts +16 -0
- package/build/migrations-v2/0000000000063-missing-account-assignments-issue-code.ts +13 -0
- package/build/migrations-v2/0000000000064-journal-invalidation-source-lookup-index.ts +26 -0
- package/build/migrations-v2/0000000000065-hasura-connection-event-credentials.ts +42 -0
- package/build/migrations-v2/0000000000066-recurring-fee-listing-subscription-match-by.ts +51 -0
- package/build/module/control-plane/index.d.ts +28 -6
- package/build/module/control-plane/index.js.map +1 -1
- package/build/module/index.d.ts +38 -2
- package/build/module/index.js +269 -55
- package/build/module/index.js.map +1 -1
- package/build/module/local/index.js +115 -8
- package/build/module/local/index.js.map +1 -1
- package/build/module/postgres-error.d.ts +3 -3
- package/build/module/postgres-error.js +4 -3
- package/build/module/postgres-error.js.map +1 -1
- package/build/module/query/listing/status.js +6 -2
- package/build/module/query/listing/status.js.map +1 -1
- package/build/module/query/tenant/status.d.ts +1 -1
- package/build/module/query/tenant/status.js +5 -2
- package/build/module/query/tenant/status.js.map +1 -1
- package/build/module/regional.d.ts +1 -0
- package/build/module/regional.js +1 -0
- package/build/module/regional.js.map +1 -1
- package/build/module/test/index.d.ts +25 -2
- package/build/module/test/index.js +39 -8
- package/build/module/test/index.js.map +1 -1
- package/build/module/v1.generated.d.ts +195 -19
- package/build/module/v1.generated.js.map +1 -1
- package/package.json +9 -7
package/build/module/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { backOff } from 'exponential-backoff';
|
|
2
|
+
import { Pool } from '@neondatabase/serverless';
|
|
3
|
+
import { PostgresDialect } from 'kysely';
|
|
2
4
|
import { PostgresJSDialect, PostgresJSDriver, } from 'kysely-postgres-js';
|
|
3
5
|
import postgres from 'postgres';
|
|
4
6
|
import { withControlPlaneSchema } from './control-plane';
|
|
5
7
|
import { KyselyError } from './error';
|
|
6
8
|
import { getKysely } from './plugins';
|
|
9
|
+
import { isPostgresError } from './postgres-error';
|
|
7
10
|
export * from './error';
|
|
8
11
|
export * from './isomorphic';
|
|
9
12
|
export * from './postgres-error';
|
|
@@ -16,25 +19,152 @@ const retryableMessages = [
|
|
|
16
19
|
'CONNECT_TIMEOUT',
|
|
17
20
|
'Idle connection closed by Hyperdrive.',
|
|
18
21
|
'server_login_retry',
|
|
22
|
+
'Timed out while creating a new server connection.',
|
|
19
23
|
];
|
|
20
24
|
const postgresRetryAttempts = 3;
|
|
25
|
+
/**
|
|
26
|
+
* Parameter types PostgreSQL described per statement signature (PRO-18871).
|
|
27
|
+
* Postgres.js describes a statement in a round trip of its own before it can
|
|
28
|
+
* execute it when a parameter type is unknown, and a Worker request always
|
|
29
|
+
* starts on new connections. The patched driver reads and fills this map
|
|
30
|
+
* through its `statement_types` option and then parses, describes, binds and
|
|
31
|
+
* executes a known statement in one round trip. It forgets a statement's
|
|
32
|
+
* types when the statement fails, so a schema change is described again.
|
|
33
|
+
*/
|
|
34
|
+
class StatementTypes extends Map {
|
|
35
|
+
set(signature, types) {
|
|
36
|
+
if (!this.has(signature) && this.size >= 2000) {
|
|
37
|
+
this.delete(this.keys().next().value);
|
|
38
|
+
}
|
|
39
|
+
return super.set(signature, types);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Kept for the isolate's lifetime, one map per Hyperdrive database. */
|
|
43
|
+
const hyperdriveStatementTypes = {
|
|
44
|
+
controlPlane: new StatementTypes(),
|
|
45
|
+
regional: new StatementTypes(),
|
|
46
|
+
};
|
|
21
47
|
function isRetryableConnectionError(error) {
|
|
22
48
|
const message = error instanceof Error ? error.message : String(error);
|
|
23
49
|
return retryableMessages.some((retryable) => message.includes(retryable));
|
|
24
50
|
}
|
|
25
|
-
function
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
function isConnectionReset(error) {
|
|
52
|
+
return (error instanceof Error && 'code' in error && error.code === 'ECONNRESET');
|
|
53
|
+
}
|
|
54
|
+
function isRetryableConnectionAcquisitionError(error) {
|
|
55
|
+
return (isRetryableConnectionError(error) ||
|
|
56
|
+
isConnectionReset(error) ||
|
|
57
|
+
(error instanceof Error &&
|
|
58
|
+
(error.message === 'connection attempt failed' ||
|
|
59
|
+
('code' in error && error.code === 'CONNECTION_DESTROYED'))));
|
|
60
|
+
}
|
|
61
|
+
function isQueryRetryDiagnosticError(error) {
|
|
62
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
63
|
+
return (isRetryableConnectionError(error) ||
|
|
64
|
+
isConnectionReset(error) ||
|
|
65
|
+
message === 'Egress connection severed outside of Hyperdrive' ||
|
|
66
|
+
message === 'Network connection lost.' ||
|
|
67
|
+
message === 'Node shutting down' ||
|
|
68
|
+
message === 'Node shutting down.' ||
|
|
69
|
+
message === 'shutting down');
|
|
70
|
+
}
|
|
71
|
+
async function retryPostgres(operation, retry, diagnostic) {
|
|
72
|
+
let attemptCount = 0;
|
|
73
|
+
let retried = false;
|
|
74
|
+
let lastError;
|
|
75
|
+
const emitDiagnostic = (outcome) => {
|
|
76
|
+
if (!diagnostic)
|
|
77
|
+
return;
|
|
78
|
+
diagnostic.emit({
|
|
79
|
+
attemptCount,
|
|
80
|
+
dataRegion: diagnostic.dataRegion,
|
|
81
|
+
errorClass: lastError instanceof Error
|
|
82
|
+
? 'code' in lastError && typeof lastError.code === 'string'
|
|
83
|
+
? lastError.code
|
|
84
|
+
: lastError.constructor.name
|
|
85
|
+
: 'unknown',
|
|
86
|
+
operationKind: diagnostic.operationKind,
|
|
87
|
+
outcome,
|
|
88
|
+
repositoryName: diagnostic.repositoryName,
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
try {
|
|
92
|
+
return await backOff(async () => {
|
|
93
|
+
attemptCount++;
|
|
94
|
+
return await operation();
|
|
95
|
+
}, {
|
|
96
|
+
numOfAttempts: postgresRetryAttempts,
|
|
97
|
+
startingDelay: parseDuration('1s'),
|
|
98
|
+
maxDelay: parseDuration('5s'),
|
|
99
|
+
// Scheduled chunk fan-outs open many connections at once; full jitter
|
|
100
|
+
// keeps their retries from reconnecting in lockstep.
|
|
101
|
+
jitter: 'full',
|
|
102
|
+
async retry(error, attemptNumber) {
|
|
103
|
+
lastError = error;
|
|
104
|
+
const shouldRetry = attemptNumber < postgresRetryAttempts &&
|
|
105
|
+
(await retry(error, attemptNumber));
|
|
106
|
+
if (shouldRetry && diagnostic) {
|
|
107
|
+
retried = true;
|
|
108
|
+
emitDiagnostic('retrying');
|
|
109
|
+
}
|
|
110
|
+
return shouldRetry;
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
lastError = error;
|
|
116
|
+
if (diagnostic?.shouldEmit(error)) {
|
|
117
|
+
emitDiagnostic(retried && attemptCount === postgresRetryAttempts
|
|
118
|
+
? 'exhausted'
|
|
119
|
+
: 'notRetryable');
|
|
120
|
+
}
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Replays a complete database operation after a PostgreSQL deadlock (40P01)
|
|
126
|
+
* or lock timeout (55P03), using the same three-attempt jittered budget as
|
|
127
|
+
* connection retries. The operation must own its transaction and must not
|
|
128
|
+
* contain external side effects such as partner API writes.
|
|
129
|
+
*/
|
|
130
|
+
export function retryPostgresLockFailure(operation) {
|
|
131
|
+
return retryPostgres(operation, (error) => isPostgresError(error, { code: '40P01' }) ||
|
|
132
|
+
isPostgresError(error, { code: '55P03' }));
|
|
32
133
|
}
|
|
33
134
|
class RetryingPostgresJSDialect extends PostgresJSDialect {
|
|
34
135
|
config;
|
|
35
|
-
|
|
136
|
+
retryDiagnostic;
|
|
137
|
+
closeOnScriptTimeout;
|
|
138
|
+
constructor(config, retryDiagnostic, closeOnScriptTimeout) {
|
|
36
139
|
super(config);
|
|
37
140
|
this.config = config;
|
|
141
|
+
this.retryDiagnostic = retryDiagnostic;
|
|
142
|
+
this.closeOnScriptTimeout = closeOnScriptTimeout;
|
|
143
|
+
}
|
|
144
|
+
/** Ends the run when a script's database operation gets no answer (PRO-19037). */
|
|
145
|
+
async withScriptDeadline(operation) {
|
|
146
|
+
if (!this.closeOnScriptTimeout)
|
|
147
|
+
return await operation();
|
|
148
|
+
let timer;
|
|
149
|
+
try {
|
|
150
|
+
return await Promise.race([
|
|
151
|
+
operation(),
|
|
152
|
+
new Promise((_, reject) => {
|
|
153
|
+
timer = setTimeout(() => {
|
|
154
|
+
console.error('Postgres operation timed out', {
|
|
155
|
+
timeoutMs: scriptQueryTimeoutMs,
|
|
156
|
+
});
|
|
157
|
+
this.closeOnScriptTimeout?.();
|
|
158
|
+
reject(new KyselyError('Postgres operation timed out', {
|
|
159
|
+
timeoutMs: scriptQueryTimeoutMs,
|
|
160
|
+
}));
|
|
161
|
+
}, scriptQueryTimeoutMs);
|
|
162
|
+
}),
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
clearTimeout(timer);
|
|
167
|
+
}
|
|
38
168
|
}
|
|
39
169
|
createDriver() {
|
|
40
170
|
const driver = new PostgresJSDriver(this.config);
|
|
@@ -53,20 +183,37 @@ class RetryingPostgresJSDialect extends PostgresJSDialect {
|
|
|
53
183
|
inTransaction: false,
|
|
54
184
|
};
|
|
55
185
|
const connection = {
|
|
56
|
-
executeQuery: (compiledQuery) =>
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
186
|
+
executeQuery: (compiledQuery) => this.withScriptDeadline(() => {
|
|
187
|
+
const operationKind = state.inTransaction
|
|
188
|
+
? 'transaction'
|
|
189
|
+
: compiledQuery.query.kind === 'SelectQueryNode'
|
|
190
|
+
? 'select'
|
|
191
|
+
: 'mutation';
|
|
192
|
+
return retryPostgres(() => state.connection.executeQuery(compiledQuery), async (error) => {
|
|
193
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
194
|
+
if (state.inTransaction ||
|
|
195
|
+
(!isRetryableConnectionError(error) &&
|
|
196
|
+
(compiledQuery.query.kind !== 'SelectQueryNode' ||
|
|
197
|
+
(!isConnectionReset(error) &&
|
|
198
|
+
message !==
|
|
199
|
+
'Egress connection severed outside of Hyperdrive' &&
|
|
200
|
+
message !== 'Network connection lost.' &&
|
|
201
|
+
message !== 'Node shutting down' &&
|
|
202
|
+
message !== 'Node shutting down.' &&
|
|
203
|
+
message !== 'shutting down')))) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
// Postgres.js already ejects a severed reserved connection.
|
|
207
|
+
// Acquire a new reservation before replaying the safe read.
|
|
208
|
+
state.connection = await driver.acquireConnection();
|
|
209
|
+
return true;
|
|
210
|
+
}, this.retryDiagnostic
|
|
211
|
+
? {
|
|
212
|
+
...this.retryDiagnostic,
|
|
213
|
+
operationKind,
|
|
214
|
+
shouldEmit: isQueryRetryDiagnosticError,
|
|
215
|
+
}
|
|
216
|
+
: undefined);
|
|
70
217
|
}),
|
|
71
218
|
streamQuery: (compiledQuery, chunkSize) => {
|
|
72
219
|
if (chunkSize === undefined) {
|
|
@@ -82,7 +229,7 @@ class RetryingPostgresJSDialect extends PostgresJSDialect {
|
|
|
82
229
|
const state = getConnection(connection);
|
|
83
230
|
state.inTransaction = true;
|
|
84
231
|
try {
|
|
85
|
-
await driver.beginTransaction(state.connection, settings);
|
|
232
|
+
await this.withScriptDeadline(() => driver.beginTransaction(state.connection, settings));
|
|
86
233
|
}
|
|
87
234
|
catch (error) {
|
|
88
235
|
state.inTransaction = false;
|
|
@@ -92,7 +239,7 @@ class RetryingPostgresJSDialect extends PostgresJSDialect {
|
|
|
92
239
|
commitTransaction: async (connection) => {
|
|
93
240
|
const state = getConnection(connection);
|
|
94
241
|
try {
|
|
95
|
-
await driver.commitTransaction(state.connection);
|
|
242
|
+
await this.withScriptDeadline(() => driver.commitTransaction(state.connection));
|
|
96
243
|
}
|
|
97
244
|
finally {
|
|
98
245
|
state.inTransaction = false;
|
|
@@ -101,15 +248,15 @@ class RetryingPostgresJSDialect extends PostgresJSDialect {
|
|
|
101
248
|
rollbackTransaction: async (connection) => {
|
|
102
249
|
const state = getConnection(connection);
|
|
103
250
|
try {
|
|
104
|
-
await driver.rollbackTransaction(state.connection);
|
|
251
|
+
await this.withScriptDeadline(() => driver.rollbackTransaction(state.connection));
|
|
105
252
|
}
|
|
106
253
|
finally {
|
|
107
254
|
state.inTransaction = false;
|
|
108
255
|
}
|
|
109
256
|
},
|
|
110
|
-
savepoint: (connection, savepointName, compileQuery) => driver.savepoint(getConnection(connection).connection, savepointName, compileQuery),
|
|
111
|
-
rollbackToSavepoint: (connection, savepointName, compileQuery) => driver.rollbackToSavepoint(getConnection(connection).connection, savepointName, compileQuery),
|
|
112
|
-
releaseSavepoint: (connection, savepointName, compileQuery) => driver.releaseSavepoint(getConnection(connection).connection, savepointName, compileQuery),
|
|
257
|
+
savepoint: (connection, savepointName, compileQuery) => this.withScriptDeadline(() => driver.savepoint(getConnection(connection).connection, savepointName, compileQuery)),
|
|
258
|
+
rollbackToSavepoint: (connection, savepointName, compileQuery) => this.withScriptDeadline(() => driver.rollbackToSavepoint(getConnection(connection).connection, savepointName, compileQuery)),
|
|
259
|
+
releaseSavepoint: (connection, savepointName, compileQuery) => this.withScriptDeadline(() => driver.releaseSavepoint(getConnection(connection).connection, savepointName, compileQuery)),
|
|
113
260
|
releaseConnection: async (connection) => {
|
|
114
261
|
await driver.releaseConnection(getConnection(connection).connection);
|
|
115
262
|
connections.delete(connection);
|
|
@@ -120,16 +267,37 @@ class RetryingPostgresJSDialect extends PostgresJSDialect {
|
|
|
120
267
|
}
|
|
121
268
|
export function useKysely(postgres, options) {
|
|
122
269
|
console.debug('Creating Kysely instance');
|
|
270
|
+
const emitRetryDiagnostic = options?.onRetryDiagnostic ??
|
|
271
|
+
(options?.repositoryName
|
|
272
|
+
? (diagnostic) => console.warn('Postgres operation retry diagnostic', diagnostic)
|
|
273
|
+
: undefined);
|
|
123
274
|
const reserveConnection = postgres.reserve.bind(postgres);
|
|
124
275
|
postgres.reserve = async () => {
|
|
125
|
-
const connection = await retryPostgres(reserveConnection,
|
|
276
|
+
const connection = await retryPostgres(reserveConnection, isRetryableConnectionAcquisitionError, emitRetryDiagnostic
|
|
277
|
+
? {
|
|
278
|
+
dataRegion: options?.dataRegion,
|
|
279
|
+
emit: emitRetryDiagnostic,
|
|
280
|
+
operationKind: 'acquisition',
|
|
281
|
+
repositoryName: options?.repositoryName,
|
|
282
|
+
shouldEmit: isRetryableConnectionAcquisitionError,
|
|
283
|
+
}
|
|
284
|
+
: undefined);
|
|
126
285
|
if (options?.hyperdrive) {
|
|
127
286
|
const unsafe = connection.unsafe;
|
|
128
287
|
connection.unsafe = (query, parameters, queryOptions) => unsafe(query, parameters, { ...queryOptions, prepare: true });
|
|
129
288
|
}
|
|
130
289
|
return connection;
|
|
131
290
|
};
|
|
132
|
-
const kysely = getKysely(new RetryingPostgresJSDialect({ postgres }
|
|
291
|
+
const kysely = getKysely(new RetryingPostgresJSDialect({ postgres }, emitRetryDiagnostic
|
|
292
|
+
? {
|
|
293
|
+
dataRegion: options?.dataRegion,
|
|
294
|
+
emit: emitRetryDiagnostic,
|
|
295
|
+
repositoryName: options?.repositoryName,
|
|
296
|
+
shouldEmit: isQueryRetryDiagnosticError,
|
|
297
|
+
}
|
|
298
|
+
: undefined, options?.script
|
|
299
|
+
? () => void postgres.end({ timeout: 0 }).catch(() => undefined)
|
|
300
|
+
: undefined), {
|
|
133
301
|
repositoryName: options?.repositoryName,
|
|
134
302
|
log: options?.log,
|
|
135
303
|
});
|
|
@@ -142,44 +310,77 @@ export function useKysely(postgres, options) {
|
|
|
142
310
|
};
|
|
143
311
|
return kysely;
|
|
144
312
|
}
|
|
313
|
+
const scriptQueryTimeoutMs = parseDuration('1h');
|
|
145
314
|
let _connections = [];
|
|
315
|
+
export function usePostgres(connectionString, opt) {
|
|
316
|
+
const ssl = opt?.forceDisableSSL !== true && !opt?.hyperdrive;
|
|
317
|
+
const sql = postgres(connectionString, {
|
|
318
|
+
ssl: ssl ? 'require' : false,
|
|
319
|
+
target_session_attrs: 'read-write',
|
|
320
|
+
// Named prepared statements: PRO-17965 (Hyperdrive and Crunchy PgBouncer).
|
|
321
|
+
prepare: true,
|
|
322
|
+
...(opt?.hyperdrive
|
|
323
|
+
? {
|
|
324
|
+
max: 5,
|
|
325
|
+
// Retire idle clients before Hyperdrive can close them asynchronously.
|
|
326
|
+
idle_timeout: 10,
|
|
327
|
+
statement_types: hyperdriveStatementTypes[opt.hyperdrive],
|
|
328
|
+
}
|
|
329
|
+
: {
|
|
330
|
+
connect_timeout: 10,
|
|
331
|
+
idle_timeout: 60,
|
|
332
|
+
...(opt?.script ? { keep_alive: 10 } : {}),
|
|
333
|
+
}),
|
|
334
|
+
});
|
|
335
|
+
if (opt?.onReserve) {
|
|
336
|
+
const { onReserve } = opt;
|
|
337
|
+
const reserve = sql.reserve.bind(sql);
|
|
338
|
+
// Innermost wrapper: it times each open or reuse of a connection, the
|
|
339
|
+
// startup probe too, and is not part of the retry loop (PRO-18952).
|
|
340
|
+
sql.reserve = async () => {
|
|
341
|
+
const startedAtEpochMs = Date.now();
|
|
342
|
+
const startedAt = performance.now();
|
|
343
|
+
try {
|
|
344
|
+
return await reserve();
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
onReserve({
|
|
348
|
+
durationMs: performance.now() - startedAt,
|
|
349
|
+
startedAtEpochMs,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
// statement_timeout/lock_timeout are role-level settings on the cluster
|
|
355
|
+
// (ALTER ROLE application SET ...) so they cover every pooled
|
|
356
|
+
// connection; per-session SETs here only ever applied to one of them
|
|
357
|
+
_connections.push(sql);
|
|
358
|
+
return sql;
|
|
359
|
+
}
|
|
146
360
|
export async function useAsyncPostgres(connectionString, opt) {
|
|
147
361
|
const delayMs = 2500;
|
|
148
362
|
const probe = opt?.probe === true;
|
|
149
363
|
const retries = probe ? (opt?.retries === false ? 0 : opt?.retries || 3) : 1;
|
|
150
|
-
const ssl = opt?.forceDisableSSL !== true && opt?.hyperdrive !== true;
|
|
151
364
|
const url = new URL(connectionString);
|
|
152
|
-
console.debug(`Creating Postgres connection with probe=${probe}, ${retries} attempts, ssl=${
|
|
365
|
+
console.debug(`Creating Postgres connection with probe=${probe}, ${retries} attempts, ssl=${opt?.forceDisableSSL !== true && !opt?.hyperdrive}, and port=${url.port}`);
|
|
153
366
|
if (_connections.length > 0)
|
|
154
367
|
console.warn(`We already have ${_connections.length} Postgres connections`);
|
|
155
368
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
369
|
+
let sql;
|
|
156
370
|
try {
|
|
157
|
-
|
|
158
|
-
ssl: ssl ? 'require' : false,
|
|
159
|
-
target_session_attrs: 'read-write',
|
|
160
|
-
// enable prepare statements, pgbouncer on crunchy allows for prepared statements
|
|
161
|
-
prepare: true,
|
|
162
|
-
...(opt?.hyperdrive
|
|
163
|
-
? {
|
|
164
|
-
max: 5,
|
|
165
|
-
// Retire idle clients before Hyperdrive can close them asynchronously.
|
|
166
|
-
idle_timeout: 10,
|
|
167
|
-
}
|
|
168
|
-
: {
|
|
169
|
-
connect_timeout: 10,
|
|
170
|
-
idle_timeout: 60,
|
|
171
|
-
}),
|
|
172
|
-
});
|
|
371
|
+
sql = usePostgres(connectionString, opt);
|
|
173
372
|
if (probe) {
|
|
174
|
-
|
|
373
|
+
// Opening a connection already checks that the session is writable
|
|
374
|
+
// and loads the array types; a probe query would add a round trip.
|
|
375
|
+
(await sql.reserve()).release();
|
|
175
376
|
}
|
|
176
|
-
// statement_timeout/lock_timeout are role-level settings on the cluster
|
|
177
|
-
// (ALTER ROLE application SET ...) so they cover every pooled
|
|
178
|
-
// connection; per-session SETs here only ever applied to one of them
|
|
179
|
-
_connections.push(sql);
|
|
180
377
|
return sql;
|
|
181
378
|
}
|
|
182
379
|
catch (err) {
|
|
380
|
+
if (sql) {
|
|
381
|
+
await sql.end({ timeout: 0 }).catch(() => undefined);
|
|
382
|
+
_connections.splice(_connections.indexOf(sql), 1);
|
|
383
|
+
}
|
|
183
384
|
if (attempt === retries)
|
|
184
385
|
throw err;
|
|
185
386
|
console.warn(`Postgres connection failed (attempt ${attempt}), retrying...`);
|
|
@@ -193,9 +394,22 @@ export async function useAsyncKysely(connectionString, opt) {
|
|
|
193
394
|
const sql = await useAsyncPostgres(connectionString, opt);
|
|
194
395
|
return useKysely(sql, opt);
|
|
195
396
|
}
|
|
397
|
+
export async function useAsyncNeonWebSocketKysely(connectionString, opt) {
|
|
398
|
+
const pool = new Pool({ connectionString, max: 5 });
|
|
399
|
+
const kysely = getKysely(new PostgresDialect({ pool }), opt ?? {});
|
|
400
|
+
kysely.destroy = async () => {
|
|
401
|
+
console.debug('Closing Neon WebSocket Kysely connection');
|
|
402
|
+
await pool.end();
|
|
403
|
+
};
|
|
404
|
+
if (opt?.probe)
|
|
405
|
+
await pool.query('select 1');
|
|
406
|
+
return kysely;
|
|
407
|
+
}
|
|
408
|
+
export function useControlPlaneKysely(postgres, options) {
|
|
409
|
+
return withControlPlaneSchema(useKysely(postgres, options), 'public');
|
|
410
|
+
}
|
|
196
411
|
export async function useAsyncControlPlaneKysely(connectionString, opt) {
|
|
197
|
-
|
|
198
|
-
return withControlPlaneSchema(useKysely(sql, opt), 'public');
|
|
412
|
+
return useControlPlaneKysely(await useAsyncPostgres(connectionString, opt), opt);
|
|
199
413
|
}
|
|
200
414
|
export async function forceCloseAllPostgres(timeoutMs = 5000) {
|
|
201
415
|
await Promise.race([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import { backOff } from 'exponential-backoff';\nimport type {\n DatabaseConnection,\n Driver,\n Kysely as K,\n LogConfig,\n Transaction as T,\n} from 'kysely';\nimport {\n PostgresJSDialect,\n type PostgresJSDialectConfig,\n PostgresJSDriver,\n} from 'kysely-postgres-js';\nimport postgres, { type Sql } from 'postgres';\nimport { type ControlPlaneDB, withControlPlaneSchema } from './control-plane';\nimport { KyselyError } from './error';\nimport { getKysely } from './plugins';\n\nexport type { Insertable, LogEvent } from 'kysely';\nexport * from './error';\nexport * from './isomorphic';\nexport * from './postgres-error';\nexport * from './query';\nexport * from './regional';\nexport * from './transaction';\n\nimport { parseDuration } from '@vrplatform/utils';\nimport type { DB } from './v1.generated';\n\ntype KyselyOpts = {\n hyperdrive?: true;\n repositoryName?: string;\n log?: true | LogConfig;\n};\n\ntype PgOpt = {\n forceDisableSSL?: boolean;\n probe?: true;\n retries?: false | number;\n} & Pick<KyselyOpts, 'hyperdrive'>;\n\nconst retryableMessages = [\n 'CONNECTION_CLOSED',\n 'CONNECT_TIMEOUT',\n 'Idle connection closed by Hyperdrive.',\n 'server_login_retry',\n];\nconst postgresRetryAttempts = 3;\n\nfunction isRetryableConnectionError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return retryableMessages.some((retryable) => message.includes(retryable));\n}\n\nfunction retryPostgres<T>(\n operation: () => Promise<T>,\n retry: (error: unknown, attemptNumber: number) => boolean | Promise<boolean>\n) {\n return backOff(operation, {\n numOfAttempts: postgresRetryAttempts,\n startingDelay: parseDuration('1s'),\n maxDelay: parseDuration('5s'),\n retry: (error, attemptNumber) =>\n attemptNumber < postgresRetryAttempts && retry(error, attemptNumber),\n });\n}\n\nclass RetryingPostgresJSDialect extends PostgresJSDialect {\n constructor(private readonly config: PostgresJSDialectConfig) {\n super(config);\n }\n\n override createDriver(): Driver {\n const driver = new PostgresJSDriver(this.config);\n const connections = new WeakMap<\n DatabaseConnection,\n { connection: DatabaseConnection; inTransaction: boolean }\n >();\n const getConnection = (connection: DatabaseConnection) => {\n const state = connections.get(connection);\n if (!state) throw new Error('Postgres connection state is unavailable');\n return state;\n };\n\n return {\n init: () => driver.init(),\n acquireConnection: async () => {\n const state = {\n connection: await driver.acquireConnection(),\n inTransaction: false,\n };\n const connection: DatabaseConnection = {\n executeQuery: (compiledQuery) =>\n retryPostgres(\n () => state.connection.executeQuery(compiledQuery),\n async (error) => {\n const message =\n error instanceof Error ? error.message : String(error);\n if (\n state.inTransaction ||\n (!isRetryableConnectionError(error) &&\n (compiledQuery.query.kind !== 'SelectQueryNode' ||\n (message !==\n 'Egress connection severed outside of Hyperdrive' &&\n message !== 'Network connection lost.')))\n ) {\n return false;\n }\n\n // Postgres.js already ejects a severed reserved connection.\n // Acquire a new reservation before replaying the safe read.\n state.connection = await driver.acquireConnection();\n return true;\n }\n ),\n streamQuery: (compiledQuery, chunkSize) => {\n if (chunkSize === undefined) {\n throw new Error('Postgres stream chunk size is required');\n }\n return state.connection.streamQuery(compiledQuery, chunkSize);\n },\n };\n connections.set(connection, state);\n return connection;\n },\n beginTransaction: async (connection, settings) => {\n const state = getConnection(connection);\n state.inTransaction = true;\n try {\n await driver.beginTransaction(state.connection, settings);\n } catch (error) {\n state.inTransaction = false;\n throw error;\n }\n },\n commitTransaction: async (connection) => {\n const state = getConnection(connection);\n try {\n await driver.commitTransaction(state.connection);\n } finally {\n state.inTransaction = false;\n }\n },\n rollbackTransaction: async (connection) => {\n const state = getConnection(connection);\n try {\n await driver.rollbackTransaction(state.connection);\n } finally {\n state.inTransaction = false;\n }\n },\n savepoint: (connection, savepointName, compileQuery) =>\n driver.savepoint(\n getConnection(connection).connection,\n savepointName,\n compileQuery\n ),\n rollbackToSavepoint: (connection, savepointName, compileQuery) =>\n driver.rollbackToSavepoint(\n getConnection(connection).connection,\n savepointName,\n compileQuery\n ),\n releaseSavepoint: (connection, savepointName, compileQuery) =>\n driver.releaseSavepoint(\n getConnection(connection).connection,\n savepointName,\n compileQuery\n ),\n releaseConnection: async (connection) => {\n await driver.releaseConnection(getConnection(connection).connection);\n connections.delete(connection);\n },\n destroy: () => driver.destroy(),\n };\n }\n}\n\nexport type Kysely = K<DB>;\nexport type ControlPlaneKysely = K<ControlPlaneDB>;\nexport type ControlPlaneTransaction = T<ControlPlaneDB>;\nexport function useKysely<Database = DB>(postgres: Sql, options?: KyselyOpts) {\n console.debug('Creating Kysely instance');\n const reserveConnection = postgres.reserve.bind(postgres);\n postgres.reserve = async () => {\n const connection = await retryPostgres(\n reserveConnection,\n isRetryableConnectionError\n );\n if (options?.hyperdrive) {\n const unsafe = connection.unsafe;\n connection.unsafe = (query, parameters, queryOptions) =>\n unsafe(query, parameters, { ...queryOptions, prepare: true });\n }\n return connection;\n };\n const kysely = getKysely<Database>(\n new RetryingPostgresJSDialect({ postgres }),\n {\n repositoryName: options?.repositoryName,\n log: options?.log,\n }\n );\n kysely.destroy = async () => {\n console.debug('Closing Kysely connection');\n await postgres.end({ timeout: 5 }).catch(() => undefined);\n const index = _connections.indexOf(postgres);\n if (index !== -1) _connections.splice(index, 1);\n };\n\n return kysely;\n}\n\nlet _connections: postgres.Sql[] = [];\nexport async function useAsyncPostgres(connectionString: string, opt?: PgOpt) {\n const delayMs = 2500;\n const probe = opt?.probe === true;\n const retries = probe ? (opt?.retries === false ? 0 : opt?.retries || 3) : 1;\n const ssl = opt?.forceDisableSSL !== true && opt?.hyperdrive !== true;\n const url = new URL(connectionString);\n\n console.debug(\n `Creating Postgres connection with probe=${probe}, ${retries} attempts, ssl=${ssl}, and port=${url.port}`\n );\n if (_connections.length > 0)\n console.warn(`We already have ${_connections.length} Postgres connections`);\n\n for (let attempt = 1; attempt <= retries; attempt++) {\n try {\n const sql = postgres(connectionString, {\n ssl: ssl ? 'require' : false,\n target_session_attrs: 'read-write',\n // enable prepare statements, pgbouncer on crunchy allows for prepared statements\n prepare: true,\n ...(opt?.hyperdrive\n ? {\n max: 5,\n // Retire idle clients before Hyperdrive can close them asynchronously.\n idle_timeout: 10,\n }\n : {\n connect_timeout: 10,\n idle_timeout: 60,\n }),\n });\n\n if (probe) {\n await sql`select 1`;\n }\n // statement_timeout/lock_timeout are role-level settings on the cluster\n // (ALTER ROLE application SET ...) so they cover every pooled\n // connection; per-session SETs here only ever applied to one of them\n\n _connections.push(sql);\n return sql;\n } catch (err) {\n if (attempt === retries) throw err;\n console.warn(\n `Postgres connection failed (attempt ${attempt}), retrying...`\n );\n console.error('error', err);\n await new Promise((res) => setTimeout(res, delayMs));\n }\n }\n throw new KyselyError('Unable to connect to Postgres');\n}\n\nexport async function useAsyncKysely(\n connectionString: string,\n opt?: PgOpt & KyselyOpts\n) {\n const sql = await useAsyncPostgres(connectionString, opt);\n return useKysely(sql, opt);\n}\n\nexport async function useAsyncControlPlaneKysely(\n connectionString: string,\n opt?: PgOpt & KyselyOpts\n) {\n const sql = await useAsyncPostgres(connectionString, opt);\n return withControlPlaneSchema(useKysely<ControlPlaneDB>(sql, opt), 'public');\n}\n\nexport async function forceCloseAllPostgres(timeoutMs = 5000) {\n await Promise.race([\n Promise.all(\n _connections.map((pg) =>\n pg.end({ timeout: timeoutMs / 1000 }).catch(() => undefined)\n )\n ),\n new Promise((res) => setTimeout(res, timeoutMs + 1000)),\n ]);\n _connections = [];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import { backOff } from 'exponential-backoff';\nimport { Pool } from '@neondatabase/serverless';\nimport type {\n DatabaseConnection,\n Driver,\n Kysely as K,\n LogConfig,\n Transaction as T,\n} from 'kysely';\nimport { PostgresDialect } from 'kysely';\nimport {\n PostgresJSDialect,\n type PostgresJSDialectConfig,\n PostgresJSDriver,\n} from 'kysely-postgres-js';\nimport postgres, { type Sql } from 'postgres';\nimport { type ControlPlaneDB, withControlPlaneSchema } from './control-plane';\nimport { KyselyError } from './error';\nimport { getKysely } from './plugins';\nimport { isPostgresError } from './postgres-error';\n\nexport type { Insertable, LogEvent } from 'kysely';\nexport * from './error';\nexport * from './isomorphic';\nexport * from './postgres-error';\nexport * from './query';\nexport * from './regional';\nexport * from './transaction';\n\nimport { parseDuration } from '@vrplatform/utils';\nimport type { TenantDataRegion } from './regional';\nimport type { DB } from './v1.generated';\n\n/** The database a Worker reaches through its Hyperdrive binding. */\ntype HyperdriveDatabase = 'controlPlane' | 'regional';\n\ntype KyselyOpts = {\n dataRegion?: TenantDataRegion;\n hyperdrive?: HyperdriveDatabase;\n /** Called after every connection reservation, including the startup probe. */\n onReserve?: (timing: {\n durationMs: number;\n startedAtEpochMs: number;\n }) => void;\n onRetryDiagnostic?: (diagnostic: PostgresRetryDiagnostic) => void;\n repositoryName?: string;\n log?: true | LogConfig;\n /**\n * Scripts and repairs (PRO-19037). Probes an idle TCP connection after 10 s.\n * A query, commit, begin, rollback or savepoint with no answer for an hour,\n * longer than any statement timeout a script sets, fails the run and closes\n * the Postgres client that issued it. Backstop for a connection the kernel\n * keeps open while the peer never answers.\n */\n script?: true;\n};\n\ntype PostgresRetryDiagnostic = {\n attemptCount: number;\n dataRegion?: TenantDataRegion;\n errorClass: string;\n operationKind: 'acquisition' | 'mutation' | 'select' | 'transaction';\n outcome: 'exhausted' | 'notRetryable' | 'retrying';\n repositoryName?: string;\n};\n\ntype PgOpt = {\n forceDisableSSL?: boolean;\n probe?: true;\n retries?: false | number;\n} & Pick<KyselyOpts, 'hyperdrive' | 'onReserve' | 'script'>;\n\nconst retryableMessages = [\n 'CONNECTION_CLOSED',\n 'CONNECT_TIMEOUT',\n 'Idle connection closed by Hyperdrive.',\n 'server_login_retry',\n 'Timed out while creating a new server connection.',\n];\nconst postgresRetryAttempts = 3;\n\n/**\n * Parameter types PostgreSQL described per statement signature (PRO-18871).\n * Postgres.js describes a statement in a round trip of its own before it can\n * execute it when a parameter type is unknown, and a Worker request always\n * starts on new connections. The patched driver reads and fills this map\n * through its `statement_types` option and then parses, describes, binds and\n * executes a known statement in one round trip. It forgets a statement's\n * types when the statement fails, so a schema change is described again.\n */\nclass StatementTypes extends Map<string, number[]> {\n override set(signature: string, types: number[]) {\n if (!this.has(signature) && this.size >= 2000) {\n this.delete(this.keys().next().value!);\n }\n return super.set(signature, types);\n }\n}\n\n/** Kept for the isolate's lifetime, one map per Hyperdrive database. */\nconst hyperdriveStatementTypes: Record<HyperdriveDatabase, StatementTypes> = {\n controlPlane: new StatementTypes(),\n regional: new StatementTypes(),\n};\n\nfunction isRetryableConnectionError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return retryableMessages.some((retryable) => message.includes(retryable));\n}\n\nfunction isConnectionReset(error: unknown) {\n return (\n error instanceof Error && 'code' in error && error.code === 'ECONNRESET'\n );\n}\n\nfunction isRetryableConnectionAcquisitionError(error: unknown) {\n return (\n isRetryableConnectionError(error) ||\n isConnectionReset(error) ||\n (error instanceof Error &&\n (error.message === 'connection attempt failed' ||\n ('code' in error && error.code === 'CONNECTION_DESTROYED')))\n );\n}\n\nfunction isQueryRetryDiagnosticError(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return (\n isRetryableConnectionError(error) ||\n isConnectionReset(error) ||\n message === 'Egress connection severed outside of Hyperdrive' ||\n message === 'Network connection lost.' ||\n message === 'Node shutting down' ||\n message === 'Node shutting down.' ||\n message === 'shutting down'\n );\n}\n\nasync function retryPostgres<T>(\n operation: () => Promise<T>,\n retry: (error: unknown, attemptNumber: number) => boolean | Promise<boolean>,\n diagnostic?: Omit<\n PostgresRetryDiagnostic,\n 'attemptCount' | 'errorClass' | 'outcome'\n > & {\n emit: (diagnostic: PostgresRetryDiagnostic) => void;\n shouldEmit: (error: unknown) => boolean;\n }\n) {\n let attemptCount = 0;\n let retried = false;\n let lastError: unknown;\n const emitDiagnostic = (outcome: PostgresRetryDiagnostic['outcome']) => {\n if (!diagnostic) return;\n diagnostic.emit({\n attemptCount,\n dataRegion: diagnostic.dataRegion,\n errorClass:\n lastError instanceof Error\n ? 'code' in lastError && typeof lastError.code === 'string'\n ? lastError.code\n : lastError.constructor.name\n : 'unknown',\n operationKind: diagnostic.operationKind,\n outcome,\n repositoryName: diagnostic.repositoryName,\n });\n };\n try {\n return await backOff(\n async () => {\n attemptCount++;\n return await operation();\n },\n {\n numOfAttempts: postgresRetryAttempts,\n startingDelay: parseDuration('1s'),\n maxDelay: parseDuration('5s'),\n // Scheduled chunk fan-outs open many connections at once; full jitter\n // keeps their retries from reconnecting in lockstep.\n jitter: 'full',\n async retry(error, attemptNumber) {\n lastError = error;\n const shouldRetry =\n attemptNumber < postgresRetryAttempts &&\n (await retry(error, attemptNumber));\n if (shouldRetry && diagnostic) {\n retried = true;\n emitDiagnostic('retrying');\n }\n return shouldRetry;\n },\n }\n );\n } catch (error) {\n lastError = error;\n if (diagnostic?.shouldEmit(error)) {\n emitDiagnostic(\n retried && attemptCount === postgresRetryAttempts\n ? 'exhausted'\n : 'notRetryable'\n );\n }\n throw error;\n }\n}\n\n/**\n * Replays a complete database operation after a PostgreSQL deadlock (40P01)\n * or lock timeout (55P03), using the same three-attempt jittered budget as\n * connection retries. The operation must own its transaction and must not\n * contain external side effects such as partner API writes.\n */\nexport function retryPostgresLockFailure<T>(operation: () => Promise<T>) {\n return retryPostgres(\n operation,\n (error) =>\n isPostgresError(error, { code: '40P01' }) ||\n isPostgresError(error, { code: '55P03' })\n );\n}\n\nclass RetryingPostgresJSDialect extends PostgresJSDialect {\n constructor(\n private readonly config: PostgresJSDialectConfig,\n private readonly retryDiagnostic?: Omit<\n PostgresRetryDiagnostic,\n 'attemptCount' | 'errorClass' | 'operationKind' | 'outcome'\n > & {\n emit: (diagnostic: PostgresRetryDiagnostic) => void;\n shouldEmit: (error: unknown) => boolean;\n },\n private readonly closeOnScriptTimeout?: () => void\n ) {\n super(config);\n }\n\n /** Ends the run when a script's database operation gets no answer (PRO-19037). */\n private async withScriptDeadline<T>(operation: () => Promise<T>) {\n if (!this.closeOnScriptTimeout) return await operation();\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n operation(),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n console.error('Postgres operation timed out', {\n timeoutMs: scriptQueryTimeoutMs,\n });\n this.closeOnScriptTimeout?.();\n reject(\n new KyselyError('Postgres operation timed out', {\n timeoutMs: scriptQueryTimeoutMs,\n })\n );\n }, scriptQueryTimeoutMs);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n }\n\n override createDriver(): Driver {\n const driver = new PostgresJSDriver(this.config);\n const connections = new WeakMap<\n DatabaseConnection,\n { connection: DatabaseConnection; inTransaction: boolean }\n >();\n const getConnection = (connection: DatabaseConnection) => {\n const state = connections.get(connection);\n if (!state) throw new Error('Postgres connection state is unavailable');\n return state;\n };\n\n return {\n init: () => driver.init(),\n acquireConnection: async () => {\n const state = {\n connection: await driver.acquireConnection(),\n inTransaction: false,\n };\n const connection: DatabaseConnection = {\n executeQuery: (compiledQuery) =>\n this.withScriptDeadline(() => {\n const operationKind = state.inTransaction\n ? 'transaction'\n : compiledQuery.query.kind === 'SelectQueryNode'\n ? 'select'\n : 'mutation';\n return retryPostgres(\n () => state.connection.executeQuery(compiledQuery),\n async (error) => {\n const message =\n error instanceof Error ? error.message : String(error);\n if (\n state.inTransaction ||\n (!isRetryableConnectionError(error) &&\n (compiledQuery.query.kind !== 'SelectQueryNode' ||\n (!isConnectionReset(error) &&\n message !==\n 'Egress connection severed outside of Hyperdrive' &&\n message !== 'Network connection lost.' &&\n message !== 'Node shutting down' &&\n message !== 'Node shutting down.' &&\n message !== 'shutting down')))\n ) {\n return false;\n }\n\n // Postgres.js already ejects a severed reserved connection.\n // Acquire a new reservation before replaying the safe read.\n state.connection = await driver.acquireConnection();\n return true;\n },\n this.retryDiagnostic\n ? {\n ...this.retryDiagnostic,\n operationKind,\n shouldEmit: isQueryRetryDiagnosticError,\n }\n : undefined\n );\n }),\n streamQuery: (compiledQuery, chunkSize) => {\n if (chunkSize === undefined) {\n throw new Error('Postgres stream chunk size is required');\n }\n return state.connection.streamQuery(compiledQuery, chunkSize);\n },\n };\n connections.set(connection, state);\n return connection;\n },\n beginTransaction: async (connection, settings) => {\n const state = getConnection(connection);\n state.inTransaction = true;\n try {\n await this.withScriptDeadline(() =>\n driver.beginTransaction(state.connection, settings)\n );\n } catch (error) {\n state.inTransaction = false;\n throw error;\n }\n },\n commitTransaction: async (connection) => {\n const state = getConnection(connection);\n try {\n await this.withScriptDeadline(() =>\n driver.commitTransaction(state.connection)\n );\n } finally {\n state.inTransaction = false;\n }\n },\n rollbackTransaction: async (connection) => {\n const state = getConnection(connection);\n try {\n await this.withScriptDeadline(() =>\n driver.rollbackTransaction(state.connection)\n );\n } finally {\n state.inTransaction = false;\n }\n },\n savepoint: (connection, savepointName, compileQuery) =>\n this.withScriptDeadline(() =>\n driver.savepoint(\n getConnection(connection).connection,\n savepointName,\n compileQuery\n )\n ),\n rollbackToSavepoint: (connection, savepointName, compileQuery) =>\n this.withScriptDeadline(() =>\n driver.rollbackToSavepoint(\n getConnection(connection).connection,\n savepointName,\n compileQuery\n )\n ),\n releaseSavepoint: (connection, savepointName, compileQuery) =>\n this.withScriptDeadline(() =>\n driver.releaseSavepoint(\n getConnection(connection).connection,\n savepointName,\n compileQuery\n )\n ),\n releaseConnection: async (connection) => {\n await driver.releaseConnection(getConnection(connection).connection);\n connections.delete(connection);\n },\n destroy: () => driver.destroy(),\n };\n }\n}\n\nexport type Kysely = K<DB>;\nexport type ControlPlaneKysely = K<ControlPlaneDB>;\nexport type ControlPlaneTransaction = T<ControlPlaneDB>;\nexport function useKysely<Database = DB>(postgres: Sql, options?: KyselyOpts) {\n console.debug('Creating Kysely instance');\n const emitRetryDiagnostic =\n options?.onRetryDiagnostic ??\n (options?.repositoryName\n ? (diagnostic: PostgresRetryDiagnostic) =>\n console.warn('Postgres operation retry diagnostic', diagnostic)\n : undefined);\n const reserveConnection = postgres.reserve.bind(postgres);\n postgres.reserve = async () => {\n const connection = await retryPostgres(\n reserveConnection,\n isRetryableConnectionAcquisitionError,\n emitRetryDiagnostic\n ? {\n dataRegion: options?.dataRegion,\n emit: emitRetryDiagnostic,\n operationKind: 'acquisition',\n repositoryName: options?.repositoryName,\n shouldEmit: isRetryableConnectionAcquisitionError,\n }\n : undefined\n );\n if (options?.hyperdrive) {\n const unsafe = connection.unsafe;\n connection.unsafe = (query, parameters, queryOptions) =>\n unsafe(query, parameters, { ...queryOptions, prepare: true });\n }\n return connection;\n };\n const kysely = getKysely<Database>(\n new RetryingPostgresJSDialect(\n { postgres },\n emitRetryDiagnostic\n ? {\n dataRegion: options?.dataRegion,\n emit: emitRetryDiagnostic,\n repositoryName: options?.repositoryName,\n shouldEmit: isQueryRetryDiagnosticError,\n }\n : undefined,\n options?.script\n ? () => void postgres.end({ timeout: 0 }).catch(() => undefined)\n : undefined\n ),\n {\n repositoryName: options?.repositoryName,\n log: options?.log,\n }\n );\n kysely.destroy = async () => {\n console.debug('Closing Kysely connection');\n await postgres.end({ timeout: 5 }).catch(() => undefined);\n const index = _connections.indexOf(postgres);\n if (index !== -1) _connections.splice(index, 1);\n };\n\n return kysely;\n}\n\nconst scriptQueryTimeoutMs = parseDuration('1h');\nlet _connections: postgres.Sql[] = [];\nexport function usePostgres(\n connectionString: string,\n opt?: Pick<PgOpt, 'forceDisableSSL' | 'hyperdrive' | 'onReserve' | 'script'>\n) {\n const ssl = opt?.forceDisableSSL !== true && !opt?.hyperdrive;\n const sql = postgres(connectionString, {\n ssl: ssl ? 'require' : false,\n target_session_attrs: 'read-write',\n // Named prepared statements: PRO-17965 (Hyperdrive and Crunchy PgBouncer).\n prepare: true,\n ...(opt?.hyperdrive\n ? {\n max: 5,\n // Retire idle clients before Hyperdrive can close them asynchronously.\n idle_timeout: 10,\n statement_types: hyperdriveStatementTypes[opt.hyperdrive],\n }\n : {\n connect_timeout: 10,\n idle_timeout: 60,\n ...(opt?.script ? { keep_alive: 10 } : {}),\n }),\n });\n if (opt?.onReserve) {\n const { onReserve } = opt;\n const reserve = sql.reserve.bind(sql);\n // Innermost wrapper: it times each open or reuse of a connection, the\n // startup probe too, and is not part of the retry loop (PRO-18952).\n sql.reserve = async () => {\n const startedAtEpochMs = Date.now();\n const startedAt = performance.now();\n try {\n return await reserve();\n } finally {\n onReserve({\n durationMs: performance.now() - startedAt,\n startedAtEpochMs,\n });\n }\n };\n }\n // statement_timeout/lock_timeout are role-level settings on the cluster\n // (ALTER ROLE application SET ...) so they cover every pooled\n // connection; per-session SETs here only ever applied to one of them\n _connections.push(sql);\n return sql;\n}\n\nexport async function useAsyncPostgres(connectionString: string, opt?: PgOpt) {\n const delayMs = 2500;\n const probe = opt?.probe === true;\n const retries = probe ? (opt?.retries === false ? 0 : opt?.retries || 3) : 1;\n const url = new URL(connectionString);\n\n console.debug(\n `Creating Postgres connection with probe=${probe}, ${retries} attempts, ssl=${opt?.forceDisableSSL !== true && !opt?.hyperdrive}, and port=${url.port}`\n );\n if (_connections.length > 0)\n console.warn(`We already have ${_connections.length} Postgres connections`);\n\n for (let attempt = 1; attempt <= retries; attempt++) {\n let sql: Sql | undefined;\n try {\n sql = usePostgres(connectionString, opt);\n if (probe) {\n // Opening a connection already checks that the session is writable\n // and loads the array types; a probe query would add a round trip.\n (await sql.reserve()).release();\n }\n return sql;\n } catch (err) {\n if (sql) {\n await sql.end({ timeout: 0 }).catch(() => undefined);\n _connections.splice(_connections.indexOf(sql), 1);\n }\n if (attempt === retries) throw err;\n console.warn(\n `Postgres connection failed (attempt ${attempt}), retrying...`\n );\n console.error('error', err);\n await new Promise((res) => setTimeout(res, delayMs));\n }\n }\n throw new KyselyError('Unable to connect to Postgres');\n}\n\nexport async function useAsyncKysely(\n connectionString: string,\n opt?: PgOpt & KyselyOpts\n) {\n const sql = await useAsyncPostgres(connectionString, opt);\n return useKysely(sql, opt);\n}\n\nexport async function useAsyncNeonWebSocketKysely(\n connectionString: string,\n opt?: Pick<PgOpt, 'probe'> & KyselyOpts\n) {\n const pool = new Pool({ connectionString, max: 5 });\n const kysely = getKysely<DB>(new PostgresDialect({ pool }), opt ?? {});\n kysely.destroy = async () => {\n console.debug('Closing Neon WebSocket Kysely connection');\n await pool.end();\n };\n if (opt?.probe) await pool.query('select 1');\n return kysely;\n}\n\nexport function useControlPlaneKysely(postgres: Sql, options?: KyselyOpts) {\n return withControlPlaneSchema(\n useKysely<ControlPlaneDB>(postgres, options),\n 'public'\n );\n}\n\nexport async function useAsyncControlPlaneKysely(\n connectionString: string,\n opt?: PgOpt & KyselyOpts\n) {\n return useControlPlaneKysely(\n await useAsyncPostgres(connectionString, opt),\n opt\n );\n}\n\nexport async function forceCloseAllPostgres(timeoutMs = 5000) {\n await Promise.race([\n Promise.all(\n _connections.map((pg) =>\n pg.end({ timeout: timeoutMs / 1000 }).catch(() => undefined)\n )\n ),\n new Promise((res) => setTimeout(res, timeoutMs + 1000)),\n ]);\n _connections = [];\n}\n"]}
|