@ohos-ports/confluentinc-kafka-javascript 1.10.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +457 -0
- package/LICENSE.kafkajs +31 -0
- package/LICENSE.node-rdkafka +25 -0
- package/LICENSE.txt +20 -0
- package/README.md +133 -0
- package/binding.gyp +184 -0
- package/build/Release/confluent-kafka-javascript.node +0 -0
- package/build/deps/librdkafka++.so.1 +0 -0
- package/build/deps/librdkafka.Makefile +6 -0
- package/build/deps/librdkafka.so.1 +0 -0
- package/build/deps/librdkafka.target.mk +55 -0
- package/deps/librdkafka/.clang-format +136 -0
- package/deps/librdkafka/.clang-format-cpp +103 -0
- package/deps/librdkafka/.dir-locals.el +10 -0
- package/deps/librdkafka/.formatignore +35 -0
- package/deps/librdkafka/.gdbmacros +19 -0
- package/deps/librdkafka/CHANGELOG.md +2477 -0
- package/deps/librdkafka/CMakeLists.txt +291 -0
- package/deps/librdkafka/CODE_OF_CONDUCT.md +46 -0
- package/deps/librdkafka/CONFIGURATION.md +212 -0
- package/deps/librdkafka/CONTRIBUTING.md +431 -0
- package/deps/librdkafka/Doxyfile +2375 -0
- package/deps/librdkafka/INTRODUCTION.md +2752 -0
- package/deps/librdkafka/LICENSE +26 -0
- package/deps/librdkafka/LICENSE.cjson +22 -0
- package/deps/librdkafka/LICENSE.crc32c +28 -0
- package/deps/librdkafka/LICENSE.fnv1a +18 -0
- package/deps/librdkafka/LICENSE.hdrhistogram +27 -0
- package/deps/librdkafka/LICENSE.lz4 +26 -0
- package/deps/librdkafka/LICENSE.murmur2 +25 -0
- package/deps/librdkafka/LICENSE.nanopb +22 -0
- package/deps/librdkafka/LICENSE.opentelemetry +203 -0
- package/deps/librdkafka/LICENSE.pycrc +23 -0
- package/deps/librdkafka/LICENSE.queue +31 -0
- package/deps/librdkafka/LICENSE.regexp +5 -0
- package/deps/librdkafka/LICENSE.snappy +36 -0
- package/deps/librdkafka/LICENSE.tinycthread +26 -0
- package/deps/librdkafka/LICENSE.wingetopt +49 -0
- package/deps/librdkafka/LICENSES.txt +625 -0
- package/deps/librdkafka/Makefile +125 -0
- package/deps/librdkafka/README.md +201 -0
- package/deps/librdkafka/README.win32 +26 -0
- package/deps/librdkafka/STATISTICS.md +624 -0
- package/deps/librdkafka/configure +214 -0
- package/deps/librdkafka/configure.self +341 -0
- package/deps/librdkafka/dev-conf.sh +123 -0
- package/deps/librdkafka/lds-gen.py +73 -0
- package/deps/librdkafka/mainpage.doxy +40 -0
- package/deps/librdkafka/mklove/Makefile.base +329 -0
- package/deps/librdkafka/mklove/modules/configure.atomics +144 -0
- package/deps/librdkafka/mklove/modules/configure.base +2484 -0
- package/deps/librdkafka/mklove/modules/configure.builtin +70 -0
- package/deps/librdkafka/mklove/modules/configure.cc +186 -0
- package/deps/librdkafka/mklove/modules/configure.cxx +8 -0
- package/deps/librdkafka/mklove/modules/configure.fileversion +65 -0
- package/deps/librdkafka/mklove/modules/configure.gitversion +29 -0
- package/deps/librdkafka/mklove/modules/configure.good_cflags +18 -0
- package/deps/librdkafka/mklove/modules/configure.host +132 -0
- package/deps/librdkafka/mklove/modules/configure.lib +49 -0
- package/deps/librdkafka/mklove/modules/configure.libcurl +99 -0
- package/deps/librdkafka/mklove/modules/configure.libsasl2 +36 -0
- package/deps/librdkafka/mklove/modules/configure.libssl +147 -0
- package/deps/librdkafka/mklove/modules/configure.libzstd +58 -0
- package/deps/librdkafka/mklove/modules/configure.parseversion +95 -0
- package/deps/librdkafka/mklove/modules/configure.pic +16 -0
- package/deps/librdkafka/mklove/modules/configure.socket +20 -0
- package/deps/librdkafka/mklove/modules/configure.zlib +61 -0
- package/deps/librdkafka/mklove/modules/patches/README.md +8 -0
- package/deps/librdkafka/mklove/modules/patches/libcurl.0000-no-runtime-linking-check.patch +11 -0
- package/deps/librdkafka/mklove/modules/patches/libssl.0000-osx-rand-include-fix-OpenSSL-PR16409.patch +56 -0
- package/deps/librdkafka/service.yml +182 -0
- package/deps/librdkafka/src/CMakeLists.txt +379 -0
- package/deps/librdkafka/src/Makefile +105 -0
- package/deps/librdkafka/src/README.lz4.md +30 -0
- package/deps/librdkafka/src/cJSON.c +3191 -0
- package/deps/librdkafka/src/cJSON.h +306 -0
- package/deps/librdkafka/src/crc32c.c +430 -0
- package/deps/librdkafka/src/crc32c.h +38 -0
- package/deps/librdkafka/src/generate_proto.sh +66 -0
- package/deps/librdkafka/src/librdkafka_cgrp_synch.png +0 -0
- package/deps/librdkafka/src/lz4.c +2727 -0
- package/deps/librdkafka/src/lz4.h +842 -0
- package/deps/librdkafka/src/lz4frame.c +2078 -0
- package/deps/librdkafka/src/lz4frame.h +692 -0
- package/deps/librdkafka/src/lz4frame_static.h +47 -0
- package/deps/librdkafka/src/lz4hc.c +1631 -0
- package/deps/librdkafka/src/lz4hc.h +413 -0
- package/deps/librdkafka/src/nanopb/pb.h +917 -0
- package/deps/librdkafka/src/nanopb/pb_common.c +388 -0
- package/deps/librdkafka/src/nanopb/pb_common.h +49 -0
- package/deps/librdkafka/src/nanopb/pb_decode.c +1727 -0
- package/deps/librdkafka/src/nanopb/pb_decode.h +193 -0
- package/deps/librdkafka/src/nanopb/pb_encode.c +1000 -0
- package/deps/librdkafka/src/nanopb/pb_encode.h +185 -0
- package/deps/librdkafka/src/opentelemetry/common.pb.c +32 -0
- package/deps/librdkafka/src/opentelemetry/common.pb.h +170 -0
- package/deps/librdkafka/src/opentelemetry/metrics.options +2 -0
- package/deps/librdkafka/src/opentelemetry/metrics.pb.c +67 -0
- package/deps/librdkafka/src/opentelemetry/metrics.pb.h +966 -0
- package/deps/librdkafka/src/opentelemetry/resource.pb.c +12 -0
- package/deps/librdkafka/src/opentelemetry/resource.pb.h +58 -0
- package/deps/librdkafka/src/queue.h +850 -0
- package/deps/librdkafka/src/rd.h +592 -0
- package/deps/librdkafka/src/rdaddr.c +255 -0
- package/deps/librdkafka/src/rdaddr.h +202 -0
- package/deps/librdkafka/src/rdatomic.h +262 -0
- package/deps/librdkafka/src/rdavg.h +260 -0
- package/deps/librdkafka/src/rdavl.c +210 -0
- package/deps/librdkafka/src/rdavl.h +250 -0
- package/deps/librdkafka/src/rdbase64.c +200 -0
- package/deps/librdkafka/src/rdbase64.h +43 -0
- package/deps/librdkafka/src/rdbuf.c +1884 -0
- package/deps/librdkafka/src/rdbuf.h +375 -0
- package/deps/librdkafka/src/rdcrc32.c +114 -0
- package/deps/librdkafka/src/rdcrc32.h +170 -0
- package/deps/librdkafka/src/rddl.c +179 -0
- package/deps/librdkafka/src/rddl.h +43 -0
- package/deps/librdkafka/src/rdendian.h +175 -0
- package/deps/librdkafka/src/rdfloat.h +67 -0
- package/deps/librdkafka/src/rdfnv1a.c +113 -0
- package/deps/librdkafka/src/rdfnv1a.h +35 -0
- package/deps/librdkafka/src/rdgz.c +120 -0
- package/deps/librdkafka/src/rdgz.h +46 -0
- package/deps/librdkafka/src/rdhdrhistogram.c +721 -0
- package/deps/librdkafka/src/rdhdrhistogram.h +87 -0
- package/deps/librdkafka/src/rdhttp.c +831 -0
- package/deps/librdkafka/src/rdhttp.h +101 -0
- package/deps/librdkafka/src/rdinterval.h +177 -0
- package/deps/librdkafka/src/rdkafka.c +7287 -0
- package/deps/librdkafka/src/rdkafka.h +11795 -0
- package/deps/librdkafka/src/rdkafka_admin.c +10165 -0
- package/deps/librdkafka/src/rdkafka_admin.h +658 -0
- package/deps/librdkafka/src/rdkafka_assignment.c +1282 -0
- package/deps/librdkafka/src/rdkafka_assignment.h +73 -0
- package/deps/librdkafka/src/rdkafka_assignor.c +1800 -0
- package/deps/librdkafka/src/rdkafka_assignor.h +402 -0
- package/deps/librdkafka/src/rdkafka_aux.c +409 -0
- package/deps/librdkafka/src/rdkafka_aux.h +174 -0
- package/deps/librdkafka/src/rdkafka_background.c +222 -0
- package/deps/librdkafka/src/rdkafka_broker.c +6736 -0
- package/deps/librdkafka/src/rdkafka_broker.h +851 -0
- package/deps/librdkafka/src/rdkafka_buf.c +543 -0
- package/deps/librdkafka/src/rdkafka_buf.h +1525 -0
- package/deps/librdkafka/src/rdkafka_cert.c +576 -0
- package/deps/librdkafka/src/rdkafka_cert.h +62 -0
- package/deps/librdkafka/src/rdkafka_cgrp.c +8310 -0
- package/deps/librdkafka/src/rdkafka_cgrp.h +589 -0
- package/deps/librdkafka/src/rdkafka_conf.c +5177 -0
- package/deps/librdkafka/src/rdkafka_conf.h +746 -0
- package/deps/librdkafka/src/rdkafka_confval.h +97 -0
- package/deps/librdkafka/src/rdkafka_coord.c +623 -0
- package/deps/librdkafka/src/rdkafka_coord.h +132 -0
- package/deps/librdkafka/src/rdkafka_error.c +228 -0
- package/deps/librdkafka/src/rdkafka_error.h +80 -0
- package/deps/librdkafka/src/rdkafka_event.c +502 -0
- package/deps/librdkafka/src/rdkafka_event.h +126 -0
- package/deps/librdkafka/src/rdkafka_feature.c +898 -0
- package/deps/librdkafka/src/rdkafka_feature.h +104 -0
- package/deps/librdkafka/src/rdkafka_fetcher.c +3820 -0
- package/deps/librdkafka/src/rdkafka_fetcher.h +62 -0
- package/deps/librdkafka/src/rdkafka_header.c +220 -0
- package/deps/librdkafka/src/rdkafka_header.h +76 -0
- package/deps/librdkafka/src/rdkafka_idempotence.c +807 -0
- package/deps/librdkafka/src/rdkafka_idempotence.h +144 -0
- package/deps/librdkafka/src/rdkafka_int.h +1505 -0
- package/deps/librdkafka/src/rdkafka_interceptor.c +842 -0
- package/deps/librdkafka/src/rdkafka_interceptor.h +104 -0
- package/deps/librdkafka/src/rdkafka_lz4.c +450 -0
- package/deps/librdkafka/src/rdkafka_lz4.h +49 -0
- package/deps/librdkafka/src/rdkafka_metadata.c +2347 -0
- package/deps/librdkafka/src/rdkafka_metadata.h +350 -0
- package/deps/librdkafka/src/rdkafka_metadata_cache.c +1183 -0
- package/deps/librdkafka/src/rdkafka_mock.c +4514 -0
- package/deps/librdkafka/src/rdkafka_mock.h +867 -0
- package/deps/librdkafka/src/rdkafka_mock_cgrp.c +1877 -0
- package/deps/librdkafka/src/rdkafka_mock_handlers.c +5255 -0
- package/deps/librdkafka/src/rdkafka_mock_int.h +1005 -0
- package/deps/librdkafka/src/rdkafka_mock_sharegrp.c +1207 -0
- package/deps/librdkafka/src/rdkafka_msg.c +2619 -0
- package/deps/librdkafka/src/rdkafka_msg.h +629 -0
- package/deps/librdkafka/src/rdkafka_msgbatch.h +62 -0
- package/deps/librdkafka/src/rdkafka_msgset.h +105 -0
- package/deps/librdkafka/src/rdkafka_msgset_reader.c +2099 -0
- package/deps/librdkafka/src/rdkafka_msgset_writer.c +1474 -0
- package/deps/librdkafka/src/rdkafka_offset.c +1565 -0
- package/deps/librdkafka/src/rdkafka_offset.h +150 -0
- package/deps/librdkafka/src/rdkafka_op.c +1219 -0
- package/deps/librdkafka/src/rdkafka_op.h +1065 -0
- package/deps/librdkafka/src/rdkafka_partition.c +5109 -0
- package/deps/librdkafka/src/rdkafka_partition.h +1215 -0
- package/deps/librdkafka/src/rdkafka_pattern.c +228 -0
- package/deps/librdkafka/src/rdkafka_pattern.h +70 -0
- package/deps/librdkafka/src/rdkafka_plugin.c +213 -0
- package/deps/librdkafka/src/rdkafka_plugin.h +41 -0
- package/deps/librdkafka/src/rdkafka_proto.h +739 -0
- package/deps/librdkafka/src/rdkafka_protocol.h +131 -0
- package/deps/librdkafka/src/rdkafka_queue.c +1351 -0
- package/deps/librdkafka/src/rdkafka_queue.h +1235 -0
- package/deps/librdkafka/src/rdkafka_range_assignor.c +1748 -0
- package/deps/librdkafka/src/rdkafka_request.c +7298 -0
- package/deps/librdkafka/src/rdkafka_request.h +761 -0
- package/deps/librdkafka/src/rdkafka_roundrobin_assignor.c +123 -0
- package/deps/librdkafka/src/rdkafka_sasl.c +589 -0
- package/deps/librdkafka/src/rdkafka_sasl.h +63 -0
- package/deps/librdkafka/src/rdkafka_sasl_cyrus.c +722 -0
- package/deps/librdkafka/src/rdkafka_sasl_int.h +89 -0
- package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.c +1833 -0
- package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.h +52 -0
- package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.c +1827 -0
- package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.h +52 -0
- package/deps/librdkafka/src/rdkafka_sasl_plain.c +142 -0
- package/deps/librdkafka/src/rdkafka_sasl_scram.c +858 -0
- package/deps/librdkafka/src/rdkafka_sasl_win32.c +550 -0
- package/deps/librdkafka/src/rdkafka_share_acknowledgement.c +1445 -0
- package/deps/librdkafka/src/rdkafka_share_acknowledgement.h +373 -0
- package/deps/librdkafka/src/rdkafka_ssl.c +2235 -0
- package/deps/librdkafka/src/rdkafka_ssl.h +86 -0
- package/deps/librdkafka/src/rdkafka_sticky_assignor.c +4785 -0
- package/deps/librdkafka/src/rdkafka_subscription.c +411 -0
- package/deps/librdkafka/src/rdkafka_telemetry.c +761 -0
- package/deps/librdkafka/src/rdkafka_telemetry.h +52 -0
- package/deps/librdkafka/src/rdkafka_telemetry_decode.c +1307 -0
- package/deps/librdkafka/src/rdkafka_telemetry_decode.h +59 -0
- package/deps/librdkafka/src/rdkafka_telemetry_encode.c +1309 -0
- package/deps/librdkafka/src/rdkafka_telemetry_encode.h +459 -0
- package/deps/librdkafka/src/rdkafka_timer.c +407 -0
- package/deps/librdkafka/src/rdkafka_timer.h +117 -0
- package/deps/librdkafka/src/rdkafka_topic.c +2382 -0
- package/deps/librdkafka/src/rdkafka_topic.h +369 -0
- package/deps/librdkafka/src/rdkafka_transport.c +1309 -0
- package/deps/librdkafka/src/rdkafka_transport.h +99 -0
- package/deps/librdkafka/src/rdkafka_transport_int.h +100 -0
- package/deps/librdkafka/src/rdkafka_txnmgr.c +3256 -0
- package/deps/librdkafka/src/rdkafka_txnmgr.h +171 -0
- package/deps/librdkafka/src/rdkafka_zstd.c +226 -0
- package/deps/librdkafka/src/rdkafka_zstd.h +57 -0
- package/deps/librdkafka/src/rdlist.c +593 -0
- package/deps/librdkafka/src/rdlist.h +444 -0
- package/deps/librdkafka/src/rdlog.c +89 -0
- package/deps/librdkafka/src/rdlog.h +41 -0
- package/deps/librdkafka/src/rdmap.c +508 -0
- package/deps/librdkafka/src/rdmap.h +492 -0
- package/deps/librdkafka/src/rdmurmur2.c +167 -0
- package/deps/librdkafka/src/rdmurmur2.h +35 -0
- package/deps/librdkafka/src/rdports.c +61 -0
- package/deps/librdkafka/src/rdports.h +38 -0
- package/deps/librdkafka/src/rdposix.h +250 -0
- package/deps/librdkafka/src/rdrand.c +151 -0
- package/deps/librdkafka/src/rdrand.h +72 -0
- package/deps/librdkafka/src/rdregex.c +156 -0
- package/deps/librdkafka/src/rdregex.h +43 -0
- package/deps/librdkafka/src/rdsignal.h +57 -0
- package/deps/librdkafka/src/rdstring.c +645 -0
- package/deps/librdkafka/src/rdstring.h +98 -0
- package/deps/librdkafka/src/rdsysqueue.h +404 -0
- package/deps/librdkafka/src/rdtime.h +356 -0
- package/deps/librdkafka/src/rdtypes.h +86 -0
- package/deps/librdkafka/src/rdunittest.c +564 -0
- package/deps/librdkafka/src/rdunittest.h +232 -0
- package/deps/librdkafka/src/rdunittest_acknowledge.c +1177 -0
- package/deps/librdkafka/src/rdunittest_fetcher.c +1211 -0
- package/deps/librdkafka/src/rdunittest_msgset_errors.c +1521 -0
- package/deps/librdkafka/src/rdvarint.c +134 -0
- package/deps/librdkafka/src/rdvarint.h +165 -0
- package/deps/librdkafka/src/rdwin32.h +382 -0
- package/deps/librdkafka/src/rdxxhash.c +1030 -0
- package/deps/librdkafka/src/rdxxhash.h +328 -0
- package/deps/librdkafka/src/regexp.c +1352 -0
- package/deps/librdkafka/src/regexp.h +41 -0
- package/deps/librdkafka/src/snappy.c +1866 -0
- package/deps/librdkafka/src/snappy.h +62 -0
- package/deps/librdkafka/src/snappy_compat.h +138 -0
- package/deps/librdkafka/src/statistics_schema.json +444 -0
- package/deps/librdkafka/src/tinycthread.c +932 -0
- package/deps/librdkafka/src/tinycthread.h +503 -0
- package/deps/librdkafka/src/tinycthread_extra.c +210 -0
- package/deps/librdkafka/src/tinycthread_extra.h +218 -0
- package/deps/librdkafka/src/win32_config.h +58 -0
- package/deps/librdkafka/src-cpp/CMakeLists.txt +90 -0
- package/deps/librdkafka/src-cpp/ConfImpl.cpp +84 -0
- package/deps/librdkafka/src-cpp/ConsumerImpl.cpp +244 -0
- package/deps/librdkafka/src-cpp/HandleImpl.cpp +436 -0
- package/deps/librdkafka/src-cpp/HeadersImpl.cpp +48 -0
- package/deps/librdkafka/src-cpp/KafkaConsumerImpl.cpp +296 -0
- package/deps/librdkafka/src-cpp/Makefile +55 -0
- package/deps/librdkafka/src-cpp/MessageImpl.cpp +38 -0
- package/deps/librdkafka/src-cpp/MetadataImpl.cpp +170 -0
- package/deps/librdkafka/src-cpp/ProducerImpl.cpp +197 -0
- package/deps/librdkafka/src-cpp/QueueImpl.cpp +70 -0
- package/deps/librdkafka/src-cpp/README.md +16 -0
- package/deps/librdkafka/src-cpp/RdKafka.cpp +59 -0
- package/deps/librdkafka/src-cpp/TopicImpl.cpp +124 -0
- package/deps/librdkafka/src-cpp/TopicPartitionImpl.cpp +57 -0
- package/deps/librdkafka/src-cpp/rdkafkacpp.h +3797 -0
- package/deps/librdkafka/src-cpp/rdkafkacpp_int.h +1641 -0
- package/deps/librdkafka/tests/0000-unittests.c +72 -0
- package/deps/librdkafka/tests/0001-multiobj.c +102 -0
- package/deps/librdkafka/tests/0002-unkpart.c +244 -0
- package/deps/librdkafka/tests/0003-msgmaxsize.c +173 -0
- package/deps/librdkafka/tests/0004-conf.c +934 -0
- package/deps/librdkafka/tests/0005-order.c +133 -0
- package/deps/librdkafka/tests/0006-symbols.c +163 -0
- package/deps/librdkafka/tests/0007-autotopic.c +136 -0
- package/deps/librdkafka/tests/0008-reqacks.c +179 -0
- package/deps/librdkafka/tests/0009-mock_cluster.c +272 -0
- package/deps/librdkafka/tests/0011-produce_batch.c +753 -0
- package/deps/librdkafka/tests/0012-produce_consume.c +537 -0
- package/deps/librdkafka/tests/0013-null-msgs.c +473 -0
- package/deps/librdkafka/tests/0014-reconsume-191.c +512 -0
- package/deps/librdkafka/tests/0015-offset_seeks.c +172 -0
- package/deps/librdkafka/tests/0016-client_swname.c +181 -0
- package/deps/librdkafka/tests/0017-compression.c +140 -0
- package/deps/librdkafka/tests/0018-cgrp_term.c +444 -0
- package/deps/librdkafka/tests/0019-list_groups.c +289 -0
- package/deps/librdkafka/tests/0020-destroy_hang.c +162 -0
- package/deps/librdkafka/tests/0021-rkt_destroy.c +72 -0
- package/deps/librdkafka/tests/0022-consume_batch.c +331 -0
- package/deps/librdkafka/tests/0025-timers.c +147 -0
- package/deps/librdkafka/tests/0026-consume_pause.c +547 -0
- package/deps/librdkafka/tests/0028-long_topicnames.c +79 -0
- package/deps/librdkafka/tests/0029-assign_offset.c +202 -0
- package/deps/librdkafka/tests/0030-offset_commit.c +589 -0
- package/deps/librdkafka/tests/0031-get_offsets.c +235 -0
- package/deps/librdkafka/tests/0033-regex_subscribe.c +536 -0
- package/deps/librdkafka/tests/0034-offset_reset.c +398 -0
- package/deps/librdkafka/tests/0035-api_version.c +73 -0
- package/deps/librdkafka/tests/0036-partial_fetch.c +87 -0
- package/deps/librdkafka/tests/0037-destroy_hang_local.c +85 -0
- package/deps/librdkafka/tests/0038-performance.c +121 -0
- package/deps/librdkafka/tests/0039-event.c +284 -0
- package/deps/librdkafka/tests/0040-io_event.c +257 -0
- package/deps/librdkafka/tests/0041-fetch_max_bytes.c +97 -0
- package/deps/librdkafka/tests/0042-many_topics.c +252 -0
- package/deps/librdkafka/tests/0043-no_connection.c +77 -0
- package/deps/librdkafka/tests/0044-partition_cnt.c +94 -0
- package/deps/librdkafka/tests/0045-subscribe_update.c +1010 -0
- package/deps/librdkafka/tests/0046-rkt_cache.c +65 -0
- package/deps/librdkafka/tests/0047-partial_buf_tmout.c +98 -0
- package/deps/librdkafka/tests/0048-partitioner.c +283 -0
- package/deps/librdkafka/tests/0049-consume_conn_close.c +162 -0
- package/deps/librdkafka/tests/0050-subscribe_adds.c +145 -0
- package/deps/librdkafka/tests/0051-assign_adds.c +126 -0
- package/deps/librdkafka/tests/0052-msg_timestamps.c +238 -0
- package/deps/librdkafka/tests/0053-stats_cb.cpp +527 -0
- package/deps/librdkafka/tests/0054-offset_time.cpp +236 -0
- package/deps/librdkafka/tests/0055-producer_latency.c +539 -0
- package/deps/librdkafka/tests/0056-balanced_group_mt.c +315 -0
- package/deps/librdkafka/tests/0057-invalid_topic.cpp +112 -0
- package/deps/librdkafka/tests/0058-log.cpp +123 -0
- package/deps/librdkafka/tests/0059-bsearch.cpp +241 -0
- package/deps/librdkafka/tests/0060-op_prio.cpp +163 -0
- package/deps/librdkafka/tests/0061-consumer_lag.cpp +295 -0
- package/deps/librdkafka/tests/0062-stats_event.c +126 -0
- package/deps/librdkafka/tests/0063-clusterid.cpp +180 -0
- package/deps/librdkafka/tests/0064-interceptors.c +529 -0
- package/deps/librdkafka/tests/0065-yield.cpp +140 -0
- package/deps/librdkafka/tests/0066-plugins.cpp +143 -0
- package/deps/librdkafka/tests/0067-empty_topic.cpp +151 -0
- package/deps/librdkafka/tests/0068-produce_timeout.c +136 -0
- package/deps/librdkafka/tests/0069-consumer_add_parts.c +119 -0
- package/deps/librdkafka/tests/0070-null_empty.cpp +197 -0
- package/deps/librdkafka/tests/0072-headers_ut.c +448 -0
- package/deps/librdkafka/tests/0073-headers.c +381 -0
- package/deps/librdkafka/tests/0074-producev.c +87 -0
- package/deps/librdkafka/tests/0075-retry.c +290 -0
- package/deps/librdkafka/tests/0076-produce_retry.c +452 -0
- package/deps/librdkafka/tests/0077-compaction.c +366 -0
- package/deps/librdkafka/tests/0078-c_from_cpp.cpp +96 -0
- package/deps/librdkafka/tests/0079-fork.c +93 -0
- package/deps/librdkafka/tests/0080-admin_ut.c +3095 -0
- package/deps/librdkafka/tests/0081-admin.c +5633 -0
- package/deps/librdkafka/tests/0082-fetch_max_bytes.cpp +137 -0
- package/deps/librdkafka/tests/0083-cb_event.c +233 -0
- package/deps/librdkafka/tests/0084-destroy_flags.c +208 -0
- package/deps/librdkafka/tests/0085-headers.cpp +392 -0
- package/deps/librdkafka/tests/0086-purge.c +368 -0
- package/deps/librdkafka/tests/0088-produce_metadata_timeout.c +162 -0
- package/deps/librdkafka/tests/0089-max_poll_interval.c +511 -0
- package/deps/librdkafka/tests/0090-idempotence.c +171 -0
- package/deps/librdkafka/tests/0091-max_poll_interval_timeout.c +295 -0
- package/deps/librdkafka/tests/0092-mixed_msgver.c +103 -0
- package/deps/librdkafka/tests/0093-holb.c +200 -0
- package/deps/librdkafka/tests/0094-idempotence_msg_timeout.c +231 -0
- package/deps/librdkafka/tests/0095-all_brokers_down.cpp +122 -0
- package/deps/librdkafka/tests/0097-ssl_verify.cpp +674 -0
- package/deps/librdkafka/tests/0098-consumer-txn.cpp +1218 -0
- package/deps/librdkafka/tests/0099-commit_metadata.c +194 -0
- package/deps/librdkafka/tests/0100-thread_interceptors.cpp +195 -0
- package/deps/librdkafka/tests/0101-fetch-from-follower.cpp +446 -0
- package/deps/librdkafka/tests/0102-static_group_rebalance.c +836 -0
- package/deps/librdkafka/tests/0103-transactions.c +1383 -0
- package/deps/librdkafka/tests/0104-fetch_from_follower_mock.c +625 -0
- package/deps/librdkafka/tests/0105-transactions_mock.c +3930 -0
- package/deps/librdkafka/tests/0106-cgrp_sess_timeout.c +318 -0
- package/deps/librdkafka/tests/0107-topic_recreate.c +259 -0
- package/deps/librdkafka/tests/0109-auto_create_topics.cpp +278 -0
- package/deps/librdkafka/tests/0110-batch_size.cpp +182 -0
- package/deps/librdkafka/tests/0111-delay_create_topics.cpp +127 -0
- package/deps/librdkafka/tests/0112-assign_unknown_part.c +87 -0
- package/deps/librdkafka/tests/0113-cooperative_rebalance.cpp +3473 -0
- package/deps/librdkafka/tests/0114-sticky_partitioning.cpp +176 -0
- package/deps/librdkafka/tests/0115-producer_auth.cpp +182 -0
- package/deps/librdkafka/tests/0116-kafkaconsumer_close.cpp +216 -0
- package/deps/librdkafka/tests/0117-mock_errors.c +331 -0
- package/deps/librdkafka/tests/0118-commit_rebalance.c +154 -0
- package/deps/librdkafka/tests/0119-consumer_auth.cpp +167 -0
- package/deps/librdkafka/tests/0120-asymmetric_subscription.c +185 -0
- package/deps/librdkafka/tests/0121-clusterid.c +115 -0
- package/deps/librdkafka/tests/0122-buffer_cleaning_after_rebalance.c +227 -0
- package/deps/librdkafka/tests/0123-connections_max_idle.c +98 -0
- package/deps/librdkafka/tests/0124-openssl_invalid_engine.c +69 -0
- package/deps/librdkafka/tests/0125-immediate_flush.c +144 -0
- package/deps/librdkafka/tests/0126-oauthbearer_oidc.c +1164 -0
- package/deps/librdkafka/tests/0127-fetch_queue_backoff.cpp +165 -0
- package/deps/librdkafka/tests/0128-sasl_callback_queue.cpp +217 -0
- package/deps/librdkafka/tests/0129-fetch_aborted_msgs.c +79 -0
- package/deps/librdkafka/tests/0130-store_offsets.c +178 -0
- package/deps/librdkafka/tests/0131-connect_timeout.c +81 -0
- package/deps/librdkafka/tests/0132-strategy_ordering.c +179 -0
- package/deps/librdkafka/tests/0133-ssl_keys.c +150 -0
- package/deps/librdkafka/tests/0134-ssl_provider.c +92 -0
- package/deps/librdkafka/tests/0135-sasl_credentials.cpp +279 -0
- package/deps/librdkafka/tests/0136-resolve_cb.c +181 -0
- package/deps/librdkafka/tests/0137-barrier_batch_consume.c +752 -0
- package/deps/librdkafka/tests/0138-admin_mock.c +281 -0
- package/deps/librdkafka/tests/0139-offset_validation_mock.c +950 -0
- package/deps/librdkafka/tests/0140-commit_metadata.cpp +108 -0
- package/deps/librdkafka/tests/0142-reauthentication.c +931 -0
- package/deps/librdkafka/tests/0143-exponential_backoff_mock.c +552 -0
- package/deps/librdkafka/tests/0144-idempotence_mock.c +373 -0
- package/deps/librdkafka/tests/0145-pause_resume_mock.c +119 -0
- package/deps/librdkafka/tests/0146-metadata_mock.c +505 -0
- package/deps/librdkafka/tests/0147-consumer_group_consumer_mock.c +1210 -0
- package/deps/librdkafka/tests/0148-offset_fetch_commit_error_mock.c +569 -0
- package/deps/librdkafka/tests/0149-broker-same-host-port.c +140 -0
- package/deps/librdkafka/tests/0150-telemetry_mock.c +651 -0
- package/deps/librdkafka/tests/0151-purge-brokers.c +566 -0
- package/deps/librdkafka/tests/0152-rebootstrap.c +59 -0
- package/deps/librdkafka/tests/0153-memberid.c +128 -0
- package/deps/librdkafka/tests/0155-share_group_heartbeat_mock.c +2695 -0
- package/deps/librdkafka/tests/0156-share_consumer_fetch_mock.c +1551 -0
- package/deps/librdkafka/tests/0157-share_consumer_ack_mock.c +1788 -0
- package/deps/librdkafka/tests/0158-share_consumer_transactions_mock.c +467 -0
- package/deps/librdkafka/tests/0170-share_consumer_subscription.c +2192 -0
- package/deps/librdkafka/tests/0171-share_consumer_consume.c +2150 -0
- package/deps/librdkafka/tests/0172-share_consumer_acknowledge.c +1919 -0
- package/deps/librdkafka/tests/0173-share_consumer_commit_async.c +2735 -0
- package/deps/librdkafka/tests/0174-share_consumer_concurrency.c +1434 -0
- package/deps/librdkafka/tests/0175-share_consumer_groups.c +574 -0
- package/deps/librdkafka/tests/0176-share_consumer_commit_sync.c +2986 -0
- package/deps/librdkafka/tests/0177-share_consumer_transactions.c +1815 -0
- package/deps/librdkafka/tests/0178-share_consumer_close.c +2102 -0
- package/deps/librdkafka/tests/0179-share_consumer_destroy.c +2482 -0
- package/deps/librdkafka/tests/0180-share_consumer_config.c +1677 -0
- package/deps/librdkafka/tests/0181-share_consumer_topic_delete.c +354 -0
- package/deps/librdkafka/tests/0182-share_consumer_error_handling_mock.c +3358 -0
- package/deps/librdkafka/tests/0183-share_consumer_leader_change_mock.c +1234 -0
- package/deps/librdkafka/tests/0184-share_consumer_topic_recreate.c +1356 -0
- package/deps/librdkafka/tests/0185-share_consumer_max_poll_interval.c +557 -0
- package/deps/librdkafka/tests/0186-share_consumer_fatal_error.c +360 -0
- package/deps/librdkafka/tests/0190-share_consumer_telemetry.c +387 -0
- package/deps/librdkafka/tests/1000-unktopic.c +164 -0
- package/deps/librdkafka/tests/8000-idle.cpp +60 -0
- package/deps/librdkafka/tests/8001-fetch_from_follower_mock_manual.c +113 -0
- package/deps/librdkafka/tests/CMakeLists.txt +193 -0
- package/deps/librdkafka/tests/LibrdkafkaTestApp.py +291 -0
- package/deps/librdkafka/tests/Makefile +182 -0
- package/deps/librdkafka/tests/README.md +514 -0
- package/deps/librdkafka/tests/autotest.sh +33 -0
- package/deps/librdkafka/tests/backtrace.gdb +30 -0
- package/deps/librdkafka/tests/broker_version_tests.py +315 -0
- package/deps/librdkafka/tests/buildbox.sh +17 -0
- package/deps/librdkafka/tests/cleanup-checker-tests.sh +20 -0
- package/deps/librdkafka/tests/cluster_testing.py +203 -0
- package/deps/librdkafka/tests/delete-test-topics.sh +56 -0
- package/deps/librdkafka/tests/fixtures/oauthbearer/jwt_assertion_template.json +10 -0
- package/deps/librdkafka/tests/fixtures/ssl/Makefile +8 -0
- package/deps/librdkafka/tests/fixtures/ssl/README.md +13 -0
- package/deps/librdkafka/tests/fixtures/ssl/client.keystore.intermediate.p12 +0 -0
- package/deps/librdkafka/tests/fixtures/ssl/client.keystore.p12 +0 -0
- package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.intermediate.pem +72 -0
- package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.pem +50 -0
- package/deps/librdkafka/tests/fixtures/ssl/client2.intermediate.key +46 -0
- package/deps/librdkafka/tests/fixtures/ssl/client2.key +46 -0
- package/deps/librdkafka/tests/fixtures/ssl/create_keys.sh +168 -0
- package/deps/librdkafka/tests/fuzzers/Makefile +12 -0
- package/deps/librdkafka/tests/fuzzers/README.md +31 -0
- package/deps/librdkafka/tests/fuzzers/fuzz_regex.c +74 -0
- package/deps/librdkafka/tests/fuzzers/helpers.h +90 -0
- package/deps/librdkafka/tests/gen-ssl-certs.sh +165 -0
- package/deps/librdkafka/tests/interactive_broker_version.py +170 -0
- package/deps/librdkafka/tests/interceptor_test/CMakeLists.txt +16 -0
- package/deps/librdkafka/tests/interceptor_test/Makefile +22 -0
- package/deps/librdkafka/tests/interceptor_test/interceptor_test.c +314 -0
- package/deps/librdkafka/tests/interceptor_test/interceptor_test.h +54 -0
- package/deps/librdkafka/tests/java/IncrementalRebalanceCli.java +97 -0
- package/deps/librdkafka/tests/java/Makefile +13 -0
- package/deps/librdkafka/tests/java/Murmur2Cli.java +46 -0
- package/deps/librdkafka/tests/java/README.md +14 -0
- package/deps/librdkafka/tests/java/TransactionProducerCli.java +162 -0
- package/deps/librdkafka/tests/java/run-class.sh +11 -0
- package/deps/librdkafka/tests/librdkafka.suppressions +483 -0
- package/deps/librdkafka/tests/lz4_manual_test.sh +59 -0
- package/deps/librdkafka/tests/multi-broker-version-test.sh +50 -0
- package/deps/librdkafka/tests/parse-refcnt.sh +43 -0
- package/deps/librdkafka/tests/performance_plot.py +115 -0
- package/deps/librdkafka/tests/plugin_test/Makefile +19 -0
- package/deps/librdkafka/tests/plugin_test/plugin_test.c +58 -0
- package/deps/librdkafka/tests/requirements.txt +2 -0
- package/deps/librdkafka/tests/run-all-tests.sh +81 -0
- package/deps/librdkafka/tests/run-consumer-tests.sh +16 -0
- package/deps/librdkafka/tests/run-producer-tests.sh +16 -0
- package/deps/librdkafka/tests/run-test-batches.py +157 -0
- package/deps/librdkafka/tests/run-test.sh +141 -0
- package/deps/librdkafka/tests/rusage.c +249 -0
- package/deps/librdkafka/tests/sasl_test.py +289 -0
- package/deps/librdkafka/tests/scenarios/README.md +6 -0
- package/deps/librdkafka/tests/scenarios/ak23.json +6 -0
- package/deps/librdkafka/tests/scenarios/default.json +5 -0
- package/deps/librdkafka/tests/scenarios/noautocreate.json +5 -0
- package/deps/librdkafka/tests/share_telemetry_verify/share-otel-collector-config.yaml +28 -0
- package/deps/librdkafka/tests/sockem.c +801 -0
- package/deps/librdkafka/tests/sockem.h +85 -0
- package/deps/librdkafka/tests/sockem_ctrl.c +145 -0
- package/deps/librdkafka/tests/sockem_ctrl.h +61 -0
- package/deps/librdkafka/tests/test.c +8376 -0
- package/deps/librdkafka/tests/test.conf.example +27 -0
- package/deps/librdkafka/tests/test.h +1150 -0
- package/deps/librdkafka/tests/testcpp.cpp +131 -0
- package/deps/librdkafka/tests/testcpp.h +388 -0
- package/deps/librdkafka/tests/testshared.h +416 -0
- package/deps/librdkafka/tests/tools/README.md +4 -0
- package/deps/librdkafka/tests/tools/stats/README.md +21 -0
- package/deps/librdkafka/tests/tools/stats/filter.jq +42 -0
- package/deps/librdkafka/tests/tools/stats/graph.py +150 -0
- package/deps/librdkafka/tests/tools/stats/requirements.txt +3 -0
- package/deps/librdkafka/tests/tools/stats/to_csv.py +124 -0
- package/deps/librdkafka/tests/trivup/trivup-0.15.0.tar.gz +0 -0
- package/deps/librdkafka/tests/until-fail.sh +87 -0
- package/deps/librdkafka/tests/xxxx-assign_partition.c +122 -0
- package/deps/librdkafka/tests/xxxx-metadata.cpp +159 -0
- package/deps/librdkafka/vcpkg.json +23 -0
- package/deps/librdkafka/win32/README.md +5 -0
- package/deps/librdkafka/win32/build-package.bat +3 -0
- package/deps/librdkafka/win32/build.bat +19 -0
- package/deps/librdkafka/win32/common.vcxproj +84 -0
- package/deps/librdkafka/win32/interceptor_test/interceptor_test.vcxproj +87 -0
- package/deps/librdkafka/win32/librdkafka.autopkg.template +54 -0
- package/deps/librdkafka/win32/librdkafka.master.testing.targets +13 -0
- package/deps/librdkafka/win32/librdkafka.sln +226 -0
- package/deps/librdkafka/win32/librdkafka.vcxproj +282 -0
- package/deps/librdkafka/win32/librdkafkacpp/librdkafkacpp.vcxproj +104 -0
- package/deps/librdkafka/win32/msbuild.ps1 +58 -0
- package/deps/librdkafka/win32/openssl_engine_example/openssl_engine_example.vcxproj +132 -0
- package/deps/librdkafka/win32/package-zip.ps1 +46 -0
- package/deps/librdkafka/win32/packages/repositories.config +4 -0
- package/deps/librdkafka/win32/push-package.bat +4 -0
- package/deps/librdkafka/win32/rdkafka_complex_consumer_example_cpp/rdkafka_complex_consumer_example_cpp.vcxproj +67 -0
- package/deps/librdkafka/win32/rdkafka_example/rdkafka_example.vcxproj +97 -0
- package/deps/librdkafka/win32/rdkafka_performance/rdkafka_performance.vcxproj +97 -0
- package/deps/librdkafka/win32/setup-msys2.ps1 +47 -0
- package/deps/librdkafka/win32/setup-vcpkg.ps1 +34 -0
- package/deps/librdkafka/win32/tests/test.conf.example +25 -0
- package/deps/librdkafka/win32/tests/tests.vcxproj +278 -0
- package/deps/librdkafka/win32/win_ssl_cert_store/win_ssl_cert_store.vcxproj +132 -0
- package/deps/librdkafka/win32/wingetopt.c +564 -0
- package/deps/librdkafka/win32/wingetopt.h +101 -0
- package/deps/librdkafka/win32/wintime.h +33 -0
- package/deps/librdkafka.gyp +83 -0
- package/deps/windows-install.py +80 -0
- package/index.d.ts +3 -0
- package/lib/admin.js +732 -0
- package/lib/client.js +679 -0
- package/lib/error.js +501 -0
- package/lib/index.js +8 -0
- package/lib/kafka-consumer-stream.js +399 -0
- package/lib/kafka-consumer.js +798 -0
- package/lib/kafkajs/_admin.js +1052 -0
- package/lib/kafkajs/_common.js +887 -0
- package/lib/kafkajs/_consumer.js +2156 -0
- package/lib/kafkajs/_consumer_cache.js +310 -0
- package/lib/kafkajs/_error.js +298 -0
- package/lib/kafkajs/_kafka.js +125 -0
- package/lib/kafkajs/_linked-list.js +220 -0
- package/lib/kafkajs/_producer.js +863 -0
- package/lib/kafkajs/index.js +5 -0
- package/lib/producer/high-level-producer.js +400 -0
- package/lib/producer-stream.js +337 -0
- package/lib/producer.js +405 -0
- package/lib/rdkafka.js +44 -0
- package/lib/tools/ref-counter.js +53 -0
- package/lib/topic-partition.js +91 -0
- package/lib/topic.js +43 -0
- package/lib/util.js +55 -0
- package/librdkafka.js +12 -0
- package/package.json +85 -0
- package/shim/source_location +54 -0
- package/src/admin.cc +1597 -0
- package/src/admin.h +112 -0
- package/src/binding.cc +76 -0
- package/src/binding.h +25 -0
- package/src/callbacks.cc +676 -0
- package/src/callbacks.h +302 -0
- package/src/common.cc +1628 -0
- package/src/common.h +192 -0
- package/src/config.cc +219 -0
- package/src/config.h +61 -0
- package/src/connection.cc +703 -0
- package/src/connection.h +117 -0
- package/src/errors.cc +119 -0
- package/src/errors.h +60 -0
- package/src/kafka-consumer.cc +1539 -0
- package/src/kafka-consumer.h +147 -0
- package/src/producer.cc +917 -0
- package/src/producer.h +124 -0
- package/src/topic.cc +173 -0
- package/src/topic.h +54 -0
- package/src/workers.cc +1714 -0
- package/src/workers.h +690 -0
- package/types/config.d.ts +1269 -0
- package/types/errors.d.ts +373 -0
- package/types/kafkajs.d.ts +568 -0
- package/types/rdkafka.d.ts +571 -0
- package/util/configure.js +32 -0
- package/util/generate-docs.sh +18 -0
- package/util/get-env.js +6 -0
- package/util/test-compile.js +11 -0
- package/util/test-producer-delivery.js +100 -0
|
@@ -0,0 +1,2150 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* librdkafka - Apache Kafka C library
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025, Confluent Inc.
|
|
5
|
+
* All rights reserved.
|
|
6
|
+
*
|
|
7
|
+
* Redistribution and use in source and binary forms, with or without
|
|
8
|
+
* modification, are permitted provided that the following conditions are met:
|
|
9
|
+
*
|
|
10
|
+
* 1. Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
* this list of conditions and the following disclaimer.
|
|
12
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
* this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
* and/or other materials provided with the distribution.
|
|
15
|
+
*
|
|
16
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
20
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
21
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
22
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
23
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
24
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
25
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
#include "test.h"
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @brief Maximum supported values for test configuration
|
|
33
|
+
*/
|
|
34
|
+
#define MAX_CONSUMERS 16
|
|
35
|
+
#define MAX_TOPICS 16
|
|
36
|
+
#define MAX_PARTITIONS 32
|
|
37
|
+
#define BATCH_SIZE 10000
|
|
38
|
+
|
|
39
|
+
/** Common producer reused across all tests. */
|
|
40
|
+
static rd_kafka_t *common_producer;
|
|
41
|
+
|
|
42
|
+
/** Common admin client reused across all tests. */
|
|
43
|
+
static rd_kafka_t *common_admin;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @brief Test configuration structure
|
|
47
|
+
*
|
|
48
|
+
* This structure defines all parameters for a share consumer test scenario.
|
|
49
|
+
*
|
|
50
|
+
* Example configurations:
|
|
51
|
+
*
|
|
52
|
+
* 1. Multiple consumers, single topic, multiple partitions:
|
|
53
|
+
* { .consumer_cnt = 2, .topic_cnt = 1, .partitions = {3},
|
|
54
|
+
* .msgs_per_partition = 100, .group_name = "test-group" }
|
|
55
|
+
*
|
|
56
|
+
* 2. Single consumer, multiple topics, single partition each:
|
|
57
|
+
* { .consumer_cnt = 1, .topic_cnt = 2, .partitions = {1, 1},
|
|
58
|
+
* .msgs_per_partition = 100, .group_name = "test-group" }
|
|
59
|
+
*
|
|
60
|
+
* 3. Multiple consumers, multiple topics, multiple partitions:
|
|
61
|
+
* { .consumer_cnt = 3, .topic_cnt = 2, .partitions = {3, 2},
|
|
62
|
+
* .msgs_per_partition = 50, .group_name = "test-group" }
|
|
63
|
+
*/
|
|
64
|
+
typedef struct {
|
|
65
|
+
int consumer_cnt; /**< Number of consumers to create */
|
|
66
|
+
int topic_cnt; /**< Number of topics to create */
|
|
67
|
+
int partitions[MAX_TOPICS]; /**< Partition count for each topic */
|
|
68
|
+
int msgs_per_partition; /**< Messages to produce per partition */
|
|
69
|
+
const char *group_name; /**< Share group name */
|
|
70
|
+
const char *test_name; /**< Test description for logging */
|
|
71
|
+
int poll_timeout_ms; /**< Timeout for each poll (default: 3000) */
|
|
72
|
+
int max_attempts; /**< Max poll attempts (default: auto-calculated) */
|
|
73
|
+
} share_test_config_t;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @brief Test state/results structure
|
|
77
|
+
*/
|
|
78
|
+
typedef struct {
|
|
79
|
+
rd_kafka_share_t *consumers[MAX_CONSUMERS];
|
|
80
|
+
char *topic_names[MAX_TOPICS];
|
|
81
|
+
int per_consumer_count[MAX_CONSUMERS];
|
|
82
|
+
int total_consumed;
|
|
83
|
+
int total_expected;
|
|
84
|
+
} share_test_state_t;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @brief Create share consumers
|
|
88
|
+
*/
|
|
89
|
+
static void create_share_consumers(share_test_config_t *config,
|
|
90
|
+
share_test_state_t *state) {
|
|
91
|
+
int i;
|
|
92
|
+
|
|
93
|
+
for (i = 0; i < config->consumer_cnt; i++) {
|
|
94
|
+
state->consumers[i] =
|
|
95
|
+
test_create_share_consumer(config->group_name, NULL);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
TEST_SAY("Created %d share consumer(s)\n", config->consumer_cnt);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @brief Create topics and produce messages
|
|
103
|
+
*/
|
|
104
|
+
static void setup_topics_and_produce(share_test_config_t *config,
|
|
105
|
+
share_test_state_t *state) {
|
|
106
|
+
int t, p;
|
|
107
|
+
int total_partitions = 0;
|
|
108
|
+
|
|
109
|
+
state->total_expected = 0;
|
|
110
|
+
|
|
111
|
+
for (t = 0; t < config->topic_cnt; t++) {
|
|
112
|
+
/* Generate unique topic name */
|
|
113
|
+
state->topic_names[t] =
|
|
114
|
+
rd_strdup(test_mk_topic_name("0171-share-test", 1));
|
|
115
|
+
|
|
116
|
+
/* Create topic with specified partitions */
|
|
117
|
+
test_create_topic_wait_exists(NULL, state->topic_names[t],
|
|
118
|
+
config->partitions[t], -1,
|
|
119
|
+
60 * 1000);
|
|
120
|
+
|
|
121
|
+
/* Produce messages to each partition */
|
|
122
|
+
for (p = 0; p < config->partitions[t]; p++) {
|
|
123
|
+
test_produce_msgs_simple(common_producer,
|
|
124
|
+
state->topic_names[t], p,
|
|
125
|
+
config->msgs_per_partition);
|
|
126
|
+
state->total_expected += config->msgs_per_partition;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
total_partitions += config->partitions[t];
|
|
130
|
+
TEST_SAY("Topic '%s': %d partition(s), %d msgs/partition\n",
|
|
131
|
+
state->topic_names[t], config->partitions[t],
|
|
132
|
+
config->msgs_per_partition);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
TEST_SAY(
|
|
136
|
+
"Setup complete: %d topic(s), %d total partition(s), "
|
|
137
|
+
"%d total messages\n",
|
|
138
|
+
config->topic_cnt, total_partitions, state->total_expected);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @brief Subscribe consumers to topics
|
|
143
|
+
*/
|
|
144
|
+
static void subscribe_consumers(share_test_config_t *config,
|
|
145
|
+
share_test_state_t *state) {
|
|
146
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
147
|
+
int t, i;
|
|
148
|
+
|
|
149
|
+
/* Set group config using a dedicated admin client */
|
|
150
|
+
test_share_set_auto_offset_reset(config->group_name, "earliest");
|
|
151
|
+
|
|
152
|
+
/* Build subscription list */
|
|
153
|
+
subs = rd_kafka_topic_partition_list_new(config->topic_cnt);
|
|
154
|
+
for (t = 0; t < config->topic_cnt; t++) {
|
|
155
|
+
rd_kafka_topic_partition_list_add(subs, state->topic_names[t],
|
|
156
|
+
RD_KAFKA_PARTITION_UA);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Subscribe all consumers */
|
|
160
|
+
for (i = 0; i < config->consumer_cnt; i++) {
|
|
161
|
+
rd_kafka_share_subscribe(state->consumers[i], subs);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
165
|
+
|
|
166
|
+
TEST_SAY("Subscribed %d consumer(s) to %d topic(s)\n",
|
|
167
|
+
config->consumer_cnt, config->topic_cnt);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @brief Consume messages function
|
|
172
|
+
*/
|
|
173
|
+
static void consume_messages(share_test_config_t *config,
|
|
174
|
+
share_test_state_t *state) {
|
|
175
|
+
rd_kafka_messages_t *batch = NULL;
|
|
176
|
+
int attempts;
|
|
177
|
+
int poll_timeout;
|
|
178
|
+
int i;
|
|
179
|
+
|
|
180
|
+
/* Initialize per-consumer counts */
|
|
181
|
+
for (i = 0; i < config->consumer_cnt; i++) {
|
|
182
|
+
state->per_consumer_count[i] = 0;
|
|
183
|
+
}
|
|
184
|
+
state->total_consumed = 0;
|
|
185
|
+
|
|
186
|
+
/* Calculate defaults if not specified */
|
|
187
|
+
poll_timeout =
|
|
188
|
+
config->poll_timeout_ms > 0 ? config->poll_timeout_ms : 3000;
|
|
189
|
+
|
|
190
|
+
/* Auto-calculate attempts based on expected messages and consumers */
|
|
191
|
+
if (config->max_attempts > 0) {
|
|
192
|
+
attempts = config->max_attempts;
|
|
193
|
+
} else {
|
|
194
|
+
/* Heuristic: at least 10 attempts, or based on message count */
|
|
195
|
+
attempts = (state->total_expected / (BATCH_SIZE / 2)) + 10;
|
|
196
|
+
if (attempts < 20)
|
|
197
|
+
attempts = 20;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
TEST_SAY(
|
|
201
|
+
"Starting consumption: expecting %d messages, "
|
|
202
|
+
"max %d attempts, %dms timeout\n",
|
|
203
|
+
state->total_expected, attempts, poll_timeout);
|
|
204
|
+
|
|
205
|
+
while (state->total_consumed < state->total_expected &&
|
|
206
|
+
attempts-- > 0) {
|
|
207
|
+
/* Round-robin poll across all consumers */
|
|
208
|
+
for (i = 0; i < config->consumer_cnt; i++) {
|
|
209
|
+
size_t rcvd = 0;
|
|
210
|
+
size_t m;
|
|
211
|
+
rd_kafka_error_t *err;
|
|
212
|
+
|
|
213
|
+
rd_kafka_messages_destroy(batch);
|
|
214
|
+
batch = NULL;
|
|
215
|
+
err = rd_kafka_share_poll(state->consumers[i],
|
|
216
|
+
poll_timeout, &batch);
|
|
217
|
+
|
|
218
|
+
if (err) {
|
|
219
|
+
rd_kafka_error_destroy(err);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
224
|
+
for (m = 0; m < rcvd; m++) {
|
|
225
|
+
rd_kafka_message_t *msg =
|
|
226
|
+
rd_kafka_messages_get(batch, m);
|
|
227
|
+
if (!msg->err) {
|
|
228
|
+
state->per_consumer_count[i]++;
|
|
229
|
+
state->total_consumed++;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* Early exit if we've consumed everything */
|
|
234
|
+
if (state->total_consumed >= state->total_expected)
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Progress logging */
|
|
239
|
+
if (config->consumer_cnt == 1) {
|
|
240
|
+
TEST_SAY("Progress: %d/%d\n", state->total_consumed,
|
|
241
|
+
state->total_expected);
|
|
242
|
+
} else {
|
|
243
|
+
/* Build distribution string for multiple consumers */
|
|
244
|
+
char dist[256] = {0};
|
|
245
|
+
int pos = 0;
|
|
246
|
+
for (i = 0; i < config->consumer_cnt && pos < 250;
|
|
247
|
+
i++) {
|
|
248
|
+
pos += rd_snprintf(
|
|
249
|
+
dist + pos, sizeof(dist) - pos, "c%d=%d ",
|
|
250
|
+
i, state->per_consumer_count[i]);
|
|
251
|
+
}
|
|
252
|
+
TEST_SAY("Progress: %d/%d (%s)\n",
|
|
253
|
+
state->total_consumed, state->total_expected,
|
|
254
|
+
dist);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
rd_kafka_messages_destroy(batch);
|
|
259
|
+
batch = NULL;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @brief Cleanup function
|
|
264
|
+
*/
|
|
265
|
+
static void cleanup_test(share_test_config_t *config,
|
|
266
|
+
share_test_state_t *state) {
|
|
267
|
+
int t, i;
|
|
268
|
+
|
|
269
|
+
for (t = 0; t < config->topic_cnt; t++) {
|
|
270
|
+
if (state->topic_names[t]) {
|
|
271
|
+
rd_free(state->topic_names[t]);
|
|
272
|
+
state->topic_names[t] = NULL;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Destroy consumers */
|
|
277
|
+
for (i = 0; i < config->consumer_cnt; i++) {
|
|
278
|
+
if (state->consumers[i]) {
|
|
279
|
+
test_share_consumer_close(state->consumers[i]);
|
|
280
|
+
test_share_destroy(state->consumers[i]);
|
|
281
|
+
state->consumers[i] = NULL;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
TEST_SAY("Cleanup complete\n");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @brief Test Runner function
|
|
290
|
+
*
|
|
291
|
+
* This function handles:
|
|
292
|
+
* 1. Create consumers
|
|
293
|
+
* 2. Create topics and produce messages
|
|
294
|
+
* 3. Subscribe consumers
|
|
295
|
+
* 4. Consume and verify messages
|
|
296
|
+
* 5. Cleanup
|
|
297
|
+
*
|
|
298
|
+
* @param config Test configuration
|
|
299
|
+
* @returns 0 on success, -1 on failure
|
|
300
|
+
*/
|
|
301
|
+
static int run_share_consumer_test(share_test_config_t *config) {
|
|
302
|
+
share_test_state_t state = {0};
|
|
303
|
+
int i;
|
|
304
|
+
char dist_str[512] = {0};
|
|
305
|
+
int pos = 0;
|
|
306
|
+
char unique_suffix[64];
|
|
307
|
+
char unique_group[128];
|
|
308
|
+
char unique_test_name[256];
|
|
309
|
+
|
|
310
|
+
/* Validate config */
|
|
311
|
+
TEST_ASSERT(config->consumer_cnt > 0 &&
|
|
312
|
+
config->consumer_cnt <= MAX_CONSUMERS,
|
|
313
|
+
"consumer_cnt must be 1-%d", MAX_CONSUMERS);
|
|
314
|
+
TEST_ASSERT(config->topic_cnt > 0 && config->topic_cnt <= MAX_TOPICS,
|
|
315
|
+
"topic_cnt must be 1-%d", MAX_TOPICS);
|
|
316
|
+
TEST_ASSERT(config->group_name != NULL, "group_name is required");
|
|
317
|
+
|
|
318
|
+
/* Append a per-invocation unique suffix to the group name and
|
|
319
|
+
* displayed test name so re-runs / parallel runs on the same
|
|
320
|
+
* cluster can't collide on share-group state. */
|
|
321
|
+
rd_snprintf(unique_suffix, sizeof(unique_suffix), "rnd%" PRIx64,
|
|
322
|
+
test_id_generate());
|
|
323
|
+
rd_snprintf(unique_group, sizeof(unique_group), "%s-%s",
|
|
324
|
+
config->group_name, unique_suffix);
|
|
325
|
+
rd_snprintf(unique_test_name, sizeof(unique_test_name), "%s [%s]",
|
|
326
|
+
config->test_name ? config->test_name
|
|
327
|
+
: "Share Consumer Test",
|
|
328
|
+
unique_suffix);
|
|
329
|
+
config->group_name = unique_group;
|
|
330
|
+
config->test_name = unique_test_name;
|
|
331
|
+
|
|
332
|
+
/* Print test header */
|
|
333
|
+
TEST_SAY("\n");
|
|
334
|
+
TEST_SAY(
|
|
335
|
+
"============================================================\n");
|
|
336
|
+
TEST_SAY("=== %s ===\n", config->test_name);
|
|
337
|
+
TEST_SAY("=== Consumers: %d, Topics: %d, Partitions: [",
|
|
338
|
+
config->consumer_cnt, config->topic_cnt);
|
|
339
|
+
for (i = 0; i < config->topic_cnt; i++) {
|
|
340
|
+
TEST_SAY("%d%s", config->partitions[i],
|
|
341
|
+
i < config->topic_cnt - 1 ? ", " : "");
|
|
342
|
+
}
|
|
343
|
+
TEST_SAY("], Msgs/Partition: %d ===\n", config->msgs_per_partition);
|
|
344
|
+
TEST_SAY(
|
|
345
|
+
"============================================================\n");
|
|
346
|
+
|
|
347
|
+
/* Execute test phases */
|
|
348
|
+
create_share_consumers(config, &state);
|
|
349
|
+
setup_topics_and_produce(config, &state);
|
|
350
|
+
subscribe_consumers(config, &state);
|
|
351
|
+
consume_messages(config, &state);
|
|
352
|
+
|
|
353
|
+
/* Verify results */
|
|
354
|
+
TEST_ASSERT(state.total_consumed == state.total_expected,
|
|
355
|
+
"Expected %d messages, consumed %d", state.total_expected,
|
|
356
|
+
state.total_consumed);
|
|
357
|
+
|
|
358
|
+
/* Build distribution string */
|
|
359
|
+
for (i = 0; i < config->consumer_cnt && pos < 500; i++) {
|
|
360
|
+
pos += rd_snprintf(dist_str + pos, sizeof(dist_str) - pos,
|
|
361
|
+
"c%d=%d ", i, state.per_consumer_count[i]);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
TEST_SAY("✓ SUCCESS: Consumed %d/%d messages (distribution: %s)\n",
|
|
365
|
+
state.total_consumed, state.total_expected, dist_str);
|
|
366
|
+
|
|
367
|
+
/* Cleanup */
|
|
368
|
+
cleanup_test(config, &state);
|
|
369
|
+
|
|
370
|
+
return 0;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* @brief Single consumer, single topic, single partition
|
|
376
|
+
*/
|
|
377
|
+
static void do_test_single_consumer_single_topic_single_partition(void) {
|
|
378
|
+
share_test_config_t config = {
|
|
379
|
+
.consumer_cnt = 1,
|
|
380
|
+
.topic_cnt = 1,
|
|
381
|
+
.partitions = {1},
|
|
382
|
+
.msgs_per_partition = 1000,
|
|
383
|
+
.group_name = "share-1c-1t-1p",
|
|
384
|
+
.test_name = "Single consumer, single topic, single partition"};
|
|
385
|
+
SUB_TEST();
|
|
386
|
+
run_share_consumer_test(&config);
|
|
387
|
+
SUB_TEST_PASS();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @brief Single consumer, single topic, multiple partitions
|
|
392
|
+
*/
|
|
393
|
+
static void do_test_single_consumer_single_topic_multiple_partitions(void) {
|
|
394
|
+
share_test_config_t config = {
|
|
395
|
+
.consumer_cnt = 1,
|
|
396
|
+
.topic_cnt = 1,
|
|
397
|
+
.partitions = {3},
|
|
398
|
+
.msgs_per_partition = 500,
|
|
399
|
+
.group_name = "share-1c-1t-3p",
|
|
400
|
+
.test_name = "Single consumer, single topic, 3 partitions"};
|
|
401
|
+
SUB_TEST();
|
|
402
|
+
run_share_consumer_test(&config);
|
|
403
|
+
SUB_TEST_PASS();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @brief Single consumer, multiple topics, single partition each
|
|
408
|
+
*/
|
|
409
|
+
static void do_test_single_consumer_multiple_topic_single_partition(void) {
|
|
410
|
+
share_test_config_t config = {
|
|
411
|
+
.consumer_cnt = 1,
|
|
412
|
+
.topic_cnt = 2,
|
|
413
|
+
.partitions = {1, 1},
|
|
414
|
+
.msgs_per_partition = 500,
|
|
415
|
+
.group_name = "share-1c-2t-1p",
|
|
416
|
+
.test_name = "Single consumer, 2 topics, 1 partition each"};
|
|
417
|
+
SUB_TEST();
|
|
418
|
+
run_share_consumer_test(&config);
|
|
419
|
+
SUB_TEST_PASS();
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @brief Single consumer, multiple topics, multiple partitions
|
|
424
|
+
*/
|
|
425
|
+
static void do_test_single_consumer_multiple_topic_multiple_partitions(void) {
|
|
426
|
+
share_test_config_t config = {
|
|
427
|
+
.consumer_cnt = 1,
|
|
428
|
+
.topic_cnt = 3,
|
|
429
|
+
.partitions = {2, 2, 2},
|
|
430
|
+
.msgs_per_partition = 500,
|
|
431
|
+
.group_name = "share-1c-3t-2p",
|
|
432
|
+
.test_name = "Single consumer, 3 topics, 2 partitions each"};
|
|
433
|
+
SUB_TEST();
|
|
434
|
+
run_share_consumer_test(&config);
|
|
435
|
+
SUB_TEST_PASS();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* @brief Multiple consumers, single topic, single partition
|
|
440
|
+
*/
|
|
441
|
+
static void do_test_multiple_consumers_single_topic_single_partition(void) {
|
|
442
|
+
share_test_config_t config = {
|
|
443
|
+
.consumer_cnt = 2,
|
|
444
|
+
.topic_cnt = 1,
|
|
445
|
+
.partitions = {1},
|
|
446
|
+
.msgs_per_partition = 1000,
|
|
447
|
+
.group_name = "share-2c-1t-1p",
|
|
448
|
+
.test_name = "2 consumers, single topic, single partition"};
|
|
449
|
+
SUB_TEST();
|
|
450
|
+
run_share_consumer_test(&config);
|
|
451
|
+
SUB_TEST_PASS();
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* @brief Multiple consumers, single topic, multiple partitions
|
|
456
|
+
*/
|
|
457
|
+
static void do_test_multiple_consumers_single_topic_multiple_partitions(void) {
|
|
458
|
+
share_test_config_t config = {
|
|
459
|
+
.consumer_cnt = 2,
|
|
460
|
+
.topic_cnt = 1,
|
|
461
|
+
.partitions = {4},
|
|
462
|
+
.msgs_per_partition = 500,
|
|
463
|
+
.group_name = "share-2c-1t-4p",
|
|
464
|
+
.test_name = "2 consumers, single topic, 4 partitions"};
|
|
465
|
+
SUB_TEST();
|
|
466
|
+
run_share_consumer_test(&config);
|
|
467
|
+
SUB_TEST_PASS();
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* @brief Multiple consumers, multiple topics, multiple partitions
|
|
472
|
+
*/
|
|
473
|
+
static void
|
|
474
|
+
do_test_multiple_consumers_multiple_topics_multiple_partitions(void) {
|
|
475
|
+
share_test_config_t config = {
|
|
476
|
+
.consumer_cnt = 3,
|
|
477
|
+
.topic_cnt = 2,
|
|
478
|
+
.partitions = {3, 3},
|
|
479
|
+
.msgs_per_partition = 500,
|
|
480
|
+
.group_name = "share-3c-2t-3p",
|
|
481
|
+
.test_name = "3 consumers, 2 topics, 3 partitions each"};
|
|
482
|
+
SUB_TEST();
|
|
483
|
+
run_share_consumer_test(&config);
|
|
484
|
+
SUB_TEST_PASS();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/***************************************************************************
|
|
488
|
+
* High Volume Tests
|
|
489
|
+
***************************************************************************/
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @brief High volume test with 10k messages on single partition
|
|
493
|
+
*/
|
|
494
|
+
static void do_test_high_volume_10k_messages(void) {
|
|
495
|
+
share_test_config_t config = {
|
|
496
|
+
.consumer_cnt = 1,
|
|
497
|
+
.topic_cnt = 1,
|
|
498
|
+
.partitions = {1},
|
|
499
|
+
.msgs_per_partition = 10000,
|
|
500
|
+
.group_name = "share-highvol-10k",
|
|
501
|
+
.test_name = "High volume: 10k messages, 1 partition",
|
|
502
|
+
.max_attempts = 100};
|
|
503
|
+
SUB_TEST();
|
|
504
|
+
run_share_consumer_test(&config);
|
|
505
|
+
SUB_TEST_PASS();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* @brief High volume test with 50k messages across 5 partitions
|
|
510
|
+
*/
|
|
511
|
+
static void do_test_high_volume_50k_multi_partition(void) {
|
|
512
|
+
share_test_config_t config = {
|
|
513
|
+
.consumer_cnt = 1,
|
|
514
|
+
.topic_cnt = 1,
|
|
515
|
+
.partitions = {5},
|
|
516
|
+
.msgs_per_partition = 10000,
|
|
517
|
+
.group_name = "share-highvol-50k",
|
|
518
|
+
.test_name = "High volume: 50k messages, 5 partitions",
|
|
519
|
+
.max_attempts = 150};
|
|
520
|
+
SUB_TEST();
|
|
521
|
+
run_share_consumer_test(&config);
|
|
522
|
+
SUB_TEST_PASS();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/***************************************************************************
|
|
526
|
+
* Multi-Topic Tests (Triggers Multiple Fetch Responses)
|
|
527
|
+
***************************************************************************/
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* @brief 15 topics to trigger multiple ShareFetch responses
|
|
531
|
+
*/
|
|
532
|
+
static void do_test_many_topics_15(void) {
|
|
533
|
+
share_test_config_t config = {
|
|
534
|
+
.consumer_cnt = 1,
|
|
535
|
+
.topic_cnt = 15,
|
|
536
|
+
.partitions = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
|
|
537
|
+
.msgs_per_partition = 100,
|
|
538
|
+
.group_name = "share-15topics",
|
|
539
|
+
.test_name = "15 topics, 1 partition each (1500 msgs)",
|
|
540
|
+
.max_attempts = 100};
|
|
541
|
+
SUB_TEST();
|
|
542
|
+
run_share_consumer_test(&config);
|
|
543
|
+
SUB_TEST_PASS();
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* @brief 10 topics with 2 partitions each
|
|
548
|
+
*/
|
|
549
|
+
static void do_test_many_topics_10_multi_partition(void) {
|
|
550
|
+
share_test_config_t config = {
|
|
551
|
+
.consumer_cnt = 1,
|
|
552
|
+
.topic_cnt = 10,
|
|
553
|
+
.partitions = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
|
|
554
|
+
.msgs_per_partition = 100,
|
|
555
|
+
.group_name = "share-10topics-2p",
|
|
556
|
+
.test_name = "10 topics, 2 partitions each (2000 msgs)",
|
|
557
|
+
.max_attempts = 100};
|
|
558
|
+
SUB_TEST();
|
|
559
|
+
run_share_consumer_test(&config);
|
|
560
|
+
SUB_TEST_PASS();
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/***************************************************************************
|
|
564
|
+
* Rapid Produce/Consume Tests
|
|
565
|
+
***************************************************************************/
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* @brief Rapid produce/consume cycles - 20 rounds of 500 messages each
|
|
569
|
+
*/
|
|
570
|
+
static void do_test_rapid_produce_consume_cycles(void) {
|
|
571
|
+
rd_kafka_share_t *consumer;
|
|
572
|
+
rd_kafka_messages_t *batch = NULL;
|
|
573
|
+
const char *topic;
|
|
574
|
+
const char *group = "share-rapid-cycles";
|
|
575
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
576
|
+
int round, total_consumed = 0;
|
|
577
|
+
const int rounds = 20;
|
|
578
|
+
const int msgs_per_round = 500;
|
|
579
|
+
const int total_expected = rounds * msgs_per_round;
|
|
580
|
+
|
|
581
|
+
SUB_TEST();
|
|
582
|
+
|
|
583
|
+
TEST_SAY("\n");
|
|
584
|
+
TEST_SAY("=== Rapid produce/consume cycles: %d rounds x %d msgs ===\n",
|
|
585
|
+
rounds, msgs_per_round);
|
|
586
|
+
|
|
587
|
+
/* Create consumer and topic */
|
|
588
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
589
|
+
topic = test_mk_topic_name("0171-rapid-cycles", 1);
|
|
590
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
591
|
+
|
|
592
|
+
/* Configure group */
|
|
593
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
594
|
+
|
|
595
|
+
/* Subscribe */
|
|
596
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
597
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
598
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
599
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
600
|
+
|
|
601
|
+
/* Rapid cycles */
|
|
602
|
+
for (round = 0; round < rounds; round++) {
|
|
603
|
+
int round_consumed = 0;
|
|
604
|
+
int attempts = 100;
|
|
605
|
+
|
|
606
|
+
/* Produce */
|
|
607
|
+
test_produce_msgs_simple(common_producer, topic, 0,
|
|
608
|
+
msgs_per_round);
|
|
609
|
+
|
|
610
|
+
/* Consume */
|
|
611
|
+
while (round_consumed < msgs_per_round && attempts-- > 0) {
|
|
612
|
+
size_t rcvd = 0;
|
|
613
|
+
size_t m;
|
|
614
|
+
rd_kafka_error_t *err;
|
|
615
|
+
|
|
616
|
+
rd_kafka_messages_destroy(batch);
|
|
617
|
+
batch = NULL;
|
|
618
|
+
err = rd_kafka_share_poll(consumer, 1000, &batch);
|
|
619
|
+
if (err) {
|
|
620
|
+
rd_kafka_error_destroy(err);
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
625
|
+
for (m = 0; m < rcvd; m++) {
|
|
626
|
+
rd_kafka_message_t *msg =
|
|
627
|
+
rd_kafka_messages_get(batch, m);
|
|
628
|
+
if (!msg->err)
|
|
629
|
+
round_consumed++;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
total_consumed += round_consumed;
|
|
634
|
+
TEST_SAY("Round %d: consumed %d/%d (total: %d/%d)\n", round + 1,
|
|
635
|
+
round_consumed, msgs_per_round, total_consumed,
|
|
636
|
+
(round + 1) * msgs_per_round);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
rd_kafka_messages_destroy(batch);
|
|
640
|
+
batch = NULL;
|
|
641
|
+
|
|
642
|
+
TEST_ASSERT(total_consumed == total_expected,
|
|
643
|
+
"Expected %d messages, consumed %d", total_expected,
|
|
644
|
+
total_consumed);
|
|
645
|
+
|
|
646
|
+
TEST_SAY("SUCCESS: Rapid cycles completed - %d messages\n",
|
|
647
|
+
total_consumed);
|
|
648
|
+
|
|
649
|
+
test_share_consumer_close(consumer);
|
|
650
|
+
test_share_destroy(consumer);
|
|
651
|
+
|
|
652
|
+
SUB_TEST_PASS();
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* @brief Empty topic then produce - verify consumer handles transition
|
|
657
|
+
*/
|
|
658
|
+
static void do_test_empty_then_produce(void) {
|
|
659
|
+
rd_kafka_share_t *consumer;
|
|
660
|
+
rd_kafka_messages_t *batch = NULL;
|
|
661
|
+
const char *topic;
|
|
662
|
+
const char *group = "share-empty-then-produce";
|
|
663
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
664
|
+
int consumed = 0, attempts;
|
|
665
|
+
|
|
666
|
+
SUB_TEST();
|
|
667
|
+
|
|
668
|
+
TEST_SAY("\n");
|
|
669
|
+
TEST_SAY("=== Empty topic then produce test ===\n");
|
|
670
|
+
|
|
671
|
+
/* Create consumer and empty topic */
|
|
672
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
673
|
+
topic = test_mk_topic_name("0171-empty-then-produce", 1);
|
|
674
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
675
|
+
|
|
676
|
+
/* Configure and subscribe */
|
|
677
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
678
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
679
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
680
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
681
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
682
|
+
|
|
683
|
+
/* Poll empty topic - should return 0 */
|
|
684
|
+
TEST_SAY("Polling empty topic...\n");
|
|
685
|
+
for (attempts = 0; attempts < 5; attempts++) {
|
|
686
|
+
size_t rcvd = 0;
|
|
687
|
+
rd_kafka_error_t *err;
|
|
688
|
+
|
|
689
|
+
rd_kafka_messages_destroy(batch);
|
|
690
|
+
batch = NULL;
|
|
691
|
+
err = rd_kafka_share_poll(consumer, 1000, &batch);
|
|
692
|
+
if (err)
|
|
693
|
+
rd_kafka_error_destroy(err);
|
|
694
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
695
|
+
TEST_SAY("Empty poll %d: received %zu\n", attempts + 1, rcvd);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/* Now produce messages */
|
|
699
|
+
TEST_SAY("Producing 100 messages...\n");
|
|
700
|
+
test_produce_msgs_simple(common_producer, topic, 0, 100);
|
|
701
|
+
|
|
702
|
+
/* Consume - should get messages now */
|
|
703
|
+
attempts = 100;
|
|
704
|
+
while (consumed < 100 && attempts-- > 0) {
|
|
705
|
+
size_t rcvd = 0;
|
|
706
|
+
size_t m;
|
|
707
|
+
rd_kafka_error_t *err;
|
|
708
|
+
|
|
709
|
+
rd_kafka_messages_destroy(batch);
|
|
710
|
+
batch = NULL;
|
|
711
|
+
err = rd_kafka_share_poll(consumer, 1000, &batch);
|
|
712
|
+
if (err) {
|
|
713
|
+
rd_kafka_error_destroy(err);
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
718
|
+
for (m = 0; m < rcvd; m++) {
|
|
719
|
+
rd_kafka_message_t *msg =
|
|
720
|
+
rd_kafka_messages_get(batch, m);
|
|
721
|
+
if (!msg->err)
|
|
722
|
+
consumed++;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
rd_kafka_messages_destroy(batch);
|
|
727
|
+
batch = NULL;
|
|
728
|
+
|
|
729
|
+
TEST_ASSERT(consumed == 100, "Expected 100 messages, consumed %d",
|
|
730
|
+
consumed);
|
|
731
|
+
TEST_SAY("SUCCESS: Empty then produce - consumed %d messages\n",
|
|
732
|
+
consumed);
|
|
733
|
+
|
|
734
|
+
test_share_consumer_close(consumer);
|
|
735
|
+
test_share_destroy(consumer);
|
|
736
|
+
|
|
737
|
+
SUB_TEST_PASS();
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* @brief Sparse partitions - produce only to some partitions
|
|
742
|
+
*/
|
|
743
|
+
static void do_test_sparse_partitions(void) {
|
|
744
|
+
rd_kafka_share_t *consumer;
|
|
745
|
+
rd_kafka_messages_t *batch = NULL;
|
|
746
|
+
const char *topic;
|
|
747
|
+
const char *group = "share-sparse-partitions";
|
|
748
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
749
|
+
int consumed = 0, attempts;
|
|
750
|
+
const int msgs_per_partition = 100;
|
|
751
|
+
const int expected = 3 * msgs_per_partition; /* partitions 0,2,4 */
|
|
752
|
+
|
|
753
|
+
SUB_TEST();
|
|
754
|
+
|
|
755
|
+
TEST_SAY("\n");
|
|
756
|
+
TEST_SAY(
|
|
757
|
+
"=== Sparse partitions test (5 partitions, produce to 0,2,4) "
|
|
758
|
+
"===\n");
|
|
759
|
+
|
|
760
|
+
/* Create consumer and topic with 5 partitions */
|
|
761
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
762
|
+
topic = test_mk_topic_name("0171-sparse-partitions", 1);
|
|
763
|
+
test_create_topic_wait_exists(NULL, topic, 5, -1, 60 * 1000);
|
|
764
|
+
|
|
765
|
+
/* Configure and subscribe */
|
|
766
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
767
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
768
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
769
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
770
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
771
|
+
|
|
772
|
+
/* Produce only to partitions 0, 2, 4 (skip 1, 3) */
|
|
773
|
+
test_produce_msgs_simple(common_producer, topic, 0, msgs_per_partition);
|
|
774
|
+
test_produce_msgs_simple(common_producer, topic, 2, msgs_per_partition);
|
|
775
|
+
test_produce_msgs_simple(common_producer, topic, 4, msgs_per_partition);
|
|
776
|
+
|
|
777
|
+
TEST_SAY("Produced %d messages to partitions 0, 2, 4\n", expected);
|
|
778
|
+
|
|
779
|
+
/* Consume all */
|
|
780
|
+
attempts = 100;
|
|
781
|
+
while (consumed < expected && attempts-- > 0) {
|
|
782
|
+
size_t rcvd = 0;
|
|
783
|
+
size_t m;
|
|
784
|
+
rd_kafka_error_t *err;
|
|
785
|
+
|
|
786
|
+
rd_kafka_messages_destroy(batch);
|
|
787
|
+
batch = NULL;
|
|
788
|
+
err = rd_kafka_share_poll(consumer, 1000, &batch);
|
|
789
|
+
if (err) {
|
|
790
|
+
rd_kafka_error_destroy(err);
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
795
|
+
for (m = 0; m < rcvd; m++) {
|
|
796
|
+
rd_kafka_message_t *msg =
|
|
797
|
+
rd_kafka_messages_get(batch, m);
|
|
798
|
+
if (!msg->err)
|
|
799
|
+
consumed++;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
TEST_SAY("Progress: %d/%d\n", consumed, expected);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
rd_kafka_messages_destroy(batch);
|
|
806
|
+
batch = NULL;
|
|
807
|
+
|
|
808
|
+
TEST_ASSERT(consumed == expected, "Expected %d messages, consumed %d",
|
|
809
|
+
expected, consumed);
|
|
810
|
+
TEST_SAY("SUCCESS: Sparse partitions - consumed %d messages\n",
|
|
811
|
+
consumed);
|
|
812
|
+
|
|
813
|
+
test_share_consumer_close(consumer);
|
|
814
|
+
test_share_destroy(consumer);
|
|
815
|
+
|
|
816
|
+
SUB_TEST_PASS();
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* @brief Poll callback - piggybacked acks on ShareFetch.
|
|
822
|
+
*
|
|
823
|
+
* In implicit mode, acks are automatically sent with the next poll().
|
|
824
|
+
* This test verifies that the callback is invoked when acks are
|
|
825
|
+
* piggybacked on ShareFetch responses.
|
|
826
|
+
*/
|
|
827
|
+
static void do_test_poll_callback_piggybacked_acks(void) {
|
|
828
|
+
rd_kafka_share_t *consumer;
|
|
829
|
+
rd_kafka_messages_t *batch = NULL;
|
|
830
|
+
const char *topic;
|
|
831
|
+
const char *group = "share-poll-callback";
|
|
832
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
833
|
+
int consumed = 0, attempts;
|
|
834
|
+
test_ack_cb_state_t state = {0};
|
|
835
|
+
|
|
836
|
+
SUB_TEST();
|
|
837
|
+
|
|
838
|
+
TEST_SAY("\n");
|
|
839
|
+
TEST_SAY(
|
|
840
|
+
"=== Poll callback test (piggybacked acks on ShareFetch) ===\n");
|
|
841
|
+
|
|
842
|
+
consumer =
|
|
843
|
+
test_create_share_consumer_with_cb(group, "implicit", &state, NULL);
|
|
844
|
+
|
|
845
|
+
/* Create topic and produce messages */
|
|
846
|
+
topic = test_mk_topic_name("0171-poll-cb", 1);
|
|
847
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
848
|
+
test_produce_msgs_simple(common_producer, topic, 0, 100);
|
|
849
|
+
|
|
850
|
+
/* Configure group */
|
|
851
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
852
|
+
|
|
853
|
+
/* Subscribe */
|
|
854
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
855
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
856
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
857
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
858
|
+
|
|
859
|
+
/* First poll: consume messages (implicit mode auto-acks) */
|
|
860
|
+
attempts = 50;
|
|
861
|
+
while (consumed < 50 && attempts-- > 0) {
|
|
862
|
+
size_t rcvd = 0;
|
|
863
|
+
size_t m;
|
|
864
|
+
rd_kafka_error_t *err;
|
|
865
|
+
|
|
866
|
+
rd_kafka_messages_destroy(batch);
|
|
867
|
+
batch = NULL;
|
|
868
|
+
err = rd_kafka_share_poll(consumer, 2000, &batch);
|
|
869
|
+
if (err) {
|
|
870
|
+
rd_kafka_error_destroy(err);
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
875
|
+
for (m = 0; m < rcvd; m++) {
|
|
876
|
+
rd_kafka_message_t *msg =
|
|
877
|
+
rd_kafka_messages_get(batch, m);
|
|
878
|
+
if (!msg->err)
|
|
879
|
+
consumed++;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
rd_kafka_messages_destroy(batch);
|
|
884
|
+
batch = NULL;
|
|
885
|
+
|
|
886
|
+
TEST_SAY("Consumed %d messages\n", consumed);
|
|
887
|
+
TEST_ASSERT(consumed > 0, "Expected to consume some messages");
|
|
888
|
+
|
|
889
|
+
/* Second poll: this sends piggybacked acks from first batch */
|
|
890
|
+
test_wait_for_cb_with_poll(&state, consumer, 1, 20000);
|
|
891
|
+
|
|
892
|
+
TEST_SAY("Callback count=%d, total_offsets=%zu\n", state.callback_cnt,
|
|
893
|
+
state.total_offsets);
|
|
894
|
+
|
|
895
|
+
TEST_ASSERT(
|
|
896
|
+
state.callback_cnt >= 1,
|
|
897
|
+
"Expected at least 1 callback from piggybacked acks, got %d",
|
|
898
|
+
state.callback_cnt);
|
|
899
|
+
TEST_ASSERT(state.total_offsets == (size_t)consumed,
|
|
900
|
+
"Expected %d offsets in callback, got %zu", consumed,
|
|
901
|
+
state.total_offsets);
|
|
902
|
+
|
|
903
|
+
TEST_SAY("SUCCESS: Poll callback received %d callbacks, %zu offsets\n",
|
|
904
|
+
state.callback_cnt, state.total_offsets);
|
|
905
|
+
|
|
906
|
+
/* Cleanup */
|
|
907
|
+
test_share_consumer_close(consumer);
|
|
908
|
+
test_share_destroy(consumer);
|
|
909
|
+
test_ack_cb_state_destroy(&state);
|
|
910
|
+
|
|
911
|
+
SUB_TEST_PASS();
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* @brief Worker-thread argument for the gate-conflict test.
|
|
917
|
+
*
|
|
918
|
+
* @c entering is set to 1 by the worker thread just before it calls
|
|
919
|
+
* share_consume_batch, so the main thread can wait for the worker to
|
|
920
|
+
* have started before probing. (entering=1 doesn't yet prove the gate
|
|
921
|
+
* is held — there's still a window between the atomic flip and the
|
|
922
|
+
* gate acquire inside consume_batch — so the main thread also retries
|
|
923
|
+
* its probe in a short polling loop below.)
|
|
924
|
+
*/
|
|
925
|
+
typedef struct {
|
|
926
|
+
rd_kafka_share_t *consumer;
|
|
927
|
+
rd_atomic32_t entering;
|
|
928
|
+
} blocker_arg_t;
|
|
929
|
+
|
|
930
|
+
static int consume_blocker_thread(void *arg) {
|
|
931
|
+
blocker_arg_t *ba = arg;
|
|
932
|
+
rd_kafka_messages_t *batch = NULL;
|
|
933
|
+
rd_kafka_error_t *err;
|
|
934
|
+
|
|
935
|
+
rd_atomic32_set(&ba->entering, 1);
|
|
936
|
+
|
|
937
|
+
/* Blocks for ~5s waiting for messages that never arrive.
|
|
938
|
+
* The share access gate is held for the full duration. */
|
|
939
|
+
err = rd_kafka_share_poll(ba->consumer, 5000, &batch);
|
|
940
|
+
if (err)
|
|
941
|
+
rd_kafka_error_destroy(err);
|
|
942
|
+
rd_kafka_messages_destroy(batch);
|
|
943
|
+
return 0;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* @brief Verify the share consumer's single-thread access gate rejects
|
|
948
|
+
* concurrent calls from a second thread, and that ownership is
|
|
949
|
+
* released after the holder returns so a later call succeeds.
|
|
950
|
+
*/
|
|
951
|
+
static void do_test_concurrent_thread_access_rejected(void) {
|
|
952
|
+
rd_kafka_share_t *consumer;
|
|
953
|
+
const char *topic;
|
|
954
|
+
const char *group = "share-concurrent-thread";
|
|
955
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
956
|
+
thrd_t blocker;
|
|
957
|
+
blocker_arg_t ba;
|
|
958
|
+
rd_kafka_messages_t *batch = NULL;
|
|
959
|
+
rd_kafka_error_t *err = NULL;
|
|
960
|
+
int probe_attempt;
|
|
961
|
+
const int probe_max_attempts = 50; /* 50 × 100ms = 5 s budget */
|
|
962
|
+
|
|
963
|
+
SUB_TEST();
|
|
964
|
+
|
|
965
|
+
/* Skip under Valgrind: Valgrind's heavy thread serialization
|
|
966
|
+
* makes the blocker thread's 5s consume_batch timeout
|
|
967
|
+
* unreliable, which can leave thrd_join / share consumer close
|
|
968
|
+
* waiting indefinitely (see logs.txt: subtest ran ~4000s
|
|
969
|
+
* before the suite watchdog killed it). */
|
|
970
|
+
if (!strcmp(test_mode, "valgrind")) {
|
|
971
|
+
SUB_TEST_SKIP(
|
|
972
|
+
"Concurrent-thread gate test is scheduling-sensitive "
|
|
973
|
+
"under Valgrind\n");
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
TEST_SAY("\n");
|
|
978
|
+
TEST_SAY("=== Concurrent thread access rejected ===\n");
|
|
979
|
+
|
|
980
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
981
|
+
topic = test_mk_topic_name("0171-concurrent-thread", 1);
|
|
982
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
983
|
+
|
|
984
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
985
|
+
|
|
986
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
987
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
988
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
989
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
990
|
+
|
|
991
|
+
/* Spawn the blocker. It enters consume_batch, takes the gate,
|
|
992
|
+
* and stays inside for ~5s on the empty topic. */
|
|
993
|
+
ba.consumer = consumer;
|
|
994
|
+
rd_atomic32_init(&ba.entering, 0);
|
|
995
|
+
if (thrd_create(&blocker, consume_blocker_thread, &ba) != thrd_success)
|
|
996
|
+
TEST_FAIL("thrd_create failed");
|
|
997
|
+
|
|
998
|
+
/* Wait until the blocker thread has at least reached the point
|
|
999
|
+
* just before its consume_batch call. */
|
|
1000
|
+
while (rd_atomic32_get(&ba.entering) == 0)
|
|
1001
|
+
rd_usleep(10 * 1000, NULL);
|
|
1002
|
+
|
|
1003
|
+
/* The blocker has flipped its `entering` flag but may not yet
|
|
1004
|
+
* have completed the gate acquire inside consume_batch. Probe
|
|
1005
|
+
* in a short polling loop until we either observe __CONFLICT
|
|
1006
|
+
* (gate is held by the blocker — the success case) or exhaust
|
|
1007
|
+
* the budget. Without the loop, slow thread scheduling (e.g.,
|
|
1008
|
+
* Windows CI) lets the main thread win the gate first and the
|
|
1009
|
+
* single-shot assertion flakes. */
|
|
1010
|
+
for (probe_attempt = 0; probe_attempt < probe_max_attempts;
|
|
1011
|
+
probe_attempt++) {
|
|
1012
|
+
rd_kafka_messages_destroy(batch);
|
|
1013
|
+
batch = NULL;
|
|
1014
|
+
err = rd_kafka_share_poll(consumer, 0, &batch);
|
|
1015
|
+
if (err &&
|
|
1016
|
+
rd_kafka_error_code(err) == RD_KAFKA_RESP_ERR__CONFLICT)
|
|
1017
|
+
break; /* observed the conflict — done */
|
|
1018
|
+
if (err) {
|
|
1019
|
+
rd_kafka_error_destroy(err);
|
|
1020
|
+
err = NULL;
|
|
1021
|
+
}
|
|
1022
|
+
rd_usleep(100 * 1000, NULL);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
TEST_ASSERT(
|
|
1026
|
+
err && rd_kafka_error_code(err) == RD_KAFKA_RESP_ERR__CONFLICT,
|
|
1027
|
+
"Expected __CONFLICT from concurrent share_poll while blocker "
|
|
1028
|
+
"thread holds the gate after %d probe attempts; got: %s",
|
|
1029
|
+
probe_attempt + 1,
|
|
1030
|
+
err ? rd_kafka_err2name(rd_kafka_error_code(err)) : "no error");
|
|
1031
|
+
rd_kafka_error_destroy(err);
|
|
1032
|
+
|
|
1033
|
+
TEST_SAY("Gate correctly rejected concurrent caller\n");
|
|
1034
|
+
|
|
1035
|
+
/* Wait for blocker to return (releases the gate). */
|
|
1036
|
+
thrd_join(blocker, NULL);
|
|
1037
|
+
|
|
1038
|
+
/* Sequential ownership transfer: gate is now free, the same
|
|
1039
|
+
* main thread should be able to call share_poll without
|
|
1040
|
+
* hitting the gate. */
|
|
1041
|
+
rd_kafka_messages_destroy(batch);
|
|
1042
|
+
batch = NULL;
|
|
1043
|
+
err = rd_kafka_share_poll(consumer, 0, &batch);
|
|
1044
|
+
TEST_ASSERT(!err, "After blocker returned, gate should be free; got %s",
|
|
1045
|
+
err ? rd_kafka_err2str(rd_kafka_error_code(err))
|
|
1046
|
+
: "no error");
|
|
1047
|
+
if (err)
|
|
1048
|
+
rd_kafka_error_destroy(err);
|
|
1049
|
+
rd_kafka_messages_destroy(batch);
|
|
1050
|
+
batch = NULL;
|
|
1051
|
+
|
|
1052
|
+
TEST_SAY("Sequential ownership transfer works after release\n");
|
|
1053
|
+
|
|
1054
|
+
test_share_consumer_close(consumer);
|
|
1055
|
+
test_share_destroy(consumer);
|
|
1056
|
+
|
|
1057
|
+
TEST_SAY("SUCCESS: Concurrent thread access correctly rejected\n");
|
|
1058
|
+
|
|
1059
|
+
SUB_TEST_PASS();
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* @brief Test that record headers are preserved end-to-end through the
|
|
1065
|
+
* share consumer.
|
|
1066
|
+
*
|
|
1067
|
+
* Produce a single record with three headers and verify the share consumer
|
|
1068
|
+
* receives all of them with the correct values.
|
|
1069
|
+
*/
|
|
1070
|
+
static void do_test_headers_preserved(void) {
|
|
1071
|
+
rd_kafka_share_t *consumer;
|
|
1072
|
+
rd_kafka_messages_t *batch = NULL;
|
|
1073
|
+
rd_kafka_message_t *msg0;
|
|
1074
|
+
const char *topic;
|
|
1075
|
+
const char *group = "share-headers";
|
|
1076
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
1077
|
+
rd_kafka_resp_err_t err;
|
|
1078
|
+
rd_kafka_headers_t *hdrs = NULL;
|
|
1079
|
+
rd_kafka_resp_err_t herr;
|
|
1080
|
+
const void *val;
|
|
1081
|
+
size_t vsz;
|
|
1082
|
+
size_t rcvd = 0;
|
|
1083
|
+
int attempts;
|
|
1084
|
+
|
|
1085
|
+
SUB_TEST();
|
|
1086
|
+
|
|
1087
|
+
TEST_SAY("\n");
|
|
1088
|
+
TEST_SAY("=== Headers preserved through share consumer ===\n");
|
|
1089
|
+
|
|
1090
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
1091
|
+
topic = test_mk_topic_name("0171-headers", 1);
|
|
1092
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
1093
|
+
|
|
1094
|
+
/* Produce a single record with three headers */
|
|
1095
|
+
err = rd_kafka_producev(
|
|
1096
|
+
common_producer, RD_KAFKA_V_TOPIC(topic),
|
|
1097
|
+
RD_KAFKA_V_KEY("hdr-key", 7), RD_KAFKA_V_VALUE("hdr-value", 9),
|
|
1098
|
+
RD_KAFKA_V_HEADER("h1", "v1", 2), RD_KAFKA_V_HEADER("h2", "v2", 2),
|
|
1099
|
+
RD_KAFKA_V_HEADER("h3", "v3", 2), RD_KAFKA_V_END);
|
|
1100
|
+
TEST_ASSERT(!err, "producev failed: %s", rd_kafka_err2name(err));
|
|
1101
|
+
rd_kafka_flush(common_producer, 10 * 1000);
|
|
1102
|
+
|
|
1103
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
1104
|
+
|
|
1105
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
1106
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
1107
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
1108
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
1109
|
+
|
|
1110
|
+
attempts = 30;
|
|
1111
|
+
while (rcvd < 1 && attempts-- > 0) {
|
|
1112
|
+
rd_kafka_error_t *e;
|
|
1113
|
+
|
|
1114
|
+
rd_kafka_messages_destroy(batch);
|
|
1115
|
+
batch = NULL;
|
|
1116
|
+
e = rd_kafka_share_poll(consumer, 2000, &batch);
|
|
1117
|
+
if (e)
|
|
1118
|
+
rd_kafka_error_destroy(e);
|
|
1119
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1120
|
+
}
|
|
1121
|
+
TEST_ASSERT(rcvd == 1, "Expected 1 record, got %zu", rcvd);
|
|
1122
|
+
|
|
1123
|
+
msg0 = rd_kafka_messages_get(batch, 0);
|
|
1124
|
+
herr = rd_kafka_message_headers(msg0, &hdrs);
|
|
1125
|
+
TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR && hdrs,
|
|
1126
|
+
"Failed to get headers: %s", rd_kafka_err2name(herr));
|
|
1127
|
+
|
|
1128
|
+
herr = rd_kafka_header_get_last(hdrs, "h1", &val, &vsz);
|
|
1129
|
+
TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR, "h1 not present: %s",
|
|
1130
|
+
rd_kafka_err2name(herr));
|
|
1131
|
+
TEST_ASSERT(vsz == 2 && memcmp(val, "v1", 2) == 0, "h1 value mismatch");
|
|
1132
|
+
|
|
1133
|
+
herr = rd_kafka_header_get_last(hdrs, "h2", &val, &vsz);
|
|
1134
|
+
TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR, "h2 not present: %s",
|
|
1135
|
+
rd_kafka_err2name(herr));
|
|
1136
|
+
TEST_ASSERT(vsz == 2 && memcmp(val, "v2", 2) == 0, "h2 value mismatch");
|
|
1137
|
+
|
|
1138
|
+
herr = rd_kafka_header_get_last(hdrs, "h3", &val, &vsz);
|
|
1139
|
+
TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR, "h3 not present: %s",
|
|
1140
|
+
rd_kafka_err2name(herr));
|
|
1141
|
+
TEST_ASSERT(vsz == 2 && memcmp(val, "v3", 2) == 0, "h3 value mismatch");
|
|
1142
|
+
|
|
1143
|
+
rd_kafka_messages_destroy(batch);
|
|
1144
|
+
batch = NULL;
|
|
1145
|
+
|
|
1146
|
+
test_share_consumer_close(consumer);
|
|
1147
|
+
test_share_destroy(consumer);
|
|
1148
|
+
|
|
1149
|
+
TEST_SAY("SUCCESS: Headers preserved through share consumer\n");
|
|
1150
|
+
|
|
1151
|
+
SUB_TEST_PASS();
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* @brief Test that a share consumer with a small session-level fetch.max.bytes
|
|
1157
|
+
* receives records one by one per fetch.
|
|
1158
|
+
*
|
|
1159
|
+
* - With fetch.max.bytes=1500 and 1000-byte records each in their own
|
|
1160
|
+
* RecordBatch (custom producer, batch.num.messages=1), every
|
|
1161
|
+
* ShareFetch response can hold at most one record.
|
|
1162
|
+
* - All 100 records are received (consumer correctly loops across
|
|
1163
|
+
* multiple ShareFetch RPCs).
|
|
1164
|
+
* - Every non-empty consume_batch returns exactly one record (one batch
|
|
1165
|
+
* per ShareFetch response, due to the cap).
|
|
1166
|
+
*/
|
|
1167
|
+
static void do_test_fetch_max_bytes_small(void) {
|
|
1168
|
+
const char *group = "share-small-fetch";
|
|
1169
|
+
const char *topic;
|
|
1170
|
+
rd_kafka_share_t *consumer;
|
|
1171
|
+
rd_kafka_conf_t *conf;
|
|
1172
|
+
rd_kafka_conf_t *prod_conf;
|
|
1173
|
+
rd_kafka_t *prod;
|
|
1174
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
1175
|
+
char errstr[512];
|
|
1176
|
+
const int msgcnt = 10;
|
|
1177
|
+
const int msgsize = 1000;
|
|
1178
|
+
int consumed = 0;
|
|
1179
|
+
int attempts;
|
|
1180
|
+
|
|
1181
|
+
SUB_TEST();
|
|
1182
|
+
|
|
1183
|
+
TEST_SAY("\n");
|
|
1184
|
+
TEST_SAY(
|
|
1185
|
+
"=== Small session-level fetch.max.bytes (1500) with 1000-byte "
|
|
1186
|
+
"msgs ===\n");
|
|
1187
|
+
|
|
1188
|
+
topic = test_mk_topic_name("0171-small-fetch", 1);
|
|
1189
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
1190
|
+
|
|
1191
|
+
/* Custom producer that emits one RecordBatch per message so the
|
|
1192
|
+
* broker has many small batches to choose from when responding to
|
|
1193
|
+
* ShareFetch */
|
|
1194
|
+
test_conf_init(&prod_conf, NULL, 60);
|
|
1195
|
+
rd_kafka_conf_set_dr_msg_cb(prod_conf, test_dr_msg_cb);
|
|
1196
|
+
rd_kafka_conf_set(prod_conf, "linger.ms", "0", errstr, sizeof(errstr));
|
|
1197
|
+
rd_kafka_conf_set(prod_conf, "batch.num.messages", "1", errstr,
|
|
1198
|
+
sizeof(errstr));
|
|
1199
|
+
prod = test_create_handle(RD_KAFKA_PRODUCER, prod_conf);
|
|
1200
|
+
|
|
1201
|
+
test_produce_msgs2(prod, topic, 0, 0, 0, msgcnt, NULL, msgsize);
|
|
1202
|
+
rd_kafka_flush(prod, 30 * 1000);
|
|
1203
|
+
rd_kafka_destroy(prod);
|
|
1204
|
+
|
|
1205
|
+
/* Create share consumer with a small session-level fetch cap.
|
|
1206
|
+
* For share consumers `message.max.bytes` does not floor
|
|
1207
|
+
* `fetch.max.bytes` (and is rejected), so set `fetch.max.bytes`
|
|
1208
|
+
* directly to 1500. */
|
|
1209
|
+
test_conf_init(&conf, NULL, 60);
|
|
1210
|
+
rd_kafka_conf_set(conf, "group.id", group, errstr, sizeof(errstr));
|
|
1211
|
+
rd_kafka_conf_set(conf, "fetch.max.bytes", "1500", errstr,
|
|
1212
|
+
sizeof(errstr));
|
|
1213
|
+
|
|
1214
|
+
consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
|
|
1215
|
+
TEST_ASSERT(consumer, "Failed to create share consumer: %s", errstr);
|
|
1216
|
+
|
|
1217
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
1218
|
+
|
|
1219
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
1220
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
1221
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
1222
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
1223
|
+
|
|
1224
|
+
/* All 100 records must arrive one by one per fetch */
|
|
1225
|
+
attempts = 30;
|
|
1226
|
+
while (consumed < msgcnt && attempts-- > 0) {
|
|
1227
|
+
int batch_cnt = 0;
|
|
1228
|
+
test_share_poll(consumer, 2000, NULL, 0, &batch_cnt);
|
|
1229
|
+
consumed += batch_cnt;
|
|
1230
|
+
if (batch_cnt > 0) {
|
|
1231
|
+
TEST_ASSERT(batch_cnt == 1,
|
|
1232
|
+
"Expected 1 record per batch with small "
|
|
1233
|
+
"fetch.max.bytes, "
|
|
1234
|
+
"got %d",
|
|
1235
|
+
batch_cnt);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
TEST_ASSERT(consumed == msgcnt,
|
|
1240
|
+
"Expected %d records with small fetch.max.bytes, "
|
|
1241
|
+
"consumed %d",
|
|
1242
|
+
msgcnt, consumed);
|
|
1243
|
+
|
|
1244
|
+
TEST_SAY("SUCCESS: small fetch.max.bytes - consumed %d records\n",
|
|
1245
|
+
consumed);
|
|
1246
|
+
|
|
1247
|
+
test_share_consumer_close(consumer);
|
|
1248
|
+
test_share_destroy(consumer);
|
|
1249
|
+
|
|
1250
|
+
SUB_TEST_PASS();
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* @brief Verify the share consumer receives records that individually
|
|
1256
|
+
* exceed fetch.max.bytes, the broker doesnt stall consumption.
|
|
1257
|
+
*
|
|
1258
|
+
* What is tested:
|
|
1259
|
+
* - Produce a mix of 10 KB and 500 KB records on a topic.
|
|
1260
|
+
* - Configure the consumer with fetch.max.bytes=1500 — well below every
|
|
1261
|
+
* record's on-wire size.
|
|
1262
|
+
* - All records must still arrive. The broker is required to return the
|
|
1263
|
+
* first batch in a non-empty partition even when it exceeds the cap
|
|
1264
|
+
* to guarantee consumer progress; the client side must accept these
|
|
1265
|
+
* oversized responses.
|
|
1266
|
+
*/
|
|
1267
|
+
static void do_test_record_larger_than_fetch_max_bytes(void) {
|
|
1268
|
+
const char *group = "share-large-record";
|
|
1269
|
+
const char *topic;
|
|
1270
|
+
rd_kafka_share_t *consumer;
|
|
1271
|
+
rd_kafka_conf_t *conf;
|
|
1272
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
1273
|
+
char errstr[512];
|
|
1274
|
+
const int MAX_BYTES = 100000;
|
|
1275
|
+
const int small_cnt = 5;
|
|
1276
|
+
const int large_cnt = 2;
|
|
1277
|
+
const int total = small_cnt + large_cnt;
|
|
1278
|
+
int consumed = 0;
|
|
1279
|
+
int attempts;
|
|
1280
|
+
|
|
1281
|
+
SUB_TEST();
|
|
1282
|
+
|
|
1283
|
+
TEST_SAY("\n");
|
|
1284
|
+
TEST_SAY("=== Records larger than fetch.message.max.bytes (%d) ===\n",
|
|
1285
|
+
MAX_BYTES);
|
|
1286
|
+
|
|
1287
|
+
topic = test_mk_topic_name("0171-large-record", 1);
|
|
1288
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
1289
|
+
|
|
1290
|
+
/* Produce small messages then large ones */
|
|
1291
|
+
test_produce_msgs2(common_producer, topic, 0, 0, 0, small_cnt, NULL,
|
|
1292
|
+
MAX_BYTES / 10);
|
|
1293
|
+
test_produce_msgs2(common_producer, topic, 0, 0, small_cnt, large_cnt,
|
|
1294
|
+
NULL, MAX_BYTES * 5);
|
|
1295
|
+
rd_kafka_flush(common_producer, 60 * 1000);
|
|
1296
|
+
|
|
1297
|
+
test_conf_init(&conf, NULL, 60);
|
|
1298
|
+
rd_kafka_conf_set(conf, "group.id", group, errstr, sizeof(errstr));
|
|
1299
|
+
rd_kafka_conf_set(conf, "fetch.max.bytes", "1500", errstr,
|
|
1300
|
+
sizeof(errstr));
|
|
1301
|
+
|
|
1302
|
+
consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
|
|
1303
|
+
TEST_ASSERT(consumer, "Failed to create share consumer: %s", errstr);
|
|
1304
|
+
|
|
1305
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
1306
|
+
|
|
1307
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
1308
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
1309
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
1310
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
1311
|
+
|
|
1312
|
+
attempts = 30;
|
|
1313
|
+
while (consumed < total && attempts-- > 0) {
|
|
1314
|
+
int batch_cnt = 0;
|
|
1315
|
+
test_share_poll(consumer, 3000, NULL, 0, &batch_cnt);
|
|
1316
|
+
consumed += batch_cnt;
|
|
1317
|
+
if (batch_cnt > 0)
|
|
1318
|
+
TEST_SAY("Progress: %d/%d\n", consumed, total);
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
TEST_ASSERT(consumed == total,
|
|
1322
|
+
"Expected %d records (incl. oversized), consumed %d", total,
|
|
1323
|
+
consumed);
|
|
1324
|
+
|
|
1325
|
+
TEST_SAY("SUCCESS: oversized record - consumed %d records\n", consumed);
|
|
1326
|
+
|
|
1327
|
+
test_share_consumer_close(consumer);
|
|
1328
|
+
test_share_destroy(consumer);
|
|
1329
|
+
|
|
1330
|
+
SUB_TEST_PASS();
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
/**
|
|
1335
|
+
* @brief Test that records held by a consumer that closes mid-flight
|
|
1336
|
+
* are released back to other consumers in the group.
|
|
1337
|
+
*
|
|
1338
|
+
* Create 4 share consumers in the same group, produce a known set of
|
|
1339
|
+
* records, have consumer 0 fetch a batch without acknowledging and then
|
|
1340
|
+
* close. The other consumers should eventually receive every record
|
|
1341
|
+
* (including the ones released by consumer 0).
|
|
1342
|
+
*/
|
|
1343
|
+
static void do_test_consumer_close_releases_to_group(void) {
|
|
1344
|
+
const char *group = "share-close-releases";
|
|
1345
|
+
const char *topic;
|
|
1346
|
+
rd_kafka_share_t *consumers[4];
|
|
1347
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
1348
|
+
rd_kafka_messages_t *batch = NULL;
|
|
1349
|
+
const int total_msgs = 200;
|
|
1350
|
+
int c0_fetched = 0;
|
|
1351
|
+
int total_consumed = 0;
|
|
1352
|
+
int per_consumer[4] = {0};
|
|
1353
|
+
int attempts;
|
|
1354
|
+
int i;
|
|
1355
|
+
|
|
1356
|
+
SUB_TEST();
|
|
1357
|
+
|
|
1358
|
+
TEST_SAY("\n");
|
|
1359
|
+
TEST_SAY(
|
|
1360
|
+
"=== Failing consumer releases unacked records to group ===\n");
|
|
1361
|
+
|
|
1362
|
+
topic = test_mk_topic_name("0171-close-release", 1);
|
|
1363
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
1364
|
+
test_produce_msgs_simple(common_producer, topic, 0, total_msgs);
|
|
1365
|
+
|
|
1366
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
1367
|
+
|
|
1368
|
+
for (i = 0; i < 4; i++)
|
|
1369
|
+
consumers[i] = test_create_share_consumer(group, NULL);
|
|
1370
|
+
|
|
1371
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
1372
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
1373
|
+
for (i = 0; i < 4; i++)
|
|
1374
|
+
rd_kafka_share_subscribe(consumers[i], subs);
|
|
1375
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
1376
|
+
|
|
1377
|
+
/* Consumer 0 fetches a batch WITHOUT acknowledging, then closes. */
|
|
1378
|
+
attempts = 30;
|
|
1379
|
+
while (c0_fetched == 0 && attempts-- > 0) {
|
|
1380
|
+
size_t rcvd = 0;
|
|
1381
|
+
size_t m;
|
|
1382
|
+
rd_kafka_error_t *err;
|
|
1383
|
+
|
|
1384
|
+
rd_kafka_messages_destroy(batch);
|
|
1385
|
+
batch = NULL;
|
|
1386
|
+
err = rd_kafka_share_poll(consumers[0], 2000, &batch);
|
|
1387
|
+
if (err) {
|
|
1388
|
+
rd_kafka_error_destroy(err);
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1393
|
+
for (m = 0; m < rcvd; m++) {
|
|
1394
|
+
rd_kafka_message_t *msg =
|
|
1395
|
+
rd_kafka_messages_get(batch, m);
|
|
1396
|
+
if (!msg->err)
|
|
1397
|
+
c0_fetched++;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
rd_kafka_messages_destroy(batch);
|
|
1402
|
+
batch = NULL;
|
|
1403
|
+
TEST_ASSERT(c0_fetched > 0,
|
|
1404
|
+
"Consumer 0 should have fetched some records, got %d",
|
|
1405
|
+
c0_fetched);
|
|
1406
|
+
|
|
1407
|
+
TEST_SAY("Consumer 0 fetched %d records (unacked), closing now\n",
|
|
1408
|
+
c0_fetched);
|
|
1409
|
+
/* Close without acknowledging - records should be released */
|
|
1410
|
+
test_share_consumer_close(consumers[0]);
|
|
1411
|
+
test_share_destroy(consumers[0]);
|
|
1412
|
+
consumers[0] = NULL;
|
|
1413
|
+
|
|
1414
|
+
/* Drain the rest with consumers 1..3 */
|
|
1415
|
+
attempts = 200;
|
|
1416
|
+
while (total_consumed < total_msgs && attempts-- > 0) {
|
|
1417
|
+
for (i = 1; i < 4; i++) {
|
|
1418
|
+
size_t rcvd = 0;
|
|
1419
|
+
size_t m;
|
|
1420
|
+
rd_kafka_error_t *err;
|
|
1421
|
+
|
|
1422
|
+
rd_kafka_messages_destroy(batch);
|
|
1423
|
+
batch = NULL;
|
|
1424
|
+
err = rd_kafka_share_poll(consumers[i], 1000, &batch);
|
|
1425
|
+
if (err) {
|
|
1426
|
+
rd_kafka_error_destroy(err);
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1431
|
+
for (m = 0; m < rcvd; m++) {
|
|
1432
|
+
rd_kafka_message_t *msg =
|
|
1433
|
+
rd_kafka_messages_get(batch, m);
|
|
1434
|
+
if (!msg->err) {
|
|
1435
|
+
per_consumer[i]++;
|
|
1436
|
+
total_consumed++;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
rd_kafka_messages_destroy(batch);
|
|
1443
|
+
batch = NULL;
|
|
1444
|
+
|
|
1445
|
+
TEST_SAY(
|
|
1446
|
+
"Distribution: c0=%d (lost on close), c1=%d, c2=%d, c3=%d "
|
|
1447
|
+
"(total %d)\n",
|
|
1448
|
+
c0_fetched, per_consumer[1], per_consumer[2], per_consumer[3],
|
|
1449
|
+
total_consumed);
|
|
1450
|
+
|
|
1451
|
+
TEST_ASSERT(total_consumed == total_msgs,
|
|
1452
|
+
"Expected exactly %d records on the survivors after "
|
|
1453
|
+
"consumer 0's records were released, got %d",
|
|
1454
|
+
total_msgs, total_consumed);
|
|
1455
|
+
|
|
1456
|
+
for (i = 1; i < 4; i++) {
|
|
1457
|
+
test_share_consumer_close(consumers[i]);
|
|
1458
|
+
test_share_destroy(consumers[i]);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
TEST_SAY("SUCCESS: closed consumer's records redelivered to group\n");
|
|
1462
|
+
|
|
1463
|
+
SUB_TEST_PASS();
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* @brief Partition count increased during consume.
|
|
1469
|
+
*
|
|
1470
|
+
* 1 consumer, topic starts with 2 partitions, grown to 5.
|
|
1471
|
+
* Produce N records to {0,1} -> consume -> grow to 5 -> produce N to
|
|
1472
|
+
* {2,3,4} -> continue consuming -> expect 5*N total.
|
|
1473
|
+
*/
|
|
1474
|
+
static void do_test_partition_increase_during_consume(void) {
|
|
1475
|
+
const char *group = "share-partition-increase";
|
|
1476
|
+
const char *topic;
|
|
1477
|
+
rd_kafka_share_t *consumer;
|
|
1478
|
+
rd_kafka_conf_t *conf;
|
|
1479
|
+
rd_kafka_conf_t *prod_conf;
|
|
1480
|
+
rd_kafka_t *producer;
|
|
1481
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
1482
|
+
rd_kafka_messages_t *batch = NULL;
|
|
1483
|
+
const int msgs_per_partition = 200;
|
|
1484
|
+
const int initial_partitions = 2;
|
|
1485
|
+
const int grown_partitions = 5;
|
|
1486
|
+
const int initial_expected = initial_partitions * msgs_per_partition;
|
|
1487
|
+
const int total_expected = grown_partitions * msgs_per_partition;
|
|
1488
|
+
int consumed = 0;
|
|
1489
|
+
int attempts;
|
|
1490
|
+
int p;
|
|
1491
|
+
rd_kafka_resp_err_t cp_err;
|
|
1492
|
+
char errstr[512];
|
|
1493
|
+
|
|
1494
|
+
SUB_TEST();
|
|
1495
|
+
|
|
1496
|
+
TEST_SAY("\n");
|
|
1497
|
+
TEST_SAY("=== Partition count increase during consume (%d -> %d) ===\n",
|
|
1498
|
+
initial_partitions, grown_partitions);
|
|
1499
|
+
|
|
1500
|
+
topic = test_mk_topic_name("0171-partition-increase", 1);
|
|
1501
|
+
test_create_topic_wait_exists(NULL, topic, initial_partitions, -1,
|
|
1502
|
+
60 * 1000);
|
|
1503
|
+
|
|
1504
|
+
/* Custom producer with a short metadata refresh interval so its
|
|
1505
|
+
* cache picks up the post-CreatePartitions partition count on its
|
|
1506
|
+
* own — avoids the explicit test_wait_metadata_update probe. */
|
|
1507
|
+
test_conf_init(&prod_conf, NULL, 60);
|
|
1508
|
+
rd_kafka_conf_set_dr_msg_cb(prod_conf, test_dr_msg_cb);
|
|
1509
|
+
rd_kafka_conf_set(prod_conf, "topic.metadata.refresh.interval.ms",
|
|
1510
|
+
"500", errstr, sizeof(errstr));
|
|
1511
|
+
producer = test_create_handle(RD_KAFKA_PRODUCER, prod_conf);
|
|
1512
|
+
|
|
1513
|
+
/* Produce to initial partitions only */
|
|
1514
|
+
for (p = 0; p < initial_partitions; p++)
|
|
1515
|
+
test_produce_msgs_simple(producer, topic, p,
|
|
1516
|
+
msgs_per_partition);
|
|
1517
|
+
|
|
1518
|
+
/* Lower the consumer's metadata refresh interval (default 5 min) so
|
|
1519
|
+
* it picks up the new partition count quickly without a long
|
|
1520
|
+
* hardcoded sleep below. */
|
|
1521
|
+
test_conf_init(&conf, NULL, 60);
|
|
1522
|
+
rd_kafka_conf_set(conf, "group.id", group, errstr, sizeof(errstr));
|
|
1523
|
+
rd_kafka_conf_set(conf, "topic.metadata.refresh.interval.ms", "500",
|
|
1524
|
+
errstr, sizeof(errstr));
|
|
1525
|
+
consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
|
|
1526
|
+
TEST_ASSERT(consumer, "Failed to create share consumer: %s", errstr);
|
|
1527
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
1528
|
+
|
|
1529
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
1530
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
1531
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
1532
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
1533
|
+
|
|
1534
|
+
/* Consume from the initial partitions */
|
|
1535
|
+
attempts = 100;
|
|
1536
|
+
while (consumed < initial_expected && attempts-- > 0) {
|
|
1537
|
+
size_t rcvd = 0;
|
|
1538
|
+
size_t m;
|
|
1539
|
+
rd_kafka_error_t *err;
|
|
1540
|
+
|
|
1541
|
+
rd_kafka_messages_destroy(batch);
|
|
1542
|
+
batch = NULL;
|
|
1543
|
+
err = rd_kafka_share_poll(consumer, 1000, &batch);
|
|
1544
|
+
if (err) {
|
|
1545
|
+
rd_kafka_error_destroy(err);
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1550
|
+
for (m = 0; m < rcvd; m++) {
|
|
1551
|
+
rd_kafka_message_t *msg =
|
|
1552
|
+
rd_kafka_messages_get(batch, m);
|
|
1553
|
+
if (!msg->err)
|
|
1554
|
+
consumed++;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
TEST_SAY("Pre-grow consumed: %d/%d\n", consumed, initial_expected);
|
|
1558
|
+
TEST_ASSERT(consumed == initial_expected,
|
|
1559
|
+
"Expected >= %d msgs from initial partitions, got %d",
|
|
1560
|
+
initial_expected, consumed);
|
|
1561
|
+
|
|
1562
|
+
/* Grow topic partitions from 2 -> 5 */
|
|
1563
|
+
TEST_SAY("Growing partitions to %d via CreatePartitions\n",
|
|
1564
|
+
grown_partitions);
|
|
1565
|
+
cp_err = test_CreatePartitions_simple(common_admin, NULL, topic,
|
|
1566
|
+
grown_partitions, NULL);
|
|
1567
|
+
TEST_ASSERT(!cp_err, "CreatePartitions failed: %s",
|
|
1568
|
+
rd_kafka_err2str(cp_err));
|
|
1569
|
+
|
|
1570
|
+
/* Both producer and consumer have topic.metadata.refresh.interval
|
|
1571
|
+
* .ms=500, so their local metadata caches pick up the new partition
|
|
1572
|
+
* count within ~500 ms via the background refresh. One second is
|
|
1573
|
+
* plenty for both: the producer can route to the new partitions,
|
|
1574
|
+
* and the consumer can be assigned them on the next
|
|
1575
|
+
* ShareGroupHeartbeat cycle. */
|
|
1576
|
+
rd_sleep(1);
|
|
1577
|
+
|
|
1578
|
+
/* Produce to the newly added partitions */
|
|
1579
|
+
for (p = initial_partitions; p < grown_partitions; p++)
|
|
1580
|
+
test_produce_msgs_simple(producer, topic, p,
|
|
1581
|
+
msgs_per_partition);
|
|
1582
|
+
|
|
1583
|
+
/* Continue consuming, expecting records from new partitions */
|
|
1584
|
+
attempts = 200;
|
|
1585
|
+
while (consumed < total_expected && attempts-- > 0) {
|
|
1586
|
+
size_t rcvd = 0;
|
|
1587
|
+
size_t m;
|
|
1588
|
+
rd_kafka_error_t *err;
|
|
1589
|
+
|
|
1590
|
+
rd_kafka_messages_destroy(batch);
|
|
1591
|
+
batch = NULL;
|
|
1592
|
+
err = rd_kafka_share_poll(consumer, 1000, &batch);
|
|
1593
|
+
if (err) {
|
|
1594
|
+
rd_kafka_error_destroy(err);
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1599
|
+
for (m = 0; m < rcvd; m++) {
|
|
1600
|
+
rd_kafka_message_t *msg =
|
|
1601
|
+
rd_kafka_messages_get(batch, m);
|
|
1602
|
+
if (!msg->err)
|
|
1603
|
+
consumed++;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
rd_kafka_messages_destroy(batch);
|
|
1608
|
+
batch = NULL;
|
|
1609
|
+
|
|
1610
|
+
TEST_ASSERT(consumed == total_expected,
|
|
1611
|
+
"Expected exactly %d total records after partition grow, "
|
|
1612
|
+
"got %d",
|
|
1613
|
+
total_expected, consumed);
|
|
1614
|
+
|
|
1615
|
+
TEST_SAY("SUCCESS: partition count grow %d->%d, consumed %d records\n",
|
|
1616
|
+
initial_partitions, grown_partitions, consumed);
|
|
1617
|
+
|
|
1618
|
+
test_share_consumer_close(consumer);
|
|
1619
|
+
test_share_destroy(consumer);
|
|
1620
|
+
rd_kafka_destroy(producer);
|
|
1621
|
+
|
|
1622
|
+
SUB_TEST_PASS();
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* @brief Zero-byte payload and zero-byte key.
|
|
1628
|
+
*
|
|
1629
|
+
* Verifies that the share consumer correctly preserves zero-length
|
|
1630
|
+
* key/value buffers (len == 0, pointer non-NULL) and NULL key/value.
|
|
1631
|
+
*/
|
|
1632
|
+
static void do_test_zero_byte_payload_and_key(void) {
|
|
1633
|
+
const char *group = "share-zero-byte";
|
|
1634
|
+
const char *topic;
|
|
1635
|
+
rd_kafka_share_t *consumer;
|
|
1636
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
1637
|
+
rd_kafka_messages_t *batch = NULL;
|
|
1638
|
+
rd_kafka_resp_err_t err;
|
|
1639
|
+
size_t rcvd = 0;
|
|
1640
|
+
int attempts;
|
|
1641
|
+
int saw_empty_kv = 0;
|
|
1642
|
+
int saw_key_only = 0;
|
|
1643
|
+
int saw_value_only = 0;
|
|
1644
|
+
int saw_null_kv = 0;
|
|
1645
|
+
size_t i;
|
|
1646
|
+
rd_bool_t key_null;
|
|
1647
|
+
rd_bool_t key_empty;
|
|
1648
|
+
rd_bool_t val_null;
|
|
1649
|
+
rd_bool_t val_empty;
|
|
1650
|
+
rd_bool_t key_is_empty_or_null;
|
|
1651
|
+
rd_bool_t val_is_empty_or_null;
|
|
1652
|
+
|
|
1653
|
+
SUB_TEST();
|
|
1654
|
+
|
|
1655
|
+
TEST_SAY("\n");
|
|
1656
|
+
TEST_SAY("=== Zero-byte payload and key ===\n");
|
|
1657
|
+
|
|
1658
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
1659
|
+
topic = test_mk_topic_name("0171-zero-byte", 1);
|
|
1660
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
1661
|
+
|
|
1662
|
+
/* Empty key, empty value (zero-length, non-NULL pointers) */
|
|
1663
|
+
err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
|
|
1664
|
+
RD_KAFKA_V_KEY("", 0), RD_KAFKA_V_VALUE("", 0),
|
|
1665
|
+
RD_KAFKA_V_END);
|
|
1666
|
+
TEST_ASSERT(!err, "produce empty kv failed: %s",
|
|
1667
|
+
rd_kafka_err2name(err));
|
|
1668
|
+
|
|
1669
|
+
/* Non-empty key, empty value */
|
|
1670
|
+
err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
|
|
1671
|
+
RD_KAFKA_V_KEY("k", 1), RD_KAFKA_V_VALUE("", 0),
|
|
1672
|
+
RD_KAFKA_V_END);
|
|
1673
|
+
TEST_ASSERT(!err, "produce key-only failed: %s",
|
|
1674
|
+
rd_kafka_err2name(err));
|
|
1675
|
+
|
|
1676
|
+
/* Empty key, non-empty value */
|
|
1677
|
+
err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
|
|
1678
|
+
RD_KAFKA_V_KEY("", 0), RD_KAFKA_V_VALUE("v", 1),
|
|
1679
|
+
RD_KAFKA_V_END);
|
|
1680
|
+
TEST_ASSERT(!err, "produce value-only failed: %s",
|
|
1681
|
+
rd_kafka_err2name(err));
|
|
1682
|
+
|
|
1683
|
+
/* NULL key, NULL value */
|
|
1684
|
+
err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
|
|
1685
|
+
RD_KAFKA_V_KEY(NULL, 0),
|
|
1686
|
+
RD_KAFKA_V_VALUE(NULL, 0), RD_KAFKA_V_END);
|
|
1687
|
+
TEST_ASSERT(!err, "produce null kv failed: %s", rd_kafka_err2name(err));
|
|
1688
|
+
|
|
1689
|
+
rd_kafka_flush(common_producer, 10 * 1000);
|
|
1690
|
+
|
|
1691
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
1692
|
+
|
|
1693
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
1694
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
1695
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
1696
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
1697
|
+
|
|
1698
|
+
attempts = 30;
|
|
1699
|
+
while (rcvd < 4 && attempts-- > 0) {
|
|
1700
|
+
size_t batch_rcvd = 0;
|
|
1701
|
+
rd_kafka_error_t *e;
|
|
1702
|
+
|
|
1703
|
+
rd_kafka_messages_destroy(batch);
|
|
1704
|
+
batch = NULL;
|
|
1705
|
+
e = rd_kafka_share_poll(consumer, 2000, &batch);
|
|
1706
|
+
if (e)
|
|
1707
|
+
rd_kafka_error_destroy(e);
|
|
1708
|
+
batch_rcvd = rd_kafka_messages_count(batch);
|
|
1709
|
+
|
|
1710
|
+
for (i = 0; i < batch_rcvd; i++) {
|
|
1711
|
+
rd_kafka_message_t *msg =
|
|
1712
|
+
rd_kafka_messages_get(batch, i);
|
|
1713
|
+
|
|
1714
|
+
key_null = msg->key == NULL;
|
|
1715
|
+
key_empty = msg->key_len == 0 && !key_null;
|
|
1716
|
+
val_null = msg->payload == NULL;
|
|
1717
|
+
val_empty = msg->len == 0 && !val_null;
|
|
1718
|
+
|
|
1719
|
+
/* Brokers may normalize empty buffers to NULL; accept
|
|
1720
|
+
* either. */
|
|
1721
|
+
key_is_empty_or_null = key_null || key_empty;
|
|
1722
|
+
val_is_empty_or_null = val_null || val_empty;
|
|
1723
|
+
|
|
1724
|
+
if (key_is_empty_or_null && val_is_empty_or_null) {
|
|
1725
|
+
/* Could be "empty kv" or "null kv" record -
|
|
1726
|
+
* count both as the "no content" class. */
|
|
1727
|
+
if (key_null && val_null)
|
|
1728
|
+
saw_null_kv++;
|
|
1729
|
+
else
|
|
1730
|
+
saw_empty_kv++;
|
|
1731
|
+
} else if (!key_is_empty_or_null &&
|
|
1732
|
+
val_is_empty_or_null) {
|
|
1733
|
+
TEST_ASSERT(msg->key_len == 1 &&
|
|
1734
|
+
memcmp(msg->key, "k", 1) == 0,
|
|
1735
|
+
"key-only record: key mismatch");
|
|
1736
|
+
saw_key_only++;
|
|
1737
|
+
} else if (key_is_empty_or_null &&
|
|
1738
|
+
!val_is_empty_or_null) {
|
|
1739
|
+
TEST_ASSERT(
|
|
1740
|
+
msg->len == 1 &&
|
|
1741
|
+
memcmp(msg->payload, "v", 1) == 0,
|
|
1742
|
+
"value-only record: value mismatch");
|
|
1743
|
+
saw_value_only++;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
rcvd += batch_rcvd;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
rd_kafka_messages_destroy(batch);
|
|
1750
|
+
batch = NULL;
|
|
1751
|
+
|
|
1752
|
+
TEST_ASSERT(rcvd == 4, "Expected 4 records, got %zu", rcvd);
|
|
1753
|
+
|
|
1754
|
+
TEST_SAY("Counts: empty_kv=%d null_kv=%d key_only=%d value_only=%d\n",
|
|
1755
|
+
saw_empty_kv, saw_null_kv, saw_key_only, saw_value_only);
|
|
1756
|
+
|
|
1757
|
+
TEST_ASSERT(saw_empty_kv + saw_null_kv == 2,
|
|
1758
|
+
"Expected 2 'no content' records, got %d",
|
|
1759
|
+
saw_empty_kv + saw_null_kv);
|
|
1760
|
+
TEST_ASSERT(saw_key_only == 1, "Expected 1 key-only record, got %d",
|
|
1761
|
+
saw_key_only);
|
|
1762
|
+
TEST_ASSERT(saw_value_only == 1, "Expected 1 value-only record, got %d",
|
|
1763
|
+
saw_value_only);
|
|
1764
|
+
|
|
1765
|
+
test_share_consumer_close(consumer);
|
|
1766
|
+
test_share_destroy(consumer);
|
|
1767
|
+
|
|
1768
|
+
TEST_SAY("SUCCESS: zero-byte payload/key preserved\n");
|
|
1769
|
+
|
|
1770
|
+
SUB_TEST_PASS();
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* @brief Consume + ack a round of messages produced with the given
|
|
1776
|
+
* compression codec.
|
|
1777
|
+
*/
|
|
1778
|
+
static void do_test_compression_codec(const char *codec) {
|
|
1779
|
+
const char *group_prefix = "share-codec-";
|
|
1780
|
+
char group[128];
|
|
1781
|
+
const char *topic;
|
|
1782
|
+
rd_kafka_share_t *consumer;
|
|
1783
|
+
rd_kafka_conf_t *conf;
|
|
1784
|
+
rd_kafka_t *producer;
|
|
1785
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
1786
|
+
rd_kafka_messages_t *batch = NULL;
|
|
1787
|
+
const int msg_cnt = 200;
|
|
1788
|
+
const int msg_sz = 256;
|
|
1789
|
+
int consumed = 0;
|
|
1790
|
+
int attempts;
|
|
1791
|
+
|
|
1792
|
+
TEST_SAY("\n");
|
|
1793
|
+
TEST_SAY("=== Compression codec: %s ===\n", codec);
|
|
1794
|
+
|
|
1795
|
+
rd_snprintf(group, sizeof(group), "%s%s", group_prefix, codec);
|
|
1796
|
+
topic = test_mk_topic_name(codec, 1);
|
|
1797
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
1798
|
+
|
|
1799
|
+
/* Producer configured with the requested compression codec.
|
|
1800
|
+
* test_produce_msgs2 → test_wait_delivery uses a counter that's
|
|
1801
|
+
* decremented inside test_dr_msg_cb, so the dr callback must
|
|
1802
|
+
* be wired up explicitly when we create our own producer (only
|
|
1803
|
+
* test_create_producer() sets it for us). */
|
|
1804
|
+
test_conf_init(&conf, NULL, 60);
|
|
1805
|
+
test_conf_set(conf, "compression.type", codec);
|
|
1806
|
+
rd_kafka_conf_set_dr_msg_cb(conf, test_dr_msg_cb);
|
|
1807
|
+
producer = test_create_handle(RD_KAFKA_PRODUCER, conf);
|
|
1808
|
+
|
|
1809
|
+
test_produce_msgs2(producer, topic, 0, 0, 0, msg_cnt, NULL, msg_sz);
|
|
1810
|
+
rd_kafka_flush(producer, 30 * 1000);
|
|
1811
|
+
rd_kafka_destroy(producer);
|
|
1812
|
+
|
|
1813
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
1814
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
1815
|
+
|
|
1816
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
1817
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
1818
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
1819
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
1820
|
+
|
|
1821
|
+
attempts = 100;
|
|
1822
|
+
while (consumed < msg_cnt && attempts-- > 0) {
|
|
1823
|
+
size_t rcvd = 0;
|
|
1824
|
+
size_t m;
|
|
1825
|
+
rd_kafka_error_t *err;
|
|
1826
|
+
|
|
1827
|
+
rd_kafka_messages_destroy(batch);
|
|
1828
|
+
batch = NULL;
|
|
1829
|
+
err = rd_kafka_share_poll(consumer, 2000, &batch);
|
|
1830
|
+
if (err) {
|
|
1831
|
+
rd_kafka_error_destroy(err);
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1836
|
+
for (m = 0; m < rcvd; m++) {
|
|
1837
|
+
rd_kafka_message_t *msg =
|
|
1838
|
+
rd_kafka_messages_get(batch, m);
|
|
1839
|
+
if (!msg->err) {
|
|
1840
|
+
TEST_ASSERT(msg->len == (size_t)msg_sz,
|
|
1841
|
+
"[%s] msg size %zu != expected %d",
|
|
1842
|
+
codec, msg->len, msg_sz);
|
|
1843
|
+
consumed++;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
rd_kafka_messages_destroy(batch);
|
|
1849
|
+
batch = NULL;
|
|
1850
|
+
|
|
1851
|
+
TEST_ASSERT(consumed == msg_cnt,
|
|
1852
|
+
"[%s] expected %d records, consumed %d", codec, msg_cnt,
|
|
1853
|
+
consumed);
|
|
1854
|
+
|
|
1855
|
+
TEST_SAY("SUCCESS: codec %s - consumed %d records\n", codec, consumed);
|
|
1856
|
+
|
|
1857
|
+
test_share_consumer_close(consumer);
|
|
1858
|
+
test_share_destroy(consumer);
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* @brief Run consume+ack roundtrip for every supported producer
|
|
1864
|
+
* compression codec.
|
|
1865
|
+
*/
|
|
1866
|
+
static void do_test_all_compression_codecs(void) {
|
|
1867
|
+
SUB_TEST();
|
|
1868
|
+
do_test_compression_codec("none");
|
|
1869
|
+
do_test_compression_codec("gzip");
|
|
1870
|
+
do_test_compression_codec("snappy");
|
|
1871
|
+
do_test_compression_codec("lz4");
|
|
1872
|
+
do_test_compression_codec("zstd");
|
|
1873
|
+
SUB_TEST_PASS();
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
|
|
1877
|
+
/**
|
|
1878
|
+
* @brief Records with 100 headers and one very large header value.
|
|
1879
|
+
*
|
|
1880
|
+
* Verifies the share consumer preserves a large header count and large
|
|
1881
|
+
* per-header value sizes end-to-end.
|
|
1882
|
+
*/
|
|
1883
|
+
static void do_test_many_and_large_headers(void) {
|
|
1884
|
+
const char *group = "share-many-headers";
|
|
1885
|
+
const char *topic;
|
|
1886
|
+
rd_kafka_share_t *consumer;
|
|
1887
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
1888
|
+
rd_kafka_messages_t *batch = NULL;
|
|
1889
|
+
rd_kafka_resp_err_t err;
|
|
1890
|
+
const int hdr_cnt = 100;
|
|
1891
|
+
const size_t big_hdr_size = 64 * 1024;
|
|
1892
|
+
char *big_hdr_value;
|
|
1893
|
+
size_t rcvd = 0;
|
|
1894
|
+
int attempts;
|
|
1895
|
+
size_t i;
|
|
1896
|
+
rd_bool_t verified_many = rd_false;
|
|
1897
|
+
rd_bool_t verified_big = rd_false;
|
|
1898
|
+
rd_kafka_headers_t *hdrs;
|
|
1899
|
+
char name[32];
|
|
1900
|
+
char value[32];
|
|
1901
|
+
rd_kafka_headers_t *msg_hdrs = NULL;
|
|
1902
|
+
rd_kafka_resp_err_t herr;
|
|
1903
|
+
size_t n;
|
|
1904
|
+
size_t j;
|
|
1905
|
+
char expected[32];
|
|
1906
|
+
const void *val;
|
|
1907
|
+
size_t vsz;
|
|
1908
|
+
size_t k;
|
|
1909
|
+
const char *bytes;
|
|
1910
|
+
char expected_byte;
|
|
1911
|
+
|
|
1912
|
+
SUB_TEST();
|
|
1913
|
+
|
|
1914
|
+
TEST_SAY("\n");
|
|
1915
|
+
TEST_SAY(
|
|
1916
|
+
"=== Many headers (%d) and one large header value (%zu B) ===\n",
|
|
1917
|
+
hdr_cnt, big_hdr_size);
|
|
1918
|
+
|
|
1919
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
1920
|
+
topic = test_mk_topic_name("0171-many-headers", 1);
|
|
1921
|
+
test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
|
|
1922
|
+
|
|
1923
|
+
/* Build the producev arg list for a record with `hdr_cnt` headers.
|
|
1924
|
+
* Each header name is "h-<i>" and each value is "v-<i>". */
|
|
1925
|
+
hdrs = rd_kafka_headers_new(hdr_cnt);
|
|
1926
|
+
for (i = 0; i < (size_t)hdr_cnt; i++) {
|
|
1927
|
+
rd_snprintf(name, sizeof(name), "h-%zu", i);
|
|
1928
|
+
rd_snprintf(value, sizeof(value), "v-%zu", i);
|
|
1929
|
+
rd_kafka_header_add(hdrs, name, -1, value, -1);
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
|
|
1933
|
+
RD_KAFKA_V_KEY("many", 4),
|
|
1934
|
+
RD_KAFKA_V_VALUE("many-payload", 12),
|
|
1935
|
+
RD_KAFKA_V_HEADERS(hdrs), RD_KAFKA_V_END);
|
|
1936
|
+
TEST_ASSERT(!err, "producev many headers failed: %s",
|
|
1937
|
+
rd_kafka_err2name(err));
|
|
1938
|
+
|
|
1939
|
+
/* Record with one very large header value. */
|
|
1940
|
+
big_hdr_value = rd_malloc(big_hdr_size);
|
|
1941
|
+
for (i = 0; i < big_hdr_size; i++)
|
|
1942
|
+
big_hdr_value[i] = (char)('A' + (i % 26));
|
|
1943
|
+
|
|
1944
|
+
err = rd_kafka_producev(
|
|
1945
|
+
common_producer, RD_KAFKA_V_TOPIC(topic), RD_KAFKA_V_KEY("big", 3),
|
|
1946
|
+
RD_KAFKA_V_VALUE("big-payload", 11),
|
|
1947
|
+
RD_KAFKA_V_HEADER("big-hdr", big_hdr_value, big_hdr_size),
|
|
1948
|
+
RD_KAFKA_V_END);
|
|
1949
|
+
TEST_ASSERT(!err, "producev big header failed: %s",
|
|
1950
|
+
rd_kafka_err2name(err));
|
|
1951
|
+
rd_free(big_hdr_value);
|
|
1952
|
+
|
|
1953
|
+
rd_kafka_flush(common_producer, 30 * 1000);
|
|
1954
|
+
|
|
1955
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
1956
|
+
|
|
1957
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
1958
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
1959
|
+
rd_kafka_share_subscribe(consumer, subs);
|
|
1960
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
1961
|
+
|
|
1962
|
+
attempts = 30;
|
|
1963
|
+
while (rcvd < 2 && attempts-- > 0) {
|
|
1964
|
+
size_t batch_rcvd = 0;
|
|
1965
|
+
rd_kafka_error_t *e;
|
|
1966
|
+
|
|
1967
|
+
rd_kafka_messages_destroy(batch);
|
|
1968
|
+
batch = NULL;
|
|
1969
|
+
e = rd_kafka_share_poll(consumer, 2000, &batch);
|
|
1970
|
+
if (e)
|
|
1971
|
+
rd_kafka_error_destroy(e);
|
|
1972
|
+
batch_rcvd = rd_kafka_messages_count(batch);
|
|
1973
|
+
|
|
1974
|
+
for (i = 0; i < batch_rcvd; i++) {
|
|
1975
|
+
rd_kafka_message_t *msg =
|
|
1976
|
+
rd_kafka_messages_get(batch, i);
|
|
1977
|
+
msg_hdrs = NULL;
|
|
1978
|
+
herr = rd_kafka_message_headers(msg, &msg_hdrs);
|
|
1979
|
+
TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR &&
|
|
1980
|
+
msg_hdrs,
|
|
1981
|
+
"headers not available: %s",
|
|
1982
|
+
rd_kafka_err2name(herr));
|
|
1983
|
+
|
|
1984
|
+
if (msg->key_len == 4 &&
|
|
1985
|
+
memcmp(msg->key, "many", 4) == 0) {
|
|
1986
|
+
n = rd_kafka_header_cnt(msg_hdrs);
|
|
1987
|
+
|
|
1988
|
+
TEST_ASSERT(n == (size_t)hdr_cnt,
|
|
1989
|
+
"many-headers record: expected %d "
|
|
1990
|
+
"headers, got %zu",
|
|
1991
|
+
hdr_cnt, n);
|
|
1992
|
+
|
|
1993
|
+
/* Verify every header name+value present. */
|
|
1994
|
+
for (j = 0; j < (size_t)hdr_cnt; j++) {
|
|
1995
|
+
rd_snprintf(name, sizeof(name), "h-%zu",
|
|
1996
|
+
j);
|
|
1997
|
+
rd_snprintf(expected, sizeof(expected),
|
|
1998
|
+
"v-%zu", j);
|
|
1999
|
+
herr = rd_kafka_header_get_last(
|
|
2000
|
+
msg_hdrs, name, &val, &vsz);
|
|
2001
|
+
TEST_ASSERT(
|
|
2002
|
+
herr == RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
2003
|
+
"%s missing: %s", name,
|
|
2004
|
+
rd_kafka_err2name(herr));
|
|
2005
|
+
TEST_ASSERT(
|
|
2006
|
+
vsz == strlen(expected) &&
|
|
2007
|
+
memcmp(val, expected, vsz) == 0,
|
|
2008
|
+
"%s value mismatch", name);
|
|
2009
|
+
}
|
|
2010
|
+
verified_many = rd_true;
|
|
2011
|
+
} else if (msg->key_len == 3 &&
|
|
2012
|
+
memcmp(msg->key, "big", 3) == 0) {
|
|
2013
|
+
herr = rd_kafka_header_get_last(
|
|
2014
|
+
msg_hdrs, "big-hdr", &val, &vsz);
|
|
2015
|
+
TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
2016
|
+
"big-hdr missing: %s",
|
|
2017
|
+
rd_kafka_err2name(herr));
|
|
2018
|
+
TEST_ASSERT(vsz == big_hdr_size,
|
|
2019
|
+
"big-hdr size mismatch: expected "
|
|
2020
|
+
"%zu, got %zu",
|
|
2021
|
+
big_hdr_size, vsz);
|
|
2022
|
+
|
|
2023
|
+
/* Spot-check the content pattern at a few
|
|
2024
|
+
* offsets. */
|
|
2025
|
+
bytes = (const char *)val;
|
|
2026
|
+
for (k = 0; k < vsz; k += vsz / 16) {
|
|
2027
|
+
expected_byte = (char)('A' + (k % 26));
|
|
2028
|
+
TEST_ASSERT(
|
|
2029
|
+
bytes[k] == expected_byte,
|
|
2030
|
+
"big-hdr byte %zu: expected %c, "
|
|
2031
|
+
"got %c",
|
|
2032
|
+
k, expected_byte, bytes[k]);
|
|
2033
|
+
}
|
|
2034
|
+
verified_big = rd_true;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
rcvd += batch_rcvd;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
rd_kafka_messages_destroy(batch);
|
|
2041
|
+
batch = NULL;
|
|
2042
|
+
|
|
2043
|
+
TEST_ASSERT(rcvd == 2, "Expected 2 records, got %zu", rcvd);
|
|
2044
|
+
|
|
2045
|
+
TEST_ASSERT(verified_many, "many-headers record was not verified");
|
|
2046
|
+
TEST_ASSERT(verified_big, "big-header record was not verified");
|
|
2047
|
+
|
|
2048
|
+
test_share_consumer_close(consumer);
|
|
2049
|
+
test_share_destroy(consumer);
|
|
2050
|
+
|
|
2051
|
+
TEST_SAY("SUCCESS: many headers + large header value preserved\n");
|
|
2052
|
+
|
|
2053
|
+
SUB_TEST_PASS();
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
|
|
2057
|
+
int main_0171_share_consumer_consume(int argc, char **argv) {
|
|
2058
|
+
/* Create common handles for all tests */
|
|
2059
|
+
common_producer = test_create_producer();
|
|
2060
|
+
common_admin = test_create_producer();
|
|
2061
|
+
|
|
2062
|
+
test_timeout_set(600);
|
|
2063
|
+
|
|
2064
|
+
/* Single-consumer tests */
|
|
2065
|
+
do_test_single_consumer_single_topic_single_partition(); /* Single
|
|
2066
|
+
consumer,
|
|
2067
|
+
single topic,
|
|
2068
|
+
single
|
|
2069
|
+
partition */
|
|
2070
|
+
do_test_single_consumer_single_topic_multiple_partitions(); /* Single
|
|
2071
|
+
consumer,
|
|
2072
|
+
single
|
|
2073
|
+
topic, multi
|
|
2074
|
+
partitions
|
|
2075
|
+
*/
|
|
2076
|
+
do_test_single_consumer_multiple_topic_single_partition(); /* Single
|
|
2077
|
+
consumer,
|
|
2078
|
+
multi topic,
|
|
2079
|
+
single
|
|
2080
|
+
partition
|
|
2081
|
+
each */
|
|
2082
|
+
do_test_single_consumer_multiple_topic_multiple_partitions(); /* Single
|
|
2083
|
+
consumer,
|
|
2084
|
+
multi
|
|
2085
|
+
topics,
|
|
2086
|
+
multi
|
|
2087
|
+
partitions
|
|
2088
|
+
each */
|
|
2089
|
+
|
|
2090
|
+
/* Multi-consumer tests */
|
|
2091
|
+
do_test_multiple_consumers_single_topic_single_partition(); /* Multi
|
|
2092
|
+
consumer
|
|
2093
|
+
sharing
|
|
2094
|
+
single
|
|
2095
|
+
partition */
|
|
2096
|
+
do_test_multiple_consumers_single_topic_multiple_partitions(); /* Multi
|
|
2097
|
+
consumer,
|
|
2098
|
+
multi
|
|
2099
|
+
partition
|
|
2100
|
+
*/
|
|
2101
|
+
do_test_multiple_consumers_multiple_topics_multiple_partitions(); /* Full
|
|
2102
|
+
matrix:
|
|
2103
|
+
multi
|
|
2104
|
+
everything
|
|
2105
|
+
*/
|
|
2106
|
+
|
|
2107
|
+
/* High volume tests */
|
|
2108
|
+
do_test_high_volume_10k_messages();
|
|
2109
|
+
do_test_high_volume_50k_multi_partition();
|
|
2110
|
+
|
|
2111
|
+
/* Multi-topic tests (triggers multiple fetch responses) */
|
|
2112
|
+
do_test_many_topics_15();
|
|
2113
|
+
do_test_many_topics_10_multi_partition();
|
|
2114
|
+
|
|
2115
|
+
/* Rapid produce/consume tests */
|
|
2116
|
+
do_test_rapid_produce_consume_cycles();
|
|
2117
|
+
|
|
2118
|
+
/* Edge case tests */
|
|
2119
|
+
do_test_empty_then_produce();
|
|
2120
|
+
do_test_sparse_partitions();
|
|
2121
|
+
|
|
2122
|
+
/* Callback tests */
|
|
2123
|
+
do_test_poll_callback_piggybacked_acks();
|
|
2124
|
+
|
|
2125
|
+
/* Concurrency tests */
|
|
2126
|
+
do_test_concurrent_thread_access_rejected();
|
|
2127
|
+
|
|
2128
|
+
/* Headers and fetch byte-limit tests */
|
|
2129
|
+
do_test_headers_preserved();
|
|
2130
|
+
do_test_fetch_max_bytes_small();
|
|
2131
|
+
do_test_record_larger_than_fetch_max_bytes();
|
|
2132
|
+
|
|
2133
|
+
/* Failing consumer releases to group */
|
|
2134
|
+
do_test_consumer_close_releases_to_group();
|
|
2135
|
+
|
|
2136
|
+
/* Topology change: grow partitions while consuming */
|
|
2137
|
+
do_test_partition_increase_during_consume();
|
|
2138
|
+
|
|
2139
|
+
/* Payload edge cases */
|
|
2140
|
+
do_test_zero_byte_payload_and_key();
|
|
2141
|
+
do_test_all_compression_codecs();
|
|
2142
|
+
do_test_many_and_large_headers();
|
|
2143
|
+
|
|
2144
|
+
|
|
2145
|
+
/* Cleanup common handles */
|
|
2146
|
+
rd_kafka_destroy(common_admin);
|
|
2147
|
+
rd_kafka_destroy(common_producer);
|
|
2148
|
+
|
|
2149
|
+
return 0;
|
|
2150
|
+
}
|