Cauldron2D.Net.Load (Cauldron2D.Net v0.1.3)

Copy Markdown View Source

A load test against a running server: players and watchers joining its arenas over the wire, the frames they get measured against the steps the server runs, and the server's own numbers (Cauldron2D.Net.Api's /stats) alongside.

Cauldron2D.Net.Load.run(url: "http://arcade:2280/api", actions: ["left", "right", "fire"], players: 40, watchers: 100, seconds: 60)
Cauldron2D.Net.Load.run(url: "http://arcade:2280/api", actions: [], players: 10, ramp: 10, every: 5, seconds: 120)

Sessions are accounts load_1, load_2… (password load test), registered on the server if they are not there, and spread over the arenas in turn. A report goes out every :every seconds: a line with the sessions joined, the frames a second they got (mean and least) against the ticks a second the server's worlds are set to, the longest gap between frames, and the node (how busy its schedulers were since the last report, the run queue, processes, memory); then a line for each of the three arenas with the longest ticks — the mean, the 95th percentile and the longest against the tick's budget, and the share of the last ticks that ran more than one step (behind; a little is timer jitter, more is a world that cannot keep up).

A run is degraded when, over one report, any world's 95th-percentile tick is longer than its budget (1 / hz), any world ran behind on more than a fifth of its ticks (a few double steps are the timer firing late on a busy node, not a world that cannot keep up — its ticks say which), the least frame rate any session saw fell under 80% of the tick rate, or a session waited over a second for a frame. The report says so and why. With :ramp, that many more sessions join at every report until the run is degraded — the sessions before the last addition are the capacity in the summary — or the time is up.

stop/0 from another process ends a run early, after the interval in progress, with the summary of what was measured; a mix task can call it on a key press.

Options

  • :url — the server's API, default http://localhost:2280/api
  • :actions — the action names a player holds at random. Required
  • :topic — the channel's topic prefix, default "world"
  • :players, :watchers — how many of each to start with, default 10 and 0
  • :arenas — the ids of the arenas to use, default every arena the server lists
  • :seconds — how long the run lasts, default 30
  • :every — seconds between reports (and ramp steps), default 5
  • :ramp — sessions to add at every report, default none; they are players and watchers in the same proportion as the start
  • :audio:personal (default) for a session that takes the server's sound as a browser would, :off for one that asks for none
  • :password — the sessions' password, default load test
  • :report — a function given each report, default IO.puts/1
  • :out — a file every report and the summary are appended to as JSON, one object a line, as the run goes, so a run cut short still leaves its record. Default none

Returns the summary: sessions, joined, refused, refusals (reasons with their counts), fps (mean, min over the whole run), gap_ms (max), join_ms (mean, max), server (the last arenas and node stats, hz and steps_per_second), degraded (%{sessions, why} for the first degraded report, else nil), capacity and stopped (:time, :degraded or :asked).

Summary

Functions

Why an interval counts as degraded, as run/1 judges it: [] when it does not.

Run a load test; see the module documentation for the options and the summary.

End the run in progress after the interval it is measuring; nothing when none is running.

Types

summary()

@type summary() :: %{
  sessions: non_neg_integer(),
  joined: non_neg_integer(),
  refused: non_neg_integer(),
  refusals: %{required(term()) => pos_integer()},
  fps: %{mean: float(), min: float()},
  gap_ms: %{max: non_neg_integer()},
  join_ms: %{mean: non_neg_integer(), max: non_neg_integer()},
  server: %{
    arenas: [map()],
    node: map(),
    hz: number(),
    steps_per_second: float()
  },
  degraded: %{sessions: non_neg_integer(), why: [String.t()]} | nil,
  capacity: non_neg_integer() | nil,
  stopped: :time | :degraded | :asked
}

Functions

degraded(interval)

@spec degraded(map()) :: [String.t()]

Why an interval counts as degraded, as run/1 judges it: [] when it does not.

run(opts)

@spec run(keyword()) :: summary()

Run a load test; see the module documentation for the options and the summary.

stop()

@spec stop() :: :ok

End the run in progress after the interval it is measuring; nothing when none is running.