Current section

113 Versions

Jump to

Compare versions

6 files changed
+173 additions
-9 deletions
  @@ -23,5 +23,5 @@
23 23 [{<<"kafka_protocol">>,
24 24 [{<<"app">>,<<"kafka_protocol">>},
25 25 {<<"optional">>,false},
26 - {<<"requirement">>,<<"4.2.6">>}]}]}.
27 - {<<"version">>,<<"4.4.4">>}.
26 + {<<"requirement">>,<<"4.2.7">>}]}]}.
27 + {<<"version">>,<<"4.4.5">>}.
  @@ -1,4 +1,4 @@
1 - {deps, [{kafka_protocol, "4.2.6"}]}.
1 + {deps, [{kafka_protocol, "4.2.7"}]}.
2 2 {project_plugins, [{rebar3_lint, "~> 3.2.5"}]}.
3 3 {edoc_opts, [{preprocess, true}]}.
4 4 {erl_opts, [warnings_as_errors, warn_unused_vars,warn_shadow_vars,warn_obsolete_guard,debug_info]}.
  @@ -1,6 +1,6 @@
1 1 {application,brod,
2 2 [{description,"Apache Kafka Erlang client library"},
3 - {vsn,"4.4.4"},
3 + {vsn,"4.4.5"},
4 4 {registered,[]},
5 5 {applications,[kernel,stdlib,kafka_protocol]},
6 6 {env,[]},
  @@ -623,6 +623,7 @@ do_get_metadata(FetchMetadataFor, Topics,
623 623 #state{ client_id = ClientId
624 624 , workers_tab = Ets
625 625 , config = Config
626 + , producers_sup = ProducersSup
626 627 } = State0) ->
627 628 FetchTopics = case FetchMetadataFor of
628 629 all -> all; %% in case no topic is given, get all
  @@ -633,11 +634,18 @@ do_get_metadata(FetchMetadataFor, Topics,
633 634 Request = brod_kafka_request:metadata(Conn, FetchTopics),
634 635 case request_sync(State, Request) of
635 636 {ok, #kpro_rsp{api = metadata, msg = Metadata}} ->
636 - TopicMetadataArray = kf(topics, Metadata),
637 + TopicsMetadata = kf(topics, Metadata),
638 + ok = maybe_start_partition_producer(
639 + filter_topics(TopicsMetadata),
640 + brod_producers_sup:count_started_children(ProducersSup),
641 + Topics,
642 + ProducersSup
643 + ),
644 +
637 645 UnknownTopicCacheTtl = config(unknown_topic_cache_ttl, Config,
638 646 ?DEFAULT_UNKNOWN_TOPIC_CACHE_TTL),
639 - ok = update_partitions_count_cache(Ets, TopicMetadataArray, UnknownTopicCacheTtl),
640 - ok = maybe_cache_unknown_topic_partition(Ets, Topics, TopicMetadataArray,
647 + ok = update_partitions_count_cache(Ets, TopicsMetadata, UnknownTopicCacheTtl),
648 + ok = maybe_cache_unknown_topic_partition(Ets, Topics, TopicsMetadata,
641 649 UnknownTopicCacheTtl),
642 650 {{ok, Metadata}, State};
643 651 {error, Reason} ->
  @@ -981,6 +989,67 @@ ensure_partition_workers(TopicName, State, F) ->
981 989 end
982 990 end).
983 991
992 + -spec maybe_start_partition_producer(TopicsMetadata, Producers, Topics, ProducerSup) -> ok
993 + when
994 + TopicsMetadata :: #{Key => non_neg_integer()},
995 + Producers :: #{Key => non_neg_integer()},
996 + Topics :: [topic()],
997 + ProducerSup :: pid(),
998 + Key :: topic().
999 + maybe_start_partition_producer(_TopicsMetadata, _Producers, [], _ProducerSuf) ->
1000 + ok;
1001 + maybe_start_partition_producer(TopicsMetadata, Producers, [Topic | Rest], ProducerSup) when
1002 + is_map_key(Topic, TopicsMetadata), is_map_key(Topic, Producers)
1003 + ->
1004 + OldPartitionCnt = maps:get(Topic, Producers),
1005 + NewPartitionCnt = maps:get(Topic, TopicsMetadata),
1006 +
1007 + case NewPartitionCnt > OldPartitionCnt of
1008 + true ->
1009 + do_start_partition_producer(ProducerSup, Topic, OldPartitionCnt, NewPartitionCnt);
1010 + _ ->
1011 + ok
1012 + end,
1013 +
1014 + maybe_start_partition_producer(TopicsMetadata, Producers, Rest, ProducerSup);
1015 + maybe_start_partition_producer(TopicsMetadata, Producers, [_Topic | Rest], ProducerSup) ->
1016 + maybe_start_partition_producer(TopicsMetadata, Producers, Rest, ProducerSup).
1017 +
1018 + -spec do_start_partition_producer(pid(), topic(), non_neg_integer(), non_neg_integer()) -> ok.
1019 + do_start_partition_producer(ProducerSup, Topic, OldPartitionCnt, NewPartitionCnt) ->
1020 + %% Get producer config from partition producer which partition index is 0.
1021 + case brod_producers_sup:get_producer_config(ProducerSup, Topic, 0) of
1022 + {ok, Config} ->
1023 + %% start new producers for the partitions that are not started yet
1024 + lists:foreach(
1025 + fun
1026 + (Partition) when is_integer(Partition) ->
1027 + %% start a new producer for the partition
1028 + brod_producers_sup:start_producer(ProducerSup, self(), Topic, Partition, Config),
1029 + ok;
1030 + (_) ->
1031 + ok
1032 + end,
1033 + lists:seq(OldPartitionCnt, NewPartitionCnt - 1)
1034 + );
1035 + _ ->
1036 + %% get producer config failed, do not start new producers
1037 + ok
1038 + end.
1039 +
1040 + -spec filter_topics([kpro:struct()]) -> #{topic() => non_neg_integer()}.
1041 + filter_topics(TopicsMetadataArray) ->
1042 + {_, NoErrors1} =
1043 + lists:partition(fun(#{error_code := E}) -> ?IS_ERROR(E) end, TopicsMetadataArray),
1044 +
1045 + NoErrors = [
1046 + {TopicName, erlang:length(Partitions)}
1047 + || #{name := <<TopicName/binary>>, partitions := Partitions} <- NoErrors1,
1048 + is_list(Partitions)
1049 + ],
1050 +
1051 + maps:from_list(NoErrors).
1052 +
984 1053 %% Catches exit exceptions when making gen_server:call.
985 1054 -spec safe_gen_call(pid() | atom(), Call, Timeout) -> Return
986 1055 when Call :: term(),
  @@ -27,7 +27,10 @@
27 27 , start_link/0
28 28 , find_producer/3
29 29 , start_producer/4
30 + , start_producer/5
30 31 , stop_producer/2
32 + , get_producer_config/3
33 + , count_started_children/1
31 34 ]).
32 35
33 36 -include("brod_int.hrl").
  @@ -60,6 +63,13 @@ start_producer(SupPid, ClientPid, TopicName, Config) ->
60 63 Spec = producers_sup_spec(ClientPid, TopicName, Config),
61 64 brod_supervisor3:start_child(SupPid, Spec).
62 65
66 + %% @doc Dynamically start a partition producer
67 + -spec start_producer(pid(), pid(), brod:topic(), brod:partition(), brod:producer_config()) ->
68 + {ok, pid()} | {error, any()}.
69 + start_producer(SupPid, ClientPid, Topic, Partition, Config) ->
70 + Spec = producer_spec(ClientPid, Topic, Partition, Config),
71 + brod_supervisor3:start_child(SupPid, Spec).
72 +
63 73 %% @doc Dynamically stop a per-topic supervisor
64 74 -spec stop_producer(pid(), brod:topic()) -> ok | {}.
65 75 stop_producer(SupPid, TopicName) ->
  @@ -92,6 +102,56 @@ find_producer(SupPid, Topic, Partition) ->
92 102 end
93 103 end.
94 104
105 + %% @doc Get topic producer config from producer child specification.
106 + -spec get_producer_config(pid(), brod:topic(), brod:partition()) ->
107 + {ok, brod_producer:config()} | {error, Reason} when
108 + Reason :: {producer_not_found, brod:topic()}
109 + | {not_found, brod:topic(), brod:partition()}
110 + | {producer_down, any()}.
111 + get_producer_config(SupPid, Topic, Partition) ->
112 + case brod_supervisor3:find_child(SupPid, Topic) of
113 + [] ->
114 + {error, {producer_not_found, Topic}};
115 + [PartitionsSupPid]->
116 + try
117 + case brod_supervisor3:get_childspec(PartitionsSupPid, Partition) of
118 + {ok, {_Name, {brod_producer, start_link, [_, _, _, Config]}, _Restart,
119 + _Shutdown, _Type, _Module}} when is_list(Config) ->
120 + {ok, Config};
121 + _ ->
122 + {error, {not_found, Topic, Partition}}
123 + end
124 + catch exit : {Reason, _} ->
125 + {error, {producer_down, Reason}}
126 + end
127 + end.
128 +
129 + %% @doc Count started children under a given supervisor process.
130 + -spec count_started_children(pid()) -> #{brod:topic() => non_neg_integer()}.
131 + count_started_children(SupRef) ->
132 + TopicSups = which_producers(SupRef),
133 + lists:foldl(
134 + fun
135 + ({Topic, Pid, _Type, _Mods}, Acc) when is_binary(Topic), is_pid(Pid) ->
136 + PartitionWorkers = which_producers(Pid),
137 + case length(PartitionWorkers) of
138 + 0 ->
139 + Acc;
140 + N ->
141 + Acc#{Topic => N}
142 + end;
143 + (_, Acc) ->
144 + Acc
145 + end, #{}, TopicSups).
146 +
147 + which_producers(Sup) ->
148 + try
149 + brod_supervisor3:which_children(Sup)
150 + catch
151 + _:_ ->
152 + []
153 + end.
154 +
95 155 %% @doc brod_supervisor3 callback.
96 156 init(?TOPICS_SUP) ->
97 157 {ok, {{one_for_one, 0, 1}, []}};
Loading more files…