defmodule Mix.Tasks.PgflowDashboard.Gen.Indexes do
@shortdoc "Generates optional performance indexes for PgFlow Dashboard"
@moduledoc """
Generates an Ecto migration that adds performance indexes to pgflow tables
for faster dashboard queries.
## Usage
mix pgflow_dashboard.gen.indexes
mix pgflow_dashboard.gen.indexes --migrations-path priv/repo/migrations
## Options
* `--migrations-path` - Path to the migrations directory.
Defaults to `priv/repo/migrations`.
## Generated Indexes
This migration creates the following indexes:
* `idx_runs_started_at` - For time-range filtering
* `idx_runs_flow_status_started` - For filtered run lists
* `idx_runs_completed_at` - For duration calculations
* `idx_step_states_status` - For progress calculations
## Note
These indexes are optional and primarily benefit dashboards with high query load.
For small deployments, the default indexes may be sufficient.
## Example
# Generate the migration
$ mix pgflow_dashboard.gen.indexes
# Run the migration
$ mix ecto.migrate
"""
use Mix.Task
alias Mix.Tasks.Pgflow.Helpers
@impl Mix.Task
def run(args) do
Helpers.write_migration(
args,
"add_pgflow_dashboard_indexes",
&generate_migration_content/1,
&message/1
)
end
defp message(filepath) do
"""
Generated migration: #{filepath}
Run the migration with:
mix ecto.migrate
This will create performance indexes for dashboard queries.
These indexes are optional but recommended for high-traffic dashboards.
"""
end
defp generate_migration_content(app_module) do
"""
defmodule #{app_module}.Repo.Migrations.AddPgflowDashboardIndexes do
@moduledoc \"\"\"
Adds optional performance indexes for PgFlow Dashboard queries.
These indexes optimize:
- Time-range filtered queries
- Status-based filtering
- Progress calculations
Generated by: mix pgflow_dashboard.gen.indexes
\"\"\"
use Ecto.Migration
@disable_ddl_transaction true
@disable_migration_lock true
def up do
# Index for time-range filtering on runs
execute \"\"\"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_runs_started_at
ON pgflow.runs (started_at DESC)
\"\"\"
# Composite index for filtered run lists
execute \"\"\"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_runs_flow_status_started
ON pgflow.runs (flow_slug, status, started_at DESC)
\"\"\"
# Index for completed runs (duration calculations)
execute \"\"\"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_runs_completed_at
ON pgflow.runs (completed_at DESC)
WHERE completed_at IS NOT NULL
\"\"\"
# Index for step state status (progress calculations)
execute \"\"\"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_step_states_status
ON pgflow.step_states (run_id, status)
\"\"\"
# Index for step tasks status
execute \"\"\"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_step_tasks_status
ON pgflow.step_tasks (run_id, step_slug, status)
\"\"\"
# Index for worker heartbeats
execute \"\"\"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_workers_heartbeat
ON pgflow.workers (last_heartbeat_at DESC)
\"\"\"
end
def down do
execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_workers_heartbeat"
execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_step_tasks_status"
execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_step_states_status"
execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_runs_completed_at"
execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_runs_flow_status_started"
execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_runs_started_at"
end
end
"""
end
end