Description
check_runs records are created every time a health check executes. With the default interval of 1 minute per check, a single health check generates 1,440 records per day. The table has no time-based partitioning, no archival strategy, and no automatic purge.
The has_many :check_runs, dependent: :destroy association in HealthCheck means deleting a check cascades to all runs—but a health check that has been running for a year with a 1-minute interval has 525,600 rows that must all be deleted in a single transaction.
Impact
- Table grows ~1M rows per check per year with 1-minute intervals.
- Queries on
check_runs (dashboard, history views) slow down as the table grows, even with indexes.
- Deleting an active check can time out due to cascaded destroy.
last_response is a TEXT column storing full HTTP response bodies—potentially KB per row.
Suggested approach
- Add a
cleanup_old_check_runs job that deletes rows older than N days (configurable per account).
- Use Rails
dependent: :delete_all (bulk SQL) instead of dependent: :destroy for the cascade.
- Consider archiving old runs to a separate table or cold storage.
- Add database-level partitioning by
created_at for MySQL 8.0+.
Effort: medium
Description
check_runsrecords are created every time a health check executes. With the default interval of 1 minute per check, a single health check generates 1,440 records per day. The table has no time-based partitioning, no archival strategy, and no automatic purge.The
has_many :check_runs, dependent: :destroyassociation inHealthCheckmeans deleting a check cascades to all runs—but a health check that has been running for a year with a 1-minute interval has 525,600 rows that must all be deleted in a single transaction.Impact
check_runs(dashboard, history views) slow down as the table grows, even with indexes.last_responseis a TEXT column storing full HTTP response bodies—potentially KB per row.Suggested approach
cleanup_old_check_runsjob that deletes rows older than N days (configurable per account).dependent: :delete_all(bulk SQL) instead ofdependent: :destroyfor the cascade.created_atfor MySQL 8.0+.Effort: medium