Current section

Files

Jump to
brucke src brucke_backlog.erl
Raw

src/brucke_backlog.erl

%%%
%%% Copyright (c) 2018 Klarna Bank AB (publ)
%%%
%%% Licensed 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.
%%%
%% This module manages an opaque structure for callers to:
%% add new items to backlog
%% acknowledge finished items
%% prune finished items (sucessive header items)
%% implemented as gb_tree because we need to
%% 1. lookup using key (kafka offset)
%% 2. scan items starting from smallest
-module(brucke_backlog).
-export([ new/0
, add/3
, ack/3
, prune/1
, get_producers/1
, to_list/1
]).
-export_type([backlog/0]).
-opaque backlog() :: gb_trees:tree().
-type offset() :: brod:offset().
-type ref() :: {pid(), reference()}.
-define(REF_TUPLE_POS, 2).
-spec new() -> backlog().
new() -> gb_trees:empty().
%% @doc Add new pending acks for the given upstream offset to backlog.
-spec add(offset(), [ref()], backlog()) -> backlog().
add(Offset, Refs, Backlog) ->
gb_trees:enter(Offset, Refs, Backlog).
%% @doc Delete pending reference associated to the given upstream offset.
-spec ack(offset(), reference(), backlog()) -> backlog().
ack(Offset, Ref, Backlog) ->
case gb_trees:lookup(Offset, Backlog) of
none -> Backlog;
{value, Refs0} ->
Refs = lists:keydelete(Ref, ?REF_TUPLE_POS, Refs0),
gb_trees:enter(Offset, Refs, Backlog)
end.
%% @doc Delete sucessive header items which are completed.
-spec prune(backlog()) -> {false | offset(), backlog()}.
prune(Backlog) ->
prune_loop(Backlog, false).
%% @doc Get all producer pids.
-spec get_producers(backlog()) -> [pid()].
get_producers(Backlog) ->
[Pid || Refs <- gb_trees:values(Backlog), {Pid, _Ref} <- Refs].
%% @hidden For test
-spec to_list(backlog()) -> [{offset(), [{pid(), reference()}]}].
to_list(Backlog) -> gb_trees:to_list(Backlog).
%%%_* internals ================================================================
prune_loop(Backlog, ResultOffset) ->
case gb_trees:is_empty(Backlog) of
true ->
{ResultOffset, Backlog};
false ->
case gb_trees:take_smallest(Backlog) of
{Offset, [], NewBacklog} ->
%% all downstream messages from this upstream offset are finished
prune_loop(NewBacklog, Offset);
_ ->
{ResultOffset, Backlog}
end
end.
%%%_* Emacs ====================================================================
%%% Local Variables:
%%% allout-layout: t
%%% erlang-indent-level: 2
%%% End: