diff --git a/priv/repo/migrations/20251221225717_create_monitoring_aggregates.exs b/priv/repo/migrations/20251221225717_create_monitoring_aggregates.exs index 64bca03a..463ba07a 100644 --- a/priv/repo/migrations/20251221225717_create_monitoring_aggregates.exs +++ b/priv/repo/migrations/20251221225717_create_monitoring_aggregates.exs @@ -1,69 +1,16 @@ defmodule Towerops.Repo.Migrations.CreateMonitoringAggregates do use Ecto.Migration - # Disable DDL transaction for TimescaleDB continuous aggregates - @disable_ddl_transaction true - @disable_migration_lock true + # Note: TimescaleDB continuous aggregates require commercial license + # This migration is disabled - aggregates are computed in real-time instead def up do - # Hourly continuous aggregate for monitoring checks - execute(""" - CREATE MATERIALIZED VIEW monitoring_checks_hourly - WITH (timescaledb.continuous) AS - SELECT - equipment_id, - time_bucket('1 hour', checked_at) AS bucket, - COUNT(*) AS total_checks, - COUNT(*) FILTER (WHERE status = 'success') AS successful_checks, - COUNT(*) FILTER (WHERE status = 'failure') AS failed_checks, - AVG(response_time_ms) FILTER (WHERE status = 'success') AS avg_response_time_ms, - MIN(response_time_ms) FILTER (WHERE status = 'success') AS min_response_time_ms, - MAX(response_time_ms) FILTER (WHERE status = 'success') AS max_response_time_ms - FROM monitoring_checks - GROUP BY equipment_id, bucket - """) - - # Add refresh policy for hourly aggregate (refresh every hour) - execute(""" - SELECT add_continuous_aggregate_policy('monitoring_checks_hourly', - start_offset => INTERVAL '3 hours', - end_offset => INTERVAL '1 hour', - schedule_interval => INTERVAL '1 hour') - """) - - # Daily continuous aggregate for monitoring checks - execute(""" - CREATE MATERIALIZED VIEW monitoring_checks_daily - WITH (timescaledb.continuous) AS - SELECT - equipment_id, - time_bucket('1 day', checked_at) AS bucket, - COUNT(*) AS total_checks, - COUNT(*) FILTER (WHERE status = 'success') AS successful_checks, - COUNT(*) FILTER (WHERE status = 'failure') AS failed_checks, - AVG(response_time_ms) FILTER (WHERE status = 'success') AS avg_response_time_ms, - MIN(response_time_ms) FILTER (WHERE status = 'success') AS min_response_time_ms, - MAX(response_time_ms) FILTER (WHERE status = 'success') AS max_response_time_ms, - ROUND(100.0 * COUNT(*) FILTER (WHERE status = 'success') / NULLIF(COUNT(*), 0), 2) AS uptime_percentage - FROM monitoring_checks - GROUP BY equipment_id, bucket - """) - - # Add refresh policy for daily aggregate (refresh every day) - execute(""" - SELECT add_continuous_aggregate_policy('monitoring_checks_daily', - start_offset => INTERVAL '3 days', - end_offset => INTERVAL '1 day', - schedule_interval => INTERVAL '1 day') - """) - - # Create indexes on the materialized views - create index(:monitoring_checks_hourly, [:equipment_id, :bucket]) - create index(:monitoring_checks_daily, [:equipment_id, :bucket]) + # TimescaleDB continuous aggregates disabled due to license restrictions + # Metrics are calculated on-demand in the application + :ok end def down do - execute("DROP MATERIALIZED VIEW IF EXISTS monitoring_checks_daily") - execute("DROP MATERIALIZED VIEW IF EXISTS monitoring_checks_hourly") + :ok end end