NAME

Punk::Observe::Route - grouping, silences, and the outbox key

SYNOPSIS

use Punk::Observe::Route;

my $r = Punk::Observe::Route::run(
    { group_wait => 30e9, repeat_interval => 300e9 },
    [ { at => $t, group => 'prod', series => 'api',
        rule => 1, fired_at => $t } ]);

printf "sent %d messages\n", scalar @{ $r->{sent} };

DESCRIPTION

A deploy breaks forty services at once. Forty firing series is one event, and delivering it as forty messages is how a channel gets muted.

Notifications are therefore grouped by a configured label set and held for group_wait before the first send: one message listing forty services rather than forty messages. A series arriving after the group was sent opens a new group, so the forty-first is a second notification rather than a lost one.

group_wait delays the first notification. That is the intent and it has to be stated in the interface, because thirty seconds of latency on a page is something an operator needs to know in advance rather than discover during an incident.

A silence suppresses notification, not state

A silenced rule still reaches firing and still renders red. It just does not page.

A silence that hid the state would be how an incident is forgotten: somebody silences an alert to get through a deploy and the dashboard shows green for the rest of the week. The state machine in Punk::Observe::Alert is a different module and knows nothing about silences, which is what enforces it.

Silences expire. One set for a deploy and forgotten is how a real page goes unsent for a month.

The outbox key

(rule, series, fired_at). A retried send job recomputes the same key and is refused, so a delivery can never happen twice however many times the job runs.

fired_at is in the key rather than the time of the send, because a series that resolves and fires again is a new notification and must not be deduplicated against the old one.

A claimed row is not claimable by a second sender. The database does that with FOR UPDATE SKIP LOCKED; the invariant is the same either way.

FUNCTIONS

run

my $r = Punk::Observe::Route::run(\%opts, \@events);

%opts takes group_wait and repeat_interval in nanoseconds, and silences as an arrayref of { pattern, prefix, until }.

Each event takes at, and optionally group, series, rule and fired_at. An event with no series is the sender waking up to see what is due, which is how group_wait is observed at all.

Returns:

{
  sent          => [ { group, members, count, overflow, at }, ... ],
  enqueued      => how many reached the outbox
  deduped       => how many were refused as duplicates
  pending       => how many were queued
  claimed       => how many a sender took
  reclaimed     => how many a SECOND sender took - always 0
  pending_after => what is left once they are claimed
}

overflow counts members past the per-message cap. They are counted rather than dropped: a message saying "and 40 more" is an answer, one silently listing 256 of 296 is a lie.

SEE ALSO

Punk::Observe, Punk::Observe::Alert, Punk::Observe::Target