@@ -129,7 +129,7 @@ check_connectivity(Hosts, ConnConfig) when Hosts =/= [] ->
129129-spec check_if_topic_exists ([host ()], kpro :conn_config (), topic ()) ->
130130 ok | {error , unknown_topic_or_partition | [#{host := binary (), reason := term ()}] | any ()}.
131131check_if_topic_exists (Hosts , ConnConfig , Topic ) when Hosts =/= [] ->
132- case get_metadata (Hosts , ConnConfig , Topic ) of
132+ case get_metadata (Hosts , ConnConfig , Topic , _IsAutoCreateAllowed = false ) of
133133 {ok , {Pid , _ }} ->
134134 ok = close_connection (Pid );
135135 {error , Errors } ->
@@ -148,7 +148,10 @@ safe_call(Pid, Call) ->
148148 catch exit : Reason -> {error , Reason }
149149 end .
150150
151- % % request client to send Pid the leader connection.
151+ % % @doc request client to send Pid the leader connection.
152+ % % The caller will receive message `{leader_connection, Pid}' if the leader is successfully connected,
153+ % % otherwise `{leader_connection, {down, Reason}}'.
154+ -spec recv_leader_connection (pid (), ? NO_GROUP | producer_group (), topic (), partition (), pid (), all_partitions | pos_integer ()) -> ok .
152155recv_leader_connection (Client , Group , Topic , Partition , Caller , MaxPartitions ) ->
153156 gen_server :cast (Client , {recv_leader_connection , Group , Topic , Partition , Caller , MaxPartitions }).
154157
@@ -288,7 +291,7 @@ ensure_metadata_conn(#{seed_hosts := Hosts, conn_config := ConnConfig, metadata_
288291 end .
289292
290293check_if_topic_exists2 (Pid , Topic , Timeout ) when is_pid (Pid ) ->
291- case do_get_metadata (Pid , Topic , Timeout ) of
294+ case do_get_metadata (Pid , Topic , Timeout , _IsAutoCreateAllowed = false ) of
292295 {ok , _ } ->
293296 ok ;
294297 {error , Reason } ->
@@ -394,9 +397,13 @@ ensure_leader_connections(St, Group, Topic, MaxPartitions) ->
394397
395398-spec ensure_leader_connections2 (state (), producer_group (), topic (), max_partitions ()) ->
396399 {ok , state ()} | {error , term ()}.
397- ensure_leader_connections2 (#{metadata_conn := Pid , conn_config := ConnConfig } = St , Group , Topic , MaxPartitions ) when is_pid (Pid ) ->
400+ ensure_leader_connections2 (#{metadata_conn := Pid ,
401+ conn_config := ConnConfig ,
402+ config := Config
403+ } = St , Group , Topic , MaxPartitions ) when is_pid (Pid ) ->
398404 Timeout = maps :get (request_timeout , ConnConfig , ? DEFAULT_METADATA_TIMEOUT ),
399- case do_get_metadata (Pid , Topic , Timeout ) of
405+ IsAutoCreateAllowed = maps :get (allow_auto_topic_creation , Config , false ),
406+ case do_get_metadata (Pid , Topic , Timeout , IsAutoCreateAllowed ) of
400407 {ok , {Brokers , PartitionMetaList }} ->
401408 ensure_leader_connections3 (St , Group , Topic , Pid , Brokers , PartitionMetaList , MaxPartitions );
402409 {error , _Reason } ->
@@ -406,8 +413,11 @@ ensure_leader_connections2(#{metadata_conn := Pid, conn_config := ConnConfig} =
406413 ensure_leader_connections2 (St #{metadata_conn => down }, Group , Topic , MaxPartitions )
407414 end ;
408415ensure_leader_connections2 (#{conn_config := ConnConfig ,
409- seed_hosts := SeedHosts } = St , Group , Topic , MaxPartitions ) ->
410- case get_metadata (SeedHosts , ConnConfig , Topic , []) of
416+ seed_hosts := SeedHosts ,
417+ config := Config
418+ } = St , Group , Topic , MaxPartitions ) ->
419+ IsAutoCreateAllowed = maps :get (allow_auto_topic_creation , Config , false ),
420+ case get_metadata (SeedHosts , ConnConfig , Topic , IsAutoCreateAllowed , []) of
411421 {ok , {ConnPid , {Brokers , PartitionMetaList }}} ->
412422 ensure_leader_connections3 (St , Group , Topic , ConnPid , Brokers , PartitionMetaList , MaxPartitions );
413423 {error , unknown_topic_or_partition } ->
@@ -572,22 +582,22 @@ split_config(Config) ->
572582 {ConnCfg , MyCfg } = lists :partition (Pred , maps :to_list (Config )),
573583 {maps :from_list (ConnCfg ), maps :from_list (MyCfg )}.
574584
575- -spec get_metadata ([_Host ], _ConnConfig , topic ()) ->
585+ -spec get_metadata ([_Host ], _ConnConfig , topic (), boolean () ) ->
576586 {ok , {pid (), term ()}} | {error , term ()}.
577- get_metadata (Hosts , _ConnectFun , _Topic ) when Hosts =:= [] ->
587+ get_metadata (Hosts , _ConnectFun , _Topic , _IsAutoCreateAllowed ) when Hosts =:= [] ->
578588 {error , no_hosts };
579- get_metadata (Hosts , ConnectFun , Topic ) ->
580- get_metadata (Hosts , ConnectFun , Topic , []).
589+ get_metadata (Hosts , ConnectFun , Topic , IsAutoCreateAllowed ) ->
590+ get_metadata (Hosts , ConnectFun , Topic , IsAutoCreateAllowed , []).
581591
582- -spec get_metadata ([_Host ], _ConnConfig , topic (), [Error ]) ->
592+ -spec get_metadata ([_Host ], _ConnConfig , topic (), boolean (), [Error ]) ->
583593 {ok , {pid (), term ()}} | {error , [Error ] | term ()}.
584- get_metadata ([], _ConnConfig , _Topic , Errors ) ->
594+ get_metadata ([], _ConnConfig , _Topic , _IsAutoCreateAllowed , Errors ) ->
585595 {error , Errors };
586- get_metadata ([Host | Rest ], ConnConfig , Topic , Errors ) ->
596+ get_metadata ([Host | Rest ], ConnConfig , Topic , IsAutoCreateAllowed , Errors ) ->
587597 case do_connect (Host , ConnConfig ) of
588598 {ok , Pid } ->
589599 Timeout = maps :get (request_timeout , ConnConfig , ? DEFAULT_METADATA_TIMEOUT ),
590- case do_get_metadata (Pid , Topic , Timeout ) of
600+ case do_get_metadata (Pid , Topic , Timeout , IsAutoCreateAllowed ) of
591601 {ok , Result } ->
592602 {ok , {Pid , Result }};
593603 {error , Reason } ->
@@ -596,23 +606,55 @@ get_metadata([Host | Rest], ConnConfig, Topic, Errors) ->
596606 {error , Reason }
597607 end ;
598608 {error , Reason } ->
599- get_metadata (Rest , ConnConfig , Topic , [Reason | Errors ])
609+ get_metadata (Rest , ConnConfig , Topic , IsAutoCreateAllowed , [Reason | Errors ])
600610 end .
601611
602- -spec do_get_metadata (connection (), topic (), timeout ()) ->
612+ -spec do_get_metadata (connection (), topic (), timeout (), boolean () ) ->
603613 {ok , {_Brokers , _Partitions }} | {error , term ()}.
604- do_get_metadata (Connection , Topic , Timeout ) ->
614+ do_get_metadata (Connection , Topic , Timeout , IsAutoCreateAllowed ) ->
605615 case kpro :get_api_versions (Connection ) of
606616 {ok , Vsns } ->
607617 {_ , Vsn } = maps :get (metadata , Vsns ),
608- do_get_metadata2 (Vsn , Connection , Topic , Timeout );
618+ do_get_metadata2 (Vsn , Connection , Topic , Timeout , IsAutoCreateAllowed );
609619 {error , Reason } ->
610620 {error , Reason }
611621 end .
612622
613- -spec do_get_metadata2 (_Vsn , connection (), topic (), timeout ()) -> {ok , {_ , _ }} | {error , term ()}.
614- do_get_metadata2 (Vsn , Connection , Topic , Timeout ) ->
615- Req = kpro_req_lib :metadata (Vsn , [Topic ], _IsAutoCreateAllowed = false ),
623+ -spec retry (fun (() -> {ok , term ()} | {error , term ()}), timeout ()) -> {ok , term ()} | {error , term ()}.
624+ retry (Fun , Timeout ) ->
625+ Deadline = erlang :monotonic_time (millisecond ) + Timeout ,
626+ retry_loop (Fun , Deadline ).
627+
628+ -spec retry_loop (fun (() -> {ok , term ()} | {error , term ()}), integer ()) -> {ok , term ()} | {error , term ()}.
629+ retry_loop (Fun , Deadline ) ->
630+ case Fun () of
631+ {ok , Result } ->
632+ {ok , Result };
633+ {error , R } when R =:= unknown_topic_or_partition orelse R =:= leader_not_available ->
634+ CurrentTime = erlang :monotonic_time (millisecond ),
635+ case CurrentTime >= Deadline of
636+ true ->
637+ {error , timeout };
638+ false ->
639+ % % Sleep for a short interval before retrying
640+ RemainingTime = Deadline - CurrentTime ,
641+ SleepTime = min (1000 , RemainingTime ),
642+ timer :sleep (SleepTime ),
643+ retry_loop (Fun , Deadline )
644+ end ;
645+ {error , Reason } ->
646+ {error , Reason }
647+ end .
648+
649+ -spec do_get_metadata2 (_Vsn , connection (), topic (), timeout (), boolean ()) -> {ok , {_ , _ }} | {error , term ()}.
650+ do_get_metadata2 (Vsn , Connection , Topic , Timeout , _IsAutoCreateAllowed = true ) ->
651+ retry (fun () -> do_get_metadata3 (Vsn , Connection , Topic , Timeout , true ) end , Timeout );
652+ do_get_metadata2 (Vsn , Connection , Topic , Timeout , _IsAutoCreateAllowed = false ) ->
653+ do_get_metadata3 (Vsn , Connection , Topic , Timeout , false ).
654+
655+ -spec do_get_metadata3 (_Vsn , connection (), topic (), timeout (), boolean ()) -> {ok , {_ , _ }} | {error , term ()}.
656+ do_get_metadata3 (Vsn , Connection , Topic , Timeout , IsAutoCreateAllowed ) ->
657+ Req = kpro_req_lib :metadata (Vsn , [Topic ], IsAutoCreateAllowed ),
616658 case kpro :request_sync (Connection , Req , Timeout ) of
617659 {ok , # kpro_rsp {msg = Meta }} ->
618660 BrokersMeta = kpro :find (brokers , Meta ),
@@ -671,7 +713,7 @@ parse_broker_meta(BrokerMeta) ->
671713 Port = kpro :find (port , BrokerMeta ),
672714 {BrokerId , {Host , Port }}.
673715
674- log_warn (Msg , Report ) -> logger :warning (Report #{msg => Msg }).
716+ log_warn (Msg , Report ) -> logger :warning (Report #{msg => Msg , pid => self () }).
675717
676718do_connect (Host , ConnConfig ) ->
677719 case kpro :connect (Host , ConnConfig ) of
0 commit comments