diff --git a/lib/new_relic/harvest/collector/metric/harvester.ex b/lib/new_relic/harvest/collector/metric/harvester.ex index 5df73238..04ed5d1c 100644 --- a/lib/new_relic/harvest/collector/metric/harvester.ex +++ b/lib/new_relic/harvest/collector/metric/harvester.ex @@ -110,13 +110,21 @@ defmodule NewRelic.Harvest.Collector.Metric.Harvester do add(counter, @total_exclusive_time, encode(metric.total_exclusive_time)) add(counter, @min_call_time, encode(metric.min_call_time)) add(counter, @max_call_time, encode(metric.max_call_time)) - add(counter, @sum_of_squares, encode(metric.sum_of_squares)) Map.put(metrics_acc, {metric.name, metric.scope}, counter) counter -> + total_call_time = metric.total_call_time + acc_mean = decode(get(counter, @total_call_time)) / get(counter, @call_count) + add(counter, @call_count, round(metric.call_count)) - add(counter, @total_call_time, encode(metric.total_call_time)) + add(counter, @total_call_time, encode(total_call_time)) + + mean = decode(get(counter, @total_call_time)) / get(counter, @call_count) + + delta = total_call_time - acc_mean + delta2 = total_call_time - mean + add(counter, @total_exclusive_time, encode(metric.total_exclusive_time)) if metric.min_call_time < decode(get(counter, @min_call_time)), @@ -125,7 +133,7 @@ defmodule NewRelic.Harvest.Collector.Metric.Harvester do if metric.max_call_time > decode(get(counter, @max_call_time)), do: put(counter, @max_call_time, encode(metric.max_call_time)) - add(counter, @sum_of_squares, encode(metric.sum_of_squares)) + add(counter, @sum_of_squares, encode(delta * delta2)) metrics_acc end diff --git a/lib/new_relic/metric/metric.ex b/lib/new_relic/metric/metric.ex index 725436aa..c94062d5 100644 --- a/lib/new_relic/metric/metric.ex +++ b/lib/new_relic/metric/metric.ex @@ -7,6 +7,5 @@ defmodule NewRelic.Metric do total_call_time: 0, total_exclusive_time: 0, min_call_time: 0, - max_call_time: 0, - sum_of_squares: 0 + max_call_time: 0 end diff --git a/test/metric_harvester_test.exs b/test/metric_harvester_test.exs index 84cd8319..2eced5c3 100644 --- a/test/metric_harvester_test.exs +++ b/test/metric_harvester_test.exs @@ -21,7 +21,41 @@ defmodule MetricHarvesterTest do [metric] = metrics [metric_ident, metric_values] = metric assert metric_ident == %{name: "TestMetric", scope: ""} - assert metric_values == [2, 150, 0, 0, 0, 0] + assert metric_values == [2, 150, 0, 0, 0, 1_250] + + # Verify that the Harvester shuts down w/o error + Process.monitor(harvester) + Harvest.HarvestCycle.send_harvest(Collector.Metric.HarvesterSupervisor, harvester) + assert_receive {:DOWN, _ref, _, ^harvester, :shutdown}, 1000 + end + + test "Harvester - ensure correct sum of squares calculation" do + {:ok, harvester} = + DynamicSupervisor.start_child( + Collector.Metric.HarvesterSupervisor, + Collector.Metric.Harvester + ) + + values = Enum.map(1..100, &:rand.uniform/1) + + Enum.each(values, fn value -> + metric = %NewRelic.Metric{name: "TestMetric", call_count: 1, total_call_time: value} + GenServer.cast(harvester, {:report, metric}) + end) + + # Calculate the actual SST value + count = Enum.count(values) + sum = Enum.sum(values) + mean = sum / count + sst = Enum.reduce(values, 0, fn val, acc -> :math.pow(val - mean, 2) + acc end) + + # Gather the metric values + metrics = GenServer.call(harvester, :gather_harvest) + [metric] = metrics + [_, [100, _, _, _, _, harvested_sst]] = metric + + # Verify online calculation is within reasonable limits + assert abs(sst - harvested_sst) < 0.1 # Verify that the Harvester shuts down w/o error Process.monitor(harvester)