@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,1895 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cold Start Optimizer for PGLite in Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* This module provides optimized initialization strategies for PGLite instances
|
|
5
|
+
* running in Cloudflare Workers' Durable Objects environment, where cold starts
|
|
6
|
+
* can significantly impact user experience.
|
|
7
|
+
*
|
|
8
|
+
* ## Overview
|
|
9
|
+
*
|
|
10
|
+
* Cloudflare Workers have ephemeral execution contexts that can be evicted at any
|
|
11
|
+
* time. When a new request arrives after eviction, a "cold start" occurs requiring
|
|
12
|
+
* full re-initialization of PGLite. This module implements several strategies to
|
|
13
|
+
* minimize cold start latency:
|
|
14
|
+
*
|
|
15
|
+
* ### Optimization Strategies
|
|
16
|
+
*
|
|
17
|
+
* 1. **WASM Caching** - Module-level singleton caching allows warm starts within
|
|
18
|
+
* the same isolate by reusing already-initialized PGLite instances.
|
|
19
|
+
*
|
|
20
|
+
* 2. **Parallel Data Fetching** - While WASM prepares (near-instant for pre-compiled
|
|
21
|
+
* modules), data is fetched from storage tiers in parallel:
|
|
22
|
+
* - HOT tier: Synchronous DO SQLite (fastest)
|
|
23
|
+
* - WARM tier: Cloudflare Cache API (FREE!)
|
|
24
|
+
* - COLD tier: R2 object storage (durable)
|
|
25
|
+
*
|
|
26
|
+
* 3. **Lazy Initialization** - PGLite initialization is deferred until the first
|
|
27
|
+
* query, allowing the DO to respond quickly to health checks and metadata requests.
|
|
28
|
+
*
|
|
29
|
+
* 4. **Memory Optimization** - Buffer pooling reduces allocation overhead and helps
|
|
30
|
+
* stay within Workers' 128MB memory limit.
|
|
31
|
+
*
|
|
32
|
+
* 5. **Pre-warming** - After initialization, common queries are executed to warm
|
|
33
|
+
* PostgreSQL's query planner cache.
|
|
34
|
+
*
|
|
35
|
+
* ## Performance Targets
|
|
36
|
+
*
|
|
37
|
+
* | Metric | p50 Target | p95 Target | Notes |
|
|
38
|
+
* |--------|------------|------------|-------|
|
|
39
|
+
* | WASM module loading | < 300ms | < 500ms | Near-zero for pre-compiled static imports |
|
|
40
|
+
* | PGLite initialization | < 500ms | < 800ms | Includes filesystem setup |
|
|
41
|
+
* | First query latency | < 20ms | < 50ms | After prewarm queries |
|
|
42
|
+
* | **Total cold start** | **< 800ms** | **< 1500ms** | End-to-end including prewarm |
|
|
43
|
+
*
|
|
44
|
+
* ## Architecture
|
|
45
|
+
*
|
|
46
|
+
* ```
|
|
47
|
+
* ┌─────────────────────────────────────────────────────────────────┐
|
|
48
|
+
* │ Cold Start Optimizer │
|
|
49
|
+
* ├─────────────────────────────────────────────────────────────────┤
|
|
50
|
+
* │ │
|
|
51
|
+
* │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
52
|
+
* │ │ WASM Cache │ │ Parallel │ │ Lazy Init │ │
|
|
53
|
+
* │ │ (Isolate) │────│ Data Fetch │────│ Pattern │ │
|
|
54
|
+
* │ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
55
|
+
* │ │ │ │ │
|
|
56
|
+
* │ ▼ ▼ ▼ │
|
|
57
|
+
* │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
58
|
+
* │ │ Warm Start │ │ HOT/WARM/ │ │ First Query │ │
|
|
59
|
+
* │ │ Detection │ │ COLD Tiers │ │ Trigger │ │
|
|
60
|
+
* │ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
61
|
+
* │ │
|
|
62
|
+
* │ ┌─────────────┐ │
|
|
63
|
+
* │ │ Buffer Pool │ │
|
|
64
|
+
* │ │ (Memory) │ │
|
|
65
|
+
* │ └─────────────┘ │
|
|
66
|
+
* └─────────────────────────────────────────────────────────────────┘
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* ## Usage
|
|
70
|
+
*
|
|
71
|
+
* ```typescript
|
|
72
|
+
* import pgliteWasm from './pglite.wasm'
|
|
73
|
+
* import { createColdStartOptimizer } from '@dotdo/postgres/pglite'
|
|
74
|
+
*
|
|
75
|
+
* // Create optimizer with tiered storage
|
|
76
|
+
* const optimizer = createColdStartOptimizer({
|
|
77
|
+
* wasmModule: pgliteWasm,
|
|
78
|
+
* cacheLayer: myCacheLayer,
|
|
79
|
+
* warmCacheKey: `db:${databaseId}`,
|
|
80
|
+
* r2Layer: myR2Layer,
|
|
81
|
+
* coldStorageKey: `databases/${databaseId}/data.tar`,
|
|
82
|
+
* lazy: true,
|
|
83
|
+
* prewarm: true,
|
|
84
|
+
* })
|
|
85
|
+
*
|
|
86
|
+
* // First query triggers initialization
|
|
87
|
+
* const result = await optimizer.query('SELECT * FROM users')
|
|
88
|
+
*
|
|
89
|
+
* // Check performance metrics
|
|
90
|
+
* const metrics = optimizer.getMetrics()
|
|
91
|
+
* console.log(`Cold start: ${metrics?.totalMs}ms from ${metrics?.dataSource}`)
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* @module pglite/cold-start-optimizer
|
|
95
|
+
* @see {@link ColdStartManager} for DO lifecycle integration
|
|
96
|
+
* @see {@link TieredVFS} for the underlying storage system
|
|
97
|
+
* @issue postgres-w6l6
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
import type { PGlite, PGliteOptions } from '@dotdo/pglite'
|
|
101
|
+
import type { CacheLayer } from './cache-layer'
|
|
102
|
+
import type { R2StorageLayer } from './r2-layer'
|
|
103
|
+
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// Types
|
|
106
|
+
// ============================================================================
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Cold start initialization phases
|
|
110
|
+
*
|
|
111
|
+
* The optimizer progresses through these phases during initialization:
|
|
112
|
+
*
|
|
113
|
+
* ```
|
|
114
|
+
* not_started → wasm_loading → parallel_loading → pglite_init → ready
|
|
115
|
+
* ↓
|
|
116
|
+
* error
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* - `not_started`: Initial state, no initialization attempted
|
|
120
|
+
* - `wasm_loading`: Preparing WASM module (near-instant for pre-compiled)
|
|
121
|
+
* - `data_fetching`: Fetching data from a single tier (deprecated, use parallel_loading)
|
|
122
|
+
* - `parallel_loading`: Fetching data from HOT/WARM/COLD tiers in parallel
|
|
123
|
+
* - `pglite_init`: Creating PGLite instance and running prewarm queries
|
|
124
|
+
* - `ready`: Initialization complete, ready for queries
|
|
125
|
+
* - `error`: Initialization failed
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* optimizer.onPhaseChange((phase, metrics) => {
|
|
130
|
+
* console.log(`Phase: ${phase}, elapsed: ${metrics.totalMs}ms`)
|
|
131
|
+
* })
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export type ColdStartPhase =
|
|
135
|
+
| 'not_started'
|
|
136
|
+
| 'wasm_loading'
|
|
137
|
+
| 'data_fetching'
|
|
138
|
+
| 'parallel_loading'
|
|
139
|
+
| 'pglite_init'
|
|
140
|
+
| 'ready'
|
|
141
|
+
| 'error'
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Detailed phase timing breakdown for profiling
|
|
145
|
+
*/
|
|
146
|
+
export interface PhaseTimingBreakdown {
|
|
147
|
+
/** Phase name */
|
|
148
|
+
phase: ColdStartPhase
|
|
149
|
+
/** Start timestamp (high-resolution) */
|
|
150
|
+
startTime: number
|
|
151
|
+
/** End timestamp (high-resolution) */
|
|
152
|
+
endTime: number
|
|
153
|
+
/** Duration in milliseconds */
|
|
154
|
+
durationMs: number
|
|
155
|
+
/** Any sub-phase timings */
|
|
156
|
+
subPhases?: Array<{
|
|
157
|
+
name: string
|
|
158
|
+
startTime: number
|
|
159
|
+
endTime: number
|
|
160
|
+
durationMs: number
|
|
161
|
+
}>
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* WASM loading strategy comparison metrics
|
|
166
|
+
*/
|
|
167
|
+
export interface WasmStrategyMetrics {
|
|
168
|
+
/** Strategy name */
|
|
169
|
+
strategy: 'static_import' | 'dynamic_import' | 'streaming' | 'cached'
|
|
170
|
+
/** Time to obtain the WASM module */
|
|
171
|
+
moduleLoadMs: number
|
|
172
|
+
/** Time to instantiate the module */
|
|
173
|
+
instantiateMs: number
|
|
174
|
+
/** Total WASM preparation time */
|
|
175
|
+
totalMs: number
|
|
176
|
+
/** Whether the module was from cache */
|
|
177
|
+
fromCache: boolean
|
|
178
|
+
/** Module size in bytes (if known) */
|
|
179
|
+
moduleSizeBytes?: number
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Detailed timing metrics for cold start optimization
|
|
184
|
+
*
|
|
185
|
+
* These metrics provide comprehensive visibility into cold start performance,
|
|
186
|
+
* enabling identification of bottlenecks and performance regression detection.
|
|
187
|
+
*
|
|
188
|
+
* ## Key Metrics
|
|
189
|
+
*
|
|
190
|
+
* | Metric | Description | Target |
|
|
191
|
+
* |--------|-------------|--------|
|
|
192
|
+
* | `totalMs` | End-to-end cold start time | < 1500ms (p95) |
|
|
193
|
+
* | `wasmPrepMs` | WASM module preparation | < 10ms (pre-compiled) |
|
|
194
|
+
* | `dataFetchMs` | Data fetch from storage tier | < 500ms (p95) |
|
|
195
|
+
* | `pgliteInitMs` | PGLite instance creation | < 800ms (p95) |
|
|
196
|
+
* | `firstQueryMs` | Prewarm query execution | < 50ms (p95) |
|
|
197
|
+
*
|
|
198
|
+
* ## Interpreting Results
|
|
199
|
+
*
|
|
200
|
+
* - High `dataFetchMs` → Consider promoting data to a faster tier
|
|
201
|
+
* - High `pgliteInitMs` → Consider using the tiny PGLite variant
|
|
202
|
+
* - `isWarmStart: true` → Instance was reused from cache (best case)
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* const metrics = optimizer.getMetrics()
|
|
207
|
+
* if (metrics) {
|
|
208
|
+
* console.log(`Total: ${metrics.totalMs}ms`)
|
|
209
|
+
* console.log(`Source: ${metrics.dataSource}`)
|
|
210
|
+
* console.log(`Warm start: ${metrics.isWarmStart}`)
|
|
211
|
+
* console.log(`Data size: ${metrics.dataSizeBytes} bytes`)
|
|
212
|
+
* }
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export interface ColdStartMetrics {
|
|
216
|
+
/** Total cold start time in milliseconds */
|
|
217
|
+
totalMs: number
|
|
218
|
+
/** WASM module preparation time (usually near-zero for pre-compiled) */
|
|
219
|
+
wasmPrepMs: number
|
|
220
|
+
/** Data fetch time from storage tier */
|
|
221
|
+
dataFetchMs: number
|
|
222
|
+
/** Overlap time saved by parallel loading */
|
|
223
|
+
parallelSavingsMs: number
|
|
224
|
+
/** PGLite instance creation time */
|
|
225
|
+
pgliteInitMs: number
|
|
226
|
+
/** First query time (query planner warmup) */
|
|
227
|
+
firstQueryMs: number
|
|
228
|
+
/** Data source that provided the filesystem bundle */
|
|
229
|
+
dataSource: 'hot' | 'warm' | 'cold' | 'empty'
|
|
230
|
+
/** Size of data loaded in bytes */
|
|
231
|
+
dataSizeBytes: number
|
|
232
|
+
/** Whether this was a warm start (cached instance) */
|
|
233
|
+
isWarmStart: boolean
|
|
234
|
+
/** Current phase */
|
|
235
|
+
phase: ColdStartPhase
|
|
236
|
+
/** Timestamp when cold start began */
|
|
237
|
+
startedAt: number
|
|
238
|
+
/** Timestamp when cold start completed */
|
|
239
|
+
completedAt: number
|
|
240
|
+
/** Memory usage at completion (if available) */
|
|
241
|
+
memoryUsedMB?: number
|
|
242
|
+
/** Detailed phase timing breakdown for profiling */
|
|
243
|
+
phaseBreakdown?: PhaseTimingBreakdown[]
|
|
244
|
+
/** WASM strategy metrics (for comparison) */
|
|
245
|
+
wasmStrategy?: WasmStrategyMetrics
|
|
246
|
+
/** Individual tier fetch timings */
|
|
247
|
+
tierFetchTimings?: {
|
|
248
|
+
hot?: { attempted: boolean; durationMs: number; success: boolean }
|
|
249
|
+
warm?: { attempted: boolean; durationMs: number; success: boolean }
|
|
250
|
+
cold?: { attempted: boolean; durationMs: number; success: boolean }
|
|
251
|
+
}
|
|
252
|
+
/** Dynamic import time for @dotdo/pglite module */
|
|
253
|
+
pgliteImportMs?: number
|
|
254
|
+
/** Prewarm query individual timings */
|
|
255
|
+
prewarmTimings?: Array<{ query: string; durationMs: number }>
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Configuration for cold start optimization
|
|
260
|
+
*
|
|
261
|
+
* This configuration controls how the optimizer initializes PGLite and
|
|
262
|
+
* fetches data from tiered storage.
|
|
263
|
+
*
|
|
264
|
+
* ## Required Configuration
|
|
265
|
+
*
|
|
266
|
+
* - `wasmModule`: Pre-compiled WASM module (must be statically imported)
|
|
267
|
+
*
|
|
268
|
+
* ## Storage Tiers (in priority order)
|
|
269
|
+
*
|
|
270
|
+
* 1. **HOT tier** (`hotData`): Data already in memory from DO SQLite.
|
|
271
|
+
* Synchronous access, fastest possible. If provided, skips all other tiers.
|
|
272
|
+
*
|
|
273
|
+
* 2. **WARM tier** (`cacheLayer` + `warmCacheKey`): Cloudflare Cache API.
|
|
274
|
+
* FREE and fast (~10-50ms). Data is cached at edge locations.
|
|
275
|
+
*
|
|
276
|
+
* 3. **COLD tier** (`r2Layer`/`r2Bucket` + `coldStorageKey`): R2 storage.
|
|
277
|
+
* Durable but slower (~100-500ms). Used for persistence and recovery.
|
|
278
|
+
*
|
|
279
|
+
* ## Initialization Behavior
|
|
280
|
+
*
|
|
281
|
+
* - `lazy: true` (default): Defer initialization until first query
|
|
282
|
+
* - `lazy: false`: Initialize immediately when optimizer is created
|
|
283
|
+
* - `prewarm: true` (default): Run warmup queries after initialization
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```typescript
|
|
287
|
+
* // Minimal configuration (empty database)
|
|
288
|
+
* const config: ColdStartConfig = {
|
|
289
|
+
* wasmModule: pgliteWasm,
|
|
290
|
+
* }
|
|
291
|
+
*
|
|
292
|
+
* // Full tiered storage configuration
|
|
293
|
+
* const config: ColdStartConfig = {
|
|
294
|
+
* wasmModule: pgliteWasm,
|
|
295
|
+
* cacheLayer: myCacheLayer,
|
|
296
|
+
* warmCacheKey: `db:${databaseId}`,
|
|
297
|
+
* r2Bucket: env.R2_BUCKET,
|
|
298
|
+
* coldStorageKey: `databases/${databaseId}/data.tar`,
|
|
299
|
+
* lazy: true,
|
|
300
|
+
* prewarm: true,
|
|
301
|
+
* debug: true,
|
|
302
|
+
* onReady: (metrics) => {
|
|
303
|
+
* console.log(`Initialized in ${metrics.totalMs}ms`)
|
|
304
|
+
* },
|
|
305
|
+
* }
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
export interface ColdStartConfig {
|
|
309
|
+
/**
|
|
310
|
+
* Pre-compiled WASM module
|
|
311
|
+
* MUST be statically imported: import pgliteWasm from './pglite.wasm'
|
|
312
|
+
*/
|
|
313
|
+
wasmModule: WebAssembly.Module
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* HOT tier: Data from DO SQLite (synchronous)
|
|
317
|
+
* Highest priority - if provided, skips all other tiers
|
|
318
|
+
*/
|
|
319
|
+
hotData?: ArrayBuffer | Uint8Array
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* WARM tier: Cache layer for Cloudflare Cache API
|
|
323
|
+
*/
|
|
324
|
+
cacheLayer?: CacheLayer
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Key for warm cache lookup
|
|
328
|
+
*/
|
|
329
|
+
warmCacheKey?: string
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* COLD tier: R2 storage layer
|
|
333
|
+
*/
|
|
334
|
+
r2Layer?: R2StorageLayer
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* COLD tier: Raw R2 bucket (alternative to r2Layer)
|
|
338
|
+
*/
|
|
339
|
+
r2Bucket?: R2Bucket
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Key for cold storage lookup
|
|
343
|
+
*/
|
|
344
|
+
coldStorageKey?: string
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Enable lazy initialization (default: true)
|
|
348
|
+
* When true, PGLite initializes on first query, not construction
|
|
349
|
+
*/
|
|
350
|
+
lazy?: boolean
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Enable pre-warming queries after initialization (default: true)
|
|
354
|
+
* Runs simple queries to warm up the query planner
|
|
355
|
+
*/
|
|
356
|
+
prewarm?: boolean
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Custom pre-warm queries to run after initialization
|
|
360
|
+
*/
|
|
361
|
+
prewarmQueries?: string[]
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Timeout for cold fetch in milliseconds (default: 5000)
|
|
365
|
+
*/
|
|
366
|
+
coldFetchTimeoutMs?: number
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Database name for PGLite (default: 'postgres')
|
|
370
|
+
*/
|
|
371
|
+
database?: string
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Additional PGLite options
|
|
375
|
+
*/
|
|
376
|
+
pgliteOptions?: Partial<PGliteOptions>
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Enable debug logging
|
|
380
|
+
*/
|
|
381
|
+
debug?: boolean
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Callback when initialization phase changes
|
|
385
|
+
*/
|
|
386
|
+
onPhaseChange?: (phase: ColdStartPhase, metrics: Partial<ColdStartMetrics>) => void
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Callback when initialization completes
|
|
390
|
+
*/
|
|
391
|
+
onReady?: (metrics: ColdStartMetrics) => void
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Callback when an error occurs
|
|
395
|
+
*/
|
|
396
|
+
onError?: (error: Error, phase: ColdStartPhase) => void
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Result from cold start optimization
|
|
401
|
+
*/
|
|
402
|
+
export interface ColdStartResult {
|
|
403
|
+
/** The initialized PGLite instance */
|
|
404
|
+
pglite: PGlite
|
|
405
|
+
/** Detailed metrics from initialization */
|
|
406
|
+
metrics: ColdStartMetrics
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Cold start optimizer interface
|
|
411
|
+
*
|
|
412
|
+
* This interface provides methods for managing PGLite lifecycle with
|
|
413
|
+
* optimized cold start handling. The optimizer handles:
|
|
414
|
+
*
|
|
415
|
+
* - Lazy initialization (deferred until first query)
|
|
416
|
+
* - WASM instance caching for warm starts
|
|
417
|
+
* - Parallel data fetching from tiered storage
|
|
418
|
+
* - Pre-warming queries for query planner cache
|
|
419
|
+
*
|
|
420
|
+
* ## Lifecycle Methods
|
|
421
|
+
*
|
|
422
|
+
* | Method | Description |
|
|
423
|
+
* |--------|-------------|
|
|
424
|
+
* | `getInstance()` | Get PGLite instance, initializing if needed |
|
|
425
|
+
* | `initialize()` | Force initialization (useful for pre-warming) |
|
|
426
|
+
* | `query()` | Execute query, auto-initializing if lazy |
|
|
427
|
+
* | `close()` | Release resources and remove from cache |
|
|
428
|
+
*
|
|
429
|
+
* ## Status Methods
|
|
430
|
+
*
|
|
431
|
+
* | Method | Description |
|
|
432
|
+
* |--------|-------------|
|
|
433
|
+
* | `isReady()` | Check if initialization is complete |
|
|
434
|
+
* | `isInitializing()` | Check if initialization is in progress |
|
|
435
|
+
* | `getPhase()` | Get current initialization phase |
|
|
436
|
+
* | `getMetrics()` | Get detailed timing metrics |
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* ```typescript
|
|
440
|
+
* const optimizer = createColdStartOptimizer(config)
|
|
441
|
+
*
|
|
442
|
+
* // Lazy initialization - first query triggers init
|
|
443
|
+
* const result = await optimizer.query('SELECT 1')
|
|
444
|
+
*
|
|
445
|
+
* // Check status
|
|
446
|
+
* console.log(`Ready: ${optimizer.isReady()}`)
|
|
447
|
+
* console.log(`Phase: ${optimizer.getPhase()}`)
|
|
448
|
+
*
|
|
449
|
+
* // Get metrics after initialization
|
|
450
|
+
* const metrics = optimizer.getMetrics()
|
|
451
|
+
* if (metrics) {
|
|
452
|
+
* console.log(`Cold start: ${metrics.totalMs}ms`)
|
|
453
|
+
* console.log(`Data source: ${metrics.dataSource}`)
|
|
454
|
+
* }
|
|
455
|
+
*
|
|
456
|
+
* // Clean up when done
|
|
457
|
+
* await optimizer.close()
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
export interface ColdStartOptimizer {
|
|
461
|
+
/**
|
|
462
|
+
* Get the PGLite instance
|
|
463
|
+
*
|
|
464
|
+
* If lazy initialization is enabled (default), this triggers initialization
|
|
465
|
+
* on first call. Subsequent calls return the cached instance.
|
|
466
|
+
*
|
|
467
|
+
* @returns The initialized PGLite instance
|
|
468
|
+
* @throws Error if the optimizer is closed or initialization failed
|
|
469
|
+
*/
|
|
470
|
+
getInstance(): Promise<PGlite>
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Check if PGLite is ready for queries
|
|
474
|
+
*
|
|
475
|
+
* @returns true if initialization is complete and queries can be executed
|
|
476
|
+
*/
|
|
477
|
+
isReady(): boolean
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Check if initialization is currently in progress
|
|
481
|
+
*
|
|
482
|
+
* @returns true if initialization has started but not completed
|
|
483
|
+
*/
|
|
484
|
+
isInitializing(): boolean
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Get the current initialization phase
|
|
488
|
+
*
|
|
489
|
+
* @returns The current phase of the cold start process
|
|
490
|
+
*/
|
|
491
|
+
getPhase(): ColdStartPhase
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Get detailed timing metrics from initialization
|
|
495
|
+
*
|
|
496
|
+
* Returns null if initialization has not completed.
|
|
497
|
+
*
|
|
498
|
+
* @returns Detailed metrics or null if not yet initialized
|
|
499
|
+
*/
|
|
500
|
+
getMetrics(): ColdStartMetrics | null
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Force initialization
|
|
504
|
+
*
|
|
505
|
+
* Use this to pre-warm the optimizer before the first query.
|
|
506
|
+
* Has no effect if already initialized.
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* ```typescript
|
|
510
|
+
* // Pre-warm in DO constructor
|
|
511
|
+
* this.pgliteInitPromise = optimizer.initialize()
|
|
512
|
+
*
|
|
513
|
+
* // Later in fetch handler
|
|
514
|
+
* await this.pgliteInitPromise
|
|
515
|
+
* const result = await optimizer.query('SELECT 1')
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
initialize(): Promise<void>
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Execute a SQL query
|
|
522
|
+
*
|
|
523
|
+
* Auto-initializes if lazy mode is enabled and not yet initialized.
|
|
524
|
+
*
|
|
525
|
+
* @param sql - The SQL query to execute
|
|
526
|
+
* @param params - Optional query parameters
|
|
527
|
+
* @returns Query result with rows
|
|
528
|
+
* @throws Error if the optimizer is closed or query fails
|
|
529
|
+
*/
|
|
530
|
+
query<T = unknown>(sql: string, params?: unknown[]): Promise<{ rows: T[] }>
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Close the optimizer and release resources
|
|
534
|
+
*
|
|
535
|
+
* Removes the PGLite instance from the warm start cache and closes
|
|
536
|
+
* the underlying database connection.
|
|
537
|
+
*/
|
|
538
|
+
close(): Promise<void>
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// ============================================================================
|
|
542
|
+
// WASM Caching Strategy
|
|
543
|
+
// ============================================================================
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Module-level cache for WASM initialization state
|
|
547
|
+
*
|
|
548
|
+
* In Cloudflare Workers, isolates persist across requests within the same
|
|
549
|
+
* execution context. This cache enables "warm starts" by reusing already-
|
|
550
|
+
* initialized PGLite instances instead of performing a full cold start.
|
|
551
|
+
*
|
|
552
|
+
* ## How Warm Starts Work
|
|
553
|
+
*
|
|
554
|
+
* 1. First request to a DO performs cold start (full initialization)
|
|
555
|
+
* 2. Initialized PGLite is cached with database-specific key
|
|
556
|
+
* 3. Subsequent requests find cached instance and reuse it
|
|
557
|
+
* 4. Cache is cleared when `close()` is called or isolate is evicted
|
|
558
|
+
*
|
|
559
|
+
* ## Cache Key Generation
|
|
560
|
+
*
|
|
561
|
+
* Keys are generated from: `${database}:${warmCacheKey}:${coldStorageKey}`
|
|
562
|
+
* This ensures different databases don't share instances.
|
|
563
|
+
*
|
|
564
|
+
* ## Memory Considerations
|
|
565
|
+
*
|
|
566
|
+
* Each cached instance uses ~64MB. With Workers' 128MB limit:
|
|
567
|
+
* - Only one instance can be effectively cached per isolate
|
|
568
|
+
* - Multiple databases should use ColdStartManager for lifecycle
|
|
569
|
+
*
|
|
570
|
+
* @internal
|
|
571
|
+
*/
|
|
572
|
+
interface WasmCacheEntry {
|
|
573
|
+
/** The initialized PGLite instance */
|
|
574
|
+
pglite: PGlite
|
|
575
|
+
/** When this entry was created */
|
|
576
|
+
createdAt: number
|
|
577
|
+
/** Metrics from initial cold start */
|
|
578
|
+
metrics: ColdStartMetrics
|
|
579
|
+
/** Number of times this cached instance was reused */
|
|
580
|
+
reuseCount: number
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Isolate-level WASM cache
|
|
585
|
+
*
|
|
586
|
+
* This Map persists across requests within the same Worker isolate.
|
|
587
|
+
* When the isolate is evicted, the cache is cleared automatically.
|
|
588
|
+
*
|
|
589
|
+
* @internal
|
|
590
|
+
*/
|
|
591
|
+
const wasmCache = new Map<string, WasmCacheEntry>()
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Generate a cache key for WASM instance caching
|
|
595
|
+
*
|
|
596
|
+
* The key should uniquely identify the WASM module and configuration.
|
|
597
|
+
*/
|
|
598
|
+
function generateCacheKey(config: ColdStartConfig): string {
|
|
599
|
+
// Use database name as primary key differentiator
|
|
600
|
+
const dbName = config.database ?? 'postgres'
|
|
601
|
+
// Include warm/cold keys to differentiate data sources
|
|
602
|
+
const warmKey = config.warmCacheKey ?? ''
|
|
603
|
+
const coldKey = config.coldStorageKey ?? ''
|
|
604
|
+
return `${dbName}:${warmKey}:${coldKey}`
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Get cached WASM instance if available
|
|
609
|
+
*/
|
|
610
|
+
function getCachedInstance(cacheKey: string): WasmCacheEntry | null {
|
|
611
|
+
const entry = wasmCache.get(cacheKey)
|
|
612
|
+
if (entry) {
|
|
613
|
+
entry.reuseCount++
|
|
614
|
+
return entry
|
|
615
|
+
}
|
|
616
|
+
return null
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Cache a WASM instance for warm starts
|
|
621
|
+
*/
|
|
622
|
+
function cacheInstance(cacheKey: string, entry: WasmCacheEntry): void {
|
|
623
|
+
wasmCache.set(cacheKey, entry)
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Clear all cached WASM instances
|
|
628
|
+
*
|
|
629
|
+
* Use this function to:
|
|
630
|
+
* - Reset state between tests
|
|
631
|
+
* - Force cold starts for benchmarking
|
|
632
|
+
* - Clear memory when switching databases
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* ```typescript
|
|
636
|
+
* // In tests
|
|
637
|
+
* beforeEach(() => {
|
|
638
|
+
* clearWasmCache()
|
|
639
|
+
* })
|
|
640
|
+
*
|
|
641
|
+
* // Force cold start for benchmarking
|
|
642
|
+
* clearWasmCache()
|
|
643
|
+
* const start = performance.now()
|
|
644
|
+
* await optimizer.initialize()
|
|
645
|
+
* const coldStartMs = performance.now() - start
|
|
646
|
+
* ```
|
|
647
|
+
*/
|
|
648
|
+
export function clearWasmCache(): void {
|
|
649
|
+
wasmCache.clear()
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Get WASM cache statistics
|
|
654
|
+
*
|
|
655
|
+
* Returns information about cached PGLite instances for monitoring
|
|
656
|
+
* and debugging purposes.
|
|
657
|
+
*
|
|
658
|
+
* @returns Object containing:
|
|
659
|
+
* - `size`: Number of cached instances
|
|
660
|
+
* - `entries`: Array of cache entries with reuse count and age
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```typescript
|
|
664
|
+
* const stats = getWasmCacheStats()
|
|
665
|
+
* console.log(`Cached instances: ${stats.size}`)
|
|
666
|
+
*
|
|
667
|
+
* for (const entry of stats.entries) {
|
|
668
|
+
* console.log(`${entry.key}: ${entry.reuseCount} reuses, ${entry.ageMs}ms old`)
|
|
669
|
+
* }
|
|
670
|
+
* ```
|
|
671
|
+
*/
|
|
672
|
+
export function getWasmCacheStats(): {
|
|
673
|
+
size: number
|
|
674
|
+
entries: Array<{ key: string; reuseCount: number; ageMs: number }>
|
|
675
|
+
} {
|
|
676
|
+
const now = Date.now()
|
|
677
|
+
const entries: Array<{ key: string; reuseCount: number; ageMs: number }> = []
|
|
678
|
+
|
|
679
|
+
for (const [key, entry] of wasmCache.entries()) {
|
|
680
|
+
entries.push({
|
|
681
|
+
key,
|
|
682
|
+
reuseCount: entry.reuseCount,
|
|
683
|
+
ageMs: now - entry.createdAt,
|
|
684
|
+
})
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
return { size: wasmCache.size, entries }
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// ============================================================================
|
|
691
|
+
// Parallel Loading Strategy
|
|
692
|
+
// ============================================================================
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Result from parallel data fetch
|
|
696
|
+
*/
|
|
697
|
+
interface ParallelFetchResult {
|
|
698
|
+
data: Uint8Array | null
|
|
699
|
+
source: 'hot' | 'warm' | 'cold' | 'empty'
|
|
700
|
+
fetchTimeMs: number
|
|
701
|
+
/** Individual tier timing details */
|
|
702
|
+
tierTimings?: {
|
|
703
|
+
hot?: { attempted: boolean; durationMs: number; success: boolean }
|
|
704
|
+
warm?: { attempted: boolean; durationMs: number; success: boolean }
|
|
705
|
+
cold?: { attempted: boolean; durationMs: number; success: boolean }
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Fetch data from tiered storage in parallel with WASM preparation
|
|
711
|
+
*
|
|
712
|
+
* This function implements the parallel loading pattern:
|
|
713
|
+
* - WASM module is already pre-compiled (zero prep time)
|
|
714
|
+
* - Data fetch from warm/cold tiers happens during that "zero time"
|
|
715
|
+
* - We race warm and cold fetches to get data ASAP
|
|
716
|
+
*/
|
|
717
|
+
async function parallelFetchData(
|
|
718
|
+
config: ColdStartConfig,
|
|
719
|
+
debug: (msg: string) => void
|
|
720
|
+
): Promise<ParallelFetchResult> {
|
|
721
|
+
const startTime = performance.now()
|
|
722
|
+
const tierTimings: ParallelFetchResult['tierTimings'] = {}
|
|
723
|
+
|
|
724
|
+
// Priority 1: Hot data (synchronous, already in memory)
|
|
725
|
+
if (config.hotData) {
|
|
726
|
+
const hotStart = performance.now()
|
|
727
|
+
debug('Using hot data from DO SQLite')
|
|
728
|
+
const data =
|
|
729
|
+
config.hotData instanceof Uint8Array
|
|
730
|
+
? config.hotData
|
|
731
|
+
: new Uint8Array(config.hotData)
|
|
732
|
+
const hotDuration = performance.now() - hotStart
|
|
733
|
+
tierTimings.hot = { attempted: true, durationMs: hotDuration, success: true }
|
|
734
|
+
return {
|
|
735
|
+
data,
|
|
736
|
+
source: 'hot',
|
|
737
|
+
fetchTimeMs: performance.now() - startTime,
|
|
738
|
+
tierTimings,
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Set up parallel fetches for warm and cold tiers
|
|
743
|
+
interface TierFetchResult extends ParallelFetchResult {
|
|
744
|
+
tierType: 'warm' | 'cold'
|
|
745
|
+
}
|
|
746
|
+
const fetchPromises: Promise<TierFetchResult>[] = []
|
|
747
|
+
|
|
748
|
+
// Warm tier fetch (Cloudflare Cache - FREE!)
|
|
749
|
+
if (config.cacheLayer && config.warmCacheKey) {
|
|
750
|
+
const warmPromise = (async (): Promise<TierFetchResult> => {
|
|
751
|
+
const warmStart = performance.now()
|
|
752
|
+
try {
|
|
753
|
+
const data = await config.cacheLayer!.get(config.warmCacheKey!)
|
|
754
|
+
const durationMs = performance.now() - warmStart
|
|
755
|
+
if (data) {
|
|
756
|
+
debug(`Warm cache hit: ${data.length} bytes in ${durationMs.toFixed(2)}ms`)
|
|
757
|
+
return {
|
|
758
|
+
data,
|
|
759
|
+
source: 'warm',
|
|
760
|
+
fetchTimeMs: durationMs,
|
|
761
|
+
tierType: 'warm',
|
|
762
|
+
tierTimings: { warm: { attempted: true, durationMs, success: true } },
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return {
|
|
766
|
+
data: null,
|
|
767
|
+
source: 'warm',
|
|
768
|
+
fetchTimeMs: durationMs,
|
|
769
|
+
tierType: 'warm',
|
|
770
|
+
tierTimings: { warm: { attempted: true, durationMs, success: false } },
|
|
771
|
+
}
|
|
772
|
+
} catch (err) {
|
|
773
|
+
const durationMs = performance.now() - warmStart
|
|
774
|
+
debug(`Warm cache error after ${durationMs.toFixed(2)}ms: ${err}`)
|
|
775
|
+
return {
|
|
776
|
+
data: null,
|
|
777
|
+
source: 'warm',
|
|
778
|
+
fetchTimeMs: durationMs,
|
|
779
|
+
tierType: 'warm',
|
|
780
|
+
tierTimings: { warm: { attempted: true, durationMs, success: false } },
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
})()
|
|
784
|
+
fetchPromises.push(warmPromise)
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Cold tier fetch (R2 - durable storage)
|
|
788
|
+
if ((config.r2Layer || config.r2Bucket) && config.coldStorageKey) {
|
|
789
|
+
const coldPromise = (async (): Promise<TierFetchResult> => {
|
|
790
|
+
const coldStart = performance.now()
|
|
791
|
+
const timeout = config.coldFetchTimeoutMs ?? 5000
|
|
792
|
+
|
|
793
|
+
try {
|
|
794
|
+
let data: Uint8Array | null = null
|
|
795
|
+
|
|
796
|
+
if (config.r2Layer) {
|
|
797
|
+
// Use R2StorageLayer with timeout
|
|
798
|
+
data = await Promise.race([
|
|
799
|
+
config.r2Layer.get(config.coldStorageKey!),
|
|
800
|
+
new Promise<null>((_, reject) =>
|
|
801
|
+
setTimeout(() => reject(new Error('Cold fetch timeout')), timeout)
|
|
802
|
+
),
|
|
803
|
+
])
|
|
804
|
+
} else if (config.r2Bucket) {
|
|
805
|
+
// Use raw R2Bucket with timeout
|
|
806
|
+
const obj = await Promise.race([
|
|
807
|
+
config.r2Bucket.get(config.coldStorageKey!),
|
|
808
|
+
new Promise<null>((_, reject) =>
|
|
809
|
+
setTimeout(() => reject(new Error('Cold fetch timeout')), timeout)
|
|
810
|
+
),
|
|
811
|
+
])
|
|
812
|
+
if (obj) {
|
|
813
|
+
data = new Uint8Array(await obj.arrayBuffer())
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const durationMs = performance.now() - coldStart
|
|
818
|
+
if (data) {
|
|
819
|
+
debug(`Cold storage hit: ${data.length} bytes in ${durationMs.toFixed(2)}ms`)
|
|
820
|
+
return {
|
|
821
|
+
data,
|
|
822
|
+
source: 'cold',
|
|
823
|
+
fetchTimeMs: durationMs,
|
|
824
|
+
tierType: 'cold',
|
|
825
|
+
tierTimings: { cold: { attempted: true, durationMs, success: true } },
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return {
|
|
829
|
+
data: null,
|
|
830
|
+
source: 'cold',
|
|
831
|
+
fetchTimeMs: durationMs,
|
|
832
|
+
tierType: 'cold',
|
|
833
|
+
tierTimings: { cold: { attempted: true, durationMs, success: false } },
|
|
834
|
+
}
|
|
835
|
+
} catch (err) {
|
|
836
|
+
const durationMs = performance.now() - coldStart
|
|
837
|
+
debug(`Cold storage error after ${durationMs.toFixed(2)}ms: ${err}`)
|
|
838
|
+
return {
|
|
839
|
+
data: null,
|
|
840
|
+
source: 'cold',
|
|
841
|
+
fetchTimeMs: durationMs,
|
|
842
|
+
tierType: 'cold',
|
|
843
|
+
tierTimings: { cold: { attempted: true, durationMs, success: false } },
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
})()
|
|
847
|
+
fetchPromises.push(coldPromise)
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Execute fetches in parallel, return first successful result
|
|
851
|
+
if (fetchPromises.length > 0) {
|
|
852
|
+
debug(`Starting ${fetchPromises.length} parallel fetches`)
|
|
853
|
+
|
|
854
|
+
// Use Promise.all to get all results, then pick the best one
|
|
855
|
+
// (We want metrics from all attempts, not just the winner)
|
|
856
|
+
const results = await Promise.all(fetchPromises)
|
|
857
|
+
|
|
858
|
+
// Merge all tier timings
|
|
859
|
+
for (const result of results) {
|
|
860
|
+
if (result.tierTimings) {
|
|
861
|
+
Object.assign(tierTimings, result.tierTimings)
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Prefer warm over cold (warm is faster and FREE)
|
|
866
|
+
const warmResult = results.find((r) => r.source === 'warm' && r.data)
|
|
867
|
+
if (warmResult?.data) {
|
|
868
|
+
return { ...warmResult, tierTimings }
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const coldResult = results.find((r) => r.source === 'cold' && r.data)
|
|
872
|
+
if (coldResult?.data) {
|
|
873
|
+
return { ...coldResult, tierTimings }
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// No data found anywhere
|
|
878
|
+
debug('No data found, returning empty')
|
|
879
|
+
return {
|
|
880
|
+
data: null,
|
|
881
|
+
source: 'empty',
|
|
882
|
+
fetchTimeMs: performance.now() - startTime,
|
|
883
|
+
tierTimings,
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// ============================================================================
|
|
888
|
+
// Lazy Initialization Pattern
|
|
889
|
+
// ============================================================================
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Default pre-warm queries to run after initialization
|
|
893
|
+
*
|
|
894
|
+
* These queries warm up:
|
|
895
|
+
* - Query planner cache
|
|
896
|
+
* - System catalog cache
|
|
897
|
+
* - Connection state
|
|
898
|
+
*/
|
|
899
|
+
const DEFAULT_PREWARM_QUERIES = [
|
|
900
|
+
'SELECT 1', // Basic connectivity
|
|
901
|
+
'SELECT current_database()', // Database state
|
|
902
|
+
'SELECT version()', // System catalog access
|
|
903
|
+
]
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Run pre-warm queries to optimize subsequent query latency
|
|
907
|
+
*/
|
|
908
|
+
async function runPrewarmQueries(
|
|
909
|
+
pglite: PGlite,
|
|
910
|
+
queries: string[],
|
|
911
|
+
debug: (msg: string) => void
|
|
912
|
+
): Promise<number> {
|
|
913
|
+
const startTime = performance.now()
|
|
914
|
+
|
|
915
|
+
for (const sql of queries) {
|
|
916
|
+
try {
|
|
917
|
+
await pglite.query(sql)
|
|
918
|
+
} catch {
|
|
919
|
+
// Ignore errors in pre-warm queries (table might not exist, etc.)
|
|
920
|
+
debug(`Prewarm query failed (ignored): ${sql}`)
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const durationMs = performance.now() - startTime
|
|
925
|
+
debug(`Prewarm completed: ${queries.length} queries in ${durationMs.toFixed(2)}ms`)
|
|
926
|
+
return durationMs
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Run pre-warm queries with individual timing tracking
|
|
931
|
+
*/
|
|
932
|
+
async function runPrewarmQueriesWithTimings(
|
|
933
|
+
pglite: PGlite,
|
|
934
|
+
queries: string[],
|
|
935
|
+
debug: (msg: string) => void
|
|
936
|
+
): Promise<Array<{ query: string; durationMs: number }>> {
|
|
937
|
+
const timings: Array<{ query: string; durationMs: number }> = []
|
|
938
|
+
|
|
939
|
+
for (const sql of queries) {
|
|
940
|
+
const queryStart = performance.now()
|
|
941
|
+
try {
|
|
942
|
+
await pglite.query(sql)
|
|
943
|
+
} catch {
|
|
944
|
+
// Ignore errors in pre-warm queries (table might not exist, etc.)
|
|
945
|
+
debug(`Prewarm query failed (ignored): ${sql}`)
|
|
946
|
+
}
|
|
947
|
+
const queryEnd = performance.now()
|
|
948
|
+
timings.push({ query: sql, durationMs: queryEnd - queryStart })
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const totalMs = timings.reduce((sum, t) => sum + t.durationMs, 0)
|
|
952
|
+
debug(`Prewarm completed: ${queries.length} queries in ${totalMs.toFixed(2)}ms`)
|
|
953
|
+
return timings
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// ============================================================================
|
|
957
|
+
// Memory Allocation Optimization
|
|
958
|
+
// ============================================================================
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Buffer pool for reducing memory allocation overhead
|
|
962
|
+
*
|
|
963
|
+
* ## Memory Constraints
|
|
964
|
+
*
|
|
965
|
+
* Cloudflare Workers have a 128MB memory limit. PGLite uses approximately:
|
|
966
|
+
* - ~15MB for WASM + data + JS runtime
|
|
967
|
+
* - ~16MB for shared_buffers
|
|
968
|
+
* - ~16MB for WAL + buffers
|
|
969
|
+
* - ~10MB for PostgreSQL catalog/metadata
|
|
970
|
+
*
|
|
971
|
+
* This leaves ~71MB for queries, results, and runtime operations.
|
|
972
|
+
*
|
|
973
|
+
* ## Buffer Pool Strategy
|
|
974
|
+
*
|
|
975
|
+
* Pre-allocated buffers reduce allocation overhead during query execution:
|
|
976
|
+
*
|
|
977
|
+
* | Size | Bytes | Use Case |
|
|
978
|
+
* |------|-------|----------|
|
|
979
|
+
* | small | 4KB | Small operations, metadata |
|
|
980
|
+
* | medium | 64KB | Typical query results |
|
|
981
|
+
* | large | 1MB | Large result sets, bulk operations |
|
|
982
|
+
*
|
|
983
|
+
* ## Usage Pattern
|
|
984
|
+
*
|
|
985
|
+
* ```typescript
|
|
986
|
+
* const buffer = getPooledBuffer('medium')
|
|
987
|
+
* try {
|
|
988
|
+
* // Use buffer for operation
|
|
989
|
+
* const view = new Uint8Array(buffer)
|
|
990
|
+
* // ... process data
|
|
991
|
+
* } finally {
|
|
992
|
+
* returnPooledBuffer(buffer, 'medium')
|
|
993
|
+
* }
|
|
994
|
+
* ```
|
|
995
|
+
*
|
|
996
|
+
* @internal
|
|
997
|
+
*/
|
|
998
|
+
interface BufferPool {
|
|
999
|
+
small: ArrayBuffer | null // 4KB for small operations
|
|
1000
|
+
medium: ArrayBuffer | null // 64KB for typical queries
|
|
1001
|
+
large: ArrayBuffer | null // 1MB for large result sets
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Buffer pool for reducing allocation overhead
|
|
1006
|
+
* @internal
|
|
1007
|
+
*/
|
|
1008
|
+
const bufferPool: BufferPool = {
|
|
1009
|
+
small: null,
|
|
1010
|
+
medium: null,
|
|
1011
|
+
large: null,
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Get a buffer from the pool or allocate a new one
|
|
1016
|
+
*
|
|
1017
|
+
* Buffers are returned from the pool if available, otherwise a new
|
|
1018
|
+
* buffer is allocated. This reduces allocation overhead for repeated
|
|
1019
|
+
* operations of similar size.
|
|
1020
|
+
*
|
|
1021
|
+
* @param size - Buffer size category: 'small' (4KB), 'medium' (64KB), or 'large' (1MB)
|
|
1022
|
+
* @returns ArrayBuffer of the specified size
|
|
1023
|
+
*
|
|
1024
|
+
* @example
|
|
1025
|
+
* ```typescript
|
|
1026
|
+
* const buffer = getPooledBuffer('medium') // 64KB
|
|
1027
|
+
* const view = new Uint8Array(buffer)
|
|
1028
|
+
* // Use the buffer...
|
|
1029
|
+
* returnPooledBuffer(buffer, 'medium')
|
|
1030
|
+
* ```
|
|
1031
|
+
*/
|
|
1032
|
+
export function getPooledBuffer(
|
|
1033
|
+
size: 'small' | 'medium' | 'large'
|
|
1034
|
+
): ArrayBuffer {
|
|
1035
|
+
const sizes = { small: 4096, medium: 65536, large: 1048576 }
|
|
1036
|
+
|
|
1037
|
+
if (bufferPool[size]) {
|
|
1038
|
+
const buffer = bufferPool[size]!
|
|
1039
|
+
bufferPool[size] = null
|
|
1040
|
+
return buffer
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
return new ArrayBuffer(sizes[size])
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* Return a buffer to the pool for reuse
|
|
1048
|
+
*
|
|
1049
|
+
* Only returns the buffer if it hasn't been detached (transferred).
|
|
1050
|
+
* Detached buffers have byteLength of 0.
|
|
1051
|
+
*
|
|
1052
|
+
* @param buffer - The buffer to return
|
|
1053
|
+
* @param size - The size category of the buffer
|
|
1054
|
+
*
|
|
1055
|
+
* @example
|
|
1056
|
+
* ```typescript
|
|
1057
|
+
* const buffer = getPooledBuffer('small')
|
|
1058
|
+
* // Use buffer...
|
|
1059
|
+
* returnPooledBuffer(buffer, 'small')
|
|
1060
|
+
* ```
|
|
1061
|
+
*/
|
|
1062
|
+
export function returnPooledBuffer(
|
|
1063
|
+
buffer: ArrayBuffer,
|
|
1064
|
+
size: 'small' | 'medium' | 'large'
|
|
1065
|
+
): void {
|
|
1066
|
+
// Only pool if the buffer hasn't been transferred
|
|
1067
|
+
if (buffer.byteLength > 0) {
|
|
1068
|
+
bufferPool[size] = buffer
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Clear the buffer pool
|
|
1074
|
+
*
|
|
1075
|
+
* Use this to:
|
|
1076
|
+
* - Reset state between tests
|
|
1077
|
+
* - Free memory when it's no longer needed
|
|
1078
|
+
* - Prepare for a clean shutdown
|
|
1079
|
+
*
|
|
1080
|
+
* @example
|
|
1081
|
+
* ```typescript
|
|
1082
|
+
* // In tests
|
|
1083
|
+
* afterEach(() => {
|
|
1084
|
+
* clearBufferPool()
|
|
1085
|
+
* })
|
|
1086
|
+
* ```
|
|
1087
|
+
*/
|
|
1088
|
+
export function clearBufferPool(): void {
|
|
1089
|
+
bufferPool.small = null
|
|
1090
|
+
bufferPool.medium = null
|
|
1091
|
+
bufferPool.large = null
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// ============================================================================
|
|
1095
|
+
// Cold Start Optimizer Implementation
|
|
1096
|
+
// ============================================================================
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Create a cold start optimized PGLite instance
|
|
1100
|
+
*
|
|
1101
|
+
* This is the main entry point for optimized PGLite initialization in Workers.
|
|
1102
|
+
*
|
|
1103
|
+
* @example
|
|
1104
|
+
* ```typescript
|
|
1105
|
+
* import pgliteWasm from './pglite.wasm'
|
|
1106
|
+
*
|
|
1107
|
+
* const optimizer = createColdStartOptimizer({
|
|
1108
|
+
* wasmModule: pgliteWasm,
|
|
1109
|
+
* cacheLayer: myCacheLayer,
|
|
1110
|
+
* warmCacheKey: `db:${databaseId}`,
|
|
1111
|
+
* r2Layer: myR2Layer,
|
|
1112
|
+
* coldStorageKey: `databases/${databaseId}/data.tar`,
|
|
1113
|
+
* lazy: true,
|
|
1114
|
+
* prewarm: true,
|
|
1115
|
+
* })
|
|
1116
|
+
*
|
|
1117
|
+
* // First query triggers initialization
|
|
1118
|
+
* const result = await optimizer.query('SELECT * FROM users')
|
|
1119
|
+
*
|
|
1120
|
+
* // Check metrics
|
|
1121
|
+
* const metrics = optimizer.getMetrics()
|
|
1122
|
+
* console.log(`Cold start: ${metrics?.totalMs}ms from ${metrics?.dataSource}`)
|
|
1123
|
+
* ```
|
|
1124
|
+
*/
|
|
1125
|
+
export function createColdStartOptimizer(
|
|
1126
|
+
config: ColdStartConfig
|
|
1127
|
+
): ColdStartOptimizer {
|
|
1128
|
+
const {
|
|
1129
|
+
lazy = true,
|
|
1130
|
+
prewarm = true,
|
|
1131
|
+
prewarmQueries = DEFAULT_PREWARM_QUERIES,
|
|
1132
|
+
debug: enableDebug = false,
|
|
1133
|
+
onPhaseChange,
|
|
1134
|
+
onReady,
|
|
1135
|
+
onError,
|
|
1136
|
+
} = config
|
|
1137
|
+
|
|
1138
|
+
// State
|
|
1139
|
+
let pglite: PGlite | null = null
|
|
1140
|
+
let initPromise: Promise<void> | null = null
|
|
1141
|
+
let phase: ColdStartPhase = 'not_started'
|
|
1142
|
+
let metrics: ColdStartMetrics | null = null
|
|
1143
|
+
let closed = false
|
|
1144
|
+
|
|
1145
|
+
// Debug logging
|
|
1146
|
+
const debug = enableDebug ? console.log.bind(console, '[ColdStartOptimizer]') : () => {}
|
|
1147
|
+
|
|
1148
|
+
// Generate cache key for WASM caching
|
|
1149
|
+
const cacheKey = generateCacheKey(config)
|
|
1150
|
+
|
|
1151
|
+
/**
|
|
1152
|
+
* Update phase and notify callback
|
|
1153
|
+
*/
|
|
1154
|
+
function setPhase(newPhase: ColdStartPhase): void {
|
|
1155
|
+
phase = newPhase
|
|
1156
|
+
debug(`Phase: ${newPhase}`)
|
|
1157
|
+
onPhaseChange?.(newPhase, metrics ?? {})
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* Perform the actual initialization
|
|
1162
|
+
*/
|
|
1163
|
+
async function doInitialize(): Promise<void> {
|
|
1164
|
+
const startTime = performance.now()
|
|
1165
|
+
const startedAt = Date.now()
|
|
1166
|
+
const phaseBreakdown: PhaseTimingBreakdown[] = []
|
|
1167
|
+
|
|
1168
|
+
// Initialize metrics
|
|
1169
|
+
const initMetrics: ColdStartMetrics = {
|
|
1170
|
+
totalMs: 0,
|
|
1171
|
+
wasmPrepMs: 0,
|
|
1172
|
+
dataFetchMs: 0,
|
|
1173
|
+
parallelSavingsMs: 0,
|
|
1174
|
+
pgliteInitMs: 0,
|
|
1175
|
+
firstQueryMs: 0,
|
|
1176
|
+
dataSource: 'empty',
|
|
1177
|
+
dataSizeBytes: 0,
|
|
1178
|
+
isWarmStart: false,
|
|
1179
|
+
phase: 'not_started',
|
|
1180
|
+
startedAt,
|
|
1181
|
+
completedAt: 0,
|
|
1182
|
+
phaseBreakdown: [],
|
|
1183
|
+
prewarmTimings: [],
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
try {
|
|
1187
|
+
// Check WASM cache first (warm start)
|
|
1188
|
+
const cached = getCachedInstance(cacheKey)
|
|
1189
|
+
if (cached) {
|
|
1190
|
+
debug('Warm start: using cached PGLite instance')
|
|
1191
|
+
pglite = cached.pglite
|
|
1192
|
+
initMetrics.isWarmStart = true
|
|
1193
|
+
initMetrics.totalMs = performance.now() - startTime
|
|
1194
|
+
initMetrics.completedAt = Date.now()
|
|
1195
|
+
initMetrics.phase = 'ready'
|
|
1196
|
+
initMetrics.wasmStrategy = {
|
|
1197
|
+
strategy: 'cached',
|
|
1198
|
+
moduleLoadMs: 0,
|
|
1199
|
+
instantiateMs: 0,
|
|
1200
|
+
totalMs: 0,
|
|
1201
|
+
fromCache: true,
|
|
1202
|
+
}
|
|
1203
|
+
metrics = initMetrics
|
|
1204
|
+
setPhase('ready')
|
|
1205
|
+
onReady?.(initMetrics)
|
|
1206
|
+
return
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// Cold start: full initialization
|
|
1210
|
+
debug('Cold start: initializing new PGLite instance')
|
|
1211
|
+
|
|
1212
|
+
// Phase 1: WASM preparation (usually instant for pre-compiled modules)
|
|
1213
|
+
setPhase('wasm_loading')
|
|
1214
|
+
const wasmPhaseStart = performance.now()
|
|
1215
|
+
// WASM module is pre-compiled via static import, nothing to do here
|
|
1216
|
+
// But we track it for comparison with other strategies
|
|
1217
|
+
const wasmPhaseEnd = performance.now()
|
|
1218
|
+
initMetrics.wasmPrepMs = wasmPhaseEnd - wasmPhaseStart
|
|
1219
|
+
|
|
1220
|
+
// Estimate WASM module size based on variant
|
|
1221
|
+
// Tiny variant: ~4.8MB (target: under 5MB), Full variant: ~13MB
|
|
1222
|
+
const variant = (config.pgliteOptions as { variant?: 'tiny' | 'full' } | undefined)?.variant
|
|
1223
|
+
const estimatedModuleSizeBytes = variant === 'tiny' ? 4.8 * 1024 * 1024 : 13 * 1024 * 1024
|
|
1224
|
+
|
|
1225
|
+
initMetrics.wasmStrategy = {
|
|
1226
|
+
strategy: 'static_import',
|
|
1227
|
+
moduleLoadMs: 0, // Pre-compiled at build time
|
|
1228
|
+
instantiateMs: initMetrics.wasmPrepMs,
|
|
1229
|
+
totalMs: initMetrics.wasmPrepMs,
|
|
1230
|
+
fromCache: false,
|
|
1231
|
+
moduleSizeBytes: estimatedModuleSizeBytes,
|
|
1232
|
+
}
|
|
1233
|
+
phaseBreakdown.push({
|
|
1234
|
+
phase: 'wasm_loading',
|
|
1235
|
+
startTime: wasmPhaseStart,
|
|
1236
|
+
endTime: wasmPhaseEnd,
|
|
1237
|
+
durationMs: initMetrics.wasmPrepMs,
|
|
1238
|
+
})
|
|
1239
|
+
|
|
1240
|
+
// Phase 2: Parallel data fetch
|
|
1241
|
+
// This happens "in parallel" with the zero-time WASM prep
|
|
1242
|
+
// The real savings come from starting this early
|
|
1243
|
+
setPhase('parallel_loading')
|
|
1244
|
+
const dataFetchStart = performance.now()
|
|
1245
|
+
const fetchResult = await parallelFetchData(config, debug)
|
|
1246
|
+
const dataFetchEnd = performance.now()
|
|
1247
|
+
initMetrics.dataFetchMs = fetchResult.fetchTimeMs
|
|
1248
|
+
initMetrics.dataSource = fetchResult.source
|
|
1249
|
+
initMetrics.dataSizeBytes = fetchResult.data?.length ?? 0
|
|
1250
|
+
initMetrics.tierFetchTimings = fetchResult.tierTimings
|
|
1251
|
+
|
|
1252
|
+
phaseBreakdown.push({
|
|
1253
|
+
phase: 'parallel_loading',
|
|
1254
|
+
startTime: dataFetchStart,
|
|
1255
|
+
endTime: dataFetchEnd,
|
|
1256
|
+
durationMs: dataFetchEnd - dataFetchStart,
|
|
1257
|
+
subPhases: Object.entries(fetchResult.tierTimings || {}).map(([tier, timing]) => ({
|
|
1258
|
+
name: `${tier}_fetch`,
|
|
1259
|
+
startTime: dataFetchStart,
|
|
1260
|
+
endTime: dataFetchStart + timing.durationMs,
|
|
1261
|
+
durationMs: timing.durationMs,
|
|
1262
|
+
})),
|
|
1263
|
+
})
|
|
1264
|
+
|
|
1265
|
+
// Calculate parallel savings (would have been sequential without this)
|
|
1266
|
+
// Since WASM prep is ~0ms, parallel savings = max(0, wasmPrepMs)
|
|
1267
|
+
initMetrics.parallelSavingsMs = Math.max(
|
|
1268
|
+
0,
|
|
1269
|
+
initMetrics.wasmPrepMs - initMetrics.dataFetchMs
|
|
1270
|
+
)
|
|
1271
|
+
|
|
1272
|
+
// Phase 3: PGLite initialization
|
|
1273
|
+
setPhase('pglite_init')
|
|
1274
|
+
const pglitePhaseStart = performance.now()
|
|
1275
|
+
|
|
1276
|
+
// Track dynamic import time separately
|
|
1277
|
+
const importStart = performance.now()
|
|
1278
|
+
const { PGlite } = await import('@dotdo/pglite')
|
|
1279
|
+
const importEnd = performance.now()
|
|
1280
|
+
initMetrics.pgliteImportMs = importEnd - importStart
|
|
1281
|
+
|
|
1282
|
+
// Build PGLite configuration
|
|
1283
|
+
const pgliteConfig: PGliteOptions = {
|
|
1284
|
+
...config.pgliteOptions,
|
|
1285
|
+
wasmModule: config.wasmModule,
|
|
1286
|
+
database: config.database ?? 'postgres',
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// Add filesystem bundle if data was fetched
|
|
1290
|
+
if (fetchResult.data) {
|
|
1291
|
+
pgliteConfig.fsBundle = new Blob([fetchResult.data])
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// Create PGLite instance
|
|
1295
|
+
const createStart = performance.now()
|
|
1296
|
+
pglite = await PGlite.create(pgliteConfig)
|
|
1297
|
+
const createEnd = performance.now()
|
|
1298
|
+
|
|
1299
|
+
const pglitePhaseEnd = performance.now()
|
|
1300
|
+
initMetrics.pgliteInitMs = pglitePhaseEnd - pglitePhaseStart
|
|
1301
|
+
|
|
1302
|
+
phaseBreakdown.push({
|
|
1303
|
+
phase: 'pglite_init',
|
|
1304
|
+
startTime: pglitePhaseStart,
|
|
1305
|
+
endTime: pglitePhaseEnd,
|
|
1306
|
+
durationMs: initMetrics.pgliteInitMs,
|
|
1307
|
+
subPhases: [
|
|
1308
|
+
{
|
|
1309
|
+
name: 'dynamic_import',
|
|
1310
|
+
startTime: importStart,
|
|
1311
|
+
endTime: importEnd,
|
|
1312
|
+
durationMs: initMetrics.pgliteImportMs,
|
|
1313
|
+
},
|
|
1314
|
+
{
|
|
1315
|
+
name: 'pglite_create',
|
|
1316
|
+
startTime: createStart,
|
|
1317
|
+
endTime: createEnd,
|
|
1318
|
+
durationMs: createEnd - createStart,
|
|
1319
|
+
},
|
|
1320
|
+
],
|
|
1321
|
+
})
|
|
1322
|
+
|
|
1323
|
+
// Phase 4: Pre-warming (optional but recommended)
|
|
1324
|
+
if (prewarm && prewarmQueries.length > 0) {
|
|
1325
|
+
const prewarmStart = performance.now()
|
|
1326
|
+
const prewarmTimings = await runPrewarmQueriesWithTimings(pglite, prewarmQueries, debug)
|
|
1327
|
+
const prewarmEnd = performance.now()
|
|
1328
|
+
initMetrics.firstQueryMs = prewarmEnd - prewarmStart
|
|
1329
|
+
initMetrics.prewarmTimings = prewarmTimings
|
|
1330
|
+
|
|
1331
|
+
phaseBreakdown.push({
|
|
1332
|
+
phase: 'ready', // Prewarm is the final phase before ready
|
|
1333
|
+
startTime: prewarmStart,
|
|
1334
|
+
endTime: prewarmEnd,
|
|
1335
|
+
durationMs: initMetrics.firstQueryMs,
|
|
1336
|
+
subPhases: prewarmTimings.map((t, i) => ({
|
|
1337
|
+
name: `prewarm_query_${i}`,
|
|
1338
|
+
startTime: prewarmStart,
|
|
1339
|
+
endTime: prewarmStart + t.durationMs,
|
|
1340
|
+
durationMs: t.durationMs,
|
|
1341
|
+
})),
|
|
1342
|
+
})
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
// Complete initialization
|
|
1346
|
+
initMetrics.totalMs = performance.now() - startTime
|
|
1347
|
+
initMetrics.completedAt = Date.now()
|
|
1348
|
+
initMetrics.phase = 'ready'
|
|
1349
|
+
initMetrics.phaseBreakdown = phaseBreakdown
|
|
1350
|
+
|
|
1351
|
+
// Try to capture memory usage
|
|
1352
|
+
if (typeof process !== 'undefined' && process.memoryUsage) {
|
|
1353
|
+
initMetrics.memoryUsedMB = process.memoryUsage().heapUsed / (1024 * 1024)
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
metrics = initMetrics
|
|
1357
|
+
|
|
1358
|
+
// Cache for warm starts
|
|
1359
|
+
cacheInstance(cacheKey, {
|
|
1360
|
+
pglite,
|
|
1361
|
+
createdAt: Date.now(),
|
|
1362
|
+
metrics: initMetrics,
|
|
1363
|
+
reuseCount: 0,
|
|
1364
|
+
})
|
|
1365
|
+
|
|
1366
|
+
setPhase('ready')
|
|
1367
|
+
onReady?.(initMetrics)
|
|
1368
|
+
|
|
1369
|
+
debug(
|
|
1370
|
+
`Cold start complete: ${initMetrics.totalMs.toFixed(2)}ms ` +
|
|
1371
|
+
`(data: ${initMetrics.dataFetchMs.toFixed(2)}ms from ${initMetrics.dataSource}, ` +
|
|
1372
|
+
`pglite: ${initMetrics.pgliteInitMs.toFixed(2)}ms, ` +
|
|
1373
|
+
`import: ${initMetrics.pgliteImportMs?.toFixed(2)}ms)`
|
|
1374
|
+
)
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
setPhase('error')
|
|
1377
|
+
onError?.(error as Error, phase)
|
|
1378
|
+
throw error
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* Ensure PGLite is initialized
|
|
1384
|
+
*/
|
|
1385
|
+
async function ensureInitialized(): Promise<void> {
|
|
1386
|
+
if (pglite && phase === 'ready') return
|
|
1387
|
+
|
|
1388
|
+
if (closed) {
|
|
1389
|
+
throw new Error('ColdStartOptimizer is closed')
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
if (phase === 'error') {
|
|
1393
|
+
throw new Error('ColdStartOptimizer initialization failed')
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
if (!initPromise) {
|
|
1397
|
+
initPromise = doInitialize()
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
await initPromise
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// Start initialization immediately if not lazy
|
|
1404
|
+
if (!lazy) {
|
|
1405
|
+
initPromise = doInitialize()
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
return {
|
|
1409
|
+
async getInstance(): Promise<PGlite> {
|
|
1410
|
+
await ensureInitialized()
|
|
1411
|
+
return pglite!
|
|
1412
|
+
},
|
|
1413
|
+
|
|
1414
|
+
isReady(): boolean {
|
|
1415
|
+
return phase === 'ready'
|
|
1416
|
+
},
|
|
1417
|
+
|
|
1418
|
+
isInitializing(): boolean {
|
|
1419
|
+
return (
|
|
1420
|
+
phase !== 'not_started' &&
|
|
1421
|
+
phase !== 'ready' &&
|
|
1422
|
+
phase !== 'error'
|
|
1423
|
+
)
|
|
1424
|
+
},
|
|
1425
|
+
|
|
1426
|
+
getPhase(): ColdStartPhase {
|
|
1427
|
+
return phase
|
|
1428
|
+
},
|
|
1429
|
+
|
|
1430
|
+
getMetrics(): ColdStartMetrics | null {
|
|
1431
|
+
return metrics
|
|
1432
|
+
},
|
|
1433
|
+
|
|
1434
|
+
async initialize(): Promise<void> {
|
|
1435
|
+
await ensureInitialized()
|
|
1436
|
+
},
|
|
1437
|
+
|
|
1438
|
+
async query<T = unknown>(
|
|
1439
|
+
sql: string,
|
|
1440
|
+
params?: unknown[]
|
|
1441
|
+
): Promise<{ rows: T[] }> {
|
|
1442
|
+
await ensureInitialized()
|
|
1443
|
+
const result = await pglite!.query<T>(sql, params)
|
|
1444
|
+
return { rows: result.rows }
|
|
1445
|
+
},
|
|
1446
|
+
|
|
1447
|
+
async close(): Promise<void> {
|
|
1448
|
+
closed = true
|
|
1449
|
+
|
|
1450
|
+
if (initPromise) {
|
|
1451
|
+
try {
|
|
1452
|
+
await initPromise
|
|
1453
|
+
} catch {
|
|
1454
|
+
// Ignore errors from pending initialization
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
if (pglite) {
|
|
1459
|
+
// Remove from cache
|
|
1460
|
+
wasmCache.delete(cacheKey)
|
|
1461
|
+
|
|
1462
|
+
try {
|
|
1463
|
+
await pglite.close()
|
|
1464
|
+
} catch {
|
|
1465
|
+
// Ignore close errors
|
|
1466
|
+
}
|
|
1467
|
+
pglite = null
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
initPromise = null
|
|
1471
|
+
},
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// ============================================================================
|
|
1476
|
+
// Factory Functions
|
|
1477
|
+
// ============================================================================
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* Create a cold start optimizer factory with pre-configured storage layers
|
|
1481
|
+
*
|
|
1482
|
+
* Use this in a Durable Object to create optimizer instances with consistent config.
|
|
1483
|
+
*
|
|
1484
|
+
* @example
|
|
1485
|
+
* ```typescript
|
|
1486
|
+
* // In DO constructor
|
|
1487
|
+
* const createOptimizer = createColdStartOptimizerFactory({
|
|
1488
|
+
* cacheLayer: myCacheLayer,
|
|
1489
|
+
* r2Layer: myR2Layer,
|
|
1490
|
+
* })
|
|
1491
|
+
*
|
|
1492
|
+
* // In DO fetch handler
|
|
1493
|
+
* const optimizer = createOptimizer({
|
|
1494
|
+
* wasmModule: pgliteWasm,
|
|
1495
|
+
* warmCacheKey: `db:${this.id}`,
|
|
1496
|
+
* coldStorageKey: `databases/${this.id}/data.tar`,
|
|
1497
|
+
* })
|
|
1498
|
+
* ```
|
|
1499
|
+
*/
|
|
1500
|
+
export function createColdStartOptimizerFactory(
|
|
1501
|
+
baseConfig: Partial<ColdStartConfig>
|
|
1502
|
+
): (instanceConfig: ColdStartConfig) => ColdStartOptimizer {
|
|
1503
|
+
return (instanceConfig) => {
|
|
1504
|
+
return createColdStartOptimizer({
|
|
1505
|
+
...baseConfig,
|
|
1506
|
+
...instanceConfig,
|
|
1507
|
+
})
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* Create a cold start optimizer with automatic tier detection
|
|
1513
|
+
*
|
|
1514
|
+
* Simplifies configuration by automatically detecting available tiers.
|
|
1515
|
+
*
|
|
1516
|
+
* @example
|
|
1517
|
+
* ```typescript
|
|
1518
|
+
* const optimizer = createAutoTieredOptimizer({
|
|
1519
|
+
* wasmModule: pgliteWasm,
|
|
1520
|
+
* env: {
|
|
1521
|
+
* R2_BUCKET: env.R2_BUCKET,
|
|
1522
|
+
* },
|
|
1523
|
+
* databaseId: 'my-database',
|
|
1524
|
+
* })
|
|
1525
|
+
* ```
|
|
1526
|
+
*/
|
|
1527
|
+
export function createAutoTieredOptimizer(options: {
|
|
1528
|
+
wasmModule: WebAssembly.Module
|
|
1529
|
+
databaseId: string
|
|
1530
|
+
env?: {
|
|
1531
|
+
R2_BUCKET?: R2Bucket
|
|
1532
|
+
}
|
|
1533
|
+
cacheLayer?: CacheLayer
|
|
1534
|
+
r2Layer?: R2StorageLayer
|
|
1535
|
+
hotData?: ArrayBuffer | Uint8Array
|
|
1536
|
+
lazy?: boolean
|
|
1537
|
+
debug?: boolean
|
|
1538
|
+
}): ColdStartOptimizer {
|
|
1539
|
+
const {
|
|
1540
|
+
wasmModule,
|
|
1541
|
+
databaseId,
|
|
1542
|
+
env,
|
|
1543
|
+
cacheLayer,
|
|
1544
|
+
r2Layer,
|
|
1545
|
+
hotData,
|
|
1546
|
+
lazy = true,
|
|
1547
|
+
debug = false,
|
|
1548
|
+
} = options
|
|
1549
|
+
|
|
1550
|
+
return createColdStartOptimizer({
|
|
1551
|
+
wasmModule,
|
|
1552
|
+
hotData,
|
|
1553
|
+
cacheLayer,
|
|
1554
|
+
warmCacheKey: cacheLayer ? `pglite:${databaseId}` : undefined,
|
|
1555
|
+
r2Layer,
|
|
1556
|
+
r2Bucket: env?.R2_BUCKET,
|
|
1557
|
+
coldStorageKey:
|
|
1558
|
+
r2Layer || env?.R2_BUCKET ? `databases/${databaseId}/data.tar` : undefined,
|
|
1559
|
+
database: databaseId,
|
|
1560
|
+
lazy,
|
|
1561
|
+
debug,
|
|
1562
|
+
})
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// ============================================================================
|
|
1566
|
+
// Exports
|
|
1567
|
+
// ============================================================================
|
|
1568
|
+
|
|
1569
|
+
export {
|
|
1570
|
+
createColdStartOptimizer as default,
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// ============================================================================
|
|
1574
|
+
// WASM Strategy Comparison
|
|
1575
|
+
// ============================================================================
|
|
1576
|
+
|
|
1577
|
+
/**
|
|
1578
|
+
* WASM loading strategy types for benchmarking
|
|
1579
|
+
*
|
|
1580
|
+
* Different strategies have different performance characteristics:
|
|
1581
|
+
*
|
|
1582
|
+
* | Strategy | Description | Best For |
|
|
1583
|
+
* |----------|-------------|----------|
|
|
1584
|
+
* | `static_import` | Pre-compiled at build time | Production (Cloudflare Workers) |
|
|
1585
|
+
* | `dynamic_import` | Compiled on demand | Development, testing |
|
|
1586
|
+
* | `streaming` | Compile during download | Large modules, Node.js |
|
|
1587
|
+
* | `cached` | Reused from isolate cache | Warm starts |
|
|
1588
|
+
*
|
|
1589
|
+
* In Cloudflare Workers, `static_import` is required because runtime
|
|
1590
|
+
* WASM compilation is blocked for security reasons.
|
|
1591
|
+
*/
|
|
1592
|
+
export type WasmLoadingStrategy = 'static_import' | 'dynamic_import' | 'streaming' | 'cached'
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* Results from comparing different WASM loading strategies
|
|
1596
|
+
*
|
|
1597
|
+
* Contains statistical analysis of multiple profiling runs for a single strategy.
|
|
1598
|
+
* Used to compare performance across different loading approaches.
|
|
1599
|
+
*/
|
|
1600
|
+
export interface WasmStrategyComparisonResult {
|
|
1601
|
+
strategy: WasmLoadingStrategy
|
|
1602
|
+
samples: number
|
|
1603
|
+
meanMs: number
|
|
1604
|
+
medianMs: number
|
|
1605
|
+
p95Ms: number
|
|
1606
|
+
p99Ms: number
|
|
1607
|
+
minMs: number
|
|
1608
|
+
maxMs: number
|
|
1609
|
+
stdDevMs: number
|
|
1610
|
+
available: boolean
|
|
1611
|
+
vsBaselinePercent?: number
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
* Profile a WASM loading strategy by running multiple iterations
|
|
1616
|
+
*
|
|
1617
|
+
* Runs the provided loader function multiple times and calculates
|
|
1618
|
+
* statistical metrics (mean, median, percentiles) for comparison.
|
|
1619
|
+
*
|
|
1620
|
+
* @param strategy - The strategy name for labeling results
|
|
1621
|
+
* @param loader - Function that loads the WASM module
|
|
1622
|
+
* @param iterations - Number of times to run the loader (default: 5)
|
|
1623
|
+
* @returns Statistical analysis of loading times
|
|
1624
|
+
*
|
|
1625
|
+
* @example
|
|
1626
|
+
* ```typescript
|
|
1627
|
+
* const result = await profileWasmStrategy(
|
|
1628
|
+
* 'static_import',
|
|
1629
|
+
* async () => {
|
|
1630
|
+
* // Load WASM module
|
|
1631
|
+
* return WebAssembly.compile(wasmBytes)
|
|
1632
|
+
* },
|
|
1633
|
+
* 10 // Run 10 iterations
|
|
1634
|
+
* )
|
|
1635
|
+
*
|
|
1636
|
+
* console.log(`Median: ${result.medianMs}ms`)
|
|
1637
|
+
* console.log(`P95: ${result.p95Ms}ms`)
|
|
1638
|
+
* ```
|
|
1639
|
+
*/
|
|
1640
|
+
export async function profileWasmStrategy(
|
|
1641
|
+
strategy: WasmLoadingStrategy,
|
|
1642
|
+
loader: () => Promise<WebAssembly.Module | void>,
|
|
1643
|
+
iterations = 5
|
|
1644
|
+
): Promise<WasmStrategyComparisonResult> {
|
|
1645
|
+
const samples: number[] = []
|
|
1646
|
+
|
|
1647
|
+
for (let i = 0; i < iterations; i++) {
|
|
1648
|
+
const start = performance.now()
|
|
1649
|
+
await loader()
|
|
1650
|
+
const end = performance.now()
|
|
1651
|
+
samples.push(end - start)
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
const sorted = [...samples].sort((a, b) => a - b)
|
|
1655
|
+
const sum = sorted.reduce((a, b) => a + b, 0)
|
|
1656
|
+
const mean = sum / sorted.length
|
|
1657
|
+
const variance = sorted.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / sorted.length
|
|
1658
|
+
|
|
1659
|
+
const percentile = (p: number): number => {
|
|
1660
|
+
const index = Math.ceil((p / 100) * sorted.length) - 1
|
|
1661
|
+
return sorted[Math.max(0, Math.min(index, sorted.length - 1))]
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
return {
|
|
1665
|
+
strategy,
|
|
1666
|
+
samples: iterations,
|
|
1667
|
+
meanMs: mean,
|
|
1668
|
+
medianMs: percentile(50),
|
|
1669
|
+
p95Ms: percentile(95),
|
|
1670
|
+
p99Ms: percentile(99),
|
|
1671
|
+
minMs: sorted[0],
|
|
1672
|
+
maxMs: sorted[sorted.length - 1],
|
|
1673
|
+
stdDevMs: Math.sqrt(variance),
|
|
1674
|
+
available: true,
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
/**
|
|
1679
|
+
* Compare multiple WASM loading strategies
|
|
1680
|
+
*
|
|
1681
|
+
* Profiles multiple loading strategies and provides a recommendation
|
|
1682
|
+
* based on median performance. Generates a human-readable analysis report.
|
|
1683
|
+
*
|
|
1684
|
+
* @param strategies - Map of strategy names to loader functions
|
|
1685
|
+
* @param iterations - Number of iterations per strategy (default: 5)
|
|
1686
|
+
* @returns Object containing:
|
|
1687
|
+
* - `results`: Statistical results for each strategy
|
|
1688
|
+
* - `recommendation`: The fastest strategy based on median time
|
|
1689
|
+
* - `analysis`: Human-readable performance report
|
|
1690
|
+
*
|
|
1691
|
+
* @example
|
|
1692
|
+
* ```typescript
|
|
1693
|
+
* const strategies = new Map([
|
|
1694
|
+
* ['static_import', async () => staticWasmModule],
|
|
1695
|
+
* ['dynamic_import', async () => {
|
|
1696
|
+
* const { PGlite } = await import('@dotdo/pglite')
|
|
1697
|
+
* return PGlite.wasmModule
|
|
1698
|
+
* }],
|
|
1699
|
+
* ])
|
|
1700
|
+
*
|
|
1701
|
+
* const { results, recommendation, analysis } = await compareWasmStrategies(strategies, 5)
|
|
1702
|
+
*
|
|
1703
|
+
* console.log(analysis)
|
|
1704
|
+
* // Output:
|
|
1705
|
+
* // WASM Loading Strategy Analysis
|
|
1706
|
+
* // ========================================
|
|
1707
|
+
* // static_import:
|
|
1708
|
+
* // Median: 0.05ms
|
|
1709
|
+
* // P95: 0.10ms
|
|
1710
|
+
* // dynamic_import:
|
|
1711
|
+
* // Median: 15.00ms
|
|
1712
|
+
* // P95: 20.00ms
|
|
1713
|
+
* // vs static_import: 29900% slower
|
|
1714
|
+
* // ========================================
|
|
1715
|
+
* // Recommendation: static_import
|
|
1716
|
+
* ```
|
|
1717
|
+
*/
|
|
1718
|
+
export async function compareWasmStrategies(
|
|
1719
|
+
strategies: Map<WasmLoadingStrategy, () => Promise<WebAssembly.Module | void>>,
|
|
1720
|
+
iterations = 5
|
|
1721
|
+
): Promise<{
|
|
1722
|
+
results: WasmStrategyComparisonResult[]
|
|
1723
|
+
recommendation: WasmLoadingStrategy
|
|
1724
|
+
analysis: string
|
|
1725
|
+
}> {
|
|
1726
|
+
const results: WasmStrategyComparisonResult[] = []
|
|
1727
|
+
let baselineMedian: number | null = null
|
|
1728
|
+
|
|
1729
|
+
for (const [strategy, loader] of strategies) {
|
|
1730
|
+
try {
|
|
1731
|
+
const result = await profileWasmStrategy(strategy, loader, iterations)
|
|
1732
|
+
if (strategy === 'static_import') {
|
|
1733
|
+
baselineMedian = result.medianMs
|
|
1734
|
+
}
|
|
1735
|
+
results.push(result)
|
|
1736
|
+
} catch {
|
|
1737
|
+
results.push({
|
|
1738
|
+
strategy,
|
|
1739
|
+
samples: 0,
|
|
1740
|
+
meanMs: 0,
|
|
1741
|
+
medianMs: 0,
|
|
1742
|
+
p95Ms: 0,
|
|
1743
|
+
p99Ms: 0,
|
|
1744
|
+
minMs: 0,
|
|
1745
|
+
maxMs: 0,
|
|
1746
|
+
stdDevMs: 0,
|
|
1747
|
+
available: false,
|
|
1748
|
+
})
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
if (baselineMedian !== null) {
|
|
1753
|
+
for (const result of results) {
|
|
1754
|
+
if (result.available && result.strategy !== 'static_import') {
|
|
1755
|
+
result.vsBaselinePercent = ((result.medianMs - baselineMedian) / baselineMedian) * 100
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
const availableResults = results.filter((r) => r.available)
|
|
1761
|
+
const sortedByMedian = [...availableResults].sort((a, b) => a.medianMs - b.medianMs)
|
|
1762
|
+
const recommendation = sortedByMedian[0]?.strategy ?? 'static_import'
|
|
1763
|
+
|
|
1764
|
+
const lines: string[] = ['WASM Loading Strategy Analysis', '='.repeat(40)]
|
|
1765
|
+
for (const result of results) {
|
|
1766
|
+
if (!result.available) {
|
|
1767
|
+
lines.push('\n' + result.strategy + ': NOT AVAILABLE')
|
|
1768
|
+
continue
|
|
1769
|
+
}
|
|
1770
|
+
lines.push('\n' + result.strategy + ':')
|
|
1771
|
+
lines.push(' Median: ' + result.medianMs.toFixed(2) + 'ms')
|
|
1772
|
+
lines.push(' P95: ' + result.p95Ms.toFixed(2) + 'ms')
|
|
1773
|
+
lines.push(' StdDev: ' + result.stdDevMs.toFixed(2) + 'ms')
|
|
1774
|
+
if (result.vsBaselinePercent !== undefined) {
|
|
1775
|
+
const comparison = result.vsBaselinePercent > 0 ? 'slower' : 'faster'
|
|
1776
|
+
lines.push(' vs static_import: ' + Math.abs(result.vsBaselinePercent).toFixed(1) + '% ' + comparison)
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
lines.push('\n' + '='.repeat(40))
|
|
1780
|
+
lines.push('Recommendation: ' + recommendation)
|
|
1781
|
+
|
|
1782
|
+
return { results, recommendation, analysis: lines.join('\n') }
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
// ============================================================================
|
|
1786
|
+
// Cold Start Profiler
|
|
1787
|
+
// ============================================================================
|
|
1788
|
+
|
|
1789
|
+
/**
|
|
1790
|
+
* Profiling result from multiple cold start runs
|
|
1791
|
+
*
|
|
1792
|
+
* Contains comprehensive statistical analysis of cold start performance,
|
|
1793
|
+
* including per-phase breakdown and data source distribution.
|
|
1794
|
+
*
|
|
1795
|
+
* ## Key Metrics
|
|
1796
|
+
*
|
|
1797
|
+
* - `totalMs`: Overall cold start time statistics
|
|
1798
|
+
* - `phases`: Per-phase timing breakdown (WASM prep, data fetch, PGLite init, prewarm)
|
|
1799
|
+
* - `dataSourceDistribution`: How often each storage tier was used
|
|
1800
|
+
* - `memoryMB`: Memory usage statistics (if available)
|
|
1801
|
+
*
|
|
1802
|
+
* ## Usage in CI
|
|
1803
|
+
*
|
|
1804
|
+
* Use this for performance regression detection:
|
|
1805
|
+
*
|
|
1806
|
+
* ```typescript
|
|
1807
|
+
* const result = await profileColdStarts(optimizer, 10)
|
|
1808
|
+
*
|
|
1809
|
+
* // Assert against performance budget
|
|
1810
|
+
* expect(result.totalMs.p95).toBeLessThan(1500)
|
|
1811
|
+
* expect(result.totalMs.median).toBeLessThan(800)
|
|
1812
|
+
* ```
|
|
1813
|
+
*/
|
|
1814
|
+
export interface ColdStartProfilingResult {
|
|
1815
|
+
samples: number
|
|
1816
|
+
totalMs: {
|
|
1817
|
+
mean: number
|
|
1818
|
+
median: number
|
|
1819
|
+
p95: number
|
|
1820
|
+
p99: number
|
|
1821
|
+
min: number
|
|
1822
|
+
max: number
|
|
1823
|
+
stdDev: number
|
|
1824
|
+
}
|
|
1825
|
+
phases: {
|
|
1826
|
+
wasmPrep: { mean: number; median: number; p95: number }
|
|
1827
|
+
dataFetch: { mean: number; median: number; p95: number }
|
|
1828
|
+
pgliteInit: { mean: number; median: number; p95: number }
|
|
1829
|
+
prewarm: { mean: number; median: number; p95: number }
|
|
1830
|
+
}
|
|
1831
|
+
dataSourceDistribution: Record<string, number>
|
|
1832
|
+
memoryMB?: { mean: number; max: number }
|
|
1833
|
+
rawMetrics: ColdStartMetrics[]
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* Format profiling results as human-readable string
|
|
1838
|
+
*
|
|
1839
|
+
* Generates a comprehensive text report suitable for logging, CI output,
|
|
1840
|
+
* or documentation.
|
|
1841
|
+
*
|
|
1842
|
+
* @param result - Profiling results to format
|
|
1843
|
+
* @returns Multi-line string with formatted statistics
|
|
1844
|
+
*
|
|
1845
|
+
* @example
|
|
1846
|
+
* ```typescript
|
|
1847
|
+
* const result = await profileColdStarts(optimizer, 10)
|
|
1848
|
+
* const report = formatProfilingResult(result)
|
|
1849
|
+
* console.log(report)
|
|
1850
|
+
*
|
|
1851
|
+
* // Output:
|
|
1852
|
+
* // Cold Start Profiling Results
|
|
1853
|
+
* // ========================================
|
|
1854
|
+
* // Samples: 10
|
|
1855
|
+
* //
|
|
1856
|
+
* // Total Cold Start Time:
|
|
1857
|
+
* // Mean: 750.00ms
|
|
1858
|
+
* // Median (p50): 720.00ms
|
|
1859
|
+
* // P95: 1200.00ms
|
|
1860
|
+
* // ...
|
|
1861
|
+
* ```
|
|
1862
|
+
*/
|
|
1863
|
+
export function formatProfilingResult(result: ColdStartProfilingResult): string {
|
|
1864
|
+
const lines: string[] = [
|
|
1865
|
+
'Cold Start Profiling Results',
|
|
1866
|
+
'='.repeat(40),
|
|
1867
|
+
'Samples: ' + result.samples,
|
|
1868
|
+
'',
|
|
1869
|
+
'Total Cold Start Time:',
|
|
1870
|
+
' Mean: ' + result.totalMs.mean.toFixed(2) + 'ms',
|
|
1871
|
+
' Median (p50): ' + result.totalMs.median.toFixed(2) + 'ms',
|
|
1872
|
+
' P95: ' + result.totalMs.p95.toFixed(2) + 'ms',
|
|
1873
|
+
' P99: ' + result.totalMs.p99.toFixed(2) + 'ms',
|
|
1874
|
+
' Min: ' + result.totalMs.min.toFixed(2) + 'ms',
|
|
1875
|
+
' Max: ' + result.totalMs.max.toFixed(2) + 'ms',
|
|
1876
|
+
' StdDev: ' + result.totalMs.stdDev.toFixed(2) + 'ms',
|
|
1877
|
+
'',
|
|
1878
|
+
'Phase Breakdown (median):',
|
|
1879
|
+
' WASM Prep: ' + result.phases.wasmPrep.median.toFixed(2) + 'ms',
|
|
1880
|
+
' Data Fetch: ' + result.phases.dataFetch.median.toFixed(2) + 'ms',
|
|
1881
|
+
' PGLite Init: ' + result.phases.pgliteInit.median.toFixed(2) + 'ms',
|
|
1882
|
+
' Prewarm: ' + result.phases.prewarm.median.toFixed(2) + 'ms',
|
|
1883
|
+
'',
|
|
1884
|
+
'Data Source Distribution:',
|
|
1885
|
+
...Object.entries(result.dataSourceDistribution).map(
|
|
1886
|
+
([source, count]) => ' ' + source + ': ' + count + ' (' + ((count / result.samples) * 100).toFixed(1) + '%)'
|
|
1887
|
+
),
|
|
1888
|
+
]
|
|
1889
|
+
|
|
1890
|
+
if (result.memoryMB) {
|
|
1891
|
+
lines.push('', 'Memory Usage:', ' Mean: ' + result.memoryMB.mean.toFixed(2) + 'MB', ' Max: ' + result.memoryMB.max.toFixed(2) + 'MB')
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
return lines.join('\n')
|
|
1895
|
+
}
|