Overview
Create the Sequin.Monitors context module providing all CRUD operations and business logic for sink monitors.
Depends on: #1 (Database Schema & Migrations)
Context: Sequin.Monitors
Monitor CRUD
list_monitors_for_consumer(consumer_id) — returns all monitors for a sink, preloading notification channels
list_monitors_for_account(account_id) — all monitors across all sinks for an account
list_enabled_monitors() — all enabled monitors across all accounts (used by evaluation worker)
get_monitor(id) — fetch by ID with preloads
get_monitor!(id) — raising version
create_monitor(sink_consumer_id, attrs) — creates monitor + nested notification channels
update_monitor(monitor, attrs) — updates monitor + upserts/removes channels
delete_monitor(monitor) — cascading delete
toggle_monitor(monitor, enabled) — enable/disable shortcut
Notification Channel CRUD
add_notification_channel(monitor, attrs)
update_notification_channel(channel, attrs)
remove_notification_channel(channel)
Alert Operations
create_alert(monitor, attrs) — records a new alert
resolve_alert(alert) — sets status: :resolved and resolved_at
list_alerts_for_monitor(monitor_id, opts) — paginated, ordered by triggered_at desc
get_active_alert(monitor_id) — gets current triggered (unresolved) alert if any
prune_old_alerts(older_than) — cleanup alerts older than N days
Metric Samples (for anomaly detection)
record_metric_sample(monitor, value) — inserts a sample
get_recent_samples(monitor_id, window_seconds) — returns samples within window
prune_old_samples(older_than) — removes samples older than 2 hours
Helper Functions
current_metric_value(monitor) — fetches the live metric value:
:pending_messages → SlotMessageStore.count_messages(consumer)
:failed_messages → Consumers.count_messages_for_consumer(consumer, delivery_count_gte: 1)
cooldown_active?(monitor) — checks if last alert was within cooldown_seconds
Files to Create
lib/sequin/monitors/monitors.ex
Implementation Notes
- Preload
notification_channels by default on monitor queries
- Use
Repo.transact for create/update operations that touch channels
- Follow existing context patterns (see
Sequin.Consumers, Sequin.Health)
current_metric_value/1 needs to preload the sink_consumer association
- Consider caching monitor list for the evaluation worker (refreshed every cycle)
Acceptance Criteria
Overview
Create the
Sequin.Monitorscontext module providing all CRUD operations and business logic for sink monitors.Depends on: #1 (Database Schema & Migrations)
Context:
Sequin.MonitorsMonitor CRUD
list_monitors_for_consumer(consumer_id)— returns all monitors for a sink, preloading notification channelslist_monitors_for_account(account_id)— all monitors across all sinks for an accountlist_enabled_monitors()— all enabled monitors across all accounts (used by evaluation worker)get_monitor(id)— fetch by ID with preloadsget_monitor!(id)— raising versioncreate_monitor(sink_consumer_id, attrs)— creates monitor + nested notification channelsupdate_monitor(monitor, attrs)— updates monitor + upserts/removes channelsdelete_monitor(monitor)— cascading deletetoggle_monitor(monitor, enabled)— enable/disable shortcutNotification Channel CRUD
add_notification_channel(monitor, attrs)update_notification_channel(channel, attrs)remove_notification_channel(channel)Alert Operations
create_alert(monitor, attrs)— records a new alertresolve_alert(alert)— setsstatus: :resolvedandresolved_atlist_alerts_for_monitor(monitor_id, opts)— paginated, ordered bytriggered_atdescget_active_alert(monitor_id)— gets current triggered (unresolved) alert if anyprune_old_alerts(older_than)— cleanup alerts older than N daysMetric Samples (for anomaly detection)
record_metric_sample(monitor, value)— inserts a sampleget_recent_samples(monitor_id, window_seconds)— returns samples within windowprune_old_samples(older_than)— removes samples older than 2 hoursHelper Functions
current_metric_value(monitor)— fetches the live metric value::pending_messages→SlotMessageStore.count_messages(consumer):failed_messages→Consumers.count_messages_for_consumer(consumer, delivery_count_gte: 1)cooldown_active?(monitor)— checks if last alert was withincooldown_secondsFiles to Create
lib/sequin/monitors/monitors.exImplementation Notes
notification_channelsby default on monitor queriesRepo.transactfor create/update operations that touch channelsSequin.Consumers,Sequin.Health)current_metric_value/1needs to preload thesink_consumerassociationAcceptance Criteria
current_metric_value/1returns correct counts for both metricscooldown_active?/1correctly checks timingSequin.Errortypes