# Execution Table → TimescaleDB Hypertable Conversion **Date**: 2026-02-27 **Scope**: Database migration, Rust code fixes, AGENTS.md updates ## Summary Converted the `execution` table from a regular PostgreSQL table to a TimescaleDB hypertable partitioned on `created` (1-day chunks), consistent with the existing `event` and `enforcement` hypertable conversions. This enables automatic time-based partitioning, compression, and retention for execution data. ## Key Design Decisions - **`updated` column preserved**: Unlike `event` (immutable) and `enforcement` (single update), executions are updated ~4 times during their lifecycle. The `updated` column and its BEFORE UPDATE trigger are kept because the timeout monitor and UI depend on them. - **`execution_history` preserved**: The execution_history hypertable tracks field-level diffs which remain valuable for a mutable table. Its continuous aggregates (`execution_status_hourly`, `execution_throughput_hourly`) are unchanged. - **7-day compression window is safe**: Executions complete within at most ~1 day, so all updates finish well before compression kicks in. - **New `execution_volume_hourly` continuous aggregate**: Queries the execution hypertable directly (like `event_volume_hourly` queries event), providing belt-and-suspenders volume monitoring alongside the history-based aggregates. ## Changes ### New Migration: `migrations/20250101000010_execution_hypertable.sql` - Drops all FK constraints referencing `execution` (inquiry, workflow_execution, self-references, action, executor, workflow_def) - Changes PK from `(id)` to `(id, created)` (TimescaleDB requirement) - Converts to hypertable with `create_hypertable('execution', 'created', chunk_time_interval => '1 day')` - Adds compression policy (segmented by `action_ref`, after 7 days) - Adds 90-day retention policy - Adds `execution_volume_hourly` continuous aggregate with 30-minute refresh policy ### Rust Code Fixes - **`crates/executor/src/timeout_monitor.rs`**: Replaced `SELECT * FROM execution` with explicit column list. `SELECT *` on hypertables is fragile — the execution table has columns (`is_workflow`, `workflow_def`) not present in the Rust `Execution` model. - **`crates/api/tests/sse_execution_stream_tests.rs`**: Fixed references to non-existent `start_time` and `end_time` columns (replaced with `updated = NOW()`). - **`crates/common/src/repositories/analytics.rs`**: Added `ExecutionVolumeBucket` struct and `execution_volume_hourly` / `execution_volume_hourly_by_action` repository methods for the new continuous aggregate. ### AGENTS.md Updates - Added **Execution Table (TimescaleDB Hypertable)** documentation - Updated FK ON DELETE Policy to reflect execution as hypertable - Updated Nullable FK Fields to list all dropped FK constraints - Updated table count (still 20) and migration count (9 → 10) - Updated continuous aggregate count (5 → 6) - Updated development status to include execution hypertable - Added pitfall #19: never use `SELECT *` on hypertable-backed models - Added pitfall #20: execution/event/enforcement cannot be FK targets ## FK Constraints Dropped | Source Column | Target | Disposition | |---|---|---| | `inquiry.execution` | `execution(id)` | Column kept as plain BIGINT | | `workflow_execution.execution` | `execution(id)` | Column kept as plain BIGINT | | `execution.parent` | `execution(id)` | Self-ref, column kept | | `execution.original_execution` | `execution(id)` | Self-ref, column kept | | `execution.workflow_def` | `workflow_definition(id)` | Column kept | | `execution.action` | `action(id)` | Column kept | | `execution.executor` | `identity(id)` | Column kept | | `execution.enforcement` | `enforcement(id)` | Already dropped in migration 000009 | ## Verification - `cargo check --all-targets --workspace`: Zero warnings - `cargo test --workspace --lib`: All 90 unit tests pass - Integration test failures are pre-existing (missing `attune_test` database), unrelated to these changes