@mistriwork/engine 0.0.0-stage → 1.0.0-rc.10
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/LICENSE +21 -0
- package/README.md +17 -2
- package/dist/adapters/adapter.d.ts +693 -0
- package/dist/adapters/adapter.js +444 -0
- package/dist/adapters/cache/cacheStore.d.ts +75 -0
- package/dist/adapters/cache/cacheStore.js +160 -0
- package/dist/adapters/cache/resp.d.ts +52 -0
- package/dist/adapters/cache/resp.js +237 -0
- package/dist/adapters/commands/canonicalJson.d.ts +19 -0
- package/dist/adapters/commands/canonicalJson.js +154 -0
- package/dist/adapters/connect.d.ts +29 -0
- package/dist/adapters/connect.js +40 -0
- package/dist/adapters/connectionBudget.d.ts +101 -0
- package/dist/adapters/connectionBudget.js +128 -0
- package/dist/adapters/dbLatency.d.ts +35 -0
- package/dist/adapters/dbLatency.js +79 -0
- package/dist/adapters/epochCache.d.ts +50 -0
- package/dist/adapters/epochCache.js +85 -0
- package/dist/adapters/http/egress.d.ts +48 -0
- package/dist/adapters/http/egress.js +175 -0
- package/dist/adapters/http/httpAdapter.d.ts +71 -0
- package/dist/adapters/http/httpAdapter.js +512 -0
- package/dist/adapters/http/httpDescription.d.ts +32 -0
- package/dist/adapters/http/httpDescription.js +246 -0
- package/dist/adapters/http/openapi.d.ts +98 -0
- package/dist/adapters/http/openapi.js +487 -0
- package/dist/adapters/layoutRefusal.d.ts +24 -0
- package/dist/adapters/layoutRefusal.js +35 -0
- package/dist/adapters/net/networkPolicy.d.ts +44 -0
- package/dist/adapters/net/networkPolicy.js +123 -0
- package/dist/adapters/net/untrustedDatabase.d.ts +18 -0
- package/dist/adapters/net/untrustedDatabase.js +120 -0
- package/dist/adapters/poolAdmission.d.ts +43 -0
- package/dist/adapters/poolAdmission.js +184 -0
- package/dist/adapters/poolMetrics.d.ts +121 -0
- package/dist/adapters/poolMetrics.js +185 -0
- package/dist/adapters/postgres.d.ts +436 -0
- package/dist/adapters/postgres.js +1890 -0
- package/dist/adapters/postgresCatalog.d.ts +96 -0
- package/dist/adapters/postgresCatalog.js +387 -0
- package/dist/adapters/postgresCatalogDigest.d.ts +30 -0
- package/dist/adapters/postgresCatalogDigest.js +87 -0
- package/dist/adapters/postgresCatalogRows.d.ts +62 -0
- package/dist/adapters/postgresCatalogRows.js +37 -0
- package/dist/adapters/postgresDates.d.ts +1 -0
- package/dist/adapters/postgresDates.js +39 -0
- package/dist/adapters/postgresMaintenance.d.ts +33 -0
- package/dist/adapters/postgresMaintenance.js +179 -0
- package/dist/adapters/postgresPool.d.ts +255 -0
- package/dist/adapters/postgresPool.js +838 -0
- package/dist/adapters/postgresPooler.d.ts +28 -0
- package/dist/adapters/postgresPooler.js +267 -0
- package/dist/adapters/postgresReadCatalog.d.ts +31 -0
- package/dist/adapters/postgresReadCatalog.js +226 -0
- package/dist/adapters/postgresReadSnapshot.d.ts +46 -0
- package/dist/adapters/postgresReadSnapshot.js +133 -0
- package/dist/adapters/postgresSchemaEpoch.d.ts +24 -0
- package/dist/adapters/postgresSchemaEpoch.js +149 -0
- package/dist/adapters/postgresTransactionHandle.d.ts +173 -0
- package/dist/adapters/postgresTransactionHandle.js +524 -0
- package/dist/adapters/postgresValues.d.ts +30 -0
- package/dist/adapters/postgresValues.js +75 -0
- package/dist/adapters/replicas.d.ts +70 -0
- package/dist/adapters/replicas.js +208 -0
- package/dist/adapters/reservedSql.d.ts +38 -0
- package/dist/adapters/reservedSql.js +264 -0
- package/dist/adapters/rolePrivileges.d.ts +17 -0
- package/dist/adapters/rolePrivileges.js +22 -0
- package/dist/adapters/scoped/keyRows.d.ts +65 -0
- package/dist/adapters/scoped/keyRows.js +173 -0
- package/dist/adapters/scoped/postgresScopeCatalog.d.ts +16 -0
- package/dist/adapters/scoped/postgresScopeCatalog.js +117 -0
- package/dist/adapters/scoped/scopedCommandOperations.d.ts +5 -0
- package/dist/adapters/scoped/scopedCommandOperations.js +48 -0
- package/dist/adapters/scoped/scopedReads.d.ts +20 -0
- package/dist/adapters/scoped/scopedReads.js +174 -0
- package/dist/adapters/scoped/scopedTransaction.d.ts +89 -0
- package/dist/adapters/scoped/scopedTransaction.js +303 -0
- package/dist/adapters/scoped/sqliteScopeCatalog.d.ts +16 -0
- package/dist/adapters/scoped/sqliteScopeCatalog.js +241 -0
- package/dist/adapters/scoped/updateGuard.d.ts +3 -0
- package/dist/adapters/scoped/updateGuard.js +40 -0
- package/dist/adapters/serverSettings.d.ts +45 -0
- package/dist/adapters/serverSettings.js +46 -0
- package/dist/adapters/sqlite.d.ts +262 -0
- package/dist/adapters/sqlite.js +1434 -0
- package/dist/adapters/sqliteCostlyReads.d.ts +33 -0
- package/dist/adapters/sqliteCostlyReads.js +49 -0
- package/dist/adapters/sqliteDecimals.d.ts +22 -0
- package/dist/adapters/sqliteDecimals.js +57 -0
- package/dist/adapters/sqliteHandles.d.ts +43 -0
- package/dist/adapters/sqliteHandles.js +143 -0
- package/dist/adapters/sqliteStatementProcess.d.ts +29 -0
- package/dist/adapters/sqliteStatementProcess.js +131 -0
- package/dist/adapters/statementControl.d.ts +34 -0
- package/dist/adapters/statementControl.js +90 -0
- package/dist/adapters/storeHost.d.ts +130 -0
- package/dist/adapters/storeHost.js +13 -0
- package/dist/adapters/telemetry/drivers.d.ts +21 -0
- package/dist/adapters/telemetry/drivers.js +321 -0
- package/dist/adapters/telemetry/metrics.d.ts +63 -0
- package/dist/adapters/telemetry/metrics.js +122 -0
- package/dist/adapters/telemetry/otlp.d.ts +12 -0
- package/dist/adapters/telemetry/otlp.js +320 -0
- package/dist/adapters/telemetry/otlpEncode.d.ts +48 -0
- package/dist/adapters/telemetry/otlpEncode.js +366 -0
- package/dist/adapters/telemetry/otlpEnv.d.ts +1 -0
- package/dist/adapters/telemetry/otlpEnv.js +12 -0
- package/dist/adapters/telemetry/serverTiming.d.ts +83 -0
- package/dist/adapters/telemetry/serverTiming.js +503 -0
- package/dist/adapters/telemetry/start.d.ts +10 -0
- package/dist/adapters/telemetry/start.js +25 -0
- package/dist/adapters/telemetry/statement.d.ts +16 -0
- package/dist/adapters/telemetry/statement.js +134 -0
- package/dist/adapters/telemetry/trace.d.ts +127 -0
- package/dist/adapters/telemetry/trace.js +295 -0
- package/dist/adapters/writeAhead.d.ts +27 -0
- package/dist/adapters/writeAhead.js +46 -0
- package/dist/adapters/writeAttribution.d.ts +18 -0
- package/dist/adapters/writeAttribution.js +23 -0
- package/dist/adapters/writeCapture.d.ts +73 -0
- package/dist/adapters/writeCapture.js +20 -0
- package/dist/adapters/writeLanes.d.ts +94 -0
- package/dist/adapters/writeLanes.js +232 -0
- package/dist/engine.d.ts +15 -0
- package/dist/engine.js +13 -0
- package/dist/server/auth/credentials.d.ts +61 -0
- package/dist/server/auth/credentials.js +260 -0
- package/dist/server/cacheService.d.ts +50 -0
- package/dist/server/cacheService.js +110 -0
- package/dist/server/cli/args.d.ts +176 -0
- package/dist/server/cli/args.js +342 -0
- package/dist/server/cli/bin.d.ts +5 -0
- package/dist/server/cli/bin.js +66 -0
- package/dist/server/cli/browser.d.ts +15 -0
- package/dist/server/cli/browser.js +37 -0
- package/dist/server/cli/config.d.ts +50 -0
- package/dist/server/cli/config.js +206 -0
- package/dist/server/cli/startup.d.ts +36 -0
- package/dist/server/cli/startup.js +128 -0
- package/dist/server/cli/startupOptions.d.ts +63 -0
- package/dist/server/cli/startupOptions.js +143 -0
- package/dist/server/cli/subcommand.d.ts +28 -0
- package/dist/server/cli/subcommand.js +32 -0
- package/dist/server/cli/usage.d.ts +13 -0
- package/dist/server/cli/usage.js +31 -0
- package/dist/server/clientHttp.d.ts +184 -0
- package/dist/server/clientHttp.js +700 -0
- package/dist/server/clientSurface.d.ts +128 -0
- package/dist/server/clientSurface.js +182 -0
- package/dist/server/columnGrants.d.ts +103 -0
- package/dist/server/columnGrants.js +730 -0
- package/dist/server/compileCache.d.ts +1 -0
- package/dist/server/compileCache.js +30 -0
- package/dist/server/config/locations.d.ts +1 -0
- package/dist/server/config/locations.js +15 -0
- package/dist/server/configPaths.d.ts +7 -0
- package/dist/server/configPaths.js +34 -0
- package/dist/server/configSections.d.ts +64 -0
- package/dist/server/configSections.js +72 -0
- package/dist/server/connectionRedaction.d.ts +30 -0
- package/dist/server/connectionRedaction.js +77 -0
- package/dist/server/connections.d.ts +353 -0
- package/dist/server/connections.js +978 -0
- package/dist/server/constraints.d.ts +82 -0
- package/dist/server/constraints.js +169 -0
- package/dist/server/demo.d.ts +9 -0
- package/dist/server/demo.js +554 -0
- package/dist/server/devclient.d.ts +108 -0
- package/dist/server/devclient.js +361 -0
- package/dist/server/evidence.d.ts +55 -0
- package/dist/server/evidence.js +323 -0
- package/dist/server/hiddenColumns.d.ts +32 -0
- package/dist/server/hiddenColumns.js +238 -0
- package/dist/server/http/baselineHeaders.d.ts +24 -0
- package/dist/server/http/baselineHeaders.js +12 -0
- package/dist/server/http/captured.d.ts +35 -0
- package/dist/server/http/captured.js +73 -0
- package/dist/server/http/clock.d.ts +1 -0
- package/dist/server/http/clock.js +14 -0
- package/dist/server/http/connectionSource.d.ts +4 -0
- package/dist/server/http/connectionSource.js +48 -0
- package/dist/server/http/context.d.ts +189 -0
- package/dist/server/http/context.js +11 -0
- package/dist/server/http/crawlerText.d.ts +15 -0
- package/dist/server/http/crawlerText.js +32 -0
- package/dist/server/http/dataRoutes.d.ts +88 -0
- package/dist/server/http/dataRoutes.js +137 -0
- package/dist/server/http/door.d.ts +57 -0
- package/dist/server/http/door.js +39 -0
- package/dist/server/http/exportRoute.d.ts +15 -0
- package/dist/server/http/exportRoute.js +320 -0
- package/dist/server/http/firstPaint.d.ts +108 -0
- package/dist/server/http/firstPaint.js +262 -0
- package/dist/server/http/httpOperationRoutes.d.ts +11 -0
- package/dist/server/http/httpOperationRoutes.js +50 -0
- package/dist/server/http/pageReads.d.ts +20 -0
- package/dist/server/http/pageReads.js +205 -0
- package/dist/server/http/pageRoutes.d.ts +39 -0
- package/dist/server/http/pageRoutes.js +291 -0
- package/dist/server/http/probes.d.ts +22 -0
- package/dist/server/http/probes.js +27 -0
- package/dist/server/http/rateLimit.d.ts +131 -0
- package/dist/server/http/rateLimit.js +351 -0
- package/dist/server/http/readRoutes.d.ts +9 -0
- package/dist/server/http/readRoutes.js +131 -0
- package/dist/server/http/readRouting.d.ts +20 -0
- package/dist/server/http/readRouting.js +80 -0
- package/dist/server/http/recordCompose.d.ts +67 -0
- package/dist/server/http/recordCompose.js +167 -0
- package/dist/server/http/recordWrites.d.ts +89 -0
- package/dist/server/http/recordWrites.js +298 -0
- package/dist/server/http/refusalReport.d.ts +38 -0
- package/dist/server/http/refusalReport.js +63 -0
- package/dist/server/http/reply.d.ts +130 -0
- package/dist/server/http/reply.js +394 -0
- package/dist/server/http/requestOrigin.d.ts +11 -0
- package/dist/server/http/requestOrigin.js +8 -0
- package/dist/server/http/router.d.ts +45 -0
- package/dist/server/http/router.js +22 -0
- package/dist/server/http/serverCore.d.ts +68 -0
- package/dist/server/http/serverCore.js +177 -0
- package/dist/server/http/traceRequest.d.ts +30 -0
- package/dist/server/http/traceRequest.js +224 -0
- package/dist/server/http/trust.d.ts +261 -0
- package/dist/server/http/trust.js +472 -0
- package/dist/server/http/writeRefusal.d.ts +33 -0
- package/dist/server/http/writeRefusal.js +24 -0
- package/dist/server/httpConnection.d.ts +49 -0
- package/dist/server/httpConnection.js +170 -0
- package/dist/server/impact.d.ts +55 -0
- package/dist/server/impact.js +47 -0
- package/dist/server/journal.d.ts +134 -0
- package/dist/server/journal.js +398 -0
- package/dist/server/keysetReadHttp.d.ts +52 -0
- package/dist/server/keysetReadHttp.js +85 -0
- package/dist/server/layouts.d.ts +49 -0
- package/dist/server/layouts.js +192 -0
- package/dist/server/migrate/cli.d.ts +55 -0
- package/dist/server/migrate/cli.js +517 -0
- package/dist/server/migrate/database.d.ts +37 -0
- package/dist/server/migrate/database.js +247 -0
- package/dist/server/migrate/desired.d.ts +76 -0
- package/dist/server/migrate/desired.js +292 -0
- package/dist/server/migrate/files.d.ts +34 -0
- package/dist/server/migrate/files.js +141 -0
- package/dist/server/migrate/flyway.d.ts +7 -0
- package/dist/server/migrate/flyway.js +69 -0
- package/dist/server/migrate/history.d.ts +70 -0
- package/dist/server/migrate/history.js +211 -0
- package/dist/server/migrate/runner.d.ts +100 -0
- package/dist/server/migrate/runner.js +409 -0
- package/dist/server/migrate/snapshot.d.ts +30 -0
- package/dist/server/migrate/snapshot.js +239 -0
- package/dist/server/migrate/target.d.ts +33 -0
- package/dist/server/migrate/target.js +82 -0
- package/dist/server/openapiDraft.d.ts +25 -0
- package/dist/server/openapiDraft.js +171 -0
- package/dist/server/operations/diagnostics.d.ts +101 -0
- package/dist/server/operations/diagnostics.js +284 -0
- package/dist/server/operations/requestMetrics.d.ts +33 -0
- package/dist/server/operations/requestMetrics.js +94 -0
- package/dist/server/packageVersion.d.ts +1 -0
- package/dist/server/packageVersion.js +11 -0
- package/dist/server/poolConfig.d.ts +34 -0
- package/dist/server/poolConfig.js +114 -0
- package/dist/server/product.d.ts +20 -0
- package/dist/server/product.js +12 -0
- package/dist/server/recordHistoryOperations.d.ts +39 -0
- package/dist/server/recordHistoryOperations.js +43 -0
- package/dist/server/recordWriteOperations.d.ts +111 -0
- package/dist/server/recordWriteOperations.js +329 -0
- package/dist/server/resourceReadHttp.d.ts +81 -0
- package/dist/server/resourceReadHttp.js +606 -0
- package/dist/server/resourceReadOperations.d.ts +147 -0
- package/dist/server/resourceReadOperations.js +452 -0
- package/dist/server/scaffoldMachine.d.ts +54 -0
- package/dist/server/scaffoldMachine.js +184 -0
- package/dist/server/seed/cli.d.ts +20 -0
- package/dist/server/seed/cli.js +164 -0
- package/dist/server/seed/lexicon.d.ts +26 -0
- package/dist/server/seed/lexicon.js +585 -0
- package/dist/server/seed/seed.d.ts +70 -0
- package/dist/server/seed/seed.js +779 -0
- package/dist/server/seed/words.d.ts +21 -0
- package/dist/server/seed/words.js +164 -0
- package/dist/server/serverRequests.d.ts +6 -0
- package/dist/server/serverRequests.js +33 -0
- package/dist/server/serverTimeouts.d.ts +33 -0
- package/dist/server/serverTimeouts.js +32 -0
- package/dist/server/shapeonly.d.ts +32 -0
- package/dist/server/shapeonly.js +110 -0
- package/dist/server/startupWarnings.d.ts +32 -0
- package/dist/server/startupWarnings.js +92 -0
- package/dist/shared/addressParams.d.ts +28 -0
- package/dist/shared/addressParams.js +47 -0
- package/dist/shared/appCards.d.ts +123 -0
- package/dist/shared/appCards.js +385 -0
- package/dist/shared/appComponents.d.ts +177 -0
- package/dist/shared/appComponents.js +422 -0
- package/dist/shared/appDocuments.d.ts +43 -0
- package/dist/shared/appDocuments.js +131 -0
- package/dist/shared/appEmbeds.d.ts +21 -0
- package/dist/shared/appEmbeds.js +24 -0
- package/dist/shared/appIcons.d.ts +28 -0
- package/dist/shared/appIcons.js +223 -0
- package/dist/shared/appLinks.d.ts +17 -0
- package/dist/shared/appLinks.js +124 -0
- package/dist/shared/appListColumns.d.ts +3 -0
- package/dist/shared/appListColumns.js +66 -0
- package/dist/shared/appLocale.d.ts +36 -0
- package/dist/shared/appLocale.js +86 -0
- package/dist/shared/appMessages.d.ts +44 -0
- package/dist/shared/appMessages.js +78 -0
- package/dist/shared/appNoun.d.ts +21 -0
- package/dist/shared/appNoun.js +32 -0
- package/dist/shared/autoBucket.d.ts +98 -0
- package/dist/shared/autoBucket.js +305 -0
- package/dist/shared/autoValues.d.ts +25 -0
- package/dist/shared/autoValues.js +25 -0
- package/dist/shared/breakdown.d.ts +228 -0
- package/dist/shared/breakdown.js +557 -0
- package/dist/shared/breakdowntext.d.ts +41 -0
- package/dist/shared/breakdowntext.js +400 -0
- package/dist/shared/builtInThemes.d.ts +2 -0
- package/dist/shared/builtInThemes.js +771 -0
- package/dist/shared/chartSeparation.d.ts +11 -0
- package/dist/shared/chartSeparation.js +58 -0
- package/dist/shared/clauses.d.ts +22 -0
- package/dist/shared/clauses.js +97 -0
- package/dist/shared/columnCaption.d.ts +16 -0
- package/dist/shared/columnCaption.js +90 -0
- package/dist/shared/commandFieldPath.d.ts +2 -0
- package/dist/shared/commandFieldPath.js +7 -0
- package/dist/shared/commandWire.d.ts +26 -0
- package/dist/shared/commandWire.js +41 -0
- package/dist/shared/computed.d.ts +161 -0
- package/dist/shared/computed.js +378 -0
- package/dist/shared/computedParse.d.ts +13 -0
- package/dist/shared/computedParse.js +278 -0
- package/dist/shared/computedValue.d.ts +47 -0
- package/dist/shared/computedValue.js +191 -0
- package/dist/shared/connection.d.ts +30 -0
- package/dist/shared/connection.js +1 -0
- package/dist/shared/csv.d.ts +20 -0
- package/dist/shared/csv.js +63 -0
- package/dist/shared/dashLayout.d.ts +30 -0
- package/dist/shared/dashLayout.js +132 -0
- package/dist/shared/dashboardAsks.d.ts +93 -0
- package/dist/shared/dashboardAsks.js +129 -0
- package/dist/shared/dashboardTrend.d.ts +20 -0
- package/dist/shared/dashboardTrend.js +50 -0
- package/dist/shared/declaredReferences.d.ts +50 -0
- package/dist/shared/declaredReferences.js +202 -0
- package/dist/shared/definedFields.d.ts +63 -0
- package/dist/shared/definedFields.js +103 -0
- package/dist/shared/definedValues.d.ts +63 -0
- package/dist/shared/definedValues.js +184 -0
- package/dist/shared/displayDefaults.d.ts +135 -0
- package/dist/shared/displayDefaults.js +529 -0
- package/dist/shared/dueDate.d.ts +6 -0
- package/dist/shared/dueDate.js +6 -0
- package/dist/shared/duration.d.ts +4 -0
- package/dist/shared/duration.js +23 -0
- package/dist/shared/fileBounds.d.ts +9 -0
- package/dist/shared/fileBounds.js +9 -0
- package/dist/shared/filter.d.ts +217 -0
- package/dist/shared/filter.js +305 -0
- package/dist/shared/firstPaint.d.ts +24 -0
- package/dist/shared/firstPaint.js +33 -0
- package/dist/shared/form.d.ts +21 -0
- package/dist/shared/form.js +25 -0
- package/dist/shared/formValues.d.ts +104 -0
- package/dist/shared/formValues.js +352 -0
- package/dist/shared/funnel.d.ts +45 -0
- package/dist/shared/funnel.js +108 -0
- package/dist/shared/httpOperations.d.ts +48 -0
- package/dist/shared/httpOperations.js +9 -0
- package/dist/shared/iconGlyphs.d.ts +4 -0
- package/dist/shared/iconGlyphs.js +151 -0
- package/dist/shared/iconWeights.d.ts +3 -0
- package/dist/shared/iconWeights.js +162 -0
- package/dist/shared/imageWidths.d.ts +32 -0
- package/dist/shared/imageWidths.js +38 -0
- package/dist/shared/keysetPage.d.ts +64 -0
- package/dist/shared/keysetPage.js +161 -0
- package/dist/shared/like.d.ts +20 -0
- package/dist/shared/like.js +29 -0
- package/dist/shared/listEmpty.d.ts +12 -0
- package/dist/shared/listEmpty.js +36 -0
- package/dist/shared/listFilters.d.ts +28 -0
- package/dist/shared/listFilters.js +84 -0
- package/dist/shared/listPresentations.d.ts +199 -0
- package/dist/shared/listPresentations.js +7 -0
- package/dist/shared/listQuestion.d.ts +52 -0
- package/dist/shared/listQuestion.js +137 -0
- package/dist/shared/lookPalettes.d.ts +62 -0
- package/dist/shared/lookPalettes.js +306 -0
- package/dist/shared/maskQuoted.d.ts +24 -0
- package/dist/shared/maskQuoted.js +48 -0
- package/dist/shared/mentionLabel.d.ts +19 -0
- package/dist/shared/mentionLabel.js +38 -0
- package/dist/shared/mentionLookup.d.ts +17 -0
- package/dist/shared/mentionLookup.js +64 -0
- package/dist/shared/menuCounts.d.ts +16 -0
- package/dist/shared/menuCounts.js +74 -0
- package/dist/shared/model.d.ts +79 -0
- package/dist/shared/model.js +153 -0
- package/dist/shared/money.d.ts +44 -0
- package/dist/shared/money.js +81 -0
- package/dist/shared/moneyEntry.d.ts +7 -0
- package/dist/shared/moneyEntry.js +27 -0
- package/dist/shared/mount.d.ts +118 -0
- package/dist/shared/mount.js +248 -0
- package/dist/shared/niceTicks.d.ts +26 -0
- package/dist/shared/niceTicks.js +50 -0
- package/dist/shared/order.d.ts +59 -0
- package/dist/shared/order.js +133 -0
- package/dist/shared/page.d.ts +135 -0
- package/dist/shared/page.js +1187 -0
- package/dist/shared/pageAnatomy.d.ts +99 -0
- package/dist/shared/pageAnatomy.js +370 -0
- package/dist/shared/pageConfig.d.ts +25 -0
- package/dist/shared/pageConfig.js +1308 -0
- package/dist/shared/pageEmptyActions.d.ts +12 -0
- package/dist/shared/pageEmptyActions.js +20 -0
- package/dist/shared/pageLayouts.d.ts +72 -0
- package/dist/shared/pageLayouts.js +205 -0
- package/dist/shared/pageModel.d.ts +785 -0
- package/dist/shared/pageModel.js +121 -0
- package/dist/shared/pagePaths.d.ts +180 -0
- package/dist/shared/pagePaths.js +869 -0
- package/dist/shared/pagePlans.d.ts +52 -0
- package/dist/shared/pagePlans.js +108 -0
- package/dist/shared/pageRoutes.d.ts +9 -0
- package/dist/shared/pageRoutes.js +9 -0
- package/dist/shared/query.d.ts +166 -0
- package/dist/shared/query.js +1302 -0
- package/dist/shared/queryDates.d.ts +12 -0
- package/dist/shared/queryDates.js +101 -0
- package/dist/shared/queryOperands.d.ts +99 -0
- package/dist/shared/queryOperands.js +131 -0
- package/dist/shared/reads.d.ts +103 -0
- package/dist/shared/reads.js +154 -0
- package/dist/shared/recordLabel.d.ts +51 -0
- package/dist/shared/recordLabel.js +123 -0
- package/dist/shared/recordReadKeys.d.ts +30 -0
- package/dist/shared/recordReadKeys.js +51 -0
- package/dist/shared/recordReads.d.ts +268 -0
- package/dist/shared/recordReads.js +368 -0
- package/dist/shared/recordview.d.ts +198 -0
- package/dist/shared/recordview.js +356 -0
- package/dist/shared/referenceModel.d.ts +58 -0
- package/dist/shared/referenceModel.js +194 -0
- package/dist/shared/refusal.d.ts +64 -0
- package/dist/shared/refusal.js +144 -0
- package/dist/shared/relatedCreate.d.ts +30 -0
- package/dist/shared/relatedCreate.js +162 -0
- package/dist/shared/resourceHttp.d.ts +464 -0
- package/dist/shared/resourceHttp.js +1 -0
- package/dist/shared/richText.d.ts +85 -0
- package/dist/shared/richText.js +305 -0
- package/dist/shared/rollups.d.ts +143 -0
- package/dist/shared/rollups.js +64 -0
- package/dist/shared/rowScope.d.ts +72 -0
- package/dist/shared/rowScope.js +376 -0
- package/dist/shared/runtimeTables.d.ts +44 -0
- package/dist/shared/runtimeTables.js +79 -0
- package/dist/shared/schema.d.ts +523 -0
- package/dist/shared/schema.js +627 -0
- package/dist/shared/schematext.d.ts +38 -0
- package/dist/shared/schematext.js +101 -0
- package/dist/shared/scopeCatalog.d.ts +29 -0
- package/dist/shared/scopeCatalog.js +468 -0
- package/dist/shared/servedApp.d.ts +681 -0
- package/dist/shared/servedApp.js +45 -0
- package/dist/shared/sessionLease.d.ts +34 -0
- package/dist/shared/sessionLease.js +3 -0
- package/dist/shared/shape.d.ts +55 -0
- package/dist/shared/shape.js +168 -0
- package/dist/shared/slug.d.ts +1 -0
- package/dist/shared/slug.js +12 -0
- package/dist/shared/sortText.d.ts +14 -0
- package/dist/shared/sortText.js +26 -0
- package/dist/shared/sqlRelation.d.ts +4 -0
- package/dist/shared/sqlRelation.js +8 -0
- package/dist/shared/sqlsaved.d.ts +44 -0
- package/dist/shared/sqlsaved.js +63 -0
- package/dist/shared/stitchedPaths.d.ts +27 -0
- package/dist/shared/stitchedPaths.js +38 -0
- package/dist/shared/structuredFilter.d.ts +22 -0
- package/dist/shared/structuredFilter.js +1 -0
- package/dist/shared/subtitlePath.d.ts +20 -0
- package/dist/shared/subtitlePath.js +24 -0
- package/dist/shared/suggest.d.ts +18 -0
- package/dist/shared/suggest.js +59 -0
- package/dist/shared/tableSizing.d.ts +4 -0
- package/dist/shared/tableSizing.js +9 -0
- package/dist/shared/tagLists.d.ts +57 -0
- package/dist/shared/tagLists.js +34 -0
- package/dist/shared/textRules.d.ts +54 -0
- package/dist/shared/textRules.js +133 -0
- package/dist/shared/theme.d.ts +94 -0
- package/dist/shared/theme.js +800 -0
- package/dist/shared/themeColor.d.ts +34 -0
- package/dist/shared/themeColor.js +109 -0
- package/dist/shared/themeFaces.d.ts +30 -0
- package/dist/shared/themeFaces.js +42 -0
- package/dist/shared/themeInk.d.ts +20 -0
- package/dist/shared/themeInk.js +52 -0
- package/dist/shared/themeLooks.d.ts +9 -0
- package/dist/shared/themeLooks.js +572 -0
- package/dist/shared/themeParts.d.ts +26 -0
- package/dist/shared/themeParts.js +71 -0
- package/dist/shared/themePartsCheck.d.ts +21 -0
- package/dist/shared/themePartsCheck.js +91 -0
- package/dist/shared/themePresets.d.ts +85 -0
- package/dist/shared/themePresets.js +139 -0
- package/dist/shared/themeRecipeWords.d.ts +156 -0
- package/dist/shared/themeRecipeWords.js +156 -0
- package/dist/shared/themeRecipes.d.ts +213 -0
- package/dist/shared/themeRecipes.js +234 -0
- package/dist/shared/themeResolve.d.ts +55 -0
- package/dist/shared/themeResolve.js +52 -0
- package/dist/shared/themeTokens.d.ts +107 -0
- package/dist/shared/themeTokens.js +116 -0
- package/dist/shared/themeType.d.ts +38 -0
- package/dist/shared/themeType.js +205 -0
- package/dist/shared/unitWord.d.ts +4 -0
- package/dist/shared/unitWord.js +37 -0
- package/dist/shared/view.d.ts +195 -0
- package/dist/shared/view.js +987 -0
- package/dist/shared/viewLabels.d.ts +47 -0
- package/dist/shared/viewLabels.js +459 -0
- package/dist/shared/viewPaths.d.ts +248 -0
- package/dist/shared/viewPaths.js +294 -0
- package/dist/shared/viewtext.d.ts +57 -0
- package/dist/shared/viewtext.js +282 -0
- package/dist/shared/vocabulary.d.ts +71 -0
- package/dist/shared/vocabulary.js +249 -0
- package/dist/shared/weekStart.d.ts +7 -0
- package/dist/shared/weekStart.js +36 -0
- package/mark.svg +1 -0
- package/package.json +261 -4
- package/schema/model.d.ts +298 -0
- package/schema/theme.d.ts +587 -0
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PostgreSQL connections of one database: a bounded reader pool, a
|
|
3
|
+
* writer pool, and one background writer, each opened on first use, every
|
|
4
|
+
* lease given back in `finally`.
|
|
5
|
+
*
|
|
6
|
+
* Three things vary by deployment, per manager or, for the whole process,
|
|
7
|
+
* from the command line:
|
|
8
|
+
*
|
|
9
|
+
* - How many readers the pool may hold: for an App as many as its writers
|
|
10
|
+
* (at least balancedPoolSize), four for the database browser, unless told
|
|
11
|
+
* otherwise; one to sixty-four.
|
|
12
|
+
* - How many writers: balancedPoolSize for a process serving Apps unless told otherwise
|
|
13
|
+
* (`pool.write`, `--write-pool`; writeLanes.ts), one to sixty-four, each running one
|
|
14
|
+
* transaction beside the others (writeLanes.ts orders writes of one
|
|
15
|
+
* record). The database browser keeps one: its writes run one at a time,
|
|
16
|
+
* the order its Undo depends on (docs/postgresql.md).
|
|
17
|
+
* - Background work — a job's command — writes through a connection of its
|
|
18
|
+
* own, so it never takes the writer a person's save is waiting for.
|
|
19
|
+
* - What sits between this process and the server. Directly, or through a
|
|
20
|
+
* pooler that gives each client a whole server session, the read-only
|
|
21
|
+
* default and the statement cap are set once per physical connection.
|
|
22
|
+
* Through a transaction-mode pooler they are set per transaction instead
|
|
23
|
+
* (postgresPooler.ts): the next transaction may run on another server
|
|
24
|
+
* connection, and anything set on this one stays for the pooler's next
|
|
25
|
+
* client.
|
|
26
|
+
*
|
|
27
|
+
* Every checkout is metered (poolMetrics.ts) — the wait for a connection,
|
|
28
|
+
* how long it was held, how many are queued now — for the operator surface.
|
|
29
|
+
*
|
|
30
|
+
* A connection that names replicas gets a manager per replica, sized like
|
|
31
|
+
* this one's readers, and the reader handle's statements go to one of them
|
|
32
|
+
* when the work they run in tolerates lag (replicas.ts). Everything that
|
|
33
|
+
* leases a whole client — a snapshot, a raw SQL read, a store's read, every
|
|
34
|
+
* write — stays here, on the primary.
|
|
35
|
+
*/
|
|
36
|
+
import { cancellation, onCancel, STATEMENT_TIMEOUT_MS, StatementStopped, throwIfCancelled } from './statementControl.js';
|
|
37
|
+
import { PoolBusy, PoolMeter, POOL_LIMIT_HOLD_MS, POOL_QUEUE_PER_CONNECTION, POOL_QUEUE_WAIT_MS, } from './poolMetrics.js';
|
|
38
|
+
import { PoolAdmission } from './poolAdmission.js';
|
|
39
|
+
import { holdConnections, managerConnections } from './connectionBudget.js';
|
|
40
|
+
import { pgConnectionOptions } from './postgresValues.js';
|
|
41
|
+
import { sealedPgOptions } from './net/untrustedDatabase.js';
|
|
42
|
+
import { transactionScoped } from './postgresPooler.js';
|
|
43
|
+
import { balancedPoolSize, inBackgroundWork, isWriterCount, lockTimeoutMs, MAX_WRITERS, writeOrder, writerPoolSize } from './writeLanes.js';
|
|
44
|
+
import { startSpan } from './telemetry/trace.js';
|
|
45
|
+
import { delayFlights } from './dbLatency.js';
|
|
46
|
+
import { configureServerSettings, serverSettings } from './serverSettings.js';
|
|
47
|
+
import { MAX_REPLICAS, noteWrite, primaryReads, ReplicaSet } from './replicas.js';
|
|
48
|
+
/* The leases whose client sends each query without waiting for the one before
|
|
49
|
+
(a writer's, on node-postgres): what pipelined() may send together. */
|
|
50
|
+
const pipelining = new WeakSet();
|
|
51
|
+
/** This lease's client pipelines: several queries asked at once leave together. */
|
|
52
|
+
export const pipelines = (client) => pipelining.has(client);
|
|
53
|
+
/** `wrapper` sends through `client` and nothing else: it pipelines where `client` does. */
|
|
54
|
+
export const pipelinesAs = (wrapper, client) => {
|
|
55
|
+
if (pipelining.has(client))
|
|
56
|
+
pipelining.add(wrapper);
|
|
57
|
+
};
|
|
58
|
+
const loadPg = async () => {
|
|
59
|
+
const specifier = 'pg';
|
|
60
|
+
return import(specifier).catch(() => {
|
|
61
|
+
throw new Error('Postgres support needs the "pg" package. Install it with: npm install pg');
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
/** The database browser's reader pool size unless a connection or the command line says otherwise.
|
|
65
|
+
* An App's is balanced against its writers (appReaders). */
|
|
66
|
+
export const DEFAULT_READERS = 4;
|
|
67
|
+
/** Readers kept connected through an idle spell. Without them the first read
|
|
68
|
+
* after thirty quiet seconds paid a new connection and its two SETs — and a
|
|
69
|
+
* dashboard's fan-out paid it several times at once. Two, not the whole pool:
|
|
70
|
+
* a warm connection is a server backend held for nothing most of the day. */
|
|
71
|
+
const WARM_READERS = 2;
|
|
72
|
+
/** The most readers one database's pool may hold. Past this, a pooler in front of the server is the tool. */
|
|
73
|
+
export const MAX_READERS = 64;
|
|
74
|
+
/** What pg-pool says when no connection came free within the acquisition timeout. */
|
|
75
|
+
const ACQUISITION_TIMEOUT = /timeout exceeded when trying to connect/i;
|
|
76
|
+
/** The server refused this process another connection: too many for its role, or for the server (SQLSTATE 53300). */
|
|
77
|
+
const atConnectionLimit = (error) => error !== null && typeof error === 'object' && error.code === '53300';
|
|
78
|
+
const LIMIT_HOLD_MS = POOL_LIMIT_HOLD_MS;
|
|
79
|
+
/** How many times one checkout asks again after a refusal, a held connection having gone meanwhile. */
|
|
80
|
+
const LIMIT_ATTEMPTS = 3;
|
|
81
|
+
/** Whether `value` is a reader pool size a manager accepts: a whole number from 1 to 64. */
|
|
82
|
+
export function isReaderCount(value) {
|
|
83
|
+
return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= MAX_READERS;
|
|
84
|
+
}
|
|
85
|
+
/* Server timing (telemetry/serverTiming.ts) is loaded with the first pool, not with this module: a print-and-exit
|
|
86
|
+
flag reaches this file through the settings it reads and never opens a pool (test/cli-startup.test.ts counts what
|
|
87
|
+
it loads). Every lease comes from a pool, so a lease always finds it loaded. */
|
|
88
|
+
let timing;
|
|
89
|
+
/** What no one chose: an App (keyed writes) reads as widely as it writes, never fewer, because writes without
|
|
90
|
+
* readers to match only moved the queue to the reads; the database browser keeps four. */
|
|
91
|
+
function defaultReaders(writers) {
|
|
92
|
+
return writeOrder() === 'keyed' ? appReaders(writers) : DEFAULT_READERS;
|
|
93
|
+
}
|
|
94
|
+
/** An App's reader pool where none was chosen: as wide as its writers, at least the balanced size for `cores`
|
|
95
|
+
* (this machine's, unless a deployment check asks for another's). */
|
|
96
|
+
export function appReaders(writers, cores) {
|
|
97
|
+
return Math.min(MAX_READERS, Math.max(balancedPoolSize(cores), writers));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The defaults every manager in this process takes when it names none: the
|
|
101
|
+
* command line's `--read-pool` and `--pooler`. Server-wide on purpose
|
|
102
|
+
* (serverSettings.ts): besides the registry's adapters, tenant sources, the
|
|
103
|
+
* job host and startup checks open pools of their own on the same databases,
|
|
104
|
+
* and a transaction-mode pooler in front of one of them has to be honored by
|
|
105
|
+
* every one; a per-connection switch would reach only some. Called once, at
|
|
106
|
+
* startup; whatever it leaves unsaid returns to the built-in default. The
|
|
107
|
+
* reader count stays unset until a manager is made: the write order, which
|
|
108
|
+
* says whether this is an App, is configured after the flags are read.
|
|
109
|
+
*/
|
|
110
|
+
export function configurePostgresPools(options) {
|
|
111
|
+
const readers = options.readers;
|
|
112
|
+
if (readers !== undefined && !isReaderCount(readers))
|
|
113
|
+
throw new RangeError(`A PostgreSQL read pool holds 1 to ${MAX_READERS} connections; ${String(readers)} is not one of them.`);
|
|
114
|
+
const pooler = options.pooler ?? 'session';
|
|
115
|
+
if (pooler !== 'session' && pooler !== 'transaction')
|
|
116
|
+
throw new RangeError('The pooler is "session" or "transaction".');
|
|
117
|
+
configureServerSettings({ readers, pooler });
|
|
118
|
+
}
|
|
119
|
+
/** The defaults, as this server's settings leave them. */
|
|
120
|
+
export function postgresPoolDefaults() {
|
|
121
|
+
const settings = serverSettings();
|
|
122
|
+
return {
|
|
123
|
+
readers: settings.readers ?? defaultReaders(writerPoolSize()),
|
|
124
|
+
pooler: settings.pooler,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Separate bounded pools keep browsing read-only and transactions on one lease. */
|
|
128
|
+
export class PgConnectionManager {
|
|
129
|
+
connectionString;
|
|
130
|
+
onLoad;
|
|
131
|
+
load;
|
|
132
|
+
reader;
|
|
133
|
+
writer;
|
|
134
|
+
background;
|
|
135
|
+
/** The pools once they exist, for the meters' physical counts. */
|
|
136
|
+
opened = {};
|
|
137
|
+
closed = false;
|
|
138
|
+
/* Server timing's gauges on this manager's pools, taken out at its close: each holds its pool alive. */
|
|
139
|
+
gauges = [];
|
|
140
|
+
closing;
|
|
141
|
+
/** What sits between this process and the server, for every lease of this manager. */
|
|
142
|
+
pooler;
|
|
143
|
+
capMs;
|
|
144
|
+
lockMs;
|
|
145
|
+
queueWaitMs;
|
|
146
|
+
queuePerConnection;
|
|
147
|
+
meters;
|
|
148
|
+
/** The line each person-facing role's checkouts wait in, turn by turn across accounts (poolAdmission.ts). */
|
|
149
|
+
admissions;
|
|
150
|
+
/* One object, so every caller of read() holds the same handle, as when the handle was the pool itself. */
|
|
151
|
+
reads;
|
|
152
|
+
/** This database's replicas, each a manager of its own whose writers are never opened. */
|
|
153
|
+
replicas;
|
|
154
|
+
network;
|
|
155
|
+
pipelineReads;
|
|
156
|
+
timingLabel;
|
|
157
|
+
/** A pool the server refused another connection, held at what it had until the timer lets it ask again. */
|
|
158
|
+
limitHolds = {};
|
|
159
|
+
/** When each pool last said it reached the connection limit: once a spell, not once a checkout. */
|
|
160
|
+
limitSaid = {};
|
|
161
|
+
/** Gives back what this manager told the connection budget it may hold (connectionBudget.ts). */
|
|
162
|
+
releaseBudget;
|
|
163
|
+
constructor(connectionString, onLoad = () => { }, load = loadPg, options = {}) {
|
|
164
|
+
this.connectionString = connectionString;
|
|
165
|
+
this.onLoad = onLoad;
|
|
166
|
+
this.load = load;
|
|
167
|
+
/* Where writes run one at a time a second writer would sit idle: the
|
|
168
|
+
pool is one, whatever the connection asked for. */
|
|
169
|
+
const writers = writeOrder() === 'serial' ? 1 : (options.writers ?? writerPoolSize());
|
|
170
|
+
const readers = options.readers ?? serverSettings().readers ?? defaultReaders(writers);
|
|
171
|
+
if (!isReaderCount(readers))
|
|
172
|
+
throw new RangeError(`A PostgreSQL read pool holds 1 to ${MAX_READERS} connections; ${String(readers)} is not one of them.`);
|
|
173
|
+
if (!isWriterCount(writers))
|
|
174
|
+
throw new RangeError(`A PostgreSQL write pool holds 1 to ${MAX_WRITERS} connections; ${String(writers)} is not one of them.`);
|
|
175
|
+
this.pooler = options.pooler ?? serverSettings().pooler;
|
|
176
|
+
this.network = options.network;
|
|
177
|
+
this.pipelineReads = options.pipelineReads === true;
|
|
178
|
+
this.timingLabel = options.timingLabel ?? 'app';
|
|
179
|
+
this.capMs = options.capMs ?? STATEMENT_TIMEOUT_MS;
|
|
180
|
+
this.queueWaitMs = options.queueWaitMs ?? POOL_QUEUE_WAIT_MS;
|
|
181
|
+
this.queuePerConnection = options.queuePerConnection ?? POOL_QUEUE_PER_CONNECTION;
|
|
182
|
+
this.lockMs = Math.min(options.lockTimeoutMs ?? lockTimeoutMs(), this.capMs);
|
|
183
|
+
this.meters = Object.freeze({
|
|
184
|
+
read: new PoolMeter('read', readers),
|
|
185
|
+
write: new PoolMeter('write', writers),
|
|
186
|
+
background: new PoolMeter('background', 1),
|
|
187
|
+
});
|
|
188
|
+
this.admissions = Object.freeze({
|
|
189
|
+
read: new PoolAdmission('read', readers, readers * this.queuePerConnection),
|
|
190
|
+
write: new PoolAdmission('write', writers, writers * this.queuePerConnection),
|
|
191
|
+
});
|
|
192
|
+
const replicas = options.replicas ?? [];
|
|
193
|
+
if (replicas.length > MAX_REPLICAS)
|
|
194
|
+
throw new RangeError(`A connection names at most ${MAX_REPLICAS} replicas; this one names ${replicas.length}.`);
|
|
195
|
+
/* A manager may open readers, writers and the background writer; a replica's own are readers only. */
|
|
196
|
+
const releases = options.counted === false
|
|
197
|
+
? []
|
|
198
|
+
: [
|
|
199
|
+
holdConnections(connectionString, options.readersOnly ? readers : managerConnections(readers, writers), () => this.read()),
|
|
200
|
+
...replicas.map((url) => holdConnections(url, readers)),
|
|
201
|
+
];
|
|
202
|
+
this.releaseBudget = () => releases.forEach((release) => release());
|
|
203
|
+
/* Sized as this manager's readers are, the same pooler, cap and queue
|
|
204
|
+
bounds: a replica is where this pool's reads go, so it must hold them. */
|
|
205
|
+
if (replicas.length)
|
|
206
|
+
this.replicas = new ReplicaSet(replicas.map((url) => new PgConnectionManager(url, onLoad, load, {
|
|
207
|
+
...options,
|
|
208
|
+
readers,
|
|
209
|
+
writers,
|
|
210
|
+
replicas: undefined,
|
|
211
|
+
counted: false,
|
|
212
|
+
timingLabel: 'app-replica',
|
|
213
|
+
})));
|
|
214
|
+
const run = (client, text, params) => client.query(text, params);
|
|
215
|
+
const statement = (text, params) => {
|
|
216
|
+
const primary = () => this.withClient(false, (client) => run(client, text, params), false);
|
|
217
|
+
return this.replicas
|
|
218
|
+
? this.replicas.read((replica) => replica.withReadClient((client) => run(client, text, params)), primary)
|
|
219
|
+
: primary();
|
|
220
|
+
};
|
|
221
|
+
this.reads = { query: statement };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* A handle for single statements on the reader pool. Each statement leases
|
|
225
|
+
* a client of its own for exactly its length, which is what `pool.query`
|
|
226
|
+
* did — so the checkout is metered, a cancel knows which client to stop,
|
|
227
|
+
* and behind a transaction-mode pooler the statement runs inside a
|
|
228
|
+
* read-only transaction of its own.
|
|
229
|
+
*/
|
|
230
|
+
async read() {
|
|
231
|
+
await this.pool('read');
|
|
232
|
+
return this.reads;
|
|
233
|
+
}
|
|
234
|
+
/** Native snapshot owners opt into callback drain after cancellation. A
|
|
235
|
+
* callback must settle after its driver operations are destroyed; arbitrary
|
|
236
|
+
* never-settling application work cannot offer a bounded drain guarantee. */
|
|
237
|
+
async withReadClient(run, destroyAfter = false, drainCancelled = false) {
|
|
238
|
+
return this.withClient(false, run, destroyAfter, drainCancelled);
|
|
239
|
+
}
|
|
240
|
+
/** Private native snapshot seam: guard observes this exact checkout, not
|
|
241
|
+
* instantaneous remote-server health. Ordinary read/write behavior is unchanged. */
|
|
242
|
+
async withObservedReadClient(run) {
|
|
243
|
+
return this.withClient(false, (client, current) => {
|
|
244
|
+
if (!current)
|
|
245
|
+
throw new Error('Observed PostgreSQL checkout is unavailable.');
|
|
246
|
+
return run(client, current);
|
|
247
|
+
}, false, true, true);
|
|
248
|
+
}
|
|
249
|
+
/** A writer for one transaction: from the writer pool, or inside background work (asBackgroundWork) the background writer. */
|
|
250
|
+
async withWriteClient(run, destroyAfter = false) {
|
|
251
|
+
return this.withClient(true, run, destroyAfter);
|
|
252
|
+
}
|
|
253
|
+
/** The meters of the pools opened so far, readers first, then each replica's readers. Readable after close. */
|
|
254
|
+
stats() {
|
|
255
|
+
const own = ['read', 'write', 'background'].flatMap((role) => {
|
|
256
|
+
const pool = this.opened[role];
|
|
257
|
+
if (!pool)
|
|
258
|
+
return [];
|
|
259
|
+
const total = pool.totalCount;
|
|
260
|
+
return [this.meters[role].stats(this.pooler, typeof total === 'number' ? total : null)];
|
|
261
|
+
});
|
|
262
|
+
const replicas = (this.replicas?.pools ?? []).flatMap((replica, index) => replica.stats().map((stats) => ({
|
|
263
|
+
...stats,
|
|
264
|
+
role: 'replica',
|
|
265
|
+
replica: index + 1,
|
|
266
|
+
})));
|
|
267
|
+
return [...own, ...replicas];
|
|
268
|
+
}
|
|
269
|
+
/** Whether this database names any replica. */
|
|
270
|
+
hasReplicas() {
|
|
271
|
+
return this.replicas !== undefined;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* `work` on each replica in use now, its failures left out: what live sync
|
|
275
|
+
* asks of every healthy replica before it tells readers of a change, so a
|
|
276
|
+
* refetch it sets off reads a replica that has the change (server/live).
|
|
277
|
+
*/
|
|
278
|
+
async onHealthyReplicas(work) {
|
|
279
|
+
const set = this.replicas;
|
|
280
|
+
if (!set)
|
|
281
|
+
return [];
|
|
282
|
+
const answers = await Promise.allSettled(set.pools.map((replica, index) => (set.healthy(index) ? replica.withReadClient(work) : Promise.reject(new Error('passed over')))));
|
|
283
|
+
return answers.flatMap((answer) => (answer.status === 'fulfilled' ? [answer.value] : []));
|
|
284
|
+
}
|
|
285
|
+
/** Each replica: whether it is in use now, and how many reads it answered and handed back to the primary. */
|
|
286
|
+
replicaHealth() {
|
|
287
|
+
const set = this.replicas;
|
|
288
|
+
return set
|
|
289
|
+
? set.pools.map((_, index) => ({
|
|
290
|
+
replica: index + 1,
|
|
291
|
+
healthy: set.healthy(index),
|
|
292
|
+
served: set.served[index],
|
|
293
|
+
fellBack: set.fellBack[index],
|
|
294
|
+
}))
|
|
295
|
+
: [];
|
|
296
|
+
}
|
|
297
|
+
close() {
|
|
298
|
+
this.closed = true;
|
|
299
|
+
this.releaseBudget();
|
|
300
|
+
for (const off of this.gauges.splice(0))
|
|
301
|
+
off();
|
|
302
|
+
for (const timer of Object.values(this.limitHolds))
|
|
303
|
+
clearTimeout(timer);
|
|
304
|
+
this.closing ??= Promise.all([this.endPools(), ...(this.replicas?.pools ?? []).map((replica) => replica.close())]).then(() => { });
|
|
305
|
+
return this.closing;
|
|
306
|
+
}
|
|
307
|
+
/* What pg is told about where to connect: the URL, or for an untrusted
|
|
308
|
+
address every part spelled out so nothing comes from this process. */
|
|
309
|
+
dialOptions() {
|
|
310
|
+
return this.network
|
|
311
|
+
? sealedPgOptions(this.connectionString, this.network)
|
|
312
|
+
: pgConnectionOptions(this.connectionString);
|
|
313
|
+
}
|
|
314
|
+
assertOpen() {
|
|
315
|
+
if (this.closed)
|
|
316
|
+
throw new Error('Postgres connection manager is closed.');
|
|
317
|
+
}
|
|
318
|
+
async pool(role) {
|
|
319
|
+
this.assertOpen();
|
|
320
|
+
const pending = role === 'write'
|
|
321
|
+
? (this.writer ??= this.createPool('write'))
|
|
322
|
+
: role === 'background'
|
|
323
|
+
? (this.background ??= this.createPool('background'))
|
|
324
|
+
: (this.reader ??= this.createPool('read'));
|
|
325
|
+
const pool = await pending;
|
|
326
|
+
this.assertOpen();
|
|
327
|
+
return pool;
|
|
328
|
+
}
|
|
329
|
+
async createPool(role) {
|
|
330
|
+
const pg = await this.load();
|
|
331
|
+
timing ??= await import('./telemetry/serverTiming.js');
|
|
332
|
+
this.assertOpen();
|
|
333
|
+
this.onLoad(pg);
|
|
334
|
+
const module = pg;
|
|
335
|
+
const Pool = module.Pool ?? module.default?.Pool;
|
|
336
|
+
if (!Pool)
|
|
337
|
+
throw new Error('Could not find a Pool export in the "pg" package.');
|
|
338
|
+
const write = role !== 'read';
|
|
339
|
+
const session = this.pooler === 'session';
|
|
340
|
+
const pool = new Pool({
|
|
341
|
+
...this.dialOptions(),
|
|
342
|
+
max: this.meters[role].size,
|
|
343
|
+
connectionTimeoutMillis: 10_000,
|
|
344
|
+
idleTimeoutMillis: 30_000,
|
|
345
|
+
/* Readers only: a writer is opened for a write and may go. The idle
|
|
346
|
+
ones never keep the process alive by themselves, so a one-shot mode
|
|
347
|
+
that forgets to close still exits. */
|
|
348
|
+
...(write
|
|
349
|
+
? {}
|
|
350
|
+
: {
|
|
351
|
+
min: Math.min(WARM_READERS, this.meters[role].size),
|
|
352
|
+
allowExitOnIdle: true,
|
|
353
|
+
}),
|
|
354
|
+
/* A writer sends each statement as it is asked, not after the one
|
|
355
|
+
before it is answered: a transaction that asks several at once
|
|
356
|
+
(pipelined, postgresTransactionHandle.ts) pays one round trip for
|
|
357
|
+
them. One asked at a time is sent and answered exactly as before.
|
|
358
|
+
A reader only where its owner asked (pipelineReads). */
|
|
359
|
+
...(write || this.pipelineReads ? { pipeline: true } : {}),
|
|
360
|
+
/* pg awaits this hook for every physical client before its first
|
|
361
|
+
checkout. Behind a transaction-mode pooler a session setting would
|
|
362
|
+
land on whichever server connection the client holds at that moment
|
|
363
|
+
and stay there for the pooler's next client, so nothing is set; each
|
|
364
|
+
lease sets its own, per transaction. */
|
|
365
|
+
onConnect: async (client) => {
|
|
366
|
+
/* A leased client's 'error' is its own to hear: the pool listens only
|
|
367
|
+
while it is idle, and a backend ended under a lease (a failover, a
|
|
368
|
+
pg_terminate_backend) would otherwise end the process. The query it
|
|
369
|
+
was running rejects with the same failure, which is where it is
|
|
370
|
+
answered. */
|
|
371
|
+
client.on?.('error', () => { });
|
|
372
|
+
if (!session)
|
|
373
|
+
return;
|
|
374
|
+
if (!write)
|
|
375
|
+
await client.query('SET default_transaction_read_only = on');
|
|
376
|
+
/* A writer also stops waiting for a lock after lockMs: a row held by
|
|
377
|
+
another transaction (another pod, a migration's DDL) fails the
|
|
378
|
+
statement fast, with 55P03, rather than holding this connection —
|
|
379
|
+
and the save behind it — for the whole statement cap. One message:
|
|
380
|
+
the settings are two statements and one round trip. */
|
|
381
|
+
await client.query(write
|
|
382
|
+
? `SET statement_timeout = '${this.capMs / 1000}s'; SET lock_timeout = '${this.lockMs}ms'`
|
|
383
|
+
: `SET statement_timeout = '${this.capMs / 1000}s'`);
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
// pg removes failed idle clients itself; an unhandled pool error would crash Node.
|
|
387
|
+
pool.on('error', () => { });
|
|
388
|
+
this.opened[role] = pool;
|
|
389
|
+
/* In use and waiting, for the process entry of server timing (a no-op when it is off). pg-pool's own counters. */
|
|
390
|
+
const counts = pool;
|
|
391
|
+
if (timing.serverTimingOn() && !this.closed)
|
|
392
|
+
this.gauges.push(timing.registerGauge(`pool-${role}-use`, () => (counts.totalCount ?? 0) - (counts.idleCount ?? 0)), timing.registerGauge(`pool-${role}-wait`, () => counts.waitingCount ?? 0));
|
|
393
|
+
/* With server timing on, the round trip from here to this database, once, on a connection of its own: a
|
|
394
|
+
driver without a Client (a test's) is not measured. */
|
|
395
|
+
if (!write)
|
|
396
|
+
void timing.probeRoundTrips(this.timingLabel, () => {
|
|
397
|
+
const found = pg;
|
|
398
|
+
const Client = found.Client ?? found.default?.Client;
|
|
399
|
+
return Client && new Client({ ...this.dialOptions(), connectionTimeoutMillis: 10_000 });
|
|
400
|
+
});
|
|
401
|
+
return pool;
|
|
402
|
+
}
|
|
403
|
+
/* `write` stays a boolean: test/support/tenantDialects.ts intercepts every
|
|
404
|
+
lease here, by that flag, to fault a writer's COMMIT and nothing else. */
|
|
405
|
+
async withClient(write, run, destroyAfter, drainCancelled = false, observeCheckout = false) {
|
|
406
|
+
const role = !write ? 'read' : inBackgroundWork() ? 'background' : 'write';
|
|
407
|
+
/* Only reads are stopped on cancel: a write that has started runs to its
|
|
408
|
+
end, so its outcome is known rather than guessed. */
|
|
409
|
+
const signal = write ? undefined : cancellation();
|
|
410
|
+
throwIfCancelled(signal);
|
|
411
|
+
const pool = await this.pool(role);
|
|
412
|
+
/* A write that finds every writer busy says so on the request's trace
|
|
413
|
+
(the dev observer): the wait for a connection, beside the wait in its
|
|
414
|
+
lane (writeLanes.ts) and the statements that follow. */
|
|
415
|
+
const waiting = write && this.meters[role].full()
|
|
416
|
+
? startSpan('writer wait', {
|
|
417
|
+
childOnly: true,
|
|
418
|
+
attributes: { 'db.client.connection.pool.name': role },
|
|
419
|
+
})
|
|
420
|
+
: undefined;
|
|
421
|
+
const meter = this.meters[role];
|
|
422
|
+
/* The background writer is one connection that background work waits for by design, for as long as the driver's
|
|
423
|
+
connect timeout rather than the queue-wait budget a person's read or write has. Its line is bounded all the
|
|
424
|
+
same: past the queue's length, or past that timeout, the work is answered busy before anything was leased, so
|
|
425
|
+
a job's command, a delivery or a workflow step is asked again (temporarily_unavailable, a retry) instead of
|
|
426
|
+
a burst of them failing together as an unavailable store. */
|
|
427
|
+
const bounded = role !== 'background';
|
|
428
|
+
if (!bounded && meter.waiting() >= meter.size * this.queuePerConnection) {
|
|
429
|
+
meter.refuse();
|
|
430
|
+
throw new PoolBusy('write', 'queue_full');
|
|
431
|
+
}
|
|
432
|
+
const checkout = meter.begin();
|
|
433
|
+
const had = timing?.poolWait(`${this.timingLabel}-${role}`);
|
|
434
|
+
let client;
|
|
435
|
+
/* A person's checkout waits its turn in the manager's own line, and only
|
|
436
|
+
then asks the driver, for a connection that is its: the line and the
|
|
437
|
+
connection share the one queue-wait budget. */
|
|
438
|
+
let place;
|
|
439
|
+
try {
|
|
440
|
+
if (bounded) {
|
|
441
|
+
const deadline = performance.now() + this.queueWaitMs;
|
|
442
|
+
place = await this.admissions[role].enter(this.queueWaitMs);
|
|
443
|
+
client = await this.connectWithin(pool, role, Math.max(1, deadline - performance.now()));
|
|
444
|
+
}
|
|
445
|
+
else
|
|
446
|
+
client = await this.connectHeld(pool, role);
|
|
447
|
+
had?.();
|
|
448
|
+
}
|
|
449
|
+
catch (error) {
|
|
450
|
+
had?.();
|
|
451
|
+
place?.();
|
|
452
|
+
if (error instanceof PoolBusy) {
|
|
453
|
+
checkout.failed(true);
|
|
454
|
+
waiting?.fail('pool_busy');
|
|
455
|
+
waiting?.end();
|
|
456
|
+
throw error;
|
|
457
|
+
}
|
|
458
|
+
const timedOut = ACQUISITION_TIMEOUT.test(error instanceof Error ? error.message : '');
|
|
459
|
+
checkout.failed(timedOut);
|
|
460
|
+
waiting?.fail('pool_timeout');
|
|
461
|
+
waiting?.end();
|
|
462
|
+
if (!bounded && timedOut)
|
|
463
|
+
throw new PoolBusy('write', 'queue_timeout', error);
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
466
|
+
waiting?.end();
|
|
467
|
+
timing?.timeClient(client, this.timingLabel);
|
|
468
|
+
delayFlights(client);
|
|
469
|
+
const giveBack = checkout.acquired();
|
|
470
|
+
const scoped = this.pooler === 'transaction'
|
|
471
|
+
? transactionScoped(client, {
|
|
472
|
+
role: write ? 'write' : 'read',
|
|
473
|
+
capMs: this.capMs,
|
|
474
|
+
untrusted: destroyAfter,
|
|
475
|
+
...(write ? { lockTimeoutMs: this.lockMs } : {}),
|
|
476
|
+
/* A writer's client pipelines (below): a statement it sends outside
|
|
477
|
+
a transaction leaves with its BEGIN and COMMIT, in one round trip. */
|
|
478
|
+
pipelines: client.pipeline === true,
|
|
479
|
+
})
|
|
480
|
+
: undefined;
|
|
481
|
+
let destroy = destroyAfter;
|
|
482
|
+
/* A cancelled client never goes back to the pool: the cancel travels on
|
|
483
|
+
another connection and can land late, and it must not land on a
|
|
484
|
+
statement a later lease of the same backend has started. */
|
|
485
|
+
let cancelled = false;
|
|
486
|
+
let released = false;
|
|
487
|
+
let invalidateHealth = () => { }, removeHealthListeners = () => { };
|
|
488
|
+
const release = (destroyed) => {
|
|
489
|
+
if (released)
|
|
490
|
+
return;
|
|
491
|
+
released = true;
|
|
492
|
+
invalidateHealth();
|
|
493
|
+
client.release(destroyed);
|
|
494
|
+
};
|
|
495
|
+
/* Behind a transaction-mode pooler the backend id a client was told is
|
|
496
|
+
the pooler's, not the server's, so a pg_cancel_backend by it would
|
|
497
|
+
stop whatever unrelated statement holds that id on the server. The
|
|
498
|
+
ordinary caller stops waiting at once instead; snapshot owners destroy
|
|
499
|
+
the client first and then drain their callback. Server work remains
|
|
500
|
+
bounded by the transaction's cap, not by local callback completion. */
|
|
501
|
+
let stop;
|
|
502
|
+
const stopped = scoped && signal && !drainCancelled
|
|
503
|
+
? new Promise((_, reject) => {
|
|
504
|
+
stop = reject;
|
|
505
|
+
})
|
|
506
|
+
: undefined;
|
|
507
|
+
void stopped?.catch(() => { });
|
|
508
|
+
const disarm = onCancel(signal, () => {
|
|
509
|
+
cancelled = true;
|
|
510
|
+
invalidateHealth();
|
|
511
|
+
if (scoped) {
|
|
512
|
+
const reason = new StatementStopped('cancelled');
|
|
513
|
+
if (drainCancelled) {
|
|
514
|
+
// Invalidate continuations first, then break active driver I/O before
|
|
515
|
+
// awaiting callback/finally drain. This drains local promises, not
|
|
516
|
+
// server locks: server work may persist until its configured cap.
|
|
517
|
+
// Never cancel a pooler's backend ID.
|
|
518
|
+
scoped.close(reason);
|
|
519
|
+
// A release exception must not escape the AbortSignal listener. The
|
|
520
|
+
// operation remains cancelled, and still waits for its callback.
|
|
521
|
+
try {
|
|
522
|
+
release(true);
|
|
523
|
+
}
|
|
524
|
+
catch {
|
|
525
|
+
/* No retry of a possibly released client. */
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else
|
|
529
|
+
stop?.(reason);
|
|
530
|
+
}
|
|
531
|
+
else
|
|
532
|
+
void this.cancel(client.processID);
|
|
533
|
+
});
|
|
534
|
+
try {
|
|
535
|
+
this.assertOpen();
|
|
536
|
+
throwIfCancelled(signal);
|
|
537
|
+
let borrowed = scoped ?? client;
|
|
538
|
+
let health;
|
|
539
|
+
if (observeCheckout) {
|
|
540
|
+
if (typeof client.on !== 'function' || typeof client.off !== 'function')
|
|
541
|
+
throw new Error('Observed PostgreSQL checkout is unavailable.');
|
|
542
|
+
let healthy = true;
|
|
543
|
+
const on = client.on.bind(client), off = client.off.bind(client);
|
|
544
|
+
invalidateHealth = () => {
|
|
545
|
+
healthy = false;
|
|
546
|
+
};
|
|
547
|
+
removeHealthListeners = () => {
|
|
548
|
+
off('error', invalidateHealth);
|
|
549
|
+
off('end', invalidateHealth);
|
|
550
|
+
};
|
|
551
|
+
on('error', invalidateHealth);
|
|
552
|
+
on('end', invalidateHealth);
|
|
553
|
+
health = () => {
|
|
554
|
+
this.assertOpen();
|
|
555
|
+
throwIfCancelled(signal);
|
|
556
|
+
if (!healthy || released)
|
|
557
|
+
throw new Error('Observed PostgreSQL checkout is unavailable.');
|
|
558
|
+
};
|
|
559
|
+
const current = health, target = borrowed;
|
|
560
|
+
async function query(text, params) {
|
|
561
|
+
try {
|
|
562
|
+
current();
|
|
563
|
+
const result = typeof text === 'string' ? await target.query(text, params) : await target.query(text);
|
|
564
|
+
current();
|
|
565
|
+
return result;
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
invalidateHealth();
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
borrowed = Object.freeze({ query });
|
|
573
|
+
}
|
|
574
|
+
/* A write's own reads — the pre-flight count, a reread — are of the
|
|
575
|
+
primary it writes. */
|
|
576
|
+
const lent = borrowed;
|
|
577
|
+
/* A lease that runs statements it does not control (destroyAfter: the
|
|
578
|
+
SQL tool's) brackets each in a savepoint it awaits first
|
|
579
|
+
(postgresPooler.ts), so one sent behind another would overtake it:
|
|
580
|
+
that lease asks one at a time. */
|
|
581
|
+
if (client.pipeline === true && !destroyAfter)
|
|
582
|
+
pipelining.add(lent);
|
|
583
|
+
const running = write ? primaryReads(() => run(lent, health)) : run(borrowed, health);
|
|
584
|
+
if (stopped)
|
|
585
|
+
void running.catch(() => { });
|
|
586
|
+
const out = await (stopped ? Promise.race([running, stopped]) : running);
|
|
587
|
+
health?.();
|
|
588
|
+
await scoped?.settle();
|
|
589
|
+
health?.();
|
|
590
|
+
if (drainCancelled)
|
|
591
|
+
throwIfCancelled(signal);
|
|
592
|
+
return out;
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
destroy = true;
|
|
596
|
+
if (scoped && !cancelled)
|
|
597
|
+
await scoped.settle().catch(() => { });
|
|
598
|
+
if (cancelled)
|
|
599
|
+
throw error instanceof StatementStopped ? error : new StatementStopped('cancelled', undefined, { cause: error });
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
finally {
|
|
603
|
+
/* The person who wrote reads the primary for a while after, so the
|
|
604
|
+
list they go back to shows what they saved (replicas.ts). Noted
|
|
605
|
+
however the write ended: a failed one may still have committed. */
|
|
606
|
+
if (write)
|
|
607
|
+
noteWrite();
|
|
608
|
+
disarm();
|
|
609
|
+
try {
|
|
610
|
+
release(destroy || cancelled);
|
|
611
|
+
}
|
|
612
|
+
finally {
|
|
613
|
+
try {
|
|
614
|
+
removeHealthListeners();
|
|
615
|
+
}
|
|
616
|
+
finally {
|
|
617
|
+
try {
|
|
618
|
+
giveBack();
|
|
619
|
+
}
|
|
620
|
+
finally {
|
|
621
|
+
place?.();
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* A connection, or PoolBusy once what is left of the queue-wait budget, `waitMs`, is spent. The driver
|
|
629
|
+
* cannot withdraw a request it has queued, so one that arrives late is given
|
|
630
|
+
* straight back to the pool rather than leaked.
|
|
631
|
+
*/
|
|
632
|
+
connectWithin(pool, role, waitMs) {
|
|
633
|
+
const connecting = this.connectHeld(pool, role);
|
|
634
|
+
return new Promise((resolve, reject) => {
|
|
635
|
+
let over = false;
|
|
636
|
+
const timer = setTimeout(() => {
|
|
637
|
+
over = true;
|
|
638
|
+
reject(new PoolBusy(role, 'queue_timeout'));
|
|
639
|
+
}, waitMs);
|
|
640
|
+
connecting.then((client) => {
|
|
641
|
+
clearTimeout(timer);
|
|
642
|
+
if (over)
|
|
643
|
+
client.release();
|
|
644
|
+
else
|
|
645
|
+
resolve(client);
|
|
646
|
+
}, (error) => {
|
|
647
|
+
clearTimeout(timer);
|
|
648
|
+
if (!over)
|
|
649
|
+
reject(error);
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* `pool.connect()`, within the connections the server allows. Refused
|
|
655
|
+
* another one (53300), the pool is held at the connections it has, so this
|
|
656
|
+
* checkout and the next wait for one of them as they would behind a full
|
|
657
|
+
* pool, and it asks for more again after LIMIT_HOLD_MS; holding none, the
|
|
658
|
+
* checkout is refused as busy. A refusal at open leased nothing, so nothing
|
|
659
|
+
* on any connection moved: a tenant writer stays open on it
|
|
660
|
+
* (tenantWriteSource.ts). It used to rise as a database fault, and a
|
|
661
|
+
* tenant writer closed for it and reopened into the same refusal, again and
|
|
662
|
+
* again, with two commands in three failing at three users while its one
|
|
663
|
+
* connection sat idle (a field report: pools sized past the role's limit).
|
|
664
|
+
*/
|
|
665
|
+
async connectHeld(pool, role) {
|
|
666
|
+
for (let attempt = 1;; attempt += 1) {
|
|
667
|
+
try {
|
|
668
|
+
return await pool.connect();
|
|
669
|
+
}
|
|
670
|
+
catch (error) {
|
|
671
|
+
if (!atConnectionLimit(error))
|
|
672
|
+
throw error;
|
|
673
|
+
const held = this.holdAtLimit(pool, role, error);
|
|
674
|
+
if (!held || this.closed || attempt >= LIMIT_ATTEMPTS)
|
|
675
|
+
throw new PoolBusy(role === 'read' ? 'read' : 'write', 'connection_limit', error);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
/** Holds `pool` at the connections it has (pg-pool reads `options.max` at each checkout), says so once a spell, and returns how many. */
|
|
680
|
+
holdAtLimit(pool, role, refusal) {
|
|
681
|
+
const found = pool;
|
|
682
|
+
const held = typeof found.totalCount === 'number' ? found.totalCount : 0;
|
|
683
|
+
const size = this.meters[role].size;
|
|
684
|
+
const options = found.options;
|
|
685
|
+
if (held > 0 && options && typeof options.max === 'number') {
|
|
686
|
+
const most = Math.min(options.max, held);
|
|
687
|
+
options.max = most;
|
|
688
|
+
const line = role === 'background' ? undefined : this.admissions[role];
|
|
689
|
+
line?.resize(most);
|
|
690
|
+
clearTimeout(this.limitHolds[role]);
|
|
691
|
+
const timer = setTimeout(() => {
|
|
692
|
+
delete this.limitHolds[role];
|
|
693
|
+
options.max = size;
|
|
694
|
+
line?.resize(size);
|
|
695
|
+
}, LIMIT_HOLD_MS);
|
|
696
|
+
timer.unref?.();
|
|
697
|
+
this.limitHolds[role] = timer;
|
|
698
|
+
}
|
|
699
|
+
const now = Date.now();
|
|
700
|
+
if (now - (this.limitSaid[role] ?? -Infinity) >= LIMIT_HOLD_MS) {
|
|
701
|
+
this.limitSaid[role] = now;
|
|
702
|
+
const why = refusal instanceof Error ? refusal.message : 'refused';
|
|
703
|
+
console.error(`mistri: connection limit reached: the ${this.timingLabel} ${role} pool holds ${held} of ${size} connections; the database refused another (${why}). ` +
|
|
704
|
+
(held
|
|
705
|
+
? `It goes on with ${held} and asks for more in ${LIMIT_HOLD_MS / 1000} s. `
|
|
706
|
+
: 'Its checkouts are answered busy (503, try again) until one opens. ') +
|
|
707
|
+
"This process's pools ask for more connections than the database allows: lower the pools (--read-pool, --write-pool, the auth pool) or raise the limit.");
|
|
708
|
+
}
|
|
709
|
+
return held;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Stop what backend `pid` is running, with pg_cancel_backend from a
|
|
713
|
+
* connection of its own: the pool's may all be busy, one of them with the
|
|
714
|
+
* statement being stopped. Best effort — a cancel that cannot connect
|
|
715
|
+
* leaves the statement to its cap.
|
|
716
|
+
*/
|
|
717
|
+
async cancel(pid) {
|
|
718
|
+
if (!pid)
|
|
719
|
+
return;
|
|
720
|
+
try {
|
|
721
|
+
const pg = (await this.load());
|
|
722
|
+
const Client = pg.Client ?? pg.default?.Client;
|
|
723
|
+
if (!Client)
|
|
724
|
+
return;
|
|
725
|
+
const client = new Client({
|
|
726
|
+
...this.dialOptions(),
|
|
727
|
+
connectionTimeoutMillis: 10_000,
|
|
728
|
+
});
|
|
729
|
+
client.on('error', () => { });
|
|
730
|
+
await client.connect();
|
|
731
|
+
try {
|
|
732
|
+
await client.query('SELECT pg_cancel_backend($1)', [pid]);
|
|
733
|
+
}
|
|
734
|
+
finally {
|
|
735
|
+
await client.end().catch(() => { });
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
catch {
|
|
739
|
+
/* The statement's cap still stops it. */
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async endPools() {
|
|
743
|
+
const opened = await Promise.allSettled([this.reader, this.writer, this.background]);
|
|
744
|
+
const ended = await Promise.allSettled(opened.map((result) => (result.status === 'fulfilled' ? result.value?.end() : undefined)));
|
|
745
|
+
const failed = ended.find((result) => result.status === 'rejected');
|
|
746
|
+
if (failed?.status === 'rejected')
|
|
747
|
+
throw failed.reason;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/** The reader and writer counts a manager takes for `shape`: its own, else the process's (`--read-pool`, `--write-pool`) or the defaults. */
|
|
751
|
+
export function poolSizes(shape = {}) {
|
|
752
|
+
const writers = writeOrder() === 'serial' ? 1 : (shape.writers ?? writerPoolSize());
|
|
753
|
+
return { writers, readers: shape.readers ?? serverSettings().readers ?? defaultReaders(writers) };
|
|
754
|
+
}
|
|
755
|
+
/* The managers holders share, by database URL and the settings that make two managers alike (sharedPgConnections). */
|
|
756
|
+
const sharedManagers = new Map();
|
|
757
|
+
/* A load hook told apart by identity in the key: two holders share only where they load pg alike. */
|
|
758
|
+
const loadHooks = new WeakMap();
|
|
759
|
+
let hooks = 0;
|
|
760
|
+
const hookId = (hook) => {
|
|
761
|
+
let id = loadHooks.get(hook);
|
|
762
|
+
if (id === undefined)
|
|
763
|
+
loadHooks.set(hook, (id = ++hooks));
|
|
764
|
+
return id;
|
|
765
|
+
};
|
|
766
|
+
/**
|
|
767
|
+
* The manager every holder of `url` with this `shape` shares, instead of one
|
|
768
|
+
* each. Every pool sized for its process and blind to its neighbors was how a
|
|
769
|
+
* tenant App came to hold `3R + 6W + 5` connections on one database — its
|
|
770
|
+
* reader, discovery, writer, administration and support each with a manager of
|
|
771
|
+
* their own (docs/postgresql.md) — while each sat mostly idle: a field
|
|
772
|
+
* deployment whose role allowed eighteen ran fixed four-and-four pools and
|
|
773
|
+
* still could not fit. Shared, the readers and writers are one pool for all of
|
|
774
|
+
* them, sized once, and counted once (connectionBudget.ts).
|
|
775
|
+
*
|
|
776
|
+
* What sharing leaves alone: each holder keeps its own write lanes
|
|
777
|
+
* (writeLanes.ts), so per-record order is what it was; and a manager is shared
|
|
778
|
+
* only with holders that would have built the same one — the URL, the sizes
|
|
779
|
+
* after the process defaults, the pooler, the lock wait and the load hook all
|
|
780
|
+
* in the key — so a handler mounted with settings of its own never borrows
|
|
781
|
+
* another's pools.
|
|
782
|
+
*
|
|
783
|
+
* Each call is one holder. The handle is the manager behind a proxy whose
|
|
784
|
+
* `close` lets that holder go — once, however often it is called — and the
|
|
785
|
+
* pools close with the last holder; everything else is the manager's own, so
|
|
786
|
+
* the handle passes wherever a manager does (`instanceof` too).
|
|
787
|
+
*/
|
|
788
|
+
export function sharedPgConnections(url, shape = {}, onLoad) {
|
|
789
|
+
const { readers, writers } = poolSizes(shape);
|
|
790
|
+
const pipelineReads = shape.pipelineReads === true;
|
|
791
|
+
const readersOnly = shape.readersOnly === true;
|
|
792
|
+
const key = JSON.stringify([
|
|
793
|
+
url,
|
|
794
|
+
readers,
|
|
795
|
+
writers,
|
|
796
|
+
pipelineReads,
|
|
797
|
+
readersOnly,
|
|
798
|
+
serverSettings().pooler,
|
|
799
|
+
lockTimeoutMs(),
|
|
800
|
+
onLoad ? hookId(onLoad) : 0,
|
|
801
|
+
]);
|
|
802
|
+
let entry = sharedManagers.get(key);
|
|
803
|
+
if (!entry) {
|
|
804
|
+
const manager = new PgConnectionManager(url, onLoad, undefined, { readers, writers, pipelineReads, readersOnly });
|
|
805
|
+
entry = { manager, holders: 0 };
|
|
806
|
+
sharedManagers.set(key, entry);
|
|
807
|
+
}
|
|
808
|
+
const held = entry;
|
|
809
|
+
held.holders += 1;
|
|
810
|
+
let released = false;
|
|
811
|
+
const close = () => {
|
|
812
|
+
if (released)
|
|
813
|
+
return Promise.resolve();
|
|
814
|
+
released = true;
|
|
815
|
+
held.holders -= 1;
|
|
816
|
+
if (held.holders > 0)
|
|
817
|
+
return Promise.resolve();
|
|
818
|
+
if (sharedManagers.get(key) === held)
|
|
819
|
+
sharedManagers.delete(key);
|
|
820
|
+
return held.manager.close();
|
|
821
|
+
};
|
|
822
|
+
/* Methods are bound once per handle: a store asks for withWriteClient on every statement. Kept by the function
|
|
823
|
+
found, not its name, so a method replaced on the class (a test's mock) is the one called. */
|
|
824
|
+
const bound = new WeakMap();
|
|
825
|
+
return new Proxy(held.manager, {
|
|
826
|
+
get(target, property) {
|
|
827
|
+
if (property === 'close')
|
|
828
|
+
return close;
|
|
829
|
+
const value = Reflect.get(target, property, target);
|
|
830
|
+
if (typeof value !== 'function')
|
|
831
|
+
return value;
|
|
832
|
+
let method = bound.get(value);
|
|
833
|
+
if (method === undefined)
|
|
834
|
+
bound.set(value, (method = value.bind(target)));
|
|
835
|
+
return method;
|
|
836
|
+
},
|
|
837
|
+
});
|
|
838
|
+
}
|