-- ================================================================================================ -- TimescaleDB Features Test Suite -- PostgreSQL 16.10 + TimescaleDB 2.22.1 -- Tests hypertables, partitioning, compression, and continuous aggregates -- ================================================================================================ BEGIN; -- ================================================================================================ -- TEST 1: TimescaleDB Extension Verification -- Verify TimescaleDB is installed and check version -- ================================================================================================ DO $$ DECLARE v_tsdb_version TEXT; v_pg_version TEXT; BEGIN -- Check TimescaleDB version SELECT extversion INTO v_tsdb_version FROM pg_extension WHERE extname = 'timescaledb'; IF v_tsdb_version IS NULL THEN RAISE EXCEPTION 'TEST 1 FAIL: TimescaleDB extension not installed'; END IF; -- Check PostgreSQL version SELECT version() INTO v_pg_version; RAISE NOTICE 'TEST 1 PASS: TimescaleDB % installed on %', v_tsdb_version, split_part(v_pg_version, ',', 1); -- Verify minimum versions IF v_tsdb_version >= '2.22.0' THEN RAISE NOTICE 'TEST 1 INFO: TimescaleDB version meets minimum requirement (2.22.1)'; ELSE RAISE WARNING 'TEST 1 WARNING: TimescaleDB version % may be below recommended 2.22.1', v_tsdb_version; END IF; END $$; -- ================================================================================================ -- TEST 2: Hypertable Configuration -- Verify tables are configured as hypertables -- ================================================================================================ DO $$ DECLARE v_expected_hypertables TEXT[] := ARRAY[ 'trading_events', 'risk_events', 'audit_events', 'market_data_raw', 'market_data_aggregated' ]; v_table TEXT; v_hypertables TEXT[] := ARRAY[]::TEXT[]; v_missing_hypertables TEXT[] := ARRAY[]::TEXT[]; BEGIN FOREACH v_table IN ARRAY v_expected_hypertables LOOP IF EXISTS ( SELECT 1 FROM timescaledb_information.hypertables WHERE hypertable_name = v_table ) THEN v_hypertables := array_append(v_hypertables, v_table); ELSE -- Only add to missing if table exists but is not a hypertable IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = v_table) THEN v_missing_hypertables := array_append(v_missing_hypertables, v_table); END IF; END IF; END LOOP; IF array_length(v_hypertables, 1) > 0 THEN RAISE NOTICE 'TEST 2 PASS: Found % hypertables: %', array_length(v_hypertables, 1), array_to_string(v_hypertables, ', '); ELSE RAISE WARNING 'TEST 2 WARNING: No hypertables found'; END IF; IF array_length(v_missing_hypertables, 1) > 0 THEN RAISE WARNING 'TEST 2 WARNING: Tables exist but are not hypertables: %', array_to_string(v_missing_hypertables, ', '); END IF; END $$; -- ================================================================================================ -- TEST 3: Partition Configuration -- Verify partition intervals are properly configured -- ================================================================================================ DO $$ DECLARE v_hypertable_record RECORD; v_partition_info TEXT; BEGIN FOR v_hypertable_record IN SELECT hypertable_name, chunk_sizing_func, chunk_target_size FROM timescaledb_information.hypertables WHERE hypertable_schema = 'public' LOOP v_partition_info := v_hypertable_record.hypertable_name || ' (sizing: ' || COALESCE(v_hypertable_record.chunk_sizing_func, 'default') || ')'; RAISE NOTICE 'TEST 3 INFO: Hypertable partition config - %', v_partition_info; END LOOP; IF FOUND THEN RAISE NOTICE 'TEST 3 PASS: Partition configurations verified'; ELSE RAISE WARNING 'TEST 3 WARNING: No hypertable partition configurations found'; END IF; END $$; -- ================================================================================================ -- TEST 4: Chunk Management -- Verify chunks are being created and managed properly -- ================================================================================================ DO $$ DECLARE v_chunk_count INTEGER; v_hypertable_record RECORD; BEGIN FOR v_hypertable_record IN SELECT h.hypertable_name, COUNT(c.chunk_name) as chunk_count, pg_size_pretty(SUM(c.total_bytes)::bigint) as total_size FROM timescaledb_information.hypertables h LEFT JOIN timescaledb_information.chunks c ON h.hypertable_name = c.hypertable_name WHERE h.hypertable_schema = 'public' GROUP BY h.hypertable_name LOOP RAISE NOTICE 'TEST 4 INFO: % has % chunks (total size: %)', v_hypertable_record.hypertable_name, v_hypertable_record.chunk_count, COALESCE(v_hypertable_record.total_size, '0 bytes'); END LOOP; SELECT COUNT(*) INTO v_chunk_count FROM timescaledb_information.chunks WHERE hypertable_schema = 'public'; IF v_chunk_count > 0 THEN RAISE NOTICE 'TEST 4 PASS: Chunk management operational (% total chunks)', v_chunk_count; ELSE RAISE NOTICE 'TEST 4 INFO: No chunks created yet (expected for empty hypertables)'; END IF; END $$; -- ================================================================================================ -- TEST 5: Compression Policies -- Verify compression is configured for time-series data -- ================================================================================================ DO $$ DECLARE v_compression_record RECORD; v_compression_count INTEGER := 0; BEGIN FOR v_compression_record IN SELECT hypertable_name, attname as compressed_column, compression_algorithm FROM timescaledb_information.compression_settings WHERE hypertable_schema = 'public' ORDER BY hypertable_name, orderby_column_index LOOP v_compression_count := v_compression_count + 1; RAISE NOTICE 'TEST 5 INFO: Compression on %.% (algorithm: %)', v_compression_record.hypertable_name, v_compression_record.compressed_column, v_compression_record.compression_algorithm; END LOOP; IF v_compression_count > 0 THEN RAISE NOTICE 'TEST 5 PASS: Compression configured on % columns', v_compression_count; ELSE RAISE NOTICE 'TEST 5 INFO: No compression policies configured (may be intentional for hot data)'; END IF; END $$; -- ================================================================================================ -- TEST 6: Retention Policies -- Verify data retention policies are configured -- ================================================================================================ DO $$ DECLARE v_retention_record RECORD; v_retention_count INTEGER := 0; BEGIN FOR v_retention_record IN SELECT hypertable_name, drop_after, schedule_interval FROM timescaledb_information.jobs j JOIN timescaledb_information.job_stats js ON j.job_id = js.job_id WHERE j.proc_name = 'policy_retention' AND hypertable_schema = 'public' LOOP v_retention_count := v_retention_count + 1; RAISE NOTICE 'TEST 6 INFO: Retention policy on % (drop after: %, interval: %)', v_retention_record.hypertable_name, v_retention_record.drop_after, v_retention_record.schedule_interval; END LOOP; IF v_retention_count > 0 THEN RAISE NOTICE 'TEST 6 PASS: Data retention policies configured (% policies)', v_retention_count; ELSE RAISE NOTICE 'TEST 6 INFO: No retention policies configured (manual management may be in use)'; END IF; END $$; -- ================================================================================================ -- TEST 7: Continuous Aggregates -- Verify continuous aggregates for real-time analytics -- ================================================================================================ DO $$ DECLARE v_cagg_record RECORD; v_cagg_count INTEGER := 0; BEGIN FOR v_cagg_record IN SELECT view_name, materialization_hypertable_name, refresh_lag, refresh_interval FROM timescaledb_information.continuous_aggregates WHERE view_schema = 'public' LOOP v_cagg_count := v_cagg_count + 1; RAISE NOTICE 'TEST 7 INFO: Continuous aggregate % (lag: %, interval: %)', v_cagg_record.view_name, v_cagg_record.refresh_lag, v_cagg_record.refresh_interval; END LOOP; IF v_cagg_count > 0 THEN RAISE NOTICE 'TEST 7 PASS: Continuous aggregates configured (% aggregates)', v_cagg_count; ELSE RAISE NOTICE 'TEST 7 INFO: No continuous aggregates configured (may use real-time queries)'; END IF; END $$; -- ================================================================================================ -- TEST 8: Hypertable Insert Performance -- Test insertion into hypertable with automatic partitioning -- ================================================================================================ DO $$ DECLARE v_start_time TIMESTAMP; v_end_time TIMESTAMP; v_duration INTERVAL; v_test_table TEXT := 'trading_events'; v_current_ns BIGINT := EXTRACT(EPOCH FROM NOW())::BIGINT * 1000000000; i INTEGER; BEGIN -- Check if table is a hypertable IF NOT EXISTS ( SELECT 1 FROM timescaledb_information.hypertables WHERE hypertable_name = v_test_table ) THEN RAISE NOTICE 'TEST 8 INFO: Skipping - % is not a hypertable', v_test_table; RETURN; END IF; v_start_time := clock_timestamp(); -- Insert 50 test events FOR i IN 1..50 LOOP INSERT INTO trading_events ( correlation_id, event_timestamp, received_timestamp, processing_timestamp, event_type, event_source, symbol, event_data, event_hash, node_id, process_id ) VALUES ( uuid_generate_v4(), v_current_ns + (i * 1000000), -- 1ms intervals v_current_ns + (i * 1000000), v_current_ns + (i * 1000000), 'order_submitted', 'test_suite', 'PERF' || (i % 5), format('{"test_id": %s}', i)::jsonb, encode(sha256(('perf' || i)::bytea), 'hex'), 'test-node', 99999 ); END LOOP; v_end_time := clock_timestamp(); v_duration := v_end_time - v_start_time; RAISE NOTICE 'TEST 8 PASS: Hypertable insert performance - 50 events in %', v_duration; IF v_duration > INTERVAL '100 milliseconds' THEN RAISE WARNING 'TEST 8 WARNING: Insert duration > 100ms, may indicate performance issue'; END IF; END $$; -- ================================================================================================ -- TEST 9: Time-based Query Performance -- Verify time-based queries use chunk exclusion -- ================================================================================================ DO $$ DECLARE v_plan_row RECORD; v_uses_chunk_exclusion BOOLEAN := FALSE; v_test_table TEXT := 'trading_events'; BEGIN -- Check if table exists and is hypertable IF NOT EXISTS ( SELECT 1 FROM timescaledb_information.hypertables WHERE hypertable_name = v_test_table ) THEN RAISE NOTICE 'TEST 9 INFO: Skipping - % is not a hypertable', v_test_table; RETURN; END IF; -- Check query plan for chunk exclusion FOR v_plan_row IN EXPLAIN (FORMAT TEXT) SELECT * FROM trading_events WHERE event_timestamp >= EXTRACT(EPOCH FROM NOW() - INTERVAL '1 hour')::BIGINT * 1000000000 LIMIT 100 LOOP IF v_plan_row."QUERY PLAN" LIKE '%Chunk%' OR v_plan_row."QUERY PLAN" LIKE '%constraint%' OR v_plan_row."QUERY PLAN" LIKE '%exclusion%' THEN v_uses_chunk_exclusion := TRUE; RAISE NOTICE 'TEST 9 INFO: Query plan - %', v_plan_row."QUERY PLAN"; END IF; END LOOP; IF v_uses_chunk_exclusion THEN RAISE NOTICE 'TEST 9 PASS: Time-based queries use chunk exclusion optimization'; ELSE RAISE NOTICE 'TEST 9 INFO: Chunk exclusion not detected (may indicate empty hypertable)'; END IF; END $$; -- ================================================================================================ -- TEST 10: Background Jobs Health -- Verify TimescaleDB background jobs are running -- ================================================================================================ DO $$ DECLARE v_job_record RECORD; v_active_jobs INTEGER := 0; v_failed_jobs INTEGER := 0; BEGIN FOR v_job_record IN SELECT j.job_id, j.proc_name, js.last_run_status, js.last_run_started_at, js.total_runs, js.total_failures FROM timescaledb_information.jobs j JOIN timescaledb_information.job_stats js ON j.job_id = js.job_id WHERE j.hypertable_schema = 'public' ORDER BY j.job_id LOOP IF v_job_record.last_run_status = 'Success' OR v_job_record.total_runs = 0 THEN v_active_jobs := v_active_jobs + 1; RAISE NOTICE 'TEST 10 INFO: Job % (%) - runs: %, failures: %', v_job_record.job_id, v_job_record.proc_name, v_job_record.total_runs, v_job_record.total_failures; ELSE v_failed_jobs := v_failed_jobs + 1; RAISE WARNING 'TEST 10 WARNING: Job % failed - last status: %', v_job_record.job_id, v_job_record.last_run_status; END IF; END LOOP; IF v_active_jobs > 0 THEN RAISE NOTICE 'TEST 10 PASS: Background jobs operational (% active, % failed)', v_active_jobs, v_failed_jobs; ELSE RAISE NOTICE 'TEST 10 INFO: No background jobs configured'; END IF; END $$; -- ================================================================================================ -- Cleanup: Rollback all test data -- ================================================================================================ ROLLBACK; -- Test Summary SELECT 'TimescaleDB Features Test Suite Completed - Review NOTICE/WARNING messages above' AS test_summary;