1%%%
2%%% Copyright 2011, Boundary
3%%% Copyright 2011, Opscode
4%%%
5%%% Licensed under the Apache License, Version 2.0 (the "License");
6%%% you may not use this file except in compliance with the License.
7%%% You may obtain a copy of the License at
8%%%
9%%%     http://www.apache.org/licenses/LICENSE-2.0
10%%%
11%%% Unless required by applicable law or agreed to in writing, software
12%%% distributed under the License is distributed on an "AS IS" BASIS,
13%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14%%% See the License for the specific language governing permissions and
15%%% limitations under the License.
16%%%
17
18
19%%%-------------------------------------------------------------------
20%%% File:      folsom_metrics_meter_reader.erl
21%%% @author    Seth Falcon <seth@opscode.com>
22%%% @author    joe williams <j@boundary.com>
23%%% @doc
24%%% @end
25%%%------------------------------------------------------------------
26
27-module(folsom_metrics_meter_reader).
28
29-export([new/1,
30         tick/1,
31         mark/1,
32         mark/2,
33         get_values/1,
34         get_acceleration/1
35        ]).
36
37
38-record(meter_reader, {
39          one,
40          five,
41          fifteen,
42          count = 0,
43          start_time,
44          last_count = unset
45         }).
46
47-include("folsom.hrl").
48
49new(Name) ->
50    OneMin = folsom_ewma:one_minute_ewma(),
51    FiveMin = folsom_ewma:five_minute_ewma(),
52    FifteenMin = folsom_ewma:fifteen_minute_ewma(),
53
54    ets:insert(?METER_READER_TABLE,
55               {Name, #meter_reader{one = OneMin,
56                                    five = FiveMin,
57                                    fifteen = FifteenMin,
58                                    start_time = folsom_utils:now_epoch_micro()}}).
59
60tick(Name) ->
61    #meter_reader{one = OneMin,
62                  five = FiveMin,
63                  fifteen = FifteenMin} = Meter = get_value(Name),
64
65    OneMin1 = folsom_ewma:tick(OneMin),
66    FiveMin1 = folsom_ewma:tick(FiveMin),
67    FifteenMin1 = folsom_ewma:tick(FifteenMin),
68
69    ets:insert(?METER_READER_TABLE,
70               {Name, Meter#meter_reader{one = OneMin1,
71                                         five = FiveMin1,
72                                         fifteen = FifteenMin1}}).
73
74mark(Name) ->
75    mark(Name, 1).
76
77mark(Name, Value) ->
78    % skip first reading to bootstrap last value
79    #meter_reader{count = Count,
80                  last_count = LastCount,
81                  one = OneMin,
82                  five = FiveMin,
83                  fifteen = FifteenMin} = Meter = get_value(Name),
84
85    NewMeter = case LastCount of
86                   unset ->
87                       Meter#meter_reader{last_count = Value};
88                   _ ->
89                       Delta = Value - LastCount,
90                       OneMin1 = folsom_ewma:update(OneMin, Delta),
91                       FiveMin1 = folsom_ewma:update(FiveMin, Delta),
92                       FifteenMin1 = folsom_ewma:update(FifteenMin, Delta),
93                       Meter#meter_reader{count = Count + Delta,
94                                          last_count = Value,
95                                          one = OneMin1,
96                                          five = FiveMin1,
97                                          fifteen = FifteenMin1}
98               end,
99
100    ets:insert(?METER_READER_TABLE, {Name, NewMeter}).
101
102get_values(Name) ->
103    #meter_reader{one = OneMin,
104                  five = FiveMin,
105                  fifteen = FifteenMin} = Meter = get_value(Name),
106
107    L = [
108         {one, get_rate(OneMin)},
109         {five, get_rate(FiveMin)},
110         {fifteen, get_rate(FifteenMin)},
111         {mean, get_mean_rate(Meter)},
112         {acceleration, get_acceleration(Name)}
113        ],
114
115    [ {K,V} || {K,V} <- L, V /= undefined ].
116
117get_acceleration(Name) ->
118    #meter_reader{one = OneMin,
119                  five = FiveMin,
120                  fifteen = FifteenMin} = get_value(Name),
121
122    [
123     {one_to_five, calc_acceleration(get_rate(OneMin), get_rate(FiveMin), 300)},
124     {five_to_fifteen, calc_acceleration(get_rate(FiveMin), get_rate(FifteenMin), 600)},
125     {one_to_fifteen, calc_acceleration(get_rate(OneMin), get_rate(FifteenMin), 900)}
126    ].
127
128% internal functions
129
130get_rate(EWMA) ->
131    folsom_ewma:rate(EWMA).
132
133get_mean_rate(#meter_reader{count = Count, start_time = Start}) ->
134    calc_mean_rate(Start, Count).
135
136get_value(Name) ->
137    [{_, Value}] = ets:lookup(?METER_READER_TABLE, Name),
138    Value.
139
140calc_mean_rate(_, 0) ->
141    0.0;
142calc_mean_rate(Start, Count) ->
143    Elapsed = folsom_utils:now_epoch_micro() - Start,
144    Count / Elapsed.
145
146calc_acceleration(Rate1, Rate2, Interval) ->
147    % most current velocity minus previous velocity
148    get_rate(Rate1, Rate2, Interval).
149
150get_rate(Value1, Value2, Interval) ->
151    Delta = Value1 - Value2,
152    Delta / Interval.
153