- Deprecated/broken migration files archived - New auth_schema migration (015) - Trading service events migration (016) - Migration renumbering utility - Migration test suite
407 lines
15 KiB
PL/PgSQL
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;
|