Packages

Fault-tolerant dataflow DSL

Current section

Files

Jump to
kflow src framework kflow_gen_demux.erl
Raw

src/framework/kflow_gen_demux.erl

%%%===================================================================
%%% @copyright 2019 Klarna Bank AB (publ)
%%%
%%% @doc This stateless stream processing node splits stream of
%%% messages into multiple sub-streams according to an output value of
%%% a callback.
%%%
%%% == Example ==
%%% ```
%%% -module(my_demux).
%%%
%%% -behavior(kflow_gen_demux).
%%%
%%% -export([init/1, demux/3, terminate/1]).
%%%
%%% init(Config) ->
%%% State = do_init(Config),
%%% State.
%%%
%%% demux(Offset, Message, State) ->
%%% maps:get(key, Message, default).
%%%
%%% terminate(State) ->
%%% do_cleanup(State).
%%%
%%% '''
%%%
%%% @end
%%%===================================================================
-module(kflow_gen_demux).
-behavior(kflow_gen).
-include("kflow_int.hrl").
%% kflow_gen callbacks:
-export([ init/2
, handle_message/3
, handle_flush/2
, terminate/2
]).
-export_type([callback_fun/0]).
-callback init(_Config) -> _State.
-callback demux(kflow:offset(), _DataIn, _State) -> _Route.
-callback terminate(_State) -> _.
-optional_callbacks([init/1, terminate/1]).
-type callback_fun() :: fun((kflow:offset(), _Message) -> _Route).
-record(s1,
{ cb_module :: module()
, cb_state :: term()
}).
-record(s2,
{ function :: callback_fun()
}).
%%%===================================================================
%%% `kflow_gen' callbacks
%%%===================================================================
%% @private
init(_NodeId, {?MODULE, Fun}) when is_function(Fun) ->
is_function(Fun, 2) orelse error({badarity, Fun}),
{ok, #s2{ function = Fun
}};
init(_NodeId, {CbModule, CbConfig}) ->
CbState = kflow_lib:optional_callback(CbModule, init, [CbConfig], CbConfig),
{ok, #s1{ cb_module = CbModule
, cb_state = CbState
}}.
%% @private
handle_message(Msg = #kflow_msg{hidden = true}, State, _) ->
%% Don't execute callback for a hidden message, simply pass it
%% downstream:
{ok, [Msg], State};
handle_message(Msg0, State, _) ->
#kflow_msg{ payload = Payload
, offset = Offset
, route = Route0
} = Msg0,
NewRoute = case State of
#s1{cb_module = CbModule, cb_state = CbState} ->
CbModule:demux(Offset, Payload, CbState);
#s2{function = Fun} ->
Fun(Offset, Payload)
end,
Msg = Msg0#kflow_msg{route = [NewRoute|Route0]},
{ok, [Msg], State}.
%% @private
handle_flush(State, _) ->
{ok, [], State}.
%% @private
terminate(#s1{cb_state = CbState, cb_module = CbModule}, _) ->
kflow_lib:optional_callback(CbModule, terminate, [CbState]);
terminate(#s2{}, _) ->
ok.