@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,1788 @@
|
|
|
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
|
+
#include "../src/rdkafka_proto.h"
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @name Share Acknowledgement (implicit ack) mock broker tests.
|
|
35
|
+
*
|
|
36
|
+
* Exercises the implicit ack flow via mock broker: records acquired
|
|
37
|
+
* by a ShareFetch are acknowledged on the next ShareFetch, which
|
|
38
|
+
* causes the mock broker to archive them and advance SPSO.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
typedef struct test_ctx_s {
|
|
42
|
+
rd_kafka_t *producer;
|
|
43
|
+
rd_kafka_mock_cluster_t *mcluster;
|
|
44
|
+
const char *bootstraps;
|
|
45
|
+
} test_ctx_t;
|
|
46
|
+
|
|
47
|
+
static test_ctx_t test_ctx_new(void) {
|
|
48
|
+
test_ctx_t ctx;
|
|
49
|
+
rd_kafka_conf_t *conf;
|
|
50
|
+
char errstr[512];
|
|
51
|
+
|
|
52
|
+
memset(&ctx, 0, sizeof(ctx));
|
|
53
|
+
|
|
54
|
+
ctx.mcluster = test_mock_cluster_new(3, &ctx.bootstraps);
|
|
55
|
+
|
|
56
|
+
TEST_ASSERT(rd_kafka_mock_set_apiversion(
|
|
57
|
+
ctx.mcluster, RD_KAFKAP_ShareGroupHeartbeat, 1, 1) ==
|
|
58
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
59
|
+
"Failed to enable ShareGroupHeartbeat");
|
|
60
|
+
TEST_ASSERT(rd_kafka_mock_set_apiversion(ctx.mcluster,
|
|
61
|
+
RD_KAFKAP_ShareFetch, 1, 1) ==
|
|
62
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
63
|
+
"Failed to enable ShareFetch");
|
|
64
|
+
|
|
65
|
+
/* Set auto.offset.reset=earliest so tests that produce
|
|
66
|
+
* before consuming see all records. */
|
|
67
|
+
rd_kafka_mock_sharegroup_set_auto_offset_reset(ctx.mcluster, 1);
|
|
68
|
+
|
|
69
|
+
/* Create a producer targeting the mock cluster */
|
|
70
|
+
test_conf_init(&conf, NULL, 0);
|
|
71
|
+
test_conf_set(conf, "bootstrap.servers", ctx.bootstraps);
|
|
72
|
+
rd_kafka_conf_set_dr_msg_cb(conf, test_dr_msg_cb);
|
|
73
|
+
|
|
74
|
+
ctx.producer =
|
|
75
|
+
rd_kafka_new(RD_KAFKA_PRODUCER, conf, errstr, sizeof(errstr));
|
|
76
|
+
TEST_ASSERT(ctx.producer != NULL, "Failed to create producer: %s",
|
|
77
|
+
errstr);
|
|
78
|
+
|
|
79
|
+
return ctx;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static void test_ctx_destroy(test_ctx_t *ctx) {
|
|
83
|
+
if (ctx->producer)
|
|
84
|
+
rd_kafka_destroy(ctx->producer);
|
|
85
|
+
if (ctx->mcluster)
|
|
86
|
+
test_mock_cluster_destroy(ctx->mcluster);
|
|
87
|
+
memset(ctx, 0, sizeof(*ctx));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static rd_kafka_share_t *new_share_consumer(const char *bootstraps,
|
|
91
|
+
const char *group_id) {
|
|
92
|
+
rd_kafka_conf_t *conf;
|
|
93
|
+
rd_kafka_share_t *consumer;
|
|
94
|
+
|
|
95
|
+
test_conf_init(&conf, NULL, 0);
|
|
96
|
+
test_conf_set(conf, "bootstrap.servers", bootstraps);
|
|
97
|
+
test_conf_set(conf, "group.id", group_id);
|
|
98
|
+
|
|
99
|
+
consumer = rd_kafka_share_consumer_new(conf, NULL, 0);
|
|
100
|
+
TEST_ASSERT(consumer != NULL, "Failed to create share consumer");
|
|
101
|
+
return consumer;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static void subscribe_topics(rd_kafka_share_t *consumer,
|
|
105
|
+
const char **topics,
|
|
106
|
+
int topic_cnt) {
|
|
107
|
+
rd_kafka_topic_partition_list_t *tpl =
|
|
108
|
+
rd_kafka_topic_partition_list_new(topic_cnt);
|
|
109
|
+
for (int i = 0; i < topic_cnt; i++) {
|
|
110
|
+
rd_kafka_topic_partition_list_add(tpl, topics[i],
|
|
111
|
+
RD_KAFKA_PARTITION_UA);
|
|
112
|
+
}
|
|
113
|
+
TEST_ASSERT(!rd_kafka_share_subscribe(consumer, tpl),
|
|
114
|
+
"Subscribe failed");
|
|
115
|
+
rd_kafka_topic_partition_list_destroy(tpl);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
/* ===================================================================
|
|
121
|
+
* Positive test scenarios
|
|
122
|
+
* =================================================================== */
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @brief Basic implicit ack prevents re-delivery.
|
|
126
|
+
*
|
|
127
|
+
* 1. Produce 5 messages.
|
|
128
|
+
* 2. Consume all 5 (first ShareFetch acquires them).
|
|
129
|
+
* 3. Poll again — the next ShareFetch carries AcknowledgementBatches
|
|
130
|
+
* (ACCEPT) for the 5 records. The mock broker archives them and
|
|
131
|
+
* advances SPSO.
|
|
132
|
+
* 4. Subsequent polls should return 0 records (no re-delivery).
|
|
133
|
+
*/
|
|
134
|
+
static void do_test_implicit_ack_no_redelivery(void) {
|
|
135
|
+
const char *topic = "kip932_ack_no_redeliver";
|
|
136
|
+
const int msgcnt = 5;
|
|
137
|
+
test_ctx_t ctx = test_ctx_new();
|
|
138
|
+
rd_kafka_share_t *consumer;
|
|
139
|
+
int consumed, extra;
|
|
140
|
+
|
|
141
|
+
SUB_TEST_QUICK();
|
|
142
|
+
|
|
143
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
144
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
145
|
+
"Failed to create mock topic");
|
|
146
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
147
|
+
msgcnt);
|
|
148
|
+
|
|
149
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-noredeliver");
|
|
150
|
+
subscribe_topics(consumer, &topic, 1);
|
|
151
|
+
|
|
152
|
+
/* Consume all records. */
|
|
153
|
+
consumed = test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
154
|
+
TEST_SAY("ack_no_redelivery: consumed %d/%d\n", consumed, msgcnt);
|
|
155
|
+
|
|
156
|
+
/* Poll again to trigger the implicit ack (piggybacked on the
|
|
157
|
+
* next ShareFetch). Expect 0 new records. */
|
|
158
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
159
|
+
TEST_SAY("ack_no_redelivery: extra %d/0 (should be 0)\n", extra);
|
|
160
|
+
|
|
161
|
+
test_share_consumer_close(consumer);
|
|
162
|
+
test_share_destroy(consumer);
|
|
163
|
+
test_ctx_destroy(&ctx);
|
|
164
|
+
|
|
165
|
+
TEST_ASSERT(consumed == msgcnt && extra == 0,
|
|
166
|
+
"Expected consumed=%d extra=0, got consumed=%d extra=%d",
|
|
167
|
+
msgcnt, consumed, extra);
|
|
168
|
+
SUB_TEST_PASS();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @brief After implicit ack, only newly-produced records are delivered.
|
|
173
|
+
*
|
|
174
|
+
* 1. Produce batch A (3 messages).
|
|
175
|
+
* 2. Consumer A consumes batch A and triggers implicit ack.
|
|
176
|
+
* 3. Consumer A closes.
|
|
177
|
+
* 4. Produce batch B (3 messages).
|
|
178
|
+
* 5. Consumer B (same group) consumes — should receive only batch B
|
|
179
|
+
* because batch A was acked (ARCHIVED, SPSO advanced).
|
|
180
|
+
*/
|
|
181
|
+
static void do_test_implicit_ack_with_new_records(void) {
|
|
182
|
+
const char *topic = "kip932_ack_new_records";
|
|
183
|
+
test_ctx_t ctx = test_ctx_new();
|
|
184
|
+
rd_kafka_share_t *consumer;
|
|
185
|
+
int consumed_a, consumed_b, extra;
|
|
186
|
+
|
|
187
|
+
SUB_TEST_QUICK();
|
|
188
|
+
|
|
189
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
190
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
191
|
+
"Failed to create mock topic");
|
|
192
|
+
|
|
193
|
+
/* Batch A */
|
|
194
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA, 3);
|
|
195
|
+
|
|
196
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-newrecords");
|
|
197
|
+
subscribe_topics(consumer, &topic, 1);
|
|
198
|
+
|
|
199
|
+
consumed_a = test_share_consume_msgs(consumer, 3, 40, 500, NULL, 0);
|
|
200
|
+
TEST_SAY("ack_new_records: A consumed %d/3\n", consumed_a);
|
|
201
|
+
|
|
202
|
+
/* Trigger implicit ack for batch A. */
|
|
203
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
204
|
+
TEST_SAY("ack_new_records: A extra %d/0 (ack sent)\n", extra);
|
|
205
|
+
|
|
206
|
+
test_share_consumer_close(consumer);
|
|
207
|
+
test_share_destroy(consumer);
|
|
208
|
+
|
|
209
|
+
/* Batch B */
|
|
210
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA, 3);
|
|
211
|
+
|
|
212
|
+
/* Consumer B: same group — batch A is archived, should get
|
|
213
|
+
* only batch B. */
|
|
214
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-newrecords");
|
|
215
|
+
subscribe_topics(consumer, &topic, 1);
|
|
216
|
+
|
|
217
|
+
consumed_b = test_share_consume_msgs(consumer, 3, 40, 500, NULL, 0);
|
|
218
|
+
TEST_SAY("ack_new_records: B consumed %d/3 (batch B only)\n",
|
|
219
|
+
consumed_b);
|
|
220
|
+
|
|
221
|
+
test_share_consumer_close(consumer);
|
|
222
|
+
test_share_destroy(consumer);
|
|
223
|
+
test_ctx_destroy(&ctx);
|
|
224
|
+
|
|
225
|
+
TEST_ASSERT(consumed_a == 3 && extra == 0 && consumed_b == 3,
|
|
226
|
+
"Expected A=3 extra=0 B=3, got A=%d extra=%d B=%d",
|
|
227
|
+
consumed_a, extra, consumed_b);
|
|
228
|
+
SUB_TEST_PASS();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @brief Acked records are not visible to a different consumer that
|
|
233
|
+
* joins the same share group afterwards.
|
|
234
|
+
*
|
|
235
|
+
* 1. Consumer A consumes all records and triggers implicit ack.
|
|
236
|
+
* 2. Consumer A closes.
|
|
237
|
+
* 3. Consumer B joins the same group — should see 0 records because
|
|
238
|
+
* the records are ARCHIVED and SPSO has advanced.
|
|
239
|
+
*/
|
|
240
|
+
static void do_test_implicit_ack_cross_consumer(void) {
|
|
241
|
+
const char *topic = "kip932_ack_cross_consumer";
|
|
242
|
+
const int msgcnt = 5;
|
|
243
|
+
test_ctx_t ctx = test_ctx_new();
|
|
244
|
+
rd_kafka_share_t *consumer_a, *consumer_b;
|
|
245
|
+
int consumed_a, consumed_b, extra;
|
|
246
|
+
|
|
247
|
+
SUB_TEST_QUICK();
|
|
248
|
+
|
|
249
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
250
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
251
|
+
"Failed to create mock topic");
|
|
252
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
253
|
+
msgcnt);
|
|
254
|
+
|
|
255
|
+
/* Consumer A: consume and ack. */
|
|
256
|
+
consumer_a = new_share_consumer(ctx.bootstraps, "sg-ack-cross");
|
|
257
|
+
subscribe_topics(consumer_a, &topic, 1);
|
|
258
|
+
|
|
259
|
+
consumed_a =
|
|
260
|
+
test_share_consume_msgs(consumer_a, msgcnt, 50, 500, NULL, 0);
|
|
261
|
+
TEST_SAY("ack_cross_consumer: A consumed %d/%d\n", consumed_a, msgcnt);
|
|
262
|
+
|
|
263
|
+
/* Trigger implicit ack. */
|
|
264
|
+
extra = test_share_consume_msgs(consumer_a, 1, 10, 500, NULL, 0);
|
|
265
|
+
TEST_SAY("ack_cross_consumer: A extra %d/0\n", extra);
|
|
266
|
+
|
|
267
|
+
test_share_consumer_close(consumer_a);
|
|
268
|
+
test_share_destroy(consumer_a);
|
|
269
|
+
|
|
270
|
+
/* Consumer B: same group, should see nothing. */
|
|
271
|
+
consumer_b = new_share_consumer(ctx.bootstraps, "sg-ack-cross");
|
|
272
|
+
subscribe_topics(consumer_b, &topic, 1);
|
|
273
|
+
|
|
274
|
+
consumed_b = test_share_consume_msgs(consumer_b, 1, 15, 500, NULL, 0);
|
|
275
|
+
TEST_SAY("ack_cross_consumer: B consumed %d/0 (should be 0)\n",
|
|
276
|
+
consumed_b);
|
|
277
|
+
|
|
278
|
+
test_share_consumer_close(consumer_b);
|
|
279
|
+
test_share_destroy(consumer_b);
|
|
280
|
+
test_ctx_destroy(&ctx);
|
|
281
|
+
|
|
282
|
+
TEST_ASSERT(consumed_a == msgcnt && extra == 0 && consumed_b == 0,
|
|
283
|
+
"Expected A=%d extra=0 B=0, got A=%d extra=%d B=%d", msgcnt,
|
|
284
|
+
consumed_a, extra, consumed_b);
|
|
285
|
+
SUB_TEST_PASS();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @brief Implicit ack works across multiple partitions.
|
|
290
|
+
*
|
|
291
|
+
* 1. Create a 2-partition topic.
|
|
292
|
+
* 2. Produce messages (distributed across partitions by the partitioner).
|
|
293
|
+
* 3. Consume all messages.
|
|
294
|
+
* 4. Trigger implicit ack.
|
|
295
|
+
* 5. Poll again — should get 0 (acked from all partitions).
|
|
296
|
+
*/
|
|
297
|
+
static void do_test_implicit_ack_multi_partition(void) {
|
|
298
|
+
const char *topic = "kip932_ack_multi_part";
|
|
299
|
+
const int msgcnt = 6;
|
|
300
|
+
test_ctx_t ctx = test_ctx_new();
|
|
301
|
+
rd_kafka_share_t *consumer;
|
|
302
|
+
int consumed, extra;
|
|
303
|
+
|
|
304
|
+
SUB_TEST_QUICK();
|
|
305
|
+
|
|
306
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 2, 1) ==
|
|
307
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
308
|
+
"Failed to create mock topic");
|
|
309
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
310
|
+
msgcnt);
|
|
311
|
+
|
|
312
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-multipart");
|
|
313
|
+
subscribe_topics(consumer, &topic, 1);
|
|
314
|
+
|
|
315
|
+
consumed = test_share_consume_msgs(consumer, msgcnt, 60, 500, NULL, 0);
|
|
316
|
+
TEST_SAY("ack_multi_partition: consumed %d/%d\n", consumed, msgcnt);
|
|
317
|
+
|
|
318
|
+
/* Trigger implicit ack. */
|
|
319
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
320
|
+
TEST_SAY("ack_multi_partition: extra %d/0\n", extra);
|
|
321
|
+
|
|
322
|
+
test_share_consumer_close(consumer);
|
|
323
|
+
test_share_destroy(consumer);
|
|
324
|
+
test_ctx_destroy(&ctx);
|
|
325
|
+
|
|
326
|
+
TEST_ASSERT(consumed == msgcnt && extra == 0,
|
|
327
|
+
"Expected consumed=%d extra=0, got consumed=%d extra=%d",
|
|
328
|
+
msgcnt, consumed, extra);
|
|
329
|
+
SUB_TEST_PASS();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* @brief Multiple rounds of produce -> consume -> ack with new consumers.
|
|
334
|
+
*
|
|
335
|
+
* Each round:
|
|
336
|
+
* 1. Produce N messages.
|
|
337
|
+
* 2. New consumer joins the same share group.
|
|
338
|
+
* 3. Consumer should get exactly the N new records (records from
|
|
339
|
+
* previous rounds were acked and SPSO advanced).
|
|
340
|
+
* 4. Consumer triggers implicit ack and closes.
|
|
341
|
+
*
|
|
342
|
+
* This verifies that SPSO advancement from acks in earlier rounds
|
|
343
|
+
* persists correctly across consumer lifetimes.
|
|
344
|
+
*/
|
|
345
|
+
static void do_test_implicit_ack_multiple_rounds(void) {
|
|
346
|
+
const char *topic = "kip932_ack_multi_round";
|
|
347
|
+
const int per_round = 2;
|
|
348
|
+
const int rounds = 3;
|
|
349
|
+
test_ctx_t ctx = test_ctx_new();
|
|
350
|
+
int total_consumed = 0;
|
|
351
|
+
int round_ok = 1;
|
|
352
|
+
|
|
353
|
+
SUB_TEST_QUICK();
|
|
354
|
+
|
|
355
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
356
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
357
|
+
"Failed to create mock topic");
|
|
358
|
+
|
|
359
|
+
for (int r = 0; r < rounds; r++) {
|
|
360
|
+
rd_kafka_share_t *consumer;
|
|
361
|
+
int got, extra;
|
|
362
|
+
|
|
363
|
+
test_produce_msgs_simple(ctx.producer, topic,
|
|
364
|
+
RD_KAFKA_PARTITION_UA, per_round);
|
|
365
|
+
|
|
366
|
+
consumer =
|
|
367
|
+
new_share_consumer(ctx.bootstraps, "sg-ack-multiround");
|
|
368
|
+
subscribe_topics(consumer, &topic, 1);
|
|
369
|
+
|
|
370
|
+
got = test_share_consume_msgs(consumer, per_round, 40, 500,
|
|
371
|
+
NULL, 0);
|
|
372
|
+
TEST_SAY("ack_multiple_rounds: round %d consumed %d/%d\n",
|
|
373
|
+
r + 1, got, per_round);
|
|
374
|
+
total_consumed += got;
|
|
375
|
+
if (got != per_round)
|
|
376
|
+
round_ok = 0;
|
|
377
|
+
|
|
378
|
+
/* Trigger implicit ack. */
|
|
379
|
+
extra = test_share_consume_msgs(consumer, 1, 5, 500, NULL, 0);
|
|
380
|
+
if (extra != 0) {
|
|
381
|
+
TEST_SAY(
|
|
382
|
+
"ack_multiple_rounds: round %d extra %d "
|
|
383
|
+
"(expected 0)\n",
|
|
384
|
+
r + 1, extra);
|
|
385
|
+
round_ok = 0;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
test_share_consumer_close(consumer);
|
|
389
|
+
test_share_destroy(consumer);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
TEST_SAY("ack_multiple_rounds: total %d/%d\n", total_consumed,
|
|
393
|
+
per_round * rounds);
|
|
394
|
+
|
|
395
|
+
test_ctx_destroy(&ctx);
|
|
396
|
+
|
|
397
|
+
TEST_ASSERT(round_ok && total_consumed == per_round * rounds,
|
|
398
|
+
"Expected %d total, got %d (round_ok=%d)",
|
|
399
|
+
per_round * rounds, total_consumed, round_ok);
|
|
400
|
+
SUB_TEST_PASS();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @brief Implicit ack with a single record (boundary case).
|
|
405
|
+
*
|
|
406
|
+
* Produce exactly 1 message, consume it, trigger implicit ack,
|
|
407
|
+
* verify no re-delivery. Tests the minimum-batch-size edge case for
|
|
408
|
+
* AcquiredRecords ranges and SPSO advancement.
|
|
409
|
+
*/
|
|
410
|
+
static void do_test_implicit_ack_single_record(void) {
|
|
411
|
+
const char *topic = "kip932_ack_single";
|
|
412
|
+
test_ctx_t ctx = test_ctx_new();
|
|
413
|
+
rd_kafka_share_t *consumer;
|
|
414
|
+
int consumed, extra;
|
|
415
|
+
|
|
416
|
+
SUB_TEST_QUICK();
|
|
417
|
+
|
|
418
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
419
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
420
|
+
"Failed to create mock topic");
|
|
421
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA, 1);
|
|
422
|
+
|
|
423
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-single");
|
|
424
|
+
subscribe_topics(consumer, &topic, 1);
|
|
425
|
+
|
|
426
|
+
consumed = test_share_consume_msgs(consumer, 1, 40, 500, NULL, 0);
|
|
427
|
+
TEST_SAY("ack_single_record: consumed %d/1\n", consumed);
|
|
428
|
+
|
|
429
|
+
/* Trigger implicit ack. */
|
|
430
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
431
|
+
TEST_SAY("ack_single_record: extra %d/0 (should be 0)\n", extra);
|
|
432
|
+
|
|
433
|
+
test_share_consumer_close(consumer);
|
|
434
|
+
test_share_destroy(consumer);
|
|
435
|
+
test_ctx_destroy(&ctx);
|
|
436
|
+
|
|
437
|
+
TEST_ASSERT(consumed == 1 && extra == 0,
|
|
438
|
+
"Expected consumed=1 extra=0, got consumed=%d extra=%d",
|
|
439
|
+
consumed, extra);
|
|
440
|
+
SUB_TEST_PASS();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* @brief Implicit ack with a large batch of records.
|
|
445
|
+
*
|
|
446
|
+
* Produce 100 messages, consume all, trigger implicit ack, verify 0
|
|
447
|
+
* on subsequent polls. Tests ack handling at scale — many records
|
|
448
|
+
* in a single AcquiredRecords range and SPSO advancement over a
|
|
449
|
+
* large offset span.
|
|
450
|
+
*/
|
|
451
|
+
static void do_test_implicit_ack_large_batch(void) {
|
|
452
|
+
const char *topic = "kip932_ack_large";
|
|
453
|
+
const int msgcnt = 100;
|
|
454
|
+
test_ctx_t ctx = test_ctx_new();
|
|
455
|
+
rd_kafka_share_t *consumer;
|
|
456
|
+
int consumed, extra;
|
|
457
|
+
|
|
458
|
+
SUB_TEST_QUICK();
|
|
459
|
+
|
|
460
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
461
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
462
|
+
"Failed to create mock topic");
|
|
463
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
464
|
+
msgcnt);
|
|
465
|
+
|
|
466
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-large");
|
|
467
|
+
subscribe_topics(consumer, &topic, 1);
|
|
468
|
+
|
|
469
|
+
consumed = test_share_consume_msgs(consumer, msgcnt, 80, 500, NULL, 0);
|
|
470
|
+
TEST_SAY("ack_large_batch: consumed %d/%d\n", consumed, msgcnt);
|
|
471
|
+
|
|
472
|
+
/* Trigger implicit ack. */
|
|
473
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
474
|
+
TEST_SAY("ack_large_batch: extra %d/0 (should be 0)\n", extra);
|
|
475
|
+
|
|
476
|
+
test_share_consumer_close(consumer);
|
|
477
|
+
test_share_destroy(consumer);
|
|
478
|
+
test_ctx_destroy(&ctx);
|
|
479
|
+
|
|
480
|
+
TEST_ASSERT(consumed == msgcnt && extra == 0,
|
|
481
|
+
"Expected consumed=%d extra=0, got consumed=%d extra=%d",
|
|
482
|
+
msgcnt, consumed, extra);
|
|
483
|
+
SUB_TEST_PASS();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @brief Implicit ack across multiple topics in the same share group.
|
|
488
|
+
*
|
|
489
|
+
* 1. Create 2 topics, produce messages to both.
|
|
490
|
+
* 2. Subscribe a single consumer to both topics via the same group.
|
|
491
|
+
* 3. Consume all messages from both topics.
|
|
492
|
+
* 4. Trigger implicit ack.
|
|
493
|
+
* 5. Consumer B joins the same group — should see 0 records from
|
|
494
|
+
* either topic (both acked independently).
|
|
495
|
+
*/
|
|
496
|
+
static void do_test_implicit_ack_multi_topic(void) {
|
|
497
|
+
const char *topic_a = "kip932_ack_mtopic_a";
|
|
498
|
+
const char *topic_b = "kip932_ack_mtopic_b";
|
|
499
|
+
const char *both[] = {topic_a, topic_b};
|
|
500
|
+
test_ctx_t ctx = test_ctx_new();
|
|
501
|
+
rd_kafka_share_t *consumer;
|
|
502
|
+
int consumed, extra, consumed_b;
|
|
503
|
+
|
|
504
|
+
SUB_TEST_QUICK();
|
|
505
|
+
|
|
506
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic_a, 1, 1) ==
|
|
507
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
508
|
+
"Failed to create mock topic A");
|
|
509
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic_b, 1, 1) ==
|
|
510
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
511
|
+
"Failed to create mock topic B");
|
|
512
|
+
|
|
513
|
+
test_produce_msgs_simple(ctx.producer, topic_a, RD_KAFKA_PARTITION_UA,
|
|
514
|
+
3);
|
|
515
|
+
test_produce_msgs_simple(ctx.producer, topic_b, RD_KAFKA_PARTITION_UA,
|
|
516
|
+
3);
|
|
517
|
+
|
|
518
|
+
/* Consumer A: subscribe to both, consume all 6, ack. */
|
|
519
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-mtopic");
|
|
520
|
+
subscribe_topics(consumer, both, 2);
|
|
521
|
+
|
|
522
|
+
consumed = test_share_consume_msgs(consumer, 6, 60, 500, NULL, 0);
|
|
523
|
+
TEST_SAY("ack_multi_topic: consumed %d/6 from both\n", consumed);
|
|
524
|
+
|
|
525
|
+
/* Trigger implicit ack. */
|
|
526
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
527
|
+
TEST_SAY("ack_multi_topic: extra %d/0\n", extra);
|
|
528
|
+
|
|
529
|
+
test_share_consumer_close(consumer);
|
|
530
|
+
test_share_destroy(consumer);
|
|
531
|
+
|
|
532
|
+
/* Consumer B: same group — should see 0 from either topic. */
|
|
533
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-mtopic");
|
|
534
|
+
subscribe_topics(consumer, both, 2);
|
|
535
|
+
|
|
536
|
+
consumed_b = test_share_consume_msgs(consumer, 1, 15, 500, NULL, 0);
|
|
537
|
+
TEST_SAY("ack_multi_topic: B consumed %d/0 (should be 0)\n",
|
|
538
|
+
consumed_b);
|
|
539
|
+
|
|
540
|
+
test_share_consumer_close(consumer);
|
|
541
|
+
test_share_destroy(consumer);
|
|
542
|
+
test_ctx_destroy(&ctx);
|
|
543
|
+
|
|
544
|
+
TEST_ASSERT(consumed == 6 && extra == 0 && consumed_b == 0,
|
|
545
|
+
"Expected consumed=6 extra=0 B=0, got %d %d %d", consumed,
|
|
546
|
+
extra, consumed_b);
|
|
547
|
+
SUB_TEST_PASS();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* @brief Implicit ack with records from separate message sets.
|
|
552
|
+
*
|
|
553
|
+
* Produce records one at a time with a flush in between, creating
|
|
554
|
+
* separate msgsets on the mock partition. Then consume all, ack,
|
|
555
|
+
* and verify 0 on subsequent polls.
|
|
556
|
+
*
|
|
557
|
+
* This validates that the ack machinery handles records spanning
|
|
558
|
+
* multiple RecordBatch wire objects.
|
|
559
|
+
*/
|
|
560
|
+
static void do_test_implicit_ack_multi_msgset(void) {
|
|
561
|
+
const char *topic = "kip932_ack_multi_msgset";
|
|
562
|
+
const int msgcnt = 5;
|
|
563
|
+
test_ctx_t ctx = test_ctx_new();
|
|
564
|
+
rd_kafka_share_t *consumer;
|
|
565
|
+
int consumed, extra;
|
|
566
|
+
|
|
567
|
+
SUB_TEST_QUICK();
|
|
568
|
+
|
|
569
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
570
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
571
|
+
"Failed to create mock topic");
|
|
572
|
+
|
|
573
|
+
/* Produce each message individually with a flush in between,
|
|
574
|
+
* guaranteeing separate msgsets on the mock partition. */
|
|
575
|
+
for (int i = 0; i < msgcnt; i++) {
|
|
576
|
+
char payload[64];
|
|
577
|
+
snprintf(payload, sizeof(payload), "msgset-%d", i);
|
|
578
|
+
TEST_ASSERT(rd_kafka_producev(
|
|
579
|
+
ctx.producer, RD_KAFKA_V_TOPIC(topic),
|
|
580
|
+
RD_KAFKA_V_VALUE(payload, strlen(payload)),
|
|
581
|
+
RD_KAFKA_V_MSGFLAGS(RD_KAFKA_MSG_F_COPY),
|
|
582
|
+
RD_KAFKA_V_END) == RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
583
|
+
"Produce failed");
|
|
584
|
+
rd_kafka_flush(ctx.producer, 5000);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-multimsgset");
|
|
588
|
+
subscribe_topics(consumer, &topic, 1);
|
|
589
|
+
|
|
590
|
+
consumed = test_share_consume_msgs(consumer, msgcnt, 60, 500, NULL, 0);
|
|
591
|
+
TEST_SAY("ack_multi_msgset: consumed %d/%d\n", consumed, msgcnt);
|
|
592
|
+
|
|
593
|
+
/* Trigger implicit ack. */
|
|
594
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
595
|
+
TEST_SAY("ack_multi_msgset: extra %d/0 (should be 0)\n", extra);
|
|
596
|
+
|
|
597
|
+
test_share_consumer_close(consumer);
|
|
598
|
+
test_share_destroy(consumer);
|
|
599
|
+
test_ctx_destroy(&ctx);
|
|
600
|
+
|
|
601
|
+
TEST_ASSERT(consumed == msgcnt && extra == 0,
|
|
602
|
+
"Expected consumed=%d extra=0, got consumed=%d extra=%d",
|
|
603
|
+
msgcnt, consumed, extra);
|
|
604
|
+
SUB_TEST_PASS();
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/* ===================================================================
|
|
608
|
+
* Negative test scenarios
|
|
609
|
+
* =================================================================== */
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* @brief Crash (no ack) -> records re-delivered after lock expiry.
|
|
613
|
+
*
|
|
614
|
+
* Consumer A consumes records but is destroyed without closing and
|
|
615
|
+
* without sending a subsequent ShareFetch that would carry the ack.
|
|
616
|
+
* After the acquisition lock expires, Consumer B should receive the
|
|
617
|
+
* same records.
|
|
618
|
+
*/
|
|
619
|
+
static void do_test_crash_before_ack_redelivery(void) {
|
|
620
|
+
const char *topic = "kip932_ack_crash_redeliver";
|
|
621
|
+
const int msgcnt = 3;
|
|
622
|
+
test_ctx_t ctx;
|
|
623
|
+
rd_kafka_share_t *consumer;
|
|
624
|
+
int consumed_a, consumed_b;
|
|
625
|
+
|
|
626
|
+
SUB_TEST();
|
|
627
|
+
ctx = test_ctx_new();
|
|
628
|
+
|
|
629
|
+
/* Short lock so the test doesn't wait too long. */
|
|
630
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 500);
|
|
631
|
+
|
|
632
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
633
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
634
|
+
"Failed to create mock topic");
|
|
635
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
636
|
+
msgcnt);
|
|
637
|
+
|
|
638
|
+
/* Consumer A: acquire records, then crash (no close, no ack). */
|
|
639
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-crash-redeliver");
|
|
640
|
+
subscribe_topics(consumer, &topic, 1);
|
|
641
|
+
consumed_a =
|
|
642
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
643
|
+
TEST_SAY("crash_before_ack: A consumed %d/%d\n", consumed_a, msgcnt);
|
|
644
|
+
test_share_destroy(consumer); /* crash — no close */
|
|
645
|
+
|
|
646
|
+
/* Wait for lock expiry (session_timeout=500ms + margin). */
|
|
647
|
+
rd_usleep(1500 * 1000, NULL);
|
|
648
|
+
|
|
649
|
+
/* Consumer B: should get the same records (re-delivered). */
|
|
650
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-crash-redeliver");
|
|
651
|
+
subscribe_topics(consumer, &topic, 1);
|
|
652
|
+
consumed_b =
|
|
653
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
654
|
+
TEST_SAY("crash_before_ack: B consumed %d/%d (re-delivered)\n",
|
|
655
|
+
consumed_b, msgcnt);
|
|
656
|
+
|
|
657
|
+
test_share_consumer_close(consumer);
|
|
658
|
+
test_share_destroy(consumer);
|
|
659
|
+
test_ctx_destroy(&ctx);
|
|
660
|
+
|
|
661
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt,
|
|
662
|
+
"Expected A=%d B=%d, got A=%d B=%d", msgcnt, msgcnt,
|
|
663
|
+
consumed_a, consumed_b);
|
|
664
|
+
SUB_TEST_PASS();
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* @brief Crash -> re-delivery -> ack stops further re-delivery.
|
|
669
|
+
*
|
|
670
|
+
* 1. Consumer A consumes records but crashes (no ack).
|
|
671
|
+
* 2. Locks expire, records become AVAILABLE again.
|
|
672
|
+
* 3. Consumer B consumes the re-delivered records and triggers an
|
|
673
|
+
* implicit ack via the next poll.
|
|
674
|
+
* 4. Consumer C joins — should see 0 records (acked by B).
|
|
675
|
+
*/
|
|
676
|
+
static void do_test_crash_then_ack_stops_redelivery(void) {
|
|
677
|
+
const char *topic = "kip932_ack_crash_then_ack";
|
|
678
|
+
const int msgcnt = 3;
|
|
679
|
+
test_ctx_t ctx;
|
|
680
|
+
rd_kafka_share_t *consumer;
|
|
681
|
+
int consumed_a, consumed_b, consumed_c, extra;
|
|
682
|
+
|
|
683
|
+
SUB_TEST();
|
|
684
|
+
ctx = test_ctx_new();
|
|
685
|
+
|
|
686
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 500);
|
|
687
|
+
|
|
688
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
689
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
690
|
+
"Failed to create mock topic");
|
|
691
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
692
|
+
msgcnt);
|
|
693
|
+
|
|
694
|
+
/* Consumer A: acquire and crash. */
|
|
695
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-crash-then-ack");
|
|
696
|
+
subscribe_topics(consumer, &topic, 1);
|
|
697
|
+
consumed_a =
|
|
698
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
699
|
+
TEST_SAY("crash_then_ack: A consumed %d/%d (will crash)\n", consumed_a,
|
|
700
|
+
msgcnt);
|
|
701
|
+
test_share_destroy(consumer);
|
|
702
|
+
rd_usleep(1500 * 1000, NULL); /* wait for lock expiry */
|
|
703
|
+
|
|
704
|
+
/* Consumer B: re-delivery, then ack via next poll. */
|
|
705
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-crash-then-ack");
|
|
706
|
+
subscribe_topics(consumer, &topic, 1);
|
|
707
|
+
consumed_b =
|
|
708
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
709
|
+
TEST_SAY("crash_then_ack: B consumed %d/%d (re-delivered)\n",
|
|
710
|
+
consumed_b, msgcnt);
|
|
711
|
+
|
|
712
|
+
/* Trigger implicit ack. */
|
|
713
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
714
|
+
TEST_SAY("crash_then_ack: B extra %d/0\n", extra);
|
|
715
|
+
test_share_consumer_close(consumer);
|
|
716
|
+
test_share_destroy(consumer);
|
|
717
|
+
|
|
718
|
+
/* Consumer C: should see 0 — records were acked by B. */
|
|
719
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-crash-then-ack");
|
|
720
|
+
subscribe_topics(consumer, &topic, 1);
|
|
721
|
+
consumed_c = test_share_consume_msgs(consumer, 1, 15, 500, NULL, 0);
|
|
722
|
+
TEST_SAY("crash_then_ack: C consumed %d/0 (should be 0)\n", consumed_c);
|
|
723
|
+
|
|
724
|
+
test_share_consumer_close(consumer);
|
|
725
|
+
test_share_destroy(consumer);
|
|
726
|
+
test_ctx_destroy(&ctx);
|
|
727
|
+
|
|
728
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt &&
|
|
729
|
+
extra == 0 && consumed_c == 0,
|
|
730
|
+
"Expected A=%d B=%d extra=0 C=0, got A=%d B=%d extra=%d "
|
|
731
|
+
"C=%d",
|
|
732
|
+
msgcnt, msgcnt, consumed_a, consumed_b, extra, consumed_c);
|
|
733
|
+
SUB_TEST_PASS();
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* @brief Session expiry via heartbeat failure causes pending ack to be lost.
|
|
738
|
+
*
|
|
739
|
+
* 1. Consumer A acquires records normally.
|
|
740
|
+
* 2. Push many SGHB errors -> heartbeats fail -> member is evicted.
|
|
741
|
+
* 3. Consumer A is destroyed (crash, no ack delivered).
|
|
742
|
+
* 4. The broker releases A's locks upon eviction.
|
|
743
|
+
* 5. Consumer B joins -> gets the same records (re-delivered).
|
|
744
|
+
*/
|
|
745
|
+
static void do_test_session_expiry_invalidates_ack(void) {
|
|
746
|
+
const char *topic = "kip932_ack_session_expire";
|
|
747
|
+
const int msgcnt = 3;
|
|
748
|
+
test_ctx_t ctx;
|
|
749
|
+
rd_kafka_share_t *consumer;
|
|
750
|
+
int consumed_a, consumed_b;
|
|
751
|
+
|
|
752
|
+
SUB_TEST();
|
|
753
|
+
ctx = test_ctx_new();
|
|
754
|
+
|
|
755
|
+
/* Short session timeout so eviction happens quickly. */
|
|
756
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 500);
|
|
757
|
+
|
|
758
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
759
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
760
|
+
"Failed to create mock topic");
|
|
761
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
762
|
+
msgcnt);
|
|
763
|
+
|
|
764
|
+
/* Consumer A: consume records (acquires them). */
|
|
765
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-session-expire");
|
|
766
|
+
subscribe_topics(consumer, &topic, 1);
|
|
767
|
+
consumed_a =
|
|
768
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
769
|
+
TEST_SAY("session_expiry: A consumed %d/%d\n", consumed_a, msgcnt);
|
|
770
|
+
|
|
771
|
+
/* Block SGHB so heartbeats fail -> member evicted. */
|
|
772
|
+
rd_kafka_mock_push_request_errors(
|
|
773
|
+
ctx.mcluster, RD_KAFKAP_ShareGroupHeartbeat, 20,
|
|
774
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
775
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
776
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
777
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
778
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
779
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
780
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
781
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
782
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
783
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
784
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
785
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
786
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
787
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
788
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
789
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
790
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
791
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
792
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
793
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE);
|
|
794
|
+
|
|
795
|
+
/* Wait for member eviction (session_timeout=500ms + margin). */
|
|
796
|
+
rd_usleep(1500 * 1000, NULL);
|
|
797
|
+
|
|
798
|
+
/* Crash consumer A without acking. */
|
|
799
|
+
test_share_destroy(consumer);
|
|
800
|
+
|
|
801
|
+
/* Consumer B: records should be re-delivered (locks released
|
|
802
|
+
* when A's membership was evicted). */
|
|
803
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-session-expire");
|
|
804
|
+
subscribe_topics(consumer, &topic, 1);
|
|
805
|
+
consumed_b =
|
|
806
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
807
|
+
TEST_SAY("session_expiry: B consumed %d/%d (re-delivered)\n",
|
|
808
|
+
consumed_b, msgcnt);
|
|
809
|
+
|
|
810
|
+
test_share_consumer_close(consumer);
|
|
811
|
+
test_share_destroy(consumer);
|
|
812
|
+
test_ctx_destroy(&ctx);
|
|
813
|
+
|
|
814
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt,
|
|
815
|
+
"Expected A=%d B=%d, got A=%d B=%d", msgcnt, msgcnt,
|
|
816
|
+
consumed_a, consumed_b);
|
|
817
|
+
SUB_TEST_PASS();
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* @brief Records archived by max_delivery_count without any ack.
|
|
822
|
+
*
|
|
823
|
+
* 1. Set max_delivery_attempts=2.
|
|
824
|
+
* 2. Consumer A acquires records and crashes (delivery 1).
|
|
825
|
+
* 3. Consumer B acquires same records and crashes (delivery 2 = max).
|
|
826
|
+
* 4. Consumer C joins — should see 0 records because the broker
|
|
827
|
+
* archived them after the delivery count was exhausted.
|
|
828
|
+
*/
|
|
829
|
+
static void do_test_max_delivery_without_ack(void) {
|
|
830
|
+
const char *topic = "kip932_ack_maxdel_noack";
|
|
831
|
+
const int msgcnt = 3;
|
|
832
|
+
test_ctx_t ctx;
|
|
833
|
+
rd_kafka_share_t *consumer;
|
|
834
|
+
int consumed_a, consumed_b, consumed_c;
|
|
835
|
+
|
|
836
|
+
SUB_TEST();
|
|
837
|
+
ctx = test_ctx_new();
|
|
838
|
+
|
|
839
|
+
rd_kafka_mock_sharegroup_set_max_delivery_attempts(ctx.mcluster, 2);
|
|
840
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 500);
|
|
841
|
+
|
|
842
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
843
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
844
|
+
"Failed to create mock topic");
|
|
845
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
846
|
+
msgcnt);
|
|
847
|
+
|
|
848
|
+
/* Delivery 1: Consumer A acquires and crashes. */
|
|
849
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-maxdel-noack");
|
|
850
|
+
subscribe_topics(consumer, &topic, 1);
|
|
851
|
+
consumed_a =
|
|
852
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
853
|
+
TEST_SAY("max_delivery_no_ack: A consumed %d/%d (delivery 1)\n",
|
|
854
|
+
consumed_a, msgcnt);
|
|
855
|
+
test_share_destroy(consumer);
|
|
856
|
+
rd_usleep(1500 * 1000, NULL); /* wait for lock expiry */
|
|
857
|
+
|
|
858
|
+
/* Delivery 2 = max: Consumer B acquires and crashes. */
|
|
859
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-maxdel-noack");
|
|
860
|
+
subscribe_topics(consumer, &topic, 1);
|
|
861
|
+
consumed_b =
|
|
862
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
863
|
+
TEST_SAY("max_delivery_no_ack: B consumed %d/%d (delivery 2)\n",
|
|
864
|
+
consumed_b, msgcnt);
|
|
865
|
+
test_share_destroy(consumer);
|
|
866
|
+
rd_usleep(1500 * 1000, NULL); /* wait for lock expiry + archival */
|
|
867
|
+
|
|
868
|
+
/* Delivery 3 attempt: records should be archived (delivery
|
|
869
|
+
* count exhausted). Consumer C sees 0. */
|
|
870
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-maxdel-noack");
|
|
871
|
+
subscribe_topics(consumer, &topic, 1);
|
|
872
|
+
consumed_c = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
873
|
+
TEST_SAY("max_delivery_no_ack: C consumed %d/0 (archived)\n",
|
|
874
|
+
consumed_c);
|
|
875
|
+
|
|
876
|
+
test_share_consumer_close(consumer);
|
|
877
|
+
test_share_destroy(consumer);
|
|
878
|
+
test_ctx_destroy(&ctx);
|
|
879
|
+
|
|
880
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt &&
|
|
881
|
+
consumed_c == 0,
|
|
882
|
+
"Expected A=%d B=%d C=0, got A=%d B=%d C=%d", msgcnt,
|
|
883
|
+
msgcnt, consumed_a, consumed_b, consumed_c);
|
|
884
|
+
SUB_TEST_PASS();
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* @brief ShareFetch error injection prevents ack from being processed.
|
|
889
|
+
*
|
|
890
|
+
* 1. Consumer A acquires records.
|
|
891
|
+
* 2. Inject ShareFetch transport errors on all brokers so the next
|
|
892
|
+
* ShareFetch (carrying the ack) causes a disconnect.
|
|
893
|
+
* 3. Consumer A is destroyed (crash — ack was never processed).
|
|
894
|
+
* 4. Wait for locks to expire.
|
|
895
|
+
* 5. Consumer B joins — should get the same records (re-delivered).
|
|
896
|
+
*/
|
|
897
|
+
static void do_test_sharefetch_error_drops_ack(void) {
|
|
898
|
+
const char *topic = "kip932_ack_sf_error";
|
|
899
|
+
const int msgcnt = 3;
|
|
900
|
+
test_ctx_t ctx;
|
|
901
|
+
rd_kafka_share_t *consumer;
|
|
902
|
+
int consumed_a, consumed_b;
|
|
903
|
+
|
|
904
|
+
SUB_TEST();
|
|
905
|
+
ctx = test_ctx_new();
|
|
906
|
+
|
|
907
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 500);
|
|
908
|
+
|
|
909
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
910
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
911
|
+
"Failed to create mock topic");
|
|
912
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
913
|
+
msgcnt);
|
|
914
|
+
|
|
915
|
+
/* Consumer A acquires records. */
|
|
916
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-sf-error");
|
|
917
|
+
subscribe_topics(consumer, &topic, 1);
|
|
918
|
+
consumed_a =
|
|
919
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
920
|
+
TEST_SAY("sf_error_drops_ack: A consumed %d/%d\n", consumed_a, msgcnt);
|
|
921
|
+
|
|
922
|
+
/* Inject ShareFetch transport errors — the next ShareFetch
|
|
923
|
+
* (which would carry acks) will cause a disconnect. */
|
|
924
|
+
rd_kafka_mock_push_request_errors(
|
|
925
|
+
ctx.mcluster, RD_KAFKAP_ShareFetch, 10,
|
|
926
|
+
RD_KAFKA_RESP_ERR__TRANSPORT, RD_KAFKA_RESP_ERR__TRANSPORT,
|
|
927
|
+
RD_KAFKA_RESP_ERR__TRANSPORT, RD_KAFKA_RESP_ERR__TRANSPORT,
|
|
928
|
+
RD_KAFKA_RESP_ERR__TRANSPORT, RD_KAFKA_RESP_ERR__TRANSPORT,
|
|
929
|
+
RD_KAFKA_RESP_ERR__TRANSPORT, RD_KAFKA_RESP_ERR__TRANSPORT,
|
|
930
|
+
RD_KAFKA_RESP_ERR__TRANSPORT, RD_KAFKA_RESP_ERR__TRANSPORT);
|
|
931
|
+
|
|
932
|
+
/* Crash consumer A (ack never delivered). */
|
|
933
|
+
test_share_destroy(consumer);
|
|
934
|
+
rd_usleep(1500 * 1000, NULL); /* wait for lock expiry */
|
|
935
|
+
|
|
936
|
+
/* Clear any remaining errors. */
|
|
937
|
+
rd_kafka_mock_clear_request_errors(ctx.mcluster, RD_KAFKAP_ShareFetch);
|
|
938
|
+
|
|
939
|
+
/* Consumer B should get re-delivered records. */
|
|
940
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-sf-error");
|
|
941
|
+
subscribe_topics(consumer, &topic, 1);
|
|
942
|
+
consumed_b =
|
|
943
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
944
|
+
TEST_SAY("sf_error_drops_ack: B consumed %d/%d (re-delivered)\n",
|
|
945
|
+
consumed_b, msgcnt);
|
|
946
|
+
|
|
947
|
+
test_share_consumer_close(consumer);
|
|
948
|
+
test_share_destroy(consumer);
|
|
949
|
+
test_ctx_destroy(&ctx);
|
|
950
|
+
|
|
951
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt,
|
|
952
|
+
"Expected A=%d B=%d, got A=%d B=%d", msgcnt, msgcnt,
|
|
953
|
+
consumed_a, consumed_b);
|
|
954
|
+
SUB_TEST_PASS();
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* @brief Un-acked records from a forgotten topic remain available.
|
|
959
|
+
*
|
|
960
|
+
* 1. Subscribe to topic_a and topic_b, consume from both.
|
|
961
|
+
* 2. Crash consumer immediately (no ack for either topic).
|
|
962
|
+
* 3. Wait for lock expiry so records become AVAILABLE.
|
|
963
|
+
* 4. Consumer B subscribes to topic_a only, consumes and acks topic_a.
|
|
964
|
+
* 5. Consumer C subscribes to topic_b only — should see topic_b's
|
|
965
|
+
* records (they were never acked, only released by lock expiry).
|
|
966
|
+
*/
|
|
967
|
+
static void do_test_forgotten_topic_releases_not_acks(void) {
|
|
968
|
+
const char *topic_a = "kip932_ack_forget_a";
|
|
969
|
+
const char *topic_b = "kip932_ack_forget_b";
|
|
970
|
+
const char *both[] = {topic_a, topic_b};
|
|
971
|
+
test_ctx_t ctx;
|
|
972
|
+
rd_kafka_share_t *consumer;
|
|
973
|
+
int consumed_both, consumed_a, consumed_b, extra;
|
|
974
|
+
|
|
975
|
+
SUB_TEST();
|
|
976
|
+
ctx = test_ctx_new();
|
|
977
|
+
|
|
978
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 2000);
|
|
979
|
+
rd_kafka_mock_sharegroup_set_heartbeat_interval(ctx.mcluster, 500);
|
|
980
|
+
|
|
981
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic_a, 1, 1) ==
|
|
982
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
983
|
+
"Failed to create mock topic A");
|
|
984
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic_b, 1, 1) ==
|
|
985
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
986
|
+
"Failed to create mock topic B");
|
|
987
|
+
|
|
988
|
+
test_produce_msgs_simple(ctx.producer, topic_a, RD_KAFKA_PARTITION_UA,
|
|
989
|
+
2);
|
|
990
|
+
test_produce_msgs_simple(ctx.producer, topic_b, RD_KAFKA_PARTITION_UA,
|
|
991
|
+
2);
|
|
992
|
+
|
|
993
|
+
/* Consumer A: subscribe to both, consume all 4, then crash. */
|
|
994
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-forget");
|
|
995
|
+
subscribe_topics(consumer, both, 2);
|
|
996
|
+
|
|
997
|
+
consumed_both = test_share_consume_msgs(consumer, 4, 60, 500, NULL, 0);
|
|
998
|
+
TEST_SAY("forgotten_releases: consumed %d/4 from both\n",
|
|
999
|
+
consumed_both);
|
|
1000
|
+
|
|
1001
|
+
/* Crash — no ack for either topic. */
|
|
1002
|
+
test_share_destroy(consumer);
|
|
1003
|
+
rd_usleep(3000 * 1000, NULL); /* wait for session + lock expiry */
|
|
1004
|
+
|
|
1005
|
+
/* Consumer B: subscribe to topic_a only, consume and ack. */
|
|
1006
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-forget");
|
|
1007
|
+
subscribe_topics(consumer, &topic_a, 1);
|
|
1008
|
+
|
|
1009
|
+
consumed_a = test_share_consume_msgs(consumer, 2, 40, 500, NULL, 0);
|
|
1010
|
+
TEST_SAY("forgotten_releases: B consumed %d/2 from topic_a\n",
|
|
1011
|
+
consumed_a);
|
|
1012
|
+
|
|
1013
|
+
/* Trigger implicit ack for topic_a. */
|
|
1014
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
1015
|
+
test_share_consumer_close(consumer);
|
|
1016
|
+
test_share_destroy(consumer);
|
|
1017
|
+
|
|
1018
|
+
/* Consumer C: subscribe to topic_b only.
|
|
1019
|
+
* Topic_b's records were never acked — should be available. */
|
|
1020
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-forget");
|
|
1021
|
+
subscribe_topics(consumer, &topic_b, 1);
|
|
1022
|
+
|
|
1023
|
+
consumed_b = test_share_consume_msgs(consumer, 2, 40, 500, NULL, 0);
|
|
1024
|
+
TEST_SAY(
|
|
1025
|
+
"forgotten_releases: C consumed %d/2 from topic_b "
|
|
1026
|
+
"(should be re-delivered)\n",
|
|
1027
|
+
consumed_b);
|
|
1028
|
+
|
|
1029
|
+
test_share_consumer_close(consumer);
|
|
1030
|
+
test_share_destroy(consumer);
|
|
1031
|
+
test_ctx_destroy(&ctx);
|
|
1032
|
+
|
|
1033
|
+
(void)extra;
|
|
1034
|
+
TEST_ASSERT(consumed_both >= 4 && consumed_a == 2 && consumed_b == 2,
|
|
1035
|
+
"Expected both>=4 A=2 B=2, got both=%d A=%d B=%d",
|
|
1036
|
+
consumed_both, consumed_a, consumed_b);
|
|
1037
|
+
SUB_TEST_PASS();
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* @brief Multiple consumers crash sequentially without acking.
|
|
1042
|
+
*
|
|
1043
|
+
* A, B, C each acquire the same records and crash without acking.
|
|
1044
|
+
* Each re-delivery round hands the same records to the next consumer
|
|
1045
|
+
* after lock expiry. This validates that without any ack, records
|
|
1046
|
+
* cycle through ACQUIRED->AVAILABLE indefinitely (bounded by
|
|
1047
|
+
* max_delivery_count, which we set high here).
|
|
1048
|
+
*/
|
|
1049
|
+
static void do_test_multi_consumer_cascade_crash(void) {
|
|
1050
|
+
const char *topic = "kip932_ack_cascade_crash";
|
|
1051
|
+
const int msgcnt = 3;
|
|
1052
|
+
test_ctx_t ctx;
|
|
1053
|
+
rd_kafka_share_t *consumer;
|
|
1054
|
+
int consumed_a, consumed_b, consumed_c;
|
|
1055
|
+
|
|
1056
|
+
SUB_TEST();
|
|
1057
|
+
ctx = test_ctx_new();
|
|
1058
|
+
|
|
1059
|
+
/* High max delivery so records don't get archived. */
|
|
1060
|
+
rd_kafka_mock_sharegroup_set_max_delivery_attempts(ctx.mcluster, 10);
|
|
1061
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 500);
|
|
1062
|
+
|
|
1063
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1064
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1065
|
+
"Failed to create mock topic");
|
|
1066
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1067
|
+
msgcnt);
|
|
1068
|
+
|
|
1069
|
+
/* Consumer A: acquire and crash. */
|
|
1070
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-cascade-crash");
|
|
1071
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1072
|
+
consumed_a =
|
|
1073
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1074
|
+
TEST_SAY("cascade_crash: A consumed %d/%d\n", consumed_a, msgcnt);
|
|
1075
|
+
test_share_destroy(consumer);
|
|
1076
|
+
rd_usleep(1500 * 1000, NULL);
|
|
1077
|
+
|
|
1078
|
+
/* Consumer B: re-acquire and crash. */
|
|
1079
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-cascade-crash");
|
|
1080
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1081
|
+
consumed_b =
|
|
1082
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1083
|
+
TEST_SAY("cascade_crash: B consumed %d/%d\n", consumed_b, msgcnt);
|
|
1084
|
+
test_share_destroy(consumer);
|
|
1085
|
+
rd_usleep(1500 * 1000, NULL);
|
|
1086
|
+
|
|
1087
|
+
/* Consumer C: re-acquire and crash. */
|
|
1088
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-cascade-crash");
|
|
1089
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1090
|
+
consumed_c =
|
|
1091
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1092
|
+
TEST_SAY("cascade_crash: C consumed %d/%d\n", consumed_c, msgcnt);
|
|
1093
|
+
|
|
1094
|
+
test_share_consumer_close(consumer);
|
|
1095
|
+
test_share_destroy(consumer);
|
|
1096
|
+
test_ctx_destroy(&ctx);
|
|
1097
|
+
|
|
1098
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt &&
|
|
1099
|
+
consumed_c == msgcnt,
|
|
1100
|
+
"Expected A=%d B=%d C=%d, got A=%d B=%d C=%d", msgcnt,
|
|
1101
|
+
msgcnt, msgcnt, consumed_a, consumed_b, consumed_c);
|
|
1102
|
+
SUB_TEST_PASS();
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* @brief Lock expires before the ack-carrying ShareFetch arrives.
|
|
1107
|
+
*
|
|
1108
|
+
* 1. Set a very short record lock duration (200ms).
|
|
1109
|
+
* 2. Consumer A acquires records.
|
|
1110
|
+
* 3. Inject high RTT on broker 1 so the next ShareFetch (carrying
|
|
1111
|
+
* the ack) is delayed beyond the lock duration.
|
|
1112
|
+
* 4. Lock expires -> records become AVAILABLE.
|
|
1113
|
+
* 5. Consumer A is destroyed.
|
|
1114
|
+
* 6. Consumer B should be able to get the same records.
|
|
1115
|
+
*/
|
|
1116
|
+
static void do_test_lock_expiry_before_ack(void) {
|
|
1117
|
+
const char *topic = "kip932_ack_lock_expire";
|
|
1118
|
+
const int msgcnt = 3;
|
|
1119
|
+
test_ctx_t ctx;
|
|
1120
|
+
rd_kafka_share_t *consumer;
|
|
1121
|
+
int consumed_a, consumed_b;
|
|
1122
|
+
|
|
1123
|
+
SUB_TEST();
|
|
1124
|
+
ctx = test_ctx_new();
|
|
1125
|
+
|
|
1126
|
+
/* Very short lock, long session timeout. */
|
|
1127
|
+
rd_kafka_mock_sharegroup_set_record_lock_duration(ctx.mcluster, 200);
|
|
1128
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 10000);
|
|
1129
|
+
|
|
1130
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1131
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1132
|
+
"Failed to create mock topic");
|
|
1133
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1134
|
+
msgcnt);
|
|
1135
|
+
|
|
1136
|
+
/* Consumer A acquires records. */
|
|
1137
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-lockexpire");
|
|
1138
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1139
|
+
consumed_a =
|
|
1140
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1141
|
+
TEST_SAY("lock_expiry_before_ack: A consumed %d/%d\n", consumed_a,
|
|
1142
|
+
msgcnt);
|
|
1143
|
+
|
|
1144
|
+
/* Inject high RTT on all brokers so the next ShareFetch
|
|
1145
|
+
* (carrying the ack) is delayed well past lock expiry. */
|
|
1146
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 1, 3000);
|
|
1147
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 2, 3000);
|
|
1148
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 3, 3000);
|
|
1149
|
+
|
|
1150
|
+
/* Wait for lock to expire (200ms + margin), then crash. */
|
|
1151
|
+
rd_usleep(800 * 1000, NULL);
|
|
1152
|
+
test_share_destroy(consumer);
|
|
1153
|
+
|
|
1154
|
+
/* Clear RTT so consumer B can operate normally. */
|
|
1155
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 1, 0);
|
|
1156
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 2, 0);
|
|
1157
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 3, 0);
|
|
1158
|
+
|
|
1159
|
+
/* Consumer B should get the records — locks expired before
|
|
1160
|
+
* A's ack could be delivered. */
|
|
1161
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-lockexpire");
|
|
1162
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1163
|
+
consumed_b =
|
|
1164
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1165
|
+
TEST_SAY("lock_expiry_before_ack: B consumed %d/%d\n", consumed_b,
|
|
1166
|
+
msgcnt);
|
|
1167
|
+
|
|
1168
|
+
test_share_consumer_close(consumer);
|
|
1169
|
+
test_share_destroy(consumer);
|
|
1170
|
+
test_ctx_destroy(&ctx);
|
|
1171
|
+
|
|
1172
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt,
|
|
1173
|
+
"Expected A=%d B=%d, got A=%d B=%d", msgcnt, msgcnt,
|
|
1174
|
+
consumed_a, consumed_b);
|
|
1175
|
+
SUB_TEST_PASS();
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/**
|
|
1179
|
+
* @brief Empty topic produces no ack side effects.
|
|
1180
|
+
*
|
|
1181
|
+
* 1. Subscribe to an empty topic, poll several times (no records,
|
|
1182
|
+
* no AcquiredRecords, no acks sent).
|
|
1183
|
+
* 2. Produce messages.
|
|
1184
|
+
* 3. Consumer should consume them normally.
|
|
1185
|
+
* 4. Ack them normally (implicit ack via next poll).
|
|
1186
|
+
* 5. Verify 0 after ack.
|
|
1187
|
+
*/
|
|
1188
|
+
static void do_test_empty_topic_no_ack_side_effects(void) {
|
|
1189
|
+
const char *topic = "kip932_ack_empty_topic";
|
|
1190
|
+
const int msgcnt = 3;
|
|
1191
|
+
test_ctx_t ctx = test_ctx_new();
|
|
1192
|
+
rd_kafka_share_t *consumer;
|
|
1193
|
+
int consumed_empty, consumed, extra;
|
|
1194
|
+
|
|
1195
|
+
SUB_TEST_QUICK();
|
|
1196
|
+
|
|
1197
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1198
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1199
|
+
"Failed to create mock topic");
|
|
1200
|
+
|
|
1201
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-empty-topic");
|
|
1202
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1203
|
+
|
|
1204
|
+
/* Phase 1: Poll empty topic — no records, no acks. */
|
|
1205
|
+
consumed_empty = test_share_consume_msgs(consumer, 1, 5, 500, NULL, 0);
|
|
1206
|
+
TEST_SAY("empty_topic: phase1 consumed %d/0 (should be 0)\n",
|
|
1207
|
+
consumed_empty);
|
|
1208
|
+
|
|
1209
|
+
/* Phase 2: Produce messages, then close this consumer and
|
|
1210
|
+
* open a new one to avoid incremental session issues. */
|
|
1211
|
+
test_share_consumer_close(consumer);
|
|
1212
|
+
test_share_destroy(consumer);
|
|
1213
|
+
|
|
1214
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1215
|
+
msgcnt);
|
|
1216
|
+
|
|
1217
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-empty-topic");
|
|
1218
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1219
|
+
|
|
1220
|
+
consumed = test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1221
|
+
TEST_SAY("empty_topic: phase2 consumed %d/%d\n", consumed, msgcnt);
|
|
1222
|
+
|
|
1223
|
+
/* Phase 3: Trigger implicit ack, verify 0. */
|
|
1224
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
1225
|
+
TEST_SAY("empty_topic: phase3 extra %d/0 (should be 0)\n", extra);
|
|
1226
|
+
|
|
1227
|
+
test_share_consumer_close(consumer);
|
|
1228
|
+
test_share_destroy(consumer);
|
|
1229
|
+
test_ctx_destroy(&ctx);
|
|
1230
|
+
|
|
1231
|
+
TEST_ASSERT(consumed_empty == 0 && consumed == msgcnt && extra == 0,
|
|
1232
|
+
"Expected empty=0 consumed=%d extra=0, got %d %d %d",
|
|
1233
|
+
msgcnt, consumed_empty, consumed, extra);
|
|
1234
|
+
SUB_TEST_PASS();
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/**
|
|
1238
|
+
* @brief Coordinator failover — consumer recovers and acks new records.
|
|
1239
|
+
*
|
|
1240
|
+
* 1. Consumer A consumes and acks records (happy path).
|
|
1241
|
+
* 2. Push SGHB errors -> member evicted.
|
|
1242
|
+
* 3. Clear errors -> member re-joins.
|
|
1243
|
+
* 4. Produce new records.
|
|
1244
|
+
* 5. Consumer (new instance, same group) consumes and acks new records.
|
|
1245
|
+
* 6. Consumer C joins — should see 0 (everything acked).
|
|
1246
|
+
*/
|
|
1247
|
+
static void do_test_coordinator_failover_ack_recovery(void) {
|
|
1248
|
+
const char *topic = "kip932_ack_coord_failover";
|
|
1249
|
+
const int msgcnt = 3;
|
|
1250
|
+
test_ctx_t ctx;
|
|
1251
|
+
rd_kafka_share_t *consumer;
|
|
1252
|
+
int consumed_a, consumed_b, consumed_c, extra;
|
|
1253
|
+
|
|
1254
|
+
SUB_TEST();
|
|
1255
|
+
ctx = test_ctx_new();
|
|
1256
|
+
|
|
1257
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 500);
|
|
1258
|
+
|
|
1259
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1260
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1261
|
+
"Failed to create mock topic");
|
|
1262
|
+
|
|
1263
|
+
/* Phase 1: Consumer A consumes and acks normally. */
|
|
1264
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1265
|
+
msgcnt);
|
|
1266
|
+
|
|
1267
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-coord-failover");
|
|
1268
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1269
|
+
consumed_a =
|
|
1270
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1271
|
+
TEST_SAY("coord_failover: A consumed %d/%d\n", consumed_a, msgcnt);
|
|
1272
|
+
|
|
1273
|
+
/* Trigger implicit ack. */
|
|
1274
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
1275
|
+
TEST_SAY("coord_failover: A extra %d/0\n", extra);
|
|
1276
|
+
|
|
1277
|
+
test_share_consumer_close(consumer);
|
|
1278
|
+
test_share_destroy(consumer);
|
|
1279
|
+
|
|
1280
|
+
/* Phase 2: Temporarily push SGHB errors (simulating coordinator
|
|
1281
|
+
* failover). Any consumer joining now will fail heartbeats. */
|
|
1282
|
+
rd_kafka_mock_push_request_errors(
|
|
1283
|
+
ctx.mcluster, RD_KAFKAP_ShareGroupHeartbeat, 10,
|
|
1284
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1285
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1286
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1287
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1288
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1289
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1290
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1291
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1292
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
|
|
1293
|
+
RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE);
|
|
1294
|
+
|
|
1295
|
+
/* Wait for errors to start draining, then produce new records. */
|
|
1296
|
+
rd_usleep(1000 * 1000, NULL);
|
|
1297
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1298
|
+
msgcnt);
|
|
1299
|
+
|
|
1300
|
+
/* Phase 3: Errors drain, new consumer B joins and consumes
|
|
1301
|
+
* the newly produced records (A's records are already acked). */
|
|
1302
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-coord-failover");
|
|
1303
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1304
|
+
consumed_b =
|
|
1305
|
+
test_share_consume_msgs(consumer, msgcnt, 60, 500, NULL, 0);
|
|
1306
|
+
TEST_SAY("coord_failover: B consumed %d/%d\n", consumed_b, msgcnt);
|
|
1307
|
+
|
|
1308
|
+
/* Trigger implicit ack for B's records. */
|
|
1309
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
1310
|
+
TEST_SAY("coord_failover: B extra %d/0\n", extra);
|
|
1311
|
+
|
|
1312
|
+
test_share_consumer_close(consumer);
|
|
1313
|
+
test_share_destroy(consumer);
|
|
1314
|
+
|
|
1315
|
+
/* Phase 4: Consumer C — everything is acked, should see 0. */
|
|
1316
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-coord-failover");
|
|
1317
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1318
|
+
consumed_c = test_share_consume_msgs(consumer, 1, 15, 500, NULL, 0);
|
|
1319
|
+
TEST_SAY("coord_failover: C consumed %d/0 (should be 0)\n", consumed_c);
|
|
1320
|
+
|
|
1321
|
+
test_share_consumer_close(consumer);
|
|
1322
|
+
test_share_destroy(consumer);
|
|
1323
|
+
test_ctx_destroy(&ctx);
|
|
1324
|
+
|
|
1325
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt &&
|
|
1326
|
+
consumed_c == 0,
|
|
1327
|
+
"Expected A=%d B=%d C=0, got A=%d B=%d C=%d", msgcnt,
|
|
1328
|
+
msgcnt, consumed_a, consumed_b, consumed_c);
|
|
1329
|
+
SUB_TEST_PASS();
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
/**
|
|
1333
|
+
* @brief Test that ack validation returns INVALID_RECORD_STATE when the
|
|
1334
|
+
* record's lock has expired and was re-acquired by another consumer.
|
|
1335
|
+
*
|
|
1336
|
+
* Consumer A acquires records, locks expire, consumer B re-acquires and
|
|
1337
|
+
* successfully acks. The records should not be redelivered a third time
|
|
1338
|
+
* (consumer B's ack succeeded because the mock broker now correctly
|
|
1339
|
+
* reports INVALID_RECORD_STATE for stale acks and honours valid ones).
|
|
1340
|
+
*/
|
|
1341
|
+
static void do_test_ack_after_lock_expiry_redelivers(void) {
|
|
1342
|
+
const char *topic = "kip932_ack_invalid_state";
|
|
1343
|
+
const int msgcnt = 3;
|
|
1344
|
+
test_ctx_t ctx;
|
|
1345
|
+
rd_kafka_share_t *consumer_a, *consumer_b, *consumer_c;
|
|
1346
|
+
int consumed_a, consumed_b, consumed_c;
|
|
1347
|
+
|
|
1348
|
+
SUB_TEST();
|
|
1349
|
+
ctx = test_ctx_new();
|
|
1350
|
+
|
|
1351
|
+
/* Short lock duration so locks expire quickly. */
|
|
1352
|
+
rd_kafka_mock_sharegroup_set_record_lock_duration(ctx.mcluster, 200);
|
|
1353
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 10000);
|
|
1354
|
+
|
|
1355
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1356
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1357
|
+
"Failed to create mock topic");
|
|
1358
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1359
|
+
msgcnt);
|
|
1360
|
+
|
|
1361
|
+
/* Consumer A acquires records. */
|
|
1362
|
+
consumer_a = new_share_consumer(ctx.bootstraps, "sg-ack-invalid-state");
|
|
1363
|
+
subscribe_topics(consumer_a, &topic, 1);
|
|
1364
|
+
consumed_a =
|
|
1365
|
+
test_share_consume_msgs(consumer_a, msgcnt, 50, 500, NULL, 0);
|
|
1366
|
+
TEST_SAY("ack_invalid_state: A consumed %d/%d\n", consumed_a, msgcnt);
|
|
1367
|
+
|
|
1368
|
+
/* Inject RTT so A's ack is delayed past lock expiry. */
|
|
1369
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 1, 3000);
|
|
1370
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 2, 3000);
|
|
1371
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 3, 3000);
|
|
1372
|
+
|
|
1373
|
+
/* Wait for lock to expire. */
|
|
1374
|
+
rd_usleep(800 * 1000, NULL);
|
|
1375
|
+
|
|
1376
|
+
/* Destroy A without close — ack never delivered. */
|
|
1377
|
+
rd_kafka_share_destroy(consumer_a);
|
|
1378
|
+
|
|
1379
|
+
/* Clear RTT. */
|
|
1380
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 1, 0);
|
|
1381
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 2, 0);
|
|
1382
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 3, 0);
|
|
1383
|
+
|
|
1384
|
+
/* Consumer B re-acquires (locks expired) and acks implicitly
|
|
1385
|
+
* by doing a second poll (which piggybacks the ack on the next
|
|
1386
|
+
* ShareFetch). */
|
|
1387
|
+
consumer_b = new_share_consumer(ctx.bootstraps, "sg-ack-invalid-state");
|
|
1388
|
+
subscribe_topics(consumer_b, &topic, 1);
|
|
1389
|
+
consumed_b =
|
|
1390
|
+
test_share_consume_msgs(consumer_b, msgcnt, 50, 500, NULL, 0);
|
|
1391
|
+
TEST_SAY("ack_invalid_state: B consumed %d/%d\n", consumed_b, msgcnt);
|
|
1392
|
+
|
|
1393
|
+
/* Second poll triggers implicit ack for B's records. */
|
|
1394
|
+
test_share_consume_msgs(consumer_b, 1, 3, 500, NULL, 0);
|
|
1395
|
+
|
|
1396
|
+
rd_kafka_share_consumer_close(consumer_b);
|
|
1397
|
+
rd_kafka_share_destroy(consumer_b);
|
|
1398
|
+
|
|
1399
|
+
/* Consumer C should get 0 records — B's ack succeeded. */
|
|
1400
|
+
consumer_c = new_share_consumer(ctx.bootstraps, "sg-ack-invalid-state");
|
|
1401
|
+
subscribe_topics(consumer_c, &topic, 1);
|
|
1402
|
+
consumed_c = test_share_consume_msgs(consumer_c, 1, 5, 500, NULL, 0);
|
|
1403
|
+
TEST_SAY("ack_invalid_state: C consumed %d (expected 0)\n", consumed_c);
|
|
1404
|
+
|
|
1405
|
+
rd_kafka_share_consumer_close(consumer_c);
|
|
1406
|
+
rd_kafka_share_destroy(consumer_c);
|
|
1407
|
+
test_ctx_destroy(&ctx);
|
|
1408
|
+
|
|
1409
|
+
TEST_ASSERT(consumed_a == msgcnt, "A: expected %d consumed, got %d",
|
|
1410
|
+
msgcnt, consumed_a);
|
|
1411
|
+
TEST_ASSERT(consumed_b == msgcnt, "B: expected %d consumed, got %d",
|
|
1412
|
+
msgcnt, consumed_b);
|
|
1413
|
+
TEST_ASSERT(consumed_c == 0, "C: expected 0 consumed (B acked), got %d",
|
|
1414
|
+
consumed_c);
|
|
1415
|
+
SUB_TEST_PASS();
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* @brief Test that two consumers can sequentially acquire, ack, and
|
|
1420
|
+
* advance SPSO without interference.
|
|
1421
|
+
*
|
|
1422
|
+
* Consumer A acquires records 0-2 and acks them (implicit ack via
|
|
1423
|
+
* second poll). Consumer B then gets records 3-5 (not 0-2, since
|
|
1424
|
+
* those were acked and SPSO advanced). Validates that the ack
|
|
1425
|
+
* error handling doesn't interfere with normal ack flow.
|
|
1426
|
+
*/
|
|
1427
|
+
static void do_test_ack_success_advances_spso(void) {
|
|
1428
|
+
const char *topic = "kip932_ack_spso_advance";
|
|
1429
|
+
const int msgcnt = 6;
|
|
1430
|
+
test_ctx_t ctx;
|
|
1431
|
+
rd_kafka_share_t *consumer;
|
|
1432
|
+
int consumed_a, consumed_b;
|
|
1433
|
+
|
|
1434
|
+
SUB_TEST();
|
|
1435
|
+
ctx = test_ctx_new();
|
|
1436
|
+
|
|
1437
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1438
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1439
|
+
"Failed to create mock topic");
|
|
1440
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1441
|
+
msgcnt);
|
|
1442
|
+
|
|
1443
|
+
/* Consumer A acquires first batch. MaxRecords in the client
|
|
1444
|
+
* defaults to a large value, so it will get all 6. We use
|
|
1445
|
+
* the max_record_locks limit to cap acquisition at 3. */
|
|
1446
|
+
rd_kafka_mock_sharegroup_set_max_record_locks(ctx.mcluster, 3);
|
|
1447
|
+
|
|
1448
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-spso");
|
|
1449
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1450
|
+
consumed_a = test_share_consume_msgs(consumer, 3, 30, 500, NULL, 0);
|
|
1451
|
+
TEST_SAY("ack_spso: A consumed %d/3\n", consumed_a);
|
|
1452
|
+
|
|
1453
|
+
/* Second poll triggers implicit ack for A's records. */
|
|
1454
|
+
test_share_consume_msgs(consumer, 1, 3, 500, NULL, 0);
|
|
1455
|
+
|
|
1456
|
+
/* Close A cleanly. */
|
|
1457
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1458
|
+
rd_kafka_share_destroy(consumer);
|
|
1459
|
+
|
|
1460
|
+
/* Remove lock limit so B can get remaining records. */
|
|
1461
|
+
rd_kafka_mock_sharegroup_set_max_record_locks(ctx.mcluster, 0);
|
|
1462
|
+
|
|
1463
|
+
/* Consumer B should get records 3-5 (SPSO advanced past 0-2). */
|
|
1464
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-spso");
|
|
1465
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1466
|
+
consumed_b = test_share_consume_msgs(consumer, 3, 30, 500, NULL, 0);
|
|
1467
|
+
TEST_SAY("ack_spso: B consumed %d/3\n", consumed_b);
|
|
1468
|
+
|
|
1469
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1470
|
+
rd_kafka_share_destroy(consumer);
|
|
1471
|
+
test_ctx_destroy(&ctx);
|
|
1472
|
+
|
|
1473
|
+
TEST_ASSERT(consumed_a == 3, "A: expected 3 consumed, got %d",
|
|
1474
|
+
consumed_a);
|
|
1475
|
+
TEST_ASSERT(consumed_b == 3, "B: expected 3 consumed, got %d",
|
|
1476
|
+
consumed_b);
|
|
1477
|
+
SUB_TEST_PASS();
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* @brief epoch=-1 (final fetch) must not acquire new records.
|
|
1483
|
+
*
|
|
1484
|
+
* Consumer A acquires two batches (max_record_locks=3 caps each),
|
|
1485
|
+
* then closes. The close sends epoch=-1 which releases A's
|
|
1486
|
+
* un-acked records but must not acquire anything new.
|
|
1487
|
+
* Consumer B picks up the released records; consumer C sees 0.
|
|
1488
|
+
*/
|
|
1489
|
+
static void do_test_final_fetch_no_acquisition(void) {
|
|
1490
|
+
const char *topic = "kip932_final_fetch_no_acq";
|
|
1491
|
+
const int msgcnt = 6;
|
|
1492
|
+
test_ctx_t ctx;
|
|
1493
|
+
rd_kafka_share_t *consumer, *consumer_c;
|
|
1494
|
+
int consumed_a, consumed_a2, consumed_b, consumed_c, extra;
|
|
1495
|
+
|
|
1496
|
+
SUB_TEST();
|
|
1497
|
+
ctx = test_ctx_new();
|
|
1498
|
+
|
|
1499
|
+
/* Limit acquisition to 3 records at a time. */
|
|
1500
|
+
rd_kafka_mock_sharegroup_set_max_record_locks(ctx.mcluster, 3);
|
|
1501
|
+
|
|
1502
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1503
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1504
|
+
"Failed to create mock topic");
|
|
1505
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1506
|
+
msgcnt);
|
|
1507
|
+
|
|
1508
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-final-no-acq");
|
|
1509
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1510
|
+
|
|
1511
|
+
consumed_a = test_share_consume_msgs(consumer, 3, 40, 500, NULL, 0);
|
|
1512
|
+
TEST_SAY("final_fetch_no_acq: A batch1 consumed %d/3\n", consumed_a);
|
|
1513
|
+
|
|
1514
|
+
/* Implicit ack for batch1, acquires batch2. */
|
|
1515
|
+
consumed_a2 = test_share_consume_msgs(consumer, 3, 40, 500, NULL, 0);
|
|
1516
|
+
TEST_SAY("final_fetch_no_acq: A batch2 consumed %d/3\n", consumed_a2);
|
|
1517
|
+
|
|
1518
|
+
/* Close sends epoch=-1; batch2 records released. */
|
|
1519
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1520
|
+
rd_kafka_share_destroy(consumer);
|
|
1521
|
+
|
|
1522
|
+
rd_kafka_mock_sharegroup_set_max_record_locks(ctx.mcluster, 0);
|
|
1523
|
+
|
|
1524
|
+
/* B picks up the released batch2 records. */
|
|
1525
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-final-no-acq");
|
|
1526
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1527
|
+
|
|
1528
|
+
consumed_b = test_share_consume_msgs(consumer, 3, 40, 500, NULL, 0);
|
|
1529
|
+
TEST_SAY("final_fetch_no_acq: B consumed %d/3\n", consumed_b);
|
|
1530
|
+
|
|
1531
|
+
extra = test_share_consume_msgs(consumer, 1, 5, 500, NULL,
|
|
1532
|
+
0); /* ack B's records */
|
|
1533
|
+
|
|
1534
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1535
|
+
rd_kafka_share_destroy(consumer);
|
|
1536
|
+
|
|
1537
|
+
/* C should see nothing — everything acked. */
|
|
1538
|
+
consumer_c = new_share_consumer(ctx.bootstraps, "sg-final-no-acq");
|
|
1539
|
+
subscribe_topics(consumer_c, &topic, 1);
|
|
1540
|
+
consumed_c = test_share_consume_msgs(consumer_c, 1, 10, 500, NULL, 0);
|
|
1541
|
+
TEST_SAY("final_fetch_no_acq: C consumed %d/0 (should be 0)\n",
|
|
1542
|
+
consumed_c);
|
|
1543
|
+
rd_kafka_share_consumer_close(consumer_c);
|
|
1544
|
+
rd_kafka_share_destroy(consumer_c);
|
|
1545
|
+
|
|
1546
|
+
TEST_ASSERT(consumed_c == 0, "C: expected 0 consumed, got %d",
|
|
1547
|
+
consumed_c);
|
|
1548
|
+
|
|
1549
|
+
test_ctx_destroy(&ctx);
|
|
1550
|
+
|
|
1551
|
+
(void)extra;
|
|
1552
|
+
TEST_ASSERT(consumed_a == 3 && consumed_a2 == 3 && consumed_b == 3,
|
|
1553
|
+
"Expected A1=3 A2=3 B=3, got A1=%d A2=%d B=%d", consumed_a,
|
|
1554
|
+
consumed_a2, consumed_b);
|
|
1555
|
+
SUB_TEST_PASS();
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* @brief Ack for records past SPSO (log retention) succeeds silently.
|
|
1560
|
+
*
|
|
1561
|
+
* Consumer A acquires 5 records, then log retention moves
|
|
1562
|
+
* start_offset past the first 3. The implicit ack covers all 5,
|
|
1563
|
+
* including the 3 that are now below SPSO. Those must be silently
|
|
1564
|
+
* accepted rather than returning INVALID_RECORD_STATE.
|
|
1565
|
+
* Consumer B should see 0.
|
|
1566
|
+
*/
|
|
1567
|
+
static void do_test_ack_after_log_retention_silent(void) {
|
|
1568
|
+
const char *topic = "kip932_ack_log_retention_silent";
|
|
1569
|
+
const int msgcnt = 5;
|
|
1570
|
+
test_ctx_t ctx;
|
|
1571
|
+
rd_kafka_share_t *consumer;
|
|
1572
|
+
int consumed_a, consumed_b, extra;
|
|
1573
|
+
|
|
1574
|
+
SUB_TEST();
|
|
1575
|
+
ctx = test_ctx_new();
|
|
1576
|
+
|
|
1577
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1578
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1579
|
+
"Failed to create mock topic");
|
|
1580
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1581
|
+
msgcnt);
|
|
1582
|
+
|
|
1583
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-log-ret-silent");
|
|
1584
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1585
|
+
|
|
1586
|
+
consumed_a =
|
|
1587
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1588
|
+
TEST_SAY("ack_log_retention_silent: A consumed %d/%d\n", consumed_a,
|
|
1589
|
+
msgcnt);
|
|
1590
|
+
|
|
1591
|
+
/* Move start_offset past first 3 records (simulates retention). */
|
|
1592
|
+
rd_kafka_mock_partition_set_follower_wmarks(ctx.mcluster, topic, 0, 3,
|
|
1593
|
+
-1);
|
|
1594
|
+
|
|
1595
|
+
/* Implicit ack covers all 5; offsets 0-2 are below SPSO now. */
|
|
1596
|
+
extra = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
1597
|
+
TEST_SAY("ack_log_retention_silent: A extra %d/0\n", extra);
|
|
1598
|
+
|
|
1599
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1600
|
+
rd_kafka_share_destroy(consumer);
|
|
1601
|
+
|
|
1602
|
+
/* B should see nothing. */
|
|
1603
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-log-ret-silent");
|
|
1604
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1605
|
+
consumed_b = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
1606
|
+
TEST_SAY("ack_log_retention_silent: B consumed %d/0 (should be 0)\n",
|
|
1607
|
+
consumed_b);
|
|
1608
|
+
|
|
1609
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1610
|
+
rd_kafka_share_destroy(consumer);
|
|
1611
|
+
test_ctx_destroy(&ctx);
|
|
1612
|
+
|
|
1613
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == 0,
|
|
1614
|
+
"Expected A=%d B=0, got A=%d B=%d", msgcnt, consumed_a,
|
|
1615
|
+
consumed_b);
|
|
1616
|
+
SUB_TEST_PASS();
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/**
|
|
1620
|
+
* @brief Ack atomicity — expired locks cause full batch rejection.
|
|
1621
|
+
*
|
|
1622
|
+
* Consumer A acquires records but its locks expire before the ack
|
|
1623
|
+
* arrives (RTT delay). Because acks are atomic per partition, the
|
|
1624
|
+
* entire batch is rejected rather than partially applied.
|
|
1625
|
+
* Consumer B re-acquires, acks, and consumer C sees 0.
|
|
1626
|
+
*/
|
|
1627
|
+
static void do_test_ack_atomicity_lock_expiry(void) {
|
|
1628
|
+
const char *topic = "kip932_ack_atomicity";
|
|
1629
|
+
const int msgcnt = 3;
|
|
1630
|
+
test_ctx_t ctx;
|
|
1631
|
+
rd_kafka_share_t *consumer;
|
|
1632
|
+
int consumed_a, consumed_b, consumed_c;
|
|
1633
|
+
|
|
1634
|
+
SUB_TEST();
|
|
1635
|
+
ctx = test_ctx_new();
|
|
1636
|
+
|
|
1637
|
+
/* Very short lock so it expires before A can ack. */
|
|
1638
|
+
rd_kafka_mock_sharegroup_set_record_lock_duration(ctx.mcluster, 200);
|
|
1639
|
+
rd_kafka_mock_sharegroup_set_session_timeout(ctx.mcluster, 10000);
|
|
1640
|
+
|
|
1641
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1642
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1643
|
+
"Failed to create mock topic");
|
|
1644
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1645
|
+
msgcnt);
|
|
1646
|
+
|
|
1647
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-atomicity");
|
|
1648
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1649
|
+
consumed_a =
|
|
1650
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1651
|
+
TEST_SAY("ack_atomicity: A consumed %d/%d\n", consumed_a, msgcnt);
|
|
1652
|
+
|
|
1653
|
+
/* Delay A's next ShareFetch past lock expiry. */
|
|
1654
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 1, 3000);
|
|
1655
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 2, 3000);
|
|
1656
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 3, 3000);
|
|
1657
|
+
|
|
1658
|
+
rd_usleep(800 * 1000, NULL); /* locks expire */
|
|
1659
|
+
rd_kafka_share_destroy(consumer); /* crash */
|
|
1660
|
+
|
|
1661
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 1, 0);
|
|
1662
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 2, 0);
|
|
1663
|
+
rd_kafka_mock_broker_set_rtt(ctx.mcluster, 3, 0);
|
|
1664
|
+
|
|
1665
|
+
/* B re-acquires everything (A's ack was rejected atomically). */
|
|
1666
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-atomicity");
|
|
1667
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1668
|
+
consumed_b =
|
|
1669
|
+
test_share_consume_msgs(consumer, msgcnt, 50, 500, NULL, 0);
|
|
1670
|
+
TEST_SAY("ack_atomicity: B consumed %d/%d (re-delivered)\n", consumed_b,
|
|
1671
|
+
msgcnt);
|
|
1672
|
+
|
|
1673
|
+
test_share_consume_msgs(consumer, 1, 5, 500, NULL,
|
|
1674
|
+
0); /* ack B's records */
|
|
1675
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1676
|
+
rd_kafka_share_destroy(consumer);
|
|
1677
|
+
|
|
1678
|
+
/* C sees nothing. */
|
|
1679
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-ack-atomicity");
|
|
1680
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1681
|
+
consumed_c = test_share_consume_msgs(consumer, 1, 10, 500, NULL, 0);
|
|
1682
|
+
TEST_SAY("ack_atomicity: C consumed %d/0 (should be 0)\n", consumed_c);
|
|
1683
|
+
|
|
1684
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1685
|
+
rd_kafka_share_destroy(consumer);
|
|
1686
|
+
test_ctx_destroy(&ctx);
|
|
1687
|
+
|
|
1688
|
+
TEST_ASSERT(consumed_a == msgcnt && consumed_b == msgcnt &&
|
|
1689
|
+
consumed_c == 0,
|
|
1690
|
+
"Expected A=%d B=%d C=0, got A=%d B=%d C=%d", msgcnt,
|
|
1691
|
+
msgcnt, consumed_a, consumed_b, consumed_c);
|
|
1692
|
+
SUB_TEST_PASS();
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
/**
|
|
1696
|
+
* @brief Leader change mid-session -> client redirects.
|
|
1697
|
+
*
|
|
1698
|
+
* Consumer fetches from broker 1, then we move the partition leader
|
|
1699
|
+
* to broker 2. The next ShareFetch to broker 1 gets
|
|
1700
|
+
* NOT_LEADER_OR_FOLLOWER; the client refreshes metadata and
|
|
1701
|
+
* continues from broker 2.
|
|
1702
|
+
*/
|
|
1703
|
+
static void do_test_not_leader_or_follower_redirect(void) {
|
|
1704
|
+
const char *topic = "kip932_not_leader";
|
|
1705
|
+
const int msgcnt = 6;
|
|
1706
|
+
test_ctx_t ctx;
|
|
1707
|
+
rd_kafka_share_t *consumer;
|
|
1708
|
+
int consumed;
|
|
1709
|
+
|
|
1710
|
+
SUB_TEST();
|
|
1711
|
+
ctx = test_ctx_new();
|
|
1712
|
+
|
|
1713
|
+
/* Two fetch rounds: 3 records each. */
|
|
1714
|
+
rd_kafka_mock_sharegroup_set_max_record_locks(ctx.mcluster, 3);
|
|
1715
|
+
|
|
1716
|
+
TEST_ASSERT(rd_kafka_mock_topic_create(ctx.mcluster, topic, 1, 1) ==
|
|
1717
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1718
|
+
"Failed to create mock topic");
|
|
1719
|
+
|
|
1720
|
+
rd_kafka_mock_partition_set_leader(ctx.mcluster, topic, 0, 1);
|
|
1721
|
+
test_produce_msgs_simple(ctx.producer, topic, RD_KAFKA_PARTITION_UA,
|
|
1722
|
+
msgcnt);
|
|
1723
|
+
|
|
1724
|
+
consumer = new_share_consumer(ctx.bootstraps, "sg-not-leader");
|
|
1725
|
+
subscribe_topics(consumer, &topic, 1);
|
|
1726
|
+
|
|
1727
|
+
consumed = test_share_consume_msgs(consumer, 3, 40, 500, NULL, 0);
|
|
1728
|
+
TEST_SAY("not_leader: consumed %d/3 from broker 1\n", consumed);
|
|
1729
|
+
|
|
1730
|
+
/* Move leader to broker 2 between fetches. */
|
|
1731
|
+
rd_kafka_mock_partition_set_leader(ctx.mcluster, topic, 0, 2);
|
|
1732
|
+
rd_kafka_mock_sharegroup_set_max_record_locks(ctx.mcluster, 0);
|
|
1733
|
+
|
|
1734
|
+
/* Client should handle the redirect transparently. */
|
|
1735
|
+
consumed += test_share_consume_msgs(consumer, 3, 60, 500, NULL, 0);
|
|
1736
|
+
TEST_SAY("not_leader: total consumed %d/6\n", consumed);
|
|
1737
|
+
|
|
1738
|
+
rd_kafka_share_consumer_close(consumer);
|
|
1739
|
+
rd_kafka_share_destroy(consumer);
|
|
1740
|
+
test_ctx_destroy(&ctx);
|
|
1741
|
+
|
|
1742
|
+
TEST_ASSERT(consumed == msgcnt, "Expected consumed=%d, got %d", msgcnt,
|
|
1743
|
+
consumed);
|
|
1744
|
+
SUB_TEST_PASS();
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
/* ===================================================================
|
|
1748
|
+
* Test runner
|
|
1749
|
+
* =================================================================== */
|
|
1750
|
+
|
|
1751
|
+
int main_0157_share_consumer_ack_mock(int argc, char **argv) {
|
|
1752
|
+
TEST_SKIP_MOCK_CLUSTER(0);
|
|
1753
|
+
test_timeout_set(200);
|
|
1754
|
+
|
|
1755
|
+
/* Positive scenarios */
|
|
1756
|
+
do_test_implicit_ack_no_redelivery();
|
|
1757
|
+
do_test_implicit_ack_with_new_records();
|
|
1758
|
+
do_test_implicit_ack_cross_consumer();
|
|
1759
|
+
do_test_implicit_ack_multi_partition();
|
|
1760
|
+
do_test_implicit_ack_multiple_rounds();
|
|
1761
|
+
do_test_implicit_ack_single_record();
|
|
1762
|
+
do_test_implicit_ack_large_batch();
|
|
1763
|
+
do_test_implicit_ack_multi_topic();
|
|
1764
|
+
do_test_implicit_ack_multi_msgset();
|
|
1765
|
+
|
|
1766
|
+
/* Negative scenarios */
|
|
1767
|
+
do_test_crash_before_ack_redelivery();
|
|
1768
|
+
do_test_crash_then_ack_stops_redelivery();
|
|
1769
|
+
do_test_session_expiry_invalidates_ack();
|
|
1770
|
+
do_test_max_delivery_without_ack();
|
|
1771
|
+
do_test_sharefetch_error_drops_ack();
|
|
1772
|
+
do_test_forgotten_topic_releases_not_acks();
|
|
1773
|
+
do_test_multi_consumer_cascade_crash();
|
|
1774
|
+
do_test_lock_expiry_before_ack();
|
|
1775
|
+
do_test_empty_topic_no_ack_side_effects();
|
|
1776
|
+
do_test_coordinator_failover_ack_recovery();
|
|
1777
|
+
|
|
1778
|
+
/* Ack validation */
|
|
1779
|
+
do_test_ack_after_lock_expiry_redelivers();
|
|
1780
|
+
do_test_ack_success_advances_spso();
|
|
1781
|
+
|
|
1782
|
+
do_test_final_fetch_no_acquisition();
|
|
1783
|
+
do_test_ack_after_log_retention_silent();
|
|
1784
|
+
do_test_ack_atomicity_lock_expiry();
|
|
1785
|
+
do_test_not_leader_or_follower_redirect();
|
|
1786
|
+
|
|
1787
|
+
return 0;
|
|
1788
|
+
}
|