/** * Shared Constants for postgres.do packages * * This module provides named constants for commonly used magic numbers * throughout the codebase. Using named constants improves code readability, * maintainability, and ensures consistency across packages. * * @module @dotdo/postgres-shared/constants */ // ============================================================================ // Time Constants (milliseconds) // ============================================================================ /** * Milliseconds per second * Used for converting between seconds and milliseconds */ export const MS_PER_SECOND = 1000 /** * Milliseconds per minute (60,000 ms) * Used for timeout and TTL configurations */ export const MS_PER_MINUTE = 60 * MS_PER_SECOND /** * Milliseconds per hour (3,600,000 ms) * Used for longer TTL and expiration configurations */ export const MS_PER_HOUR = 60 * MS_PER_MINUTE /** * Milliseconds per day (86,400,000 ms) * Used for retention policies and long-lived caches */ export const MS_PER_DAY = 24 * MS_PER_HOUR /** * Milliseconds per week (604,800,000 ms) * Used for retention policies */ export const MS_PER_WEEK = 7 * MS_PER_DAY // ============================================================================ // Timeout Constants (milliseconds) // ============================================================================ /** * Default connection timeout: 10 seconds (10,000 ms) * Used for establishing WebSocket and RPC connections */ export const DEFAULT_CONNECT_TIMEOUT_MS = 10000 /** * Default request/socket timeout: 30 seconds (30,000 ms) * Used for HTTP requests, database queries, and RPC calls */ export const DEFAULT_REQUEST_TIMEOUT_MS = 30000 /** * Default pool acquire timeout: 30 seconds (30,000 ms) * Used when waiting to acquire a connection from a pool */ export const DEFAULT_ACQUIRE_TIMEOUT_MS = 30000 /** * Short timeout for quick operations: 5 seconds (5,000 ms) * Used for health checks, pings, and validation operations */ export const SHORT_TIMEOUT_MS = 5000 /** * Long timeout for expensive operations: 60 seconds (60,000 ms) * Used for WASM loading, bulk operations, and complex queries */ export const LONG_TIMEOUT_MS = 60000 /** * Circuit breaker reset timeout: 30 seconds (30,000 ms) * Time to wait before moving from OPEN to HALF_OPEN state */ export const CIRCUIT_BREAKER_RESET_TIMEOUT_MS = 30000 /** * Circuit breaker failure window: 60 seconds (60,000 ms) * Time window for counting failures before tripping the circuit */ export const CIRCUIT_BREAKER_FAILURE_WINDOW_MS = 60000 /** * Maximum circuit breaker reset timeout: 5 minutes (300,000 ms) * Cap for exponential backoff in circuit breaker recovery */ export const CIRCUIT_BREAKER_MAX_RESET_TIMEOUT_MS = 300000 /** * Extension load timeout: 10 seconds (10,000 ms) * Maximum time to wait for PostgreSQL extension loading */ export const EXTENSION_LOAD_TIMEOUT_MS = 10000 // ============================================================================ // Cache and TTL Constants (milliseconds) // ============================================================================ /** * Default cache TTL: 60 seconds (60,000 ms) * Standard TTL for in-memory caches */ export const DEFAULT_CACHE_TTL_MS = 60000 /** * Default token cache TTL: 60 seconds (60,000 ms) * TTL for caching validated authentication tokens */ export const DEFAULT_TOKEN_CACHE_TTL_MS = 60000 /** * Invalid token TTL: 5 seconds (5,000 ms) * Short TTL for caching invalid token results to prevent abuse */ export const INVALID_TOKEN_TTL_MS = 5000 /** * Default cache cleanup interval: 60 seconds (60,000 ms) * Interval for removing expired entries from caches */ export const DEFAULT_CACHE_CLEANUP_INTERVAL_MS = 60000 /** * Schema cache TTL: 60 seconds (60,000 ms) * TTL for cached database schema information */ export const SCHEMA_CACHE_TTL_MS = 60000 // ============================================================================ // Size Limits and Counts // ============================================================================ /** * Default cache max size: 1,000 entries * Maximum number of entries in bounded caches */ export const DEFAULT_CACHE_MAX_SIZE = 1000 /** * Default query result limit: 1,000 rows * Default LIMIT for queries without explicit limits */ export const DEFAULT_QUERY_LIMIT = 1000 /** * Default batch size: 1,000 items * Default size for batch processing operations */ export const DEFAULT_BATCH_SIZE = 1000 /** * Maximum tracked pages: 10,000 entries * Limit for access pattern tracking to prevent memory issues */ export const MAX_TRACKED_PAGES = 10000 // ============================================================================ // Retry Constants // ============================================================================ /** * Default retry count: 3 attempts * Number of retry attempts for failed operations */ export const DEFAULT_MAX_RETRIES = 3 /** * Default retry delay: 1 second (1,000 ms) * Base delay between retry attempts */ export const DEFAULT_RETRY_DELAY_MS = 1000 /** * Maximum retry delay: 30 seconds (30,000 ms) * Cap for exponential backoff in retries */ export const MAX_RETRY_DELAY_MS = 30000 /** * Exponential backoff multiplier: 2x * Factor to multiply delay by for each retry */ export const EXPONENTIAL_BACKOFF_MULTIPLIER = 2 // ============================================================================ // Circuit Breaker Constants // ============================================================================ /** * Default failure threshold: 5 failures * Number of consecutive failures before opening the circuit */ export const CIRCUIT_BREAKER_FAILURE_THRESHOLD = 5 /** * Half-open success threshold: 3 successes * Number of successful requests in HALF_OPEN to close the circuit */ export const CIRCUIT_BREAKER_HALF_OPEN_SUCCESS_THRESHOLD = 3 // ============================================================================ // Memory and Buffer Sizes (bytes) // ============================================================================ /** * Bytes per kilobyte */ export const BYTES_PER_KB = 1024 /** * Bytes per megabyte (1,048,576 bytes) */ export const BYTES_PER_MB = 1024 * BYTES_PER_KB /** * Bytes per gigabyte (1,073,741,824 bytes) */ export const BYTES_PER_GB = 1024 * BYTES_PER_MB /** * Cloudflare Workers memory limit: 128 MB (134,217,728 bytes) * Hard limit for Worker memory consumption */ export const CLOUDFLARE_WORKERS_MEMORY_LIMIT_BYTES = 128 * BYTES_PER_MB /** * Default target file size: 128 MB (134,217,728 bytes) * Target size for Parquet and Iceberg data files */ export const DEFAULT_TARGET_FILE_SIZE_BYTES = 128 * BYTES_PER_MB /** * Default target row group size: 128 MB (134,217,728 bytes) * Target size for Parquet row groups */ export const DEFAULT_TARGET_ROW_GROUP_SIZE_BYTES = 128 * BYTES_PER_MB /** * Default target page size: 1 MB (1,048,576 bytes) * Target size for Parquet data pages */ export const DEFAULT_TARGET_PAGE_SIZE_BYTES = BYTES_PER_MB /** * Maximum document size: 16 MB (16,777,216 bytes) * Maximum size for MongoDB-compatible documents */ export const MAX_DOCUMENT_SIZE_BYTES = 16 * BYTES_PER_MB /** * PostgreSQL default page size: 8 KB (8,192 bytes) * Standard PostgreSQL data page size */ export const PG_PAGE_SIZE_BYTES = 8 * BYTES_PER_KB /** * Input buffer size: 1 MB (1,048,576 bytes) * Default size for input data buffers */ export const DEFAULT_INPUT_BUFFER_SIZE_BYTES = BYTES_PER_MB /** * Rebalance threshold: 512 MB (536,870,912 bytes) * Size threshold triggering shard rebalancing */ export const SHARD_REBALANCE_THRESHOLD_BYTES = 512 * BYTES_PER_MB // ============================================================================ // Network Constants // ============================================================================ /** * PostgreSQL default port: 5432 */ export const POSTGRES_DEFAULT_PORT = 5432 /** * WebSocket normal close code: 1000 * Used when closing WebSocket connections cleanly */ export const WEBSOCKET_NORMAL_CLOSE_CODE = 1000 /** * Default health check interval: 30 seconds (30,000 ms) * Interval between health monitoring checks */ export const DEFAULT_HEALTH_CHECK_INTERVAL_MS = 30000 /** * Idle connection timeout: 30 seconds (30,000 ms) * Time before closing an idle pooled connection */ export const DEFAULT_IDLE_TIMEOUT_MS = 30000 // ============================================================================ // Queue Constants // ============================================================================ /** * Default queue max retries: 3 attempts * Number of message delivery attempts before moving to DLQ */ export const DEFAULT_QUEUE_MAX_RETRIES = 3 /** * Default visibility timeout: 30 seconds * Time before an unacknowledged message becomes visible again */ export const DEFAULT_VISIBILITY_TIMEOUT_SECONDS = 30 // ============================================================================ // Search Constants // ============================================================================ /** * Default search result limit: 1,000 results * Maximum results returned from search operations */ export const DEFAULT_SEARCH_LIMIT = 1000 /** * Search cleanup delay: 5 minutes (300,000 ms) * Delay before cleaning up completed search resources */ export const SEARCH_CLEANUP_DELAY_MS = 5 * MS_PER_MINUTE /** * WebSocket reconnect delay: 1 second (1,000 ms) * Initial delay before attempting WebSocket reconnection */ export const DEFAULT_RECONNECT_DELAY_MS = 1000 // ============================================================================ // Sync and Replication Constants // ============================================================================ /** * Throughput calculation window: 5 seconds (5,000 ms) * Rolling window for calculating sync throughput metrics */ export const THROUGHPUT_WINDOW_MS = 5000 /** * Expected changes for sync progress: 10,000 changes * Default estimate for calculating sync progress percentage */ export const DEFAULT_EXPECTED_CHANGES = 10000 // ============================================================================ // Storage Policy Constants // ============================================================================ /** * Hot tier default TTL: 5 minutes (300,000 ms) * Default time data stays in fastest storage tier */ export const HOT_TIER_DEFAULT_TTL_MS = 5 * MS_PER_MINUTE /** * Warm tier default TTL: 1 hour (3,600,000 ms) * Default time data stays in warm storage tier */ export const WARM_TIER_DEFAULT_TTL_MS = MS_PER_HOUR /** * Warm to hot promotion time window: 60 seconds (60,000 ms) * Time window for detecting frequent access patterns */ export const WARM_TO_HOT_TIME_WINDOW_MS = MS_PER_MINUTE // ============================================================================ // Rate Limiting Constants // ============================================================================ /** * Default rate limit window: 60 seconds (60,000 ms) * Standard time window for rate limiting calculations */ export const DEFAULT_RATE_LIMIT_WINDOW_MS = 60000 // ============================================================================ // Hash Ring Constants // ============================================================================ /** * Default virtual nodes per shard: 150 * Number of virtual nodes for consistent hashing */ export const DEFAULT_VIRTUAL_NODES = 150 /** * Maximum shards: 1,024 * Upper limit for shard count in distributed systems */ export const MAX_SHARDS = 1024 // ============================================================================ // Iceberg/Parquet Constants // ============================================================================ /** * Iceberg partition field ID base: 1000 * Starting ID for partition fields in Iceberg schemas */ export const ICEBERG_PARTITION_FIELD_ID_BASE = 1000 // ============================================================================ // Electric Package Constants // ============================================================================ /** * Heartbeat interval for Electric stream consumers. * Sends periodic heartbeats to maintain connection liveness. * * @see packages/electric/src/streams/consumer.ts */ export const ELECTRIC_HEARTBEAT_INTERVAL_MS = 5000 /** * Polling interval for Electric streams and shape managers. * Used for checking stream state and ready conditions. * * @see packages/electric/src/streams/stream.ts * @see packages/electric/src/shapes/manager.ts */ export const ELECTRIC_POLLING_INTERVAL_MS = 100 /** * Default batch size for Electric sync engine operations. * * @see packages/electric/src/sync/engine.ts */ export const ELECTRIC_DEFAULT_BATCH_SIZE = 100 /** * Maximum size for the Electric offline queue. * Prevents unbounded memory growth during offline operations. * * @see packages/electric/src/sync/offline-queue.ts */ export const ELECTRIC_OFFLINE_QUEUE_MAX_SIZE = 10000 // ============================================================================ // postgres.do Package Constants // ============================================================================ /** * GDPR data retention deadline in milliseconds (72 hours). * European GDPR requires deletion requests be honored within 72 hours. * * @see packages/postgres.do/src/retention.ts */ export const GDPR_RETENTION_DEADLINE_MS = 72 * MS_PER_HOUR /** * Maximum event listeners for CDC event emitters. * Prevents memory leaks from excessive listeners. * * @see packages/postgres.do/src/cdc/event-emitter.ts */ export const CDC_MAX_LISTENERS = 100 /** * Maximum batch size for CDC batch processor. * * @see packages/postgres.do/src/cdc/batch-processor.ts */ export const CDC_MAX_BATCH_SIZE = 100 /** * Default histogram buckets for latency metrics (in milliseconds). * Standard percentile-friendly distribution for observability. * * @see packages/postgres.do/src/middleware.ts */ export const DEFAULT_HISTOGRAM_BUCKETS = [1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000] // ============================================================================ // DocumentDB/MongoDB Package Constants // ============================================================================ /** * MongoDB Wire Protocol operation codes. * These are standardized codes from the MongoDB Wire Protocol specification. * * @see https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/ * @see packages/documentdb/src/types/index.ts */ export const MONGODB_OP_CODES = { OP_UPDATE: 2001, OP_INSERT: 2002, OP_QUERY: 2004, OP_GET_MORE: 2005, OP_DELETE: 2006, OP_KILL_CURSORS: 2007, OP_COMPRESSED: 2012, OP_MSG: 2013, } as const /** * Maximum batch size for MongoDB bulk operations. * * @see packages/documentdb/src/client.ts */ export const MONGODB_MAX_BATCH_SIZE = 100000 /** * Default query limit for MongoDB find operations. * * @see packages/documentdb/src/worker/index.ts */ export const MONGODB_DEFAULT_QUERY_LIMIT = 100 // ============================================================================ // PGLake Package Constants // ============================================================================ /** * Target shard size for PGLake data distribution (512MB). * Optimal size for parallel query processing and storage efficiency. * * @see packages/pg-lake/src/catalog/router.ts */ export const PGLAKE_TARGET_SHARD_SIZE_BYTES = 512 * 1024 * 1024 /** * Maximum message size for PGLake protocol (10MB). * Prevents excessive memory allocation for single messages. * * @see packages/pg-lake/src/protocol/index.ts */ export const PGLAKE_MAX_MESSAGE_SIZE_BYTES = 10 * 1024 * 1024 /** * Default Parquet block size for data encoding. * * @see packages/pg-lake/src/ingest/parquet-writer.ts */ export const PARQUET_DEFAULT_BLOCK_SIZE = 128 // ============================================================================ // Postgres Package Constants // ============================================================================ /** * WebAssembly page size (64KB). * Standard WASM memory page size as per WebAssembly specification. * * @see https://webassembly.github.io/spec/core/exec/runtime.html#page-size * @see packages/postgres/src/observability/memory-metrics.ts */ export const WASM_PAGE_SIZE_BYTES = 64 * 1024 /** * Iceberg time-travel retention window (30 days). * Default retention period for historical snapshots. * * @see packages/postgres/src/iceberg/time-travel-api.ts */ export const ICEBERG_RETENTION_WINDOW_MS = 30 * MS_PER_DAY /** * Analytics sampling interval (24 hours). * Interval for collecting analytics data points. * * @see packages/postgres/src/iceberg/analytics.ts */ export const ANALYTICS_SAMPLING_INTERVAL_MS = MS_PER_DAY /** * Maximum chunk size for DOVFS operations (16MB). * Balances memory usage with I/O efficiency. * * @see packages/postgres/src/pglite/dovfs.ts */ export const DOVFS_MAX_CHUNK_SIZE_BYTES = 16 * 1024 * 1024 /** * PostgreSQL catalog overhead (10MB). * Reserved memory for PostgreSQL system catalogs and metadata. * * @see packages/postgres/src/observability/memory-metrics.ts */ export const PG_CATALOG_OVERHEAD_BYTES = 10 * 1024 * 1024 // ============================================================================ // Sync Primitives / Hash Constants // ============================================================================ /** * FNV-1a hash offset basis (32-bit). * Initial value for FNV-1a hash computation. * * @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function * @see packages/shared/src/sync-primitives.ts */ export const FNV_OFFSET_BASIS = 2166136261 /** * FNV-1a hash prime (32-bit). * Multiplier for FNV-1a hash computation. * * @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function * @see packages/shared/src/sync-primitives.ts */ export const FNV_PRIME = 16777619 /** * MurmurHash3 hash multiplier. * Used in hybrid Bloom filter implementations. * * @see packages/shared/src/sync-primitives.ts */ export const MURMUR_HASH_MULTIPLIER = 2654435761 // ============================================================================ // Access Pattern Tracker Constants // ============================================================================ /** * Default decay half-life for access frequency tracking (5 minutes). * Controls how quickly old access patterns fade in importance. * * @see packages/shared/src/access-pattern-tracker.ts */ export const ACCESS_PATTERN_DECAY_HALF_LIFE_MS = 5 * MS_PER_MINUTE /** * Default sliding window for access pattern analysis (1 minute). * Window size for computing recent access statistics. * * @see packages/shared/src/access-pattern-tracker.ts */ export const ACCESS_PATTERN_SLIDING_WINDOW_MS = MS_PER_MINUTE /** * Correlation window for detecting related page accesses (100ms). * Accesses within this window are considered potentially correlated. * * @see packages/shared/src/access-pattern-tracker.ts */ export const ACCESS_PATTERN_CORRELATION_WINDOW_MS = 100 /** * Interval between access tracker pruning operations (5 minutes). * Removes stale entries to prevent unbounded memory growth. * * @see packages/shared/src/access-pattern-tracker.ts */ export const ACCESS_PATTERN_PRUNE_INTERVAL_MS = 5 * MS_PER_MINUTE // ============================================================================ // Constants Metadata // ============================================================================ /** * Metadata for all constants including documentation, units, and rationale. * This provides programmatic access to constant documentation for tooling, * introspection, and runtime validation. */ export const CONSTANTS_METADATA = { // Time Constants MS_PER_SECOND: { description: 'Milliseconds per second - fundamental time unit conversion', unit: 'milliseconds', defaultValue: MS_PER_SECOND, category: 'time', }, MS_PER_MINUTE: { description: 'Milliseconds per minute - derived from MS_PER_SECOND', unit: 'milliseconds', defaultValue: MS_PER_MINUTE, category: 'time', }, MS_PER_HOUR: { description: 'Milliseconds per hour - derived from MS_PER_MINUTE', unit: 'milliseconds', defaultValue: MS_PER_HOUR, category: 'time', }, MS_PER_DAY: { description: 'Milliseconds per day - derived from MS_PER_HOUR', unit: 'milliseconds', defaultValue: MS_PER_DAY, category: 'time', }, MS_PER_WEEK: { description: 'Milliseconds per week - derived from MS_PER_DAY', unit: 'milliseconds', defaultValue: MS_PER_WEEK, category: 'time', }, // Timeout Constants DEFAULT_CONNECT_TIMEOUT_MS: { description: 'Default connection timeout for establishing WebSocket and RPC connections', unit: 'milliseconds', defaultValue: DEFAULT_CONNECT_TIMEOUT_MS, category: 'timeout', }, DEFAULT_REQUEST_TIMEOUT_MS: { description: 'Default request/socket timeout for HTTP requests, database queries, and RPC calls', unit: 'milliseconds', defaultValue: DEFAULT_REQUEST_TIMEOUT_MS, category: 'timeout', }, DEFAULT_ACQUIRE_TIMEOUT_MS: { description: 'Default pool acquire timeout when waiting to acquire a connection from a pool', unit: 'milliseconds', defaultValue: DEFAULT_ACQUIRE_TIMEOUT_MS, category: 'timeout', }, SHORT_TIMEOUT_MS: { description: 'Short timeout for quick operations like health checks, pings, and validation', unit: 'milliseconds', defaultValue: SHORT_TIMEOUT_MS, category: 'timeout', }, LONG_TIMEOUT_MS: { description: 'Long timeout for expensive operations like WASM loading, bulk operations, and complex queries', unit: 'milliseconds', defaultValue: LONG_TIMEOUT_MS, category: 'timeout', }, // Circuit Breaker Constants CIRCUIT_BREAKER_RESET_TIMEOUT_MS: { description: 'Time to wait before moving circuit breaker from OPEN to HALF_OPEN state', unit: 'milliseconds', defaultValue: CIRCUIT_BREAKER_RESET_TIMEOUT_MS, category: 'circuit-breaker', }, CIRCUIT_BREAKER_FAILURE_WINDOW_MS: { description: 'Time window for counting failures before tripping the circuit', unit: 'milliseconds', defaultValue: CIRCUIT_BREAKER_FAILURE_WINDOW_MS, category: 'circuit-breaker', }, CIRCUIT_BREAKER_MAX_RESET_TIMEOUT_MS: { description: 'Cap for exponential backoff in circuit breaker recovery', unit: 'milliseconds', defaultValue: CIRCUIT_BREAKER_MAX_RESET_TIMEOUT_MS, category: 'circuit-breaker', }, CIRCUIT_BREAKER_FAILURE_THRESHOLD: { description: 'Number of consecutive failures before opening the circuit', unit: 'count', defaultValue: CIRCUIT_BREAKER_FAILURE_THRESHOLD, category: 'circuit-breaker', }, CIRCUIT_BREAKER_HALF_OPEN_SUCCESS_THRESHOLD: { description: 'Number of successful requests in HALF_OPEN to close the circuit', unit: 'count', defaultValue: CIRCUIT_BREAKER_HALF_OPEN_SUCCESS_THRESHOLD, category: 'circuit-breaker', }, // Cache Constants DEFAULT_CACHE_TTL_MS: { description: 'Standard TTL for in-memory caches', unit: 'milliseconds', defaultValue: DEFAULT_CACHE_TTL_MS, category: 'cache', }, DEFAULT_TOKEN_CACHE_TTL_MS: { description: 'TTL for caching validated authentication tokens', unit: 'milliseconds', defaultValue: DEFAULT_TOKEN_CACHE_TTL_MS, category: 'cache', }, INVALID_TOKEN_TTL_MS: { description: 'Short TTL for caching invalid token results to prevent abuse', unit: 'milliseconds', defaultValue: INVALID_TOKEN_TTL_MS, category: 'cache', }, DEFAULT_CACHE_CLEANUP_INTERVAL_MS: { description: 'Interval for removing expired entries from caches', unit: 'milliseconds', defaultValue: DEFAULT_CACHE_CLEANUP_INTERVAL_MS, category: 'cache', }, SCHEMA_CACHE_TTL_MS: { description: 'TTL for cached database schema information', unit: 'milliseconds', defaultValue: SCHEMA_CACHE_TTL_MS, category: 'cache', }, DEFAULT_CACHE_MAX_SIZE: { description: 'Maximum number of entries in bounded caches', unit: 'count', defaultValue: DEFAULT_CACHE_MAX_SIZE, category: 'cache', }, // Size Limits DEFAULT_QUERY_LIMIT: { description: 'Default LIMIT for queries without explicit limits', unit: 'rows', defaultValue: DEFAULT_QUERY_LIMIT, category: 'limits', }, DEFAULT_BATCH_SIZE: { description: 'Default size for batch processing operations', unit: 'count', defaultValue: DEFAULT_BATCH_SIZE, category: 'limits', }, MAX_TRACKED_PAGES: { description: 'Limit for access pattern tracking to prevent memory issues', unit: 'count', defaultValue: MAX_TRACKED_PAGES, category: 'limits', }, // Retry Constants DEFAULT_MAX_RETRIES: { description: 'Number of retry attempts for failed operations', unit: 'count', defaultValue: DEFAULT_MAX_RETRIES, category: 'retry', }, DEFAULT_RETRY_DELAY_MS: { description: 'Base delay between retry attempts', unit: 'milliseconds', defaultValue: DEFAULT_RETRY_DELAY_MS, category: 'retry', }, MAX_RETRY_DELAY_MS: { description: 'Cap for exponential backoff in retries', unit: 'milliseconds', defaultValue: MAX_RETRY_DELAY_MS, category: 'retry', }, EXPONENTIAL_BACKOFF_MULTIPLIER: { description: 'Factor to multiply delay by for each retry', unit: 'multiplier', defaultValue: EXPONENTIAL_BACKOFF_MULTIPLIER, category: 'retry', }, // Memory Constants BYTES_PER_KB: { description: 'Bytes per kilobyte - fundamental memory unit conversion', unit: 'bytes', defaultValue: BYTES_PER_KB, category: 'memory', }, BYTES_PER_MB: { description: 'Bytes per megabyte - derived from BYTES_PER_KB', unit: 'bytes', defaultValue: BYTES_PER_MB, category: 'memory', }, BYTES_PER_GB: { description: 'Bytes per gigabyte - derived from BYTES_PER_MB', unit: 'bytes', defaultValue: BYTES_PER_GB, category: 'memory', }, DEFAULT_TARGET_FILE_SIZE_BYTES: { description: 'Target size for Parquet and Iceberg data files', unit: 'bytes', defaultValue: DEFAULT_TARGET_FILE_SIZE_BYTES, category: 'storage', }, DEFAULT_TARGET_ROW_GROUP_SIZE_BYTES: { description: 'Target size for Parquet row groups', unit: 'bytes', defaultValue: DEFAULT_TARGET_ROW_GROUP_SIZE_BYTES, category: 'storage', }, DEFAULT_TARGET_PAGE_SIZE_BYTES: { description: 'Target size for Parquet data pages', unit: 'bytes', defaultValue: DEFAULT_TARGET_PAGE_SIZE_BYTES, category: 'storage', }, MAX_DOCUMENT_SIZE_BYTES: { description: 'Maximum size for MongoDB-compatible documents', unit: 'bytes', defaultValue: MAX_DOCUMENT_SIZE_BYTES, category: 'storage', }, PG_PAGE_SIZE_BYTES: { description: 'Standard PostgreSQL data page size', unit: 'bytes', defaultValue: PG_PAGE_SIZE_BYTES, category: 'postgres', }, DEFAULT_INPUT_BUFFER_SIZE_BYTES: { description: 'Default size for input data buffers', unit: 'bytes', defaultValue: DEFAULT_INPUT_BUFFER_SIZE_BYTES, category: 'storage', }, SHARD_REBALANCE_THRESHOLD_BYTES: { description: 'Size threshold triggering shard rebalancing', unit: 'bytes', defaultValue: SHARD_REBALANCE_THRESHOLD_BYTES, category: 'storage', }, // Network Constants POSTGRES_DEFAULT_PORT: { description: 'Default PostgreSQL port number', unit: 'port', defaultValue: POSTGRES_DEFAULT_PORT, category: 'network', }, WEBSOCKET_NORMAL_CLOSE_CODE: { description: 'WebSocket normal close code for clean disconnections', unit: 'code', defaultValue: WEBSOCKET_NORMAL_CLOSE_CODE, category: 'network', }, DEFAULT_HEALTH_CHECK_INTERVAL_MS: { description: 'Interval between health monitoring checks', unit: 'milliseconds', defaultValue: DEFAULT_HEALTH_CHECK_INTERVAL_MS, category: 'network', }, DEFAULT_IDLE_TIMEOUT_MS: { description: 'Time before closing an idle pooled connection', unit: 'milliseconds', defaultValue: DEFAULT_IDLE_TIMEOUT_MS, category: 'network', }, // Queue Constants DEFAULT_QUEUE_MAX_RETRIES: { description: 'Number of message delivery attempts before moving to DLQ', unit: 'count', defaultValue: DEFAULT_QUEUE_MAX_RETRIES, category: 'queue', }, DEFAULT_VISIBILITY_TIMEOUT_SECONDS: { description: 'Time before an unacknowledged message becomes visible again', unit: 'seconds', defaultValue: DEFAULT_VISIBILITY_TIMEOUT_SECONDS, category: 'queue', }, // Search Constants DEFAULT_SEARCH_LIMIT: { description: 'Maximum results returned from search operations', unit: 'count', defaultValue: DEFAULT_SEARCH_LIMIT, category: 'search', }, SEARCH_CLEANUP_DELAY_MS: { description: 'Delay before cleaning up completed search resources', unit: 'milliseconds', defaultValue: SEARCH_CLEANUP_DELAY_MS, category: 'search', }, DEFAULT_RECONNECT_DELAY_MS: { description: 'Initial delay before attempting WebSocket reconnection', unit: 'milliseconds', defaultValue: DEFAULT_RECONNECT_DELAY_MS, category: 'network', }, // Sync Constants THROUGHPUT_WINDOW_MS: { description: 'Rolling window for calculating sync throughput metrics', unit: 'milliseconds', defaultValue: THROUGHPUT_WINDOW_MS, category: 'sync', }, DEFAULT_EXPECTED_CHANGES: { description: 'Default estimate for calculating sync progress percentage', unit: 'count', defaultValue: DEFAULT_EXPECTED_CHANGES, category: 'sync', }, // Storage Policy Constants HOT_TIER_DEFAULT_TTL_MS: { description: 'Default time data stays in fastest storage tier', unit: 'milliseconds', defaultValue: HOT_TIER_DEFAULT_TTL_MS, category: 'storage-policy', }, WARM_TIER_DEFAULT_TTL_MS: { description: 'Default time data stays in warm storage tier', unit: 'milliseconds', defaultValue: WARM_TIER_DEFAULT_TTL_MS, category: 'storage-policy', }, WARM_TO_HOT_TIME_WINDOW_MS: { description: 'Time window for detecting frequent access patterns', unit: 'milliseconds', defaultValue: WARM_TO_HOT_TIME_WINDOW_MS, category: 'storage-policy', }, // Rate Limiting Constants DEFAULT_RATE_LIMIT_WINDOW_MS: { description: 'Standard time window for rate limiting calculations', unit: 'milliseconds', defaultValue: DEFAULT_RATE_LIMIT_WINDOW_MS, category: 'rate-limiting', }, // Hash Ring Constants DEFAULT_VIRTUAL_NODES: { description: 'Number of virtual nodes for consistent hashing', unit: 'count', defaultValue: DEFAULT_VIRTUAL_NODES, category: 'hash-ring', }, MAX_SHARDS: { description: 'Upper limit for shard count in distributed systems', unit: 'count', defaultValue: MAX_SHARDS, category: 'hash-ring', rationale: '1024 = 2^10, optimal for bitwise operations in consistent hashing', }, // Iceberg/Parquet Constants ICEBERG_PARTITION_FIELD_ID_BASE: { description: 'Starting ID for partition fields in Iceberg schemas', unit: 'id', defaultValue: ICEBERG_PARTITION_FIELD_ID_BASE, category: 'iceberg', }, EXTENSION_LOAD_TIMEOUT_MS: { description: 'Maximum time to wait for PostgreSQL extension loading', unit: 'milliseconds', defaultValue: EXTENSION_LOAD_TIMEOUT_MS, category: 'timeout', }, // Electric Package Constants ELECTRIC_HEARTBEAT_INTERVAL_MS: { description: 'Heartbeat interval for Electric stream consumers to maintain connection liveness', unit: 'milliseconds', defaultValue: ELECTRIC_HEARTBEAT_INTERVAL_MS, category: 'electric', }, ELECTRIC_POLLING_INTERVAL_MS: { description: 'Polling interval for Electric streams and shape managers', unit: 'milliseconds', defaultValue: ELECTRIC_POLLING_INTERVAL_MS, category: 'electric', }, ELECTRIC_DEFAULT_BATCH_SIZE: { description: 'Default batch size for Electric sync engine operations', unit: 'count', defaultValue: ELECTRIC_DEFAULT_BATCH_SIZE, category: 'electric', }, ELECTRIC_OFFLINE_QUEUE_MAX_SIZE: { description: 'Maximum size for the Electric offline queue', unit: 'count', defaultValue: ELECTRIC_OFFLINE_QUEUE_MAX_SIZE, category: 'electric', }, GDPR_RETENTION_DEADLINE_MS: { description: 'GDPR data retention deadline - European regulation requires deletion within 72 hours', unit: 'milliseconds', defaultValue: GDPR_RETENTION_DEADLINE_MS, category: 'compliance', rationale: '72 hours is the maximum time allowed by GDPR for honoring deletion requests', }, CDC_MAX_LISTENERS: { description: 'Maximum event listeners for CDC event emitters', unit: 'count', defaultValue: CDC_MAX_LISTENERS, category: 'cdc', }, CDC_MAX_BATCH_SIZE: { description: 'Maximum batch size for CDC batch processor', unit: 'count', defaultValue: CDC_MAX_BATCH_SIZE, category: 'cdc', }, DEFAULT_HISTOGRAM_BUCKETS: { description: 'Default histogram buckets for latency metrics', unit: 'milliseconds', defaultValue: DEFAULT_HISTOGRAM_BUCKETS, category: 'observability', }, MONGODB_OP_CODES: { description: 'MongoDB Wire Protocol operation codes for client-server communication', unit: 'opcode', defaultValue: MONGODB_OP_CODES, category: 'protocol', reference: 'MongoDB Wire Protocol specification: https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/', }, MONGODB_MAX_BATCH_SIZE: { description: 'Maximum batch size for MongoDB bulk operations', unit: 'count', defaultValue: MONGODB_MAX_BATCH_SIZE, category: 'mongodb', }, MONGODB_DEFAULT_QUERY_LIMIT: { description: 'Default query limit for MongoDB find operations', unit: 'count', defaultValue: MONGODB_DEFAULT_QUERY_LIMIT, category: 'mongodb', }, PGLAKE_TARGET_SHARD_SIZE_BYTES: { description: 'Target shard size for PGLake data distribution', unit: 'bytes', defaultValue: PGLAKE_TARGET_SHARD_SIZE_BYTES, category: 'storage', }, PGLAKE_MAX_MESSAGE_SIZE_BYTES: { description: 'Maximum message size for PGLake protocol', unit: 'bytes', defaultValue: PGLAKE_MAX_MESSAGE_SIZE_BYTES, category: 'protocol', }, PARQUET_DEFAULT_BLOCK_SIZE: { description: 'Default Parquet block size for data encoding', unit: 'count', defaultValue: PARQUET_DEFAULT_BLOCK_SIZE, category: 'storage', }, WASM_PAGE_SIZE_BYTES: { description: 'WebAssembly page size as per WASM specification', unit: 'bytes', defaultValue: WASM_PAGE_SIZE_BYTES, category: 'wasm', }, ICEBERG_RETENTION_WINDOW_MS: { description: 'Iceberg time-travel retention window for historical snapshots', unit: 'milliseconds', defaultValue: ICEBERG_RETENTION_WINDOW_MS, category: 'storage', }, ANALYTICS_SAMPLING_INTERVAL_MS: { description: 'Analytics sampling interval for collecting data points', unit: 'milliseconds', defaultValue: ANALYTICS_SAMPLING_INTERVAL_MS, category: 'observability', }, DOVFS_MAX_CHUNK_SIZE_BYTES: { description: 'Maximum chunk size for DOVFS operations', unit: 'bytes', defaultValue: DOVFS_MAX_CHUNK_SIZE_BYTES, category: 'storage', }, PG_CATALOG_OVERHEAD_BYTES: { description: 'PostgreSQL catalog overhead for system catalogs and metadata', unit: 'bytes', defaultValue: PG_CATALOG_OVERHEAD_BYTES, category: 'postgres', }, CLOUDFLARE_WORKERS_MEMORY_LIMIT_BYTES: { description: 'Cloudflare Workers memory limit', unit: 'bytes', defaultValue: CLOUDFLARE_WORKERS_MEMORY_LIMIT_BYTES, category: 'cloudflare', }, FNV_OFFSET_BASIS: { description: 'FNV-1a hash offset basis (32-bit) - initial value for hash computation', unit: 'numeric', defaultValue: FNV_OFFSET_BASIS, category: 'hash', algorithm: 'FNV-1a', }, FNV_PRIME: { description: 'FNV-1a hash prime (32-bit) - multiplier for hash computation', unit: 'numeric', defaultValue: FNV_PRIME, category: 'hash', algorithm: 'FNV-1a', }, MURMUR_HASH_MULTIPLIER: { description: 'MurmurHash3 multiplier for hybrid Bloom filter implementations', unit: 'numeric', defaultValue: MURMUR_HASH_MULTIPLIER, category: 'hash', algorithm: 'MurmurHash3', }, ACCESS_PATTERN_DECAY_HALF_LIFE_MS: { description: 'Decay half-life for access frequency tracking', unit: 'milliseconds', defaultValue: ACCESS_PATTERN_DECAY_HALF_LIFE_MS, category: 'caching', }, ACCESS_PATTERN_SLIDING_WINDOW_MS: { description: 'Sliding window for access pattern analysis', unit: 'milliseconds', defaultValue: ACCESS_PATTERN_SLIDING_WINDOW_MS, category: 'caching', }, ACCESS_PATTERN_CORRELATION_WINDOW_MS: { description: 'Correlation window for detecting related page accesses', unit: 'milliseconds', defaultValue: ACCESS_PATTERN_CORRELATION_WINDOW_MS, category: 'caching', }, ACCESS_PATTERN_PRUNE_INTERVAL_MS: { description: 'Interval between access tracker pruning operations', unit: 'milliseconds', defaultValue: ACCESS_PATTERN_PRUNE_INTERVAL_MS, category: 'caching', }, } as const // ============================================================================ // Validation Functions // ============================================================================ /** * Validates timeout configuration values. * * @param config - Configuration object with timeout value * @param options - Validation options with min/max constraints * @throws Error if timeout is outside valid range */ export function validateTimeoutConfig( config: { timeout: number }, options: { min: number; max: number } ): void { if (config.timeout < options.min) { throw new Error(`Timeout ${config.timeout}ms is below minimum ${options.min}ms`) } if (config.timeout > options.max) { throw new Error(`Timeout ${config.timeout}ms exceeds maximum ${options.max}ms`) } } /** * Validates size configuration values against Cloudflare Workers constraints. * * @param config - Configuration object with size value * @throws Error if size exceeds worker memory limit */ export function validateSizeConfig(config: { targetSize: number }): void { if (config.targetSize > CLOUDFLARE_WORKERS_MEMORY_LIMIT_BYTES) { throw new Error( `Target size ${config.targetSize} bytes exceeds Cloudflare worker memory limit of ${CLOUDFLARE_WORKERS_MEMORY_LIMIT_BYTES} bytes` ) } } /** * Validates batch configuration values. * * @param config - Configuration object with batch size * @throws Error if batch size is not a positive integer */ export function validateBatchConfig(config: { batchSize: number }): void { if (config.batchSize <= 0) { throw new Error(`Batch size must be positive, got ${config.batchSize}`) } if (!Number.isInteger(config.batchSize)) { throw new Error(`Batch size must be an integer, got ${config.batchSize}`) } } /** * Validates retry configuration consistency. * * @param config - Retry configuration object * @throws Error if configuration is inconsistent */ export function validateRetryConfig(config: { maxRetries: number initialDelay: number maxDelay: number }): void { if (config.maxDelay < config.initialDelay) { throw new Error( `maxDelay (${config.maxDelay}ms) must be >= initialDelay (${config.initialDelay}ms)` ) } if (config.maxRetries < 0) { throw new Error(`maxRetries must be non-negative, got ${config.maxRetries}`) } if (config.initialDelay < 0) { throw new Error(`initialDelay must be non-negative, got ${config.initialDelay}`) } } // ============================================================================ // Environment-Specific Configuration // ============================================================================ /** * Predefined environment configurations. */ export const ENVIRONMENT_CONFIGS = { development: { requestTimeout: 60000, maxBatchSize: 1000, maxMemoryBytes: 256 * 1024 * 1024, // More permissive for local dev doIdleTimeout: 300000, doWarmupTime: 5000, retryDelay: 1000, }, staging: { requestTimeout: 45000, maxBatchSize: 500, maxMemoryBytes: 128 * 1024 * 1024, doIdleTimeout: 120000, doWarmupTime: 3000, retryDelay: 500, }, production: { requestTimeout: 30000, maxBatchSize: 100, maxMemoryBytes: 128 * 1024 * 1024, // Cloudflare Workers limit doIdleTimeout: 60000, doWarmupTime: 1000, retryDelay: 100, }, test: { requestTimeout: 5000, maxBatchSize: 10, maxMemoryBytes: 64 * 1024 * 1024, doIdleTimeout: 10000, doWarmupTime: 100, retryDelay: 10, }, } as const export type EnvironmentName = keyof typeof ENVIRONMENT_CONFIGS /** * Gets configuration for a specific environment. * * @param env - Environment name * @returns Configuration object for the environment */ export function getConfigForEnvironment(env: string): { requestTimeout: number maxBatchSize: number maxMemoryBytes: number doIdleTimeout: number doWarmupTime: number retryDelay: number } { const envKey = env as EnvironmentName if (envKey in ENVIRONMENT_CONFIGS) { return { ...ENVIRONMENT_CONFIGS[envKey] } } // Default to production for unknown environments return { ...ENVIRONMENT_CONFIGS.production } } /** * Gets configuration with custom overrides applied. * * @param env - Environment name * @param overrides - Custom overrides to apply * @returns Merged configuration object */ export function getConfigWithOverrides( env: string, overrides?: Record ): { requestTimeout: number maxBatchSize: number maxMemoryBytes: number doIdleTimeout: number doWarmupTime: number retryDelay: number } { const baseConfig = getConfigForEnvironment(env) if (!overrides) { return baseConfig } // Map environment variable names to config keys const keyMapping: Record = { REQUEST_TIMEOUT_MS: 'requestTimeout', MAX_BATCH_SIZE: 'maxBatchSize', MAX_MEMORY_BYTES: 'maxMemoryBytes', DO_IDLE_TIMEOUT: 'doIdleTimeout', DO_WARMUP_TIME: 'doWarmupTime', RETRY_DELAY: 'retryDelay', } for (const [envVar, configKey] of Object.entries(keyMapping)) { if (envVar in overrides && typeof overrides[envVar] === 'number') { ;(baseConfig as Record)[configKey] = overrides[envVar] as number } } return baseConfig } // ============================================================================ // Configuration Helpers // ============================================================================ /** * Creates a timeout configuration for specific operations. * * @param options - Options for creating the timeout config * @returns Timeout configuration object */ export function createTimeoutConfig(options?: { operation?: string environment?: string }): { connect: number request: number idle: number } { const env = options?.environment || 'production' const envConfig = getConfigForEnvironment(env) return { connect: Math.min(envConfig.requestTimeout, 10000), request: envConfig.requestTimeout, idle: envConfig.doIdleTimeout, } } /** * Creates a retry configuration for specific operations. * * @param options - Options for creating the retry config * @returns Retry configuration object */ export function createRetryConfig(options?: { operation?: string environment?: string }): { maxRetries: number initialDelay: number maxDelay: number backoffMultiplier: number } { const env = options?.environment || 'production' const envConfig = getConfigForEnvironment(env) return { maxRetries: 3, initialDelay: envConfig.retryDelay, maxDelay: envConfig.retryDelay * 50, backoffMultiplier: 2, } } /** * Creates a storage configuration for specific tiers. * * @param options - Options for creating the storage config * @returns Storage configuration object */ export function createStorageConfig(options?: { tier?: 'hot' | 'warm' | 'cold' environment?: string }): { ttlMs: number maxSize: number cleanupInterval: number } { const tier = options?.tier || 'hot' const env = options?.environment || 'production' const tierConfigs = { hot: { ttlMs: 60 * 1000, // 1 minute maxSize: 1000, cleanupInterval: 30 * 1000, }, warm: { ttlMs: 5 * 60 * 1000, // 5 minutes maxSize: 10000, cleanupInterval: 60 * 1000, }, cold: { ttlMs: 24 * 60 * 60 * 1000, // 24 hours maxSize: 100000, cleanupInterval: 60 * 60 * 1000, }, } const tierConfig = tierConfigs[tier] // Scale based on environment const scaleFactor = env === 'test' ? 0.1 : env === 'development' ? 2 : 1 return { ttlMs: Math.round(tierConfig.ttlMs * scaleFactor), maxSize: Math.round(tierConfig.maxSize * scaleFactor), cleanupInterval: Math.round(tierConfig.cleanupInterval * scaleFactor), } } // ============================================================================ // Deprecated Constants Migration // ============================================================================ /** * Map of deprecated constants to their replacements. */ export const DEPRECATED_CONSTANTS = { 'postgres.WORKER_MEMORY_LIMIT_BYTES': { replacement: 'CLOUDFLARE_WORKERS_MEMORY_LIMIT_BYTES', deprecatedIn: '1.0.0', removeIn: '2.0.0', location: 'packages/postgres/src/observability/memory-metrics.ts', }, } as const /** * Gets a migration guide for a deprecated constant. * * @param name - Name of the deprecated constant (e.g., 'postgres.WORKER_MEMORY_LIMIT_BYTES') * @returns Migration guide string with instructions */ export function getMigrationGuide(name: string): string { const info = DEPRECATED_CONSTANTS[name as keyof typeof DEPRECATED_CONSTANTS] if (!info) { return `No migration guide found for '${name}'` } return ` Migration Guide for '${name}': ============================== The constant '${name}' is deprecated and will be removed in version ${info.removeIn}. Replace with: ${info.replacement} Before: // ${info.location} const limit = WORKER_MEMORY_LIMIT_BYTES After: import { ${info.replacement} } from '@dotdo/postgres-shared' const limit = ${info.replacement} This constant was deprecated in version ${info.deprecatedIn}. `.trim() } // ============================================================================ // Additional Validation Functions // ============================================================================ /** * Validates TTL configuration values. * * @param config - Configuration object with TTL value in milliseconds * @param options - Validation options (optional min/max constraints) * @throws Error if TTL is invalid */ export function validateTTLConfig( config: { ttlMs: number }, options?: { minMs?: number; maxMs?: number } ): void { if (config.ttlMs < 0) { throw new Error(`TTL must be non-negative, got ${config.ttlMs}ms`) } if (!Number.isFinite(config.ttlMs) && config.ttlMs !== Infinity) { throw new Error(`TTL must be a finite number or Infinity, got ${config.ttlMs}`) } if (options?.minMs !== undefined && config.ttlMs < options.minMs) { throw new Error(`TTL ${config.ttlMs}ms is below minimum ${options.minMs}ms`) } if (options?.maxMs !== undefined && config.ttlMs > options.maxMs) { throw new Error(`TTL ${config.ttlMs}ms exceeds maximum ${options.maxMs}ms`) } } /** * Validates port configuration values. * * @param config - Configuration object with port number * @throws Error if port is invalid */ export function validatePortConfig(config: { port: number }): void { if (!Number.isInteger(config.port)) { throw new Error(`Port must be an integer, got ${config.port}`) } if (config.port < 0 || config.port > 65535) { throw new Error(`Port must be between 0 and 65535, got ${config.port}`) } } /** * Validates memory configuration values. * * @param config - Configuration object with memory size in bytes * @param options - Validation options (optional limit) * @throws Error if memory configuration is invalid */ export function validateMemoryConfig( config: { memoryBytes: number }, options?: { maxBytes?: number } ): void { if (config.memoryBytes < 0) { throw new Error(`Memory size must be non-negative, got ${config.memoryBytes} bytes`) } if (!Number.isInteger(config.memoryBytes)) { throw new Error(`Memory size must be an integer, got ${config.memoryBytes} bytes`) } const maxBytes = options?.maxBytes ?? CLOUDFLARE_WORKERS_MEMORY_LIMIT_BYTES if (config.memoryBytes > maxBytes) { throw new Error( `Memory size ${config.memoryBytes} bytes exceeds limit of ${maxBytes} bytes` ) } } /** * Validates cache configuration values. * * @param config - Cache configuration object * @throws Error if cache configuration is invalid */ export function validateCacheConfig(config: { maxSize?: number ttlMs?: number cleanupIntervalMs?: number }): void { if (config.maxSize !== undefined) { if (!Number.isInteger(config.maxSize) || config.maxSize < 1) { throw new Error(`Cache maxSize must be a positive integer, got ${config.maxSize}`) } } if (config.ttlMs !== undefined) { validateTTLConfig({ ttlMs: config.ttlMs }) } if (config.cleanupIntervalMs !== undefined) { if (config.cleanupIntervalMs < 0) { throw new Error(`Cleanup interval must be non-negative, got ${config.cleanupIntervalMs}ms`) } } } /** * Validates circuit breaker configuration values. * * @param config - Circuit breaker configuration object * @throws Error if circuit breaker configuration is invalid */ export function validateCircuitBreakerConfig(config: { failureThreshold?: number resetTimeoutMs?: number halfOpenSuccessThreshold?: number failureWindowMs?: number }): void { if (config.failureThreshold !== undefined) { if (!Number.isInteger(config.failureThreshold) || config.failureThreshold < 1) { throw new Error( `Failure threshold must be a positive integer, got ${config.failureThreshold}` ) } } if (config.resetTimeoutMs !== undefined) { if (config.resetTimeoutMs < 0) { throw new Error(`Reset timeout must be non-negative, got ${config.resetTimeoutMs}ms`) } } if (config.halfOpenSuccessThreshold !== undefined) { if ( !Number.isInteger(config.halfOpenSuccessThreshold) || config.halfOpenSuccessThreshold < 1 ) { throw new Error( `Half-open success threshold must be a positive integer, got ${config.halfOpenSuccessThreshold}` ) } } if (config.failureWindowMs !== undefined) { if (config.failureWindowMs < 0) { throw new Error(`Failure window must be non-negative, got ${config.failureWindowMs}ms`) } } } /** * Validates hash ring configuration values. * * @param config - Hash ring configuration object * @throws Error if hash ring configuration is invalid */ export function validateHashRingConfig(config: { virtualNodes?: number shardCount?: number }): void { if (config.virtualNodes !== undefined) { if (!Number.isInteger(config.virtualNodes) || config.virtualNodes < 1) { throw new Error(`Virtual nodes must be a positive integer, got ${config.virtualNodes}`) } if (config.virtualNodes > 1000) { throw new Error(`Virtual nodes should not exceed 1000 for performance, got ${config.virtualNodes}`) } } if (config.shardCount !== undefined) { if (!Number.isInteger(config.shardCount) || config.shardCount < 1) { throw new Error(`Shard count must be a positive integer, got ${config.shardCount}`) } if (config.shardCount > MAX_SHARDS) { throw new Error(`Shard count exceeds maximum of ${MAX_SHARDS}, got ${config.shardCount}`) } } } /** * Validates histogram bucket configuration for metrics. * * @param buckets - Array of bucket boundaries * @throws Error if histogram bucket configuration is invalid */ export function validateHistogramBuckets(buckets: number[]): void { if (!Array.isArray(buckets)) { throw new Error('Histogram buckets must be an array') } if (buckets.length === 0) { throw new Error('Histogram buckets array cannot be empty') } // Check all values are positive numbers for (let i = 0; i < buckets.length; i++) { const bucket = buckets[i] if (typeof bucket !== 'number' || bucket <= 0) { throw new Error(`Histogram bucket at index ${i} must be a positive number, got ${bucket}`) } } // Check buckets are in ascending order for (let i = 1; i < buckets.length; i++) { const current = buckets[i]! const previous = buckets[i - 1]! if (current <= previous) { throw new Error( `Histogram buckets must be in ascending order, but bucket[${i}] (${current}) <= bucket[${i - 1}] (${previous})` ) } } } /** * Validates shard configuration values. * * @param config - Shard configuration object * @throws Error if shard configuration is invalid */ export function validateShardConfig(config: { targetSizeBytes?: number rebalanceThresholdBytes?: number }): void { if (config.targetSizeBytes !== undefined) { if (!Number.isInteger(config.targetSizeBytes) || config.targetSizeBytes < 1) { throw new Error(`Target shard size must be a positive integer, got ${config.targetSizeBytes}`) } } if (config.rebalanceThresholdBytes !== undefined) { if (!Number.isInteger(config.rebalanceThresholdBytes) || config.rebalanceThresholdBytes < 1) { throw new Error( `Rebalance threshold must be a positive integer, got ${config.rebalanceThresholdBytes}` ) } if (config.targetSizeBytes !== undefined && config.rebalanceThresholdBytes < config.targetSizeBytes) { throw new Error( `Rebalance threshold (${config.rebalanceThresholdBytes}) should be >= target size (${config.targetSizeBytes})` ) } } } /** * Validates access pattern tracker configuration. * * @param config - Access pattern tracker configuration object * @throws Error if configuration is invalid */ export function validateAccessPatternConfig(config: { decayHalfLifeMs?: number slidingWindowMs?: number correlationWindowMs?: number maxTrackedPages?: number pruneIntervalMs?: number }): void { if (config.decayHalfLifeMs !== undefined) { if (config.decayHalfLifeMs < 0) { throw new Error(`Decay half-life must be non-negative, got ${config.decayHalfLifeMs}ms`) } } if (config.slidingWindowMs !== undefined) { if (config.slidingWindowMs < 0) { throw new Error(`Sliding window must be non-negative, got ${config.slidingWindowMs}ms`) } } if (config.correlationWindowMs !== undefined) { if (config.correlationWindowMs < 0) { throw new Error(`Correlation window must be non-negative, got ${config.correlationWindowMs}ms`) } if (config.slidingWindowMs !== undefined && config.correlationWindowMs > config.slidingWindowMs) { throw new Error( `Correlation window (${config.correlationWindowMs}ms) should be <= sliding window (${config.slidingWindowMs}ms)` ) } } if (config.maxTrackedPages !== undefined) { if (!Number.isInteger(config.maxTrackedPages) || config.maxTrackedPages < 1) { throw new Error(`Max tracked pages must be a positive integer, got ${config.maxTrackedPages}`) } } if (config.pruneIntervalMs !== undefined) { if (config.pruneIntervalMs < 0) { throw new Error(`Prune interval must be non-negative, got ${config.pruneIntervalMs}ms`) } } } /** * Validates MongoDB operation code. * * @param opCode - MongoDB operation code to validate * @throws Error if opCode is not a valid MongoDB operation code */ export function validateMongoDBOpCode(opCode: number): void { const validOpCodes = Object.values(MONGODB_OP_CODES) as number[] if (!validOpCodes.includes(opCode)) { throw new Error( `Invalid MongoDB operation code ${opCode}. Valid codes: ${validOpCodes.join(', ')}` ) } } /** * Gets all constant categories defined in CONSTANTS_METADATA. * * @returns Array of unique category names */ export function getConstantCategories(): string[] { const categories = new Set() for (const metadata of Object.values(CONSTANTS_METADATA)) { categories.add(metadata.category) } return Array.from(categories).sort() } /** * Gets all constants for a specific category. * * @param category - Category name to filter by * @returns Object containing constants for the category */ export function getConstantsByCategory( category: string ): Record { const result: Record = {} for (const [name, metadata] of Object.entries(CONSTANTS_METADATA)) { if (metadata.category === category) { result[name] = { description: metadata.description, unit: metadata.unit, defaultValue: metadata.defaultValue, } } } return result }