Skip to content
This repository
branch: jdm-0.14-start…
Fetching contributors…

Cannot retrieve contributors at this time

file 128 lines (110 sloc) 4.471 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
%% -------------------------------------------------------------------
%%
%% riak_core: Core Riak Application
%%
%% Copyright (c) 2007-2010 Basho Technologies, Inc. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% -------------------------------------------------------------------

%% @doc A set of sliding windows for recording N-per-second running stats.
%%
%% This keeps stats per second for the last minute.
%%
%% See git commit history for versions of this module which keep stats
%% for more than 1 minute.

-module(spiraltime).
-author('Justin Sheehy <justin@basho.com>').
-export([fresh/0,fresh/1,n/0,incr/2,incr/3,
         rep_second/1,rep_minute/1,
         test_spiraltime/0]).

%% @type moment() = integer().
%% This is a number of seconds, as produced by
%% calendar:datetime_to_gregorian_seconds(calendar:universal_time())

%% @type count() = integer().
%% The number of entries recorded in some time period.

-record(spiral, {moment :: integer(),
                 seconds :: [integer()]
                }).

n() ->
    calendar:datetime_to_gregorian_seconds(calendar:universal_time()).

%% @doc Create an empty spiral with which to begin recording entries.
%% @spec fresh() -> spiral()
fresh() ->
    fresh(n()).

%% @doc Create an empty spiral with which to begin recording entries.
%% @spec fresh(moment()) -> spiral()
fresh(Moment) ->
    #spiral{moment=Moment,
            seconds=[0 || _ <- lists:seq(1,60)]
           }.

fieldlen(#spiral.seconds) -> 60.

nextfield(#spiral.seconds) -> done.

%% @doc Produce the number of entries recorded in the last second.
%% @spec rep_second(spiral()) -> {moment(), count()}
rep_second(Spiral) ->
    {Spiral#spiral.moment, hd(Spiral#spiral.seconds)}.

%% @doc Produce the number of entries recorded in the last minute.
%% @spec rep_minute(spiral()) -> {moment(), count()}
rep_minute(Spiral) ->
    {Minute,_} = lists:split(60,Spiral#spiral.seconds),
    {Spiral#spiral.moment, lists:sum(Minute)}.

%% @doc Add N to the counter of events, as recently as possible.
%% @spec incr(count(), spiral()) -> spiral()
incr(N, Spiral) -> incr(N,n(),Spiral).

%% @doc Add N to the counter of events occurring at Moment.
%% @spec incr(count(), moment(), spiral()) -> spiral()
incr(N, Moment, Spiral) when Spiral#spiral.moment =:= Moment ->
    % common case -- updates for "now"
    Spiral#spiral{seconds=[hd(Spiral#spiral.seconds)+N|
                           tl(Spiral#spiral.seconds)]};
incr(_N, Moment, Spiral) when Spiral#spiral.moment - Moment > 60 ->
    Spiral; % updates more than a minute old are dropped! whee!
incr(N, Moment, Spiral) ->
    S1 = update_moment(Moment, Spiral),
    {Front,Back} = lists:split(S1#spiral.moment - Moment,
                               S1#spiral.seconds),
    S1#spiral{seconds=Front ++ [hd(Back)+N|tl(Back)]}.

update_moment(Moment, Spiral) when Moment =< Spiral#spiral.moment ->
    Spiral;
update_moment(Moment, Spiral) when Moment - Spiral#spiral.moment > 36288000 ->
    fresh(Moment);
update_moment(Moment, Spiral) ->
    update_moment(Moment, push(0, Spiral#spiral{
                                    moment=Spiral#spiral.moment+1},
                               #spiral.seconds)).

getfield(Spiral,Field) -> element(Field, Spiral).
setfield(Spiral,X,Field) -> setelement(Field, Spiral, X).

push(_N, Spiral, done) ->
    Spiral;
push(N, Spiral, Field) ->
    Full = [N|getfield(Spiral,Field)],
    Double = 2 * fieldlen(Field),
    case length(Full) of
        Double ->
            {Keep, _Past} = lists:split(fieldlen(Field), Full),
            push(lists:sum(Keep),setfield(Spiral,Keep,Field),nextfield(Field));
        _ ->
            setfield(Spiral,Full,Field)
    end.

test_spiraltime() ->
    Start = n(),
    S0 = fresh(Start),
    S1 = incr(17, Start, S0),
    PlusOne = Start+1,
    S2 = incr(3, PlusOne, S1),
    {PlusOne, 3} = rep_second(S2),
    {PlusOne, 20} = rep_minute(S2),
    true.
Something went wrong with that request. Please try again.