Current section

Files

Jump to
brod src brod_utils.erl
Raw

src/brod_utils.erl

%%%
%%% Copyright (c) 2014, 2015, Klarna AB
%%%
%%% 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.
%%%
%%%=============================================================================
%%% @doc
%%% @copyright 2014, 2015 Klarna AB
%%% @end
%%%=============================================================================
-module(brod_utils).
%% Exports
-export([ find_leader_in_metadata/3
, get_metadata/1
, get_metadata/2
, is_normal_reason/1
, is_pid_alive/1
, log/3
, os_time_utc_str/0
, shutdown_pid/1
, try_connect/1
]).
-include("brod_int.hrl").
%%%_* APIs =====================================================================
%% try to connect to any of bootstrapped nodes and fetch metadata
get_metadata(Hosts) ->
get_metadata(Hosts, []).
get_metadata(Hosts, Topics) ->
{ok, Pid} = try_connect(Hosts),
Request = #kpro_MetadataRequest{topicName_L = Topics},
Response = brod_sock:request_sync(Pid, Request, 10000),
ok = brod_sock:stop(Pid),
Response.
try_connect(Hosts) ->
try_connect(Hosts, []).
try_connect([], LastError) ->
LastError;
try_connect([{Host, Port} | Hosts], _) ->
%% Do not 'start_link' to avoid unexpected 'EXIT' message.
%% Should be ok since we're using a single blocking request which
%% monitors the process anyway.
case brod_sock:start(self(), Host, Port, ?BROD_DEFAULT_CLIENT_ID, []) of
{ok, Pid} -> {ok, Pid};
Error -> try_connect(Hosts, Error)
end.
%% @doc Check terminate reason for a gen_server implementation
is_normal_reason(normal) -> true;
is_normal_reason(shutdown) -> true;
is_normal_reason({shutdown, _}) -> true;
is_normal_reason(_) -> false.
is_pid_alive(Pid) ->
is_pid(Pid) andalso is_process_alive(Pid).
shutdown_pid(Pid) ->
case is_pid_alive(Pid) of
true -> exit(Pid, shutdown);
false -> ok
end.
%% @doc Find leader broker ID for the given topic-partiton in
%% the metadata response received from socket.
%% @end
-spec find_leader_in_metadata(kpro_MetadataResponse(), topic(), partition()) ->
{ok, endpoint()} | {error, any()}.
find_leader_in_metadata(Metadata, Topic, Partition) ->
try
{ok, do_find_leader_in_metadata(Metadata, Topic, Partition)}
catch throw : Reason ->
{error, Reason}
end.
-spec os_time_utc_str() -> string().
os_time_utc_str() ->
Ts = os:timestamp(),
{{Y,M,D}, {H,Min,Sec}} = calendar:now_to_universal_time(Ts),
{_, _, Micro} = Ts,
S = io_lib:format("~4.4.0w-~2.2.0w-~2.2.0w:~2.2.0w:~2.2.0w:~2.2.0w.~6.6.0w",
[Y, M, D, H, Min, Sec, Micro]),
lists:flatten(S).
%% @doc simple wrapper around error_logger.
%% NOTE: keep making MFA calls to error_logger to
%% 1. allow logging libraries such as larger parse_transform
%% 2. be more xref friendly
%% @end
-spec log(info | warning | error, string(), [any()]) -> ok.
log(info, Fmt, Args) -> error_logger:info_msg(Fmt, Args);
log(warning, Fmt, Args) -> error_logger:warning_msg(Fmt, Args);
log(error, Fmt, Args) -> error_logger:error_msg(Fmt, Args).
%%%_* Internal Functions =======================================================
-spec do_find_leader_in_metadata(kpro_MetadataResponse(),
topic(), partition()) ->
endpoint() | no_return().
do_find_leader_in_metadata(Metadata, Topic, Partition) ->
#kpro_MetadataResponse{ broker_L = Brokers
, topicMetadata_L = [TopicMetadata]
} = Metadata,
#kpro_TopicMetadata{ errorCode = TopicEC
, partitionMetadata_L = Partitions
} = TopicMetadata,
kpro_ErrorCode:is_error(TopicEC) andalso erlang:throw(TopicEC),
#kpro_PartitionMetadata{leader = Id} =
lists:keyfind(Partition, #kpro_PartitionMetadata.partition, Partitions),
Id >= 0 orelse erlang:throw({no_leader, {Topic, Partition}}),
Broker = lists:keyfind(Id, #kpro_Broker.nodeId, Brokers),
Host = Broker#kpro_Broker.host,
Port = Broker#kpro_Broker.port,
{binary_to_list(Host), Port}.
%%%_* Tests ====================================================================
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif. % TEST
%%%_* Emacs ====================================================================
%%% Local Variables:
%%% allout-layout: t
%%% erlang-indent-level: 2