Limen.State (limen v0.2.1)

Copy Markdown View Source

An instance's shared, lock-free state.

new/1 creates every ETS table and :atomics array an instance needs; the instance's Limen.Owner calls it when the instance starts, owns the tables, and publishes them in its Limen.Instance. Requests read and write the tables directly; they never message a process. Background work (rotating time windows, sweeping expired entries) lives in Limen.State.Rotator and Limen.State.Sweeper, one of each per instance.

Tables

Memory bounds

Every table has a hard cap on the number of keys (see the :state options in Limen.Config), counted with :atomics so the request path never has to ask ETS for a table size. Sketches are allocated once and never grow. Once a table is full:

  • windows count new keys in the slot's sketch instead (estimates may overcount, never undercount);
  • GCRA stops tracking new keys, which are then not limited;
  • the ban list refuses new bans.

Each case increments the :saturated counter in Limen.Stats.

Summary

Functions

Distinct client prefixes instance saw over the last one to two minutes, as last estimated in the background.

Duration of window in milliseconds.

Memory used by an instance's state, in bytes, by table.

Whether a client prefix had not requested path recently.

Adds a client prefix to the active prefix count.

Clears every table and counter of an instance. Meant for tests.

Number of epoch slots per window.

Window names and their durations in milliseconds.

Types

t()

@type t() :: %{
  windows: %{
    required(window()) => %{
      tables: tuple(),
      counts: :atomics.atomics_ref(),
      sketches: tuple(),
      max_keys: pos_integer()
    }
  },
  gcra: %{table: :ets.tid(), size: :atomics.atomics_ref()},
  bans: %{
    table: :ets.tid(),
    size: :atomics.atomics_ref(),
    outbox: :ets.tid(),
    outbox_size: :atomics.atomics_ref()
  },
  distinct: %{
    prefixes: tuple(),
    paths: Limen.Sketch.RotatingBloom.t(),
    estimate: :atomics.atomics_ref()
  },
  fcrdns: %{
    cache: :ets.tid(),
    pending: :ets.tid(),
    size: :atomics.atomics_ref()
  }
}

window()

@type window() :: :second | :minute | :hour

Functions

active_prefixes(instance)

@spec active_prefixes(Limen.Instance.t()) :: non_neg_integer()

Distinct client prefixes instance saw over the last one to two minutes, as last estimated in the background.

duration(atom)

@spec duration(window()) :: pos_integer()

Duration of window in milliseconds.

memory(instance)

@spec memory(atom() | Limen.Instance.t()) :: %{required(term()) => non_neg_integer()}

Memory used by an instance's state, in bytes, by table.

new_path?(instance, prefix, path)

@spec new_path?(Limen.Instance.t(), Limen.IP.prefix(), String.t()) :: boolean()

Whether a client prefix had not requested path recently.

observe_prefix(instance, prefix, now)

@spec observe_prefix(Limen.Instance.t(), Limen.IP.prefix(), integer()) :: :ok

Adds a client prefix to the active prefix count.

Adding a prefix again in the same minute changes nothing, so callers only need to on its first request of the minute.

reset(instance)

@spec reset(atom() | Limen.Instance.t()) :: :ok

Clears every table and counter of an instance. Meant for tests.

slots()

@spec slots() :: pos_integer()

Number of epoch slots per window.

windows()

@spec windows() :: keyword(pos_integer())

Window names and their durations in milliseconds.