Files
foxhunt/migrations/tests/test_timescaledb_features.sql
jgrusewski 0d9f890aa6 🗄️ Wave 112: Migration cleanup and new schemas
- Deprecated/broken migration files archived
- New auth_schema migration (015)
- Trading service events migration (016)
- Migration renumbering utility
- Migration test suite
2025-10-05 22:23:37 +02:00

407 lines
15 KiB
PL/PgSQL

-- ================================================================================================
-- 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;