rbed

Pulse Channels

Some Channels report one-off occurrences rather than a state: a deployment, a button press, a trade. An ordinary Channel keeps its last value until the next one arrives, so a deploy value of 1 would stay 1 forever. A Pulse Channel's value exists only in the Sample that carries it.

Declaring one

Create the Channel with Channel.pulse(name) instead of Channel.named(name). Everything else stays the same: push its value in the Samples that carry an occurrence, and leave it out of the others.

Deployments and errors

A service reports its errors once a minute, and the Pulse Channel deploy reports its deployments, at minutes 10 and 60. Deployment is an Event while a deployment is reported, which is exactly one Sample: at the next Sample deploy is absent, and the Event Ends. Regression is an error spike within a window after a Deployment, written with after(deployment).within(...).

The live chart and its controls need JavaScript. The tables show the Events at the default parameters.

The site cannot run the default parameters: (JavaScript) TypeError: Cannot read properties of undefined (reading 'length')

import static io.github.romanhaendler.rbed.Conditions.*;

import io.github.romanhaendler.rbed.*;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

Channel deploy = Channel.pulse("deploy");
Channel errors = Channel.named("errors");

Rule deployment = Rule.named("Deployment").when(deploy.above(0.5));
Rule regression = Rule.named("Regression")
    .when(after(deployment).within(Duration.ofMinutes(30)).and(errors.above(5)));
Detector detector = Detector.builder()
    .rules(deployment, regression)
    .build();

for (int m = 0; m <= 120; m++) {   // one Sample a minute
  Map<Channel, Double> values = new HashMap<>();
  values.put(errors, m >= 25 && m < 35 ? 12.0 : m >= 100 && m < 105 ? 9.0 : 3.0);
  if (m == 10 || m == 60) values.put(deploy, 1.0);   // only the Samples with a deployment carry deploy
  detector.push(m * 60_000L, values);
}
detector.close();

At the defaults, the spike at minute 25 starts 14 minutes after the first Deployment Ended (minute 11) and is a Regression. The second spike, at minute 100, starts 39 minutes after the second Deployment Ended (minute 61), outside the 30-minute window.

Things to try

  • Widen the window to 45 minutes: the second spike is a Regression too.
  • Set it to 40 minutes: the second spike counts only until minute 101, the last minute within 40 minutes of the Deployment's end, so its Regression runs from 100 to 102.

Absent is not undefined

In every Sample that does not carry it, and at every time advance, a Pulse Channel is absent. Absent is not undefined, because the absence of a pulse is known:

  • Comparisons, rising and falling on an absent pulse are false, so not() of them is true: deploy.above(0.5).not() means "no deployment now".
  • Arithmetic on an absent pulse is undefined.
  • age() is the time since the last pulse: a heartbeat.age() above a minute is a heartbeat that has gone quiet.

Reference

Methods used: Channel.pulse, Channel.named, Rule.named, Rule.when, Expression.above, Conditions.after, After.within, Condition.and, Detector.builder, Detector.Builder.rules, Detector.Builder.build, Detector.push, Detector.close.