@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,1815 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* librdkafka - Apache Kafka C library
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025, Confluent Inc.
|
|
5
|
+
* All rights reserved.
|
|
6
|
+
*
|
|
7
|
+
* Redistribution and use in source and binary forms, with or without
|
|
8
|
+
* modification, are permitted provided that the following conditions are met:
|
|
9
|
+
*
|
|
10
|
+
* 1. Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
* this list of conditions and the following disclaimer.
|
|
12
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
* this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
* and/or other materials provided with the distribution.
|
|
15
|
+
*
|
|
16
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
20
|
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
21
|
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
22
|
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
23
|
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
24
|
+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
25
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
#include "test.h"
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @brief Share consumer transaction tests.
|
|
33
|
+
*
|
|
34
|
+
* Tests that share consumers correctly handle transactional messages
|
|
35
|
+
* based on the isolation.level configuration:
|
|
36
|
+
*
|
|
37
|
+
* - read_committed: Only see committed messages, aborted filtered out
|
|
38
|
+
* - read_uncommitted: See ALL messages including aborted ones
|
|
39
|
+
*
|
|
40
|
+
* Both modes are tested to verify correct behavior.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
#define BATCH_SIZE 1000
|
|
44
|
+
|
|
45
|
+
/* Common handles shared across all tests */
|
|
46
|
+
static rd_kafka_t *common_admin;
|
|
47
|
+
static rd_kafka_t *common_regular_producer;
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @brief Create a transactional producer.
|
|
52
|
+
*/
|
|
53
|
+
static rd_kafka_t *create_txn_producer(const char *txn_id) {
|
|
54
|
+
rd_kafka_t *rk;
|
|
55
|
+
rd_kafka_conf_t *conf;
|
|
56
|
+
|
|
57
|
+
test_conf_init(&conf, NULL, 0);
|
|
58
|
+
test_conf_set(conf, "transactional.id", txn_id);
|
|
59
|
+
rd_kafka_conf_set_dr_msg_cb(conf, test_dr_msg_cb);
|
|
60
|
+
|
|
61
|
+
rk = test_create_handle(RD_KAFKA_PRODUCER, conf);
|
|
62
|
+
TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
|
|
63
|
+
|
|
64
|
+
return rk;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @brief Configure share group settings.
|
|
70
|
+
*
|
|
71
|
+
* Sets share.auto.offset.reset=earliest and share.isolation.level
|
|
72
|
+
* based on the isolation_level parameter.
|
|
73
|
+
*
|
|
74
|
+
* Note: share.isolation.level is a BROKER-SIDE share group configuration.
|
|
75
|
+
* Transaction filtering happens on the broker, not the client.
|
|
76
|
+
*/
|
|
77
|
+
static void configure_share_group(const char *group,
|
|
78
|
+
const char *isolation_level) {
|
|
79
|
+
test_share_set_auto_offset_reset(group, "earliest");
|
|
80
|
+
test_share_set_isolation_level(group, isolation_level);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @brief Subscribe share consumer to a topic.
|
|
86
|
+
*/
|
|
87
|
+
static void subscribe_share_consumer(rd_kafka_share_t *rkshare,
|
|
88
|
+
const char *topic) {
|
|
89
|
+
rd_kafka_topic_partition_list_t *subs;
|
|
90
|
+
rd_kafka_resp_err_t err;
|
|
91
|
+
|
|
92
|
+
subs = rd_kafka_topic_partition_list_new(1);
|
|
93
|
+
rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
|
|
94
|
+
|
|
95
|
+
err = rd_kafka_share_subscribe(rkshare, subs);
|
|
96
|
+
TEST_ASSERT(!err, "Share subscribe failed: %s", rd_kafka_err2str(err));
|
|
97
|
+
|
|
98
|
+
rd_kafka_topic_partition_list_destroy(subs);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @brief Consume messages from share consumer.
|
|
104
|
+
*
|
|
105
|
+
* @returns Number of non-error messages consumed.
|
|
106
|
+
*/
|
|
107
|
+
static int consume_share_messages(rd_kafka_share_t *rkshare,
|
|
108
|
+
int expected_cnt,
|
|
109
|
+
int max_attempts,
|
|
110
|
+
int poll_timeout_ms) {
|
|
111
|
+
rd_kafka_messages_t *batch = NULL;
|
|
112
|
+
int consumed = 0;
|
|
113
|
+
int attempts = max_attempts;
|
|
114
|
+
|
|
115
|
+
while (consumed < expected_cnt && attempts-- > 0) {
|
|
116
|
+
size_t rcvd = 0;
|
|
117
|
+
size_t i;
|
|
118
|
+
rd_kafka_error_t *err;
|
|
119
|
+
|
|
120
|
+
err = rd_kafka_share_poll(rkshare, poll_timeout_ms, &batch);
|
|
121
|
+
if (err) {
|
|
122
|
+
rd_kafka_error_destroy(err);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
127
|
+
for (i = 0; i < rcvd; i++) {
|
|
128
|
+
rd_kafka_message_t *msg =
|
|
129
|
+
rd_kafka_messages_get(batch, i);
|
|
130
|
+
if (!msg->err)
|
|
131
|
+
consumed++;
|
|
132
|
+
}
|
|
133
|
+
rd_kafka_messages_destroy(batch);
|
|
134
|
+
batch = NULL;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return consumed;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @brief Poll share consumer expecting no messages (for abort verification).
|
|
143
|
+
*
|
|
144
|
+
* @returns Number of messages received (should be 0 for aborted txns
|
|
145
|
+
* with read_committed).
|
|
146
|
+
*/
|
|
147
|
+
static int consume_share_no_msgs(rd_kafka_share_t *rkshare,
|
|
148
|
+
int poll_attempts,
|
|
149
|
+
int poll_timeout_ms) {
|
|
150
|
+
rd_kafka_messages_t *batch = NULL;
|
|
151
|
+
int consumed = 0;
|
|
152
|
+
int attempts = poll_attempts;
|
|
153
|
+
|
|
154
|
+
while (attempts-- > 0) {
|
|
155
|
+
size_t rcvd = 0;
|
|
156
|
+
size_t i;
|
|
157
|
+
rd_kafka_error_t *err;
|
|
158
|
+
|
|
159
|
+
err = rd_kafka_share_poll(rkshare, poll_timeout_ms, &batch);
|
|
160
|
+
if (err) {
|
|
161
|
+
rd_kafka_error_destroy(err);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
166
|
+
for (i = 0; i < rcvd; i++) {
|
|
167
|
+
rd_kafka_message_t *msg =
|
|
168
|
+
rd_kafka_messages_get(batch, i);
|
|
169
|
+
if (!msg->err)
|
|
170
|
+
consumed++;
|
|
171
|
+
}
|
|
172
|
+
rd_kafka_messages_destroy(batch);
|
|
173
|
+
batch = NULL;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return consumed;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @brief Consume messages and verify exact offsets.
|
|
182
|
+
*
|
|
183
|
+
* Consumes messages from the share consumer and verifies that:
|
|
184
|
+
* 1. The number of messages matches expected_msg_cnt
|
|
185
|
+
* 2. The offsets match the expected_offsets array
|
|
186
|
+
* 3. None of the forbidden_offsets appear (e.g., control records)
|
|
187
|
+
*
|
|
188
|
+
* @param rkshare Share consumer instance
|
|
189
|
+
* @param expected_msg_cnt Expected number of messages to consume
|
|
190
|
+
* @param expected_offsets Array of expected offsets in order
|
|
191
|
+
* @param forbidden_offsets Array of offsets that should never appear
|
|
192
|
+
* @param forbidden_cnt Number of forbidden offsets
|
|
193
|
+
* @param isolation_level Isolation level string (for logging)
|
|
194
|
+
*/
|
|
195
|
+
static void consume_and_verify_offsets(rd_kafka_share_t *rkshare,
|
|
196
|
+
int expected_msg_cnt,
|
|
197
|
+
const int64_t *expected_offsets,
|
|
198
|
+
const int64_t *forbidden_offsets,
|
|
199
|
+
int forbidden_cnt,
|
|
200
|
+
const char *isolation_level) {
|
|
201
|
+
rd_kafka_messages_t *batch = NULL;
|
|
202
|
+
int attempts = 50;
|
|
203
|
+
int64_t received_offsets[10];
|
|
204
|
+
int consumed = 0;
|
|
205
|
+
int i, j;
|
|
206
|
+
|
|
207
|
+
TEST_SAY("[%s] Consuming messages and verifying offsets:\n",
|
|
208
|
+
isolation_level);
|
|
209
|
+
|
|
210
|
+
/* Consume messages and collect offsets */
|
|
211
|
+
while (consumed < expected_msg_cnt && attempts-- > 0) {
|
|
212
|
+
size_t rcvd = 0;
|
|
213
|
+
rd_kafka_error_t *err;
|
|
214
|
+
|
|
215
|
+
err = rd_kafka_share_poll(rkshare, 3000, &batch);
|
|
216
|
+
if (err) {
|
|
217
|
+
rd_kafka_error_destroy(err);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
222
|
+
for (i = 0; i < (int)rcvd; i++) {
|
|
223
|
+
rd_kafka_message_t *msg =
|
|
224
|
+
rd_kafka_messages_get(batch, i);
|
|
225
|
+
if (!msg->err && consumed < 10) {
|
|
226
|
+
received_offsets[consumed] = msg->offset;
|
|
227
|
+
TEST_SAY(" Message %d: offset=%" PRId64 "\n",
|
|
228
|
+
consumed, msg->offset);
|
|
229
|
+
consumed++;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
rd_kafka_messages_destroy(batch);
|
|
233
|
+
batch = NULL;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* Verify no forbidden offsets were received */
|
|
237
|
+
for (i = 0; i < consumed; i++) {
|
|
238
|
+
for (j = 0; j < forbidden_cnt; j++) {
|
|
239
|
+
TEST_ASSERT(received_offsets[i] != forbidden_offsets[j],
|
|
240
|
+
"[%s] Received forbidden offset %" PRId64
|
|
241
|
+
" at position %d - should be filtered!",
|
|
242
|
+
isolation_level, forbidden_offsets[j], i);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Verify message count */
|
|
247
|
+
TEST_ASSERT(consumed == expected_msg_cnt,
|
|
248
|
+
"[%s] Expected %d messages, got %d", isolation_level,
|
|
249
|
+
expected_msg_cnt, consumed);
|
|
250
|
+
|
|
251
|
+
/* Verify exact offsets */
|
|
252
|
+
for (i = 0; i < consumed; i++) {
|
|
253
|
+
TEST_ASSERT(received_offsets[i] == expected_offsets[i],
|
|
254
|
+
"[%s] Message %d: expected offset %" PRId64
|
|
255
|
+
", got %" PRId64,
|
|
256
|
+
isolation_level, i, expected_offsets[i],
|
|
257
|
+
received_offsets[i]);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
TEST_SAY(
|
|
261
|
+
"SUCCESS [%s]: All %d offsets verified correctly, "
|
|
262
|
+
"forbidden offsets filtered\n",
|
|
263
|
+
isolation_level, consumed);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @brief Test committed transaction visibility.
|
|
269
|
+
*
|
|
270
|
+
* Both read_committed and read_uncommitted should see committed messages.
|
|
271
|
+
*/
|
|
272
|
+
static void do_test_committed_transaction(const char *isolation_level) {
|
|
273
|
+
char topic_suffix[64];
|
|
274
|
+
const char *topic;
|
|
275
|
+
char group[128];
|
|
276
|
+
const char *txn_id = "txn-commit-test";
|
|
277
|
+
const int msg_cnt = 100;
|
|
278
|
+
rd_kafka_t *producer;
|
|
279
|
+
rd_kafka_share_t *consumer;
|
|
280
|
+
int consumed;
|
|
281
|
+
|
|
282
|
+
rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-commit-%s",
|
|
283
|
+
isolation_level);
|
|
284
|
+
topic = test_mk_topic_name(topic_suffix, 1);
|
|
285
|
+
rd_snprintf(group, sizeof(group), "share-txn-commit-%s",
|
|
286
|
+
isolation_level);
|
|
287
|
+
|
|
288
|
+
SUB_TEST("isolation.level=%s", isolation_level);
|
|
289
|
+
|
|
290
|
+
/* Create topic */
|
|
291
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
292
|
+
|
|
293
|
+
/* Create transactional producer */
|
|
294
|
+
producer = create_txn_producer(txn_id);
|
|
295
|
+
|
|
296
|
+
/* Create share consumer and set group config */
|
|
297
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
298
|
+
configure_share_group(group, isolation_level);
|
|
299
|
+
subscribe_share_consumer(consumer, topic);
|
|
300
|
+
|
|
301
|
+
/* Begin transaction */
|
|
302
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
303
|
+
|
|
304
|
+
/* Produce messages */
|
|
305
|
+
TEST_SAY("Producing %d messages in transaction\n", msg_cnt);
|
|
306
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
307
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
308
|
+
|
|
309
|
+
/* Commit transaction */
|
|
310
|
+
TEST_SAY("Committing transaction\n");
|
|
311
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
|
|
312
|
+
|
|
313
|
+
/* Consume messages - both modes should receive committed messages */
|
|
314
|
+
TEST_SAY("Consuming messages via share consumer (%s)\n",
|
|
315
|
+
isolation_level);
|
|
316
|
+
consumed = consume_share_messages(consumer, msg_cnt, 50, 3000);
|
|
317
|
+
|
|
318
|
+
TEST_ASSERT(consumed == msg_cnt,
|
|
319
|
+
"[%s] Expected %d messages from committed transaction, "
|
|
320
|
+
"got %d",
|
|
321
|
+
isolation_level, msg_cnt, consumed);
|
|
322
|
+
|
|
323
|
+
TEST_SAY(
|
|
324
|
+
"SUCCESS [%s]: Share consumer received %d committed "
|
|
325
|
+
"messages\n",
|
|
326
|
+
isolation_level, consumed);
|
|
327
|
+
|
|
328
|
+
/* Cleanup */
|
|
329
|
+
test_share_consumer_close(consumer);
|
|
330
|
+
test_share_destroy(consumer);
|
|
331
|
+
rd_kafka_destroy(producer);
|
|
332
|
+
|
|
333
|
+
SUB_TEST_PASS();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* @brief Test aborted transaction visibility.
|
|
339
|
+
*
|
|
340
|
+
* - read_committed: Should NOT see aborted messages (expect 0)
|
|
341
|
+
* - read_uncommitted: Should see ALL messages (expect msg_cnt)
|
|
342
|
+
*/
|
|
343
|
+
static void do_test_aborted_transaction(const char *isolation_level) {
|
|
344
|
+
char topic_suffix[64];
|
|
345
|
+
const char *topic;
|
|
346
|
+
char group[128];
|
|
347
|
+
const char *txn_id = "txn-abort-test";
|
|
348
|
+
const int msg_cnt = 100;
|
|
349
|
+
rd_kafka_t *producer;
|
|
350
|
+
rd_kafka_share_t *consumer;
|
|
351
|
+
int consumed;
|
|
352
|
+
int expected_msgs;
|
|
353
|
+
rd_bool_t is_read_committed =
|
|
354
|
+
!strcmp(isolation_level, "read_committed");
|
|
355
|
+
|
|
356
|
+
rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-abort-%s",
|
|
357
|
+
isolation_level);
|
|
358
|
+
topic = test_mk_topic_name(topic_suffix, 1);
|
|
359
|
+
rd_snprintf(group, sizeof(group), "share-txn-abort-%s",
|
|
360
|
+
isolation_level);
|
|
361
|
+
|
|
362
|
+
SUB_TEST("isolation.level=%s", isolation_level);
|
|
363
|
+
|
|
364
|
+
/* Expected messages depend on isolation level:
|
|
365
|
+
* - read_committed: 0 (aborted messages filtered)
|
|
366
|
+
* - read_uncommitted: msg_cnt (all messages visible) */
|
|
367
|
+
expected_msgs = is_read_committed ? 0 : msg_cnt;
|
|
368
|
+
|
|
369
|
+
/* Create topic */
|
|
370
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
371
|
+
|
|
372
|
+
/* Create transactional producer */
|
|
373
|
+
producer = create_txn_producer(txn_id);
|
|
374
|
+
|
|
375
|
+
/* Create share consumer and set group config */
|
|
376
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
377
|
+
configure_share_group(group, isolation_level);
|
|
378
|
+
subscribe_share_consumer(consumer, topic);
|
|
379
|
+
|
|
380
|
+
/* Begin transaction */
|
|
381
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
382
|
+
|
|
383
|
+
/* Produce messages */
|
|
384
|
+
TEST_SAY("Producing %d messages in transaction\n", msg_cnt);
|
|
385
|
+
test_curr->ignore_dr_err = rd_true; /* Aborted messages may fail */
|
|
386
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
387
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
388
|
+
|
|
389
|
+
/* Abort transaction */
|
|
390
|
+
TEST_SAY("Aborting transaction\n");
|
|
391
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
|
|
392
|
+
test_curr->ignore_dr_err = rd_false;
|
|
393
|
+
|
|
394
|
+
/* Consume messages */
|
|
395
|
+
TEST_SAY("Consuming messages via share consumer (%s), expecting %d\n",
|
|
396
|
+
isolation_level, expected_msgs);
|
|
397
|
+
|
|
398
|
+
if (is_read_committed) {
|
|
399
|
+
consumed = consume_share_no_msgs(consumer, 10, 2000);
|
|
400
|
+
} else {
|
|
401
|
+
consumed =
|
|
402
|
+
consume_share_messages(consumer, expected_msgs, 50, 3000);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
TEST_ASSERT(consumed == expected_msgs,
|
|
406
|
+
"[%s] Expected %d messages from aborted transaction, "
|
|
407
|
+
"got %d",
|
|
408
|
+
isolation_level, expected_msgs, consumed);
|
|
409
|
+
|
|
410
|
+
TEST_SAY(
|
|
411
|
+
"SUCCESS [%s]: Share consumer received %d messages "
|
|
412
|
+
"(expected %d)\n",
|
|
413
|
+
isolation_level, consumed, expected_msgs);
|
|
414
|
+
|
|
415
|
+
/* Cleanup */
|
|
416
|
+
test_share_consumer_close(consumer);
|
|
417
|
+
test_share_destroy(consumer);
|
|
418
|
+
rd_kafka_destroy(producer);
|
|
419
|
+
|
|
420
|
+
SUB_TEST_PASS();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* @brief Test mixed transactions (some committed, some aborted).
|
|
426
|
+
*
|
|
427
|
+
* - read_committed: Only see committed messages
|
|
428
|
+
* - read_uncommitted: See ALL messages
|
|
429
|
+
*/
|
|
430
|
+
static void do_test_mixed_transactions(const char *isolation_level) {
|
|
431
|
+
char topic_suffix[64];
|
|
432
|
+
const char *topic;
|
|
433
|
+
char group[128];
|
|
434
|
+
const char *txn_id = "txn-mixed-test";
|
|
435
|
+
rd_kafka_t *producer;
|
|
436
|
+
rd_kafka_share_t *consumer;
|
|
437
|
+
int consumed;
|
|
438
|
+
int i;
|
|
439
|
+
int expected_committed = 0;
|
|
440
|
+
int expected_total = 0;
|
|
441
|
+
int expected_msgs;
|
|
442
|
+
rd_bool_t is_read_committed =
|
|
443
|
+
!strcmp(isolation_level, "read_committed");
|
|
444
|
+
|
|
445
|
+
/* Define transaction scenarios */
|
|
446
|
+
struct {
|
|
447
|
+
const char *desc;
|
|
448
|
+
rd_bool_t commit;
|
|
449
|
+
int msg_cnt;
|
|
450
|
+
} txns[] = {
|
|
451
|
+
{"Commit 50 msgs", rd_true, 50}, {"Abort 50 msgs", rd_false, 50},
|
|
452
|
+
{"Commit 30 msgs", rd_true, 30}, {"Abort 20 msgs", rd_false, 20},
|
|
453
|
+
{"Commit 40 msgs", rd_true, 40},
|
|
454
|
+
};
|
|
455
|
+
const int txn_cnt = sizeof(txns) / sizeof(txns[0]);
|
|
456
|
+
|
|
457
|
+
rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-mixed-%s",
|
|
458
|
+
isolation_level);
|
|
459
|
+
topic = test_mk_topic_name(topic_suffix, 1);
|
|
460
|
+
rd_snprintf(group, sizeof(group), "share-txn-mixed-%s",
|
|
461
|
+
isolation_level);
|
|
462
|
+
|
|
463
|
+
SUB_TEST("isolation.level=%s", isolation_level);
|
|
464
|
+
|
|
465
|
+
/* Create topic */
|
|
466
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
467
|
+
|
|
468
|
+
/* Create transactional producer */
|
|
469
|
+
producer = create_txn_producer(txn_id);
|
|
470
|
+
|
|
471
|
+
/* Create share consumer and set group config */
|
|
472
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
473
|
+
configure_share_group(group, isolation_level);
|
|
474
|
+
subscribe_share_consumer(consumer, topic);
|
|
475
|
+
|
|
476
|
+
/* Execute transactions */
|
|
477
|
+
for (i = 0; i < txn_cnt; i++) {
|
|
478
|
+
TEST_SAY("Transaction %d: %s\n", i + 1, txns[i].desc);
|
|
479
|
+
|
|
480
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
481
|
+
|
|
482
|
+
test_curr->ignore_dr_err = !txns[i].commit;
|
|
483
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
484
|
+
RD_KAFKA_PARTITION_UA, 0, txns[i].msg_cnt,
|
|
485
|
+
NULL, 0);
|
|
486
|
+
|
|
487
|
+
if (txns[i].commit) {
|
|
488
|
+
TEST_CALL_ERROR__(
|
|
489
|
+
rd_kafka_commit_transaction(producer, 30000));
|
|
490
|
+
expected_committed += txns[i].msg_cnt;
|
|
491
|
+
} else {
|
|
492
|
+
TEST_CALL_ERROR__(
|
|
493
|
+
rd_kafka_abort_transaction(producer, 30000));
|
|
494
|
+
}
|
|
495
|
+
expected_total += txns[i].msg_cnt;
|
|
496
|
+
test_curr->ignore_dr_err = rd_false;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* Expected depends on isolation level */
|
|
500
|
+
expected_msgs = is_read_committed ? expected_committed : expected_total;
|
|
501
|
+
|
|
502
|
+
TEST_SAY("Expected messages [%s]: %d (committed=%d, total=%d)\n",
|
|
503
|
+
isolation_level, expected_msgs, expected_committed,
|
|
504
|
+
expected_total);
|
|
505
|
+
|
|
506
|
+
/* Consume messages */
|
|
507
|
+
consumed = consume_share_messages(consumer, expected_msgs, 100, 3000);
|
|
508
|
+
|
|
509
|
+
TEST_ASSERT(consumed == expected_msgs,
|
|
510
|
+
"[%s] Expected %d messages, got %d", isolation_level,
|
|
511
|
+
expected_msgs, consumed);
|
|
512
|
+
|
|
513
|
+
TEST_SAY("SUCCESS [%s]: Share consumer received %d messages\n",
|
|
514
|
+
isolation_level, consumed);
|
|
515
|
+
|
|
516
|
+
/* Cleanup */
|
|
517
|
+
test_share_consumer_close(consumer);
|
|
518
|
+
test_share_destroy(consumer);
|
|
519
|
+
rd_kafka_destroy(producer);
|
|
520
|
+
|
|
521
|
+
SUB_TEST_PASS();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* @brief Test control records filtering.
|
|
527
|
+
*
|
|
528
|
+
* Control records (abort/commit markers) should never be visible to the
|
|
529
|
+
* application, regardless of isolation level.
|
|
530
|
+
*/
|
|
531
|
+
static void do_test_control_records_filtered(const char *isolation_level) {
|
|
532
|
+
char topic_suffix[64];
|
|
533
|
+
const char *topic;
|
|
534
|
+
char group[128];
|
|
535
|
+
const char *txn_id = "txn-ctrl-test";
|
|
536
|
+
rd_kafka_t *producer;
|
|
537
|
+
rd_kafka_share_t *consumer;
|
|
538
|
+
int expected_msgs;
|
|
539
|
+
rd_bool_t is_read_committed =
|
|
540
|
+
!strcmp(isolation_level, "read_committed");
|
|
541
|
+
|
|
542
|
+
rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-ctrl-%s",
|
|
543
|
+
isolation_level);
|
|
544
|
+
topic = test_mk_topic_name(topic_suffix, 1);
|
|
545
|
+
rd_snprintf(group, sizeof(group), "share-txn-ctrl-%s", isolation_level);
|
|
546
|
+
|
|
547
|
+
SUB_TEST("isolation.level=%s", isolation_level);
|
|
548
|
+
|
|
549
|
+
/* Create topic */
|
|
550
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
551
|
+
|
|
552
|
+
/* Create transactional producer */
|
|
553
|
+
producer = create_txn_producer(txn_id);
|
|
554
|
+
|
|
555
|
+
/* Create share consumer and set group config */
|
|
556
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
557
|
+
configure_share_group(group, isolation_level);
|
|
558
|
+
subscribe_share_consumer(consumer, topic);
|
|
559
|
+
|
|
560
|
+
/* Transaction 1: Produce and ABORT (1 msg + 1 abort marker) */
|
|
561
|
+
TEST_SAY("Transaction 1: Produce 1 message, then ABORT\n");
|
|
562
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
563
|
+
test_curr->ignore_dr_err = rd_true;
|
|
564
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
565
|
+
RD_KAFKA_PARTITION_UA, 0, 1, NULL, 0);
|
|
566
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
|
|
567
|
+
test_curr->ignore_dr_err = rd_false;
|
|
568
|
+
|
|
569
|
+
/* Transaction 2: Produce and COMMIT (5 msgs + 1 commit marker) */
|
|
570
|
+
TEST_SAY("Transaction 2: Produce 5 messages, then COMMIT\n");
|
|
571
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
572
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
573
|
+
RD_KAFKA_PARTITION_UA, 0, 5, NULL, 0);
|
|
574
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
|
|
575
|
+
|
|
576
|
+
/*
|
|
577
|
+
* Topic now contains:
|
|
578
|
+
* - Offset 0: aborted message
|
|
579
|
+
* - Offset 1: abort control record
|
|
580
|
+
* - Offset 2-6: committed messages
|
|
581
|
+
* - Offset 7: commit control record
|
|
582
|
+
*
|
|
583
|
+
* read_committed: should see offsets 2,3,4,5,6 (committed only)
|
|
584
|
+
* read_uncommitted: should see offsets 0,2,3,4,5,6 (aborted +
|
|
585
|
+
* committed) Control records (offsets 1 and 7) should NEVER be visible.
|
|
586
|
+
*/
|
|
587
|
+
expected_msgs = is_read_committed ? 5 : 6;
|
|
588
|
+
|
|
589
|
+
/* Consume and verify exact offsets */
|
|
590
|
+
int64_t expected_offsets_committed[] = {2, 3, 4, 5, 6};
|
|
591
|
+
int64_t expected_offsets_uncommitted[] = {0, 2, 3, 4, 5, 6};
|
|
592
|
+
int64_t control_record_offsets[] = {1, 7};
|
|
593
|
+
const int64_t *expected_offsets;
|
|
594
|
+
|
|
595
|
+
expected_offsets = is_read_committed ? expected_offsets_committed
|
|
596
|
+
: expected_offsets_uncommitted;
|
|
597
|
+
|
|
598
|
+
consume_and_verify_offsets(consumer, expected_msgs, expected_offsets,
|
|
599
|
+
control_record_offsets, 2, isolation_level);
|
|
600
|
+
|
|
601
|
+
/* Cleanup */
|
|
602
|
+
test_share_consumer_close(consumer);
|
|
603
|
+
test_share_destroy(consumer);
|
|
604
|
+
rd_kafka_destroy(producer);
|
|
605
|
+
|
|
606
|
+
SUB_TEST_PASS();
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* @brief Test multiple partitions with transactions.
|
|
612
|
+
*/
|
|
613
|
+
static void do_test_multi_partition_transactions(const char *isolation_level) {
|
|
614
|
+
char topic_suffix[64];
|
|
615
|
+
const char *topic;
|
|
616
|
+
char group[128];
|
|
617
|
+
const char *txn_id = "txn-multipart-test";
|
|
618
|
+
const int partition_cnt = 3;
|
|
619
|
+
const int msgs_per_partition = 30;
|
|
620
|
+
rd_kafka_t *producer;
|
|
621
|
+
rd_kafka_share_t *consumer;
|
|
622
|
+
int consumed;
|
|
623
|
+
int expected_committed;
|
|
624
|
+
int expected_total;
|
|
625
|
+
int expected_msgs;
|
|
626
|
+
rd_bool_t is_read_committed =
|
|
627
|
+
!strcmp(isolation_level, "read_committed");
|
|
628
|
+
|
|
629
|
+
rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-multipart-%s",
|
|
630
|
+
isolation_level);
|
|
631
|
+
topic = test_mk_topic_name(topic_suffix, 1);
|
|
632
|
+
rd_snprintf(group, sizeof(group), "share-txn-multipart-%s",
|
|
633
|
+
isolation_level);
|
|
634
|
+
|
|
635
|
+
SUB_TEST("isolation.level=%s", isolation_level);
|
|
636
|
+
|
|
637
|
+
/* Create topic with multiple partitions */
|
|
638
|
+
test_create_topic(NULL, topic, partition_cnt, -1);
|
|
639
|
+
|
|
640
|
+
/* Create transactional producer */
|
|
641
|
+
producer = create_txn_producer(txn_id);
|
|
642
|
+
|
|
643
|
+
/* Create share consumer and set group config */
|
|
644
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
645
|
+
configure_share_group(group, isolation_level);
|
|
646
|
+
subscribe_share_consumer(consumer, topic);
|
|
647
|
+
|
|
648
|
+
/* Transaction 1: Produce to all partitions and COMMIT */
|
|
649
|
+
TEST_SAY("Transaction 1: Produce to all partitions, COMMIT\n");
|
|
650
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
651
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
652
|
+
RD_KAFKA_PARTITION_UA, 0,
|
|
653
|
+
msgs_per_partition * partition_cnt, NULL, 0);
|
|
654
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
|
|
655
|
+
|
|
656
|
+
/* Transaction 2: Produce to all partitions and ABORT */
|
|
657
|
+
TEST_SAY("Transaction 2: Produce to all partitions, ABORT\n");
|
|
658
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
659
|
+
test_curr->ignore_dr_err = rd_true;
|
|
660
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
661
|
+
RD_KAFKA_PARTITION_UA, 0,
|
|
662
|
+
msgs_per_partition * partition_cnt, NULL, 0);
|
|
663
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
|
|
664
|
+
test_curr->ignore_dr_err = rd_false;
|
|
665
|
+
|
|
666
|
+
/* Transaction 3: Produce to all partitions and COMMIT */
|
|
667
|
+
TEST_SAY("Transaction 3: Produce to all partitions, COMMIT\n");
|
|
668
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
669
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
670
|
+
RD_KAFKA_PARTITION_UA, 0,
|
|
671
|
+
msgs_per_partition * partition_cnt, NULL, 0);
|
|
672
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
|
|
673
|
+
|
|
674
|
+
/* Calculate expected messages */
|
|
675
|
+
expected_committed = 2 * msgs_per_partition * partition_cnt;
|
|
676
|
+
expected_total = 3 * msgs_per_partition * partition_cnt;
|
|
677
|
+
expected_msgs = is_read_committed ? expected_committed : expected_total;
|
|
678
|
+
|
|
679
|
+
TEST_SAY("Expected messages [%s]: %d\n", isolation_level,
|
|
680
|
+
expected_msgs);
|
|
681
|
+
|
|
682
|
+
consumed = consume_share_messages(consumer, expected_msgs, 100, 3000);
|
|
683
|
+
|
|
684
|
+
TEST_ASSERT(consumed == expected_msgs,
|
|
685
|
+
"[%s] Expected %d messages, got %d", isolation_level,
|
|
686
|
+
expected_msgs, consumed);
|
|
687
|
+
|
|
688
|
+
TEST_SAY("SUCCESS [%s]: Share consumer received %d messages\n",
|
|
689
|
+
isolation_level, consumed);
|
|
690
|
+
|
|
691
|
+
/* Cleanup */
|
|
692
|
+
test_share_consumer_close(consumer);
|
|
693
|
+
test_share_destroy(consumer);
|
|
694
|
+
rd_kafka_destroy(producer);
|
|
695
|
+
|
|
696
|
+
SUB_TEST_PASS();
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* @brief Test interleaved transactions from multiple producers.
|
|
702
|
+
*/
|
|
703
|
+
static void do_test_interleaved_producers(const char *isolation_level) {
|
|
704
|
+
char topic_suffix[64];
|
|
705
|
+
const char *topic;
|
|
706
|
+
char group[128];
|
|
707
|
+
const int msg_cnt = 50;
|
|
708
|
+
rd_kafka_t *producer1, *producer2;
|
|
709
|
+
rd_kafka_share_t *consumer;
|
|
710
|
+
int consumed;
|
|
711
|
+
int expected_msgs;
|
|
712
|
+
rd_bool_t is_read_committed =
|
|
713
|
+
!strcmp(isolation_level, "read_committed");
|
|
714
|
+
|
|
715
|
+
rd_snprintf(topic_suffix, sizeof(topic_suffix),
|
|
716
|
+
"0177-txn-interleave-%s", isolation_level);
|
|
717
|
+
topic = test_mk_topic_name(topic_suffix, 1);
|
|
718
|
+
rd_snprintf(group, sizeof(group), "share-txn-interleave-%s",
|
|
719
|
+
isolation_level);
|
|
720
|
+
|
|
721
|
+
SUB_TEST("isolation.level=%s", isolation_level);
|
|
722
|
+
|
|
723
|
+
/* Create topic */
|
|
724
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
725
|
+
|
|
726
|
+
/* Create two transactional producers */
|
|
727
|
+
producer1 = create_txn_producer("txn-producer-1");
|
|
728
|
+
producer2 = create_txn_producer("txn-producer-2");
|
|
729
|
+
|
|
730
|
+
/* Create share consumer and set group config */
|
|
731
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
732
|
+
configure_share_group(group, isolation_level);
|
|
733
|
+
subscribe_share_consumer(consumer, topic);
|
|
734
|
+
|
|
735
|
+
/* Producer 1: Begin transaction */
|
|
736
|
+
TEST_SAY("Producer 1: Begin transaction\n");
|
|
737
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer1));
|
|
738
|
+
|
|
739
|
+
/* Producer 2: Begin transaction */
|
|
740
|
+
TEST_SAY("Producer 2: Begin transaction\n");
|
|
741
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer2));
|
|
742
|
+
|
|
743
|
+
/* Producer 1: Produce messages */
|
|
744
|
+
TEST_SAY("Producer 1: Produce %d messages\n", msg_cnt);
|
|
745
|
+
test_produce_msgs2(producer1, topic, test_id_generate(),
|
|
746
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
747
|
+
|
|
748
|
+
/* Producer 2: Produce messages */
|
|
749
|
+
TEST_SAY("Producer 2: Produce %d messages\n", msg_cnt);
|
|
750
|
+
test_curr->ignore_dr_err = rd_true;
|
|
751
|
+
test_produce_msgs2(producer2, topic, test_id_generate(),
|
|
752
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
753
|
+
|
|
754
|
+
/* Producer 1: COMMIT */
|
|
755
|
+
TEST_SAY("Producer 1: COMMIT\n");
|
|
756
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer1, 30000));
|
|
757
|
+
|
|
758
|
+
/* Producer 2: ABORT */
|
|
759
|
+
TEST_SAY("Producer 2: ABORT\n");
|
|
760
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer2, 30000));
|
|
761
|
+
test_curr->ignore_dr_err = rd_false;
|
|
762
|
+
|
|
763
|
+
/* Expected: read_committed sees only producer1's msgs,
|
|
764
|
+
* read_uncommitted sees both */
|
|
765
|
+
expected_msgs = is_read_committed ? msg_cnt : (2 * msg_cnt);
|
|
766
|
+
|
|
767
|
+
consumed = consume_share_messages(consumer, expected_msgs, 50, 3000);
|
|
768
|
+
|
|
769
|
+
TEST_ASSERT(consumed == expected_msgs,
|
|
770
|
+
"[%s] Expected %d messages, got %d", isolation_level,
|
|
771
|
+
expected_msgs, consumed);
|
|
772
|
+
|
|
773
|
+
TEST_SAY("SUCCESS [%s]: Share consumer received %d messages\n",
|
|
774
|
+
isolation_level, consumed);
|
|
775
|
+
|
|
776
|
+
/* Cleanup */
|
|
777
|
+
test_share_consumer_close(consumer);
|
|
778
|
+
test_share_destroy(consumer);
|
|
779
|
+
rd_kafka_destroy(producer1);
|
|
780
|
+
rd_kafka_destroy(producer2);
|
|
781
|
+
|
|
782
|
+
SUB_TEST_PASS();
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* @brief Test non-transactional and transactional messages mixed.
|
|
788
|
+
*/
|
|
789
|
+
static void do_test_mixed_txn_non_txn(const char *isolation_level) {
|
|
790
|
+
char topic_suffix[64];
|
|
791
|
+
const char *topic;
|
|
792
|
+
char group[128];
|
|
793
|
+
const char *txn_id = "txn-mixed-type-test";
|
|
794
|
+
const int non_txn_msgs = 50;
|
|
795
|
+
const int txn_commit_msgs = 30;
|
|
796
|
+
const int txn_abort_msgs = 20;
|
|
797
|
+
rd_kafka_t *txn_producer;
|
|
798
|
+
rd_kafka_share_t *consumer;
|
|
799
|
+
int consumed;
|
|
800
|
+
int expected_msgs;
|
|
801
|
+
rd_bool_t is_read_committed =
|
|
802
|
+
!strcmp(isolation_level, "read_committed");
|
|
803
|
+
|
|
804
|
+
rd_snprintf(topic_suffix, sizeof(topic_suffix),
|
|
805
|
+
"0177-txn-mixed-type-%s", isolation_level);
|
|
806
|
+
topic = test_mk_topic_name(topic_suffix, 1);
|
|
807
|
+
rd_snprintf(group, sizeof(group), "share-txn-mixed-type-%s",
|
|
808
|
+
isolation_level);
|
|
809
|
+
|
|
810
|
+
SUB_TEST("isolation.level=%s", isolation_level);
|
|
811
|
+
|
|
812
|
+
/* Create topic */
|
|
813
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
814
|
+
|
|
815
|
+
/* Create transactional producer */
|
|
816
|
+
txn_producer = create_txn_producer(txn_id);
|
|
817
|
+
|
|
818
|
+
/* Create share consumer and set group config */
|
|
819
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
820
|
+
configure_share_group(group, isolation_level);
|
|
821
|
+
subscribe_share_consumer(consumer, topic);
|
|
822
|
+
|
|
823
|
+
/* Produce non-transactional messages */
|
|
824
|
+
TEST_SAY("Producing %d non-transactional messages\n", non_txn_msgs);
|
|
825
|
+
test_produce_msgs2(common_regular_producer, topic, test_id_generate(),
|
|
826
|
+
RD_KAFKA_PARTITION_UA, 0, non_txn_msgs, NULL, 0);
|
|
827
|
+
|
|
828
|
+
/* Transactional: Commit */
|
|
829
|
+
TEST_SAY("Producing %d transactional messages (will commit)\n",
|
|
830
|
+
txn_commit_msgs);
|
|
831
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(txn_producer));
|
|
832
|
+
test_produce_msgs2(txn_producer, topic, test_id_generate(),
|
|
833
|
+
RD_KAFKA_PARTITION_UA, 0, txn_commit_msgs, NULL, 0);
|
|
834
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(txn_producer, 30000));
|
|
835
|
+
|
|
836
|
+
/* Transactional: Abort */
|
|
837
|
+
TEST_SAY("Producing %d transactional messages (will abort)\n",
|
|
838
|
+
txn_abort_msgs);
|
|
839
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(txn_producer));
|
|
840
|
+
test_curr->ignore_dr_err = rd_true;
|
|
841
|
+
test_produce_msgs2(txn_producer, topic, test_id_generate(),
|
|
842
|
+
RD_KAFKA_PARTITION_UA, 0, txn_abort_msgs, NULL, 0);
|
|
843
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(txn_producer, 30000));
|
|
844
|
+
test_curr->ignore_dr_err = rd_false;
|
|
845
|
+
|
|
846
|
+
/* Expected:
|
|
847
|
+
* read_committed: non-txn + committed = 50 + 30 = 80
|
|
848
|
+
* read_uncommitted: all = 50 + 30 + 20 = 100 */
|
|
849
|
+
expected_msgs = is_read_committed
|
|
850
|
+
? (non_txn_msgs + txn_commit_msgs)
|
|
851
|
+
: (non_txn_msgs + txn_commit_msgs + txn_abort_msgs);
|
|
852
|
+
|
|
853
|
+
TEST_SAY("Expected messages [%s]: %d\n", isolation_level,
|
|
854
|
+
expected_msgs);
|
|
855
|
+
|
|
856
|
+
consumed = consume_share_messages(consumer, expected_msgs, 100, 3000);
|
|
857
|
+
|
|
858
|
+
TEST_ASSERT(consumed == expected_msgs,
|
|
859
|
+
"[%s] Expected %d messages, got %d", isolation_level,
|
|
860
|
+
expected_msgs, consumed);
|
|
861
|
+
|
|
862
|
+
TEST_SAY("SUCCESS [%s]: Share consumer received %d messages\n",
|
|
863
|
+
isolation_level, consumed);
|
|
864
|
+
|
|
865
|
+
/* Cleanup */
|
|
866
|
+
test_share_consumer_close(consumer);
|
|
867
|
+
test_share_destroy(consumer);
|
|
868
|
+
rd_kafka_destroy(txn_producer);
|
|
869
|
+
|
|
870
|
+
SUB_TEST_PASS();
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* @brief Test dynamic isolation level change (READ_UNCOMMITTED ->
|
|
876
|
+
* READ_COMMITTED).
|
|
877
|
+
*
|
|
878
|
+
* 1. Start with READ_UNCOMMITTED
|
|
879
|
+
* 2. Produce committed txn, consume and acknowledge
|
|
880
|
+
* 3. Produce aborted txn, consume and release
|
|
881
|
+
* 4. Change to READ_COMMITTED
|
|
882
|
+
* 5. Produce more committed/aborted txns
|
|
883
|
+
* 6. Verify only committed txns are now returned
|
|
884
|
+
*/
|
|
885
|
+
static void do_test_dynamic_uncommitted_to_committed(void) {
|
|
886
|
+
const char *topic;
|
|
887
|
+
const char *group = "share-dynamic-uc-to-c";
|
|
888
|
+
const char *txn_id = "txn-dynamic-uc-c";
|
|
889
|
+
const int msg_cnt = 10;
|
|
890
|
+
rd_kafka_t *producer;
|
|
891
|
+
rd_kafka_share_t *consumer;
|
|
892
|
+
int consumed;
|
|
893
|
+
|
|
894
|
+
SUB_TEST();
|
|
895
|
+
|
|
896
|
+
topic = test_mk_topic_name("0177-dynamic-uc-to-c", 1);
|
|
897
|
+
|
|
898
|
+
/* Create topic */
|
|
899
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
900
|
+
|
|
901
|
+
/* Create transactional producer */
|
|
902
|
+
producer = create_txn_producer(txn_id);
|
|
903
|
+
|
|
904
|
+
/* Create share consumer with READ_UNCOMMITTED */
|
|
905
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
906
|
+
configure_share_group(group, "read_uncommitted");
|
|
907
|
+
subscribe_share_consumer(consumer, topic);
|
|
908
|
+
|
|
909
|
+
/* Phase 1: Produce and consume committed transaction */
|
|
910
|
+
TEST_SAY("Phase 1: Committed transaction with READ_UNCOMMITTED\n");
|
|
911
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
912
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
913
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
914
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
|
|
915
|
+
|
|
916
|
+
consumed = consume_share_messages(consumer, msg_cnt, 50, 3000);
|
|
917
|
+
TEST_ASSERT(consumed == msg_cnt,
|
|
918
|
+
"Phase 1: Expected %d committed msgs, got %d", msg_cnt,
|
|
919
|
+
consumed);
|
|
920
|
+
|
|
921
|
+
/* Phase 2: Produce aborted transaction - should be visible in
|
|
922
|
+
* READ_UNCOMMITTED */
|
|
923
|
+
TEST_SAY("Phase 2: Aborted transaction with READ_UNCOMMITTED\n");
|
|
924
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
925
|
+
test_curr->ignore_dr_err = rd_true;
|
|
926
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
927
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
928
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
|
|
929
|
+
test_curr->ignore_dr_err = rd_false;
|
|
930
|
+
|
|
931
|
+
consumed = consume_share_messages(consumer, msg_cnt, 50, 3000);
|
|
932
|
+
TEST_ASSERT(consumed == msg_cnt,
|
|
933
|
+
"Phase 2: Expected %d aborted msgs (uncommitted mode), "
|
|
934
|
+
"got %d",
|
|
935
|
+
msg_cnt, consumed);
|
|
936
|
+
|
|
937
|
+
/* Phase 3: Change isolation level to READ_COMMITTED */
|
|
938
|
+
TEST_SAY("Phase 3: Changing isolation level to READ_COMMITTED\n");
|
|
939
|
+
configure_share_group(group, "read_committed");
|
|
940
|
+
|
|
941
|
+
/* Phase 4: Produce aborted transaction - should NOT be visible now */
|
|
942
|
+
TEST_SAY("Phase 4: Aborted transaction with READ_COMMITTED\n");
|
|
943
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
944
|
+
test_curr->ignore_dr_err = rd_true;
|
|
945
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
946
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
947
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
|
|
948
|
+
test_curr->ignore_dr_err = rd_false;
|
|
949
|
+
|
|
950
|
+
consumed = consume_share_no_msgs(consumer, 10, 2000);
|
|
951
|
+
TEST_ASSERT(consumed == 0,
|
|
952
|
+
"Phase 4: Expected 0 aborted msgs (committed mode), got %d",
|
|
953
|
+
consumed);
|
|
954
|
+
|
|
955
|
+
/* Phase 5: Produce committed transaction - should be visible */
|
|
956
|
+
TEST_SAY("Phase 5: Committed transaction with READ_COMMITTED\n");
|
|
957
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
958
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
959
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
960
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
|
|
961
|
+
|
|
962
|
+
consumed = consume_share_messages(consumer, msg_cnt, 50, 3000);
|
|
963
|
+
TEST_ASSERT(consumed == msg_cnt,
|
|
964
|
+
"Phase 5: Expected %d committed msgs, got %d", msg_cnt,
|
|
965
|
+
consumed);
|
|
966
|
+
|
|
967
|
+
TEST_SAY(
|
|
968
|
+
"SUCCESS: Dynamic isolation change READ_UNCOMMITTED -> "
|
|
969
|
+
"READ_COMMITTED works correctly\n");
|
|
970
|
+
|
|
971
|
+
/* Cleanup */
|
|
972
|
+
test_share_consumer_close(consumer);
|
|
973
|
+
test_share_destroy(consumer);
|
|
974
|
+
rd_kafka_destroy(producer);
|
|
975
|
+
|
|
976
|
+
SUB_TEST_PASS();
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* @brief Test interval-based abort pattern.
|
|
982
|
+
*
|
|
983
|
+
* Produce N transactions where every Kth one is aborted.
|
|
984
|
+
* Verify READ_COMMITTED skips exactly the aborted ones.
|
|
985
|
+
* Verify READ_UNCOMMITTED sees all.
|
|
986
|
+
*/
|
|
987
|
+
static void do_test_interval_abort_pattern(const char *isolation_level) {
|
|
988
|
+
char topic_suffix[64];
|
|
989
|
+
const char *topic;
|
|
990
|
+
char group[128];
|
|
991
|
+
const char *txn_id = "txn-interval-abort";
|
|
992
|
+
const int total_txns = 20;
|
|
993
|
+
const int abort_interval = 5; /* Every 5th transaction is aborted */
|
|
994
|
+
const int msgs_per_txn = 5;
|
|
995
|
+
rd_kafka_t *producer;
|
|
996
|
+
rd_kafka_share_t *consumer;
|
|
997
|
+
int consumed;
|
|
998
|
+
int i;
|
|
999
|
+
int committed_txns = 0;
|
|
1000
|
+
int aborted_txns = 0;
|
|
1001
|
+
int expected_msgs;
|
|
1002
|
+
rd_bool_t is_read_committed =
|
|
1003
|
+
!strcmp(isolation_level, "read_committed");
|
|
1004
|
+
|
|
1005
|
+
rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-interval-%s",
|
|
1006
|
+
isolation_level);
|
|
1007
|
+
topic = test_mk_topic_name(topic_suffix, 1);
|
|
1008
|
+
rd_snprintf(group, sizeof(group), "share-interval-%s", isolation_level);
|
|
1009
|
+
|
|
1010
|
+
SUB_TEST("isolation.level=%s", isolation_level);
|
|
1011
|
+
|
|
1012
|
+
/* Create topic */
|
|
1013
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
1014
|
+
|
|
1015
|
+
/* Create transactional producer */
|
|
1016
|
+
producer = create_txn_producer(txn_id);
|
|
1017
|
+
|
|
1018
|
+
/* Create share consumer and set group config */
|
|
1019
|
+
consumer = test_create_share_consumer(group, NULL);
|
|
1020
|
+
configure_share_group(group, isolation_level);
|
|
1021
|
+
subscribe_share_consumer(consumer, topic);
|
|
1022
|
+
|
|
1023
|
+
/* Produce transactions with interval-based abort pattern */
|
|
1024
|
+
TEST_SAY("Producing %d transactions (every %dth is aborted)\n",
|
|
1025
|
+
total_txns, abort_interval);
|
|
1026
|
+
|
|
1027
|
+
for (i = 1; i <= total_txns; i++) {
|
|
1028
|
+
rd_bool_t should_abort = (i % abort_interval == 0);
|
|
1029
|
+
|
|
1030
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
1031
|
+
|
|
1032
|
+
if (should_abort)
|
|
1033
|
+
test_curr->ignore_dr_err = rd_true;
|
|
1034
|
+
|
|
1035
|
+
test_produce_msgs2(producer, topic, test_id_generate(),
|
|
1036
|
+
RD_KAFKA_PARTITION_UA, 0, msgs_per_txn, NULL,
|
|
1037
|
+
0);
|
|
1038
|
+
|
|
1039
|
+
if (should_abort) {
|
|
1040
|
+
TEST_CALL_ERROR__(
|
|
1041
|
+
rd_kafka_abort_transaction(producer, 30000));
|
|
1042
|
+
test_curr->ignore_dr_err = rd_false;
|
|
1043
|
+
aborted_txns++;
|
|
1044
|
+
TEST_SAY(" Transaction %d: ABORTED\n", i);
|
|
1045
|
+
} else {
|
|
1046
|
+
TEST_CALL_ERROR__(
|
|
1047
|
+
rd_kafka_commit_transaction(producer, 30000));
|
|
1048
|
+
committed_txns++;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
TEST_SAY("Produced: %d committed txns, %d aborted txns\n",
|
|
1053
|
+
committed_txns, aborted_txns);
|
|
1054
|
+
|
|
1055
|
+
/* Calculate expected messages */
|
|
1056
|
+
if (is_read_committed) {
|
|
1057
|
+
expected_msgs = committed_txns * msgs_per_txn;
|
|
1058
|
+
} else {
|
|
1059
|
+
expected_msgs = total_txns * msgs_per_txn;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
TEST_SAY("[%s] Expected %d messages\n", isolation_level, expected_msgs);
|
|
1063
|
+
|
|
1064
|
+
/* Consume and verify */
|
|
1065
|
+
consumed = consume_share_messages(consumer, expected_msgs, 200, 3000);
|
|
1066
|
+
|
|
1067
|
+
TEST_ASSERT(consumed == expected_msgs,
|
|
1068
|
+
"[%s] Expected %d messages, got %d", isolation_level,
|
|
1069
|
+
expected_msgs, consumed);
|
|
1070
|
+
|
|
1071
|
+
/* Verify no extra messages */
|
|
1072
|
+
{
|
|
1073
|
+
int extra = consume_share_no_msgs(consumer, 5, 2000);
|
|
1074
|
+
TEST_ASSERT(extra == 0, "[%s] Unexpected extra messages: %d",
|
|
1075
|
+
isolation_level, extra);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
TEST_SAY(
|
|
1079
|
+
"SUCCESS [%s]: Interval abort pattern - received exactly %d "
|
|
1080
|
+
"messages (committed=%d, aborted=%d txns)\n",
|
|
1081
|
+
isolation_level, consumed, committed_txns, aborted_txns);
|
|
1082
|
+
|
|
1083
|
+
/* Cleanup */
|
|
1084
|
+
test_share_consumer_close(consumer);
|
|
1085
|
+
test_share_destroy(consumer);
|
|
1086
|
+
rd_kafka_destroy(producer);
|
|
1087
|
+
|
|
1088
|
+
SUB_TEST_PASS();
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* @brief Produce exactly one transactional record carrying \p value, then
|
|
1094
|
+
* commit (\p commit=rd_true) or abort the transaction.
|
|
1095
|
+
*/
|
|
1096
|
+
static void produce_msg_txn(rd_kafka_t *producer,
|
|
1097
|
+
const char *topic,
|
|
1098
|
+
const char *value,
|
|
1099
|
+
rd_bool_t commit) {
|
|
1100
|
+
rd_kafka_resp_err_t err;
|
|
1101
|
+
|
|
1102
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
|
|
1103
|
+
|
|
1104
|
+
if (!commit)
|
|
1105
|
+
test_curr->ignore_dr_err = rd_true;
|
|
1106
|
+
|
|
1107
|
+
err = rd_kafka_producev(producer, RD_KAFKA_V_TOPIC(topic),
|
|
1108
|
+
RD_KAFKA_V_VALUE((void *)value, strlen(value)),
|
|
1109
|
+
RD_KAFKA_V_END);
|
|
1110
|
+
TEST_ASSERT(!err, "producev failed: %s", rd_kafka_err2name(err));
|
|
1111
|
+
rd_kafka_flush(producer, 30000);
|
|
1112
|
+
|
|
1113
|
+
if (commit)
|
|
1114
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
|
|
1115
|
+
else
|
|
1116
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
|
|
1117
|
+
|
|
1118
|
+
test_curr->ignore_dr_err = rd_false;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* @brief Test: dynamic isolation level change READ_COMMITTED ->
|
|
1124
|
+
* READ_UNCOMMITTED with RELEASE acknowledgement on a committed
|
|
1125
|
+
* record.
|
|
1126
|
+
*
|
|
1127
|
+
* Phase 1: produce committed Msg1, consume + ACCEPT.
|
|
1128
|
+
* Phase 2: produce aborted Msg2 (no records visible in read_committed).
|
|
1129
|
+
* Phase 3: produce committed Msg3, consume + RELEASE.
|
|
1130
|
+
* Phase 4: produce aborted Msg4. Released Msg3 may reappear; keep RELEASE-ing
|
|
1131
|
+
* it so the broker can advance past the abort marker.
|
|
1132
|
+
* Phase 5: alter isolation level to read_uncommitted.
|
|
1133
|
+
* Phase 6: produce committed Msg5, aborted Msg6, aborted Msg7, committed Msg8.
|
|
1134
|
+
* Phase 7: consume; the released Msg3 (offset 4) plus the four new messages
|
|
1135
|
+
* (offsets 8, 10, 12, 14) should all be visible -> 5 unique offsets.
|
|
1136
|
+
*
|
|
1137
|
+
* With one record per transaction, the offset layout is:
|
|
1138
|
+
* 0 Msg1 (commit marker 1)
|
|
1139
|
+
* 2 Msg2 (abort marker 3)
|
|
1140
|
+
* 4 Msg3 (commit marker 5)
|
|
1141
|
+
* 6 Msg4 (abort marker 7)
|
|
1142
|
+
* 8 Msg5 (commit marker 9)
|
|
1143
|
+
* 10 Msg6 (abort marker 11)
|
|
1144
|
+
* 12 Msg7 (abort marker 13)
|
|
1145
|
+
* 14 Msg8 (commit marker 15)
|
|
1146
|
+
*/
|
|
1147
|
+
static void do_test_dynamic_committed_to_uncommitted_with_release(void) {
|
|
1148
|
+
const char *topic;
|
|
1149
|
+
const char *group = "share-dynamic-c-to-uc-release";
|
|
1150
|
+
const char *txn_id = "txn-dynamic-c-uc-release";
|
|
1151
|
+
rd_kafka_t *producer;
|
|
1152
|
+
rd_kafka_share_t *consumer;
|
|
1153
|
+
rd_kafka_messages_t *batch = NULL;
|
|
1154
|
+
rd_kafka_error_t *error;
|
|
1155
|
+
rd_kafka_resp_err_t ack_err;
|
|
1156
|
+
size_t rcvd, j;
|
|
1157
|
+
int attempts;
|
|
1158
|
+
int consumed;
|
|
1159
|
+
rd_bool_t seen[20] = {0};
|
|
1160
|
+
const int64_t expected_offsets[] = {4, 8, 10, 12, 14};
|
|
1161
|
+
size_t i;
|
|
1162
|
+
|
|
1163
|
+
SUB_TEST();
|
|
1164
|
+
|
|
1165
|
+
topic = test_mk_topic_name("0177-dyn-c-uc-release", 1);
|
|
1166
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
1167
|
+
|
|
1168
|
+
producer = create_txn_producer(txn_id);
|
|
1169
|
+
|
|
1170
|
+
consumer = test_create_share_consumer(group, "explicit");
|
|
1171
|
+
configure_share_group(group, "read_committed");
|
|
1172
|
+
subscribe_share_consumer(consumer, topic);
|
|
1173
|
+
|
|
1174
|
+
/* Phase 1: committed Msg1 -> ACCEPT */
|
|
1175
|
+
TEST_SAY("Phase 1: committed Msg1 with READ_COMMITTED\n");
|
|
1176
|
+
produce_msg_txn(producer, topic, "Message 1", rd_true);
|
|
1177
|
+
consumed = 0;
|
|
1178
|
+
attempts = 0;
|
|
1179
|
+
while (consumed == 0 && attempts++ < 30) {
|
|
1180
|
+
rcvd = 0;
|
|
1181
|
+
error = rd_kafka_share_poll(consumer, 3000, &batch);
|
|
1182
|
+
if (error) {
|
|
1183
|
+
rd_kafka_error_destroy(error);
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1187
|
+
for (j = 0; j < rcvd; j++) {
|
|
1188
|
+
rd_kafka_message_t *msg =
|
|
1189
|
+
rd_kafka_messages_get(batch, j);
|
|
1190
|
+
if (!msg->err) {
|
|
1191
|
+
TEST_ASSERT(msg->offset == 0,
|
|
1192
|
+
"Phase 1: expected offset 0, "
|
|
1193
|
+
"got %" PRId64,
|
|
1194
|
+
msg->offset);
|
|
1195
|
+
ack_err = rd_kafka_share_acknowledge_type(
|
|
1196
|
+
consumer, msg,
|
|
1197
|
+
RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
|
|
1198
|
+
TEST_ASSERT(ack_err ==
|
|
1199
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1200
|
+
"Phase 1 ACCEPT failed: %s",
|
|
1201
|
+
rd_kafka_err2name(ack_err));
|
|
1202
|
+
consumed++;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
rd_kafka_messages_destroy(batch);
|
|
1206
|
+
batch = NULL;
|
|
1207
|
+
}
|
|
1208
|
+
TEST_ASSERT(consumed == 1, "Phase 1: expected 1 record, got %d",
|
|
1209
|
+
consumed);
|
|
1210
|
+
error = rd_kafka_share_commit_async(consumer);
|
|
1211
|
+
TEST_ASSERT(!error, "Phase 1 commit_async failed");
|
|
1212
|
+
|
|
1213
|
+
/* Phase 2: aborted Msg2 - read_committed should hide the record;
|
|
1214
|
+
* the broker eventually advances past the abort marker. */
|
|
1215
|
+
TEST_SAY("Phase 2: aborted Msg2 (read_committed hides it)\n");
|
|
1216
|
+
produce_msg_txn(producer, topic, "Message 2", rd_false);
|
|
1217
|
+
attempts = 10;
|
|
1218
|
+
while (attempts-- > 0) {
|
|
1219
|
+
rcvd = 0;
|
|
1220
|
+
error = rd_kafka_share_poll(consumer, 500, &batch);
|
|
1221
|
+
if (error)
|
|
1222
|
+
rd_kafka_error_destroy(error);
|
|
1223
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1224
|
+
for (j = 0; j < rcvd; j++) {
|
|
1225
|
+
rd_kafka_message_t *msg =
|
|
1226
|
+
rd_kafka_messages_get(batch, j);
|
|
1227
|
+
TEST_ASSERT(msg->err || msg->offset != 2,
|
|
1228
|
+
"Phase 2: aborted Msg2 leaked at "
|
|
1229
|
+
"offset 2 in read_committed");
|
|
1230
|
+
}
|
|
1231
|
+
rd_kafka_messages_destroy(batch);
|
|
1232
|
+
batch = NULL;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/* Phase 3: committed Msg3 -> RELEASE */
|
|
1236
|
+
TEST_SAY("Phase 3: committed Msg3 -> RELEASE\n");
|
|
1237
|
+
produce_msg_txn(producer, topic, "Message 3", rd_true);
|
|
1238
|
+
consumed = 0;
|
|
1239
|
+
attempts = 0;
|
|
1240
|
+
while (consumed == 0 && attempts++ < 30) {
|
|
1241
|
+
rcvd = 0;
|
|
1242
|
+
error = rd_kafka_share_poll(consumer, 3000, &batch);
|
|
1243
|
+
if (error) {
|
|
1244
|
+
rd_kafka_error_destroy(error);
|
|
1245
|
+
continue;
|
|
1246
|
+
}
|
|
1247
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1248
|
+
for (j = 0; j < rcvd; j++) {
|
|
1249
|
+
rd_kafka_message_t *msg =
|
|
1250
|
+
rd_kafka_messages_get(batch, j);
|
|
1251
|
+
if (!msg->err) {
|
|
1252
|
+
TEST_ASSERT(msg->offset == 4,
|
|
1253
|
+
"Phase 3: expected offset 4 "
|
|
1254
|
+
"(Msg3), got %" PRId64,
|
|
1255
|
+
msg->offset);
|
|
1256
|
+
ack_err = rd_kafka_share_acknowledge_type(
|
|
1257
|
+
consumer, msg,
|
|
1258
|
+
RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_RELEASE);
|
|
1259
|
+
TEST_ASSERT(ack_err ==
|
|
1260
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1261
|
+
"Phase 3 RELEASE failed: %s",
|
|
1262
|
+
rd_kafka_err2name(ack_err));
|
|
1263
|
+
consumed++;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
rd_kafka_messages_destroy(batch);
|
|
1267
|
+
batch = NULL;
|
|
1268
|
+
}
|
|
1269
|
+
TEST_ASSERT(consumed == 1, "Phase 3: expected 1 record, got %d",
|
|
1270
|
+
consumed);
|
|
1271
|
+
error = rd_kafka_share_commit_async(consumer);
|
|
1272
|
+
TEST_ASSERT(!error, "Phase 3 commit_async failed");
|
|
1273
|
+
|
|
1274
|
+
/* Phase 4: aborted Msg4. Released Msg3 may re-appear; RELEASE it
|
|
1275
|
+
* again so the next poll can advance. Keep this loop short: each
|
|
1276
|
+
* RELEASE-and-redeliver bumps Msg3's delivery_count. The default
|
|
1277
|
+
* share.delivery.count.limit is 5; Phase 3 already used one
|
|
1278
|
+
* delivery and Phase 7's final ACCEPT will need one, so cap
|
|
1279
|
+
* Phase 4 at 3 redeliveries to leave headroom and avoid the
|
|
1280
|
+
* record being archived. */
|
|
1281
|
+
TEST_SAY("Phase 4: aborted Msg4; Msg3 may redeliver -> RELEASE\n");
|
|
1282
|
+
produce_msg_txn(producer, topic, "Message 4", rd_false);
|
|
1283
|
+
attempts = 3;
|
|
1284
|
+
while (attempts-- > 0) {
|
|
1285
|
+
rcvd = 0;
|
|
1286
|
+
error = rd_kafka_share_poll(consumer, 1000, &batch);
|
|
1287
|
+
if (error) {
|
|
1288
|
+
rd_kafka_error_destroy(error);
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1292
|
+
for (j = 0; j < rcvd; j++) {
|
|
1293
|
+
rd_kafka_message_t *msg =
|
|
1294
|
+
rd_kafka_messages_get(batch, j);
|
|
1295
|
+
if (!msg->err) {
|
|
1296
|
+
/* Should only be Msg3 (offset 4) re-delivered;
|
|
1297
|
+
* Msg4 at offset 6 is aborted in
|
|
1298
|
+
* read_committed.
|
|
1299
|
+
*/
|
|
1300
|
+
TEST_ASSERT(
|
|
1301
|
+
msg->offset == 4,
|
|
1302
|
+
"Phase 4: unexpected offset %" PRId64,
|
|
1303
|
+
msg->offset);
|
|
1304
|
+
ack_err = rd_kafka_share_acknowledge_type(
|
|
1305
|
+
consumer, msg,
|
|
1306
|
+
RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_RELEASE);
|
|
1307
|
+
TEST_ASSERT(ack_err ==
|
|
1308
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1309
|
+
"Phase 4 RELEASE failed: %s",
|
|
1310
|
+
rd_kafka_err2name(ack_err));
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
rd_kafka_messages_destroy(batch);
|
|
1314
|
+
batch = NULL;
|
|
1315
|
+
if (rcvd > 0) {
|
|
1316
|
+
error = rd_kafka_share_commit_async(consumer);
|
|
1317
|
+
if (error)
|
|
1318
|
+
rd_kafka_error_destroy(error);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/* Phase 5: alter isolation level to read_uncommitted */
|
|
1323
|
+
TEST_SAY("Phase 5: alter share.isolation.level to read_uncommitted\n");
|
|
1324
|
+
test_share_set_isolation_level(group, "read_uncommitted");
|
|
1325
|
+
|
|
1326
|
+
/* Phase 6: produce 4 more transactions */
|
|
1327
|
+
TEST_SAY("Phase 6: produce Msg5..Msg8\n");
|
|
1328
|
+
produce_msg_txn(producer, topic, "Message 5", rd_true);
|
|
1329
|
+
produce_msg_txn(producer, topic, "Message 6", rd_false);
|
|
1330
|
+
produce_msg_txn(producer, topic, "Message 7", rd_false);
|
|
1331
|
+
produce_msg_txn(producer, topic, "Message 8", rd_true);
|
|
1332
|
+
|
|
1333
|
+
/* Phase 7: expect to see 5 unique offsets: 4 (released Msg3),
|
|
1334
|
+
* 8, 10, 12, 14 (Msg5..Msg8). */
|
|
1335
|
+
TEST_SAY("Phase 7: consume - expect offsets {4,8,10,12,14}\n");
|
|
1336
|
+
attempts = 0;
|
|
1337
|
+
while (attempts++ < 60) {
|
|
1338
|
+
int new_acks = 0;
|
|
1339
|
+
rd_bool_t all_seen;
|
|
1340
|
+
rcvd = 0;
|
|
1341
|
+
error = rd_kafka_share_poll(consumer, 2000, &batch);
|
|
1342
|
+
if (error) {
|
|
1343
|
+
rd_kafka_error_destroy(error);
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1347
|
+
for (j = 0; j < rcvd; j++) {
|
|
1348
|
+
rd_kafka_message_t *msg =
|
|
1349
|
+
rd_kafka_messages_get(batch, j);
|
|
1350
|
+
if (!msg->err) {
|
|
1351
|
+
int64_t off = msg->offset;
|
|
1352
|
+
if (off >= 0 && off < 20)
|
|
1353
|
+
seen[off] = rd_true;
|
|
1354
|
+
ack_err = rd_kafka_share_acknowledge_type(
|
|
1355
|
+
consumer, msg,
|
|
1356
|
+
RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
|
|
1357
|
+
TEST_ASSERT(ack_err ==
|
|
1358
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1359
|
+
"Phase 7 ACCEPT failed: %s",
|
|
1360
|
+
rd_kafka_err2name(ack_err));
|
|
1361
|
+
new_acks++;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
rd_kafka_messages_destroy(batch);
|
|
1365
|
+
batch = NULL;
|
|
1366
|
+
if (new_acks > 0) {
|
|
1367
|
+
error = rd_kafka_share_commit_async(consumer);
|
|
1368
|
+
if (error)
|
|
1369
|
+
rd_kafka_error_destroy(error);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
all_seen = rd_true;
|
|
1373
|
+
for (i = 0;
|
|
1374
|
+
i < sizeof(expected_offsets) / sizeof(expected_offsets[0]);
|
|
1375
|
+
i++) {
|
|
1376
|
+
if (!seen[expected_offsets[i]]) {
|
|
1377
|
+
all_seen = rd_false;
|
|
1378
|
+
break;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (all_seen)
|
|
1382
|
+
break;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
for (i = 0; i < sizeof(expected_offsets) / sizeof(expected_offsets[0]);
|
|
1386
|
+
i++) {
|
|
1387
|
+
TEST_ASSERT(seen[expected_offsets[i]],
|
|
1388
|
+
"Phase 7: expected offset %" PRId64
|
|
1389
|
+
" not seen after isolation change",
|
|
1390
|
+
expected_offsets[i]);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
TEST_SAY(
|
|
1394
|
+
"SUCCESS: dynamic READ_COMMITTED -> READ_UNCOMMITTED with "
|
|
1395
|
+
"RELEASE\n");
|
|
1396
|
+
|
|
1397
|
+
test_share_consumer_close(consumer);
|
|
1398
|
+
test_share_destroy(consumer);
|
|
1399
|
+
rd_kafka_destroy(producer);
|
|
1400
|
+
|
|
1401
|
+
SUB_TEST_PASS();
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* @brief Test: dynamic isolation level change READ_COMMITTED ->
|
|
1407
|
+
* READ_UNCOMMITTED with REJECT acknowledgement on a committed
|
|
1408
|
+
* record.
|
|
1409
|
+
*
|
|
1410
|
+
* Same setup as the RELEASE variant, except Msg3 is REJECTed instead of
|
|
1411
|
+
* RELEASEd. After the isolation level switch, Msg3 must NOT be redelivered;
|
|
1412
|
+
* only the four new messages are expected -> 4 unique offsets.
|
|
1413
|
+
*/
|
|
1414
|
+
static void do_test_dynamic_committed_to_uncommitted_with_reject(void) {
|
|
1415
|
+
const char *topic;
|
|
1416
|
+
const char *group = "share-dynamic-c-to-uc-reject";
|
|
1417
|
+
const char *txn_id = "txn-dynamic-c-uc-reject";
|
|
1418
|
+
rd_kafka_t *producer;
|
|
1419
|
+
rd_kafka_share_t *consumer;
|
|
1420
|
+
rd_kafka_messages_t *batch = NULL;
|
|
1421
|
+
rd_kafka_error_t *error;
|
|
1422
|
+
rd_kafka_resp_err_t ack_err;
|
|
1423
|
+
size_t rcvd, j;
|
|
1424
|
+
int attempts;
|
|
1425
|
+
int consumed;
|
|
1426
|
+
rd_bool_t seen[20] = {0};
|
|
1427
|
+
const int64_t expected_offsets[] = {8, 10, 12, 14};
|
|
1428
|
+
size_t i;
|
|
1429
|
+
|
|
1430
|
+
SUB_TEST();
|
|
1431
|
+
|
|
1432
|
+
topic = test_mk_topic_name("0177-dyn-c-uc-reject", 1);
|
|
1433
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
1434
|
+
|
|
1435
|
+
producer = create_txn_producer(txn_id);
|
|
1436
|
+
|
|
1437
|
+
consumer = test_create_share_consumer(group, "explicit");
|
|
1438
|
+
configure_share_group(group, "read_committed");
|
|
1439
|
+
subscribe_share_consumer(consumer, topic);
|
|
1440
|
+
|
|
1441
|
+
/* Phase 1: committed Msg1 -> ACCEPT */
|
|
1442
|
+
TEST_SAY("Phase 1: committed Msg1 with READ_COMMITTED\n");
|
|
1443
|
+
produce_msg_txn(producer, topic, "Message 1", rd_true);
|
|
1444
|
+
consumed = 0;
|
|
1445
|
+
attempts = 0;
|
|
1446
|
+
while (consumed == 0 && attempts++ < 30) {
|
|
1447
|
+
rcvd = 0;
|
|
1448
|
+
error = rd_kafka_share_poll(consumer, 3000, &batch);
|
|
1449
|
+
if (error) {
|
|
1450
|
+
rd_kafka_error_destroy(error);
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1453
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1454
|
+
for (j = 0; j < rcvd; j++) {
|
|
1455
|
+
rd_kafka_message_t *msg =
|
|
1456
|
+
rd_kafka_messages_get(batch, j);
|
|
1457
|
+
if (!msg->err) {
|
|
1458
|
+
TEST_ASSERT(msg->offset == 0,
|
|
1459
|
+
"Phase 1: expected offset 0, "
|
|
1460
|
+
"got %" PRId64,
|
|
1461
|
+
msg->offset);
|
|
1462
|
+
ack_err = rd_kafka_share_acknowledge_type(
|
|
1463
|
+
consumer, msg,
|
|
1464
|
+
RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
|
|
1465
|
+
TEST_ASSERT(ack_err ==
|
|
1466
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1467
|
+
"Phase 1 ACCEPT failed: %s",
|
|
1468
|
+
rd_kafka_err2name(ack_err));
|
|
1469
|
+
consumed++;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
rd_kafka_messages_destroy(batch);
|
|
1473
|
+
batch = NULL;
|
|
1474
|
+
}
|
|
1475
|
+
TEST_ASSERT(consumed == 1, "Phase 1: expected 1 record, got %d",
|
|
1476
|
+
consumed);
|
|
1477
|
+
error = rd_kafka_share_commit_async(consumer);
|
|
1478
|
+
TEST_ASSERT(!error, "Phase 1 commit_async failed");
|
|
1479
|
+
|
|
1480
|
+
/* Phase 2: aborted Msg2 */
|
|
1481
|
+
TEST_SAY("Phase 2: aborted Msg2 (read_committed hides it)\n");
|
|
1482
|
+
produce_msg_txn(producer, topic, "Message 2", rd_false);
|
|
1483
|
+
attempts = 10;
|
|
1484
|
+
while (attempts-- > 0) {
|
|
1485
|
+
rcvd = 0;
|
|
1486
|
+
error = rd_kafka_share_poll(consumer, 500, &batch);
|
|
1487
|
+
if (error)
|
|
1488
|
+
rd_kafka_error_destroy(error);
|
|
1489
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1490
|
+
for (j = 0; j < rcvd; j++) {
|
|
1491
|
+
rd_kafka_message_t *msg =
|
|
1492
|
+
rd_kafka_messages_get(batch, j);
|
|
1493
|
+
TEST_ASSERT(msg->err || msg->offset != 2,
|
|
1494
|
+
"Phase 2: aborted Msg2 leaked at "
|
|
1495
|
+
"offset 2 in read_committed");
|
|
1496
|
+
}
|
|
1497
|
+
rd_kafka_messages_destroy(batch);
|
|
1498
|
+
batch = NULL;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
/* Phase 3: committed Msg3 -> REJECT */
|
|
1502
|
+
TEST_SAY("Phase 3: committed Msg3 -> REJECT\n");
|
|
1503
|
+
produce_msg_txn(producer, topic, "Message 3", rd_true);
|
|
1504
|
+
consumed = 0;
|
|
1505
|
+
attempts = 0;
|
|
1506
|
+
while (consumed == 0 && attempts++ < 30) {
|
|
1507
|
+
rcvd = 0;
|
|
1508
|
+
error = rd_kafka_share_poll(consumer, 3000, &batch);
|
|
1509
|
+
if (error) {
|
|
1510
|
+
rd_kafka_error_destroy(error);
|
|
1511
|
+
continue;
|
|
1512
|
+
}
|
|
1513
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1514
|
+
for (j = 0; j < rcvd; j++) {
|
|
1515
|
+
rd_kafka_message_t *msg =
|
|
1516
|
+
rd_kafka_messages_get(batch, j);
|
|
1517
|
+
if (!msg->err) {
|
|
1518
|
+
TEST_ASSERT(msg->offset == 4,
|
|
1519
|
+
"Phase 3: expected offset 4 "
|
|
1520
|
+
"(Msg3), got %" PRId64,
|
|
1521
|
+
msg->offset);
|
|
1522
|
+
ack_err = rd_kafka_share_acknowledge_type(
|
|
1523
|
+
consumer, msg,
|
|
1524
|
+
RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_REJECT);
|
|
1525
|
+
TEST_ASSERT(ack_err ==
|
|
1526
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1527
|
+
"Phase 3 REJECT failed: %s",
|
|
1528
|
+
rd_kafka_err2name(ack_err));
|
|
1529
|
+
consumed++;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
rd_kafka_messages_destroy(batch);
|
|
1533
|
+
batch = NULL;
|
|
1534
|
+
}
|
|
1535
|
+
TEST_ASSERT(consumed == 1, "Phase 3: expected 1 record, got %d",
|
|
1536
|
+
consumed);
|
|
1537
|
+
error = rd_kafka_share_commit_async(consumer);
|
|
1538
|
+
TEST_ASSERT(!error, "Phase 3 commit_async failed");
|
|
1539
|
+
|
|
1540
|
+
/* Phase 4: aborted Msg4. Msg3 was REJECTed so it should not return.
|
|
1541
|
+
* Just poll for a while to let the broker process the abort marker. */
|
|
1542
|
+
TEST_SAY("Phase 4: aborted Msg4 (Msg3 was REJECTed)\n");
|
|
1543
|
+
produce_msg_txn(producer, topic, "Message 4", rd_false);
|
|
1544
|
+
attempts = 20;
|
|
1545
|
+
while (attempts-- > 0) {
|
|
1546
|
+
rcvd = 0;
|
|
1547
|
+
error = rd_kafka_share_poll(consumer, 500, &batch);
|
|
1548
|
+
if (error)
|
|
1549
|
+
rd_kafka_error_destroy(error);
|
|
1550
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1551
|
+
for (j = 0; j < rcvd; j++) {
|
|
1552
|
+
rd_kafka_message_t *msg =
|
|
1553
|
+
rd_kafka_messages_get(batch, j);
|
|
1554
|
+
if (!msg->err) {
|
|
1555
|
+
TEST_FAIL(
|
|
1556
|
+
"Phase 4: unexpected record at "
|
|
1557
|
+
"offset %" PRId64
|
|
1558
|
+
" (Msg3 was REJECTed, Msg4 is "
|
|
1559
|
+
"aborted in read_committed)",
|
|
1560
|
+
msg->offset);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
rd_kafka_messages_destroy(batch);
|
|
1564
|
+
batch = NULL;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
/* Phase 5: alter isolation level to read_uncommitted */
|
|
1568
|
+
TEST_SAY("Phase 5: alter share.isolation.level to read_uncommitted\n");
|
|
1569
|
+
test_share_set_isolation_level(group, "read_uncommitted");
|
|
1570
|
+
|
|
1571
|
+
/* Phase 6: produce 4 more transactions */
|
|
1572
|
+
TEST_SAY("Phase 6: produce Msg5..Msg8\n");
|
|
1573
|
+
produce_msg_txn(producer, topic, "Message 5", rd_true);
|
|
1574
|
+
produce_msg_txn(producer, topic, "Message 6", rd_false);
|
|
1575
|
+
produce_msg_txn(producer, topic, "Message 7", rd_false);
|
|
1576
|
+
produce_msg_txn(producer, topic, "Message 8", rd_true);
|
|
1577
|
+
|
|
1578
|
+
/* Phase 7: expect offsets {8, 10, 12, 14}. Msg3 (offset 4) must NOT
|
|
1579
|
+
* reappear because it was REJECTed. */
|
|
1580
|
+
TEST_SAY("Phase 7: consume - expect offsets {8,10,12,14}, no Msg3\n");
|
|
1581
|
+
attempts = 0;
|
|
1582
|
+
while (attempts++ < 60) {
|
|
1583
|
+
int new_acks = 0;
|
|
1584
|
+
rd_bool_t all_seen;
|
|
1585
|
+
rcvd = 0;
|
|
1586
|
+
error = rd_kafka_share_poll(consumer, 2000, &batch);
|
|
1587
|
+
if (error) {
|
|
1588
|
+
rd_kafka_error_destroy(error);
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
rcvd = rd_kafka_messages_count(batch);
|
|
1592
|
+
for (j = 0; j < rcvd; j++) {
|
|
1593
|
+
rd_kafka_message_t *msg =
|
|
1594
|
+
rd_kafka_messages_get(batch, j);
|
|
1595
|
+
if (!msg->err) {
|
|
1596
|
+
int64_t off = msg->offset;
|
|
1597
|
+
TEST_ASSERT(off != 4,
|
|
1598
|
+
"Phase 7: Msg3 (offset 4) "
|
|
1599
|
+
"should not be redelivered "
|
|
1600
|
+
"after REJECT");
|
|
1601
|
+
if (off >= 0 && off < 20)
|
|
1602
|
+
seen[off] = rd_true;
|
|
1603
|
+
ack_err = rd_kafka_share_acknowledge_type(
|
|
1604
|
+
consumer, msg,
|
|
1605
|
+
RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
|
|
1606
|
+
TEST_ASSERT(ack_err ==
|
|
1607
|
+
RD_KAFKA_RESP_ERR_NO_ERROR,
|
|
1608
|
+
"Phase 7 ACCEPT failed: %s",
|
|
1609
|
+
rd_kafka_err2name(ack_err));
|
|
1610
|
+
new_acks++;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
rd_kafka_messages_destroy(batch);
|
|
1614
|
+
batch = NULL;
|
|
1615
|
+
if (new_acks > 0) {
|
|
1616
|
+
error = rd_kafka_share_commit_async(consumer);
|
|
1617
|
+
if (error)
|
|
1618
|
+
rd_kafka_error_destroy(error);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
all_seen = rd_true;
|
|
1622
|
+
for (i = 0;
|
|
1623
|
+
i < sizeof(expected_offsets) / sizeof(expected_offsets[0]);
|
|
1624
|
+
i++) {
|
|
1625
|
+
if (!seen[expected_offsets[i]]) {
|
|
1626
|
+
all_seen = rd_false;
|
|
1627
|
+
break;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
if (all_seen)
|
|
1631
|
+
break;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
for (i = 0; i < sizeof(expected_offsets) / sizeof(expected_offsets[0]);
|
|
1635
|
+
i++) {
|
|
1636
|
+
TEST_ASSERT(seen[expected_offsets[i]],
|
|
1637
|
+
"Phase 7: expected offset %" PRId64
|
|
1638
|
+
" not seen after isolation change",
|
|
1639
|
+
expected_offsets[i]);
|
|
1640
|
+
}
|
|
1641
|
+
TEST_ASSERT(!seen[4],
|
|
1642
|
+
"Phase 7: Msg3 (offset 4) appeared but it was REJECTed");
|
|
1643
|
+
|
|
1644
|
+
TEST_SAY(
|
|
1645
|
+
"SUCCESS: dynamic READ_COMMITTED -> READ_UNCOMMITTED with "
|
|
1646
|
+
"REJECT\n");
|
|
1647
|
+
|
|
1648
|
+
test_share_consumer_close(consumer);
|
|
1649
|
+
test_share_destroy(consumer);
|
|
1650
|
+
rd_kafka_destroy(producer);
|
|
1651
|
+
|
|
1652
|
+
SUB_TEST_PASS();
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* @brief Two share groups, different isolation levels, same topic.
|
|
1658
|
+
*
|
|
1659
|
+
* share.isolation.level is a BROKER-SIDE group config — consumers in
|
|
1660
|
+
* the SAME group cannot have different isolation levels. This test
|
|
1661
|
+
* exercises two distinct groups consuming the same topic concurrently
|
|
1662
|
+
* with different isolation levels.
|
|
1663
|
+
*
|
|
1664
|
+
* Producer mix on the topic:
|
|
1665
|
+
* - txn-A committed: 100 records
|
|
1666
|
+
* - txn-B aborted: 100 records
|
|
1667
|
+
* - txn-C committed: 100 records
|
|
1668
|
+
*
|
|
1669
|
+
* Expected:
|
|
1670
|
+
* - Group G1 (read_committed): sees 200 records (A+C only)
|
|
1671
|
+
* - Group G2 (read_uncommitted): sees 300 records (A+B+C)
|
|
1672
|
+
*/
|
|
1673
|
+
static void do_test_two_groups_different_isolation_levels(void) {
|
|
1674
|
+
const char *topic;
|
|
1675
|
+
const char *grp_committed = "share-txn-twogroup-rc";
|
|
1676
|
+
const char *grp_uncommitted = "share-txn-twogroup-ru";
|
|
1677
|
+
rd_kafka_t *producer_a;
|
|
1678
|
+
rd_kafka_t *producer_b;
|
|
1679
|
+
rd_kafka_t *producer_c;
|
|
1680
|
+
rd_kafka_share_t *c_committed;
|
|
1681
|
+
rd_kafka_share_t *c_uncommitted;
|
|
1682
|
+
const int msg_cnt = 100;
|
|
1683
|
+
const int expected_rc = 2 * msg_cnt;
|
|
1684
|
+
const int expected_ru = 3 * msg_cnt;
|
|
1685
|
+
int consumed_rc;
|
|
1686
|
+
int consumed_ru;
|
|
1687
|
+
|
|
1688
|
+
SUB_TEST("two groups, different isolation levels");
|
|
1689
|
+
|
|
1690
|
+
topic = test_mk_topic_name("0177-twogroup-iso", 1);
|
|
1691
|
+
test_create_topic(NULL, topic, 1, -1);
|
|
1692
|
+
|
|
1693
|
+
producer_a = create_txn_producer("twogroup-iso-A");
|
|
1694
|
+
producer_b = create_txn_producer("twogroup-iso-B");
|
|
1695
|
+
producer_c = create_txn_producer("twogroup-iso-C");
|
|
1696
|
+
|
|
1697
|
+
/* Subscribe both consumers BEFORE producing so they're ready to
|
|
1698
|
+
* consume from the earliest offset. */
|
|
1699
|
+
c_committed = test_create_share_consumer(grp_committed, NULL);
|
|
1700
|
+
configure_share_group(grp_committed, "read_committed");
|
|
1701
|
+
subscribe_share_consumer(c_committed, topic);
|
|
1702
|
+
|
|
1703
|
+
c_uncommitted = test_create_share_consumer(grp_uncommitted, NULL);
|
|
1704
|
+
configure_share_group(grp_uncommitted, "read_uncommitted");
|
|
1705
|
+
subscribe_share_consumer(c_uncommitted, topic);
|
|
1706
|
+
|
|
1707
|
+
/* txn-A: committed */
|
|
1708
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer_a));
|
|
1709
|
+
test_produce_msgs2(producer_a, topic, test_id_generate(),
|
|
1710
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
1711
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer_a, 30000));
|
|
1712
|
+
|
|
1713
|
+
/* txn-B: aborted */
|
|
1714
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer_b));
|
|
1715
|
+
test_curr->ignore_dr_err = rd_true;
|
|
1716
|
+
test_produce_msgs2(producer_b, topic, test_id_generate(),
|
|
1717
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
1718
|
+
TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer_b, 30000));
|
|
1719
|
+
test_curr->ignore_dr_err = rd_false;
|
|
1720
|
+
|
|
1721
|
+
/* txn-C: committed */
|
|
1722
|
+
TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer_c));
|
|
1723
|
+
test_produce_msgs2(producer_c, topic, test_id_generate(),
|
|
1724
|
+
RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
|
|
1725
|
+
TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer_c, 30000));
|
|
1726
|
+
|
|
1727
|
+
/* Both groups consume the same topic concurrently. */
|
|
1728
|
+
TEST_SAY("Consuming via read_committed group\n");
|
|
1729
|
+
consumed_rc =
|
|
1730
|
+
consume_share_messages(c_committed, expected_rc, 80, 3000);
|
|
1731
|
+
TEST_SAY("Consuming via read_uncommitted group\n");
|
|
1732
|
+
consumed_ru =
|
|
1733
|
+
consume_share_messages(c_uncommitted, expected_ru, 80, 3000);
|
|
1734
|
+
|
|
1735
|
+
TEST_SAY(
|
|
1736
|
+
"Result: read_committed=%d (expected %d), "
|
|
1737
|
+
"read_uncommitted=%d (expected %d)\n",
|
|
1738
|
+
consumed_rc, expected_rc, consumed_ru, expected_ru);
|
|
1739
|
+
|
|
1740
|
+
TEST_ASSERT(consumed_rc == expected_rc,
|
|
1741
|
+
"read_committed group expected %d records, got %d",
|
|
1742
|
+
expected_rc, consumed_rc);
|
|
1743
|
+
TEST_ASSERT(consumed_ru == expected_ru,
|
|
1744
|
+
"read_uncommitted group expected %d records, got %d",
|
|
1745
|
+
expected_ru, consumed_ru);
|
|
1746
|
+
|
|
1747
|
+
test_share_consumer_close(c_committed);
|
|
1748
|
+
test_share_destroy(c_committed);
|
|
1749
|
+
test_share_consumer_close(c_uncommitted);
|
|
1750
|
+
test_share_destroy(c_uncommitted);
|
|
1751
|
+
rd_kafka_destroy(producer_a);
|
|
1752
|
+
rd_kafka_destroy(producer_b);
|
|
1753
|
+
rd_kafka_destroy(producer_c);
|
|
1754
|
+
|
|
1755
|
+
SUB_TEST_PASS();
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
|
|
1759
|
+
int main_0177_share_consumer_transactions(int argc, char **argv) {
|
|
1760
|
+
/* Test both isolation levels */
|
|
1761
|
+
const char *isolation_levels[] = {"read_committed", "read_uncommitted"};
|
|
1762
|
+
int i;
|
|
1763
|
+
|
|
1764
|
+
test_timeout_set(200);
|
|
1765
|
+
|
|
1766
|
+
/* Create common handles for all tests */
|
|
1767
|
+
common_admin = test_create_producer();
|
|
1768
|
+
common_regular_producer = test_create_producer();
|
|
1769
|
+
|
|
1770
|
+
for (i = 0; i < 2; i++) {
|
|
1771
|
+
const char *level = isolation_levels[i];
|
|
1772
|
+
|
|
1773
|
+
TEST_SAY("\n");
|
|
1774
|
+
TEST_SAY("========================================\n");
|
|
1775
|
+
TEST_SAY("Testing with isolation.level=%s\n", level);
|
|
1776
|
+
TEST_SAY("========================================\n");
|
|
1777
|
+
|
|
1778
|
+
/* Basic transaction tests */
|
|
1779
|
+
do_test_committed_transaction(level);
|
|
1780
|
+
do_test_aborted_transaction(level);
|
|
1781
|
+
do_test_mixed_transactions(level);
|
|
1782
|
+
|
|
1783
|
+
/* Control records filtering */
|
|
1784
|
+
do_test_control_records_filtered(level);
|
|
1785
|
+
|
|
1786
|
+
/* Multi-partition and multi-producer scenarios */
|
|
1787
|
+
do_test_multi_partition_transactions(level);
|
|
1788
|
+
do_test_interleaved_producers(level);
|
|
1789
|
+
|
|
1790
|
+
/* Mixed workloads */
|
|
1791
|
+
do_test_mixed_txn_non_txn(level);
|
|
1792
|
+
|
|
1793
|
+
/* Interval-based abort pattern */
|
|
1794
|
+
do_test_interval_abort_pattern(level);
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
/* Dynamic isolation level change tests (run once, not per-level) */
|
|
1798
|
+
TEST_SAY("\n");
|
|
1799
|
+
TEST_SAY("========================================\n");
|
|
1800
|
+
TEST_SAY("Testing dynamic isolation level changes\n");
|
|
1801
|
+
TEST_SAY("========================================\n");
|
|
1802
|
+
|
|
1803
|
+
do_test_dynamic_uncommitted_to_committed();
|
|
1804
|
+
do_test_dynamic_committed_to_uncommitted_with_release();
|
|
1805
|
+
do_test_dynamic_committed_to_uncommitted_with_reject();
|
|
1806
|
+
|
|
1807
|
+
/* Two-groups, different isolation levels on the same topic */
|
|
1808
|
+
do_test_two_groups_different_isolation_levels();
|
|
1809
|
+
|
|
1810
|
+
/* Cleanup common handles */
|
|
1811
|
+
rd_kafka_destroy(common_admin);
|
|
1812
|
+
rd_kafka_destroy(common_regular_producer);
|
|
1813
|
+
|
|
1814
|
+
return 0;
|
|
1815
|
+
}
|