Packages
brod
2.2.16
4.5.7
4.5.6
4.5.5
4.5.4
4.5.3
4.5.2
4.5.1
4.5.0
4.4.7
4.4.6
4.4.5
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.3
4.3.2
4.3.1
4.3.0
4.2.0
4.1.1
4.1.0
4.0.0
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.5
3.16.4
3.16.3
3.16.2
3.16.1
3.16.0
3.15.6
3.15.5
3.15.4
3.15.3
3.15.1
3.15.0
3.14.0
3.13.0
3.12.0
3.11.0
3.10.0
3.9.5
3.9.3
3.9.2
3.9.1
3.9.0
3.8.1
3.8.0
3.7.11
3.7.10
3.7.9
3.7.8
3.7.7
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.2
3.6.1
3.6.0
3.5.2
3.5.1
3.5.0
3.4.0
3.3.5
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.0
3.0.0
2.5.0
2.4.1
2.4.0
2.3.7
2.3.6
2.3.5
2.3.4
2.3.3
2.3.1
2.2.16
2.2.15
2.2.14
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.12
2.1.11
2.1.10
2.1.8
2.1.7
2.1.4
2.1.2
2.0.0
Apache Kafka Erlang client library
Current section
Files
Jump to
Current section
Files
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([ bytes/1
, 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
, fetch_offsets/5
, kafka_message/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).
%% @doc Request (sync) for topic-partition offsets.
-spec fetch_offsets(pid(), topic(), partition(),
offset_time(), pos_integer()) -> {ok, [offset()]}.
fetch_offsets(SocketPid, Topic, Partition, TimeOrSemanticOffset, NrOfOffsets) ->
Request = offset_request(Topic, Partition, TimeOrSemanticOffset, NrOfOffsets),
{ok, Response} = brod_sock:request_sync(SocketPid, Request, 5000),
#kpro_OffsetResponse{topicOffsets_L = [TopicOffsets]} = Response,
#kpro_TopicOffsets{partitionOffsets_L = [PartitionOffsets]} = TopicOffsets,
#kpro_PartitionOffsets{offset_L = Offsets} = PartitionOffsets,
{ok, Offsets}.
%% @doc Convert a `kpro_Message' to a `kafka_message'.
-spec kafka_message(#kpro_Message{}) -> #kafka_message{};
(?incomplete_message) -> ?incomplete_message.
kafka_message(#kpro_Message{ offset = Offset
, magicByte = MagicByte
, attributes = Attributes
, key = MaybeKey
, value = Value
, crc = Crc
}) ->
Key = case MaybeKey of
?undef -> <<>>;
_ -> MaybeKey
end,
#kafka_message{ offset = Offset
, magic_byte = MagicByte
, attributes = Attributes
, key = Key
, value = Value
, crc = Crc
};
kafka_message(?incomplete_message) ->
?incomplete_message.
%%%_* Internal Functions =======================================================
%% @private Make a 'OffsetRequest' request message for fetching offsets.
%% In kafka protocol, -2 and -1 are semantic 'time' to request for
%% 'earliest' and 'latest' offsets.
%% In brod implementation, -2, -1, 'earliest' and 'latest'
%% are semantic 'offset', this is why often a variable named
%% Offset is used as the Time argument.
%% @end
-spec offset_request(topic(), partition(),
offset_time(), pos_integer()) -> kpro_OffsetRequest().
offset_request(Topic, Partition, TimeOrSemanticOffset, MaxOffsets) ->
Time = ensure_integer_offset_time(TimeOrSemanticOffset),
kpro:offset_request(Topic, Partition, Time, MaxOffsets).
ensure_integer_offset_time(?OFFSET_EARLIEST) -> -2;
ensure_integer_offset_time(?OFFSET_LATEST) -> -1;
ensure_integer_offset_time(T) when is_integer(T) -> T.
-spec do_find_leader_in_metadata(kpro_MetadataResponse(),
topic(), partition()) -> endpoint().
do_find_leader_in_metadata(Metadata, Topic, Partition) ->
#kpro_MetadataResponse{ broker_L = Brokers
, topicMetadata_L = [TopicMetadata]
} = Metadata,
#kpro_TopicMetadata{ errorCode = TopicEC
, topicName = RealTopic
, partitionMetadata_L = Partitions
} = TopicMetadata,
RealTopic /= Topic andalso erlang:throw(?EC_UNKNOWN_TOPIC_OR_PARTITION),
kpro_ErrorCode:is_error(TopicEC) andalso erlang:throw(TopicEC),
Id = case lists:keyfind(Partition,
#kpro_PartitionMetadata.partition, Partitions) of
#kpro_PartitionMetadata{leader = Leader} when Leader >= 0 ->
Leader;
#kpro_PartitionMetadata{} ->
erlang:throw(?EC_LEADER_NOT_AVAILABLE);
false ->
erlang:throw(?EC_UNKNOWN_TOPIC_OR_PARTITION)
end,
Broker = lists:keyfind(Id, #kpro_Broker.nodeId, Brokers),
Host = Broker#kpro_Broker.host,
Port = Broker#kpro_Broker.port,
{binary_to_list(Host), Port}.
-define(IS_BYTE(I), (I>=0 andalso I<256)).
-spec bytes(key() | value() | kv_list()) -> non_neg_integer().
bytes([]) -> 0;
bytes(undefined) -> 0;
bytes(I) when ?IS_BYTE(I) -> 1;
bytes(B) when is_binary(B) -> erlang:size(B);
bytes({K, V}) -> bytes(K) + bytes(V);
bytes([H | T]) -> bytes(H) + bytes(T).
%%%_* Tests ====================================================================
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif. % TEST
%%%_* Emacs ====================================================================
%%% Local Variables:
%%% allout-layout: t
%%% erlang-indent-level: 2