Beethoven.Allocator (Beethoven v0.3.9)

Allocator is a stack of processes that facilitate the aggregation of telemetry signals to determine how busy a given Beethoven node is.

Public API

  • allocate/0 Provides the name of the least-busy node in the cluster.
  • allocation_list/0 Provides a sorted list of all the nodes in the mnesia cluster. Nodes sorted from least-to-most busy.
  • get_all/0 Dumps a sorted list of all records from the AllocTracker mnesia table. Records sorted from least-to-most busy.

Readme

The busyness of nodes is determined via signals. A signal is defined via the signal/1 macro in Elixir.Beethoven.Allocator.Agent

Example

defmodule HttpSignal do
  use Elixir.Beethoven.Allocator.Agent
  signal(name: :http_connections, weight: 10.0, type: :count)
end

This creates a function on compile time that is used to send the signal to the Allocator.

Example for :count type signals

  # Increases the internal count by 1
  HttpSignal.increment_http_connections_count/0

  # Decreases the internal count by 1
  HttpSignal.decrement_http_connections_count/0

Signal types

  • :count -> Controls a counter for a given metric. Creates 2 functions. increment_{:name}_count/0
  • :percent -> Represents a percent value. Creates 1 function. percent_{:name}/1
  • :pre_processed -> Represents an abstract value. Creates 1 function. pre_processed_{:name}/1

Signal handling

Ingress

Once the signals are sent via the generated function, they are casted to a local instance of Elixir.Beethoven.Allocator.Ingress. This service will normalize the data from the function and save to ETS. Once saved, it will signal Elixir.Beethoven.Allocator.Cruncher to check the new data.

Cruncher

Once signaled, this service will call the ETS table shared with Elixir.Beethoven.Allocator.Ingress and grab all the current signal data. Using the weight and data payload for the signal, a busy score is generated. This score is stored in an Mnesia table for all other nodes to access. This flow allows other PIDs to call the public API for Elixir.Beethoven.Allocator and get the nodeURI for the node with the least amount of work on it.

Summary

Functions

CoreServer

Tell the local CoreServer that we want to be alerted to changes to cluster node state. Update will be sent in the form of a cast.

Provides the URI of the least-busy node in the cluster. Using this function has no side effects so discarding the output without using it will not cause issues.

Gets all the records from the Allocator table, but returns only the URIs of the nodes.. Records come pre-sorted from least-to-most busy.

Returns a specification to start this module under a supervisor.

Similar to :mnesia.dirty_select/2 but only needs the match spec as an argument. The table name of the DistrServer Elixir.Beethoven.Allocator's mnesia table is input automatically as the 1st arity.

Fetches data from the DistrServer Elixir.Beethoven.Allocator's mnesia table. Uses a record key to query the data. Will return all matching records.

Fetches all records from the DistrServer Elixir.Beethoven.Allocator's mnesia table.

Gets all the records from the Allocator table. Records come pre-sorted from least-to-most busy.

Returns the name of the DistrServer Elixir.Beethoven.Allocator's mnesia table.

Supervisor Entry point.

Subscribes to the table mapped to the DistrServer Elixir.Beethoven.Allocator's mnesia table.

Checks if the DistrServer Elixir.Beethoven.Allocator's mnesia table exists.

Holds the thread until the DistrServer Elixir.Beethoven.Allocator's mnesia table becomes available, or timeout occurs. Defaults to 1_000 milliseconds for timeouts and 15 milliseconds for checking intervals.

Functions

alert_me()

@spec alert_me() :: :ok

CoreServer

Tell the local CoreServer that we want to be alerted to changes to cluster node state. Update will be sent in the form of a cast.

Example

{:node_update, {nodeName, status}}

allocate()

@spec allocate() :: node()

Provides the URI of the least-busy node in the cluster. Using this function has no side effects so discarding the output without using it will not cause issues.

allocation_list()

@spec allocation_list() :: [node()]

Gets all the records from the Allocator table, but returns only the URIs of the nodes.. Records come pre-sorted from least-to-most busy.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

dirty_select(matchSpec)

@spec dirty_select(:ets.match_spec()) :: [tuple()] | list()

Similar to :mnesia.dirty_select/2 but only needs the match spec as an argument. The table name of the DistrServer Elixir.Beethoven.Allocator's mnesia table is input automatically as the 1st arity.

fetch(key)

@spec fetch(any()) :: [tuple()] | list()

Fetches data from the DistrServer Elixir.Beethoven.Allocator's mnesia table. Uses a record key to query the data. Will return all matching records.

fetch_all()

@spec fetch_all() :: [tuple()] | list()

Fetches all records from the DistrServer Elixir.Beethoven.Allocator's mnesia table.

get_all()

@spec get_all() :: [tuple()]

Gets all the records from the Allocator table. Records come pre-sorted from least-to-most busy.

get_table_name()

@spec get_table_name() :: module() | atom()

Returns the name of the DistrServer Elixir.Beethoven.Allocator's mnesia table.

start_link(init_args)

@spec start_link(any()) :: GenServer.on_start()

Supervisor Entry point.

subscribe(type \\ :simple)

@spec subscribe(:simple | :detailed) :: :ok

Subscribes to the table mapped to the DistrServer Elixir.Beethoven.Allocator's mnesia table.

Matches based on subscription

:simple

{:mnesia_table_event, {:atom, record(), _op_data}}

:detailed

{:mnesia_table_event, {:atom, module() | :atom(), record(), [] | [record()], _op_data}}

table_exists?()

@spec table_exists?() :: boolean()

Checks if the DistrServer Elixir.Beethoven.Allocator's mnesia table exists.

until_exists(int \\ 15, timeout \\ 1000, acc \\ 0)

Holds the thread until the DistrServer Elixir.Beethoven.Allocator's mnesia table becomes available, or timeout occurs. Defaults to 1_000 milliseconds for timeouts and 15 milliseconds for checking intervals.