Limen.State.Window (limen v0.2.1)

Copy Markdown View Source

Sliding-window request counters.

Time is cut into epochs of the window's duration. Each window has three slots used round-robin: the current epoch, the previous one, and one being cleared by Limen.State.Rotator. Clearing a whole slot replaces per-entry TTL scans. Keys carry their epoch, so a slot that is cleared late can never leak counts into a newer epoch.

The rate over the last window is estimated from the current and previous epochs, weighting the previous one by how much of it still overlaps the sliding window:

estimate = current + previous × (duration - elapsed) / duration

A key holds one count (incr/4, count/4) or a row of several (add/5, read/5). A row keeps counts about the same subject together: updating any of them costs one table update and one lookup of the previous epoch, and returns all of them.

Each slot counts up to :max_keys keys exactly, a row being one key. After that, new keys are counted in the slot's Count-Min Sketch (Limen.Sketch.CountMin), whose estimates may overcount but never undercount.

Summary

Functions

Adds increments to the row of counts of key at now (milliseconds) and returns the sliding estimate of every count, including them.

Like add/5, and also returns whether this update made the row's first count positive in the current epoch. For a row whose first count is requests, that is whether this is the key's first request of the epoch.

Returns the sliding estimate for key at now without counting.

Counts one event for key at now (milliseconds) and returns the sliding estimate including it.

Returns the sliding estimates of the width counts in the row of key at now without counting.

Clears the slot that the next epoch will use.

Returns the exact counts of the current epoch, largest first.

Types

window()

@type window() :: Limen.State.window()

Functions

add(instance, window, key, increments, now)

@spec add(Limen.Instance.t(), window(), term(), tuple(), integer()) :: tuple()

Adds increments to the row of counts of key at now (milliseconds) and returns the sliding estimate of every count, including them.

increments has one non-negative integer per count, at least one of them positive. A key's rows always have the same number of counts.

add_first(instance, window, key, increments, now)

@spec add_first(Limen.Instance.t(), window(), term(), tuple(), integer()) ::
  {tuple(), boolean()}

Like add/5, and also returns whether this update made the row's first count positive in the current epoch. For a row whose first count is requests, that is whether this is the key's first request of the epoch.

A row counted in the sketch, once the slot is full, always reports true: the sketch cannot tell.

count(instance, window, key, now)

@spec count(Limen.Instance.t(), window(), term(), integer()) :: non_neg_integer()

Returns the sliding estimate for key at now without counting.

incr(instance, window, key, now)

@spec incr(Limen.Instance.t(), window(), term(), integer()) :: non_neg_integer()

Counts one event for key at now (milliseconds) and returns the sliding estimate including it.

read(instance, window, key, width, now)

@spec read(Limen.Instance.t(), window(), term(), pos_integer(), integer()) :: tuple()

Returns the sliding estimates of the width counts in the row of key at now without counting.

rotate(instance, window, now)

@spec rotate(Limen.Instance.t(), window(), integer()) :: non_neg_integer()

Clears the slot that the next epoch will use.

Only entries older than epoch are removed, so running late (after the next epoch started) never drops fresh counts.

top(instance, window, filter, limit, now, column \\ 1)

@spec top(
  Limen.Instance.t(),
  window(),
  (term() -> boolean()),
  pos_integer(),
  integer(),
  pos_integer()
) ::
  [{term(), non_neg_integer()}]

Returns the exact counts of the current epoch, largest first.

Keys for which filter returns true are ranked by their count, and rows by their count in column, from 1.

Scans the slot, so it is meant for dashboards, not for the request path. Keys counted in the sketch are not included.