@spfn/core 0.2.0-beta.43 → 0.2.0-beta.45
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/dist/db/index.d.ts +85 -1
- package/dist/db/index.js +178 -11
- package/dist/db/index.js.map +1 -1
- package/dist/event/sse/index.js +31 -9
- package/dist/event/sse/index.js.map +1 -1
- package/dist/server/index.js +31 -9
- package/dist/server/index.js.map +1 -1
- package/docs/database.md +66 -0
- package/package.json +1 -1
package/dist/db/index.d.ts
CHANGED
|
@@ -273,6 +273,36 @@ declare function initDatabase(options?: DatabaseOptions): Promise<{
|
|
|
273
273
|
* ```
|
|
274
274
|
*/
|
|
275
275
|
declare function closeDatabase(): Promise<void>;
|
|
276
|
+
/**
|
|
277
|
+
* Force an immediate database pool rebuild
|
|
278
|
+
*
|
|
279
|
+
* Destroys the current postgres.js pool(s) and rebuilds them with the same
|
|
280
|
+
* configuration passed to the original `initDatabase()` call (or whatever
|
|
281
|
+
* was detected from environment variables). Uses the same atomic-swap
|
|
282
|
+
* strategy as the periodic health check: new connections are created and
|
|
283
|
+
* tested BEFORE the old ones are torn down, so `getDatabase()` callers never
|
|
284
|
+
* observe a missing instance.
|
|
285
|
+
*
|
|
286
|
+
* Use this when application code detects that the pool is stuck and does not
|
|
287
|
+
* want to wait for the next periodic health check tick. Concurrent calls are
|
|
288
|
+
* coalesced — if a reconnect is already in progress, this resolves to `false`
|
|
289
|
+
* without starting a second one.
|
|
290
|
+
*
|
|
291
|
+
* @param reason - Short label describing why the rebuild was requested (for logs)
|
|
292
|
+
* @returns `true` if a reconnection ran, `false` if one was already in-flight.
|
|
293
|
+
* Resolves after the rebuild completes (success or max retries exhausted).
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* import { forceReconnectDatabase } from '@spfn/core/db';
|
|
298
|
+
*
|
|
299
|
+
* app.post('/admin/db/reconnect', async (c) => {
|
|
300
|
+
* const ran = await forceReconnectDatabase('admin_request');
|
|
301
|
+
* return c.json({ reconnected: ran });
|
|
302
|
+
* });
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
declare function forceReconnectDatabase(reason?: string): Promise<boolean>;
|
|
276
306
|
/**
|
|
277
307
|
* Get database connection info (for debugging)
|
|
278
308
|
*
|
|
@@ -308,6 +338,60 @@ declare function getDatabaseInfo(): {
|
|
|
308
338
|
isReplica: boolean;
|
|
309
339
|
};
|
|
310
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Reconnect Trigger — Query-error driven pool rebuild
|
|
343
|
+
*
|
|
344
|
+
* Complements the periodic health check with a fast-path: when application
|
|
345
|
+
* queries start failing with connection-level errors, we do not wait up to
|
|
346
|
+
* DB_HEALTH_CHECK_INTERVAL (default 60s) to notice. A sliding-window counter
|
|
347
|
+
* trips a force-reconnect as soon as the failure rate crosses a threshold.
|
|
348
|
+
*
|
|
349
|
+
* Why this exists:
|
|
350
|
+
* - postgres.js transparently drops dead sockets and opens new ones on the
|
|
351
|
+
* next query. A single `SELECT 1` on the periodic interval can therefore
|
|
352
|
+
* false-pass while user-facing queries keep hitting the remaining dead
|
|
353
|
+
* sockets in the pool.
|
|
354
|
+
* - This module observes real query errors and, when it sees a burst of
|
|
355
|
+
* connection-level failures, calls triggerForceReconnect() which performs
|
|
356
|
+
* the same atomic-swap rebuild as the health check.
|
|
357
|
+
*
|
|
358
|
+
* Configuration (env vars, hardcoded defaults):
|
|
359
|
+
* - DB_RECONNECT_ERROR_THRESHOLD (default 3): errors needed in window
|
|
360
|
+
* - DB_RECONNECT_ERROR_WINDOW_MS (default 10000): sliding window size
|
|
361
|
+
*/
|
|
362
|
+
/**
|
|
363
|
+
* Determine whether an error looks like a pool/connection failure
|
|
364
|
+
*
|
|
365
|
+
* Returns true when any layer in the error chain exposes a connection-level
|
|
366
|
+
* code (postgres.js driver code, Node network errno, PG SQLSTATE class 08 etc.)
|
|
367
|
+
* or is an instance of our own ConnectionError wrapper.
|
|
368
|
+
*
|
|
369
|
+
* Returns false for query errors (syntax, constraint violations, etc.) — those
|
|
370
|
+
* should NOT trigger a pool rebuild.
|
|
371
|
+
*/
|
|
372
|
+
declare function isConnectionLevelError(error: unknown): boolean;
|
|
373
|
+
/**
|
|
374
|
+
* Reset the internal error counter
|
|
375
|
+
*
|
|
376
|
+
* Exposed for tests that need a clean slate between cases. Does not clear
|
|
377
|
+
* the WeakSet (which is GC-backed and self-cleans with error lifetimes).
|
|
378
|
+
*/
|
|
379
|
+
declare function resetConnectionErrorCounter(): void;
|
|
380
|
+
/**
|
|
381
|
+
* Report a database error to the reconnect trigger
|
|
382
|
+
*
|
|
383
|
+
* Call this from any site that catches a query error before rethrowing.
|
|
384
|
+
* It is a no-op for non-connection-level errors. When the threshold is
|
|
385
|
+
* crossed it calls triggerForceReconnect() in the background — callers
|
|
386
|
+
* should NOT await it.
|
|
387
|
+
*
|
|
388
|
+
* Safe to call from any context: catches its own errors so it cannot
|
|
389
|
+
* disrupt the calling catch block. Deduplicates across error-chain
|
|
390
|
+
* re-wrapping so one failure counts exactly once regardless of how many
|
|
391
|
+
* catch layers it passes through.
|
|
392
|
+
*/
|
|
393
|
+
declare function reportDatabaseError(error: unknown): void;
|
|
394
|
+
|
|
311
395
|
/**
|
|
312
396
|
* Create database connection with exponential backoff retry strategy
|
|
313
397
|
*
|
|
@@ -1674,4 +1758,4 @@ declare abstract class BaseRepository<TSchema extends Record<string, unknown> =
|
|
|
1674
1758
|
protected _count<T extends PgTable>(table: T, where?: Record<string, any> | SQL | undefined): Promise<number>;
|
|
1675
1759
|
}
|
|
1676
1760
|
|
|
1677
|
-
export { type AfterCommitCallback, BaseRepository, type DatabaseClients, type DrizzleConfigOptions, type PoolConfig, RepositoryError, type RetryConfig, type RunInTransactionOptions, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, runInTransaction, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
|
|
1761
|
+
export { type AfterCommitCallback, BaseRepository, type DatabaseClients, type DrizzleConfigOptions, type PoolConfig, RepositoryError, type RetryConfig, type RunInTransactionOptions, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, forceReconnectDatabase, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, isConnectionLevelError, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, reportDatabaseError, resetConnectionErrorCounter, runInTransaction, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
|
package/dist/db/index.js
CHANGED
|
@@ -520,9 +520,20 @@ var setHealthCheckInterval = (interval) => {
|
|
|
520
520
|
var setMonitoringConfig = (config) => {
|
|
521
521
|
globalThis.__SPFN_DB_MONITORING__ = config;
|
|
522
522
|
};
|
|
523
|
+
var getInitOptions = () => globalThis.__SPFN_DB_INIT_OPTIONS__;
|
|
524
|
+
var setInitOptions = (options) => {
|
|
525
|
+
globalThis.__SPFN_DB_INIT_OPTIONS__ = options;
|
|
526
|
+
};
|
|
527
|
+
var getIsClosing = () => globalThis.__SPFN_DB_CLOSING__ === true;
|
|
528
|
+
var setIsClosing = (closing) => {
|
|
529
|
+
globalThis.__SPFN_DB_CLOSING__ = closing;
|
|
530
|
+
};
|
|
523
531
|
var dbLogger3 = logger.child("@spfn/core:database");
|
|
524
532
|
var CLIENT_CLOSE_TIMEOUT = 5;
|
|
525
533
|
var isReconnecting = false;
|
|
534
|
+
function isReconnectingNow() {
|
|
535
|
+
return isReconnecting;
|
|
536
|
+
}
|
|
526
537
|
async function testDatabaseConnection(db) {
|
|
527
538
|
await db.execute("SELECT 1");
|
|
528
539
|
}
|
|
@@ -541,6 +552,10 @@ async function closeClient(client) {
|
|
|
541
552
|
}
|
|
542
553
|
}
|
|
543
554
|
async function reconnectAndRestore(options) {
|
|
555
|
+
if (getIsClosing()) {
|
|
556
|
+
dbLogger3.debug("reconnectAndRestore aborted: database is closing");
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
544
559
|
const result = await createDatabaseFromEnv(options);
|
|
545
560
|
if (!result.write) {
|
|
546
561
|
return false;
|
|
@@ -549,6 +564,16 @@ async function reconnectAndRestore(options) {
|
|
|
549
564
|
if (result.read && result.read !== result.write) {
|
|
550
565
|
await testDatabaseConnection(result.read);
|
|
551
566
|
}
|
|
567
|
+
if (getIsClosing()) {
|
|
568
|
+
dbLogger3.warn("reconnectAndRestore: close started mid-rebuild, discarding new pool");
|
|
569
|
+
if (result.writeClient) {
|
|
570
|
+
await closeClient(result.writeClient);
|
|
571
|
+
}
|
|
572
|
+
if (result.readClient && result.readClient !== result.writeClient) {
|
|
573
|
+
await closeClient(result.readClient);
|
|
574
|
+
}
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
552
577
|
const oldWriteClient = getWriteClient();
|
|
553
578
|
const oldReadClient = getReadClient();
|
|
554
579
|
setWriteInstance(result.write);
|
|
@@ -586,15 +611,34 @@ function startHealthCheck(config, options, getDatabase2) {
|
|
|
586
611
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
587
612
|
dbLogger3.error("Database health check failed", { error: message });
|
|
588
613
|
if (config.reconnect) {
|
|
589
|
-
await attemptReconnection(config, options);
|
|
614
|
+
await attemptReconnection(config, options, "health_check_failed");
|
|
590
615
|
}
|
|
591
616
|
}
|
|
592
617
|
}, config.interval);
|
|
593
618
|
setHealthCheckInterval(interval);
|
|
594
619
|
}
|
|
595
|
-
async function
|
|
620
|
+
async function triggerForceReconnect(reason) {
|
|
621
|
+
if (!getWriteInstance()) {
|
|
622
|
+
dbLogger3.warn("Force reconnect skipped: database not initialized", { reason });
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
if (getIsClosing()) {
|
|
626
|
+
dbLogger3.debug("Force reconnect skipped: database is closing", { reason });
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
const options = getInitOptions();
|
|
630
|
+
const config = buildHealthCheckConfig(options?.healthCheck);
|
|
631
|
+
dbLogger3.warn("Force reconnect triggered", { reason });
|
|
632
|
+
return await attemptReconnection(config, options, reason);
|
|
633
|
+
}
|
|
634
|
+
async function attemptReconnection(config, options, reason) {
|
|
635
|
+
if (isReconnecting) {
|
|
636
|
+
dbLogger3.debug("Reconnection coalesced: attempt already in progress", { reason });
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
596
639
|
isReconnecting = true;
|
|
597
640
|
dbLogger3.warn("Attempting database reconnection", {
|
|
641
|
+
reason,
|
|
598
642
|
maxRetries: config.maxRetries,
|
|
599
643
|
retryInterval: `${config.retryInterval}ms`
|
|
600
644
|
});
|
|
@@ -608,7 +652,7 @@ async function attemptReconnection(config, options) {
|
|
|
608
652
|
const success = await reconnectAndRestore(options);
|
|
609
653
|
if (success) {
|
|
610
654
|
dbLogger3.info("Database reconnection successful", { attempt });
|
|
611
|
-
return;
|
|
655
|
+
return true;
|
|
612
656
|
} else {
|
|
613
657
|
dbLogger3.error(`Reconnection attempt ${attempt} failed: No write database instance created`);
|
|
614
658
|
}
|
|
@@ -627,6 +671,7 @@ async function attemptReconnection(config, options) {
|
|
|
627
671
|
} finally {
|
|
628
672
|
isReconnecting = false;
|
|
629
673
|
}
|
|
674
|
+
return true;
|
|
630
675
|
}
|
|
631
676
|
function stopHealthCheck() {
|
|
632
677
|
const healthCheck = getHealthCheckInterval();
|
|
@@ -647,7 +692,6 @@ var STACK_TRACE_PATTERNS = {
|
|
|
647
692
|
withoutParens: /at (.+):(\d+):(\d+)/
|
|
648
693
|
};
|
|
649
694
|
var initPromise = null;
|
|
650
|
-
var isClosing = false;
|
|
651
695
|
async function cleanupDatabaseConnections(writeClient, readClient) {
|
|
652
696
|
const cleanupPromises = [];
|
|
653
697
|
if (writeClient) {
|
|
@@ -748,7 +792,7 @@ function setDatabase(write, read) {
|
|
|
748
792
|
setReadInstance(read ?? write);
|
|
749
793
|
}
|
|
750
794
|
async function initDatabase(options) {
|
|
751
|
-
if (
|
|
795
|
+
if (getIsClosing()) {
|
|
752
796
|
throw new Error("Cannot initialize database while closing");
|
|
753
797
|
}
|
|
754
798
|
const writeInst = getWriteInstance();
|
|
@@ -770,7 +814,7 @@ async function initDatabase(options) {
|
|
|
770
814
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
771
815
|
throw new Error(`Database connection test failed: ${message}`);
|
|
772
816
|
}
|
|
773
|
-
if (
|
|
817
|
+
if (getIsClosing()) {
|
|
774
818
|
dbLogger4.warn("Database closed during initialization, cleaning up...");
|
|
775
819
|
await cleanupDatabaseConnections(result.writeClient, result.readClient);
|
|
776
820
|
throw new Error("Database closed during initialization");
|
|
@@ -779,6 +823,7 @@ async function initDatabase(options) {
|
|
|
779
823
|
setReadInstance(result.read);
|
|
780
824
|
setWriteClient(result.writeClient);
|
|
781
825
|
setReadClient(result.readClient);
|
|
826
|
+
setInitOptions(options);
|
|
782
827
|
const hasReplica = result.read && result.read !== result.write;
|
|
783
828
|
dbLogger4.info(
|
|
784
829
|
hasReplica ? "Database connected (Primary + Replica)" : "Database connected"
|
|
@@ -803,11 +848,11 @@ async function initDatabase(options) {
|
|
|
803
848
|
return await initPromise;
|
|
804
849
|
}
|
|
805
850
|
async function closeDatabase() {
|
|
806
|
-
if (
|
|
851
|
+
if (getIsClosing()) {
|
|
807
852
|
dbLogger4.debug("Database close already in progress");
|
|
808
853
|
return;
|
|
809
854
|
}
|
|
810
|
-
|
|
855
|
+
setIsClosing(true);
|
|
811
856
|
if (initPromise) {
|
|
812
857
|
dbLogger4.debug("Waiting for database initialization to complete before closing...");
|
|
813
858
|
try {
|
|
@@ -820,7 +865,7 @@ async function closeDatabase() {
|
|
|
820
865
|
const readInst = getReadInstance();
|
|
821
866
|
if (!writeInst && !readInst) {
|
|
822
867
|
dbLogger4.debug("No database connections to close");
|
|
823
|
-
|
|
868
|
+
setIsClosing(false);
|
|
824
869
|
return;
|
|
825
870
|
}
|
|
826
871
|
try {
|
|
@@ -842,9 +887,13 @@ async function closeDatabase() {
|
|
|
842
887
|
setWriteClient(void 0);
|
|
843
888
|
setReadClient(void 0);
|
|
844
889
|
setMonitoringConfig(void 0);
|
|
845
|
-
|
|
890
|
+
setInitOptions(void 0);
|
|
891
|
+
setIsClosing(false);
|
|
846
892
|
}
|
|
847
893
|
}
|
|
894
|
+
async function forceReconnectDatabase(reason = "manual") {
|
|
895
|
+
return await triggerForceReconnect(reason);
|
|
896
|
+
}
|
|
848
897
|
function getDatabaseInfo() {
|
|
849
898
|
const writeInst = getWriteInstance();
|
|
850
899
|
const readInst = getReadInstance();
|
|
@@ -854,6 +903,122 @@ function getDatabaseInfo() {
|
|
|
854
903
|
isReplica: !!(readInst && readInst !== writeInst)
|
|
855
904
|
};
|
|
856
905
|
}
|
|
906
|
+
var dbLogger5 = logger.child("@spfn/core:database");
|
|
907
|
+
var POSTGRES_JS_CONNECTION_CODES = /* @__PURE__ */ new Set([
|
|
908
|
+
"CONNECTION_ENDED",
|
|
909
|
+
"CONNECTION_CLOSED",
|
|
910
|
+
"CONNECTION_DESTROYED",
|
|
911
|
+
"CONNECT_TIMEOUT",
|
|
912
|
+
"CONNECTION_CONNECT_TIMEOUT"
|
|
913
|
+
]);
|
|
914
|
+
var NODE_NET_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
915
|
+
"ECONNRESET",
|
|
916
|
+
"ECONNREFUSED",
|
|
917
|
+
"EPIPE",
|
|
918
|
+
"ETIMEDOUT",
|
|
919
|
+
"EHOSTUNREACH",
|
|
920
|
+
"ENETUNREACH",
|
|
921
|
+
"ENOTFOUND"
|
|
922
|
+
]);
|
|
923
|
+
function isConnectionSqlState(code) {
|
|
924
|
+
if (code.startsWith("08")) return true;
|
|
925
|
+
if (code === "53300") return true;
|
|
926
|
+
if (code === "57P01" || code === "57P02" || code === "57P03") return true;
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
function* unwrap(error) {
|
|
930
|
+
const seen = /* @__PURE__ */ new Set();
|
|
931
|
+
const stack = [error];
|
|
932
|
+
while (stack.length > 0) {
|
|
933
|
+
const current = stack.pop();
|
|
934
|
+
if (!current || typeof current !== "object" || seen.has(current)) {
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
seen.add(current);
|
|
938
|
+
const obj = current;
|
|
939
|
+
yield obj;
|
|
940
|
+
for (const key of ["cause", "original", "error", "err", "inner"]) {
|
|
941
|
+
const nested = obj[key];
|
|
942
|
+
if (nested && typeof nested === "object" && !seen.has(nested)) {
|
|
943
|
+
stack.push(nested);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
function isConnectionLevelError(error) {
|
|
949
|
+
if (!error) return false;
|
|
950
|
+
if (error instanceof ConnectionError) return true;
|
|
951
|
+
for (const candidate of unwrap(error)) {
|
|
952
|
+
if (candidate instanceof ConnectionError) return true;
|
|
953
|
+
const code = candidate.code;
|
|
954
|
+
if (typeof code === "string") {
|
|
955
|
+
if (POSTGRES_JS_CONNECTION_CODES.has(code)) return true;
|
|
956
|
+
if (NODE_NET_ERROR_CODES.has(code)) return true;
|
|
957
|
+
if (isConnectionSqlState(code)) return true;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
var DEFAULT_THRESHOLD = 3;
|
|
963
|
+
var DEFAULT_WINDOW_MS = 1e4;
|
|
964
|
+
var MIN_WINDOW_MS = 1e3;
|
|
965
|
+
function readPositiveIntEnv(key, defaultValue, min) {
|
|
966
|
+
const raw = process.env[key];
|
|
967
|
+
if (raw === void 0) return defaultValue;
|
|
968
|
+
try {
|
|
969
|
+
return parseNumber(raw, { min, integer: true });
|
|
970
|
+
} catch {
|
|
971
|
+
return defaultValue;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
var ERROR_THRESHOLD = readPositiveIntEnv("DB_RECONNECT_ERROR_THRESHOLD", DEFAULT_THRESHOLD, 1);
|
|
975
|
+
var ERROR_WINDOW_MS = readPositiveIntEnv("DB_RECONNECT_ERROR_WINDOW_MS", DEFAULT_WINDOW_MS, MIN_WINDOW_MS);
|
|
976
|
+
var errorTimestamps = [];
|
|
977
|
+
var reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
978
|
+
function resetConnectionErrorCounter() {
|
|
979
|
+
errorTimestamps.length = 0;
|
|
980
|
+
}
|
|
981
|
+
function checkAndMarkReported(error) {
|
|
982
|
+
let alreadySeen = false;
|
|
983
|
+
const toMark = [];
|
|
984
|
+
for (const candidate of unwrap(error)) {
|
|
985
|
+
if (reportedErrors.has(candidate)) {
|
|
986
|
+
alreadySeen = true;
|
|
987
|
+
}
|
|
988
|
+
toMark.push(candidate);
|
|
989
|
+
}
|
|
990
|
+
if (alreadySeen) return true;
|
|
991
|
+
for (const obj of toMark) {
|
|
992
|
+
reportedErrors.add(obj);
|
|
993
|
+
}
|
|
994
|
+
return false;
|
|
995
|
+
}
|
|
996
|
+
function reportDatabaseError(error) {
|
|
997
|
+
try {
|
|
998
|
+
if (!isConnectionLevelError(error)) return;
|
|
999
|
+
if (isReconnectingNow()) return;
|
|
1000
|
+
if (checkAndMarkReported(error)) return;
|
|
1001
|
+
const now = Date.now();
|
|
1002
|
+
errorTimestamps.push(now);
|
|
1003
|
+
const cutoff = now - ERROR_WINDOW_MS;
|
|
1004
|
+
while (errorTimestamps.length > 0 && errorTimestamps[0] < cutoff) {
|
|
1005
|
+
errorTimestamps.shift();
|
|
1006
|
+
}
|
|
1007
|
+
if (errorTimestamps.length < ERROR_THRESHOLD) return;
|
|
1008
|
+
errorTimestamps.length = 0;
|
|
1009
|
+
dbLogger5.error("Connection-error threshold crossed, forcing pool rebuild", {
|
|
1010
|
+
threshold: ERROR_THRESHOLD,
|
|
1011
|
+
windowMs: ERROR_WINDOW_MS
|
|
1012
|
+
});
|
|
1013
|
+
triggerForceReconnect("query_error_threshold").catch((err) => {
|
|
1014
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1015
|
+
dbLogger5.error("Forced reconnect after error threshold failed", { error: message });
|
|
1016
|
+
});
|
|
1017
|
+
} catch (err) {
|
|
1018
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1019
|
+
dbLogger5.debug("reportDatabaseError itself threw, ignoring", { error: message });
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
857
1022
|
var BARREL_FILE_PATTERNS = [
|
|
858
1023
|
"/index",
|
|
859
1024
|
"/index.ts",
|
|
@@ -1416,6 +1581,7 @@ function Transactional(options = {}) {
|
|
|
1416
1581
|
}
|
|
1417
1582
|
);
|
|
1418
1583
|
} catch (error) {
|
|
1584
|
+
reportDatabaseError(error);
|
|
1419
1585
|
if (error instanceof DatabaseError) {
|
|
1420
1586
|
throw error;
|
|
1421
1587
|
}
|
|
@@ -1663,6 +1829,7 @@ var BaseRepository = class {
|
|
|
1663
1829
|
try {
|
|
1664
1830
|
return await queryFn();
|
|
1665
1831
|
} catch (error) {
|
|
1832
|
+
reportDatabaseError(error);
|
|
1666
1833
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
1667
1834
|
const repositoryName = this.constructor.name;
|
|
1668
1835
|
throw new RepositoryError(
|
|
@@ -1915,6 +2082,6 @@ var BaseRepository = class {
|
|
|
1915
2082
|
}
|
|
1916
2083
|
};
|
|
1917
2084
|
|
|
1918
|
-
export { BaseRepository, RepositoryError, Transactional, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, runInTransaction, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
|
|
2085
|
+
export { BaseRepository, RepositoryError, Transactional, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, forceReconnectDatabase, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, isConnectionLevelError, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, reportDatabaseError, resetConnectionErrorCounter, runInTransaction, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
|
|
1919
2086
|
//# sourceMappingURL=index.js.map
|
|
1920
2087
|
//# sourceMappingURL=index.js.map
|