Skip to content

Commit d1706dc

Browse files
committed
Merge remote-tracking branch 'origin/main-1.5' into 250910-sync-main-1.5
2 parents 7bdcccb + 88a7b58 commit d1706dc

3 files changed

Lines changed: 61 additions & 23 deletions

File tree

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* 4.0.13 (merge 1.5.19)
2+
- Handle `record_list_too_large` error returned from Kafka.
3+
Similar to `message_too_large` error, the batch is split, then dropped if single call is still too large.
4+
15
* 4.0.12 (merge 1.5.18)
26
- Partition metadata handling.
37
- Fixed an issue introduced in 4.0.7 (1.5.15) where temporarily missing partitions in the metadata response could leave a `wolff_producer` process permanently disconnected.

src/wolff_producer.erl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -614,16 +614,21 @@ note(Fmt, Args) ->
614614
-spec do_handle_kafka_ack(_, _, sent(), state()) -> state().
615615
do_handle_kafka_ack(?no_error, BaseOffset, _Sent, St) ->
616616
clear_sent_and_ack_callers(?no_error, BaseOffset, St);
617-
do_handle_kafka_ack(?message_too_large = EC, _BaseOffset, Sent, St) ->
617+
do_handle_kafka_ack(EC, _BaseOffset, Sent, St) when EC =:= ?message_too_large orelse EC =:= ?record_list_too_large ->
618618
#{topic := Topic, partition := Partition, config := Config} = St,
619619
#{q_items := Items} = Sent,
620620
#kpro_req{msg = IoData} = make_request(Items, St),
621621
Bytes = iolist_size(IoData),
622+
Hint = case EC of
623+
?message_too_large ->
624+
"Consider increasing server side topic config 'max.message.bytes'!";
625+
?record_list_too_large ->
626+
"Cnosider increasing server side topic config 'segment.bytes'!"
627+
end,
622628
case length(Items) of
623629
1 ->
624630
%% This is a single message batch, but it's still too large
625-
Note = "A single-request batch is dropped because it's too large for this topic! "
626-
"Consider increasing 'max.message.bytes' config on the server side!",
631+
Note = "A single-request batch is dropped because it's too large for this topic! " ++ Hint,
627632
log_error(Topic, Partition, EC, #{note => Note, encode_bytes => Bytes}),
628633
wolff_metrics:dropped_inc(Config, 1),
629634
clear_sent_and_ack_callers(EC, EC, St);
@@ -635,10 +640,10 @@ do_handle_kafka_ack(?message_too_large = EC, _BaseOffset, Sent, St) ->
635640
St1 = St#{config := Config#{max_batch_bytes := NewMax}},
636641
Note = note("Config max_batch_bytes=~p is too large for this topic, "
637642
"trying to split the current batch and retry. "
638-
"Will use max_batch_bytes=~p to collect future batches.",
643+
"Will use max_batch_bytes=~p to collect future batches. " ++ Hint,
639644
[Max, NewMax]),
640645
log_warn(Topic, Partition, EC, #{note => Note, calls_count => N, encode_bytes => Bytes}),
641-
resend_sent_reqs(St1, EC)
646+
resend_sent_reqs(St1, ?message_too_large)
642647
end;
643648
do_handle_kafka_ack(ErrorCode, _BaseOffset, Sent, St) ->
644649
%% Other errors, such as not_leader_for_partition

test/wolff_tests.erl

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,10 @@ test_leader_restart() ->
833833
end).
834834

835835
with_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes, TestFunc) ->
836-
ok = create_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes),
836+
with_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes, default_sgement_bytes, TestFunc).
837+
838+
with_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes, SegmentBytes, TestFunc) ->
839+
ok = create_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes, SegmentBytes),
837840
try
838841
_ = application:stop(wolff), %% ensure stopped
839842
{ok, _} = application:ensure_all_started(wolff),
@@ -842,23 +845,13 @@ with_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes, TestFunc) ->
842845
ok = delete_topic(Topic)
843846
end.
844847

845-
846-
message_too_large_test_() ->
847-
{timeout, 60,
848-
fun() -> test_message_too_large() end}.
849-
850-
test_message_too_large() ->
851-
Topic = "message-too-large-" ++ integer_to_list(abs(erlang:monotonic_time())),
852-
Partitions = 1,
853-
ReplicationFactor = 1,
854-
MaxMessageBytes = 100,
855-
with_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes, fun() ->
848+
test_batch_split_then_drop(Topic, MaxMessageBytes) ->
856849
ClientCfg = client_config(),
857850
ClientId = iolist_to_binary("client-" ++ Topic),
858851
{ok, Client} = start_client(ClientId, ?HOSTS, ClientCfg#{connection_strategy => per_partition}),
859852
TopicBin = iolist_to_binary(Topic),
860853
%% try to batch more messages than Kafka's limit,
861-
%% the producer will get message_too_large error back
854+
%% the producer will get message_too_large or record_list_too_large error back
862855
%% then it should retry sending one message at a time
863856
ProducerCfg = #{partitioner => fun(_, _) -> 0 end,
864857
max_batch_bytes => MaxMessageBytes * 3,
@@ -894,8 +887,38 @@ test_message_too_large() ->
894887
?assertEqual(message_too_large, (SendFunc([Msg(<<"0123456789">>)]))()),
895888
ok = wolff:stop_producers(Producers),
896889
ok = stop_client(Client),
897-
ok = application:stop(wolff)
898-
end).
890+
ok = application:stop(wolff).
891+
892+
%% Max message size is smaller than segment bytes to tigger record_list_too_large error.
893+
%% This is usually a bad server/topic configuration, but we need to cover it anyways.
894+
record_list_too_large_test_() ->
895+
{timeout, 60,
896+
fun() -> test_record_list_too_large() end}.
897+
898+
test_record_list_too_large() ->
899+
Topic = "record-list-too-large-" ++ integer_to_list(abs(erlang:monotonic_time())),
900+
Partitions = 1,
901+
ReplicationFactor = 1,
902+
MaxMessageBytes = 1000,
903+
SegmentBytes = 100,
904+
with_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes, SegmentBytes,
905+
fun() ->
906+
test_batch_split_then_drop(Topic, SegmentBytes)
907+
end).
908+
909+
message_too_large_test_() ->
910+
{timeout, 60,
911+
fun() -> test_message_too_large() end}.
912+
913+
test_message_too_large() ->
914+
Topic = "message-too-large-" ++ integer_to_list(abs(erlang:monotonic_time())),
915+
Partitions = 1,
916+
ReplicationFactor = 1,
917+
MaxMessageBytes = 100,
918+
with_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes,
919+
fun() ->
920+
test_batch_split_then_drop(Topic, MaxMessageBytes)
921+
end).
899922

900923
one_byte_limit_test_() ->
901924
{timeout, 10, fun one_byte_limit/0}.
@@ -1010,8 +1033,8 @@ encoded_bytes(Batch) ->
10101033
Encoded = kpro_batch:encode(2, Batch, no_compression),
10111034
iolist_size(Encoded).
10121035

1013-
create_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes) ->
1014-
Cmd = create_topic_cmd(Topic, Partitions, ReplicationFactor, MaxMessageBytes),
1036+
create_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes, SegmentBytes) ->
1037+
Cmd = create_topic_cmd(Topic, Partitions, ReplicationFactor, MaxMessageBytes, SegmentBytes),
10151038
Result = os:cmd(Cmd),
10161039
Pattern = "Created topic ",
10171040
?assert(string:str(Result, Pattern) > 0, Result),
@@ -1020,7 +1043,7 @@ create_topic(Topic, Partitions, ReplicationFactor, MaxMessageBytes) ->
10201043
delete_topic(Topic) ->
10211044
wolff_test_utils:delete_topic(Topic).
10221045

1023-
create_topic_cmd(Topic, Partitions, ReplicationFactor, MaxMessageBytes) ->
1046+
create_topic_cmd(Topic, Partitions, ReplicationFactor, MaxMessageBytes, SegmentBytes) ->
10241047
wolff_test_utils:topics_cmd_base(Topic) ++
10251048
" --create" ++
10261049
" --partitions " ++ integer_to_list(Partitions) ++
@@ -1030,6 +1053,12 @@ create_topic_cmd(Topic, Partitions, ReplicationFactor, MaxMessageBytes) ->
10301053
" --config max.message.bytes=" ++ integer_to_list(MaxMessageBytes);
10311054
false ->
10321055
""
1056+
end ++
1057+
case is_integer(SegmentBytes) of
1058+
true ->
1059+
" --config segment.bytes=" ++ integer_to_list(SegmentBytes);
1060+
false ->
1061+
""
10331062
end.
10341063

10351064
stop_kafka_2() ->

0 commit comments

Comments
 (0)