@dotdo/postgres 0.1.0
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/README.md +868 -0
- package/dist/cdc/change-stream.d.ts +44 -0
- package/dist/cdc/change-stream.d.ts.map +1 -0
- package/dist/cdc/change-stream.js +95 -0
- package/dist/cdc/change-stream.js.map +1 -0
- package/dist/cdc/filter.d.ts +58 -0
- package/dist/cdc/filter.d.ts.map +1 -0
- package/dist/cdc/filter.js +520 -0
- package/dist/cdc/filter.js.map +1 -0
- package/dist/cdc/index.d.ts +47 -0
- package/dist/cdc/index.d.ts.map +1 -0
- package/dist/cdc/index.js +50 -0
- package/dist/cdc/index.js.map +1 -0
- package/dist/cdc/resume-token.d.ts +60 -0
- package/dist/cdc/resume-token.d.ts.map +1 -0
- package/dist/cdc/resume-token.js +228 -0
- package/dist/cdc/resume-token.js.map +1 -0
- package/dist/cdc/transport/index.d.ts +7 -0
- package/dist/cdc/transport/index.d.ts.map +1 -0
- package/dist/cdc/transport/index.js +7 -0
- package/dist/cdc/transport/index.js.map +1 -0
- package/dist/cdc/transport/sse.d.ts +120 -0
- package/dist/cdc/transport/sse.d.ts.map +1 -0
- package/dist/cdc/transport/sse.js +590 -0
- package/dist/cdc/transport/sse.js.map +1 -0
- package/dist/cdc/transport/websocket.d.ts +130 -0
- package/dist/cdc/transport/websocket.d.ts.map +1 -0
- package/dist/cdc/transport/websocket.js +688 -0
- package/dist/cdc/transport/websocket.js.map +1 -0
- package/dist/cdc/types.d.ts +306 -0
- package/dist/cdc/types.d.ts.map +1 -0
- package/dist/cdc/types.js +8 -0
- package/dist/cdc/types.js.map +1 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +25 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/memory.d.ts +139 -0
- package/dist/config/memory.d.ts.map +1 -0
- package/dist/config/memory.js +157 -0
- package/dist/config/memory.js.map +1 -0
- package/dist/config/storage.d.ts +157 -0
- package/dist/config/storage.d.ts.map +1 -0
- package/dist/config/storage.js +178 -0
- package/dist/config/storage.js.map +1 -0
- package/dist/config/streaming.d.ts +117 -0
- package/dist/config/streaming.d.ts.map +1 -0
- package/dist/config/streaming.js +132 -0
- package/dist/config/streaming.js.map +1 -0
- package/dist/config/timeouts.d.ts +168 -0
- package/dist/config/timeouts.d.ts.map +1 -0
- package/dist/config/timeouts.js +192 -0
- package/dist/config/timeouts.js.map +1 -0
- package/dist/extensions/config.d.ts +89 -0
- package/dist/extensions/config.d.ts.map +1 -0
- package/dist/extensions/config.js +216 -0
- package/dist/extensions/config.js.map +1 -0
- package/dist/extensions/geo.d.ts +452 -0
- package/dist/extensions/geo.d.ts.map +1 -0
- package/dist/extensions/geo.js +583 -0
- package/dist/extensions/geo.js.map +1 -0
- package/dist/extensions/index.d.ts +167 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +99 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/loader.d.ts +226 -0
- package/dist/extensions/loader.d.ts.map +1 -0
- package/dist/extensions/loader.js +456 -0
- package/dist/extensions/loader.js.map +1 -0
- package/dist/extensions/pgmq-lite.d.ts +330 -0
- package/dist/extensions/pgmq-lite.d.ts.map +1 -0
- package/dist/extensions/pgmq-lite.js +648 -0
- package/dist/extensions/pgmq-lite.js.map +1 -0
- package/dist/extensions/plugins.d.ts +260 -0
- package/dist/extensions/plugins.d.ts.map +1 -0
- package/dist/extensions/plugins.js +535 -0
- package/dist/extensions/plugins.js.map +1 -0
- package/dist/extensions/registry.d.ts +93 -0
- package/dist/extensions/registry.d.ts.map +1 -0
- package/dist/extensions/registry.js +182 -0
- package/dist/extensions/registry.js.map +1 -0
- package/dist/extensions/vector.d.ts +106 -0
- package/dist/extensions/vector.d.ts.map +1 -0
- package/dist/extensions/vector.js +129 -0
- package/dist/extensions/vector.js.map +1 -0
- package/dist/iceberg/analytics.d.ts +279 -0
- package/dist/iceberg/analytics.d.ts.map +1 -0
- package/dist/iceberg/analytics.js +448 -0
- package/dist/iceberg/analytics.js.map +1 -0
- package/dist/iceberg/catalog-api.d.ts +39 -0
- package/dist/iceberg/catalog-api.d.ts.map +1 -0
- package/dist/iceberg/catalog-api.js +388 -0
- package/dist/iceberg/catalog-api.js.map +1 -0
- package/dist/iceberg/catalog.d.ts +401 -0
- package/dist/iceberg/catalog.d.ts.map +1 -0
- package/dist/iceberg/catalog.js +677 -0
- package/dist/iceberg/catalog.js.map +1 -0
- package/dist/iceberg/duckdb-wasm.d.ts +447 -0
- package/dist/iceberg/duckdb-wasm.d.ts.map +1 -0
- package/dist/iceberg/duckdb-wasm.js +600 -0
- package/dist/iceberg/duckdb-wasm.js.map +1 -0
- package/dist/iceberg/index.d.ts +92 -0
- package/dist/iceberg/index.d.ts.map +1 -0
- package/dist/iceberg/index.js +119 -0
- package/dist/iceberg/index.js.map +1 -0
- package/dist/iceberg/metadata.d.ts +214 -0
- package/dist/iceberg/metadata.d.ts.map +1 -0
- package/dist/iceberg/metadata.js +535 -0
- package/dist/iceberg/metadata.js.map +1 -0
- package/dist/iceberg/optimizer.d.ts +296 -0
- package/dist/iceberg/optimizer.d.ts.map +1 -0
- package/dist/iceberg/optimizer.js +889 -0
- package/dist/iceberg/optimizer.js.map +1 -0
- package/dist/iceberg/parquet.d.ts +447 -0
- package/dist/iceberg/parquet.d.ts.map +1 -0
- package/dist/iceberg/parquet.js +1225 -0
- package/dist/iceberg/parquet.js.map +1 -0
- package/dist/iceberg/r2-organization.d.ts +422 -0
- package/dist/iceberg/r2-organization.d.ts.map +1 -0
- package/dist/iceberg/r2-organization.js +672 -0
- package/dist/iceberg/r2-organization.js.map +1 -0
- package/dist/iceberg/scheduler-do-example.d.ts +158 -0
- package/dist/iceberg/scheduler-do-example.d.ts.map +1 -0
- package/dist/iceberg/scheduler-do-example.js +261 -0
- package/dist/iceberg/scheduler-do-example.js.map +1 -0
- package/dist/iceberg/scheduler.d.ts +434 -0
- package/dist/iceberg/scheduler.d.ts.map +1 -0
- package/dist/iceberg/scheduler.js +818 -0
- package/dist/iceberg/scheduler.js.map +1 -0
- package/dist/iceberg/schema.d.ts +149 -0
- package/dist/iceberg/schema.d.ts.map +1 -0
- package/dist/iceberg/schema.js +525 -0
- package/dist/iceberg/schema.js.map +1 -0
- package/dist/iceberg/snapshot-manager.d.ts +406 -0
- package/dist/iceberg/snapshot-manager.d.ts.map +1 -0
- package/dist/iceberg/snapshot-manager.js +934 -0
- package/dist/iceberg/snapshot-manager.js.map +1 -0
- package/dist/iceberg/sql-router.d.ts +194 -0
- package/dist/iceberg/sql-router.d.ts.map +1 -0
- package/dist/iceberg/sql-router.js +180 -0
- package/dist/iceberg/sql-router.js.map +1 -0
- package/dist/iceberg/test-fixtures.d.ts +151 -0
- package/dist/iceberg/test-fixtures.d.ts.map +1 -0
- package/dist/iceberg/test-fixtures.js +446 -0
- package/dist/iceberg/test-fixtures.js.map +1 -0
- package/dist/iceberg/time-travel-api.d.ts +102 -0
- package/dist/iceberg/time-travel-api.d.ts.map +1 -0
- package/dist/iceberg/time-travel-api.js +437 -0
- package/dist/iceberg/time-travel-api.js.map +1 -0
- package/dist/iceberg/time-travel.d.ts +293 -0
- package/dist/iceberg/time-travel.d.ts.map +1 -0
- package/dist/iceberg/time-travel.js +689 -0
- package/dist/iceberg/time-travel.js.map +1 -0
- package/dist/iceberg/transformer.d.ts +356 -0
- package/dist/iceberg/transformer.d.ts.map +1 -0
- package/dist/iceberg/transformer.js +770 -0
- package/dist/iceberg/transformer.js.map +1 -0
- package/dist/iceberg/types.d.ts +318 -0
- package/dist/iceberg/types.d.ts.map +1 -0
- package/dist/iceberg/types.js +9 -0
- package/dist/iceberg/types.js.map +1 -0
- package/dist/iceberg/writer.d.ts +144 -0
- package/dist/iceberg/writer.d.ts.map +1 -0
- package/dist/iceberg/writer.js +452 -0
- package/dist/iceberg/writer.js.map +1 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/lineage/index.d.ts +11 -0
- package/dist/lineage/index.d.ts.map +1 -0
- package/dist/lineage/index.js +11 -0
- package/dist/lineage/index.js.map +1 -0
- package/dist/lineage/integration.d.ts +134 -0
- package/dist/lineage/integration.d.ts.map +1 -0
- package/dist/lineage/integration.js +258 -0
- package/dist/lineage/integration.js.map +1 -0
- package/dist/lineage/tracker.d.ts +189 -0
- package/dist/lineage/tracker.d.ts.map +1 -0
- package/dist/lineage/tracker.js +1352 -0
- package/dist/lineage/tracker.js.map +1 -0
- package/dist/lineage/types.d.ts +318 -0
- package/dist/lineage/types.d.ts.map +1 -0
- package/dist/lineage/types.js +9 -0
- package/dist/lineage/types.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +16 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +397 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +507 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/migration-tooling/external-migration.d.ts +601 -0
- package/dist/migration-tooling/external-migration.d.ts.map +1 -0
- package/dist/migration-tooling/external-migration.js +1612 -0
- package/dist/migration-tooling/external-migration.js.map +1 -0
- package/dist/migration-tooling/index.d.ts +19 -0
- package/dist/migration-tooling/index.d.ts.map +1 -0
- package/dist/migration-tooling/index.js +19 -0
- package/dist/migration-tooling/index.js.map +1 -0
- package/dist/migrations/auto-migrator.d.ts +289 -0
- package/dist/migrations/auto-migrator.d.ts.map +1 -0
- package/dist/migrations/auto-migrator.js +396 -0
- package/dist/migrations/auto-migrator.js.map +1 -0
- package/dist/migrations/bulk-orchestrator.d.ts +403 -0
- package/dist/migrations/bulk-orchestrator.d.ts.map +1 -0
- package/dist/migrations/bulk-orchestrator.js +646 -0
- package/dist/migrations/bulk-orchestrator.js.map +1 -0
- package/dist/migrations/compatibility.d.ts +216 -0
- package/dist/migrations/compatibility.d.ts.map +1 -0
- package/dist/migrations/compatibility.js +651 -0
- package/dist/migrations/compatibility.js.map +1 -0
- package/dist/migrations/do-migrations.d.ts +101 -0
- package/dist/migrations/do-migrations.d.ts.map +1 -0
- package/dist/migrations/do-migrations.js +1060 -0
- package/dist/migrations/do-migrations.js.map +1 -0
- package/dist/migrations/do-migrations.types.d.ts +550 -0
- package/dist/migrations/do-migrations.types.d.ts.map +1 -0
- package/dist/migrations/do-migrations.types.js +15 -0
- package/dist/migrations/do-migrations.types.js.map +1 -0
- package/dist/migrations/drizzle-compat.d.ts +163 -0
- package/dist/migrations/drizzle-compat.d.ts.map +1 -0
- package/dist/migrations/drizzle-compat.js +273 -0
- package/dist/migrations/drizzle-compat.js.map +1 -0
- package/dist/migrations/index.d.ts +109 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +127 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/migration-api.d.ts +161 -0
- package/dist/migrations/migration-api.d.ts.map +1 -0
- package/dist/migrations/migration-api.js +499 -0
- package/dist/migrations/migration-api.js.map +1 -0
- package/dist/migrations/progress-tracker-do.d.ts +195 -0
- package/dist/migrations/progress-tracker-do.d.ts.map +1 -0
- package/dist/migrations/progress-tracker-do.js +339 -0
- package/dist/migrations/progress-tracker-do.js.map +1 -0
- package/dist/migrations/progress-tracker-kv.d.ts +103 -0
- package/dist/migrations/progress-tracker-kv.d.ts.map +1 -0
- package/dist/migrations/progress-tracker-kv.js +231 -0
- package/dist/migrations/progress-tracker-kv.js.map +1 -0
- package/dist/migrations/progress-tracker.d.ts +320 -0
- package/dist/migrations/progress-tracker.d.ts.map +1 -0
- package/dist/migrations/progress-tracker.js +443 -0
- package/dist/migrations/progress-tracker.js.map +1 -0
- package/dist/migrations/registry.d.ts +231 -0
- package/dist/migrations/registry.d.ts.map +1 -0
- package/dist/migrations/registry.js +376 -0
- package/dist/migrations/registry.js.map +1 -0
- package/dist/migrations/runner.d.ts +197 -0
- package/dist/migrations/runner.d.ts.map +1 -0
- package/dist/migrations/runner.js +1167 -0
- package/dist/migrations/runner.js.map +1 -0
- package/dist/migrations/schema-generator.d.ts +111 -0
- package/dist/migrations/schema-generator.d.ts.map +1 -0
- package/dist/migrations/schema-generator.js +335 -0
- package/dist/migrations/schema-generator.js.map +1 -0
- package/dist/migrations/testing.d.ts +321 -0
- package/dist/migrations/testing.d.ts.map +1 -0
- package/dist/migrations/testing.js +645 -0
- package/dist/migrations/testing.js.map +1 -0
- package/dist/migrations/types.d.ts +503 -0
- package/dist/migrations/types.d.ts.map +1 -0
- package/dist/migrations/types.js +11 -0
- package/dist/migrations/types.js.map +1 -0
- package/dist/migrations/validator.d.ts +215 -0
- package/dist/migrations/validator.d.ts.map +1 -0
- package/dist/migrations/validator.js +494 -0
- package/dist/migrations/validator.js.map +1 -0
- package/dist/observability/alerting.d.ts +116 -0
- package/dist/observability/alerting.d.ts.map +1 -0
- package/dist/observability/alerting.js +353 -0
- package/dist/observability/alerting.js.map +1 -0
- package/dist/observability/analytics-engine.d.ts +357 -0
- package/dist/observability/analytics-engine.d.ts.map +1 -0
- package/dist/observability/analytics-engine.js +430 -0
- package/dist/observability/analytics-engine.js.map +1 -0
- package/dist/observability/cost-metrics.d.ts +269 -0
- package/dist/observability/cost-metrics.d.ts.map +1 -0
- package/dist/observability/cost-metrics.js +560 -0
- package/dist/observability/cost-metrics.js.map +1 -0
- package/dist/observability/cross-do-tracing.d.ts +305 -0
- package/dist/observability/cross-do-tracing.d.ts.map +1 -0
- package/dist/observability/cross-do-tracing.js +431 -0
- package/dist/observability/cross-do-tracing.js.map +1 -0
- package/dist/observability/error-rate-collector.d.ts +163 -0
- package/dist/observability/error-rate-collector.d.ts.map +1 -0
- package/dist/observability/error-rate-collector.js +306 -0
- package/dist/observability/error-rate-collector.js.map +1 -0
- package/dist/observability/exporters.d.ts +231 -0
- package/dist/observability/exporters.d.ts.map +1 -0
- package/dist/observability/exporters.js +479 -0
- package/dist/observability/exporters.js.map +1 -0
- package/dist/observability/health-check.d.ts +106 -0
- package/dist/observability/health-check.d.ts.map +1 -0
- package/dist/observability/health-check.js +243 -0
- package/dist/observability/health-check.js.map +1 -0
- package/dist/observability/index.d.ts +297 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +455 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/instrumentation.d.ts +222 -0
- package/dist/observability/instrumentation.d.ts.map +1 -0
- package/dist/observability/instrumentation.js +532 -0
- package/dist/observability/instrumentation.js.map +1 -0
- package/dist/observability/memory-metrics.d.ts +227 -0
- package/dist/observability/memory-metrics.d.ts.map +1 -0
- package/dist/observability/memory-metrics.js +688 -0
- package/dist/observability/memory-metrics.js.map +1 -0
- package/dist/observability/metrics-endpoint.d.ts +91 -0
- package/dist/observability/metrics-endpoint.d.ts.map +1 -0
- package/dist/observability/metrics-endpoint.js +246 -0
- package/dist/observability/metrics-endpoint.js.map +1 -0
- package/dist/observability/metrics.d.ts +88 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +253 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/observability-features.d.ts +488 -0
- package/dist/observability/observability-features.d.ts.map +1 -0
- package/dist/observability/observability-features.js +773 -0
- package/dist/observability/observability-features.js.map +1 -0
- package/dist/observability/prometheus.d.ts +39 -0
- package/dist/observability/prometheus.d.ts.map +1 -0
- package/dist/observability/prometheus.js +120 -0
- package/dist/observability/prometheus.js.map +1 -0
- package/dist/observability/propagation.d.ts +126 -0
- package/dist/observability/propagation.d.ts.map +1 -0
- package/dist/observability/propagation.js +234 -0
- package/dist/observability/propagation.js.map +1 -0
- package/dist/observability/query-latency.d.ts +243 -0
- package/dist/observability/query-latency.d.ts.map +1 -0
- package/dist/observability/query-latency.js +292 -0
- package/dist/observability/query-latency.js.map +1 -0
- package/dist/observability/query-performance.d.ts +169 -0
- package/dist/observability/query-performance.d.ts.map +1 -0
- package/dist/observability/query-performance.js +290 -0
- package/dist/observability/query-performance.js.map +1 -0
- package/dist/observability/storage-tier-metrics.d.ts +174 -0
- package/dist/observability/storage-tier-metrics.d.ts.map +1 -0
- package/dist/observability/storage-tier-metrics.js +306 -0
- package/dist/observability/storage-tier-metrics.js.map +1 -0
- package/dist/observability/tier-cost-optimizer.d.ts +155 -0
- package/dist/observability/tier-cost-optimizer.d.ts.map +1 -0
- package/dist/observability/tier-cost-optimizer.js +536 -0
- package/dist/observability/tier-cost-optimizer.js.map +1 -0
- package/dist/observability/tracer.d.ts +149 -0
- package/dist/observability/tracer.d.ts.map +1 -0
- package/dist/observability/tracer.js +435 -0
- package/dist/observability/tracer.js.map +1 -0
- package/dist/observability/types.d.ts +402 -0
- package/dist/observability/types.d.ts.map +1 -0
- package/dist/observability/types.js +103 -0
- package/dist/observability/types.js.map +1 -0
- package/dist/pglite/workers-pglite.d.ts +138 -0
- package/dist/pglite/workers-pglite.d.ts.map +1 -0
- package/dist/pglite/workers-pglite.js +143 -0
- package/dist/pglite/workers-pglite.js.map +1 -0
- package/dist/pglite-assets/pglite.data +0 -0
- package/dist/pglite-assets/pglite.wasm +0 -0
- package/dist/playground/index.d.ts +52 -0
- package/dist/playground/index.d.ts.map +1 -0
- package/dist/playground/index.js +55 -0
- package/dist/playground/index.js.map +1 -0
- package/dist/playground/keyboard-shortcuts.d.ts +116 -0
- package/dist/playground/keyboard-shortcuts.d.ts.map +1 -0
- package/dist/playground/keyboard-shortcuts.js +588 -0
- package/dist/playground/keyboard-shortcuts.js.map +1 -0
- package/dist/playground/playground.d.ts +82 -0
- package/dist/playground/playground.d.ts.map +1 -0
- package/dist/playground/playground.js +271 -0
- package/dist/playground/playground.js.map +1 -0
- package/dist/playground/query-executor.d.ts +115 -0
- package/dist/playground/query-executor.d.ts.map +1 -0
- package/dist/playground/query-executor.js +558 -0
- package/dist/playground/query-executor.js.map +1 -0
- package/dist/playground/query-history.d.ts +92 -0
- package/dist/playground/query-history.d.ts.map +1 -0
- package/dist/playground/query-history.js +259 -0
- package/dist/playground/query-history.js.map +1 -0
- package/dist/playground/result-formatter.d.ts +59 -0
- package/dist/playground/result-formatter.d.ts.map +1 -0
- package/dist/playground/result-formatter.js +341 -0
- package/dist/playground/result-formatter.js.map +1 -0
- package/dist/playground/sample-datasets.d.ts +77 -0
- package/dist/playground/sample-datasets.d.ts.map +1 -0
- package/dist/playground/sample-datasets.js +641 -0
- package/dist/playground/sample-datasets.js.map +1 -0
- package/dist/playground/sample-queries.d.ts +73 -0
- package/dist/playground/sample-queries.d.ts.map +1 -0
- package/dist/playground/sample-queries.js +1095 -0
- package/dist/playground/sample-queries.js.map +1 -0
- package/dist/playground/schema-explorer.d.ts +55 -0
- package/dist/playground/schema-explorer.d.ts.map +1 -0
- package/dist/playground/schema-explorer.js +473 -0
- package/dist/playground/schema-explorer.js.map +1 -0
- package/dist/playground/types.d.ts +430 -0
- package/dist/playground/types.d.ts.map +1 -0
- package/dist/playground/types.js +10 -0
- package/dist/playground/types.js.map +1 -0
- package/dist/readonly/cache-reader.d.ts +145 -0
- package/dist/readonly/cache-reader.d.ts.map +1 -0
- package/dist/readonly/cache-reader.js +198 -0
- package/dist/readonly/cache-reader.js.map +1 -0
- package/dist/readonly/config.d.ts +74 -0
- package/dist/readonly/config.d.ts.map +1 -0
- package/dist/readonly/config.js +67 -0
- package/dist/readonly/config.js.map +1 -0
- package/dist/readonly/index.d.ts +22 -0
- package/dist/readonly/index.d.ts.map +1 -0
- package/dist/readonly/index.js +17 -0
- package/dist/readonly/index.js.map +1 -0
- package/dist/readonly/pglite-wrapper.d.ts +82 -0
- package/dist/readonly/pglite-wrapper.d.ts.map +1 -0
- package/dist/readonly/pglite-wrapper.js +123 -0
- package/dist/readonly/pglite-wrapper.js.map +1 -0
- package/dist/readonly/worker.d.ts +142 -0
- package/dist/readonly/worker.d.ts.map +1 -0
- package/dist/readonly/worker.js +187 -0
- package/dist/readonly/worker.js.map +1 -0
- package/dist/readonly/write-blocker.d.ts +47 -0
- package/dist/readonly/write-blocker.d.ts.map +1 -0
- package/dist/readonly/write-blocker.js +136 -0
- package/dist/readonly/write-blocker.js.map +1 -0
- package/dist/recovery/disaster-recovery.d.ts +326 -0
- package/dist/recovery/disaster-recovery.d.ts.map +1 -0
- package/dist/recovery/disaster-recovery.js +799 -0
- package/dist/recovery/disaster-recovery.js.map +1 -0
- package/dist/recovery/index.d.ts +12 -0
- package/dist/recovery/index.d.ts.map +1 -0
- package/dist/recovery/index.js +12 -0
- package/dist/recovery/index.js.map +1 -0
- package/dist/recovery/parquet-parser.d.ts +321 -0
- package/dist/recovery/parquet-parser.d.ts.map +1 -0
- package/dist/recovery/parquet-parser.js +797 -0
- package/dist/recovery/parquet-parser.js.map +1 -0
- package/dist/retention/index.d.ts +50 -0
- package/dist/retention/index.d.ts.map +1 -0
- package/dist/retention/index.js +50 -0
- package/dist/retention/index.js.map +1 -0
- package/dist/retention/policy.d.ts +344 -0
- package/dist/retention/policy.d.ts.map +1 -0
- package/dist/retention/policy.js +472 -0
- package/dist/retention/policy.js.map +1 -0
- package/dist/retention/purger.d.ts +187 -0
- package/dist/retention/purger.d.ts.map +1 -0
- package/dist/retention/purger.js +411 -0
- package/dist/retention/purger.js.map +1 -0
- package/dist/rls/auth-integration.d.ts +280 -0
- package/dist/rls/auth-integration.d.ts.map +1 -0
- package/dist/rls/auth-integration.js +399 -0
- package/dist/rls/auth-integration.js.map +1 -0
- package/dist/rls/generator.d.ts +249 -0
- package/dist/rls/generator.d.ts.map +1 -0
- package/dist/rls/generator.js +495 -0
- package/dist/rls/generator.js.map +1 -0
- package/dist/rls/index.d.ts +26 -0
- package/dist/rls/index.d.ts.map +1 -0
- package/dist/rls/index.js +58 -0
- package/dist/rls/index.js.map +1 -0
- package/dist/rls/policy.d.ts +116 -0
- package/dist/rls/policy.d.ts.map +1 -0
- package/dist/rls/policy.js +77 -0
- package/dist/rls/policy.js.map +1 -0
- package/dist/rls/validator.d.ts +155 -0
- package/dist/rls/validator.d.ts.map +1 -0
- package/dist/rls/validator.js +792 -0
- package/dist/rls/validator.js.map +1 -0
- package/dist/routing/adaptive-router.d.ts +317 -0
- package/dist/routing/adaptive-router.d.ts.map +1 -0
- package/dist/routing/adaptive-router.js +554 -0
- package/dist/routing/adaptive-router.js.map +1 -0
- package/dist/routing/circuit-breaker.d.ts +339 -0
- package/dist/routing/circuit-breaker.d.ts.map +1 -0
- package/dist/routing/circuit-breaker.js +620 -0
- package/dist/routing/circuit-breaker.js.map +1 -0
- package/dist/routing/cost-metrics.d.ts +133 -0
- package/dist/routing/cost-metrics.d.ts.map +1 -0
- package/dist/routing/cost-metrics.js +259 -0
- package/dist/routing/cost-metrics.js.map +1 -0
- package/dist/routing/do-connection-pool.d.ts +243 -0
- package/dist/routing/do-connection-pool.d.ts.map +1 -0
- package/dist/routing/do-connection-pool.js +572 -0
- package/dist/routing/do-connection-pool.js.map +1 -0
- package/dist/routing/index.d.ts +59 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +59 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/query-complexity-estimator.d.ts +73 -0
- package/dist/routing/query-complexity-estimator.d.ts.map +1 -0
- package/dist/routing/query-complexity-estimator.js +327 -0
- package/dist/routing/query-complexity-estimator.js.map +1 -0
- package/dist/routing/request-coalescing.d.ts +178 -0
- package/dist/routing/request-coalescing.d.ts.map +1 -0
- package/dist/routing/request-coalescing.js +325 -0
- package/dist/routing/request-coalescing.js.map +1 -0
- package/dist/routing/runtime-router.d.ts +107 -0
- package/dist/routing/runtime-router.d.ts.map +1 -0
- package/dist/routing/runtime-router.js +246 -0
- package/dist/routing/runtime-router.js.map +1 -0
- package/dist/routing/tenant-router.d.ts +848 -0
- package/dist/routing/tenant-router.d.ts.map +1 -0
- package/dist/routing/tenant-router.js +1056 -0
- package/dist/routing/tenant-router.js.map +1 -0
- package/dist/routing/websocket-pool.d.ts +119 -0
- package/dist/routing/websocket-pool.d.ts.map +1 -0
- package/dist/routing/websocket-pool.js +436 -0
- package/dist/routing/websocket-pool.js.map +1 -0
- package/dist/storage/cache-layer.d.ts +159 -0
- package/dist/storage/cache-layer.d.ts.map +1 -0
- package/dist/storage/cache-layer.js +245 -0
- package/dist/storage/cache-layer.js.map +1 -0
- package/dist/storage/cost-aware-tiering.d.ts +258 -0
- package/dist/storage/cost-aware-tiering.d.ts.map +1 -0
- package/dist/storage/cost-aware-tiering.js +526 -0
- package/dist/storage/cost-aware-tiering.js.map +1 -0
- package/dist/storage/index.d.ts +87 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +78 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/interfaces.d.ts +856 -0
- package/dist/storage/interfaces.d.ts.map +1 -0
- package/dist/storage/interfaces.js +69 -0
- package/dist/storage/interfaces.js.map +1 -0
- package/dist/storage/r2-layer.d.ts +226 -0
- package/dist/storage/r2-layer.d.ts.map +1 -0
- package/dist/storage/r2-layer.js +307 -0
- package/dist/storage/r2-layer.js.map +1 -0
- package/dist/storage/r2-overflow.d.ts +344 -0
- package/dist/storage/r2-overflow.d.ts.map +1 -0
- package/dist/storage/r2-overflow.js +730 -0
- package/dist/storage/r2-overflow.js.map +1 -0
- package/dist/storage/r2-page-vfs.d.ts +374 -0
- package/dist/storage/r2-page-vfs.d.ts.map +1 -0
- package/dist/storage/r2-page-vfs.js +754 -0
- package/dist/storage/r2-page-vfs.js.map +1 -0
- package/dist/storage/swr-cache.d.ts +181 -0
- package/dist/storage/swr-cache.d.ts.map +1 -0
- package/dist/storage/swr-cache.js +295 -0
- package/dist/storage/swr-cache.js.map +1 -0
- package/dist/storage/tiered-orchestrator.d.ts +951 -0
- package/dist/storage/tiered-orchestrator.d.ts.map +1 -0
- package/dist/storage/tiered-orchestrator.js +1731 -0
- package/dist/storage/tiered-orchestrator.js.map +1 -0
- package/dist/storage/tiered-vfs-swr.d.ts +279 -0
- package/dist/storage/tiered-vfs-swr.d.ts.map +1 -0
- package/dist/storage/tiered-vfs-swr.js +584 -0
- package/dist/storage/tiered-vfs-swr.js.map +1 -0
- package/dist/storage/tiered-vfs.d.ts +405 -0
- package/dist/storage/tiered-vfs.d.ts.map +1 -0
- package/dist/storage/tiered-vfs.js +833 -0
- package/dist/storage/tiered-vfs.js.map +1 -0
- package/dist/streaming/backpressure-controller.d.ts +173 -0
- package/dist/streaming/backpressure-controller.d.ts.map +1 -0
- package/dist/streaming/backpressure-controller.js +344 -0
- package/dist/streaming/backpressure-controller.js.map +1 -0
- package/dist/streaming/buffer-pool.d.ts +241 -0
- package/dist/streaming/buffer-pool.d.ts.map +1 -0
- package/dist/streaming/buffer-pool.js +381 -0
- package/dist/streaming/buffer-pool.js.map +1 -0
- package/dist/streaming/cdc-iceberg-connector.d.ts +272 -0
- package/dist/streaming/cdc-iceberg-connector.d.ts.map +1 -0
- package/dist/streaming/cdc-iceberg-connector.js +408 -0
- package/dist/streaming/cdc-iceberg-connector.js.map +1 -0
- package/dist/streaming/index.d.ts +111 -0
- package/dist/streaming/index.d.ts.map +1 -0
- package/dist/streaming/index.js +128 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/streaming/live-cdc-stream.d.ts +400 -0
- package/dist/streaming/live-cdc-stream.d.ts.map +1 -0
- package/dist/streaming/live-cdc-stream.js +703 -0
- package/dist/streaming/live-cdc-stream.js.map +1 -0
- package/dist/streaming/memory-bounded-stream.d.ts +207 -0
- package/dist/streaming/memory-bounded-stream.d.ts.map +1 -0
- package/dist/streaming/memory-bounded-stream.js +340 -0
- package/dist/streaming/memory-bounded-stream.js.map +1 -0
- package/dist/streaming/query-streamer.d.ts +379 -0
- package/dist/streaming/query-streamer.d.ts.map +1 -0
- package/dist/streaming/query-streamer.js +495 -0
- package/dist/streaming/query-streamer.js.map +1 -0
- package/dist/streaming/response-streaming.d.ts +203 -0
- package/dist/streaming/response-streaming.d.ts.map +1 -0
- package/dist/streaming/response-streaming.js +449 -0
- package/dist/streaming/response-streaming.js.map +1 -0
- package/dist/types/branded.d.ts +859 -0
- package/dist/types/branded.d.ts.map +1 -0
- package/dist/types/branded.js +891 -0
- package/dist/types/branded.js.map +1 -0
- package/dist/types/utilities.d.ts +757 -0
- package/dist/types/utilities.d.ts.map +1 -0
- package/dist/types/utilities.js +447 -0
- package/dist/types/utilities.js.map +1 -0
- package/dist/wal/replay-engine.d.ts +344 -0
- package/dist/wal/replay-engine.d.ts.map +1 -0
- package/dist/wal/replay-engine.js +975 -0
- package/dist/wal/replay-engine.js.map +1 -0
- package/dist/worker/__mocks__/capnweb.d.ts +13 -0
- package/dist/worker/__mocks__/capnweb.d.ts.map +1 -0
- package/dist/worker/__mocks__/capnweb.js +15 -0
- package/dist/worker/__mocks__/capnweb.js.map +1 -0
- package/dist/worker/__mocks__/cloudflare-workers.d.ts +31 -0
- package/dist/worker/__mocks__/cloudflare-workers.d.ts.map +1 -0
- package/dist/worker/__mocks__/cloudflare-workers.js +33 -0
- package/dist/worker/__mocks__/cloudflare-workers.js.map +1 -0
- package/dist/worker/__mocks__/pglite.data.d.ts +3 -0
- package/dist/worker/__mocks__/pglite.data.d.ts.map +1 -0
- package/dist/worker/__mocks__/pglite.data.js +20 -0
- package/dist/worker/__mocks__/pglite.data.js.map +1 -0
- package/dist/worker/__mocks__/pglite.wasm.d.ts +3 -0
- package/dist/worker/__mocks__/pglite.wasm.d.ts.map +1 -0
- package/dist/worker/__mocks__/pglite.wasm.js +30 -0
- package/dist/worker/__mocks__/pglite.wasm.js.map +1 -0
- package/dist/worker/auth-rate-limiter.d.ts +270 -0
- package/dist/worker/auth-rate-limiter.d.ts.map +1 -0
- package/dist/worker/auth-rate-limiter.js +332 -0
- package/dist/worker/auth-rate-limiter.js.map +1 -0
- package/dist/worker/auth.d.ts +345 -0
- package/dist/worker/auth.d.ts.map +1 -0
- package/dist/worker/auth.js +837 -0
- package/dist/worker/auth.js.map +1 -0
- package/dist/worker/cdc-backpressure.d.ts +338 -0
- package/dist/worker/cdc-backpressure.d.ts.map +1 -0
- package/dist/worker/cdc-backpressure.js +619 -0
- package/dist/worker/cdc-backpressure.js.map +1 -0
- package/dist/worker/cdc-sse.d.ts +277 -0
- package/dist/worker/cdc-sse.d.ts.map +1 -0
- package/dist/worker/cdc-sse.js +528 -0
- package/dist/worker/cdc-sse.js.map +1 -0
- package/dist/worker/cdc-websocket.d.ts +252 -0
- package/dist/worker/cdc-websocket.d.ts.map +1 -0
- package/dist/worker/cdc-websocket.js +940 -0
- package/dist/worker/cdc-websocket.js.map +1 -0
- package/dist/worker/cdc.d.ts +95 -0
- package/dist/worker/cdc.d.ts.map +1 -0
- package/dist/worker/cdc.js +211 -0
- package/dist/worker/cdc.js.map +1 -0
- package/dist/worker/concerns/auth-concern.d.ts +50 -0
- package/dist/worker/concerns/auth-concern.d.ts.map +1 -0
- package/dist/worker/concerns/auth-concern.js +131 -0
- package/dist/worker/concerns/auth-concern.js.map +1 -0
- package/dist/worker/concerns/cdc-concern.d.ts +99 -0
- package/dist/worker/concerns/cdc-concern.d.ts.map +1 -0
- package/dist/worker/concerns/cdc-concern.js +137 -0
- package/dist/worker/concerns/cdc-concern.js.map +1 -0
- package/dist/worker/concerns/index.d.ts +22 -0
- package/dist/worker/concerns/index.d.ts.map +1 -0
- package/dist/worker/concerns/index.js +13 -0
- package/dist/worker/concerns/index.js.map +1 -0
- package/dist/worker/concerns/query-execution-concern.d.ts +104 -0
- package/dist/worker/concerns/query-execution-concern.d.ts.map +1 -0
- package/dist/worker/concerns/query-execution-concern.js +95 -0
- package/dist/worker/concerns/query-execution-concern.js.map +1 -0
- package/dist/worker/concerns/storage-orchestration-concern.d.ts +78 -0
- package/dist/worker/concerns/storage-orchestration-concern.d.ts.map +1 -0
- package/dist/worker/concerns/storage-orchestration-concern.js +240 -0
- package/dist/worker/concerns/storage-orchestration-concern.js.map +1 -0
- package/dist/worker/do-auth-manager.d.ts +108 -0
- package/dist/worker/do-auth-manager.d.ts.map +1 -0
- package/dist/worker/do-auth-manager.js +212 -0
- package/dist/worker/do-auth-manager.js.map +1 -0
- package/dist/worker/do-pglite-manager.d.ts +137 -0
- package/dist/worker/do-pglite-manager.d.ts.map +1 -0
- package/dist/worker/do-pglite-manager.js +228 -0
- package/dist/worker/do-pglite-manager.js.map +1 -0
- package/dist/worker/do.d.ts +556 -0
- package/dist/worker/do.d.ts.map +1 -0
- package/dist/worker/do.js +1441 -0
- package/dist/worker/do.js.map +1 -0
- package/dist/worker/entry.d.ts +23 -0
- package/dist/worker/entry.d.ts.map +1 -0
- package/dist/worker/entry.js +362 -0
- package/dist/worker/entry.js.map +1 -0
- package/dist/worker/errors.d.ts +106 -0
- package/dist/worker/errors.d.ts.map +1 -0
- package/dist/worker/errors.js +178 -0
- package/dist/worker/errors.js.map +1 -0
- package/dist/worker/health-check-manager.d.ts +141 -0
- package/dist/worker/health-check-manager.d.ts.map +1 -0
- package/dist/worker/health-check-manager.js +145 -0
- package/dist/worker/health-check-manager.js.map +1 -0
- package/dist/worker/index.d.ts +60 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +67 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/memory-pressure.d.ts +892 -0
- package/dist/worker/memory-pressure.d.ts.map +1 -0
- package/dist/worker/memory-pressure.js +1990 -0
- package/dist/worker/memory-pressure.js.map +1 -0
- package/dist/worker/migration-manager.d.ts +153 -0
- package/dist/worker/migration-manager.d.ts.map +1 -0
- package/dist/worker/migration-manager.js +461 -0
- package/dist/worker/migration-manager.js.map +1 -0
- package/dist/worker/plugin-manager.d.ts +147 -0
- package/dist/worker/plugin-manager.d.ts.map +1 -0
- package/dist/worker/plugin-manager.js +408 -0
- package/dist/worker/plugin-manager.js.map +1 -0
- package/dist/worker/proxy.d.ts +330 -0
- package/dist/worker/proxy.d.ts.map +1 -0
- package/dist/worker/proxy.js +504 -0
- package/dist/worker/proxy.js.map +1 -0
- package/dist/worker/query-execution-manager.d.ts +107 -0
- package/dist/worker/query-execution-manager.d.ts.map +1 -0
- package/dist/worker/query-execution-manager.js +155 -0
- package/dist/worker/query-execution-manager.js.map +1 -0
- package/dist/worker/query-executor.d.ts +163 -0
- package/dist/worker/query-executor.d.ts.map +1 -0
- package/dist/worker/query-executor.js +413 -0
- package/dist/worker/query-executor.js.map +1 -0
- package/dist/worker/query-stats-manager.d.ts +117 -0
- package/dist/worker/query-stats-manager.d.ts.map +1 -0
- package/dist/worker/query-stats-manager.js +162 -0
- package/dist/worker/query-stats-manager.js.map +1 -0
- package/dist/worker/result-handler.d.ts +192 -0
- package/dist/worker/result-handler.d.ts.map +1 -0
- package/dist/worker/result-handler.js +346 -0
- package/dist/worker/result-handler.js.map +1 -0
- package/dist/worker/routes.d.ts +135 -0
- package/dist/worker/routes.d.ts.map +1 -0
- package/dist/worker/routes.js +460 -0
- package/dist/worker/routes.js.map +1 -0
- package/dist/worker/rpc-methods-manager.d.ts +142 -0
- package/dist/worker/rpc-methods-manager.d.ts.map +1 -0
- package/dist/worker/rpc-methods-manager.js +195 -0
- package/dist/worker/rpc-methods-manager.js.map +1 -0
- package/dist/worker/rpc.d.ts +259 -0
- package/dist/worker/rpc.d.ts.map +1 -0
- package/dist/worker/rpc.js +398 -0
- package/dist/worker/rpc.js.map +1 -0
- package/dist/worker/schema-version.d.ts +209 -0
- package/dist/worker/schema-version.d.ts.map +1 -0
- package/dist/worker/schema-version.js +450 -0
- package/dist/worker/schema-version.js.map +1 -0
- package/dist/worker/session-manager.d.ts +282 -0
- package/dist/worker/session-manager.d.ts.map +1 -0
- package/dist/worker/session-manager.js +523 -0
- package/dist/worker/session-manager.js.map +1 -0
- package/dist/worker/shutdown-manager.d.ts +188 -0
- package/dist/worker/shutdown-manager.d.ts.map +1 -0
- package/dist/worker/shutdown-manager.js +347 -0
- package/dist/worker/shutdown-manager.js.map +1 -0
- package/dist/worker/sql-transform.d.ts +61 -0
- package/dist/worker/sql-transform.d.ts.map +1 -0
- package/dist/worker/sql-transform.js +312 -0
- package/dist/worker/sql-transform.js.map +1 -0
- package/dist/worker/types.d.ts +738 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +6 -0
- package/dist/worker/types.js.map +1 -0
- package/dist/worker/user-routes.d.ts +76 -0
- package/dist/worker/user-routes.d.ts.map +1 -0
- package/dist/worker/user-routes.js +188 -0
- package/dist/worker/user-routes.js.map +1 -0
- package/dist/worker/wal-facade.d.ts +138 -0
- package/dist/worker/wal-facade.d.ts.map +1 -0
- package/dist/worker/wal-facade.js +184 -0
- package/dist/worker/wal-facade.js.map +1 -0
- package/dist/worker/wal-r2.d.ts +271 -0
- package/dist/worker/wal-r2.d.ts.map +1 -0
- package/dist/worker/wal-r2.js +689 -0
- package/dist/worker/wal-r2.js.map +1 -0
- package/dist/worker/wal-replay.d.ts +361 -0
- package/dist/worker/wal-replay.d.ts.map +1 -0
- package/dist/worker/wal-replay.js +628 -0
- package/dist/worker/wal-replay.js.map +1 -0
- package/dist/worker/wal-retention.d.ts +389 -0
- package/dist/worker/wal-retention.d.ts.map +1 -0
- package/dist/worker/wal-retention.js +763 -0
- package/dist/worker/wal-retention.js.map +1 -0
- package/dist/worker/wal.d.ts +278 -0
- package/dist/worker/wal.d.ts.map +1 -0
- package/dist/worker/wal.js +467 -0
- package/dist/worker/wal.js.map +1 -0
- package/dist/worker/websocket.d.ts +85 -0
- package/dist/worker/websocket.d.ts.map +1 -0
- package/dist/worker/websocket.js +227 -0
- package/dist/worker/websocket.js.map +1 -0
- package/package.json +108 -0
- package/src/cdc/change-stream.ts +137 -0
- package/src/cdc/filter.ts +646 -0
- package/src/cdc/index.ts +112 -0
- package/src/cdc/resume-token.ts +280 -0
- package/src/cdc/transport/index.ts +7 -0
- package/src/cdc/transport/sse.ts +723 -0
- package/src/cdc/transport/websocket.ts +873 -0
- package/src/cdc/types.ts +346 -0
- package/src/config/index.ts +25 -0
- package/src/config/memory.ts +177 -0
- package/src/config/storage.ts +204 -0
- package/src/config/streaming.ts +147 -0
- package/src/config/timeouts.ts +221 -0
- package/src/extensions/config.test.ts +187 -0
- package/src/extensions/config.ts +278 -0
- package/src/extensions/geo.test.ts +455 -0
- package/src/extensions/geo.ts +858 -0
- package/src/extensions/index.test.ts +259 -0
- package/src/extensions/index.ts +227 -0
- package/src/extensions/loader.test.ts +555 -0
- package/src/extensions/loader.ts +588 -0
- package/src/extensions/pgmq-lite.test.ts +727 -0
- package/src/extensions/pgmq-lite.ts +770 -0
- package/src/extensions/plugins.test.ts +528 -0
- package/src/extensions/plugins.ts +718 -0
- package/src/extensions/registry.test.ts +202 -0
- package/src/extensions/registry.ts +267 -0
- package/src/extensions/vector.test.ts +195 -0
- package/src/extensions/vector.ts +217 -0
- package/src/iceberg/SCHEDULER.md +580 -0
- package/src/iceberg/analytics.test.ts +703 -0
- package/src/iceberg/analytics.ts +727 -0
- package/src/iceberg/catalog-api.test.ts +838 -0
- package/src/iceberg/catalog-api.ts +520 -0
- package/src/iceberg/catalog.test.ts +680 -0
- package/src/iceberg/catalog.ts +1007 -0
- package/src/iceberg/iceberg.test.ts +705 -0
- package/src/iceberg/index.ts +406 -0
- package/src/iceberg/metadata.test.ts +632 -0
- package/src/iceberg/metadata.ts +649 -0
- package/src/iceberg/optimizer.test.ts +868 -0
- package/src/iceberg/optimizer.ts +1287 -0
- package/src/iceberg/parquet.test.ts +899 -0
- package/src/iceberg/parquet.ts +1640 -0
- package/src/iceberg/r2-organization.test.ts +615 -0
- package/src/iceberg/r2-organization.ts +951 -0
- package/src/iceberg/scheduler-do-example.ts +364 -0
- package/src/iceberg/scheduler.test.ts +861 -0
- package/src/iceberg/scheduler.ts +1201 -0
- package/src/iceberg/schema.test.ts +547 -0
- package/src/iceberg/schema.ts +616 -0
- package/src/iceberg/snapshot-manager.test.ts +919 -0
- package/src/iceberg/snapshot-manager.ts +1369 -0
- package/src/iceberg/sql-router.test.ts +334 -0
- package/src/iceberg/sql-router.ts +337 -0
- package/src/iceberg/test-fixtures.ts +605 -0
- package/src/iceberg/time-travel-api.test.ts +1029 -0
- package/src/iceberg/time-travel-api.ts +731 -0
- package/src/iceberg/time-travel.test.ts +1218 -0
- package/src/iceberg/time-travel.ts +1052 -0
- package/src/iceberg/transformer.test.ts +689 -0
- package/src/iceberg/transformer.ts +1029 -0
- package/src/iceberg/types.ts +373 -0
- package/src/iceberg/writer.test.ts +716 -0
- package/src/iceberg/writer.ts +590 -0
- package/src/index.ts +212 -0
- package/src/lineage/index.ts +42 -0
- package/src/lineage/integration.ts +334 -0
- package/src/lineage/tracker.ts +1618 -0
- package/src/lineage/types.ts +354 -0
- package/src/middleware/index.ts +36 -0
- package/src/middleware/rate-limit-concurrent.test.ts +794 -0
- package/src/middleware/rate-limit.test.ts +1568 -0
- package/src/middleware/rate-limit.ts +840 -0
- package/src/migration-tooling/external-migration.test.ts +1864 -0
- package/src/migration-tooling/external-migration.ts +2355 -0
- package/src/migration-tooling/index.ts +19 -0
- package/src/migrations/ARCHITECTURE.md +474 -0
- package/src/migrations/PROGRESS_TRACKING.md +485 -0
- package/src/migrations/auto-migrator.test.ts +732 -0
- package/src/migrations/auto-migrator.ts +531 -0
- package/src/migrations/bulk-orchestrator.test.ts +801 -0
- package/src/migrations/bulk-orchestrator.ts +1039 -0
- package/src/migrations/compatibility.test.ts +958 -0
- package/src/migrations/compatibility.ts +902 -0
- package/src/migrations/do-migrations.test.ts +2620 -0
- package/src/migrations/do-migrations.ts +1289 -0
- package/src/migrations/do-migrations.types.ts +715 -0
- package/src/migrations/drizzle-compat.test.ts +210 -0
- package/src/migrations/drizzle-compat.ts +337 -0
- package/src/migrations/index.ts +334 -0
- package/src/migrations/migration-api.test.ts +438 -0
- package/src/migrations/migration-api.ts +704 -0
- package/src/migrations/progress-tracker-do.ts +518 -0
- package/src/migrations/progress-tracker-kv.ts +305 -0
- package/src/migrations/progress-tracker.test.ts +937 -0
- package/src/migrations/progress-tracker.ts +665 -0
- package/src/migrations/registry.test.ts +331 -0
- package/src/migrations/registry.ts +468 -0
- package/src/migrations/rollback.test.ts +644 -0
- package/src/migrations/runner.test.ts +807 -0
- package/src/migrations/runner.test.ts.backup +759 -0
- package/src/migrations/runner.ts +1459 -0
- package/src/migrations/schema-generator.test.ts +649 -0
- package/src/migrations/schema-generator.ts +513 -0
- package/src/migrations/testing.ts +1037 -0
- package/src/migrations/types.ts +573 -0
- package/src/migrations/validator.test.ts +660 -0
- package/src/migrations/validator.ts +741 -0
- package/src/observability/alerting.test.ts +1133 -0
- package/src/observability/alerting.ts +455 -0
- package/src/observability/analytics-engine.ts +733 -0
- package/src/observability/cost-metrics.ts +804 -0
- package/src/observability/cross-do-tracing.test.ts +516 -0
- package/src/observability/cross-do-tracing.ts +588 -0
- package/src/observability/dashboards/postgres-do-overview.json +1656 -0
- package/src/observability/error-rate-collector.test.ts +977 -0
- package/src/observability/error-rate-collector.ts +518 -0
- package/src/observability/exporters.test.ts +365 -0
- package/src/observability/exporters.ts +650 -0
- package/src/observability/health-check.test.ts +353 -0
- package/src/observability/health-check.ts +341 -0
- package/src/observability/index.test.ts +298 -0
- package/src/observability/index.ts +885 -0
- package/src/observability/instrumentation.test.ts +428 -0
- package/src/observability/instrumentation.ts +788 -0
- package/src/observability/memory-metrics.test.ts +355 -0
- package/src/observability/memory-metrics.ts +990 -0
- package/src/observability/metrics-endpoint.test.ts +402 -0
- package/src/observability/metrics-endpoint.ts +374 -0
- package/src/observability/metrics.test.ts +291 -0
- package/src/observability/metrics.ts +315 -0
- package/src/observability/observability-features.ts +1296 -0
- package/src/observability/prometheus.test.ts +292 -0
- package/src/observability/prometheus.ts +170 -0
- package/src/observability/propagation.test.ts +417 -0
- package/src/observability/propagation.ts +294 -0
- package/src/observability/query-latency.ts +586 -0
- package/src/observability/query-performance.test.ts +406 -0
- package/src/observability/query-performance.ts +491 -0
- package/src/observability/storage-tier-metrics.test.ts +633 -0
- package/src/observability/storage-tier-metrics.ts +570 -0
- package/src/observability/tier-cost-optimizer.ts +740 -0
- package/src/observability/tracer.test.ts +346 -0
- package/src/observability/tracer.ts +585 -0
- package/src/observability/types.test.ts +726 -0
- package/src/observability/types.ts +434 -0
- package/src/pglite/auto-demotion.test.ts +477 -0
- package/src/pglite/auto-demotion.ts +385 -0
- package/src/pglite/auto-promotion.test.ts +824 -0
- package/src/pglite/auto-promotion.ts +547 -0
- package/src/pglite/cache-layer.test.ts +469 -0
- package/src/pglite/cache-layer.ts +271 -0
- package/src/pglite/cold-start-manager.ts +1260 -0
- package/src/pglite/cold-start-optimizer.test.ts +937 -0
- package/src/pglite/cold-start-optimizer.ts +1895 -0
- package/src/pglite/dovfs-adapter.ts +1122 -0
- package/src/pglite/dovfs.ts +1258 -0
- package/src/pglite/etag-cache.test.ts +844 -0
- package/src/pglite/etag-cache.ts +526 -0
- package/src/pglite/index.ts +442 -0
- package/src/pglite/init.test.ts +455 -0
- package/src/pglite/init.ts +574 -0
- package/src/pglite/lifecycle.test.ts +599 -0
- package/src/pglite/lifecycle.ts +704 -0
- package/src/pglite/parallel-loader.test.ts +586 -0
- package/src/pglite/parallel-loader.ts +481 -0
- package/src/pglite/production-pglite.test.ts +666 -0
- package/src/pglite/production-pglite.ts +537 -0
- package/src/pglite/query-executor.ts +614 -0
- package/src/pglite/r2-layer.test.ts +501 -0
- package/src/pglite/r2-layer.ts +322 -0
- package/src/pglite/tiered-init.test.ts +725 -0
- package/src/pglite/tiered-init.ts +556 -0
- package/src/pglite/tiered-vfs.test.ts +726 -0
- package/src/pglite/tiered-vfs.ts +33 -0
- package/src/pglite/tiering-stats.test.ts +531 -0
- package/src/pglite/tiering-stats.ts +407 -0
- package/src/pglite/transaction-hooks.ts +343 -0
- package/src/pglite/warm-loader.test.ts +1701 -0
- package/src/pglite/warm-loader.ts +528 -0
- package/src/pglite/workers-pglite.ts +224 -0
- package/src/pglite-assets/pglite.data +0 -0
- package/src/pglite-assets/pglite.wasm +0 -0
- package/src/pglite.d.ts +47 -0
- package/src/playground/index.ts +137 -0
- package/src/playground/keyboard-shortcuts.ts +677 -0
- package/src/playground/playground.ts +323 -0
- package/src/playground/query-executor.ts +669 -0
- package/src/playground/query-history.ts +328 -0
- package/src/playground/result-formatter.ts +420 -0
- package/src/playground/sample-datasets.ts +674 -0
- package/src/playground/sample-queries.ts +1168 -0
- package/src/playground/schema-explorer.ts +558 -0
- package/src/playground/types.ts +518 -0
- package/src/readonly/cache-reader.test.ts +460 -0
- package/src/readonly/cache-reader.ts +313 -0
- package/src/readonly/config.test.ts +187 -0
- package/src/readonly/config.ts +128 -0
- package/src/readonly/index.ts +50 -0
- package/src/readonly/pglite-wrapper.test.ts +278 -0
- package/src/readonly/pglite-wrapper.ts +184 -0
- package/src/readonly/worker.test.ts +533 -0
- package/src/readonly/worker.ts +341 -0
- package/src/readonly/write-blocker.test.ts +459 -0
- package/src/readonly/write-blocker.ts +175 -0
- package/src/recovery/disaster-recovery.test.ts +618 -0
- package/src/recovery/disaster-recovery.ts +1181 -0
- package/src/recovery/index.ts +43 -0
- package/src/recovery/parquet-parser.ts +974 -0
- package/src/retention/index.ts +74 -0
- package/src/retention/policy.test.ts +571 -0
- package/src/retention/policy.ts +774 -0
- package/src/retention/purger.test.ts +465 -0
- package/src/retention/purger.ts +558 -0
- package/src/rls/auth-integration.test.ts +752 -0
- package/src/rls/auth-integration.ts +533 -0
- package/src/rls/generator.test.ts +829 -0
- package/src/rls/generator.ts +573 -0
- package/src/rls/index.ts +128 -0
- package/src/rls/policy.ts +208 -0
- package/src/rls/rls.test.ts +1071 -0
- package/src/rls/validator.test.ts +930 -0
- package/src/rls/validator.ts +895 -0
- package/src/routing/adaptive-router.test.ts +884 -0
- package/src/routing/adaptive-router.ts +845 -0
- package/src/routing/circuit-breaker.test.ts +1505 -0
- package/src/routing/circuit-breaker.ts +852 -0
- package/src/routing/cost-metrics.test.ts +565 -0
- package/src/routing/cost-metrics.ts +408 -0
- package/src/routing/do-connection-pool.test.ts +1109 -0
- package/src/routing/do-connection-pool.ts +828 -0
- package/src/routing/index.ts +158 -0
- package/src/routing/query-complexity-estimator.test.ts +356 -0
- package/src/routing/query-complexity-estimator.ts +444 -0
- package/src/routing/request-coalescing.test.ts +738 -0
- package/src/routing/request-coalescing.ts +475 -0
- package/src/routing/runtime-router.test.ts +436 -0
- package/src/routing/runtime-router.ts +357 -0
- package/src/routing/tenant-router.test.ts +2493 -0
- package/src/routing/tenant-router.ts +1908 -0
- package/src/routing/websocket-pool.test.ts +551 -0
- package/src/routing/websocket-pool.ts +577 -0
- package/src/storage/access-pattern-tracker.test.ts +874 -0
- package/src/storage/cache-layer.test.ts +560 -0
- package/src/storage/cache-layer.ts +328 -0
- package/src/storage/cost-aware-tiering.test.ts +652 -0
- package/src/storage/cost-aware-tiering.ts +794 -0
- package/src/storage/do-sqlite-blobs.test.ts +937 -0
- package/src/storage/index.ts +272 -0
- package/src/storage/interfaces.ts +974 -0
- package/src/storage/r2-layer.test.ts +653 -0
- package/src/storage/r2-layer.ts +434 -0
- package/src/storage/r2-overflow.ts +920 -0
- package/src/storage/r2-page-vfs.test.ts +2348 -0
- package/src/storage/r2-page-vfs.ts +1054 -0
- package/src/storage/swr-cache.test.ts +832 -0
- package/src/storage/swr-cache.ts +398 -0
- package/src/storage/swr-tiered-integration.test.ts +617 -0
- package/src/storage/tiered-orchestrator.test.ts +2441 -0
- package/src/storage/tiered-orchestrator.ts +2081 -0
- package/src/storage/tiered-vfs-swr.test.ts +736 -0
- package/src/storage/tiered-vfs-swr.ts +735 -0
- package/src/storage/tiered-vfs.test.ts +793 -0
- package/src/storage/tiered-vfs.ts +1082 -0
- package/src/streaming/backpressure-controller.ts +452 -0
- package/src/streaming/buffer-pool.ts +484 -0
- package/src/streaming/cdc-iceberg-connector.ts +605 -0
- package/src/streaming/index.ts +225 -0
- package/src/streaming/live-cdc-stream.ts +985 -0
- package/src/streaming/memory-bounded-stream.ts +443 -0
- package/src/streaming/query-streamer.ts +662 -0
- package/src/streaming/response-streaming.ts +557 -0
- package/src/types/branded.ts +1075 -0
- package/src/types/branded.ts.backup +273 -0
- package/src/types/utilities.ts +1023 -0
- package/src/types/wasm.d.ts +30 -0
- package/src/validation/typed-errors.test.ts +420 -0
- package/src/wal/replay-engine.ts +1264 -0
- package/src/worker/__mocks__/capnweb.ts +15 -0
- package/src/worker/__mocks__/pglite.data.ts +22 -0
- package/src/worker/__mocks__/pglite.wasm.ts +33 -0
- package/src/worker/auth-rate-limiter.test.ts +272 -0
- package/src/worker/auth-rate-limiter.ts +448 -0
- package/src/worker/auth.security-red.test.ts +1236 -0
- package/src/worker/auth.security.test.ts +822 -0
- package/src/worker/auth.test.ts +469 -0
- package/src/worker/auth.ts +1104 -0
- package/src/worker/cdc-backpressure.test.ts +726 -0
- package/src/worker/cdc-backpressure.ts +866 -0
- package/src/worker/cdc-sse.test.ts +780 -0
- package/src/worker/cdc-sse.ts +728 -0
- package/src/worker/cdc-websocket.ts +1229 -0
- package/src/worker/cdc-ws.test.ts +1009 -0
- package/src/worker/cdc.test.ts +327 -0
- package/src/worker/cdc.ts +289 -0
- package/src/worker/concerns/auth-concern.ts +179 -0
- package/src/worker/concerns/cdc-concern.ts +247 -0
- package/src/worker/concerns/index.ts +58 -0
- package/src/worker/concerns/query-execution-concern.ts +194 -0
- package/src/worker/concerns/storage-orchestration-concern.ts +373 -0
- package/src/worker/discriminated-types.test.ts +280 -0
- package/src/worker/do-auth-manager.ts +257 -0
- package/src/worker/do-decomposition.test.ts +1236 -0
- package/src/worker/do-pglite-manager.ts +302 -0
- package/src/worker/do.test.ts +2254 -0
- package/src/worker/do.ts +1878 -0
- package/src/worker/entry.ts +417 -0
- package/src/worker/errors.ts +285 -0
- package/src/worker/health-check-manager.test.ts +261 -0
- package/src/worker/health-check-manager.ts +231 -0
- package/src/worker/index.ts +389 -0
- package/src/worker/memory-pressure.test.ts +1460 -0
- package/src/worker/memory-pressure.ts +2650 -0
- package/src/worker/migration-manager.ts +582 -0
- package/src/worker/neon-compat.test.ts +332 -0
- package/src/worker/plugin-manager.ts +485 -0
- package/src/worker/postgres.do-rpc.d.ts +76 -0
- package/src/worker/proxy.ts +694 -0
- package/src/worker/query-execution-manager.test.ts +303 -0
- package/src/worker/query-execution-manager.ts +219 -0
- package/src/worker/query-executor.test.ts +282 -0
- package/src/worker/query-executor.ts +560 -0
- package/src/worker/query-stats-manager.ts +229 -0
- package/src/worker/result-handler.test.ts +364 -0
- package/src/worker/result-handler.ts +510 -0
- package/src/worker/routes.test.ts +795 -0
- package/src/worker/routes.ts +650 -0
- package/src/worker/rpc-methods-manager.test.ts +326 -0
- package/src/worker/rpc-methods-manager.ts +276 -0
- package/src/worker/rpc.ts +524 -0
- package/src/worker/schema-version.ts +605 -0
- package/src/worker/session-manager.test.ts +506 -0
- package/src/worker/session-manager.ts +732 -0
- package/src/worker/shutdown-manager.ts +469 -0
- package/src/worker/sql-transform.test.ts +286 -0
- package/src/worker/sql-transform.ts +368 -0
- package/src/worker/supabase-compat.test.ts +621 -0
- package/src/worker/types.test.ts +292 -0
- package/src/worker/types.ts +873 -0
- package/src/worker/user-routes.test.ts +703 -0
- package/src/worker/user-routes.ts +303 -0
- package/src/worker/wal-facade.ts +235 -0
- package/src/worker/wal-r2.test.ts +570 -0
- package/src/worker/wal-r2.ts +930 -0
- package/src/worker/wal-replay.test.ts +845 -0
- package/src/worker/wal-replay.ts +897 -0
- package/src/worker/wal-retention.test.ts +758 -0
- package/src/worker/wal-retention.ts +1075 -0
- package/src/worker/wal.test.ts +618 -0
- package/src/worker/wal.ts +697 -0
- package/src/worker/websocket.test.ts +296 -0
- package/src/worker/websocket.ts +284 -0
|
@@ -0,0 +1,1990 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Pressure Monitoring and OOM Protection for PostgresDO
|
|
3
|
+
*
|
|
4
|
+
* This module provides memory monitoring capabilities for PGLite running
|
|
5
|
+
* in Cloudflare Workers (128MB limit). It detects high memory usage and
|
|
6
|
+
* can automatically take protective actions to prevent OOM crashes.
|
|
7
|
+
*
|
|
8
|
+
* ## Key Features
|
|
9
|
+
*
|
|
10
|
+
* - **Real-time memory pressure tracking** - Monitors heap usage and classifies
|
|
11
|
+
* pressure levels (normal, warning, critical, oom)
|
|
12
|
+
* - **Automatic extension unloading** - Drops least-important PostgreSQL extensions
|
|
13
|
+
* when memory is critically low
|
|
14
|
+
* - **GC hint triggering** - Suggests garbage collection at configurable thresholds
|
|
15
|
+
* with rate limiting to prevent thrashing
|
|
16
|
+
* - **Cache eviction** - Automatically evicts cache entries under pressure using
|
|
17
|
+
* configurable strategies (LRU, largest-first)
|
|
18
|
+
* - **Query management** - Pauses or rejects queries under extreme pressure
|
|
19
|
+
* - **Recovery with hysteresis** - Prevents oscillation by requiring sustained
|
|
20
|
+
* normal pressure before full recovery
|
|
21
|
+
* - **Metrics export** - Prometheus and JSON format metrics for observability
|
|
22
|
+
*
|
|
23
|
+
* ## Usage
|
|
24
|
+
*
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { createMemoryPressureManager } from './memory-pressure'
|
|
27
|
+
*
|
|
28
|
+
* const manager = createMemoryPressureManager({
|
|
29
|
+
* warningThresholdPercent: 70,
|
|
30
|
+
* criticalThresholdPercent: 85,
|
|
31
|
+
* autoUnloadExtensions: true,
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* // Listen for pressure changes
|
|
35
|
+
* const unsubscribe = manager.onPressureChange((event) => {
|
|
36
|
+
* console.log(`Pressure: ${event.previousLevel} -> ${event.currentLevel}`)
|
|
37
|
+
* })
|
|
38
|
+
*
|
|
39
|
+
* // Start monitoring
|
|
40
|
+
* manager.startMonitoring()
|
|
41
|
+
*
|
|
42
|
+
* // Check status at any time
|
|
43
|
+
* const stats = manager.getMemoryStats()
|
|
44
|
+
*
|
|
45
|
+
* // Clean up when done
|
|
46
|
+
* manager.dispose()
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* ## Cloudflare Workers Constraints
|
|
50
|
+
*
|
|
51
|
+
* - 128MB total memory limit per isolate
|
|
52
|
+
* - ~15MB static (WASM + data + JS)
|
|
53
|
+
* - ~16MB shared_buffers
|
|
54
|
+
* - ~16MB WAL + buffers
|
|
55
|
+
* - ~10MB PostgreSQL catalog/metadata
|
|
56
|
+
* - ~71MB available for queries, results, and runtime
|
|
57
|
+
*
|
|
58
|
+
* @module worker/memory-pressure
|
|
59
|
+
*/
|
|
60
|
+
// =============================================================================
|
|
61
|
+
// Imported Constants (from centralized config)
|
|
62
|
+
// =============================================================================
|
|
63
|
+
import { WORKER_MEMORY_LIMIT_BYTES, PGLITE_BASELINE_BYTES, DEFAULT_WARNING_THRESHOLD_PERCENT, DEFAULT_CRITICAL_THRESHOLD_PERCENT, DEFAULT_OOM_THRESHOLD_PERCENT, MEMORY_SPIKE_THRESHOLD_BYTES, DEFAULT_METRICS_HISTORY_SIZE, } from '../config/memory';
|
|
64
|
+
import { DEFAULT_CHECK_INTERVAL_MS, DEFAULT_GC_HINT_MIN_INTERVAL_MS, DEFAULT_RECOVERY_HYSTERESIS_MS, DEFAULT_QUERY_PAUSE_TIMEOUT_MS, DEFAULT_RECOVERY_STEP_INTERVAL_MS, DEFAULT_SUSTAINED_PRESSURE_THRESHOLD_MS, DEFAULT_MAX_LISTENERS, } from '../config/timeouts';
|
|
65
|
+
// =============================================================================
|
|
66
|
+
// Local Constant Aliases (for backward compatibility in this module)
|
|
67
|
+
// =============================================================================
|
|
68
|
+
/** Default Cloudflare Worker memory limit: 128MB */
|
|
69
|
+
const DEFAULT_MEMORY_LIMIT_BYTES = WORKER_MEMORY_LIMIT_BYTES;
|
|
70
|
+
/** Default pressure thresholds (percentage of memory limit) */
|
|
71
|
+
const DEFAULT_WARNING_THRESHOLD = DEFAULT_WARNING_THRESHOLD_PERCENT;
|
|
72
|
+
const DEFAULT_CRITICAL_THRESHOLD = DEFAULT_CRITICAL_THRESHOLD_PERCENT;
|
|
73
|
+
const DEFAULT_OOM_THRESHOLD = DEFAULT_OOM_THRESHOLD_PERCENT;
|
|
74
|
+
/** Spike detection threshold: 10MB sudden increase */
|
|
75
|
+
const SPIKE_DETECTION_THRESHOLD_BYTES = MEMORY_SPIKE_THRESHOLD_BYTES;
|
|
76
|
+
/**
|
|
77
|
+
* Extensions safe to unload under memory pressure, ordered by priority.
|
|
78
|
+
* First = least important = dropped first.
|
|
79
|
+
*/
|
|
80
|
+
const DEFAULT_EXTENSION_UNLOAD_PRIORITY = [
|
|
81
|
+
'tablefunc', // Rarely used in typical workloads
|
|
82
|
+
'fuzzystrmatch', // Nice to have for fuzzy matching
|
|
83
|
+
'unaccent', // Text processing utility
|
|
84
|
+
'btree_gist', // Index type - usually not critical
|
|
85
|
+
'btree_gin', // Index type - usually not critical
|
|
86
|
+
'intarray', // Array functions
|
|
87
|
+
'cube', // Multi-dimensional data
|
|
88
|
+
'earthdistance', // Geo calculations (depends on cube)
|
|
89
|
+
'ltree', // Tree data
|
|
90
|
+
'hstore', // Key-value pairs
|
|
91
|
+
'citext', // Case-insensitive text
|
|
92
|
+
'pg_trgm', // Trigram matching
|
|
93
|
+
'pgcrypto', // Crypto functions - often important
|
|
94
|
+
'uuid_ossp', // UUID generation - often important
|
|
95
|
+
'vector', // Vector search - often critical for AI apps
|
|
96
|
+
];
|
|
97
|
+
/** Default features that can be disabled under pressure */
|
|
98
|
+
const DEFAULT_FEATURES_TO_DISABLE = ['caching', 'prefetching', 'parallel-queries'];
|
|
99
|
+
/** Default eviction target percentages by pressure level */
|
|
100
|
+
const DEFAULT_EVICTION_TARGET_PERCENT = {
|
|
101
|
+
normal: 0,
|
|
102
|
+
warning: 20,
|
|
103
|
+
critical: 50,
|
|
104
|
+
oom: 100,
|
|
105
|
+
};
|
|
106
|
+
/** Pressure level numeric indices for fast comparison */
|
|
107
|
+
const PRESSURE_LEVEL_INDEX = {
|
|
108
|
+
normal: 0,
|
|
109
|
+
warning: 1,
|
|
110
|
+
critical: 2,
|
|
111
|
+
oom: 3,
|
|
112
|
+
};
|
|
113
|
+
/** Default demotion configuration per pressure level */
|
|
114
|
+
const DEFAULT_DEMOTION_CONFIG = {
|
|
115
|
+
warning: { hotEvictCount: 10, warmDemoteCount: 0 },
|
|
116
|
+
critical: { hotEvictCount: 25, warmDemoteCount: 5 },
|
|
117
|
+
oom: { hotEvictCount: 50, warmDemoteCount: 15 },
|
|
118
|
+
};
|
|
119
|
+
/** Default minimum interval between pressure-triggered demotions */
|
|
120
|
+
const DEFAULT_DEMOTION_MIN_INTERVAL_MS = 500;
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// Optimized Constants for Memory Pressure Heuristics
|
|
123
|
+
// =============================================================================
|
|
124
|
+
/**
|
|
125
|
+
* EMA (Exponential Moving Average) smoothing factor for heap usage.
|
|
126
|
+
* Range: 0-1, higher = more weight on recent samples, lower = more smoothing.
|
|
127
|
+
* 0.3 provides good balance between responsiveness and noise reduction.
|
|
128
|
+
*/
|
|
129
|
+
const EMA_SMOOTHING_FACTOR = 0.3;
|
|
130
|
+
/**
|
|
131
|
+
* Minimum samples required for reliable trend detection.
|
|
132
|
+
* Below this, predictions have low confidence.
|
|
133
|
+
*/
|
|
134
|
+
const MIN_SAMPLES_FOR_TREND = 5;
|
|
135
|
+
/**
|
|
136
|
+
* Minimum samples for high-confidence OOM prediction.
|
|
137
|
+
*/
|
|
138
|
+
const MIN_SAMPLES_FOR_OOM_PREDICTION = 10;
|
|
139
|
+
/**
|
|
140
|
+
* Growth rate threshold for "stable" classification (1KB/s).
|
|
141
|
+
* Memory changes below this are considered noise.
|
|
142
|
+
*/
|
|
143
|
+
const STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC = 1024;
|
|
144
|
+
/**
|
|
145
|
+
* Rapid growth threshold (5MB/s) - triggers more aggressive responses.
|
|
146
|
+
*/
|
|
147
|
+
const RAPID_GROWTH_THRESHOLD_BYTES_PER_SEC = 5 * 1024 * 1024;
|
|
148
|
+
/**
|
|
149
|
+
* OOM prediction horizon - warn if OOM predicted within this time.
|
|
150
|
+
*/
|
|
151
|
+
const OOM_PREDICTION_HORIZON_MS = 60000;
|
|
152
|
+
/**
|
|
153
|
+
* Hysteresis margin for pressure level changes (2%).
|
|
154
|
+
* Prevents oscillation when hovering near thresholds.
|
|
155
|
+
* Reserved for future hysteresis implementation.
|
|
156
|
+
*/
|
|
157
|
+
const _PRESSURE_HYSTERESIS_PERCENT = 2;
|
|
158
|
+
void _PRESSURE_HYSTERESIS_PERCENT; // Suppress unused warning - reserved for future use
|
|
159
|
+
/**
|
|
160
|
+
* Heap sampling constants - optimized for balance between accuracy and speed.
|
|
161
|
+
* Reserved for future heap sampling implementation.
|
|
162
|
+
*/
|
|
163
|
+
const _HEAP_SAMPLE_STRIDE_SHIFT = 8; // ~256 samples, uses bit shift for speed
|
|
164
|
+
const _HEAP_SAMPLE_UNROLL_FACTOR = 4; // Unroll factor for loop optimization
|
|
165
|
+
void _HEAP_SAMPLE_STRIDE_SHIFT; // Suppress unused warning - reserved for future use
|
|
166
|
+
void _HEAP_SAMPLE_UNROLL_FACTOR; // Suppress unused warning - reserved for future use
|
|
167
|
+
/**
|
|
168
|
+
* Error thrown when memory pressure prevents an operation
|
|
169
|
+
*/
|
|
170
|
+
export class MemoryPressureError extends Error {
|
|
171
|
+
code = 'MEMORY_PRESSURE_HIGH';
|
|
172
|
+
memoryStats;
|
|
173
|
+
constructor(message, memoryStats) {
|
|
174
|
+
super(message);
|
|
175
|
+
this.name = 'MemoryPressureError';
|
|
176
|
+
this.memoryStats = memoryStats;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// =============================================================================
|
|
180
|
+
// Helper Functions
|
|
181
|
+
// =============================================================================
|
|
182
|
+
// Cache performance.memory reference to avoid repeated property access
|
|
183
|
+
let cachedPerformanceMemory;
|
|
184
|
+
/**
|
|
185
|
+
* Estimate WASM heap memory usage.
|
|
186
|
+
*
|
|
187
|
+
* In Cloudflare Workers, we can't directly access Emscripten's HEAP.
|
|
188
|
+
* This function provides an estimate based on available metrics.
|
|
189
|
+
*
|
|
190
|
+
* Optimized: Caches performance.memory reference to reduce property lookups.
|
|
191
|
+
*
|
|
192
|
+
* Priority:
|
|
193
|
+
* 1. performance.memory.usedJSHeapSize (if available)
|
|
194
|
+
* 2. Conservative estimate based on typical PGLite footprint
|
|
195
|
+
*/
|
|
196
|
+
function estimateHeapUsage() {
|
|
197
|
+
// Check cache first (undefined = not checked, null = not available)
|
|
198
|
+
if (cachedPerformanceMemory === undefined) {
|
|
199
|
+
if (typeof performance !== 'undefined' && 'memory' in performance) {
|
|
200
|
+
cachedPerformanceMemory = performance.memory ?? null;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
cachedPerformanceMemory = null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (cachedPerformanceMemory && typeof cachedPerformanceMemory.usedJSHeapSize === 'number') {
|
|
207
|
+
return cachedPerformanceMemory.usedJSHeapSize;
|
|
208
|
+
}
|
|
209
|
+
// Fallback: use pre-computed constant
|
|
210
|
+
return PGLITE_BASELINE_BYTES;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Determine memory pressure level from usage percentage.
|
|
214
|
+
* Optimized: Uses direct comparisons in descending order for early exit.
|
|
215
|
+
*/
|
|
216
|
+
function getPressureLevelFromPercent(usagePercent, thresholds) {
|
|
217
|
+
// Check highest threshold first for fast rejection
|
|
218
|
+
if (usagePercent >= thresholds.oomThresholdPercent)
|
|
219
|
+
return 'oom';
|
|
220
|
+
if (usagePercent >= thresholds.criticalThresholdPercent)
|
|
221
|
+
return 'critical';
|
|
222
|
+
if (usagePercent >= thresholds.warningThresholdPercent)
|
|
223
|
+
return 'warning';
|
|
224
|
+
return 'normal';
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get numeric index for pressure level comparison.
|
|
228
|
+
* Optimized: Uses pre-computed lookup table instead of array search.
|
|
229
|
+
*/
|
|
230
|
+
function getPressureLevelIndex(level) {
|
|
231
|
+
return PRESSURE_LEVEL_INDEX[level];
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Calculate average of an array of numbers.
|
|
235
|
+
* Optimized: Uses for loop instead of reduce for better performance.
|
|
236
|
+
*/
|
|
237
|
+
function average(values) {
|
|
238
|
+
const len = values.length;
|
|
239
|
+
if (len === 0)
|
|
240
|
+
return 0;
|
|
241
|
+
let sum = 0;
|
|
242
|
+
for (let i = 0; i < len; i++) {
|
|
243
|
+
sum += values[i] ?? 0;
|
|
244
|
+
}
|
|
245
|
+
return sum / len;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Calculate variance of an array of numbers.
|
|
249
|
+
* Optimized: Single-pass Welford's algorithm for numerical stability.
|
|
250
|
+
*/
|
|
251
|
+
function variance(values) {
|
|
252
|
+
const len = values.length;
|
|
253
|
+
if (len < 2)
|
|
254
|
+
return 0;
|
|
255
|
+
let mean = 0;
|
|
256
|
+
let m2 = 0;
|
|
257
|
+
for (let i = 0; i < len; i++) {
|
|
258
|
+
const value = values[i] ?? 0;
|
|
259
|
+
const delta = value - mean;
|
|
260
|
+
mean += delta / (i + 1);
|
|
261
|
+
m2 += delta * (value - mean);
|
|
262
|
+
}
|
|
263
|
+
return m2 / (len - 1);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Simple linear regression for trend analysis.
|
|
267
|
+
* Returns slope (bytes/ms), intercept, and R-squared.
|
|
268
|
+
* Optimized: Single-pass calculation.
|
|
269
|
+
*/
|
|
270
|
+
function linearRegression(samples) {
|
|
271
|
+
const n = samples.length;
|
|
272
|
+
if (n < 2)
|
|
273
|
+
return { slope: 0, intercept: 0, rSquared: 0 };
|
|
274
|
+
// Use first timestamp as origin for numerical stability
|
|
275
|
+
const t0 = samples[0].timestamp.getTime();
|
|
276
|
+
let sumX = 0;
|
|
277
|
+
let sumY = 0;
|
|
278
|
+
let sumXY = 0;
|
|
279
|
+
let sumX2 = 0;
|
|
280
|
+
let sumY2 = 0;
|
|
281
|
+
for (let i = 0; i < n; i++) {
|
|
282
|
+
const sample = samples[i];
|
|
283
|
+
const x = sample.timestamp.getTime() - t0;
|
|
284
|
+
const y = sample.heapUsed;
|
|
285
|
+
sumX += x;
|
|
286
|
+
sumY += y;
|
|
287
|
+
sumXY += x * y;
|
|
288
|
+
sumX2 += x * x;
|
|
289
|
+
sumY2 += y * y;
|
|
290
|
+
}
|
|
291
|
+
const denom = n * sumX2 - sumX * sumX;
|
|
292
|
+
if (Math.abs(denom) < 1e-10)
|
|
293
|
+
return { slope: 0, intercept: sumY / n, rSquared: 0 };
|
|
294
|
+
const slope = (n * sumXY - sumX * sumY) / denom;
|
|
295
|
+
const intercept = (sumY - slope * sumX) / n;
|
|
296
|
+
// Calculate R-squared
|
|
297
|
+
const meanY = sumY / n;
|
|
298
|
+
let ssRes = 0;
|
|
299
|
+
let ssTot = 0;
|
|
300
|
+
for (let i = 0; i < n; i++) {
|
|
301
|
+
const sample = samples[i];
|
|
302
|
+
const x = sample.timestamp.getTime() - t0;
|
|
303
|
+
const y = sample.heapUsed;
|
|
304
|
+
const yPred = slope * x + intercept;
|
|
305
|
+
ssRes += (y - yPred) ** 2;
|
|
306
|
+
ssTot += (y - meanY) ** 2;
|
|
307
|
+
}
|
|
308
|
+
const rSquared = ssTot > 0 ? 1 - ssRes / ssTot : 0;
|
|
309
|
+
return { slope, intercept, rSquared };
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Apply EMA (Exponential Moving Average) update.
|
|
313
|
+
* @param current - Current EMA value
|
|
314
|
+
* @param newValue - New sample value
|
|
315
|
+
* @param alpha - Smoothing factor (0-1)
|
|
316
|
+
*/
|
|
317
|
+
function updateEMA(current, newValue, alpha = EMA_SMOOTHING_FACTOR) {
|
|
318
|
+
return alpha * newValue + (1 - alpha) * current;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Ring buffer for efficient fixed-size history storage.
|
|
322
|
+
* Avoids shift() operations which are O(n) on arrays.
|
|
323
|
+
*/
|
|
324
|
+
class RingBuffer {
|
|
325
|
+
buffer;
|
|
326
|
+
capacity;
|
|
327
|
+
head = 0;
|
|
328
|
+
_size = 0;
|
|
329
|
+
constructor(capacity) {
|
|
330
|
+
this.capacity = capacity;
|
|
331
|
+
this.buffer = new Array(capacity);
|
|
332
|
+
}
|
|
333
|
+
push(item) {
|
|
334
|
+
this.buffer[this.head] = item;
|
|
335
|
+
this.head = (this.head + 1) % this.capacity;
|
|
336
|
+
if (this._size < this.capacity)
|
|
337
|
+
this._size++;
|
|
338
|
+
}
|
|
339
|
+
get size() {
|
|
340
|
+
return this._size;
|
|
341
|
+
}
|
|
342
|
+
/** Get item at logical index (0 = oldest, size-1 = newest) */
|
|
343
|
+
get(index) {
|
|
344
|
+
if (index < 0 || index >= this._size)
|
|
345
|
+
return undefined;
|
|
346
|
+
const bufferIndex = (this.head - this._size + index + this.capacity) % this.capacity;
|
|
347
|
+
return this.buffer[bufferIndex];
|
|
348
|
+
}
|
|
349
|
+
/** Get the oldest item */
|
|
350
|
+
first() {
|
|
351
|
+
return this._size > 0 ? this.get(0) : undefined;
|
|
352
|
+
}
|
|
353
|
+
/** Get the newest item */
|
|
354
|
+
last() {
|
|
355
|
+
return this._size > 0 ? this.get(this._size - 1) : undefined;
|
|
356
|
+
}
|
|
357
|
+
/** Convert to array (oldest to newest) */
|
|
358
|
+
toArray() {
|
|
359
|
+
const result = [];
|
|
360
|
+
for (let i = 0; i < this._size; i++) {
|
|
361
|
+
const item = this.get(i);
|
|
362
|
+
if (item !== undefined)
|
|
363
|
+
result.push(item);
|
|
364
|
+
}
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
clear() {
|
|
368
|
+
this.head = 0;
|
|
369
|
+
this._size = 0;
|
|
370
|
+
// Don't clear buffer - items will be overwritten
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// =============================================================================
|
|
374
|
+
// Memory Pressure Manager
|
|
375
|
+
// =============================================================================
|
|
376
|
+
/**
|
|
377
|
+
* Memory Pressure Manager for PostgresDO
|
|
378
|
+
*
|
|
379
|
+
* Monitors memory usage and takes protective actions to prevent OOM crashes
|
|
380
|
+
* in Cloudflare Workers. Provides configurable thresholds, automatic actions,
|
|
381
|
+
* and detailed metrics for observability.
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* const manager = new MemoryPressureManager({
|
|
386
|
+
* warningThresholdPercent: 70,
|
|
387
|
+
* criticalThresholdPercent: 85,
|
|
388
|
+
* onGCHint: (level, { aggressive }) => {
|
|
389
|
+
* if (aggressive) globalThis.gc?.()
|
|
390
|
+
* },
|
|
391
|
+
* })
|
|
392
|
+
*
|
|
393
|
+
* manager.onPressureChange((event) => {
|
|
394
|
+
* console.log(`Pressure: ${event.currentLevel}`)
|
|
395
|
+
* })
|
|
396
|
+
*
|
|
397
|
+
* manager.startMonitoring()
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
export class MemoryPressureManager {
|
|
401
|
+
// === Configuration ===
|
|
402
|
+
config;
|
|
403
|
+
extendedConfig;
|
|
404
|
+
metricsHistorySize;
|
|
405
|
+
// === External Dependencies ===
|
|
406
|
+
pglite = null;
|
|
407
|
+
wasmModule = null;
|
|
408
|
+
cacheManager = null;
|
|
409
|
+
// === State Tracking ===
|
|
410
|
+
lastPressureLevel = 'normal';
|
|
411
|
+
pressureLevelStartTime = Date.now();
|
|
412
|
+
pressureDurations = { normal: 0, warning: 0, critical: 0, oom: 0 };
|
|
413
|
+
sustainedPressureAlerted = false;
|
|
414
|
+
// === Recovery State ===
|
|
415
|
+
inRecoveryMode = false;
|
|
416
|
+
recoveryProgress = 1.0;
|
|
417
|
+
recoveryStartTime = null;
|
|
418
|
+
recoveryPaused = false;
|
|
419
|
+
recoveryStepsExecuted = [];
|
|
420
|
+
// === Feature Management ===
|
|
421
|
+
enabledFeatures;
|
|
422
|
+
disabledFeatures = new Set();
|
|
423
|
+
unloadedExtensions = new Set();
|
|
424
|
+
// === Query Management ===
|
|
425
|
+
pausedQueries = new Map();
|
|
426
|
+
inFlightQueries = new Map();
|
|
427
|
+
// === Metrics & History (using ring buffers for efficiency) ===
|
|
428
|
+
metricsHistory;
|
|
429
|
+
lastHeapSample = 0;
|
|
430
|
+
gcMetrics;
|
|
431
|
+
lastGCHintTime = 0;
|
|
432
|
+
// === Listeners ===
|
|
433
|
+
listeners = [];
|
|
434
|
+
memorySpikeListeners = [];
|
|
435
|
+
recoveryListeners = [];
|
|
436
|
+
pressureDemotionListeners = [];
|
|
437
|
+
// === Tiered Storage Integration ===
|
|
438
|
+
tieredStorageOrchestrator = null;
|
|
439
|
+
demotionMetrics = {
|
|
440
|
+
pressureTriggeredDemotions: 0,
|
|
441
|
+
demotionsByPressureLevel: { normal: 0, warning: 0, critical: 0, oom: 0 },
|
|
442
|
+
failedDemotions: 0,
|
|
443
|
+
bytesFreedByDemotion: 0,
|
|
444
|
+
lastDemotionTimestamp: null,
|
|
445
|
+
};
|
|
446
|
+
lastPressureDemotionTime = 0;
|
|
447
|
+
// === Intervals ===
|
|
448
|
+
checkInterval = null;
|
|
449
|
+
sustainedPressureCheckInterval = null;
|
|
450
|
+
gradualRecoveryInterval = null;
|
|
451
|
+
// === Smoothed Pressure State (for EMA-based detection) ===
|
|
452
|
+
smoothedState = {
|
|
453
|
+
emaHeapUsage: 0,
|
|
454
|
+
emaPressureLevel: 0,
|
|
455
|
+
recentVariance: 0,
|
|
456
|
+
trend: 'stable',
|
|
457
|
+
lastRawHeapUsage: 0,
|
|
458
|
+
};
|
|
459
|
+
smoothedStateInitialized = false;
|
|
460
|
+
// === Testing Support ===
|
|
461
|
+
simulatedHeapBytes = 0;
|
|
462
|
+
forcedHeapUsage = null;
|
|
463
|
+
constructor(config = {}) {
|
|
464
|
+
this.extendedConfig = config;
|
|
465
|
+
this.config = {
|
|
466
|
+
enabled: config.enabled ?? true,
|
|
467
|
+
memoryLimitBytes: config.memoryLimitBytes ?? DEFAULT_MEMORY_LIMIT_BYTES,
|
|
468
|
+
warningThresholdPercent: config.warningThresholdPercent ?? DEFAULT_WARNING_THRESHOLD,
|
|
469
|
+
criticalThresholdPercent: config.criticalThresholdPercent ?? DEFAULT_CRITICAL_THRESHOLD,
|
|
470
|
+
oomThresholdPercent: config.oomThresholdPercent ?? DEFAULT_OOM_THRESHOLD,
|
|
471
|
+
checkIntervalMs: config.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS,
|
|
472
|
+
autoUnloadExtensions: config.autoUnloadExtensions ?? true,
|
|
473
|
+
extensionUnloadPriority: config.extensionUnloadPriority ?? DEFAULT_EXTENSION_UNLOAD_PRIORITY,
|
|
474
|
+
maxListeners: config.maxListeners ?? DEFAULT_MAX_LISTENERS,
|
|
475
|
+
};
|
|
476
|
+
this.metricsHistorySize = config.metricsHistorySize ?? DEFAULT_METRICS_HISTORY_SIZE;
|
|
477
|
+
this.enabledFeatures = new Set(config.featuresToDisable ?? DEFAULT_FEATURES_TO_DISABLE);
|
|
478
|
+
// Initialize ring buffers for efficient history storage
|
|
479
|
+
this.metricsHistory = new RingBuffer(this.metricsHistorySize);
|
|
480
|
+
this.gcMetrics = {
|
|
481
|
+
hints: 0,
|
|
482
|
+
bytesFreed: new RingBuffer(50), // Keep last 50 GC results
|
|
483
|
+
durations: new RingBuffer(50),
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
// ===========================================================================
|
|
487
|
+
// Configuration & Setup
|
|
488
|
+
// ===========================================================================
|
|
489
|
+
/**
|
|
490
|
+
* Set the PGlite instance for extension unloading operations.
|
|
491
|
+
*
|
|
492
|
+
* @param pglite - PGLite instance with query method
|
|
493
|
+
*/
|
|
494
|
+
setPGlite(pglite) {
|
|
495
|
+
this.pglite = pglite;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Set the WASM module for real-time heap tracking.
|
|
499
|
+
*
|
|
500
|
+
* When set, heap usage is calculated by sampling the HEAPU8 array
|
|
501
|
+
* instead of using estimates.
|
|
502
|
+
*
|
|
503
|
+
* @param module - Emscripten module with HEAPU8 property
|
|
504
|
+
*/
|
|
505
|
+
setWasmHeapSource(module) {
|
|
506
|
+
this.wasmModule = module;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Set the cache manager for eviction operations.
|
|
510
|
+
*
|
|
511
|
+
* @param manager - Cache manager with eviction methods
|
|
512
|
+
*/
|
|
513
|
+
setCacheManager(manager) {
|
|
514
|
+
this.cacheManager = manager;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Get the current configuration.
|
|
518
|
+
*
|
|
519
|
+
* @returns Copy of the current configuration
|
|
520
|
+
*/
|
|
521
|
+
getConfig() {
|
|
522
|
+
return { ...this.config };
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Update configuration at runtime.
|
|
526
|
+
*
|
|
527
|
+
* If checkIntervalMs is changed and monitoring is active, it will be restarted.
|
|
528
|
+
*
|
|
529
|
+
* @param config - Partial configuration to merge
|
|
530
|
+
*/
|
|
531
|
+
setConfig(config) {
|
|
532
|
+
Object.assign(this.config, config);
|
|
533
|
+
// Restart monitoring if interval changed
|
|
534
|
+
if (config.checkIntervalMs !== undefined && this.checkInterval) {
|
|
535
|
+
this.stopMonitoring();
|
|
536
|
+
this.startMonitoring();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
// ===========================================================================
|
|
540
|
+
// Tiered Storage Orchestrator Integration
|
|
541
|
+
// ===========================================================================
|
|
542
|
+
/**
|
|
543
|
+
* Connect a TieredStorageOrchestrator for pressure-triggered demotions.
|
|
544
|
+
*
|
|
545
|
+
* When connected, the manager will automatically trigger demotions
|
|
546
|
+
* when memory pressure rises above warning level.
|
|
547
|
+
*
|
|
548
|
+
* @param orchestrator - TieredStorageOrchestrator instance or null to disconnect
|
|
549
|
+
*/
|
|
550
|
+
setTieredStorageOrchestrator(orchestrator) {
|
|
551
|
+
this.tieredStorageOrchestrator = orchestrator;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Check if a TieredStorageOrchestrator is connected.
|
|
555
|
+
*
|
|
556
|
+
* @returns True if an orchestrator is connected
|
|
557
|
+
*/
|
|
558
|
+
hasTieredStorageOrchestrator() {
|
|
559
|
+
return this.tieredStorageOrchestrator !== null;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Get demotion metrics for pressure-triggered demotions.
|
|
563
|
+
*
|
|
564
|
+
* @returns Demotion metrics
|
|
565
|
+
*/
|
|
566
|
+
getDemotionMetrics() {
|
|
567
|
+
return { ...this.demotionMetrics };
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Add a listener for pressure-triggered demotion events.
|
|
571
|
+
*
|
|
572
|
+
* @param listener - Callback for demotion events
|
|
573
|
+
* @returns Unsubscribe function
|
|
574
|
+
*/
|
|
575
|
+
onPressureDemotion(listener) {
|
|
576
|
+
this.pressureDemotionListeners.push(listener);
|
|
577
|
+
return () => {
|
|
578
|
+
const index = this.pressureDemotionListeners.indexOf(listener);
|
|
579
|
+
if (index >= 0)
|
|
580
|
+
this.pressureDemotionListeners.splice(index, 1);
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Manually trigger demotions.
|
|
585
|
+
*
|
|
586
|
+
* @param options - Demotion options specifying counts
|
|
587
|
+
* @returns Result of the demotion operation
|
|
588
|
+
*/
|
|
589
|
+
async triggerDemotion(options) {
|
|
590
|
+
if (!this.tieredStorageOrchestrator) {
|
|
591
|
+
return { success: false, demotedCount: 0, error: 'Tiered storage orchestrator not connected' };
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
let totalDemoted = 0;
|
|
595
|
+
// Evict from hot tier
|
|
596
|
+
if (options.hotEvictCount > 0) {
|
|
597
|
+
const hotEvents = await this.tieredStorageOrchestrator.evictLRUFromHot(options.hotEvictCount);
|
|
598
|
+
totalDemoted += hotEvents.filter((e) => e.success).length;
|
|
599
|
+
}
|
|
600
|
+
// Demote from warm tier
|
|
601
|
+
if (options.warmDemoteCount > 0) {
|
|
602
|
+
const warmEvents = await this.tieredStorageOrchestrator.runDemotionCycle(options.warmDemoteCount);
|
|
603
|
+
totalDemoted += warmEvents.filter((e) => e.success).length;
|
|
604
|
+
}
|
|
605
|
+
return { success: true, demotedCount: totalDemoted };
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
return {
|
|
609
|
+
success: false,
|
|
610
|
+
demotedCount: 0,
|
|
611
|
+
error: error instanceof Error ? error.message : String(error),
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Handle pressure-triggered demotion based on the current level.
|
|
617
|
+
*
|
|
618
|
+
* This is called automatically when pressure changes if autoDemoteOnPressure is enabled.
|
|
619
|
+
*
|
|
620
|
+
* @param level - Current pressure level
|
|
621
|
+
* @param forceBypassRateLimit - If true, bypass rate limiting (used when pressure escalates)
|
|
622
|
+
*/
|
|
623
|
+
async handlePressureDemotion(level, forceBypassRateLimit = false) {
|
|
624
|
+
// Skip if auto-demotion is disabled
|
|
625
|
+
if (this.extendedConfig.autoDemoteOnPressure === false)
|
|
626
|
+
return;
|
|
627
|
+
// Skip if no orchestrator connected
|
|
628
|
+
if (!this.tieredStorageOrchestrator)
|
|
629
|
+
return;
|
|
630
|
+
// Skip for normal pressure
|
|
631
|
+
if (level === 'normal')
|
|
632
|
+
return;
|
|
633
|
+
// Check rate limiting (can be bypassed for escalation)
|
|
634
|
+
const now = Date.now();
|
|
635
|
+
const minInterval = this.extendedConfig.demotionMinIntervalMs ?? DEFAULT_DEMOTION_MIN_INTERVAL_MS;
|
|
636
|
+
if (!forceBypassRateLimit && now - this.lastPressureDemotionTime < minInterval)
|
|
637
|
+
return;
|
|
638
|
+
// Get demotion config for this level
|
|
639
|
+
const demotionConfig = this.extendedConfig.demotionConfig ?? DEFAULT_DEMOTION_CONFIG;
|
|
640
|
+
const levelConfig = demotionConfig[level];
|
|
641
|
+
if (!levelConfig || (levelConfig.hotEvictCount === 0 && levelConfig.warmDemoteCount === 0))
|
|
642
|
+
return;
|
|
643
|
+
// Optionally trigger GC before demotion
|
|
644
|
+
if (this.extendedConfig.gcBeforeDemotion && this.extendedConfig.onGCHint) {
|
|
645
|
+
this.extendedConfig.onGCHint(level, { aggressive: level === 'critical' || level === 'oom' });
|
|
646
|
+
}
|
|
647
|
+
this.lastPressureDemotionTime = now;
|
|
648
|
+
try {
|
|
649
|
+
const allDemotions = [];
|
|
650
|
+
let bytesFreed = 0;
|
|
651
|
+
// Evict from hot tier
|
|
652
|
+
if (levelConfig.hotEvictCount > 0) {
|
|
653
|
+
const hotEvents = await this.tieredStorageOrchestrator.evictLRUFromHot(levelConfig.hotEvictCount);
|
|
654
|
+
allDemotions.push(...hotEvents);
|
|
655
|
+
// Track bytes freed
|
|
656
|
+
for (const event of hotEvents) {
|
|
657
|
+
if (event.success) {
|
|
658
|
+
const entry = this.tieredStorageOrchestrator.getIndexEntry(event.key);
|
|
659
|
+
if (entry)
|
|
660
|
+
bytesFreed += entry.size;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
// Demote from warm tier if critical or oom
|
|
665
|
+
if (levelConfig.warmDemoteCount > 0 && (level === 'critical' || level === 'oom')) {
|
|
666
|
+
const warmEvents = await this.tieredStorageOrchestrator.runDemotionCycle(levelConfig.warmDemoteCount);
|
|
667
|
+
allDemotions.push(...warmEvents);
|
|
668
|
+
}
|
|
669
|
+
// Update metrics
|
|
670
|
+
const successCount = allDemotions.filter((e) => e.success).length;
|
|
671
|
+
const failCount = allDemotions.filter((e) => !e.success).length;
|
|
672
|
+
this.demotionMetrics.pressureTriggeredDemotions += successCount;
|
|
673
|
+
this.demotionMetrics.demotionsByPressureLevel[level] += successCount;
|
|
674
|
+
this.demotionMetrics.failedDemotions += failCount;
|
|
675
|
+
this.demotionMetrics.bytesFreedByDemotion += bytesFreed;
|
|
676
|
+
this.demotionMetrics.lastDemotionTimestamp = now;
|
|
677
|
+
// Emit demotion event
|
|
678
|
+
if (allDemotions.length > 0) {
|
|
679
|
+
const event = {
|
|
680
|
+
pressureLevel: level,
|
|
681
|
+
demotions: allDemotions,
|
|
682
|
+
totalDemoted: successCount,
|
|
683
|
+
timestamp: new Date(),
|
|
684
|
+
};
|
|
685
|
+
for (const listener of this.pressureDemotionListeners) {
|
|
686
|
+
try {
|
|
687
|
+
listener(event);
|
|
688
|
+
}
|
|
689
|
+
catch {
|
|
690
|
+
// Ignore listener errors
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
catch (error) {
|
|
696
|
+
this.demotionMetrics.failedDemotions++;
|
|
697
|
+
console.error('Pressure-triggered demotion failed:', error);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
// ===========================================================================
|
|
701
|
+
// Monitoring Control
|
|
702
|
+
// ===========================================================================
|
|
703
|
+
/**
|
|
704
|
+
* Start periodic memory monitoring.
|
|
705
|
+
*
|
|
706
|
+
* Checks memory pressure at the configured interval and takes
|
|
707
|
+
* automatic actions when thresholds are crossed.
|
|
708
|
+
*/
|
|
709
|
+
startMonitoring() {
|
|
710
|
+
if (!this.config.enabled || this.checkInterval)
|
|
711
|
+
return;
|
|
712
|
+
this.checkInterval = setInterval(() => {
|
|
713
|
+
this.checkMemoryPressure();
|
|
714
|
+
}, this.config.checkIntervalMs);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Stop periodic memory monitoring.
|
|
718
|
+
*/
|
|
719
|
+
stopMonitoring() {
|
|
720
|
+
if (this.checkInterval) {
|
|
721
|
+
clearInterval(this.checkInterval);
|
|
722
|
+
this.checkInterval = null;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
// ===========================================================================
|
|
726
|
+
// Memory Stats & Pressure Detection
|
|
727
|
+
// ===========================================================================
|
|
728
|
+
/**
|
|
729
|
+
* Get current memory statistics.
|
|
730
|
+
*
|
|
731
|
+
* @returns Current memory stats including pressure level
|
|
732
|
+
*/
|
|
733
|
+
getMemoryStats() {
|
|
734
|
+
const heapUsed = this.getCurrentHeapUsage();
|
|
735
|
+
const heapLimit = this.config.memoryLimitBytes;
|
|
736
|
+
const usagePercent = (heapUsed / heapLimit) * 100;
|
|
737
|
+
const pressureLevel = getPressureLevelFromPercent(usagePercent, this.config);
|
|
738
|
+
return {
|
|
739
|
+
heapUsed,
|
|
740
|
+
heapLimit,
|
|
741
|
+
usagePercent,
|
|
742
|
+
pressureLevel,
|
|
743
|
+
timestamp: new Date(),
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Get current memory pressure level.
|
|
748
|
+
*
|
|
749
|
+
* This calculates the level based on current heap usage,
|
|
750
|
+
* not the cached lastPressureLevel.
|
|
751
|
+
*
|
|
752
|
+
* @returns Current pressure level
|
|
753
|
+
*/
|
|
754
|
+
getPressureLevel() {
|
|
755
|
+
return this.getMemoryStats().pressureLevel;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Check if current pressure is at or above a threshold.
|
|
759
|
+
*
|
|
760
|
+
* Uses the cached lastPressureLevel for efficiency.
|
|
761
|
+
*
|
|
762
|
+
* @param level - Level to compare against
|
|
763
|
+
* @returns True if current pressure >= level
|
|
764
|
+
*/
|
|
765
|
+
isPressureAtOrAbove(level) {
|
|
766
|
+
return getPressureLevelIndex(this.lastPressureLevel) >= getPressureLevelIndex(level);
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Get current heap usage from the best available source.
|
|
770
|
+
*
|
|
771
|
+
* Priority:
|
|
772
|
+
* 1. Forced heap usage (for testing)
|
|
773
|
+
* 2. Simulated heap (for testing)
|
|
774
|
+
* 3. WASM module HEAPU8 (sampled)
|
|
775
|
+
* 4. Estimation fallback
|
|
776
|
+
*/
|
|
777
|
+
getCurrentHeapUsage() {
|
|
778
|
+
// Testing overrides
|
|
779
|
+
if (this.forcedHeapUsage !== null)
|
|
780
|
+
return this.forcedHeapUsage;
|
|
781
|
+
if (this.simulatedHeapBytes > 0)
|
|
782
|
+
return this.simulatedHeapBytes;
|
|
783
|
+
// Real WASM heap measurement via sampling
|
|
784
|
+
if (this.wasmModule?.HEAPU8) {
|
|
785
|
+
return this.sampleHeapUsage(this.wasmModule.HEAPU8);
|
|
786
|
+
}
|
|
787
|
+
// Fallback estimation
|
|
788
|
+
return estimateHeapUsage();
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Sample WASM heap to estimate used bytes.
|
|
792
|
+
*
|
|
793
|
+
* Optimized: Uses bit-shift for stride calculation (avoiding division),
|
|
794
|
+
* reduced sample size for lower overhead, and unrolled loop for better
|
|
795
|
+
* CPU branch prediction.
|
|
796
|
+
*/
|
|
797
|
+
sampleHeapUsage(heap) {
|
|
798
|
+
const len = heap.length;
|
|
799
|
+
if (len === 0)
|
|
800
|
+
return 0;
|
|
801
|
+
// Use bit shift for fast stride calculation (4KB stride = 2^12)
|
|
802
|
+
// This gives us len >> 12 samples, capped at HEAP_SAMPLE_SIZE
|
|
803
|
+
const stride = Math.max(1, len >> 8); // ~256 samples for typical heaps
|
|
804
|
+
let nonZeroCount = 0;
|
|
805
|
+
let sampledCount = 0;
|
|
806
|
+
// Unrolled loop: check 4 positions per iteration when possible
|
|
807
|
+
const end = len - (stride * 3);
|
|
808
|
+
let i = 0;
|
|
809
|
+
for (; i < end; i += stride * 4) {
|
|
810
|
+
if (heap[i] !== 0)
|
|
811
|
+
nonZeroCount++;
|
|
812
|
+
if (heap[i + stride] !== 0)
|
|
813
|
+
nonZeroCount++;
|
|
814
|
+
if (heap[i + stride * 2] !== 0)
|
|
815
|
+
nonZeroCount++;
|
|
816
|
+
if (heap[i + stride * 3] !== 0)
|
|
817
|
+
nonZeroCount++;
|
|
818
|
+
sampledCount += 4;
|
|
819
|
+
}
|
|
820
|
+
// Handle remaining samples
|
|
821
|
+
for (; i < len; i += stride) {
|
|
822
|
+
if (heap[i] !== 0)
|
|
823
|
+
nonZeroCount++;
|
|
824
|
+
sampledCount++;
|
|
825
|
+
}
|
|
826
|
+
if (sampledCount === 0)
|
|
827
|
+
return 0;
|
|
828
|
+
// Use integer math where possible
|
|
829
|
+
return ((len * nonZeroCount) / sampledCount) | 0;
|
|
830
|
+
}
|
|
831
|
+
// ===========================================================================
|
|
832
|
+
// Pressure Check & Response
|
|
833
|
+
// ===========================================================================
|
|
834
|
+
/**
|
|
835
|
+
* Check current memory pressure and take appropriate actions.
|
|
836
|
+
*
|
|
837
|
+
* This is called periodically by startMonitoring() and can also
|
|
838
|
+
* be called manually before expensive operations.
|
|
839
|
+
*
|
|
840
|
+
* @returns Pressure event if level changed, null otherwise
|
|
841
|
+
*/
|
|
842
|
+
async checkMemoryPressure() {
|
|
843
|
+
if (!this.config.enabled)
|
|
844
|
+
return null;
|
|
845
|
+
const stats = this.getMemoryStats();
|
|
846
|
+
const previousLevel = this.lastPressureLevel;
|
|
847
|
+
const currentLevel = stats.pressureLevel;
|
|
848
|
+
const now = Date.now();
|
|
849
|
+
// Always record metrics
|
|
850
|
+
this.recordMetricSample();
|
|
851
|
+
// No change - handle sustained pressure check
|
|
852
|
+
if (previousLevel === currentLevel) {
|
|
853
|
+
this.handleSustainedPressure(currentLevel, now);
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
// Level changed - update tracking
|
|
857
|
+
this.updatePressureDuration(previousLevel, now);
|
|
858
|
+
this.lastPressureLevel = currentLevel;
|
|
859
|
+
this.sustainedPressureAlerted = false;
|
|
860
|
+
// Handle transitions
|
|
861
|
+
const previousIndex = getPressureLevelIndex(previousLevel);
|
|
862
|
+
const currentIndex = getPressureLevelIndex(currentLevel);
|
|
863
|
+
const actionsTaken = [];
|
|
864
|
+
if (currentIndex < previousIndex) {
|
|
865
|
+
// Pressure dropped - handle recovery
|
|
866
|
+
this.handleRecovery(previousLevel, currentLevel, now);
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
// Pressure rose - exit recovery mode
|
|
870
|
+
this.exitRecoveryMode();
|
|
871
|
+
}
|
|
872
|
+
// Take actions based on new pressure level
|
|
873
|
+
if (currentLevel !== 'normal') {
|
|
874
|
+
actionsTaken.push(...(await this.handleElevatedPressure(currentLevel, now)));
|
|
875
|
+
}
|
|
876
|
+
// Emit event
|
|
877
|
+
const event = {
|
|
878
|
+
previousLevel,
|
|
879
|
+
currentLevel,
|
|
880
|
+
stats,
|
|
881
|
+
actionsTaken,
|
|
882
|
+
timestamp: new Date(),
|
|
883
|
+
};
|
|
884
|
+
this.emitPressureEvent(event);
|
|
885
|
+
return event;
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Handle sustained pressure alerts.
|
|
889
|
+
*/
|
|
890
|
+
handleSustainedPressure(level, now) {
|
|
891
|
+
if (level === 'normal' || this.sustainedPressureAlerted)
|
|
892
|
+
return;
|
|
893
|
+
const threshold = this.extendedConfig.sustainedPressureThresholdMs ?? DEFAULT_SUSTAINED_PRESSURE_THRESHOLD_MS;
|
|
894
|
+
const elapsed = now - this.pressureLevelStartTime;
|
|
895
|
+
if (elapsed >= threshold && this.extendedConfig.onSustainedPressure) {
|
|
896
|
+
this.extendedConfig.onSustainedPressure(level, elapsed);
|
|
897
|
+
this.sustainedPressureAlerted = true;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Handle recovery from elevated pressure.
|
|
902
|
+
*/
|
|
903
|
+
handleRecovery(fromLevel, toLevel, now) {
|
|
904
|
+
this.inRecoveryMode = true;
|
|
905
|
+
this.recoveryStartTime = now;
|
|
906
|
+
this.recoveryProgress = 0;
|
|
907
|
+
if (toLevel === 'normal') {
|
|
908
|
+
this.resumePausedOperations();
|
|
909
|
+
if (this.extendedConfig.autoDisableFeatures) {
|
|
910
|
+
this.enableFeaturesAfterRecovery();
|
|
911
|
+
}
|
|
912
|
+
const elapsed = now - this.pressureLevelStartTime;
|
|
913
|
+
const recoveryEvent = {
|
|
914
|
+
fromLevel,
|
|
915
|
+
toLevel,
|
|
916
|
+
recoveryDurationMs: elapsed,
|
|
917
|
+
};
|
|
918
|
+
this.extendedConfig.onRecovery?.(recoveryEvent);
|
|
919
|
+
this.recoveryListeners.forEach((listener) => listener(recoveryEvent));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Exit recovery mode when pressure rises.
|
|
924
|
+
*/
|
|
925
|
+
exitRecoveryMode() {
|
|
926
|
+
this.inRecoveryMode = false;
|
|
927
|
+
this.recoveryProgress = 0;
|
|
928
|
+
this.recoveryStartTime = null;
|
|
929
|
+
this.pauseGradualRecovery();
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Handle actions for elevated pressure levels.
|
|
933
|
+
* Optimized: Uses adaptive GC timing based on pressure level and memory growth rate.
|
|
934
|
+
*/
|
|
935
|
+
async handleElevatedPressure(level, now) {
|
|
936
|
+
const actionsTaken = [];
|
|
937
|
+
// GC hint with adaptive timing based on pressure level
|
|
938
|
+
if (this.extendedConfig.onGCHint) {
|
|
939
|
+
const minInterval = this.getAdaptiveGCInterval(level, now);
|
|
940
|
+
if (now - this.lastGCHintTime >= minInterval) {
|
|
941
|
+
this.lastGCHintTime = now;
|
|
942
|
+
const aggressive = level === 'critical' || level === 'oom';
|
|
943
|
+
this.extendedConfig.onGCHint(level, { aggressive });
|
|
944
|
+
actionsTaken.push('gc_triggered');
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
// Cache eviction
|
|
948
|
+
if (this.extendedConfig.autoCacheEviction && this.cacheManager) {
|
|
949
|
+
const evicted = this.evictCache(level);
|
|
950
|
+
if (evicted)
|
|
951
|
+
actionsTaken.push('cache_cleared');
|
|
952
|
+
}
|
|
953
|
+
// Feature disabling
|
|
954
|
+
if ((level === 'critical' || level === 'oom') && this.extendedConfig.autoDisableFeatures) {
|
|
955
|
+
this.disableFeaturesUnderPressure();
|
|
956
|
+
}
|
|
957
|
+
// Extension unloading
|
|
958
|
+
if ((level === 'critical' || level === 'oom') && this.config.autoUnloadExtensions && this.pglite) {
|
|
959
|
+
const result = await this.unloadLeastImportantExtension();
|
|
960
|
+
if (result.success)
|
|
961
|
+
actionsTaken.push('extension_unloaded');
|
|
962
|
+
actionsTaken.push('warning_emitted');
|
|
963
|
+
}
|
|
964
|
+
return actionsTaken;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Calculate adaptive GC interval based on pressure level and memory growth rate.
|
|
968
|
+
*
|
|
969
|
+
* - At OOM: Minimal delay (500ms) to maximize chances of freeing memory
|
|
970
|
+
* - At critical: Reduced delay (2s) with faster triggering if memory is growing rapidly
|
|
971
|
+
* - At warning: Standard delay (5s) unless memory is growing rapidly
|
|
972
|
+
*
|
|
973
|
+
* @param level - Current pressure level
|
|
974
|
+
* @param now - Current timestamp
|
|
975
|
+
* @returns Minimum interval in milliseconds before next GC hint
|
|
976
|
+
*/
|
|
977
|
+
getAdaptiveGCInterval(level, _now) {
|
|
978
|
+
const baseInterval = this.extendedConfig.gcHintMinIntervalMs ?? DEFAULT_GC_HINT_MIN_INTERVAL_MS;
|
|
979
|
+
// At OOM, be very aggressive
|
|
980
|
+
if (level === 'oom') {
|
|
981
|
+
return Math.min(500, baseInterval);
|
|
982
|
+
}
|
|
983
|
+
// At critical, reduce interval and consider growth rate
|
|
984
|
+
if (level === 'critical') {
|
|
985
|
+
const growthRate = this.getMemoryGrowthRate();
|
|
986
|
+
// If memory is growing rapidly (>5MB/s), be more aggressive
|
|
987
|
+
if (growthRate.bytesPerSecond > 5 * 1024 * 1024) {
|
|
988
|
+
return Math.min(1000, baseInterval / 2);
|
|
989
|
+
}
|
|
990
|
+
return Math.min(2000, baseInterval);
|
|
991
|
+
}
|
|
992
|
+
// At warning level, check if we should trigger earlier based on OOM prediction
|
|
993
|
+
if (level === 'warning') {
|
|
994
|
+
const prediction = this.predictTimeToOOM();
|
|
995
|
+
// If OOM predicted within 30 seconds with high confidence, reduce interval
|
|
996
|
+
if (prediction.willOOM && prediction.estimatedMs < 30000 && prediction.confidence > 0.7) {
|
|
997
|
+
return Math.min(2000, baseInterval / 2);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
return baseInterval;
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Evict cache entries based on pressure level.
|
|
1004
|
+
*/
|
|
1005
|
+
evictCache(level) {
|
|
1006
|
+
if (!this.cacheManager)
|
|
1007
|
+
return false;
|
|
1008
|
+
const evictionTargets = this.extendedConfig.evictionTargetPercent ?? DEFAULT_EVICTION_TARGET_PERCENT;
|
|
1009
|
+
const targetPercent = evictionTargets[level] ?? 0;
|
|
1010
|
+
if (targetPercent <= 0)
|
|
1011
|
+
return false;
|
|
1012
|
+
const totalCacheSize = this.cacheManager.getTotalSize();
|
|
1013
|
+
const targetBytes = Math.floor(totalCacheSize * (targetPercent / 100));
|
|
1014
|
+
if (level === 'oom' && this.cacheManager.clear) {
|
|
1015
|
+
this.cacheManager.clear();
|
|
1016
|
+
return true;
|
|
1017
|
+
}
|
|
1018
|
+
if (this.cacheManager.evictLRU) {
|
|
1019
|
+
this.cacheManager.evictLRU(targetBytes);
|
|
1020
|
+
return true;
|
|
1021
|
+
}
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Update pressure duration tracking.
|
|
1026
|
+
*/
|
|
1027
|
+
updatePressureDuration(level, now) {
|
|
1028
|
+
const elapsed = now - this.pressureLevelStartTime;
|
|
1029
|
+
this.pressureDurations[level] += elapsed;
|
|
1030
|
+
this.pressureLevelStartTime = now;
|
|
1031
|
+
}
|
|
1032
|
+
// ===========================================================================
|
|
1033
|
+
// Extension Management
|
|
1034
|
+
// ===========================================================================
|
|
1035
|
+
/**
|
|
1036
|
+
* Unload the least important extension to free memory.
|
|
1037
|
+
*
|
|
1038
|
+
* Uses the extensionUnloadPriority list to determine which extension
|
|
1039
|
+
* to drop first.
|
|
1040
|
+
*
|
|
1041
|
+
* @returns Result of the unload operation
|
|
1042
|
+
*/
|
|
1043
|
+
async unloadLeastImportantExtension() {
|
|
1044
|
+
if (!this.pglite) {
|
|
1045
|
+
return { action: 'extension_unloaded', success: false, error: 'PGlite not available' };
|
|
1046
|
+
}
|
|
1047
|
+
// Get installed extensions
|
|
1048
|
+
let installedExtensions;
|
|
1049
|
+
try {
|
|
1050
|
+
const result = await this.pglite.query(`SELECT extname FROM pg_extension WHERE extname != 'plpgsql'`);
|
|
1051
|
+
installedExtensions = new Set(result.rows
|
|
1052
|
+
.map((r) => r.extname)
|
|
1053
|
+
.filter((name) => !this.unloadedExtensions.has(name)));
|
|
1054
|
+
}
|
|
1055
|
+
catch (error) {
|
|
1056
|
+
return {
|
|
1057
|
+
action: 'extension_unloaded',
|
|
1058
|
+
success: false,
|
|
1059
|
+
error: `Failed to get extensions: ${error instanceof Error ? error.message : 'Unknown'}`,
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
// Find and unload the least important
|
|
1063
|
+
for (const extName of this.config.extensionUnloadPriority) {
|
|
1064
|
+
if (installedExtensions.has(extName)) {
|
|
1065
|
+
try {
|
|
1066
|
+
await this.pglite.query(`DROP EXTENSION IF EXISTS "${extName}" CASCADE`);
|
|
1067
|
+
this.unloadedExtensions.add(extName);
|
|
1068
|
+
return { action: 'extension_unloaded', success: true, extensionName: extName };
|
|
1069
|
+
}
|
|
1070
|
+
catch (error) {
|
|
1071
|
+
return {
|
|
1072
|
+
action: 'extension_unloaded',
|
|
1073
|
+
success: false,
|
|
1074
|
+
extensionName: extName,
|
|
1075
|
+
error: `Failed to unload: ${error instanceof Error ? error.message : 'Unknown'}`,
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return { action: 'extension_unloaded', success: false, error: 'No extensions available to unload' };
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Get list of extensions unloaded due to memory pressure.
|
|
1084
|
+
*/
|
|
1085
|
+
getUnloadedExtensions() {
|
|
1086
|
+
return Array.from(this.unloadedExtensions);
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Check if an extension was unloaded due to memory pressure.
|
|
1090
|
+
*/
|
|
1091
|
+
wasExtensionUnloaded(extensionName) {
|
|
1092
|
+
return this.unloadedExtensions.has(extensionName);
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Clear the list of unloaded extensions.
|
|
1096
|
+
*/
|
|
1097
|
+
clearUnloadedExtensions() {
|
|
1098
|
+
this.unloadedExtensions.clear();
|
|
1099
|
+
}
|
|
1100
|
+
// ===========================================================================
|
|
1101
|
+
// GC Hints
|
|
1102
|
+
// ===========================================================================
|
|
1103
|
+
/**
|
|
1104
|
+
* Request a garbage collection hint.
|
|
1105
|
+
*
|
|
1106
|
+
* Attempts to trigger GC if available and tracks effectiveness.
|
|
1107
|
+
* Optimized: Uses performance.now() for higher precision timing.
|
|
1108
|
+
*
|
|
1109
|
+
* @returns Result including bytes freed estimate
|
|
1110
|
+
*/
|
|
1111
|
+
async requestGC() {
|
|
1112
|
+
const startTime = performance.now();
|
|
1113
|
+
const heapBefore = this.getCurrentHeapUsage();
|
|
1114
|
+
let triggered = false;
|
|
1115
|
+
// Try to trigger GC if available
|
|
1116
|
+
const gc = globalThis.gc;
|
|
1117
|
+
if (typeof gc === 'function') {
|
|
1118
|
+
try {
|
|
1119
|
+
gc();
|
|
1120
|
+
triggered = true;
|
|
1121
|
+
}
|
|
1122
|
+
catch {
|
|
1123
|
+
// GC not available or failed
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
const durationMs = performance.now() - startTime;
|
|
1127
|
+
const heapAfter = this.getCurrentHeapUsage();
|
|
1128
|
+
const bytesFreedEstimate = Math.max(0, heapBefore - heapAfter);
|
|
1129
|
+
// Track metrics using ring buffers
|
|
1130
|
+
this.gcMetrics.hints++;
|
|
1131
|
+
this.gcMetrics.bytesFreed.push(bytesFreedEstimate);
|
|
1132
|
+
this.gcMetrics.durations.push(durationMs);
|
|
1133
|
+
return { triggered, bytesFreedEstimate, durationMs };
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Get aggregated GC metrics.
|
|
1137
|
+
* Optimized: Uses ring buffer's toArray() for efficient conversion.
|
|
1138
|
+
*/
|
|
1139
|
+
getGCMetrics() {
|
|
1140
|
+
const { hints, bytesFreed, durations } = this.gcMetrics;
|
|
1141
|
+
const bytesFreedArray = bytesFreed.toArray();
|
|
1142
|
+
const durationsArray = durations.toArray();
|
|
1143
|
+
// Count successful GC hints (where memory was actually freed)
|
|
1144
|
+
let successCount = 0;
|
|
1145
|
+
for (let i = 0; i < bytesFreedArray.length; i++) {
|
|
1146
|
+
if ((bytesFreedArray[i] ?? 0) > 0)
|
|
1147
|
+
successCount++;
|
|
1148
|
+
}
|
|
1149
|
+
return {
|
|
1150
|
+
totalHints: hints,
|
|
1151
|
+
avgBytesFreed: average(bytesFreedArray),
|
|
1152
|
+
avgDurationMs: average(durationsArray),
|
|
1153
|
+
successRate: hints > 0 ? successCount / Math.min(hints, bytesFreedArray.length) : 0,
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
// ===========================================================================
|
|
1157
|
+
// Metrics & History
|
|
1158
|
+
// ===========================================================================
|
|
1159
|
+
/**
|
|
1160
|
+
* Record a metric sample for history tracking.
|
|
1161
|
+
*
|
|
1162
|
+
* Also detects memory spikes, updates EMA state, and emits events.
|
|
1163
|
+
* Optimized: Uses ring buffer (no shift operations).
|
|
1164
|
+
*/
|
|
1165
|
+
recordMetricSample() {
|
|
1166
|
+
const stats = this.getMemoryStats();
|
|
1167
|
+
const sample = {
|
|
1168
|
+
timestamp: new Date(),
|
|
1169
|
+
heapUsed: stats.heapUsed,
|
|
1170
|
+
pressureLevel: stats.pressureLevel,
|
|
1171
|
+
};
|
|
1172
|
+
// Add to ring buffer (automatically handles size limit)
|
|
1173
|
+
this.metricsHistory.push(sample);
|
|
1174
|
+
// Update smoothed state for EMA-based detection
|
|
1175
|
+
this.updateSmoothedState(stats.heapUsed, PRESSURE_LEVEL_INDEX[stats.pressureLevel]);
|
|
1176
|
+
// Spike detection
|
|
1177
|
+
if (this.lastHeapSample > 0 && stats.heapUsed - this.lastHeapSample >= SPIKE_DETECTION_THRESHOLD_BYTES) {
|
|
1178
|
+
const spike = {
|
|
1179
|
+
deltaBytes: stats.heapUsed - this.lastHeapSample,
|
|
1180
|
+
previousBytes: this.lastHeapSample,
|
|
1181
|
+
currentBytes: stats.heapUsed,
|
|
1182
|
+
timestamp: new Date(),
|
|
1183
|
+
};
|
|
1184
|
+
// Use for loop instead of forEach for better performance
|
|
1185
|
+
const listeners = this.memorySpikeListeners;
|
|
1186
|
+
for (let i = 0; i < listeners.length; i++) {
|
|
1187
|
+
const listener = listeners[i];
|
|
1188
|
+
if (listener)
|
|
1189
|
+
listener(spike);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
this.lastHeapSample = stats.heapUsed;
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Update smoothed pressure state using EMA.
|
|
1196
|
+
* This provides noise-resistant pressure tracking.
|
|
1197
|
+
*/
|
|
1198
|
+
updateSmoothedState(rawHeapUsage, rawPressureLevel) {
|
|
1199
|
+
if (!this.smoothedStateInitialized) {
|
|
1200
|
+
// Initialize with first sample
|
|
1201
|
+
this.smoothedState = {
|
|
1202
|
+
emaHeapUsage: rawHeapUsage,
|
|
1203
|
+
emaPressureLevel: rawPressureLevel,
|
|
1204
|
+
recentVariance: 0,
|
|
1205
|
+
trend: 'stable',
|
|
1206
|
+
lastRawHeapUsage: rawHeapUsage,
|
|
1207
|
+
};
|
|
1208
|
+
this.smoothedStateInitialized = true;
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
// Update EMAs
|
|
1212
|
+
this.smoothedState.emaHeapUsage = updateEMA(this.smoothedState.emaHeapUsage, rawHeapUsage);
|
|
1213
|
+
this.smoothedState.emaPressureLevel = updateEMA(this.smoothedState.emaPressureLevel, rawPressureLevel);
|
|
1214
|
+
// Calculate variance from recent samples
|
|
1215
|
+
if (this.metricsHistory.size >= 3) {
|
|
1216
|
+
const recentSamples = this.metricsHistory.toArray().slice(-10);
|
|
1217
|
+
const heapValues = recentSamples.map((s) => s.heapUsed);
|
|
1218
|
+
this.smoothedState.recentVariance = variance(heapValues);
|
|
1219
|
+
}
|
|
1220
|
+
// Determine trend from delta
|
|
1221
|
+
const delta = rawHeapUsage - this.smoothedState.lastRawHeapUsage;
|
|
1222
|
+
if (delta > STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC) {
|
|
1223
|
+
this.smoothedState.trend = 'rising';
|
|
1224
|
+
}
|
|
1225
|
+
else if (delta < -STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC) {
|
|
1226
|
+
this.smoothedState.trend = 'falling';
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
this.smoothedState.trend = 'stable';
|
|
1230
|
+
}
|
|
1231
|
+
this.smoothedState.lastRawHeapUsage = rawHeapUsage;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Get the smoothed pressure state.
|
|
1235
|
+
* Useful for observability and debugging.
|
|
1236
|
+
*/
|
|
1237
|
+
getSmoothedPressureState() {
|
|
1238
|
+
return { ...this.smoothedState };
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Get the smoothed pressure level (EMA-based).
|
|
1242
|
+
* This is less sensitive to momentary spikes than raw pressure level.
|
|
1243
|
+
*/
|
|
1244
|
+
getSmoothedPressureLevel() {
|
|
1245
|
+
const levelIndex = Math.round(this.smoothedState.emaPressureLevel);
|
|
1246
|
+
const levels = ['normal', 'warning', 'critical', 'oom'];
|
|
1247
|
+
return levels[Math.max(0, Math.min(3, levelIndex))] ?? 'normal';
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Check if pressure is volatile (high variance in recent samples).
|
|
1251
|
+
* Useful for deciding whether to take aggressive action.
|
|
1252
|
+
*/
|
|
1253
|
+
isPressureVolatile() {
|
|
1254
|
+
const stats = this.getMemoryStats();
|
|
1255
|
+
// Coefficient of variation > 10% indicates volatility
|
|
1256
|
+
const cv = stats.heapUsed > 0
|
|
1257
|
+
? Math.sqrt(this.smoothedState.recentVariance) / stats.heapUsed
|
|
1258
|
+
: 0;
|
|
1259
|
+
return cv > 0.1;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Get adaptive thresholds based on current memory behavior.
|
|
1263
|
+
*
|
|
1264
|
+
* Adjusts thresholds based on:
|
|
1265
|
+
* 1. Memory growth rate - faster growth = lower thresholds
|
|
1266
|
+
* 2. Volatility - high volatility = higher thresholds (less sensitive)
|
|
1267
|
+
* 3. OOM prediction - imminent OOM = lower thresholds
|
|
1268
|
+
*/
|
|
1269
|
+
getAdaptiveThresholds() {
|
|
1270
|
+
const baseConfig = this.config;
|
|
1271
|
+
// Return base thresholds if insufficient data
|
|
1272
|
+
if (this.metricsHistory.size < MIN_SAMPLES_FOR_TREND) {
|
|
1273
|
+
return {
|
|
1274
|
+
warningThresholdPercent: baseConfig.warningThresholdPercent,
|
|
1275
|
+
criticalThresholdPercent: baseConfig.criticalThresholdPercent,
|
|
1276
|
+
oomThresholdPercent: baseConfig.oomThresholdPercent,
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
const growthRate = this.getMemoryGrowthRate();
|
|
1280
|
+
const oomPrediction = this.predictTimeToOOM();
|
|
1281
|
+
let warningAdjustment = 0;
|
|
1282
|
+
let criticalAdjustment = 0;
|
|
1283
|
+
// Adjust for growth rate
|
|
1284
|
+
if (growthRate.bytesPerSecond > RAPID_GROWTH_THRESHOLD_BYTES_PER_SEC) {
|
|
1285
|
+
// Rapid growth - lower thresholds by up to 10%
|
|
1286
|
+
warningAdjustment -= 10;
|
|
1287
|
+
criticalAdjustment -= 5;
|
|
1288
|
+
}
|
|
1289
|
+
else if (growthRate.trend === 'increasing') {
|
|
1290
|
+
// Moderate growth - lower thresholds by up to 5%
|
|
1291
|
+
warningAdjustment -= 5;
|
|
1292
|
+
criticalAdjustment -= 3;
|
|
1293
|
+
}
|
|
1294
|
+
// Adjust for volatility (increase thresholds if volatile to reduce false alarms)
|
|
1295
|
+
if (this.isPressureVolatile()) {
|
|
1296
|
+
warningAdjustment += 3;
|
|
1297
|
+
criticalAdjustment += 2;
|
|
1298
|
+
}
|
|
1299
|
+
// Adjust for imminent OOM prediction
|
|
1300
|
+
if (oomPrediction.willOOM && (oomPrediction.rSquared ?? 0) > 0.5) {
|
|
1301
|
+
warningAdjustment -= 5;
|
|
1302
|
+
criticalAdjustment -= 3;
|
|
1303
|
+
}
|
|
1304
|
+
// Apply adjustments with clamping
|
|
1305
|
+
const warningThresholdPercent = Math.max(50, Math.min(80, baseConfig.warningThresholdPercent + warningAdjustment));
|
|
1306
|
+
const criticalThresholdPercent = Math.max(warningThresholdPercent + 5, Math.min(90, baseConfig.criticalThresholdPercent + criticalAdjustment));
|
|
1307
|
+
const oomThresholdPercent = Math.max(criticalThresholdPercent + 5, baseConfig.oomThresholdPercent);
|
|
1308
|
+
return { warningThresholdPercent, criticalThresholdPercent, oomThresholdPercent };
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Get pressure level using adaptive thresholds.
|
|
1312
|
+
* This provides more intelligent pressure detection based on memory behavior.
|
|
1313
|
+
*/
|
|
1314
|
+
getAdaptivePressureLevel() {
|
|
1315
|
+
const stats = this.getMemoryStats();
|
|
1316
|
+
const adaptiveThresholds = this.getAdaptiveThresholds();
|
|
1317
|
+
return getPressureLevelFromPercent(stats.usagePercent, adaptiveThresholds);
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Get metrics history.
|
|
1321
|
+
* Returns a copy of the samples array.
|
|
1322
|
+
*/
|
|
1323
|
+
getMetricsHistory() {
|
|
1324
|
+
return { samples: this.metricsHistory.toArray() };
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Calculate memory growth rate using linear regression.
|
|
1328
|
+
*
|
|
1329
|
+
* Optimized improvements over simple first/last calculation:
|
|
1330
|
+
* 1. Uses all samples via linear regression for more accurate trend
|
|
1331
|
+
* 2. Filters out noise using configurable threshold
|
|
1332
|
+
* 3. Provides confidence via R-squared value
|
|
1333
|
+
*
|
|
1334
|
+
* Falls back to simple calculation if insufficient samples.
|
|
1335
|
+
*/
|
|
1336
|
+
getMemoryGrowthRate() {
|
|
1337
|
+
const historySize = this.metricsHistory.size;
|
|
1338
|
+
if (historySize < 2) {
|
|
1339
|
+
return { bytesPerSecond: 0, trend: 'stable' };
|
|
1340
|
+
}
|
|
1341
|
+
// For small sample counts, use simple first/last calculation
|
|
1342
|
+
if (historySize < MIN_SAMPLES_FOR_TREND) {
|
|
1343
|
+
const first = this.metricsHistory.first();
|
|
1344
|
+
const last = this.metricsHistory.last();
|
|
1345
|
+
if (!first || !last)
|
|
1346
|
+
return { bytesPerSecond: 0, trend: 'stable' };
|
|
1347
|
+
const timeDeltaMs = last.timestamp.getTime() - first.timestamp.getTime();
|
|
1348
|
+
if (timeDeltaMs <= 0)
|
|
1349
|
+
return { bytesPerSecond: 0, trend: 'stable' };
|
|
1350
|
+
const bytesDelta = last.heapUsed - first.heapUsed;
|
|
1351
|
+
const bytesPerSecond = (bytesDelta / timeDeltaMs) * 1000;
|
|
1352
|
+
return {
|
|
1353
|
+
bytesPerSecond,
|
|
1354
|
+
trend: this.classifyGrowthTrend(bytesPerSecond),
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
// Use linear regression for more accurate trend analysis
|
|
1358
|
+
const samples = this.metricsHistory.toArray();
|
|
1359
|
+
const { slope, rSquared } = linearRegression(samples);
|
|
1360
|
+
// slope is in bytes/ms, convert to bytes/second
|
|
1361
|
+
const bytesPerSecond = slope * 1000;
|
|
1362
|
+
// If regression fit is poor (R² < 0.5), the trend is likely noisy/unstable
|
|
1363
|
+
// Use a higher threshold for stable classification
|
|
1364
|
+
const effectiveThreshold = rSquared < 0.5
|
|
1365
|
+
? STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC * 2
|
|
1366
|
+
: STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC;
|
|
1367
|
+
return {
|
|
1368
|
+
bytesPerSecond,
|
|
1369
|
+
trend: this.classifyGrowthTrend(bytesPerSecond, effectiveThreshold),
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Classify growth trend based on bytes per second.
|
|
1374
|
+
*/
|
|
1375
|
+
classifyGrowthTrend(bytesPerSecond, threshold = STABLE_GROWTH_THRESHOLD_BYTES_PER_SEC) {
|
|
1376
|
+
if (bytesPerSecond > threshold)
|
|
1377
|
+
return 'increasing';
|
|
1378
|
+
if (bytesPerSecond < -threshold)
|
|
1379
|
+
return 'decreasing';
|
|
1380
|
+
return 'stable';
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Predict time to OOM using linear regression and statistical analysis.
|
|
1384
|
+
*
|
|
1385
|
+
* Improved prediction algorithm:
|
|
1386
|
+
* 1. Uses linear regression for trend-based prediction
|
|
1387
|
+
* 2. Includes R-squared for confidence estimation
|
|
1388
|
+
* 3. Accounts for variance in samples (high variance = lower confidence)
|
|
1389
|
+
* 4. Predicts usage at OOM horizon for early warning
|
|
1390
|
+
*/
|
|
1391
|
+
predictTimeToOOM() {
|
|
1392
|
+
const stats = this.getMemoryStats();
|
|
1393
|
+
const historySize = this.metricsHistory.size;
|
|
1394
|
+
// Default return for insufficient data
|
|
1395
|
+
const defaultPrediction = {
|
|
1396
|
+
estimatedMs: Infinity,
|
|
1397
|
+
confidence: 0,
|
|
1398
|
+
willOOM: false,
|
|
1399
|
+
rSquared: 0,
|
|
1400
|
+
predictedUsageAtHorizon: stats.heapUsed,
|
|
1401
|
+
};
|
|
1402
|
+
if (historySize < 2) {
|
|
1403
|
+
return defaultPrediction;
|
|
1404
|
+
}
|
|
1405
|
+
const samples = this.metricsHistory.toArray();
|
|
1406
|
+
const { slope, intercept, rSquared } = linearRegression(samples);
|
|
1407
|
+
// slope is bytes/ms
|
|
1408
|
+
// If not growing (slope <= 0), no OOM predicted
|
|
1409
|
+
if (slope <= 0) {
|
|
1410
|
+
return {
|
|
1411
|
+
...defaultPrediction,
|
|
1412
|
+
rSquared,
|
|
1413
|
+
predictedUsageAtHorizon: Math.max(0, stats.heapUsed + slope * OOM_PREDICTION_HORIZON_MS),
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
// Calculate time to reach OOM threshold
|
|
1417
|
+
const oomThresholdBytes = (stats.heapLimit * this.config.oomThresholdPercent) / 100;
|
|
1418
|
+
const bytesRemaining = oomThresholdBytes - stats.heapUsed;
|
|
1419
|
+
if (bytesRemaining <= 0) {
|
|
1420
|
+
// Already at or above OOM threshold
|
|
1421
|
+
return {
|
|
1422
|
+
estimatedMs: 0,
|
|
1423
|
+
confidence: 1.0,
|
|
1424
|
+
willOOM: true,
|
|
1425
|
+
rSquared,
|
|
1426
|
+
predictedUsageAtHorizon: stats.heapUsed,
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
// Time to OOM in milliseconds: (oomThreshold - currentUsage) / slope
|
|
1430
|
+
const estimatedMs = bytesRemaining / slope;
|
|
1431
|
+
// Calculate confidence based on multiple factors:
|
|
1432
|
+
// 1. Sample count (more samples = more confidence)
|
|
1433
|
+
// 2. R-squared (better fit = more confidence)
|
|
1434
|
+
// 3. Variance consistency (less variance = more confidence)
|
|
1435
|
+
const sampleConfidence = Math.min(1.0, historySize / MIN_SAMPLES_FOR_OOM_PREDICTION);
|
|
1436
|
+
const fitConfidence = Math.max(0, rSquared); // R² is already 0-1
|
|
1437
|
+
// Calculate variance confidence
|
|
1438
|
+
const heapValues = samples.map((s) => s.heapUsed);
|
|
1439
|
+
const sampleVariance = variance(heapValues);
|
|
1440
|
+
const meanHeap = average(heapValues);
|
|
1441
|
+
// Coefficient of variation - lower is more consistent
|
|
1442
|
+
const cv = meanHeap > 0 ? Math.sqrt(sampleVariance) / meanHeap : 0;
|
|
1443
|
+
const varianceConfidence = Math.max(0, 1 - cv * 10); // Scale CV to 0-1 range
|
|
1444
|
+
// Combined confidence (weighted average)
|
|
1445
|
+
const confidence = Math.min(1.0, sampleConfidence * 0.3 +
|
|
1446
|
+
fitConfidence * 0.5 +
|
|
1447
|
+
varianceConfidence * 0.2);
|
|
1448
|
+
// Predict usage at the OOM horizon
|
|
1449
|
+
const now = Date.now();
|
|
1450
|
+
const horizonTime = now + OOM_PREDICTION_HORIZON_MS;
|
|
1451
|
+
const t0 = samples[0]?.timestamp.getTime() ?? now;
|
|
1452
|
+
const predictedUsageAtHorizon = Math.max(0, slope * (horizonTime - t0) + intercept);
|
|
1453
|
+
return {
|
|
1454
|
+
estimatedMs: Math.max(0, estimatedMs),
|
|
1455
|
+
confidence,
|
|
1456
|
+
willOOM: estimatedMs < OOM_PREDICTION_HORIZON_MS && confidence > 0.5,
|
|
1457
|
+
rSquared,
|
|
1458
|
+
predictedUsageAtHorizon,
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Get time spent at each pressure level.
|
|
1463
|
+
*/
|
|
1464
|
+
getPressureDurations() {
|
|
1465
|
+
const now = Date.now();
|
|
1466
|
+
const currentElapsed = now - this.pressureLevelStartTime;
|
|
1467
|
+
const durations = { ...this.pressureDurations };
|
|
1468
|
+
durations[this.lastPressureLevel] += currentElapsed;
|
|
1469
|
+
return durations;
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Export metrics in Prometheus format.
|
|
1473
|
+
* Enhanced: Includes heuristics metrics for better observability.
|
|
1474
|
+
*/
|
|
1475
|
+
exportPrometheusMetrics() {
|
|
1476
|
+
const stats = this.getMemoryStats();
|
|
1477
|
+
const gcMetrics = this.getGCMetrics();
|
|
1478
|
+
const demotionMetrics = this.getDemotionMetrics();
|
|
1479
|
+
const growthRate = this.getMemoryGrowthRate();
|
|
1480
|
+
const oomPrediction = this.predictTimeToOOM();
|
|
1481
|
+
const smoothedState = this.getSmoothedPressureState();
|
|
1482
|
+
const adaptiveThresholds = this.getAdaptiveThresholds();
|
|
1483
|
+
const levels = { normal: 0, warning: 1, critical: 2, oom: 3 };
|
|
1484
|
+
const trends = { decreasing: -1, stable: 0, increasing: 1 };
|
|
1485
|
+
return [
|
|
1486
|
+
// Core metrics
|
|
1487
|
+
`# HELP postgres_memory_heap_bytes Current WASM heap usage in bytes`,
|
|
1488
|
+
`# TYPE postgres_memory_heap_bytes gauge`,
|
|
1489
|
+
`postgres_memory_heap_bytes ${stats.heapUsed}`,
|
|
1490
|
+
`# HELP postgres_memory_pressure_level Current memory pressure level (0=normal, 1=warning, 2=critical, 3=oom)`,
|
|
1491
|
+
`# TYPE postgres_memory_pressure_level gauge`,
|
|
1492
|
+
`postgres_memory_pressure_level ${levels[stats.pressureLevel]}`,
|
|
1493
|
+
`# HELP postgres_memory_gc_count Total number of GC hints triggered`,
|
|
1494
|
+
`# TYPE postgres_memory_gc_count counter`,
|
|
1495
|
+
`postgres_memory_gc_count ${gcMetrics.totalHints}`,
|
|
1496
|
+
`# HELP postgres_memory_pressure_demotions_total Total number of pressure-triggered demotions`,
|
|
1497
|
+
`# TYPE postgres_memory_pressure_demotions_total counter`,
|
|
1498
|
+
`postgres_memory_pressure_demotions_total ${demotionMetrics.pressureTriggeredDemotions}`,
|
|
1499
|
+
`# HELP postgres_memory_pressure_demotions_failed Total number of failed pressure-triggered demotions`,
|
|
1500
|
+
`# TYPE postgres_memory_pressure_demotions_failed counter`,
|
|
1501
|
+
`postgres_memory_pressure_demotions_failed ${demotionMetrics.failedDemotions}`,
|
|
1502
|
+
// Heuristics metrics
|
|
1503
|
+
`# HELP postgres_memory_heap_ema_bytes Exponential moving average of heap usage`,
|
|
1504
|
+
`# TYPE postgres_memory_heap_ema_bytes gauge`,
|
|
1505
|
+
`postgres_memory_heap_ema_bytes ${Math.round(smoothedState.emaHeapUsage)}`,
|
|
1506
|
+
`# HELP postgres_memory_growth_rate_bytes_per_sec Memory growth rate in bytes per second`,
|
|
1507
|
+
`# TYPE postgres_memory_growth_rate_bytes_per_sec gauge`,
|
|
1508
|
+
`postgres_memory_growth_rate_bytes_per_sec ${Math.round(growthRate.bytesPerSecond)}`,
|
|
1509
|
+
`# HELP postgres_memory_growth_trend Memory growth trend (-1=decreasing, 0=stable, 1=increasing)`,
|
|
1510
|
+
`# TYPE postgres_memory_growth_trend gauge`,
|
|
1511
|
+
`postgres_memory_growth_trend ${trends[growthRate.trend] ?? 0}`,
|
|
1512
|
+
`# HELP postgres_memory_oom_prediction_ms Predicted time to OOM in milliseconds (-1 if not growing)`,
|
|
1513
|
+
`# TYPE postgres_memory_oom_prediction_ms gauge`,
|
|
1514
|
+
`postgres_memory_oom_prediction_ms ${oomPrediction.estimatedMs === Infinity ? -1 : Math.round(oomPrediction.estimatedMs)}`,
|
|
1515
|
+
`# HELP postgres_memory_oom_prediction_confidence Confidence in OOM prediction (0-1)`,
|
|
1516
|
+
`# TYPE postgres_memory_oom_prediction_confidence gauge`,
|
|
1517
|
+
`postgres_memory_oom_prediction_confidence ${oomPrediction.confidence.toFixed(3)}`,
|
|
1518
|
+
`# HELP postgres_memory_pressure_volatile Whether pressure readings are currently volatile (0=stable, 1=volatile)`,
|
|
1519
|
+
`# TYPE postgres_memory_pressure_volatile gauge`,
|
|
1520
|
+
`postgres_memory_pressure_volatile ${this.isPressureVolatile() ? 1 : 0}`,
|
|
1521
|
+
`# HELP postgres_memory_adaptive_warning_threshold Current adaptive warning threshold percentage`,
|
|
1522
|
+
`# TYPE postgres_memory_adaptive_warning_threshold gauge`,
|
|
1523
|
+
`postgres_memory_adaptive_warning_threshold ${adaptiveThresholds.warningThresholdPercent}`,
|
|
1524
|
+
`# HELP postgres_memory_adaptive_critical_threshold Current adaptive critical threshold percentage`,
|
|
1525
|
+
`# TYPE postgres_memory_adaptive_critical_threshold gauge`,
|
|
1526
|
+
`postgres_memory_adaptive_critical_threshold ${adaptiveThresholds.criticalThresholdPercent}`,
|
|
1527
|
+
].join('\n');
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Export metrics in JSON format.
|
|
1531
|
+
* Optimized: Calculates aggregates in a single pass.
|
|
1532
|
+
* Enhanced: Includes heuristics data for observability.
|
|
1533
|
+
*/
|
|
1534
|
+
exportJSONMetrics() {
|
|
1535
|
+
const current = this.getMemoryStats();
|
|
1536
|
+
const history = this.getMetricsHistory();
|
|
1537
|
+
const gcMetrics = this.getGCMetrics();
|
|
1538
|
+
// Single-pass calculation of avg and peak
|
|
1539
|
+
const samples = history.samples;
|
|
1540
|
+
let sum = 0;
|
|
1541
|
+
let peak = current.heapUsed;
|
|
1542
|
+
if (samples.length > 0) {
|
|
1543
|
+
for (let i = 0; i < samples.length; i++) {
|
|
1544
|
+
const sample = samples[i];
|
|
1545
|
+
if (sample) {
|
|
1546
|
+
const heapUsed = sample.heapUsed;
|
|
1547
|
+
sum += heapUsed;
|
|
1548
|
+
if (heapUsed > peak)
|
|
1549
|
+
peak = heapUsed;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
const avgHeapUsed = samples.length > 0 ? sum / samples.length : current.heapUsed;
|
|
1554
|
+
const demotionMetrics = this.getDemotionMetrics();
|
|
1555
|
+
return {
|
|
1556
|
+
current,
|
|
1557
|
+
history,
|
|
1558
|
+
aggregates: {
|
|
1559
|
+
avgHeapUsed,
|
|
1560
|
+
peakHeapUsed: peak,
|
|
1561
|
+
totalGCCount: gcMetrics.totalHints,
|
|
1562
|
+
pressureTriggeredDemotions: demotionMetrics.pressureTriggeredDemotions,
|
|
1563
|
+
failedDemotions: demotionMetrics.failedDemotions,
|
|
1564
|
+
bytesFreedByDemotion: demotionMetrics.bytesFreedByDemotion,
|
|
1565
|
+
},
|
|
1566
|
+
heuristics: {
|
|
1567
|
+
smoothedState: this.getSmoothedPressureState(),
|
|
1568
|
+
adaptiveThresholds: this.getAdaptiveThresholds(),
|
|
1569
|
+
oomPrediction: this.predictTimeToOOM(),
|
|
1570
|
+
growthRate: this.getMemoryGrowthRate(),
|
|
1571
|
+
isVolatile: this.isPressureVolatile(),
|
|
1572
|
+
},
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
// ===========================================================================
|
|
1576
|
+
// Query Management
|
|
1577
|
+
// ===========================================================================
|
|
1578
|
+
/**
|
|
1579
|
+
* Execute a query with memory pressure guards.
|
|
1580
|
+
*
|
|
1581
|
+
* - Pauses at critical pressure (with timeout)
|
|
1582
|
+
* - Rejects at OOM pressure
|
|
1583
|
+
* - Enforces hard limits if configured
|
|
1584
|
+
*/
|
|
1585
|
+
async guardedQuery(sql, params, options) {
|
|
1586
|
+
const stats = this.getMemoryStats();
|
|
1587
|
+
// Hard limit enforcement
|
|
1588
|
+
if (this.extendedConfig.hardLimitEnforcement && this.forcedHeapUsage !== null) {
|
|
1589
|
+
const estimatedUsage = options?.estimatedResultBytes ?? 0;
|
|
1590
|
+
if (this.forcedHeapUsage + estimatedUsage > this.config.memoryLimitBytes) {
|
|
1591
|
+
throw new MemoryPressureError('Would exceed Worker memory limit', stats);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
// OOM rejection
|
|
1595
|
+
if (this.extendedConfig.queryRejectionEnabled && this.lastPressureLevel === 'oom') {
|
|
1596
|
+
throw new MemoryPressureError('Memory pressure too high', stats);
|
|
1597
|
+
}
|
|
1598
|
+
// Critical pausing
|
|
1599
|
+
if (this.extendedConfig.queryPausingEnabled && this.lastPressureLevel === 'critical') {
|
|
1600
|
+
await this.waitForPressureRelief();
|
|
1601
|
+
}
|
|
1602
|
+
// Execute query
|
|
1603
|
+
if (this.pglite) {
|
|
1604
|
+
const result = await this.pglite.query(sql, params);
|
|
1605
|
+
return result;
|
|
1606
|
+
}
|
|
1607
|
+
return { rows: [], fields: [] };
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Wait for pressure to drop with timeout.
|
|
1611
|
+
*/
|
|
1612
|
+
async waitForPressureRelief() {
|
|
1613
|
+
const timeoutMs = this.extendedConfig.queryPauseTimeoutMs ?? DEFAULT_QUERY_PAUSE_TIMEOUT_MS;
|
|
1614
|
+
const operation = this.pauseOperation(`query-${Date.now()}`);
|
|
1615
|
+
// Track the operation so it can be resumed when pressure drops
|
|
1616
|
+
const dummyPromise = Promise.resolve();
|
|
1617
|
+
this.pausedQueries.set(dummyPromise, operation);
|
|
1618
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1619
|
+
setTimeout(() => {
|
|
1620
|
+
operation.status = 'cancelled';
|
|
1621
|
+
this.pausedQueries.delete(dummyPromise);
|
|
1622
|
+
reject(new Error('Query paused due to memory pressure and timed out'));
|
|
1623
|
+
}, timeoutMs);
|
|
1624
|
+
});
|
|
1625
|
+
try {
|
|
1626
|
+
await Promise.race([operation.resumePromise, timeoutPromise]);
|
|
1627
|
+
}
|
|
1628
|
+
finally {
|
|
1629
|
+
this.pausedQueries.delete(dummyPromise);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Get status of a query promise.
|
|
1634
|
+
*/
|
|
1635
|
+
getQueryStatus(queryPromise) {
|
|
1636
|
+
const operation = this.pausedQueries.get(queryPromise);
|
|
1637
|
+
if (operation) {
|
|
1638
|
+
return operation.status === 'paused' ? 'paused' : 'executing';
|
|
1639
|
+
}
|
|
1640
|
+
return 'unknown';
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Pause an operation and return a handle.
|
|
1644
|
+
*/
|
|
1645
|
+
pauseOperation(id) {
|
|
1646
|
+
let resolveFunc = () => { };
|
|
1647
|
+
let rejectFunc = () => { };
|
|
1648
|
+
const resumePromise = new Promise((resolve, reject) => {
|
|
1649
|
+
resolveFunc = resolve;
|
|
1650
|
+
rejectFunc = reject;
|
|
1651
|
+
});
|
|
1652
|
+
return {
|
|
1653
|
+
id,
|
|
1654
|
+
status: 'paused',
|
|
1655
|
+
pausedAt: new Date(),
|
|
1656
|
+
resumePromise,
|
|
1657
|
+
resolve: resolveFunc,
|
|
1658
|
+
reject: rejectFunc,
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Resume all paused operations.
|
|
1663
|
+
*/
|
|
1664
|
+
resumePausedOperations() {
|
|
1665
|
+
for (const [, operation] of this.pausedQueries) {
|
|
1666
|
+
if (operation.status === 'paused') {
|
|
1667
|
+
operation.status = 'resumed';
|
|
1668
|
+
operation.resolve();
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
this.pausedQueries.clear();
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Enforce a hard allocation limit.
|
|
1675
|
+
*
|
|
1676
|
+
* Throws if the allocation would exceed the memory limit.
|
|
1677
|
+
*/
|
|
1678
|
+
async guardedAllocation(bytes) {
|
|
1679
|
+
const currentUsage = this.forcedHeapUsage ?? this.getCurrentHeapUsage();
|
|
1680
|
+
const totalAfter = currentUsage + bytes;
|
|
1681
|
+
if (this.extendedConfig.hardLimitEnforcement && totalAfter > this.config.memoryLimitBytes) {
|
|
1682
|
+
throw new MemoryPressureError('Would exceed Worker memory limit', this.getMemoryStats());
|
|
1683
|
+
}
|
|
1684
|
+
return { allowed: true };
|
|
1685
|
+
}
|
|
1686
|
+
// ===========================================================================
|
|
1687
|
+
// Feature Management
|
|
1688
|
+
// ===========================================================================
|
|
1689
|
+
/**
|
|
1690
|
+
* Check if a feature is currently enabled.
|
|
1691
|
+
*/
|
|
1692
|
+
isFeatureEnabled(feature) {
|
|
1693
|
+
return this.enabledFeatures.has(feature) && !this.disabledFeatures.has(feature);
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* Get count of enabled features.
|
|
1697
|
+
*/
|
|
1698
|
+
getEnabledFeatureCount() {
|
|
1699
|
+
let count = 0;
|
|
1700
|
+
for (const feature of this.enabledFeatures) {
|
|
1701
|
+
if (!this.disabledFeatures.has(feature))
|
|
1702
|
+
count++;
|
|
1703
|
+
}
|
|
1704
|
+
return count;
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Disable features under pressure.
|
|
1708
|
+
*/
|
|
1709
|
+
disableFeaturesUnderPressure() {
|
|
1710
|
+
if (!this.extendedConfig.autoDisableFeatures)
|
|
1711
|
+
return;
|
|
1712
|
+
const features = this.extendedConfig.featuresToDisable ?? DEFAULT_FEATURES_TO_DISABLE;
|
|
1713
|
+
for (const feature of features) {
|
|
1714
|
+
this.disabledFeatures.add(feature);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Re-enable features after recovery.
|
|
1719
|
+
*/
|
|
1720
|
+
enableFeaturesAfterRecovery() {
|
|
1721
|
+
if (!this.extendedConfig.autoDisableFeatures)
|
|
1722
|
+
return;
|
|
1723
|
+
this.disabledFeatures.clear();
|
|
1724
|
+
}
|
|
1725
|
+
// ===========================================================================
|
|
1726
|
+
// Recovery Management
|
|
1727
|
+
// ===========================================================================
|
|
1728
|
+
/**
|
|
1729
|
+
* Check if manager is in recovery mode.
|
|
1730
|
+
*/
|
|
1731
|
+
isInRecoveryMode() {
|
|
1732
|
+
return this.inRecoveryMode;
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1735
|
+
* Get recovery progress (0.0 to 1.0).
|
|
1736
|
+
*/
|
|
1737
|
+
getRecoveryProgress() {
|
|
1738
|
+
if (!this.inRecoveryMode) {
|
|
1739
|
+
return this.lastPressureLevel === 'normal' ? 1.0 : 0.0;
|
|
1740
|
+
}
|
|
1741
|
+
const hysteresisMs = this.extendedConfig.recoveryHysteresisMs ?? DEFAULT_RECOVERY_HYSTERESIS_MS;
|
|
1742
|
+
if (this.recoveryStartTime && hysteresisMs > 0) {
|
|
1743
|
+
const elapsed = Date.now() - this.recoveryStartTime;
|
|
1744
|
+
this.recoveryProgress = Math.min(1.0, elapsed / hysteresisMs);
|
|
1745
|
+
if (this.recoveryProgress >= 1.0 && this.lastPressureLevel === 'normal') {
|
|
1746
|
+
this.inRecoveryMode = false;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
return this.recoveryProgress;
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Start gradual recovery of features.
|
|
1753
|
+
*/
|
|
1754
|
+
async startGradualRecovery() {
|
|
1755
|
+
if (!this.extendedConfig.gradualRecovery)
|
|
1756
|
+
return;
|
|
1757
|
+
this.recoveryPaused = false;
|
|
1758
|
+
const intervalMs = this.extendedConfig.recoveryStepIntervalMs ?? DEFAULT_RECOVERY_STEP_INTERVAL_MS;
|
|
1759
|
+
const features = this.extendedConfig.featuresToDisable ?? DEFAULT_FEATURES_TO_DISABLE;
|
|
1760
|
+
let featureIndex = 0;
|
|
1761
|
+
this.gradualRecoveryInterval = setInterval(() => {
|
|
1762
|
+
if (this.recoveryPaused || featureIndex >= features.length) {
|
|
1763
|
+
if (this.gradualRecoveryInterval) {
|
|
1764
|
+
clearInterval(this.gradualRecoveryInterval);
|
|
1765
|
+
this.gradualRecoveryInterval = null;
|
|
1766
|
+
}
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
const feature = features[featureIndex];
|
|
1770
|
+
if (feature) {
|
|
1771
|
+
this.disabledFeatures.delete(feature);
|
|
1772
|
+
this.recoveryStepsExecuted.push(feature);
|
|
1773
|
+
this.extendedConfig.onRecoveryStep?.(feature);
|
|
1774
|
+
}
|
|
1775
|
+
featureIndex++;
|
|
1776
|
+
}, intervalMs);
|
|
1777
|
+
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Pause gradual recovery.
|
|
1780
|
+
*/
|
|
1781
|
+
pauseGradualRecovery() {
|
|
1782
|
+
this.recoveryPaused = true;
|
|
1783
|
+
if (this.gradualRecoveryInterval) {
|
|
1784
|
+
clearInterval(this.gradualRecoveryInterval);
|
|
1785
|
+
this.gradualRecoveryInterval = null;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Check if recovery is paused.
|
|
1790
|
+
*/
|
|
1791
|
+
isRecoveryPaused() {
|
|
1792
|
+
return this.recoveryPaused;
|
|
1793
|
+
}
|
|
1794
|
+
// ===========================================================================
|
|
1795
|
+
// Event Listeners
|
|
1796
|
+
// ===========================================================================
|
|
1797
|
+
/**
|
|
1798
|
+
* Add a memory pressure event listener.
|
|
1799
|
+
*
|
|
1800
|
+
* @param listener - Callback for pressure events
|
|
1801
|
+
* @returns Unsubscribe function
|
|
1802
|
+
*/
|
|
1803
|
+
onPressureChange(listener) {
|
|
1804
|
+
// Warn when approaching limit
|
|
1805
|
+
const warningThreshold = Math.floor(this.config.maxListeners * 0.8);
|
|
1806
|
+
if (this.listeners.length >= warningThreshold && this.listeners.length < this.config.maxListeners) {
|
|
1807
|
+
console.warn(`Approaching memory pressure listener limit: ${this.listeners.length}/${this.config.maxListeners}`);
|
|
1808
|
+
}
|
|
1809
|
+
// Cap at max listeners (drop oldest)
|
|
1810
|
+
if (this.listeners.length >= this.config.maxListeners) {
|
|
1811
|
+
console.warn(`Memory pressure listener limit (${this.config.maxListeners}) reached. Dropping oldest listener.`);
|
|
1812
|
+
this.listeners.shift();
|
|
1813
|
+
}
|
|
1814
|
+
this.listeners.push(listener);
|
|
1815
|
+
return () => {
|
|
1816
|
+
const index = this.listeners.indexOf(listener);
|
|
1817
|
+
if (index >= 0)
|
|
1818
|
+
this.listeners.splice(index, 1);
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Get current listener count.
|
|
1823
|
+
*/
|
|
1824
|
+
getListenerCount() {
|
|
1825
|
+
return this.listeners.length;
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Add a memory spike listener.
|
|
1829
|
+
*
|
|
1830
|
+
* @param listener - Callback for spike events
|
|
1831
|
+
* @returns Unsubscribe function
|
|
1832
|
+
*/
|
|
1833
|
+
onMemorySpike(listener) {
|
|
1834
|
+
this.memorySpikeListeners.push(listener);
|
|
1835
|
+
return () => {
|
|
1836
|
+
const index = this.memorySpikeListeners.indexOf(listener);
|
|
1837
|
+
if (index >= 0)
|
|
1838
|
+
this.memorySpikeListeners.splice(index, 1);
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Add a recovery event listener.
|
|
1843
|
+
*
|
|
1844
|
+
* @param listener - Callback for recovery events
|
|
1845
|
+
* @returns Unsubscribe function
|
|
1846
|
+
*/
|
|
1847
|
+
onRecovery(listener) {
|
|
1848
|
+
this.recoveryListeners.push(listener);
|
|
1849
|
+
return () => {
|
|
1850
|
+
const index = this.recoveryListeners.indexOf(listener);
|
|
1851
|
+
if (index >= 0)
|
|
1852
|
+
this.recoveryListeners.splice(index, 1);
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Emit a pressure event to all listeners.
|
|
1857
|
+
*/
|
|
1858
|
+
emitPressureEvent(event) {
|
|
1859
|
+
for (const listener of this.listeners) {
|
|
1860
|
+
try {
|
|
1861
|
+
listener(event);
|
|
1862
|
+
}
|
|
1863
|
+
catch (error) {
|
|
1864
|
+
console.error('Memory pressure listener error:', error);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
// ===========================================================================
|
|
1869
|
+
// Testing Support
|
|
1870
|
+
// ===========================================================================
|
|
1871
|
+
/**
|
|
1872
|
+
* Simulate heap growth for testing.
|
|
1873
|
+
* @internal
|
|
1874
|
+
*/
|
|
1875
|
+
_simulateHeapGrowth(bytes) {
|
|
1876
|
+
this.simulatedHeapBytes += bytes;
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Set current heap usage for testing.
|
|
1880
|
+
* @internal
|
|
1881
|
+
*/
|
|
1882
|
+
_setCurrentHeapUsage(bytes) {
|
|
1883
|
+
this.forcedHeapUsage = bytes;
|
|
1884
|
+
}
|
|
1885
|
+
/**
|
|
1886
|
+
* Force a memory pressure level for testing.
|
|
1887
|
+
*
|
|
1888
|
+
* This updates the internal state and emits events as if the pressure
|
|
1889
|
+
* had actually changed. Useful for testing pressure handling without
|
|
1890
|
+
* actually consuming memory.
|
|
1891
|
+
*
|
|
1892
|
+
* @internal
|
|
1893
|
+
*/
|
|
1894
|
+
_forceMemoryPressure(level) {
|
|
1895
|
+
const previousLevel = this.lastPressureLevel;
|
|
1896
|
+
if (previousLevel === level)
|
|
1897
|
+
return null;
|
|
1898
|
+
const now = Date.now();
|
|
1899
|
+
this.updatePressureDuration(previousLevel, now);
|
|
1900
|
+
// Handle recovery/escalation transitions
|
|
1901
|
+
const previousIndex = getPressureLevelIndex(previousLevel);
|
|
1902
|
+
const newIndex = getPressureLevelIndex(level);
|
|
1903
|
+
if (previousIndex > 0 && newIndex < previousIndex) {
|
|
1904
|
+
// Pressure dropped
|
|
1905
|
+
this.handleRecovery(previousLevel, level, now);
|
|
1906
|
+
}
|
|
1907
|
+
else if (newIndex > 0 && newIndex >= previousIndex) {
|
|
1908
|
+
// Pressure rose
|
|
1909
|
+
this.exitRecoveryMode();
|
|
1910
|
+
}
|
|
1911
|
+
this.lastPressureLevel = level;
|
|
1912
|
+
// Create synthetic stats
|
|
1913
|
+
const usagePercent = level === 'oom' ? 96 : level === 'critical' ? 88 : level === 'warning' ? 75 : 50;
|
|
1914
|
+
const stats = {
|
|
1915
|
+
heapUsed: this.config.memoryLimitBytes * (usagePercent / 100),
|
|
1916
|
+
heapLimit: this.config.memoryLimitBytes,
|
|
1917
|
+
usagePercent,
|
|
1918
|
+
pressureLevel: level,
|
|
1919
|
+
timestamp: new Date(),
|
|
1920
|
+
};
|
|
1921
|
+
const event = {
|
|
1922
|
+
previousLevel,
|
|
1923
|
+
currentLevel: level,
|
|
1924
|
+
stats,
|
|
1925
|
+
actionsTaken: [],
|
|
1926
|
+
timestamp: new Date(),
|
|
1927
|
+
};
|
|
1928
|
+
this.emitPressureEvent(event);
|
|
1929
|
+
// Trigger pressure-triggered demotion if pressure rose
|
|
1930
|
+
if (newIndex > previousIndex && level !== 'normal') {
|
|
1931
|
+
// Fire and forget - don't await to avoid blocking test
|
|
1932
|
+
// Pass true to bypass rate limiting since this is a pressure escalation
|
|
1933
|
+
this.handlePressureDemotion(level, true).catch(() => { });
|
|
1934
|
+
}
|
|
1935
|
+
return event;
|
|
1936
|
+
}
|
|
1937
|
+
// ===========================================================================
|
|
1938
|
+
// Cleanup
|
|
1939
|
+
// ===========================================================================
|
|
1940
|
+
/**
|
|
1941
|
+
* Dispose of the manager and clean up all resources.
|
|
1942
|
+
*
|
|
1943
|
+
* This stops all intervals, clears all listeners, and releases references.
|
|
1944
|
+
* Always call this when the manager is no longer needed.
|
|
1945
|
+
*/
|
|
1946
|
+
dispose() {
|
|
1947
|
+
// Stop all intervals
|
|
1948
|
+
this.stopMonitoring();
|
|
1949
|
+
this.pauseGradualRecovery();
|
|
1950
|
+
if (this.sustainedPressureCheckInterval) {
|
|
1951
|
+
clearInterval(this.sustainedPressureCheckInterval);
|
|
1952
|
+
this.sustainedPressureCheckInterval = null;
|
|
1953
|
+
}
|
|
1954
|
+
// Clear all listeners (set to empty arrays to avoid memory leaks)
|
|
1955
|
+
this.listeners.length = 0;
|
|
1956
|
+
this.memorySpikeListeners.length = 0;
|
|
1957
|
+
this.recoveryListeners.length = 0;
|
|
1958
|
+
this.pressureDemotionListeners.length = 0;
|
|
1959
|
+
// Release external references
|
|
1960
|
+
this.pglite = null;
|
|
1961
|
+
this.wasmModule = null;
|
|
1962
|
+
this.cacheManager = null;
|
|
1963
|
+
this.tieredStorageOrchestrator = null;
|
|
1964
|
+
// Clear state
|
|
1965
|
+
this.unloadedExtensions.clear();
|
|
1966
|
+
this.pausedQueries.clear();
|
|
1967
|
+
this.inFlightQueries.clear();
|
|
1968
|
+
this.metricsHistory.clear();
|
|
1969
|
+
this.gcMetrics.bytesFreed.clear();
|
|
1970
|
+
this.gcMetrics.durations.clear();
|
|
1971
|
+
this.disabledFeatures.clear();
|
|
1972
|
+
this.recoveryStepsExecuted.length = 0;
|
|
1973
|
+
// Reset testing state
|
|
1974
|
+
this.simulatedHeapBytes = 0;
|
|
1975
|
+
this.forcedHeapUsage = null;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
// =============================================================================
|
|
1979
|
+
// Factory Function
|
|
1980
|
+
// =============================================================================
|
|
1981
|
+
/**
|
|
1982
|
+
* Create a memory pressure manager with default configuration.
|
|
1983
|
+
*
|
|
1984
|
+
* @param config - Optional configuration overrides
|
|
1985
|
+
* @returns Configured MemoryPressureManager instance
|
|
1986
|
+
*/
|
|
1987
|
+
export function createMemoryPressureManager(config) {
|
|
1988
|
+
return new MemoryPressureManager(config);
|
|
1989
|
+
}
|
|
1990
|
+
//# sourceMappingURL=memory-pressure.js.map
|