Skip to content
This repository
Browse code

Remove redundant stat mode slide and spiraltime

  • Loading branch information...
commit 5ba970c013043a7a1054696e33dd6332956d55a7 1 parent 68ed502
Russell Brown authored June 27, 2012
2  ebin/riak_core.app
@@ -72,8 +72,6 @@
72 72
              riak_core_vnode_worker_pool,
73 73
              riak_core_web,
74 74
              riak_core_wm_urlmap,
75  
-             slide,
76  
-             spiraltime,
77 75
              supervisor_pre_r14b04,
78 76
              vclock
79 77
             ]},
484  src/slide.erl
... ...
@@ -1,484 +0,0 @@
1  
-%% -------------------------------------------------------------------
2  
-%%
3  
-%% riak_core: Core Riak Application
4  
-%%
5  
-%% Copyright (c) 2007-2010 Basho Technologies, Inc.  All Rights Reserved.
6  
-%%
7  
-%% This file is provided to you under the Apache License,
8  
-%% Version 2.0 (the "License"); you may not use this file
9  
-%% except in compliance with the License.  You may obtain
10  
-%% a copy of the License at
11  
-%%
12  
-%%   http://www.apache.org/licenses/LICENSE-2.0
13  
-%%
14  
-%% Unless required by applicable law or agreed to in writing,
15  
-%% software distributed under the License is distributed on an
16  
-%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  
-%% KIND, either express or implied.  See the License for the
18  
-%% specific language governing permissions and limitations
19  
-%% under the License.
20  
-%%
21  
-%% -------------------------------------------------------------------
22  
-
23  
-%% @doc Keep track of thing in a sliding time window.  The idea here
24  
-%%      is that you have some reading to take several times.
25  
-%%      Occasionally, you want to compute some aggregation of those
26  
-%%      readings for the last N seconds.
27  
-%%
28  
-%%      For example, you might read the weight of cars passing a point
29  
-%%      in the road.  You want to compute some statistics every hour.
30  
-%%      You could:
31  
-%%
32  
-%%      %% create a new slide, with an hour window
33  
-%%      T0 = slide:fresh(60*60)
34  
-%%
35  
-%%      %% update it every time a car passes
36  
-%%      T1 = slide:update(T0, Weight, slide:moment())
37  
-%%
38  
-%%      %% eventually ask for stats
39  
-%%      {NumberOfCars, TotalWeight} = slide:sum(TN, slide:moment())
40  
-%%      {NumberOfCars, AverageWeight} = slide:mean(TN, slide:moment())
41  
-%%      {NumberOfCars, {MedianWeight,
42  
-%%                      NinetyFivePercentWeight,
43  
-%%                      NinetyNinePercentWeight,
44  
-%%                      HeaviestWeight} = slide:nines(TN, slide:moment())
45  
-
46  
--module(slide).
47  
-
48  
--export([fresh/0, fresh/1, fresh/2]).
49  
--export([update/2, update/3, moment/0]).
50  
--export([sum/1, sum/2, sum/3]).
51  
--export([mean/1, mean/2, mean/3]).
52  
--export([nines/1, nines/2, nines/3]).
53  
--export([mean_and_nines/2, mean_and_nines/6]).
54  
--export([private_dir/0, sync/1]).
55  
-
56  
--include_lib("kernel/include/file.hrl").
57  
--include_lib("eunit/include/eunit.hrl").
58  
-
59  
--define(DIR, "/tmp/riak/slide-data"). % SLF TODO: need pkg-specific data dir handling
60  
--define(REC_BYTES, 12).          % 4 + (size(term_to_binary(4000000000)) = 8)
61  
-
62  
--record(slide, {
63  
-          oldest,   %% oldest timestamp here
64  
-          window,   %% window to which to trim
65  
-          trigger,  %% age at which to trigger pruning
66  
-          dir,      %% directory for data
67  
-          readings_fh, %% filehandle for current moment's readings
68  
-          readings_m %% moment associated with readings_fh
69  
-         }).
70  
-
71  
-%% @spec fresh() -> slide()
72  
-%% @equiv fresh(60)
73  
-fresh() -> fresh(60).
74  
-
75  
-%% @spec fresh(integer()) -> slide()
76  
-%% @equiv fresh(Window, Window)
77  
-fresh(Window) -> fresh(Window, Window).
78  
-
79  
-%% @spec fresh(integer(), integer()) -> slide()
80  
-%% @doc Create an empty slide for tracking Window-seconds worth of
81  
-%%      readings, and pruning those readings after Trigger seconds.
82  
-fresh(Window, Trigger) when Trigger >= Window ->
83  
-    {A,B,C} = now(),
84  
-    Dir = lists:flatten(io_lib:format("~s/~p.~p.~p", [private_dir(), A, B, C])),
85  
-    {ok, parent, Dir} = {filelib:ensure_dir(Dir), parent, Dir},
86  
-    {ok, Dir} = {file:make_dir(Dir), Dir},
87  
-    #slide{window=Window, trigger=Trigger, dir=Dir}.
88  
-
89  
-%% @spec moment() -> integer()
90  
-%% @doc Get the current time in seconds.
91  
-moment() ->
92  
-    calendar:datetime_to_gregorian_seconds(calendar:local_time()).
93  
-
94  
-%% @spec update(slide(), term()) -> slide()
95  
-%% @equiv update(S, Reading, moment())
96  
-update(S, Reading) -> update(S, Reading, moment()).
97  
-
98  
-%% @spec update(slide(), term(), integer()) -> slide()
99  
-%% @doc Store a new reading.  The current list of readings will be
100  
-%%      pruned if Moment is as new as or newer than the most recent
101  
-%%      reading stored, and more than Trigger seconds newer than the
102  
-%%      oldest reading stored.
103  
-update(S0=#slide{oldest=Oldest,dir=Dir,readings_m=RdMoment,readings_fh=FH},
104  
-       Reading0, Moment) ->
105  
-    S1 = if Moment == RdMoment ->
106  
-                 S0;
107  
-            true ->
108  
-                 catch file:close(FH),
109  
-                 File = integer_to_list(Moment rem S0#slide.window),
110  
-                 {ok, FH2} = file:open(filename:join(Dir, File),
111  
-                                       file_write_options()),
112  
-                 S0#slide{readings_m = Moment,
113  
-                          readings_fh = FH2,
114  
-                          oldest = if Oldest == undefined ->
115  
-                                           Moment;
116  
-                                      true ->
117  
-                                           Oldest
118  
-                                   end}
119  
-         end,
120  
-    Reading = if Reading0 < 4000000000 -> Reading0;
121  
-                 true                  -> 4000000000
122  
-              end,
123  
-    %% 4 bytes len header + 8 bytes ...
124  
-    Bin = pad_bin(term_to_binary(Reading), 8),
125  
-    ok = file:write(S1#slide.readings_fh, [<<8:32>>, Bin]),
126  
-    S1.
127  
-
128  
-%% @spec sum(slide()) -> {Count::integer(), Sum::integer()}
129  
-%% @doc Sum of readings from now through Window seconds ago.  Return is
130  
-%%      number of readings in the range and the sum of those readings.
131  
-sum(Slide) -> sum(Slide, moment()).
132  
-
133  
-%% @spec sum(slide(), integer()) -> {Count::integer(), Sum::integer()}
134  
-%% @doc Sum of readings from Moment through Window seconds before Moment.
135  
-%%      Return is number of readings in the range and the sum of those
136  
-%%      readings.
137  
-sum(Slide, Moment) -> sum(Slide, Moment, Slide#slide.window).
138  
-
139  
-%% @spec sum(slide(), integer(), integer()) ->
140  
-%%          {Count::integer(), Sum::integer()}
141  
-%% @doc Sum of readings from Moment through Seconds seconds before
142  
-%%      Moment.  Return is number of readings in the range and the sum
143  
-%%      of those readings.
144  
-sum(#slide{dir=Dir}, Moment, Seconds) ->
145  
-    Cutoff = Moment-Seconds,
146  
-    Names = filelib:wildcard("*", Dir),
147  
-    ToScan = [Name || Name <- Names, list_to_integer(Name) >= 0],
148  
-    Blobs = [element(2, file:read_file(filename:join(Dir, Name))) ||
149  
-                        Name <- ToScan],
150  
-    %% histo_experiment(Blobs),
151  
-    sum_blobs(Blobs, Moment, Cutoff).
152  
-
153  
-private_dir() ->
154  
-    case application:get_env(riak_core, slide_private_dir) of
155  
-        undefined ->
156  
-            Root = case application:get_env(riak_core, platform_data_dir) of
157  
-                       undefined -> ?DIR;
158  
-                       {ok, X} -> filename:join([X, "slide-data"])
159  
-                   end,
160  
-            filename:join([Root, os:getpid()]);
161  
-        {ok, Dir} ->
162  
-            Dir
163  
-    end.
164  
-
165  
-sync(_S) ->
166  
-    todo.
167  
-
168  
-mean_and_nines(Slide, Moment) ->
169  
-    mean_and_nines(Slide, Moment, 0, 5000000, 20000, down).
170  
-
171  
-mean_and_nines(#slide{dir=Dir, window = Window}, _Moment, HistMin, HistMax, HistBins, RoundingMode) ->
172  
-    Now = moment(),
173  
-    Names = filelib:wildcard("*", Dir),
174  
-    ModTime = fun(Name) ->
175  
-                      {ok, FI} = file:read_file_info(filename:join(Dir, Name)),
176  
-                      calendar:datetime_to_gregorian_seconds(FI#file_info.mtime)
177  
-              end,
178  
-    ToScan = [Name || Name <- Names,
179  
-                      Now - ModTime(Name) =< Window],
180  
-    Blobs = [element(2, file:read_file(filename:join(Dir, Name))) ||
181  
-                        Name <- ToScan],
182  
-    compute_quantiles(Blobs, HistMin, HistMax, HistBins, RoundingMode).
183  
-
184  
-compute_quantiles(Blobs, HistMin, HistMax, HistBins, RoundingMode) ->
185  
-    {H, Count} = compute_quantiles(
186  
-                   Blobs, basho_stats_histogram:new(HistMin, HistMax, HistBins), 0),
187  
-    {_Min, Mean, Max, _Var, _SDev} = basho_stats_histogram:summary_stats(H),
188  
-    P50 = basho_stats_histogram:quantile(0.50, H),
189  
-    P95 = basho_stats_histogram:quantile(0.95, H),
190  
-    P99 = basho_stats_histogram:quantile(0.99, H),
191  
-
192  
-    %% RoundingMode allows the caller to decide whether to round up or
193  
-    %% down to the nearest integer. This is useful in cases where we
194  
-    %% measure very small, but non-zero integer values where rounding
195  
-    %% down would give a zero rather than a one.
196  
-
197  
-    %% The calls to erlang:min/N exist because the histogram estimates
198  
-    %% percentiles. Depending on the sample size or distribution, it
199  
-    %% is possible that the estimated percentile is larger than the
200  
-    %% max, which is foolish. If that happens, then we ignore the
201  
-    %% estimate and use the value of max instead.
202  
-    case RoundingMode of
203  
-        up ->
204  
-            RMax = my_ceil(Max),
205  
-            {Count, my_ceil(Mean), {
206  
-                              erlang:min(my_ceil(P50), RMax),
207  
-                              erlang:min(my_ceil(P95), RMax),
208  
-                              erlang:min(my_ceil(P99), RMax),
209  
-                              erlang:min(my_ceil(Max), RMax)
210  
-                            }};
211  
-        _ -> %% 'down'
212  
-            RMax = my_trunc(Max),
213  
-            {Count, my_trunc(Mean), {
214  
-                              erlang:min(my_trunc(P50), RMax),
215  
-                              erlang:min(my_trunc(P95), RMax),
216  
-                              erlang:min(my_trunc(P99), RMax),
217  
-                              erlang:min(my_trunc(Max), RMax)
218  
-                            }}
219  
-    end.
220  
-
221  
-compute_quantiles([Blob|Blobs], H, Count) ->
222  
-    Ns = [binary_to_term(Bin) || <<_Hdr:32, Bin:8/binary>> <= Blob],
223  
-    H2 = basho_stats_histogram:update_all(Ns, H),
224  
-    compute_quantiles(Blobs, H2, Count + length(Ns));
225  
-compute_quantiles([], H, Count) ->
226  
-    {H, Count}.
227  
-
228  
-my_trunc(X) when is_atom(X) ->
229  
-    0;
230  
-my_trunc(N) ->
231  
-    trunc(N).
232  
-
233  
-my_ceil(X) when is_atom(X) ->
234  
-    0;
235  
-my_ceil(X) ->
236  
-    T = erlang:trunc(X),
237  
-    case (X - T) of
238  
-        Neg when Neg < 0 -> T;
239  
-        Pos when Pos > 0 -> T + 1;
240  
-        _ -> T
241  
-    end.
242  
-
243  
-%% @spec mean(slide()) -> {Count::integer(), Mean::number()}
244  
-%% @doc Mean of readings from now through Window seconds ago.  Return is
245  
-%%      number of readings in the range and the mean of those readings.
246  
-mean(Slide) -> mean(Slide, moment()).
247  
-
248  
-%% @spec mean(slide(), integer()) -> {Count::integer(), Mean::number()}
249  
-%% @doc Mean of readings from Moment through Window seconds before Moment.
250  
-%%      Return is number of readings in the range and the mean of those
251  
-%%      readings.
252  
-mean(Slide, Moment) -> mean(Slide, Moment, Slide#slide.window).
253  
-
254  
-%% @spec mean(slide(), integer(), integer()) ->
255  
-%%          {Count::integer(), Mean::number()}
256  
-%% @doc Mean of readings from Moment through Seconds seconds before
257  
-%%      Moment.  Return is number of readings in the range and the mean
258  
-%%      of those readings.
259  
-mean(S, Moment, Seconds) ->
260  
-    case sum(S, Moment, Seconds) of
261  
-        {0, _}       -> {0, undefined};
262  
-        {Count, Sum} -> {Count, Sum/Count}
263  
-    end.
264  
-
265  
-%% @spec nines(slide()) ->
266  
-%%         {Count::integer(), {Median::number(), NinetyFive::number(),
267  
-%%                             NinetyNine::number(), Hundred::number()}}
268  
-%% @doc Median, 95%, 99%, and 100% readings from now through Window
269  
-%%  seconds ago.  Return is number of readings in the range and the
270  
-%%  nines of those readings.
271  
-nines(Slide) -> nines(Slide, moment()).
272  
-
273  
-%% @spec nines(slide(), integer()) ->
274  
-%%         {Count::integer(), {Median::number(), NinetyFive::number(),
275  
-%%                             NinetyNine::number(), Hundred::number()}}
276  
-%% @doc Median, 95%, 99%, and 100% readings from Moment through Window
277  
-%%      seconds before Moment.  Return is number of readings in the
278  
-%%      range and the nines of those readings.
279  
-nines(Slide, Moment) -> nines(Slide, Moment, Slide#slide.window).
280  
-
281  
-%% @spec nines(slide(), integer(), integer()) ->
282  
-%%         {Count::integer(), {Median::number(), NinetyFive::number(),
283  
-%%                             NinetyNine::number(), Hundred::number()}}
284  
-%% @doc Median, 95%, 99%, and 100% readings from Moment through
285  
-%%      Seconds seconds before Moment.  Return is number of readings
286  
-%%      in the range and the nines of those readings.
287  
-nines(#slide{dir=Dir}, Moment, Seconds) ->
288  
-    _Cutoff = Moment-Seconds,
289  
-    Names = filelib:wildcard("*", Dir),
290  
-    ToScan = [Name || Name <- Names, list_to_integer(Name) >= 0],
291  
-    OutFile = filename:join(Dir, "-42"),
292  
-    Opts = [], %%[{no_files, 64}],
293  
-    ok = file_sorter:sort([filename:join(Dir, Name) || Name <- ToScan],
294  
-                          OutFile, Opts),
295  
-    {ok, FI} = file:read_file_info(OutFile),
296  
-    case FI#file_info.size of
297  
-        0 ->
298  
-            {0, {undefined, undefined, undefined, undefined}};
299  
-        Size ->
300  
-            Count = (Size div ?REC_BYTES) - 1,
301  
-            {Count,
302  
-             {read_word_at(mochinum:int_ceil(Count*0.50) * ?REC_BYTES, OutFile),
303  
-              read_word_at(mochinum:int_ceil(Count*0.95) * ?REC_BYTES, OutFile),
304  
-              read_word_at(mochinum:int_ceil(Count*0.99) * ?REC_BYTES, OutFile),
305  
-              read_word_at(Count * ?REC_BYTES, OutFile)}}
306  
-    end.
307  
-
308  
-read_word_at(Offset, File) ->
309  
-    {ok, FH} = file:open(File, [read, raw, binary]),
310  
-    {ok, Bin} = file:pread(FH, Offset + 4, ?REC_BYTES - 4), % 4 = header to skip
311  
-    binary_to_term(Bin).
312  
-
313  
-%% Using accumulator func args avoids the garbage creation by
314  
-%% lists:foldl's need to create 2-tuples to manage accumulator.
315  
-
316  
-sum_blobs(Blobs, Moment, Cutoff) ->
317  
-    sum_blobs2(Blobs, Moment, Cutoff, 0, 0).
318  
-
319  
-sum_blobs2([], _Moment, _Cutoff, TCount, TSum) ->
320  
-    {TCount, TSum};
321  
-sum_blobs2([Blob|Blobs], Moment, Cutoff, TCount, TSum) ->
322  
-    {Count, Sum} = sum_ints(
323  
-                     [binary_to_term(Bin) || <<_Hdr:32, Bin:8/binary>> <= Blob],
324  
-                     0, 0),
325  
-    sum_blobs2(Blobs, Moment, Cutoff, TCount + Count, TSum + Sum).
326  
-
327  
-%% Dunno if this is any faster/slower than lists:sum/1 + erlang:length/1.
328  
-
329  
-sum_ints([I|Is], Count, Sum) ->
330  
-    sum_ints(Is, Count + 1, Sum + I);
331  
-sum_ints([], Count, Sum) ->
332  
-    {Count, Sum}.
333  
-
334  
-pad_bin(Bin, Size) when size(Bin) == Size ->
335  
-    Bin;
336  
-pad_bin(Bin, Size) ->
337  
-    Bits = (Size - size(Bin)) * 8,
338  
-    <<Bin/binary, 0:Bits>>.
339  
-
340  
-%%
341  
-%% Test
342  
-%%
343  
-
344  
-setup_eunit_proc_dict() ->
345  
-    erlang:put({?MODULE, eunit}, true).
346  
-
347  
-file_write_options() ->
348  
-    case erlang:get({?MODULE, eunit}) of
349  
-        true ->
350  
-            [write, raw, binary];
351  
-        _ ->
352  
-            [write, raw, binary, delayed_write]
353  
-    end.
354  
-
355  
--ifdef(TEST).
356  
-
357  
-auto_prune_test() ->
358  
-    S0 = slide:fresh(10),
359  
-    S1 = slide:update(S0, 5, 3),
360  
-    S1b = idle_time_passing(S1, 4, 13),
361  
-    S2 = slide:update(S1b, 6, 14),
362  
-    S2b = idle_time_passing(S2, 15, 15),
363  
-    ?assertEqual(6, element(2, slide:sum(S2b, 15, 10))).
364  
-
365  
-sum_test() ->
366  
-    setup_eunit_proc_dict(),
367  
-    S0 = slide:fresh(10),
368  
-    ?assertEqual({0, 0}, % no points, sum = 0
369  
-                 slide:sum(S0, 9, 10)),
370  
-    S1 = slide:update(S0, 3, 1),
371  
-    ?assertEqual({1, 3}, % one point, sum = 3
372  
-                 slide:sum(S1, 9, 10)),
373  
-    S2 = slide:update(S1, 5, 5),
374  
-    ?assertEqual({2, 8}, % two points, sum = 8
375  
-                 slide:sum(S2, 9, 10)),
376  
-    S3 = slide:update(S2, 7, 5),
377  
-    ?assertEqual({3, 15}, % three points (two concurrent), sum = 15
378  
-                 slide:sum(S3, 9, 10)),
379  
-    S3b = idle_time_passing(S3, 6, 13),
380  
-    S4 = slide:update(S3b, 11, 14),
381  
-    ?assertEqual(23, % ignoring first reading, sum = 23
382  
-                 element(2, slide:sum(S4, 14, 10))),
383  
-    S4b = idle_time_passing(S4, 15, 18),
384  
-    ?assertEqual(11, % shifted window
385  
-                 element(2, slide:sum(S4b, 18, 10))),
386  
-    S4c = idle_time_passing(S4b, 19, 21),
387  
-    S5 = slide:update(S4c, 13, 22),
388  
-    ?assertEqual(24, % shifted window
389  
-                 element(2, slide:sum(S5, 22, 10))).
390  
-
391  
-idle_time_passing(Slide, StartMoment, EndMoment) ->
392  
-    lists:foldl(fun(Moment, S) -> slide:update(S, 0, Moment) end,
393  
-                Slide, lists:seq(StartMoment, EndMoment)).
394  
-
395  
-mean_test() ->
396  
-    setup_eunit_proc_dict(),
397  
-    S0 = slide:fresh(10),
398  
-    ?assertEqual({0, undefined}, % no points, no average
399  
-                 slide:mean(S0)),
400  
-    S1 = slide:update(S0, 3, 1),
401  
-    ?assertEqual({1, 3.0}, % one point, avg = 3
402  
-                 slide:mean(S1, 9, 10)),
403  
-    S2 = slide:update(S1, 5, 5),
404  
-    ?assertEqual({2, 4.0}, % two points, avg = 4
405  
-                  slide:mean(S2, 9, 10)),
406  
-    S3 = slide:update(S2, 7, 5),
407  
-    ?assertEqual({3, 5.0}, % three points (two concurrent), avg = 5
408  
-                  slide:mean(S3, 9, 10)),
409  
-    S3b = idle_time_passing(S3, 6, 13),
410  
-    S4 = slide:update(S3b, 11, 14),
411  
-    ?assertEqual(23/11, % ignoring first reading, avg =
412  
-                 element(2, slide:mean(S4, 14, 10))),
413  
-    S4b = idle_time_passing(S4, 15, 18),
414  
-    ?assertEqual(11/10, % shifted window
415  
-                 element(2, slide:mean(S4b, 18, 10))),
416  
-    S4c = idle_time_passing(S4b, 19, 21),
417  
-    S5 = slide:update(S4c, 13, 22),
418  
-    ?assertEqual(24/10, % shifted window
419  
-                 element(2, slide:mean(S5, 22, 10))).
420  
-
421  
-mean_and_nines_test() ->
422  
-    setup_eunit_proc_dict(),
423  
-    PushReadings = fun(S, Readings) ->
424  
-                           lists:foldl(
425  
-                             fun({R,T}, A) ->
426  
-                                     slide:update(A, R, T)
427  
-                             end,
428  
-                             S, Readings)
429  
-                   end,
430  
-    S0 = slide:fresh(10),
431  
-    ?assertEqual({0, {undefined, undefined, undefined, undefined}},
432  
-                 slide:nines(S0)),
433  
-    S1 = PushReadings(S0, [ {R*100, 1} || R <- lists:seq(1, 10) ]),
434  
-    %% lists:sum([X*100 || X <- lists:seq(1,10)]) / 10 -> 550
435  
-    ?assertEqual({10, 550, {500, 958, 991, 1000}},
436  
-                 slide:mean_and_nines(S1, 10)),
437  
-    S2 = PushReadings(S1, [ {R*100, 2} || R <- lists:seq(11, 20) ]),
438  
-    %% lists:sum([X*100 || X <- lists:seq(1,20)]) / 20 -> 1050
439  
-    ?assertEqual({20, 1050, {1000, 1916, 1983, 2000}},
440  
-                 slide:mean_and_nines(S2, 10)),
441  
-    S3 = PushReadings(S2, [ {R*100, 3} || R <- lists:seq(21, 100) ]),
442  
-    %% lists:sum([X*100 || X <- lists:seq(1,100)]) / 100 -> 5050
443  
-    ?assertEqual({100, 5050, {5000, 9500, 9916, 10000}},
444  
-                 slide:mean_and_nines(S3, 10)),
445  
-    S4 = idle_time_passing(S3, 4, 11),          % 8 samples
446  
-    %% lists:sum([X*100 || X <- lists:seq(11,100)]) / (90+8) -> 5096.9
447  
-    ?assertEqual({98, 5096, {5125, 9512, 9918, 10000}},
448  
-                 slide:mean_and_nines(S4, 11)).
449  
-
450  
-private_dir_test() ->
451  
-    %% Capture the initial state
452  
-    Pid = os:getpid(),
453  
-    OldSlide = application:get_env(riak_core, slide_private_dir),
454  
-    OldPlatform = application:get_env(riak_core, platform_data_dir),
455  
-
456  
-    %% When slide_private_dir is set, use that.
457  
-    application:set_env(riak_core, slide_private_dir, "foo"),
458  
-    ?assertEqual("foo", private_dir()),
459  
-
460  
-    %% When slide_private_dir is unset, but platform_data_dir is set,
461  
-    %% use a subdirectory of the platform_data_dir.
462  
-    application:unset_env(riak_core, slide_private_dir),
463  
-    application:set_env(riak_core, platform_data_dir, "./data"),
464  
-    ?assertEqual("./data/slide-data/" ++ Pid, private_dir()),
465  
-
466  
-    %% When neither slide_private_dir nor platform_data_dir is set,
467  
-    %% use the hardcoded path.
468  
-    application:unset_env(riak_core, slide_private_dir),
469  
-    application:unset_env(riak_core, platform_data_dir),
470  
-    ?assertEqual(?DIR ++ "/" ++ Pid, private_dir()),
471  
-
472  
-    %% Cleanup after ourselves
473  
-    case OldSlide of
474  
-        {ok, S} ->
475  
-            application:set_env(riak_core, slide_private_dir, S);
476  
-        _ -> ok
477  
-    end,
478  
-    case OldPlatform of
479  
-        {ok, P} ->
480  
-            application:set_env(riak_core, platform_data_dir, P);
481  
-        _ -> ok
482  
-    end.
483  
-
484  
--endif. %TEST
166  src/spiraltime.erl
... ...
@@ -1,166 +0,0 @@
1  
-%% -------------------------------------------------------------------
2  
-%%
3  
-%% riak_core: Core Riak Application
4  
-%%
5  
-%% Copyright (c) 2007-2010 Basho Technologies, Inc.  All Rights Reserved.
6  
-%%
7  
-%% This file is provided to you under the Apache License,
8  
-%% Version 2.0 (the "License"); you may not use this file
9  
-%% except in compliance with the License.  You may obtain
10  
-%% a copy of the License at
11  
-%%
12  
-%%   http://www.apache.org/licenses/LICENSE-2.0
13  
-%%
14  
-%% Unless required by applicable law or agreed to in writing,
15  
-%% software distributed under the License is distributed on an
16  
-%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  
-%% KIND, either express or implied.  See the License for the
18  
-%% specific language governing permissions and limitations
19  
-%% under the License.
20  
-%%
21  
-%% -------------------------------------------------------------------
22  
-
23  
-%% @doc A set of sliding windows for recording N-per-second running stats.
24  
-%%
25  
-%% This keeps stats per second for the last minute.
26  
-%%
27  
-%% See git commit history for versions of this module which keep stats
28  
-%% for more than 1 minute.
29  
-
30  
--module(spiraltime).
31  
--author('Justin Sheehy <justin@basho.com>').
32  
-
33  
--ifdef(TEST).
34  
--ifdef(EQC).
35  
--include_lib("eqc/include/eqc.hrl").
36  
--endif.
37  
--include_lib("eunit/include/eunit.hrl").
38  
--endif.
39  
-
40  
--export([fresh/0,fresh/1,n/0,incr/2,incr/3,
41  
-         rep_second/1,rep_minute/1,
42  
-         test_spiraltime/0]).
43  
-
44  
--export_type([spiral/0]).
45  
-
46  
-%% @type moment() = integer().
47  
-%% This is a number of seconds, as produced by
48  
-%% calendar:datetime_to_gregorian_seconds(calendar:local_time())
49  
-
50  
-%% @type count() = integer().
51  
-%% The number of entries recorded in some time period.
52  
-
53  
--record(spiral, {moment :: integer(),
54  
-                 seconds :: [integer()]
55  
-                }).
56  
-
57  
--type spiral() :: #spiral{}.
58  
-
59  
-n() ->
60  
-    calendar:datetime_to_gregorian_seconds(calendar:local_time()).
61  
-
62  
-%% @doc Create an empty spiral with which to begin recording entries.
63  
-%% @spec fresh() -> spiral()
64  
-fresh() ->
65  
-    fresh(n()).
66  
-
67  
-%% @doc Create an empty spiral with which to begin recording entries.
68  
-%% @spec fresh(moment()) -> spiral()
69  
-fresh(Moment) ->
70  
-    #spiral{moment=Moment,
71  
-            seconds=[0 || _ <- lists:seq(1,60)]
72  
-           }.
73  
-
74  
-fieldlen(#spiral.seconds) -> 60.
75  
-
76  
-nextfield(#spiral.seconds) -> done.
77  
-
78  
-%% @doc Produce the number of entries recorded in the last second.
79  
-%% @spec rep_second(spiral()) -> {moment(), count()}
80  
-rep_second(Spiral) ->
81  
-    {Spiral#spiral.moment, hd(Spiral#spiral.seconds)}.
82  
-
83  
-%% @doc Produce the number of entries recorded in the last minute.
84  
-%% @spec rep_minute(spiral()) -> {moment(), count()}
85  
-rep_minute(Spiral) ->
86  
-    {Minute,_} = lists:split(60,Spiral#spiral.seconds),
87  
-    {Spiral#spiral.moment, lists:sum(Minute)}.
88  
-
89  
-%% @doc Add N to the counter of events, as recently as possible.
90  
-%% @spec incr(count(), spiral()) -> spiral()
91  
-incr(N, Spiral) -> incr(N,n(),Spiral).
92  
-
93  
-%% @doc Add N to the counter of events occurring at Moment.
94  
-%% @spec incr(count(), moment(), spiral()) -> spiral()
95  
-incr(N, Moment, Spiral) when Spiral#spiral.moment =:= Moment ->
96  
-    % common case -- updates for "now"
97  
-    Spiral#spiral{seconds=[hd(Spiral#spiral.seconds)+N|
98  
-                           tl(Spiral#spiral.seconds)]};
99  
-incr(_N, Moment, Spiral) when Spiral#spiral.moment - Moment > 59 ->
100  
-    Spiral; % updates more than a minute old are dropped! whee!
101  
-incr(N, Moment, Spiral) ->
102  
-    S1 = update_moment(Moment, Spiral),
103  
-    {Front,Back} = lists:split(S1#spiral.moment - Moment,
104  
-                               S1#spiral.seconds),
105  
-    S1#spiral{seconds=Front ++ [hd(Back)+N|tl(Back)]}.
106  
-
107  
-update_moment(Moment, Spiral) when Moment =< Spiral#spiral.moment ->
108  
-    Spiral;
109  
-update_moment(Moment, Spiral) when Moment - Spiral#spiral.moment > 36288000 ->
110  
-    fresh(Moment);
111  
-update_moment(Moment, Spiral) ->
112  
-    update_moment(Moment, push(0, Spiral#spiral{
113  
-                                    moment=Spiral#spiral.moment+1},
114  
-                               #spiral.seconds)).
115  
-
116  
-getfield(Spiral,Field)   -> element(Field, Spiral).
117  
-setfield(Spiral,X,Field) -> setelement(Field, Spiral, X).
118  
-
119  
-push(_N, Spiral, done) ->
120  
-    Spiral;
121  
-push(N, Spiral, Field) ->
122  
-    Full = [N|getfield(Spiral,Field)],
123  
-    Double = 2 * fieldlen(Field),
124  
-    case length(Full) of
125  
-        Double ->
126  
-            {Keep, _Past} = lists:split(fieldlen(Field), Full),
127  
-            push(lists:sum(Keep),setfield(Spiral,Keep,Field),nextfield(Field));
128  
-        _ ->
129  
-            setfield(Spiral,Full,Field)
130  
-    end.
131  
-
132  
-test_spiraltime() ->
133  
-    Start = n(),
134  
-    S0 = fresh(Start),
135  
-    S1 = incr(17, Start, S0),
136  
-    PlusOne = Start+1,
137  
-    S2 = incr(3, PlusOne, S1),
138  
-    {PlusOne, 3} = rep_second(S2),
139  
-    {PlusOne, 20} = rep_minute(S2),
140  
-    %% Drops items 60 seconds or older
141  
-    S2 = incr(1, PlusOne-60, S2),
142  
-    true.
143  
-
144  
--ifdef(TEST).
145  
-
146  
-all_test() ->
147  
-    true = test_spiraltime().
148  
-
149  
--ifdef(EQC).
150  
-
151  
-prop_dontcrash() ->
152  
-    ?FORALL(Mods, list({choose(0, 65), choose(-10, 10)}),
153  
-            begin
154  
-                Start = n(),
155  
-                lists:foldl(fun({When, Amt}, Sp) ->
156  
-                                    incr(Amt, Start + When, Sp)
157  
-                            end, fresh(Start), Mods),
158  
-                true
159  
-            end).
160  
-
161  
-%% Don't run for now b/c it always times out
162  
-%% eqc_test() ->
163  
-%%     eqc:quickcheck(eqc:numtests(5*1000, prop_dontcrash())).
164  
-
165  
--endif.
166  
--endif.

0 notes on commit 5ba970c

Please sign in to comment.
Something went wrong with that request. Please try again.