anchor.diff:      {TS, L}.
   
   
  -%% @doc Truncate a timestamp to a multiple of the given number of seconds.
  -ts_trunc(Resolution, TS) ->
  -    {MegaSecs, Secs, _} = TS,
  -    MegaSecs * 1000000 + trunc(Secs / Resolution) * Resolution.
  -
  -
   %% @doc Generate a suitable name for the per-bucket gen_server.
   server(Bucket) ->
       list_to_atom(?MODULE_STRING ++ "-" ++ Bucket).
   
   %% @doc Generate a suitable name for the Mnesia table.
  -table_name(Bucket) ->
  +table_name(Node, Bucket) ->
       list_to_atom(lists:flatten(io_lib:format("~s-~s-~s", [?MODULE_STRING,
  -                                                          node(), Bucket]))).
  +                                                          Node, Bucket]))).
  +
  +
  +%% @doc Truncate the given table to the last N records.
  +truncate(Tab, N) ->
  +    {atomic, _M} = mnesia:transaction(
  +                     fun () -> truncate(Tab, mnesia:last(Tab), N, 0) end).
  +
  +
  +
  +truncate(_Tab, '$end_of_table', N, M) ->
  +    case N of
  +        0 -> M;
  +        _ -> -N
  +    end;
  +truncate(Tab, Key, 0, M) ->
  +    NextKey = mnesia:prev(Tab, Key),
  +    ok = mnesia:delete({Tab, Key}),
  +    truncate(Tab, NextKey, 0, M + 1);
  +truncate(Tab, Key, N, 0) ->
  +    truncate(Tab, mnesia:prev(Tab, Key), N - 1, 0).
  +
  +
  +%% @doc Return the last N records starting with the given key from Tab.
  +walk(Tab, N) ->
  +    {atomic, Results} = mnesia:transaction(
  +                          fun () -> walk(Tab, mnesia:last(Tab), N, []) end),
  +    Results.
  +
  +
  +walk(_Tab, Key, N, Records) when N == 0; Key == '$end_of_table' ->
  +    Records;
  +walk(Tab, Key, N, Records) ->
  +    [Record] = mnesia:read(Tab, Key),
  +    NextKey = mnesia:prev(Tab, Key),
  +    walk(Tab, NextKey, N - 1, [Record | Records]).
  
positive.code: %%
  
  start_link(Bucket) ->
      gen_server:start_link({local, server(Bucket)}, ?MODULE, Bucket, []).
  
  
  %% @doc Get the latest samples for a given interval from the archive
  latest(minute, Bucket) ->
      mnesia:activity(transaction,
                      fun () ->
                              Tab = table_name(Bucket),
                              Key = mnesia:last(table_name(Bucket)),
                              mnesia:read(Tab, Key)
                      end).
  
  
  latest(minute, Node, Bucket) when is_atom(Node), is_list(Bucket) ->
      gen_server:call({server(Bucket), Node}, latest);
  latest(minute, Nodes, Bucket) when is_list(Bucket) ->
      gen_server:multi_call(Nodes, server(Bucket), latest);
  latest(minute, Bucket, N) ->
      gen_server:call(server(Bucket), {latest, N}).
  
  
  latest(minute, Node, Bucket, N) when is_atom(Node) ->
      gen_server:call({server(Bucket), Node}, {latest, N});
  latest(minute, Nodes, Bucket, N) ->
      gen_server:multi_call(Nodes, server(Bucket), {latest, N}).
  
  
  latest_all(minute, Bucket) ->
      gen_server:multi_call(server(Bucket), latest).
  
  
  latest_all(minute, Bucket, N) ->
      gen_server:multi_call(server(Bucket), {latest, N}).
  
  
  %%
  
negative.code:     list_to_tuple([stat_entry, TS | List]).
  
  
  %% @doc Truncate the table if necessary.
  maybe_truncate(#state{table=Tab, samples_since_truncate=?TRUNC_FREQ} = State) ->
      truncate(Tab, 60),
      State#state{samples_since_truncate=1};
  maybe_truncate(#state{samples_since_truncate=N} = State) when N < ?TRUNC_FREQ ->
      State#state{samples_since_truncate=N+1}.
  
  
  %% @doc Convert a stat entry to a list of values.
  