Skip to content

Commit

Permalink
Approximate percentiles using t-digest
Browse files Browse the repository at this point in the history
  • Loading branch information
Ukaza Perdana committed Jul 18, 2022
1 parent 7e2994a commit ebce9d0
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 12 deletions.
29 changes: 24 additions & 5 deletions lib/mobius/summary.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ defmodule Mobius.Summary do
@typedoc """
Calculated summary statistics
"""
@type t() :: %{min: integer(), max: integer(), average: float(), std_dev: float()}
@type t() :: %{
min: integer(),
max: integer(),
average: float(),
std_dev: float(),
p50: float(),
p75: float(),
p95: float(),
p99: float()
}

@typedoc """
A data type to store snapshot information about a summary in order
Expand All @@ -15,7 +24,8 @@ defmodule Mobius.Summary do
max: integer(),
accumulated: integer(),
accumulated_sqrd: integer(),
reports: non_neg_integer()
reports: non_neg_integer(),
t_digest: map()
}

@doc """
Expand All @@ -28,7 +38,8 @@ defmodule Mobius.Summary do
max: metric_value,
accumulated: metric_value,
accumulated_sqrd: metric_value * metric_value,
reports: 1
reports: 1,
t_digest: TDigest.new() |> TDigest.update(metric_value)
}
end

Expand All @@ -42,7 +53,8 @@ defmodule Mobius.Summary do
max: max(summary_data.max, new_metric_value),
accumulated: summary_data.accumulated + new_metric_value,
accumulated_sqrd: summary_data.accumulated_sqrd + new_metric_value * new_metric_value,
reports: summary_data.reports + 1
reports: summary_data.reports + 1,
t_digest: TDigest.update(summary_data.t_digest, new_metric_value)
}
end

Expand All @@ -56,7 +68,11 @@ defmodule Mobius.Summary do
max: summary_data.max,
average: summary_data.accumulated / summary_data.reports,
std_dev:
std_dev(summary_data.accumulated, summary_data.accumulated_sqrd, summary_data.reports)
std_dev(summary_data.accumulated, summary_data.accumulated_sqrd, summary_data.reports),
p50: percentile(summary_data.t_digest, 0.5),
p75: percentile(summary_data.t_digest, 0.75),
p95: percentile(summary_data.t_digest, 0.95),
p99: percentile(summary_data.t_digest, 0.99)
}
end

Expand All @@ -67,4 +83,7 @@ defmodule Mobius.Summary do
((sum_sqrd - sum * sum / n) / (n - 1))
|> :math.sqrt()
end

# Approximate percentiles using t-digest
defdelegate percentile(t, p), to: TDigest
end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ defmodule Mobius.MixProject do
{:credo, "~> 1.4", only: [:dev, :test], runtime: false},
{:telemetry, "~> 0.4.3 or ~> 1.0"},
{:telemetry_metrics, "~> 0.6.0"},
{:circular_buffer, "~> 0.4.0"}
{:circular_buffer, "~> 0.4.0"},
{:t_digest, "~> 0.1.1"}
]
end

Expand Down
2 changes: 2 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"shorter_maps": {:hex, :shorter_maps, "1.2.0", "c017bf9f9555f394942970d4c9bda4958c9c7acd326e28c7e535ac2f2e8ecd9c", [:mix], [], "hexpm", "ba43e31fd25745c6193ca8af7def203e6daebbc9c489d8399a5e40cc1f44f766"},
"t_digest": {:hex, :t_digest, "0.1.1", "3e9d72eacb70ea270c6b09364497359ec54199fda144c9ada09b7931d8c40220", [:mix], [{:shorter_maps, "~> 1.0", [hex: :shorter_maps, repo: "hexpm", optional: false]}], "hexpm", "2f57ade419cec604f58dd8fab9ca195bad1ed65f985867c6ceab41b0c0a267b2"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
}
25 changes: 19 additions & 6 deletions test/mobius/summary_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ defmodule Mobius.SummaryTest do
accumulated: 100,
accumulated_sqrd: 10000,
min: 100,
max: 100
max: 100,
t_digest: TDigest.new() |> TDigest.update(100)
}

assert expected_summary_data == Summary.new(100)
Expand All @@ -21,19 +22,31 @@ defmodule Mobius.SummaryTest do
accumulated: 100,
accumulated_sqrd: 10000,
min: 100,
max: 100
max: 100,
t_digest: TDigest.new() |> TDigest.update(100)
}

assert expected_summary_data == Summary.new(100)
end

test "calculate summary from summary data" do
expected_summary = %{min: 100, max: 400, average: 250, std_dev: 212.13203435596427}
expected_summary = %{
min: 10,
max: 750,
average: 382,
std_dev: 301.1016808691413,
p50: 350,
p75: 750,
p95: 750,
p99: 750
}

[first_value | tail_values] = [10, 10, 100, 200, 300, 400, 600, 700, 750, 750]

summary_data =
100
|> Summary.new()
|> Summary.update(400)
for metric_value <- tail_values, reduce: Summary.new(first_value) do
acc -> Summary.update(acc, metric_value)
end

assert expected_summary == Summary.calculate(summary_data)
end
Expand Down

0 comments on commit ebce9d0

Please sign in to comment.