@point3/node-rdkafka 3.6.0-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/LICENSE.txt +20 -0
- package/README.md +636 -0
- package/binding.gyp +154 -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 +33 -0
- package/deps/librdkafka/.gdbmacros +19 -0
- package/deps/librdkafka/.github/CODEOWNERS +1 -0
- package/deps/librdkafka/.github/ISSUE_TEMPLATE +34 -0
- package/deps/librdkafka/.semaphore/run-all-tests.yml +77 -0
- package/deps/librdkafka/.semaphore/semaphore-integration.yml +250 -0
- package/deps/librdkafka/.semaphore/semaphore.yml +378 -0
- package/deps/librdkafka/.semaphore/verify-linux-packages.yml +41 -0
- package/deps/librdkafka/CHANGELOG.md +2208 -0
- package/deps/librdkafka/CMakeLists.txt +291 -0
- package/deps/librdkafka/CODE_OF_CONDUCT.md +46 -0
- package/deps/librdkafka/CONFIGURATION.md +209 -0
- package/deps/librdkafka/CONTRIBUTING.md +431 -0
- package/deps/librdkafka/Doxyfile +2375 -0
- package/deps/librdkafka/INTRODUCTION.md +2481 -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 +199 -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 +331 -0
- package/deps/librdkafka/debian/changelog +111 -0
- package/deps/librdkafka/debian/compat +1 -0
- package/deps/librdkafka/debian/control +71 -0
- package/deps/librdkafka/debian/copyright +99 -0
- package/deps/librdkafka/debian/gbp.conf +9 -0
- package/deps/librdkafka/debian/librdkafka++1.install +1 -0
- package/deps/librdkafka/debian/librdkafka-dev.examples +2 -0
- package/deps/librdkafka/debian/librdkafka-dev.install +9 -0
- package/deps/librdkafka/debian/librdkafka1.docs +5 -0
- package/deps/librdkafka/debian/librdkafka1.install +1 -0
- package/deps/librdkafka/debian/librdkafka1.symbols +135 -0
- package/deps/librdkafka/debian/rules +19 -0
- package/deps/librdkafka/debian/source/format +1 -0
- package/deps/librdkafka/debian/watch +2 -0
- package/deps/librdkafka/dev-conf.sh +123 -0
- package/deps/librdkafka/examples/CMakeLists.txt +79 -0
- package/deps/librdkafka/examples/Makefile +167 -0
- package/deps/librdkafka/examples/README.md +42 -0
- package/deps/librdkafka/examples/alter_consumer_group_offsets.c +338 -0
- package/deps/librdkafka/examples/consumer.c +271 -0
- package/deps/librdkafka/examples/delete_records.c +233 -0
- package/deps/librdkafka/examples/describe_cluster.c +322 -0
- package/deps/librdkafka/examples/describe_consumer_groups.c +455 -0
- package/deps/librdkafka/examples/describe_topics.c +427 -0
- package/deps/librdkafka/examples/elect_leaders.c +317 -0
- package/deps/librdkafka/examples/globals.json +11 -0
- package/deps/librdkafka/examples/idempotent_producer.c +344 -0
- package/deps/librdkafka/examples/incremental_alter_configs.c +347 -0
- package/deps/librdkafka/examples/kafkatest_verifiable_client.cpp +945 -0
- package/deps/librdkafka/examples/list_consumer_group_offsets.c +359 -0
- package/deps/librdkafka/examples/list_consumer_groups.c +365 -0
- package/deps/librdkafka/examples/list_offsets.c +327 -0
- package/deps/librdkafka/examples/misc.c +287 -0
- package/deps/librdkafka/examples/openssl_engine_example.cpp +248 -0
- package/deps/librdkafka/examples/producer.c +251 -0
- package/deps/librdkafka/examples/producer.cpp +228 -0
- package/deps/librdkafka/examples/rdkafka_complex_consumer_example.c +617 -0
- package/deps/librdkafka/examples/rdkafka_complex_consumer_example.cpp +467 -0
- package/deps/librdkafka/examples/rdkafka_consume_batch.cpp +264 -0
- package/deps/librdkafka/examples/rdkafka_example.c +853 -0
- package/deps/librdkafka/examples/rdkafka_example.cpp +679 -0
- package/deps/librdkafka/examples/rdkafka_performance.c +1781 -0
- package/deps/librdkafka/examples/transactions-older-broker.c +668 -0
- package/deps/librdkafka/examples/transactions.c +665 -0
- package/deps/librdkafka/examples/user_scram.c +491 -0
- package/deps/librdkafka/examples/win_ssl_cert_store.cpp +396 -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/packaging/RELEASE.md +319 -0
- package/deps/librdkafka/packaging/alpine/build-alpine.sh +38 -0
- package/deps/librdkafka/packaging/archlinux/PKGBUILD +30 -0
- package/deps/librdkafka/packaging/cmake/Config.cmake.in +37 -0
- package/deps/librdkafka/packaging/cmake/Modules/FindLZ4.cmake +38 -0
- package/deps/librdkafka/packaging/cmake/Modules/FindZSTD.cmake +27 -0
- package/deps/librdkafka/packaging/cmake/Modules/LICENSE.FindZstd +178 -0
- package/deps/librdkafka/packaging/cmake/README.md +38 -0
- package/deps/librdkafka/packaging/cmake/config.h.in +52 -0
- package/deps/librdkafka/packaging/cmake/parseversion.cmake +60 -0
- package/deps/librdkafka/packaging/cmake/rdkafka.pc.in +12 -0
- package/deps/librdkafka/packaging/cmake/try_compile/atomic_32_test.c +8 -0
- package/deps/librdkafka/packaging/cmake/try_compile/atomic_64_test.c +8 -0
- package/deps/librdkafka/packaging/cmake/try_compile/c11threads_test.c +14 -0
- package/deps/librdkafka/packaging/cmake/try_compile/crc32c_hw_test.c +27 -0
- package/deps/librdkafka/packaging/cmake/try_compile/dlopen_test.c +11 -0
- package/deps/librdkafka/packaging/cmake/try_compile/libsasl2_test.c +7 -0
- package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_darwin_test.c +6 -0
- package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_freebsd_test.c +7 -0
- package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_gnu_test.c +5 -0
- package/deps/librdkafka/packaging/cmake/try_compile/rand_r_test.c +7 -0
- package/deps/librdkafka/packaging/cmake/try_compile/rdkafka_setup.cmake +122 -0
- package/deps/librdkafka/packaging/cmake/try_compile/regex_test.c +10 -0
- package/deps/librdkafka/packaging/cmake/try_compile/strndup_test.c +5 -0
- package/deps/librdkafka/packaging/cmake/try_compile/sync_32_test.c +8 -0
- package/deps/librdkafka/packaging/cmake/try_compile/sync_64_test.c +8 -0
- package/deps/librdkafka/packaging/cp/README.md +16 -0
- package/deps/librdkafka/packaging/cp/check_features.c +72 -0
- package/deps/librdkafka/packaging/cp/verify-deb.sh +33 -0
- package/deps/librdkafka/packaging/cp/verify-packages.sh +69 -0
- package/deps/librdkafka/packaging/cp/verify-rpm.sh +32 -0
- package/deps/librdkafka/packaging/debian/changelog +66 -0
- package/deps/librdkafka/packaging/debian/compat +1 -0
- package/deps/librdkafka/packaging/debian/control +49 -0
- package/deps/librdkafka/packaging/debian/copyright +84 -0
- package/deps/librdkafka/packaging/debian/docs +5 -0
- package/deps/librdkafka/packaging/debian/gbp.conf +9 -0
- package/deps/librdkafka/packaging/debian/librdkafka-dev.dirs +2 -0
- package/deps/librdkafka/packaging/debian/librdkafka-dev.examples +2 -0
- package/deps/librdkafka/packaging/debian/librdkafka-dev.install +6 -0
- package/deps/librdkafka/packaging/debian/librdkafka-dev.substvars +1 -0
- package/deps/librdkafka/packaging/debian/librdkafka.dsc +16 -0
- package/deps/librdkafka/packaging/debian/librdkafka1-dbg.substvars +1 -0
- package/deps/librdkafka/packaging/debian/librdkafka1.dirs +1 -0
- package/deps/librdkafka/packaging/debian/librdkafka1.install +2 -0
- package/deps/librdkafka/packaging/debian/librdkafka1.postinst.debhelper +5 -0
- package/deps/librdkafka/packaging/debian/librdkafka1.postrm.debhelper +5 -0
- package/deps/librdkafka/packaging/debian/librdkafka1.symbols +64 -0
- package/deps/librdkafka/packaging/debian/rules +19 -0
- package/deps/librdkafka/packaging/debian/source/format +1 -0
- package/deps/librdkafka/packaging/debian/watch +2 -0
- package/deps/librdkafka/packaging/get_version.py +21 -0
- package/deps/librdkafka/packaging/homebrew/README.md +15 -0
- package/deps/librdkafka/packaging/homebrew/brew-update-pr.sh +31 -0
- package/deps/librdkafka/packaging/mingw-w64/configure-build-msys2-mingw-static.sh +52 -0
- package/deps/librdkafka/packaging/mingw-w64/configure-build-msys2-mingw.sh +21 -0
- package/deps/librdkafka/packaging/mingw-w64/export-variables.sh +13 -0
- package/deps/librdkafka/packaging/mingw-w64/run-tests.sh +6 -0
- package/deps/librdkafka/packaging/mingw-w64/semaphoreci-build.sh +38 -0
- package/deps/librdkafka/packaging/nuget/README.md +84 -0
- package/deps/librdkafka/packaging/nuget/artifact.py +177 -0
- package/deps/librdkafka/packaging/nuget/cleanup-s3.py +143 -0
- package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-win32__bldtype-Release/msvcr120.zip +0 -0
- package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-win32__bldtype-Release/msvcr140.zip +0 -0
- package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-x64__bldtype-Release/msvcr120.zip +0 -0
- package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-x64__bldtype-Release/msvcr140.zip +0 -0
- package/deps/librdkafka/packaging/nuget/nuget.sh +21 -0
- package/deps/librdkafka/packaging/nuget/nugetpackage.py +278 -0
- package/deps/librdkafka/packaging/nuget/packaging.py +448 -0
- package/deps/librdkafka/packaging/nuget/push-to-nuget.sh +21 -0
- package/deps/librdkafka/packaging/nuget/release.py +167 -0
- package/deps/librdkafka/packaging/nuget/requirements.txt +3 -0
- package/deps/librdkafka/packaging/nuget/staticpackage.py +178 -0
- package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.nuspec +21 -0
- package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.props +18 -0
- package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.targets +19 -0
- package/deps/librdkafka/packaging/nuget/zfile/__init__.py +0 -0
- package/deps/librdkafka/packaging/nuget/zfile/zfile.py +98 -0
- package/deps/librdkafka/packaging/rpm/Makefile +92 -0
- package/deps/librdkafka/packaging/rpm/README.md +23 -0
- package/deps/librdkafka/packaging/rpm/el7-x86_64.cfg +40 -0
- package/deps/librdkafka/packaging/rpm/librdkafka.spec +118 -0
- package/deps/librdkafka/packaging/rpm/mock-on-docker.sh +96 -0
- package/deps/librdkafka/packaging/rpm/tests/Makefile +25 -0
- package/deps/librdkafka/packaging/rpm/tests/README.md +8 -0
- package/deps/librdkafka/packaging/rpm/tests/run-test.sh +42 -0
- package/deps/librdkafka/packaging/rpm/tests/test-on-docker.sh +56 -0
- package/deps/librdkafka/packaging/rpm/tests/test.c +77 -0
- package/deps/librdkafka/packaging/rpm/tests/test.cpp +34 -0
- package/deps/librdkafka/packaging/tools/Dockerfile +31 -0
- package/deps/librdkafka/packaging/tools/build-configurations-checks.sh +12 -0
- package/deps/librdkafka/packaging/tools/build-deb-package.sh +64 -0
- package/deps/librdkafka/packaging/tools/build-debian.sh +65 -0
- package/deps/librdkafka/packaging/tools/build-manylinux.sh +68 -0
- package/deps/librdkafka/packaging/tools/build-release-artifacts.sh +139 -0
- package/deps/librdkafka/packaging/tools/distro-build.sh +38 -0
- package/deps/librdkafka/packaging/tools/gh-release-checksums.py +39 -0
- package/deps/librdkafka/packaging/tools/rdutcoverage.sh +25 -0
- package/deps/librdkafka/packaging/tools/requirements.txt +2 -0
- package/deps/librdkafka/packaging/tools/run-in-docker.sh +28 -0
- package/deps/librdkafka/packaging/tools/run-integration-tests.sh +31 -0
- package/deps/librdkafka/packaging/tools/run-style-check.sh +4 -0
- package/deps/librdkafka/packaging/tools/style-format.sh +149 -0
- package/deps/librdkafka/packaging/tools/update_rpcs_max_versions.py +100 -0
- package/deps/librdkafka/service.yml +172 -0
- package/deps/librdkafka/src/CMakeLists.txt +374 -0
- package/deps/librdkafka/src/Makefile +103 -0
- package/deps/librdkafka/src/README.lz4.md +30 -0
- package/deps/librdkafka/src/cJSON.c +2834 -0
- package/deps/librdkafka/src/cJSON.h +398 -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 +584 -0
- package/deps/librdkafka/src/rdaddr.c +255 -0
- package/deps/librdkafka/src/rdaddr.h +202 -0
- package/deps/librdkafka/src/rdatomic.h +230 -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 +830 -0
- package/deps/librdkafka/src/rdhttp.h +101 -0
- package/deps/librdkafka/src/rdinterval.h +177 -0
- package/deps/librdkafka/src/rdkafka.c +5505 -0
- package/deps/librdkafka/src/rdkafka.h +10686 -0
- package/deps/librdkafka/src/rdkafka_admin.c +9794 -0
- package/deps/librdkafka/src/rdkafka_admin.h +661 -0
- package/deps/librdkafka/src/rdkafka_assignment.c +1010 -0
- package/deps/librdkafka/src/rdkafka_assignment.h +73 -0
- package/deps/librdkafka/src/rdkafka_assignor.c +1786 -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 +221 -0
- package/deps/librdkafka/src/rdkafka_broker.c +6337 -0
- package/deps/librdkafka/src/rdkafka_broker.h +744 -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 +7587 -0
- package/deps/librdkafka/src/rdkafka_cgrp.h +477 -0
- package/deps/librdkafka/src/rdkafka_conf.c +4880 -0
- package/deps/librdkafka/src/rdkafka_conf.h +732 -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 +1422 -0
- package/deps/librdkafka/src/rdkafka_fetcher.h +44 -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 +1260 -0
- package/deps/librdkafka/src/rdkafka_interceptor.c +819 -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 +2209 -0
- package/deps/librdkafka/src/rdkafka_metadata.h +345 -0
- package/deps/librdkafka/src/rdkafka_metadata_cache.c +1183 -0
- package/deps/librdkafka/src/rdkafka_mock.c +3661 -0
- package/deps/librdkafka/src/rdkafka_mock.h +610 -0
- package/deps/librdkafka/src/rdkafka_mock_cgrp.c +1876 -0
- package/deps/librdkafka/src/rdkafka_mock_handlers.c +3113 -0
- package/deps/librdkafka/src/rdkafka_mock_int.h +710 -0
- package/deps/librdkafka/src/rdkafka_msg.c +2589 -0
- package/deps/librdkafka/src/rdkafka_msg.h +614 -0
- package/deps/librdkafka/src/rdkafka_msgbatch.h +62 -0
- package/deps/librdkafka/src/rdkafka_msgset.h +98 -0
- package/deps/librdkafka/src/rdkafka_msgset_reader.c +1806 -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 +997 -0
- package/deps/librdkafka/src/rdkafka_op.h +858 -0
- package/deps/librdkafka/src/rdkafka_partition.c +4896 -0
- package/deps/librdkafka/src/rdkafka_partition.h +1182 -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 +736 -0
- package/deps/librdkafka/src/rdkafka_protocol.h +128 -0
- package/deps/librdkafka/src/rdkafka_queue.c +1230 -0
- package/deps/librdkafka/src/rdkafka_queue.h +1220 -0
- package/deps/librdkafka/src/rdkafka_range_assignor.c +1748 -0
- package/deps/librdkafka/src/rdkafka_request.c +7089 -0
- package/deps/librdkafka/src/rdkafka_request.h +732 -0
- package/deps/librdkafka/src/rdkafka_roundrobin_assignor.c +123 -0
- package/deps/librdkafka/src/rdkafka_sasl.c +530 -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 +1666 -0
- package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.h +47 -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_ssl.c +2129 -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 +278 -0
- package/deps/librdkafka/src/rdkafka_telemetry.c +760 -0
- package/deps/librdkafka/src/rdkafka_telemetry.h +52 -0
- package/deps/librdkafka/src/rdkafka_telemetry_decode.c +1053 -0
- package/deps/librdkafka/src/rdkafka_telemetry_decode.h +59 -0
- package/deps/librdkafka/src/rdkafka_telemetry_encode.c +997 -0
- package/deps/librdkafka/src/rdkafka_telemetry_encode.h +301 -0
- package/deps/librdkafka/src/rdkafka_timer.c +402 -0
- package/deps/librdkafka/src/rdkafka_timer.h +117 -0
- package/deps/librdkafka/src/rdkafka_topic.c +2161 -0
- package/deps/librdkafka/src/rdkafka_topic.h +334 -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 +576 -0
- package/deps/librdkafka/src/rdlist.h +434 -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 +80 -0
- package/deps/librdkafka/src/rdrand.h +43 -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 +549 -0
- package/deps/librdkafka/src/rdunittest.h +232 -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 +199 -0
- package/deps/librdkafka/src/tinycthread_extra.h +212 -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 +97 -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 +338 -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 +279 -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 +481 -0
- package/deps/librdkafka/tests/0065-yield.cpp +140 -0
- package/deps/librdkafka/tests/0066-plugins.cpp +129 -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 +363 -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 +658 -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 +528 -0
- package/deps/librdkafka/tests/0127-fetch_queue_backoff.cpp +165 -0
- package/deps/librdkafka/tests/0128-sasl_callback_queue.cpp +125 -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 +143 -0
- package/deps/librdkafka/tests/0136-resolve_cb.c +181 -0
- package/deps/librdkafka/tests/0137-barrier_batch_consume.c +619 -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 +515 -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 +952 -0
- package/deps/librdkafka/tests/0148-offset_fetch_commit_error_mock.c +563 -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/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 +170 -0
- package/deps/librdkafka/tests/LibrdkafkaTestApp.py +291 -0
- package/deps/librdkafka/tests/Makefile +182 -0
- package/deps/librdkafka/tests/README.md +509 -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 +191 -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 +79 -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 +140 -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/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 +7778 -0
- package/deps/librdkafka/tests/test.conf.example +27 -0
- package/deps/librdkafka/tests/test.h +1028 -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.14.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 +276 -0
- package/deps/librdkafka/win32/librdkafkacpp/librdkafkacpp.vcxproj +104 -0
- package/deps/librdkafka/win32/msbuild.ps1 +15 -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 +253 -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 +62 -0
- package/lib/admin.js +233 -0
- package/lib/client.js +573 -0
- package/lib/error.js +500 -0
- package/lib/index.js +34 -0
- package/lib/kafka-consumer-stream.js +397 -0
- package/lib/kafka-consumer.js +698 -0
- package/lib/producer/high-level-producer.js +323 -0
- package/lib/producer-stream.js +307 -0
- package/lib/producer.js +375 -0
- package/lib/tools/ref-counter.js +52 -0
- package/lib/topic-partition.js +88 -0
- package/lib/topic.js +42 -0
- package/lib/util.js +29 -0
- package/package.json +61 -0
- package/prebuilds/darwin-arm64/@point3+node-rdkafka.node +0 -0
- package/prebuilds/linux-x64/@point3+node-rdkafka.node +0 -0
- package/util/configure.js +30 -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,3256 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* librdkafka - Apache Kafka C library
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2019-2022, Magnus Edenhill
|
|
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
|
+
/**
|
|
30
|
+
* @name Transaction Manager
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
#include <stdarg.h>
|
|
35
|
+
|
|
36
|
+
#include "rd.h"
|
|
37
|
+
#include "rdkafka_int.h"
|
|
38
|
+
#include "rdkafka_txnmgr.h"
|
|
39
|
+
#include "rdkafka_idempotence.h"
|
|
40
|
+
#include "rdkafka_request.h"
|
|
41
|
+
#include "rdkafka_error.h"
|
|
42
|
+
#include "rdunittest.h"
|
|
43
|
+
#include "rdrand.h"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
static void rd_kafka_txn_coord_timer_start(rd_kafka_t *rk, int timeout_ms);
|
|
47
|
+
|
|
48
|
+
#define rd_kafka_txn_curr_api_set_result(rk, actions, error) \
|
|
49
|
+
rd_kafka_txn_curr_api_set_result0(__FUNCTION__, __LINE__, rk, actions, \
|
|
50
|
+
error)
|
|
51
|
+
static void rd_kafka_txn_curr_api_set_result0(const char *func,
|
|
52
|
+
int line,
|
|
53
|
+
rd_kafka_t *rk,
|
|
54
|
+
int actions,
|
|
55
|
+
rd_kafka_error_t *error);
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @return a normalized error code, this for instance abstracts different
|
|
61
|
+
* fencing errors to return one single fencing error to the application.
|
|
62
|
+
*/
|
|
63
|
+
static rd_kafka_resp_err_t rd_kafka_txn_normalize_err(rd_kafka_resp_err_t err) {
|
|
64
|
+
|
|
65
|
+
switch (err) {
|
|
66
|
+
case RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH:
|
|
67
|
+
case RD_KAFKA_RESP_ERR_PRODUCER_FENCED:
|
|
68
|
+
return RD_KAFKA_RESP_ERR__FENCED;
|
|
69
|
+
case RD_KAFKA_RESP_ERR__TIMED_OUT_QUEUE:
|
|
70
|
+
return RD_KAFKA_RESP_ERR__TIMED_OUT;
|
|
71
|
+
default:
|
|
72
|
+
return err;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @brief Ensure client is configured as a transactional producer,
|
|
79
|
+
* else return error.
|
|
80
|
+
*
|
|
81
|
+
* @locality application thread
|
|
82
|
+
* @locks none
|
|
83
|
+
*/
|
|
84
|
+
static RD_INLINE rd_kafka_error_t *
|
|
85
|
+
rd_kafka_ensure_transactional(const rd_kafka_t *rk) {
|
|
86
|
+
if (unlikely(rk->rk_type != RD_KAFKA_PRODUCER))
|
|
87
|
+
return rd_kafka_error_new(
|
|
88
|
+
RD_KAFKA_RESP_ERR__INVALID_ARG,
|
|
89
|
+
"The Transactional API can only be used "
|
|
90
|
+
"on producer instances");
|
|
91
|
+
|
|
92
|
+
if (unlikely(!rk->rk_conf.eos.transactional_id))
|
|
93
|
+
return rd_kafka_error_new(RD_KAFKA_RESP_ERR__NOT_CONFIGURED,
|
|
94
|
+
"The Transactional API requires "
|
|
95
|
+
"transactional.id to be configured");
|
|
96
|
+
|
|
97
|
+
return NULL;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @brief Ensure transaction state is one of \p states.
|
|
104
|
+
*
|
|
105
|
+
* @param the required states, ended by a -1 sentinel.
|
|
106
|
+
*
|
|
107
|
+
* @locks_required rd_kafka_*lock(rk) MUST be held
|
|
108
|
+
* @locality any
|
|
109
|
+
*/
|
|
110
|
+
static RD_INLINE rd_kafka_error_t *
|
|
111
|
+
rd_kafka_txn_require_states0(rd_kafka_t *rk, rd_kafka_txn_state_t states[]) {
|
|
112
|
+
rd_kafka_error_t *error;
|
|
113
|
+
size_t i;
|
|
114
|
+
|
|
115
|
+
if (unlikely((error = rd_kafka_ensure_transactional(rk)) != NULL))
|
|
116
|
+
return error;
|
|
117
|
+
|
|
118
|
+
for (i = 0; (int)states[i] != -1; i++)
|
|
119
|
+
if (rk->rk_eos.txn_state == states[i])
|
|
120
|
+
return NULL;
|
|
121
|
+
|
|
122
|
+
/* For fatal and abortable states return the last transactional
|
|
123
|
+
* error, for all other states just return a state error. */
|
|
124
|
+
if (rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_FATAL_ERROR)
|
|
125
|
+
error = rd_kafka_error_new_fatal(rk->rk_eos.txn_err, "%s",
|
|
126
|
+
rk->rk_eos.txn_errstr);
|
|
127
|
+
else if (rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_ABORTABLE_ERROR) {
|
|
128
|
+
error = rd_kafka_error_new(rk->rk_eos.txn_err, "%s",
|
|
129
|
+
rk->rk_eos.txn_errstr);
|
|
130
|
+
rd_kafka_error_set_txn_requires_abort(error);
|
|
131
|
+
} else
|
|
132
|
+
error = rd_kafka_error_new(
|
|
133
|
+
RD_KAFKA_RESP_ERR__STATE, "Operation not valid in state %s",
|
|
134
|
+
rd_kafka_txn_state2str(rk->rk_eos.txn_state));
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
return error;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** @brief \p ... is a list of states */
|
|
141
|
+
#define rd_kafka_txn_require_state(rk, ...) \
|
|
142
|
+
rd_kafka_txn_require_states0( \
|
|
143
|
+
rk, (rd_kafka_txn_state_t[]) {__VA_ARGS__, -1})
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param ignore Will be set to true if the state transition should be
|
|
149
|
+
* completely ignored.
|
|
150
|
+
* @returns true if the state transition is valid, else false.
|
|
151
|
+
*/
|
|
152
|
+
static rd_bool_t
|
|
153
|
+
rd_kafka_txn_state_transition_is_valid(rd_kafka_txn_state_t curr,
|
|
154
|
+
rd_kafka_txn_state_t new_state,
|
|
155
|
+
rd_bool_t *ignore) {
|
|
156
|
+
|
|
157
|
+
*ignore = rd_false;
|
|
158
|
+
|
|
159
|
+
switch (new_state) {
|
|
160
|
+
case RD_KAFKA_TXN_STATE_INIT:
|
|
161
|
+
/* This is the initialized value and this transition will
|
|
162
|
+
* never happen. */
|
|
163
|
+
return rd_false;
|
|
164
|
+
|
|
165
|
+
case RD_KAFKA_TXN_STATE_WAIT_PID:
|
|
166
|
+
return curr == RD_KAFKA_TXN_STATE_INIT;
|
|
167
|
+
|
|
168
|
+
case RD_KAFKA_TXN_STATE_READY_NOT_ACKED:
|
|
169
|
+
return curr == RD_KAFKA_TXN_STATE_WAIT_PID;
|
|
170
|
+
|
|
171
|
+
case RD_KAFKA_TXN_STATE_READY:
|
|
172
|
+
return curr == RD_KAFKA_TXN_STATE_READY_NOT_ACKED ||
|
|
173
|
+
curr == RD_KAFKA_TXN_STATE_COMMIT_NOT_ACKED ||
|
|
174
|
+
curr == RD_KAFKA_TXN_STATE_ABORT_NOT_ACKED;
|
|
175
|
+
|
|
176
|
+
case RD_KAFKA_TXN_STATE_IN_TRANSACTION:
|
|
177
|
+
return curr == RD_KAFKA_TXN_STATE_READY;
|
|
178
|
+
|
|
179
|
+
case RD_KAFKA_TXN_STATE_BEGIN_COMMIT:
|
|
180
|
+
return curr == RD_KAFKA_TXN_STATE_IN_TRANSACTION;
|
|
181
|
+
|
|
182
|
+
case RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION:
|
|
183
|
+
return curr == RD_KAFKA_TXN_STATE_BEGIN_COMMIT;
|
|
184
|
+
|
|
185
|
+
case RD_KAFKA_TXN_STATE_COMMIT_NOT_ACKED:
|
|
186
|
+
return curr == RD_KAFKA_TXN_STATE_BEGIN_COMMIT ||
|
|
187
|
+
curr == RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION;
|
|
188
|
+
|
|
189
|
+
case RD_KAFKA_TXN_STATE_BEGIN_ABORT:
|
|
190
|
+
return curr == RD_KAFKA_TXN_STATE_IN_TRANSACTION ||
|
|
191
|
+
curr == RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION ||
|
|
192
|
+
curr == RD_KAFKA_TXN_STATE_ABORTABLE_ERROR;
|
|
193
|
+
|
|
194
|
+
case RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION:
|
|
195
|
+
return curr == RD_KAFKA_TXN_STATE_BEGIN_ABORT;
|
|
196
|
+
|
|
197
|
+
case RD_KAFKA_TXN_STATE_ABORT_NOT_ACKED:
|
|
198
|
+
return curr == RD_KAFKA_TXN_STATE_BEGIN_ABORT ||
|
|
199
|
+
curr == RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION;
|
|
200
|
+
|
|
201
|
+
case RD_KAFKA_TXN_STATE_ABORTABLE_ERROR:
|
|
202
|
+
if (curr == RD_KAFKA_TXN_STATE_BEGIN_ABORT ||
|
|
203
|
+
curr == RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION ||
|
|
204
|
+
curr == RD_KAFKA_TXN_STATE_FATAL_ERROR) {
|
|
205
|
+
/* Ignore sub-sequent abortable errors in
|
|
206
|
+
* these states. */
|
|
207
|
+
*ignore = rd_true;
|
|
208
|
+
return 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return curr == RD_KAFKA_TXN_STATE_IN_TRANSACTION ||
|
|
212
|
+
curr == RD_KAFKA_TXN_STATE_BEGIN_COMMIT ||
|
|
213
|
+
curr == RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION;
|
|
214
|
+
|
|
215
|
+
case RD_KAFKA_TXN_STATE_FATAL_ERROR:
|
|
216
|
+
/* Any state can transition to a fatal error */
|
|
217
|
+
return rd_true;
|
|
218
|
+
|
|
219
|
+
default:
|
|
220
|
+
RD_BUG("Invalid txn state transition: %s -> %s",
|
|
221
|
+
rd_kafka_txn_state2str(curr),
|
|
222
|
+
rd_kafka_txn_state2str(new_state));
|
|
223
|
+
return rd_false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @brief Transition the transaction state to \p new_state.
|
|
230
|
+
*
|
|
231
|
+
* @returns 0 on success or an error code if the state transition
|
|
232
|
+
* was invalid.
|
|
233
|
+
*
|
|
234
|
+
* @locality rdkafka main thread
|
|
235
|
+
* @locks_required rd_kafka_wrlock MUST be held
|
|
236
|
+
*/
|
|
237
|
+
static void rd_kafka_txn_set_state(rd_kafka_t *rk,
|
|
238
|
+
rd_kafka_txn_state_t new_state) {
|
|
239
|
+
rd_bool_t ignore;
|
|
240
|
+
|
|
241
|
+
if (rk->rk_eos.txn_state == new_state)
|
|
242
|
+
return;
|
|
243
|
+
|
|
244
|
+
/* Check if state transition is valid */
|
|
245
|
+
if (!rd_kafka_txn_state_transition_is_valid(rk->rk_eos.txn_state,
|
|
246
|
+
new_state, &ignore)) {
|
|
247
|
+
rd_kafka_log(rk, LOG_CRIT, "TXNSTATE",
|
|
248
|
+
"BUG: Invalid transaction state transition "
|
|
249
|
+
"attempted: %s -> %s",
|
|
250
|
+
rd_kafka_txn_state2str(rk->rk_eos.txn_state),
|
|
251
|
+
rd_kafka_txn_state2str(new_state));
|
|
252
|
+
|
|
253
|
+
rd_assert(!*"BUG: Invalid transaction state transition");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (ignore) {
|
|
257
|
+
/* Ignore this state change */
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
rd_kafka_dbg(rk, EOS, "TXNSTATE", "Transaction state change %s -> %s",
|
|
262
|
+
rd_kafka_txn_state2str(rk->rk_eos.txn_state),
|
|
263
|
+
rd_kafka_txn_state2str(new_state));
|
|
264
|
+
|
|
265
|
+
/* If transitioning from IN_TRANSACTION, the app is no longer
|
|
266
|
+
* allowed to enqueue (produce) messages. */
|
|
267
|
+
if (rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_IN_TRANSACTION)
|
|
268
|
+
rd_atomic32_set(&rk->rk_eos.txn_may_enq, 0);
|
|
269
|
+
else if (new_state == RD_KAFKA_TXN_STATE_IN_TRANSACTION)
|
|
270
|
+
rd_atomic32_set(&rk->rk_eos.txn_may_enq, 1);
|
|
271
|
+
|
|
272
|
+
rk->rk_eos.txn_state = new_state;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @returns the current transaction timeout, i.e., the time remaining in
|
|
278
|
+
* the current transaction.
|
|
279
|
+
*
|
|
280
|
+
* @remark The remaining timeout is currently not tracked, so this function
|
|
281
|
+
* will always return the remaining time based on transaction.timeout.ms
|
|
282
|
+
* and we rely on the broker to enforce the actual remaining timeout.
|
|
283
|
+
* This is still better than not having a timeout cap at all, which
|
|
284
|
+
* used to be the case.
|
|
285
|
+
* It's also tricky knowing exactly what the controller thinks the
|
|
286
|
+
* remaining transaction time is.
|
|
287
|
+
*
|
|
288
|
+
* @locks_required rd_kafka_*lock(rk) MUST be held.
|
|
289
|
+
*/
|
|
290
|
+
static RD_INLINE rd_ts_t rd_kafka_txn_current_timeout(const rd_kafka_t *rk) {
|
|
291
|
+
return rd_timeout_init(rk->rk_conf.eos.transaction_timeout_ms);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* @brief An unrecoverable transactional error has occurred.
|
|
297
|
+
*
|
|
298
|
+
* @param do_lock RD_DO_LOCK: rd_kafka_wrlock(rk) will be acquired and released,
|
|
299
|
+
* RD_DONT_LOCK: rd_kafka_wrlock(rk) MUST be held by the caller.
|
|
300
|
+
* @locality any
|
|
301
|
+
* @locks rd_kafka_wrlock MUST NOT be held
|
|
302
|
+
*/
|
|
303
|
+
void rd_kafka_txn_set_fatal_error(rd_kafka_t *rk,
|
|
304
|
+
rd_dolock_t do_lock,
|
|
305
|
+
rd_kafka_resp_err_t err,
|
|
306
|
+
const char *fmt,
|
|
307
|
+
...) {
|
|
308
|
+
char errstr[512];
|
|
309
|
+
va_list ap;
|
|
310
|
+
|
|
311
|
+
va_start(ap, fmt);
|
|
312
|
+
vsnprintf(errstr, sizeof(errstr), fmt, ap);
|
|
313
|
+
va_end(ap);
|
|
314
|
+
|
|
315
|
+
rd_kafka_log(rk, LOG_ALERT, "TXNERR",
|
|
316
|
+
"Fatal transaction error: %s (%s)", errstr,
|
|
317
|
+
rd_kafka_err2name(err));
|
|
318
|
+
|
|
319
|
+
if (do_lock)
|
|
320
|
+
rd_kafka_wrlock(rk);
|
|
321
|
+
rd_kafka_set_fatal_error0(rk, RD_DONT_LOCK, err, "%s", errstr);
|
|
322
|
+
|
|
323
|
+
rk->rk_eos.txn_err = err;
|
|
324
|
+
if (rk->rk_eos.txn_errstr)
|
|
325
|
+
rd_free(rk->rk_eos.txn_errstr);
|
|
326
|
+
rk->rk_eos.txn_errstr = rd_strdup(errstr);
|
|
327
|
+
|
|
328
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_FATAL_ERROR);
|
|
329
|
+
|
|
330
|
+
if (do_lock)
|
|
331
|
+
rd_kafka_wrunlock(rk);
|
|
332
|
+
|
|
333
|
+
/* If application has called a transactional API and
|
|
334
|
+
* it has now failed, reply to the app.
|
|
335
|
+
* If there is no currently called API then this is a no-op. */
|
|
336
|
+
rd_kafka_txn_curr_api_set_result(
|
|
337
|
+
rk, 0, rd_kafka_error_new_fatal(err, "%s", errstr));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @brief An abortable/recoverable transactional error has occured.
|
|
343
|
+
*
|
|
344
|
+
* @param requires_epoch_bump If true; abort_transaction() will bump the epoch
|
|
345
|
+
* on the coordinator (KIP-360).
|
|
346
|
+
|
|
347
|
+
* @locality rdkafka main thread
|
|
348
|
+
* @locks rd_kafka_wrlock MUST NOT be held
|
|
349
|
+
*/
|
|
350
|
+
void rd_kafka_txn_set_abortable_error0(rd_kafka_t *rk,
|
|
351
|
+
rd_kafka_resp_err_t err,
|
|
352
|
+
rd_bool_t requires_epoch_bump,
|
|
353
|
+
const char *fmt,
|
|
354
|
+
...) {
|
|
355
|
+
char errstr[512];
|
|
356
|
+
va_list ap;
|
|
357
|
+
|
|
358
|
+
if (rd_kafka_fatal_error(rk, NULL, 0)) {
|
|
359
|
+
rd_kafka_dbg(rk, EOS, "FATAL",
|
|
360
|
+
"Not propagating abortable transactional "
|
|
361
|
+
"error (%s) "
|
|
362
|
+
"since previous fatal error already raised",
|
|
363
|
+
rd_kafka_err2name(err));
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
va_start(ap, fmt);
|
|
368
|
+
vsnprintf(errstr, sizeof(errstr), fmt, ap);
|
|
369
|
+
va_end(ap);
|
|
370
|
+
|
|
371
|
+
rd_kafka_wrlock(rk);
|
|
372
|
+
|
|
373
|
+
if (requires_epoch_bump)
|
|
374
|
+
rk->rk_eos.txn_requires_epoch_bump = requires_epoch_bump;
|
|
375
|
+
|
|
376
|
+
if (rk->rk_eos.txn_err) {
|
|
377
|
+
rd_kafka_dbg(rk, EOS, "TXNERR",
|
|
378
|
+
"Ignoring sub-sequent abortable transaction "
|
|
379
|
+
"error: %s (%s): "
|
|
380
|
+
"previous error (%s) already raised",
|
|
381
|
+
errstr, rd_kafka_err2name(err),
|
|
382
|
+
rd_kafka_err2name(rk->rk_eos.txn_err));
|
|
383
|
+
rd_kafka_wrunlock(rk);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
rk->rk_eos.txn_err = err;
|
|
388
|
+
if (rk->rk_eos.txn_errstr)
|
|
389
|
+
rd_free(rk->rk_eos.txn_errstr);
|
|
390
|
+
rk->rk_eos.txn_errstr = rd_strdup(errstr);
|
|
391
|
+
|
|
392
|
+
rd_kafka_log(rk, LOG_ERR, "TXNERR",
|
|
393
|
+
"Current transaction failed in state %s: %s (%s%s)",
|
|
394
|
+
rd_kafka_txn_state2str(rk->rk_eos.txn_state), errstr,
|
|
395
|
+
rd_kafka_err2name(err),
|
|
396
|
+
requires_epoch_bump ? ", requires epoch bump" : "");
|
|
397
|
+
|
|
398
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_ABORTABLE_ERROR);
|
|
399
|
+
rd_kafka_wrunlock(rk);
|
|
400
|
+
|
|
401
|
+
/* Purge all messages in queue/flight */
|
|
402
|
+
rd_kafka_purge(rk, RD_KAFKA_PURGE_F_QUEUE | RD_KAFKA_PURGE_F_ABORT_TXN |
|
|
403
|
+
RD_KAFKA_PURGE_F_NON_BLOCKING);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* @brief Send request-reply op to txnmgr callback, waits for a reply
|
|
410
|
+
* or timeout, and returns an error object or NULL on success.
|
|
411
|
+
*
|
|
412
|
+
* @remark Does not alter the current API state.
|
|
413
|
+
*
|
|
414
|
+
* @returns an error object on failure, else NULL.
|
|
415
|
+
*
|
|
416
|
+
* @locality application thread
|
|
417
|
+
*
|
|
418
|
+
* @locks_acquired rk->rk_eos.txn_curr_api.lock
|
|
419
|
+
*/
|
|
420
|
+
#define rd_kafka_txn_op_req(rk, op_cb, abs_timeout) \
|
|
421
|
+
rd_kafka_txn_op_req0(__FUNCTION__, __LINE__, rk, \
|
|
422
|
+
rd_kafka_op_new_cb(rk, RD_KAFKA_OP_TXN, op_cb), \
|
|
423
|
+
abs_timeout)
|
|
424
|
+
#define rd_kafka_txn_op_req1(rk, rko, abs_timeout) \
|
|
425
|
+
rd_kafka_txn_op_req0(__FUNCTION__, __LINE__, rk, rko, abs_timeout)
|
|
426
|
+
static rd_kafka_error_t *rd_kafka_txn_op_req0(const char *func,
|
|
427
|
+
int line,
|
|
428
|
+
rd_kafka_t *rk,
|
|
429
|
+
rd_kafka_op_t *rko,
|
|
430
|
+
rd_ts_t abs_timeout) {
|
|
431
|
+
rd_kafka_error_t *error = NULL;
|
|
432
|
+
rd_bool_t has_result = rd_false;
|
|
433
|
+
|
|
434
|
+
mtx_lock(&rk->rk_eos.txn_curr_api.lock);
|
|
435
|
+
|
|
436
|
+
/* See if there's already a result, if so return that immediately. */
|
|
437
|
+
if (rk->rk_eos.txn_curr_api.has_result) {
|
|
438
|
+
error = rk->rk_eos.txn_curr_api.error;
|
|
439
|
+
rk->rk_eos.txn_curr_api.error = NULL;
|
|
440
|
+
rk->rk_eos.txn_curr_api.has_result = rd_false;
|
|
441
|
+
mtx_unlock(&rk->rk_eos.txn_curr_api.lock);
|
|
442
|
+
rd_kafka_op_destroy(rko);
|
|
443
|
+
rd_kafka_dbg(rk, EOS, "OPREQ",
|
|
444
|
+
"%s:%d: %s: returning already set result: %s",
|
|
445
|
+
func, line, rk->rk_eos.txn_curr_api.name,
|
|
446
|
+
error ? rd_kafka_error_string(error) : "Success");
|
|
447
|
+
return error;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Send one-way op to txnmgr */
|
|
451
|
+
if (!rd_kafka_q_enq(rk->rk_ops, rko))
|
|
452
|
+
RD_BUG("rk_ops queue disabled");
|
|
453
|
+
|
|
454
|
+
/* Wait for result to be set, or timeout */
|
|
455
|
+
do {
|
|
456
|
+
if (cnd_timedwait_ms(&rk->rk_eos.txn_curr_api.cnd,
|
|
457
|
+
&rk->rk_eos.txn_curr_api.lock,
|
|
458
|
+
rd_timeout_remains(abs_timeout)) ==
|
|
459
|
+
thrd_timedout)
|
|
460
|
+
break;
|
|
461
|
+
} while (!rk->rk_eos.txn_curr_api.has_result);
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
if ((has_result = rk->rk_eos.txn_curr_api.has_result)) {
|
|
466
|
+
rk->rk_eos.txn_curr_api.has_result = rd_false;
|
|
467
|
+
error = rk->rk_eos.txn_curr_api.error;
|
|
468
|
+
rk->rk_eos.txn_curr_api.error = NULL;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
mtx_unlock(&rk->rk_eos.txn_curr_api.lock);
|
|
472
|
+
|
|
473
|
+
/* If there was no reply it means the background operation is still
|
|
474
|
+
* in progress and its result will be set later, so the application
|
|
475
|
+
* should call this API again to resume. */
|
|
476
|
+
if (!has_result) {
|
|
477
|
+
error = rd_kafka_error_new_retriable(
|
|
478
|
+
RD_KAFKA_RESP_ERR__TIMED_OUT,
|
|
479
|
+
"Timed out waiting for operation to finish, "
|
|
480
|
+
"retry call to resume");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return error;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* @brief Begin (or resume) a public API call.
|
|
489
|
+
*
|
|
490
|
+
* This function will prevent conflicting calls.
|
|
491
|
+
*
|
|
492
|
+
* @returns an error on failure, or NULL on success.
|
|
493
|
+
*
|
|
494
|
+
* @locality application thread
|
|
495
|
+
*
|
|
496
|
+
* @locks_acquired rk->rk_eos.txn_curr_api.lock
|
|
497
|
+
*/
|
|
498
|
+
static rd_kafka_error_t *rd_kafka_txn_curr_api_begin(rd_kafka_t *rk,
|
|
499
|
+
const char *api_name,
|
|
500
|
+
rd_bool_t cap_timeout,
|
|
501
|
+
int timeout_ms,
|
|
502
|
+
rd_ts_t *abs_timeoutp) {
|
|
503
|
+
rd_kafka_error_t *error = NULL;
|
|
504
|
+
|
|
505
|
+
if ((error = rd_kafka_ensure_transactional(rk)))
|
|
506
|
+
return error;
|
|
507
|
+
|
|
508
|
+
rd_kafka_rdlock(rk); /* Need lock for retrieving the states */
|
|
509
|
+
rd_kafka_dbg(rk, EOS, "TXNAPI",
|
|
510
|
+
"Transactional API called: %s "
|
|
511
|
+
"(in txn state %s, idemp state %s, API timeout %d)",
|
|
512
|
+
api_name, rd_kafka_txn_state2str(rk->rk_eos.txn_state),
|
|
513
|
+
rd_kafka_idemp_state2str(rk->rk_eos.idemp_state),
|
|
514
|
+
timeout_ms);
|
|
515
|
+
rd_kafka_rdunlock(rk);
|
|
516
|
+
|
|
517
|
+
mtx_lock(&rk->rk_eos.txn_curr_api.lock);
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
/* Make sure there is no other conflicting in-progress API call,
|
|
521
|
+
* and that this same call is not currently under way in another thread.
|
|
522
|
+
*/
|
|
523
|
+
if (unlikely(*rk->rk_eos.txn_curr_api.name &&
|
|
524
|
+
strcmp(rk->rk_eos.txn_curr_api.name, api_name))) {
|
|
525
|
+
/* Another API is being called. */
|
|
526
|
+
error = rd_kafka_error_new_retriable(
|
|
527
|
+
RD_KAFKA_RESP_ERR__CONFLICT,
|
|
528
|
+
"Conflicting %s API call is already in progress",
|
|
529
|
+
rk->rk_eos.txn_curr_api.name);
|
|
530
|
+
|
|
531
|
+
} else if (unlikely(rk->rk_eos.txn_curr_api.calling)) {
|
|
532
|
+
/* There is an active call to this same API
|
|
533
|
+
* from another thread. */
|
|
534
|
+
error = rd_kafka_error_new_retriable(
|
|
535
|
+
RD_KAFKA_RESP_ERR__PREV_IN_PROGRESS,
|
|
536
|
+
"Simultaneous %s API calls not allowed",
|
|
537
|
+
rk->rk_eos.txn_curr_api.name);
|
|
538
|
+
|
|
539
|
+
} else if (*rk->rk_eos.txn_curr_api.name) {
|
|
540
|
+
/* Resumed call */
|
|
541
|
+
rk->rk_eos.txn_curr_api.calling = rd_true;
|
|
542
|
+
|
|
543
|
+
} else {
|
|
544
|
+
/* New call */
|
|
545
|
+
rd_snprintf(rk->rk_eos.txn_curr_api.name,
|
|
546
|
+
sizeof(rk->rk_eos.txn_curr_api.name), "%s",
|
|
547
|
+
api_name);
|
|
548
|
+
rk->rk_eos.txn_curr_api.calling = rd_true;
|
|
549
|
+
rd_assert(!rk->rk_eos.txn_curr_api.error);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (!error && abs_timeoutp) {
|
|
553
|
+
rd_ts_t abs_timeout = rd_timeout_init(timeout_ms);
|
|
554
|
+
|
|
555
|
+
if (cap_timeout) {
|
|
556
|
+
/* Cap API timeout to remaining transaction timeout */
|
|
557
|
+
rd_ts_t abs_txn_timeout =
|
|
558
|
+
rd_kafka_txn_current_timeout(rk);
|
|
559
|
+
if (abs_timeout > abs_txn_timeout ||
|
|
560
|
+
abs_timeout == RD_POLL_INFINITE)
|
|
561
|
+
abs_timeout = abs_txn_timeout;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
*abs_timeoutp = abs_timeout;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
mtx_unlock(&rk->rk_eos.txn_curr_api.lock);
|
|
568
|
+
|
|
569
|
+
return error;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* @brief Return from public API.
|
|
576
|
+
*
|
|
577
|
+
* This function updates the current API state and must be used in
|
|
578
|
+
* all return statements from the public txn API.
|
|
579
|
+
*
|
|
580
|
+
* @param resumable If true and the error is retriable, the current API state
|
|
581
|
+
* will be maintained to allow a future call to the same API
|
|
582
|
+
* to resume the background operation that is in progress.
|
|
583
|
+
* @param error The error object, if not NULL, is simply inspected and returned.
|
|
584
|
+
*
|
|
585
|
+
* @returns the \p error object as-is.
|
|
586
|
+
*
|
|
587
|
+
* @locality application thread
|
|
588
|
+
* @locks_acquired rk->rk_eos.txn_curr_api.lock
|
|
589
|
+
*/
|
|
590
|
+
#define rd_kafka_txn_curr_api_return(rk, resumable, error) \
|
|
591
|
+
rd_kafka_txn_curr_api_return0(__FUNCTION__, __LINE__, rk, resumable, \
|
|
592
|
+
error)
|
|
593
|
+
static rd_kafka_error_t *
|
|
594
|
+
rd_kafka_txn_curr_api_return0(const char *func,
|
|
595
|
+
int line,
|
|
596
|
+
rd_kafka_t *rk,
|
|
597
|
+
rd_bool_t resumable,
|
|
598
|
+
rd_kafka_error_t *error) {
|
|
599
|
+
|
|
600
|
+
mtx_lock(&rk->rk_eos.txn_curr_api.lock);
|
|
601
|
+
|
|
602
|
+
rd_kafka_dbg(
|
|
603
|
+
rk, EOS, "TXNAPI", "Transactional API %s return%s at %s:%d: %s",
|
|
604
|
+
rk->rk_eos.txn_curr_api.name,
|
|
605
|
+
resumable && rd_kafka_error_is_retriable(error) ? " resumable" : "",
|
|
606
|
+
func, line, error ? rd_kafka_error_string(error) : "Success");
|
|
607
|
+
|
|
608
|
+
rd_assert(*rk->rk_eos.txn_curr_api.name);
|
|
609
|
+
rd_assert(rk->rk_eos.txn_curr_api.calling);
|
|
610
|
+
|
|
611
|
+
rk->rk_eos.txn_curr_api.calling = rd_false;
|
|
612
|
+
|
|
613
|
+
/* Reset the current API call so that other APIs may be called,
|
|
614
|
+
* unless this is a resumable API and the error is retriable. */
|
|
615
|
+
if (!resumable || (error && !rd_kafka_error_is_retriable(error))) {
|
|
616
|
+
*rk->rk_eos.txn_curr_api.name = '\0';
|
|
617
|
+
/* It is possible for another error to have been set,
|
|
618
|
+
* typically when a fatal error is raised, so make sure
|
|
619
|
+
* we're not destroying the error we're supposed to return. */
|
|
620
|
+
if (rk->rk_eos.txn_curr_api.error != error)
|
|
621
|
+
rd_kafka_error_destroy(rk->rk_eos.txn_curr_api.error);
|
|
622
|
+
rk->rk_eos.txn_curr_api.error = NULL;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
mtx_unlock(&rk->rk_eos.txn_curr_api.lock);
|
|
626
|
+
|
|
627
|
+
return error;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* @brief Set the (possibly intermediary) result for the current API call.
|
|
634
|
+
*
|
|
635
|
+
* The result is \p error NULL for success or \p error object on failure.
|
|
636
|
+
* If the application is actively blocked on the call the result will be
|
|
637
|
+
* sent on its replyq, otherwise the result will be stored for future retrieval
|
|
638
|
+
* the next time the application calls the API again.
|
|
639
|
+
*
|
|
640
|
+
* @locality rdkafka main thread
|
|
641
|
+
* @locks_acquired rk->rk_eos.txn_curr_api.lock
|
|
642
|
+
*/
|
|
643
|
+
static void rd_kafka_txn_curr_api_set_result0(const char *func,
|
|
644
|
+
int line,
|
|
645
|
+
rd_kafka_t *rk,
|
|
646
|
+
int actions,
|
|
647
|
+
rd_kafka_error_t *error) {
|
|
648
|
+
|
|
649
|
+
mtx_lock(&rk->rk_eos.txn_curr_api.lock);
|
|
650
|
+
|
|
651
|
+
if (!*rk->rk_eos.txn_curr_api.name) {
|
|
652
|
+
/* No current API being called, this could happen
|
|
653
|
+
* if the application thread API deemed the API was done,
|
|
654
|
+
* or for fatal errors that attempt to set the result
|
|
655
|
+
* regardless of current API state.
|
|
656
|
+
* In this case we simply throw away this result. */
|
|
657
|
+
if (error)
|
|
658
|
+
rd_kafka_error_destroy(error);
|
|
659
|
+
mtx_unlock(&rk->rk_eos.txn_curr_api.lock);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
rd_kafka_dbg(rk, EOS, "APIRESULT",
|
|
664
|
+
"Transactional API %s (intermediary%s) result set "
|
|
665
|
+
"at %s:%d: %s (%sprevious result%s%s)",
|
|
666
|
+
rk->rk_eos.txn_curr_api.name,
|
|
667
|
+
rk->rk_eos.txn_curr_api.calling ? ", calling" : "", func,
|
|
668
|
+
line, error ? rd_kafka_error_string(error) : "Success",
|
|
669
|
+
rk->rk_eos.txn_curr_api.has_result ? "" : "no ",
|
|
670
|
+
rk->rk_eos.txn_curr_api.error ? ": " : "",
|
|
671
|
+
rd_kafka_error_string(rk->rk_eos.txn_curr_api.error));
|
|
672
|
+
|
|
673
|
+
rk->rk_eos.txn_curr_api.has_result = rd_true;
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
if (rk->rk_eos.txn_curr_api.error) {
|
|
677
|
+
/* If there's already an error it typically means
|
|
678
|
+
* a fatal error has been raised, so nothing more to do here. */
|
|
679
|
+
rd_kafka_dbg(
|
|
680
|
+
rk, EOS, "APIRESULT",
|
|
681
|
+
"Transactional API %s error "
|
|
682
|
+
"already set: %s",
|
|
683
|
+
rk->rk_eos.txn_curr_api.name,
|
|
684
|
+
rd_kafka_error_string(rk->rk_eos.txn_curr_api.error));
|
|
685
|
+
|
|
686
|
+
mtx_unlock(&rk->rk_eos.txn_curr_api.lock);
|
|
687
|
+
|
|
688
|
+
if (error)
|
|
689
|
+
rd_kafka_error_destroy(error);
|
|
690
|
+
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (error) {
|
|
695
|
+
if (actions & RD_KAFKA_ERR_ACTION_FATAL)
|
|
696
|
+
rd_kafka_error_set_fatal(error);
|
|
697
|
+
else if (actions & RD_KAFKA_ERR_ACTION_PERMANENT)
|
|
698
|
+
rd_kafka_error_set_txn_requires_abort(error);
|
|
699
|
+
else if (actions & RD_KAFKA_ERR_ACTION_RETRY)
|
|
700
|
+
rd_kafka_error_set_retriable(error);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
rk->rk_eos.txn_curr_api.error = error;
|
|
704
|
+
error = NULL;
|
|
705
|
+
cnd_broadcast(&rk->rk_eos.txn_curr_api.cnd);
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
mtx_unlock(&rk->rk_eos.txn_curr_api.lock);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* @brief The underlying idempotent producer state changed,
|
|
715
|
+
* see if this affects the transactional operations.
|
|
716
|
+
*
|
|
717
|
+
* @locality any thread
|
|
718
|
+
* @locks rd_kafka_wrlock(rk) MUST be held
|
|
719
|
+
*/
|
|
720
|
+
void rd_kafka_txn_idemp_state_change(rd_kafka_t *rk,
|
|
721
|
+
rd_kafka_idemp_state_t idemp_state) {
|
|
722
|
+
rd_bool_t set_result = rd_false;
|
|
723
|
+
|
|
724
|
+
if (idemp_state == RD_KAFKA_IDEMP_STATE_ASSIGNED &&
|
|
725
|
+
rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_WAIT_PID) {
|
|
726
|
+
/* Application is calling (or has called) init_transactions() */
|
|
727
|
+
RD_UT_COVERAGE(1);
|
|
728
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_READY_NOT_ACKED);
|
|
729
|
+
set_result = rd_true;
|
|
730
|
+
|
|
731
|
+
} else if (idemp_state == RD_KAFKA_IDEMP_STATE_ASSIGNED &&
|
|
732
|
+
(rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_BEGIN_ABORT ||
|
|
733
|
+
rk->rk_eos.txn_state ==
|
|
734
|
+
RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION)) {
|
|
735
|
+
/* Application is calling abort_transaction() as we're
|
|
736
|
+
* recovering from a fatal idempotence error. */
|
|
737
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_ABORT_NOT_ACKED);
|
|
738
|
+
set_result = rd_true;
|
|
739
|
+
|
|
740
|
+
} else if (idemp_state == RD_KAFKA_IDEMP_STATE_FATAL_ERROR &&
|
|
741
|
+
rk->rk_eos.txn_state != RD_KAFKA_TXN_STATE_FATAL_ERROR) {
|
|
742
|
+
/* A fatal error has been raised. */
|
|
743
|
+
|
|
744
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_FATAL_ERROR);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (set_result) {
|
|
748
|
+
/* Application has called init_transactions() or
|
|
749
|
+
* abort_transaction() and it is now complete,
|
|
750
|
+
* reply to the app. */
|
|
751
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, NULL);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* @brief Moves a partition from the pending list to the proper list.
|
|
758
|
+
*
|
|
759
|
+
* @locality rdkafka main thread
|
|
760
|
+
* @locks none
|
|
761
|
+
*/
|
|
762
|
+
static void rd_kafka_txn_partition_registered(rd_kafka_toppar_t *rktp) {
|
|
763
|
+
rd_kafka_t *rk = rktp->rktp_rkt->rkt_rk;
|
|
764
|
+
|
|
765
|
+
rd_kafka_toppar_lock(rktp);
|
|
766
|
+
|
|
767
|
+
if (unlikely(!(rktp->rktp_flags & RD_KAFKA_TOPPAR_F_PEND_TXN))) {
|
|
768
|
+
rd_kafka_dbg(rk, EOS | RD_KAFKA_DBG_PROTOCOL, "ADDPARTS",
|
|
769
|
+
"\"%.*s\" [%" PRId32
|
|
770
|
+
"] is not in pending "
|
|
771
|
+
"list but returned in AddPartitionsToTxn "
|
|
772
|
+
"response: ignoring",
|
|
773
|
+
RD_KAFKAP_STR_PR(rktp->rktp_rkt->rkt_topic),
|
|
774
|
+
rktp->rktp_partition);
|
|
775
|
+
rd_kafka_toppar_unlock(rktp);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
rd_kafka_dbg(rk, EOS | RD_KAFKA_DBG_TOPIC, "ADDPARTS",
|
|
780
|
+
"%.*s [%" PRId32 "] registered with transaction",
|
|
781
|
+
RD_KAFKAP_STR_PR(rktp->rktp_rkt->rkt_topic),
|
|
782
|
+
rktp->rktp_partition);
|
|
783
|
+
|
|
784
|
+
rd_assert((rktp->rktp_flags &
|
|
785
|
+
(RD_KAFKA_TOPPAR_F_PEND_TXN | RD_KAFKA_TOPPAR_F_IN_TXN)) ==
|
|
786
|
+
RD_KAFKA_TOPPAR_F_PEND_TXN);
|
|
787
|
+
|
|
788
|
+
rktp->rktp_flags = (rktp->rktp_flags & ~RD_KAFKA_TOPPAR_F_PEND_TXN) |
|
|
789
|
+
RD_KAFKA_TOPPAR_F_IN_TXN;
|
|
790
|
+
|
|
791
|
+
rd_kafka_toppar_unlock(rktp);
|
|
792
|
+
|
|
793
|
+
mtx_lock(&rk->rk_eos.txn_pending_lock);
|
|
794
|
+
TAILQ_REMOVE(&rk->rk_eos.txn_waitresp_rktps, rktp, rktp_txnlink);
|
|
795
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
796
|
+
|
|
797
|
+
/* Not destroy()/keep():ing rktp since it just changes tailq. */
|
|
798
|
+
|
|
799
|
+
TAILQ_INSERT_TAIL(&rk->rk_eos.txn_rktps, rktp, rktp_txnlink);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* @brief Handle AddPartitionsToTxnResponse
|
|
806
|
+
*
|
|
807
|
+
* @locality rdkafka main thread
|
|
808
|
+
* @locks none
|
|
809
|
+
*/
|
|
810
|
+
static void rd_kafka_txn_handle_AddPartitionsToTxn(rd_kafka_t *rk,
|
|
811
|
+
rd_kafka_broker_t *rkb,
|
|
812
|
+
rd_kafka_resp_err_t err,
|
|
813
|
+
rd_kafka_buf_t *rkbuf,
|
|
814
|
+
rd_kafka_buf_t *request,
|
|
815
|
+
void *opaque) {
|
|
816
|
+
const int log_decode_errors = LOG_ERR;
|
|
817
|
+
int32_t TopicCnt;
|
|
818
|
+
int actions = 0;
|
|
819
|
+
int retry_backoff_ms = 500; /* retry backoff */
|
|
820
|
+
rd_kafka_resp_err_t reset_coord_err = RD_KAFKA_RESP_ERR_NO_ERROR;
|
|
821
|
+
rd_bool_t require_bump = rd_false;
|
|
822
|
+
|
|
823
|
+
if (err)
|
|
824
|
+
goto done;
|
|
825
|
+
|
|
826
|
+
rd_kafka_rdlock(rk);
|
|
827
|
+
rd_assert(rk->rk_eos.txn_state !=
|
|
828
|
+
RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION);
|
|
829
|
+
|
|
830
|
+
if (rk->rk_eos.txn_state != RD_KAFKA_TXN_STATE_IN_TRANSACTION &&
|
|
831
|
+
rk->rk_eos.txn_state != RD_KAFKA_TXN_STATE_BEGIN_COMMIT) {
|
|
832
|
+
/* Response received after aborting transaction */
|
|
833
|
+
rd_rkb_dbg(rkb, EOS, "ADDPARTS",
|
|
834
|
+
"Ignoring outdated AddPartitionsToTxn response in "
|
|
835
|
+
"state %s",
|
|
836
|
+
rd_kafka_txn_state2str(rk->rk_eos.txn_state));
|
|
837
|
+
rd_kafka_rdunlock(rk);
|
|
838
|
+
err = RD_KAFKA_RESP_ERR__OUTDATED;
|
|
839
|
+
goto done;
|
|
840
|
+
}
|
|
841
|
+
rd_kafka_rdunlock(rk);
|
|
842
|
+
|
|
843
|
+
rd_kafka_buf_read_throttle_time(rkbuf);
|
|
844
|
+
|
|
845
|
+
rd_kafka_buf_read_i32(rkbuf, &TopicCnt);
|
|
846
|
+
|
|
847
|
+
while (TopicCnt-- > 0) {
|
|
848
|
+
rd_kafkap_str_t Topic;
|
|
849
|
+
rd_kafka_topic_t *rkt;
|
|
850
|
+
int32_t PartCnt;
|
|
851
|
+
rd_bool_t request_error = rd_false;
|
|
852
|
+
|
|
853
|
+
rd_kafka_buf_read_str(rkbuf, &Topic);
|
|
854
|
+
rd_kafka_buf_read_i32(rkbuf, &PartCnt);
|
|
855
|
+
|
|
856
|
+
rkt = rd_kafka_topic_find0(rk, &Topic);
|
|
857
|
+
if (rkt)
|
|
858
|
+
rd_kafka_topic_rdlock(rkt); /* for toppar_get() */
|
|
859
|
+
|
|
860
|
+
while (PartCnt-- > 0) {
|
|
861
|
+
rd_kafka_toppar_t *rktp = NULL;
|
|
862
|
+
int32_t Partition;
|
|
863
|
+
int16_t ErrorCode;
|
|
864
|
+
int p_actions = 0;
|
|
865
|
+
|
|
866
|
+
rd_kafka_buf_read_i32(rkbuf, &Partition);
|
|
867
|
+
rd_kafka_buf_read_i16(rkbuf, &ErrorCode);
|
|
868
|
+
|
|
869
|
+
if (rkt)
|
|
870
|
+
rktp = rd_kafka_toppar_get(rkt, Partition,
|
|
871
|
+
rd_false);
|
|
872
|
+
|
|
873
|
+
if (!rktp) {
|
|
874
|
+
rd_rkb_dbg(rkb, EOS | RD_KAFKA_DBG_PROTOCOL,
|
|
875
|
+
"ADDPARTS",
|
|
876
|
+
"Unknown partition \"%.*s\" "
|
|
877
|
+
"[%" PRId32
|
|
878
|
+
"] in AddPartitionsToTxn "
|
|
879
|
+
"response: ignoring",
|
|
880
|
+
RD_KAFKAP_STR_PR(&Topic), Partition);
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
switch (ErrorCode) {
|
|
885
|
+
case RD_KAFKA_RESP_ERR_NO_ERROR:
|
|
886
|
+
/* Move rktp from pending to proper list */
|
|
887
|
+
rd_kafka_txn_partition_registered(rktp);
|
|
888
|
+
break;
|
|
889
|
+
|
|
890
|
+
/* Request-level errors.
|
|
891
|
+
* As soon as any of these errors are seen
|
|
892
|
+
* the rest of the partitions are ignored
|
|
893
|
+
* since they will have the same error. */
|
|
894
|
+
case RD_KAFKA_RESP_ERR_NOT_COORDINATOR:
|
|
895
|
+
case RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE:
|
|
896
|
+
reset_coord_err = ErrorCode;
|
|
897
|
+
p_actions |= RD_KAFKA_ERR_ACTION_RETRY;
|
|
898
|
+
err = ErrorCode;
|
|
899
|
+
request_error = rd_true;
|
|
900
|
+
break;
|
|
901
|
+
|
|
902
|
+
case RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS:
|
|
903
|
+
retry_backoff_ms = 20;
|
|
904
|
+
/* FALLTHRU */
|
|
905
|
+
case RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS:
|
|
906
|
+
case RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART:
|
|
907
|
+
p_actions |= RD_KAFKA_ERR_ACTION_RETRY;
|
|
908
|
+
err = ErrorCode;
|
|
909
|
+
request_error = rd_true;
|
|
910
|
+
break;
|
|
911
|
+
|
|
912
|
+
case RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH:
|
|
913
|
+
case RD_KAFKA_RESP_ERR_PRODUCER_FENCED:
|
|
914
|
+
case RD_KAFKA_RESP_ERR_TRANSACTIONAL_ID_AUTHORIZATION_FAILED:
|
|
915
|
+
case RD_KAFKA_RESP_ERR_INVALID_TXN_STATE:
|
|
916
|
+
case RD_KAFKA_RESP_ERR_CLUSTER_AUTHORIZATION_FAILED:
|
|
917
|
+
p_actions |= RD_KAFKA_ERR_ACTION_FATAL;
|
|
918
|
+
err = ErrorCode;
|
|
919
|
+
request_error = rd_true;
|
|
920
|
+
break;
|
|
921
|
+
|
|
922
|
+
case RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID:
|
|
923
|
+
case RD_KAFKA_RESP_ERR_INVALID_PRODUCER_ID_MAPPING:
|
|
924
|
+
require_bump = rd_true;
|
|
925
|
+
p_actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
926
|
+
err = ErrorCode;
|
|
927
|
+
request_error = rd_true;
|
|
928
|
+
break;
|
|
929
|
+
|
|
930
|
+
/* Partition-level errors.
|
|
931
|
+
* Continue with rest of partitions. */
|
|
932
|
+
case RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED:
|
|
933
|
+
p_actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
934
|
+
err = ErrorCode;
|
|
935
|
+
break;
|
|
936
|
+
|
|
937
|
+
case RD_KAFKA_RESP_ERR_OPERATION_NOT_ATTEMPTED:
|
|
938
|
+
/* Partition skipped due to other partition's
|
|
939
|
+
* error. */
|
|
940
|
+
p_actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
941
|
+
if (!err)
|
|
942
|
+
err = ErrorCode;
|
|
943
|
+
break;
|
|
944
|
+
|
|
945
|
+
default:
|
|
946
|
+
/* Other partition error */
|
|
947
|
+
p_actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
948
|
+
err = ErrorCode;
|
|
949
|
+
break;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (ErrorCode) {
|
|
953
|
+
actions |= p_actions;
|
|
954
|
+
|
|
955
|
+
if (!(p_actions &
|
|
956
|
+
(RD_KAFKA_ERR_ACTION_FATAL |
|
|
957
|
+
RD_KAFKA_ERR_ACTION_PERMANENT)))
|
|
958
|
+
rd_rkb_dbg(
|
|
959
|
+
rkb, EOS, "ADDPARTS",
|
|
960
|
+
"AddPartitionsToTxn response: "
|
|
961
|
+
"partition \"%.*s\": "
|
|
962
|
+
"[%" PRId32 "]: %s",
|
|
963
|
+
RD_KAFKAP_STR_PR(&Topic), Partition,
|
|
964
|
+
rd_kafka_err2str(ErrorCode));
|
|
965
|
+
else
|
|
966
|
+
rd_rkb_log(rkb, LOG_ERR, "ADDPARTS",
|
|
967
|
+
"Failed to add partition "
|
|
968
|
+
"\"%.*s\" [%" PRId32
|
|
969
|
+
"] to "
|
|
970
|
+
"transaction: %s",
|
|
971
|
+
RD_KAFKAP_STR_PR(&Topic),
|
|
972
|
+
Partition,
|
|
973
|
+
rd_kafka_err2str(ErrorCode));
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
rd_kafka_toppar_destroy(rktp);
|
|
977
|
+
|
|
978
|
+
if (request_error)
|
|
979
|
+
break; /* Request-level error seen, bail out */
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if (rkt) {
|
|
983
|
+
rd_kafka_topic_rdunlock(rkt);
|
|
984
|
+
rd_kafka_topic_destroy0(rkt);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
if (request_error)
|
|
988
|
+
break; /* Request-level error seen, bail out */
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if (actions) /* Actions set from encountered errors */
|
|
992
|
+
goto done;
|
|
993
|
+
|
|
994
|
+
/* Since these partitions are now allowed to produce
|
|
995
|
+
* we wake up all broker threads. */
|
|
996
|
+
rd_kafka_all_brokers_wakeup(rk, RD_KAFKA_BROKER_STATE_INIT,
|
|
997
|
+
"partitions added to transaction");
|
|
998
|
+
|
|
999
|
+
goto done;
|
|
1000
|
+
|
|
1001
|
+
err_parse:
|
|
1002
|
+
err = rkbuf->rkbuf_err;
|
|
1003
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
1004
|
+
|
|
1005
|
+
done:
|
|
1006
|
+
if (err) {
|
|
1007
|
+
rd_assert(rk->rk_eos.txn_req_cnt > 0);
|
|
1008
|
+
rk->rk_eos.txn_req_cnt--;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/* Handle local request-level errors */
|
|
1012
|
+
switch (err) {
|
|
1013
|
+
case RD_KAFKA_RESP_ERR_NO_ERROR:
|
|
1014
|
+
break;
|
|
1015
|
+
|
|
1016
|
+
case RD_KAFKA_RESP_ERR__DESTROY:
|
|
1017
|
+
case RD_KAFKA_RESP_ERR__OUTDATED:
|
|
1018
|
+
/* Terminating or outdated, ignore response */
|
|
1019
|
+
return;
|
|
1020
|
+
|
|
1021
|
+
case RD_KAFKA_RESP_ERR__TRANSPORT:
|
|
1022
|
+
case RD_KAFKA_RESP_ERR__TIMED_OUT:
|
|
1023
|
+
default:
|
|
1024
|
+
/* For these errors we can't be sure if the
|
|
1025
|
+
* request was received by the broker or not,
|
|
1026
|
+
* so increase the txn_req_cnt back up as if
|
|
1027
|
+
* they were received so that and EndTxnRequest
|
|
1028
|
+
* is sent on abort_transaction(). */
|
|
1029
|
+
rk->rk_eos.txn_req_cnt++;
|
|
1030
|
+
actions |= RD_KAFKA_ERR_ACTION_RETRY;
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
if (reset_coord_err) {
|
|
1035
|
+
rd_kafka_wrlock(rk);
|
|
1036
|
+
rd_kafka_txn_coord_set(rk, NULL,
|
|
1037
|
+
"AddPartitionsToTxn failed: %s",
|
|
1038
|
+
rd_kafka_err2str(reset_coord_err));
|
|
1039
|
+
rd_kafka_wrunlock(rk);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/* Partitions that failed will still be on the waitresp list
|
|
1043
|
+
* and are moved back to the pending list for the next scheduled
|
|
1044
|
+
* AddPartitionsToTxn request.
|
|
1045
|
+
* If this request was successful there will be no remaining partitions
|
|
1046
|
+
* on the waitresp list.
|
|
1047
|
+
*/
|
|
1048
|
+
mtx_lock(&rk->rk_eos.txn_pending_lock);
|
|
1049
|
+
TAILQ_CONCAT_SORTED(&rk->rk_eos.txn_pending_rktps,
|
|
1050
|
+
&rk->rk_eos.txn_waitresp_rktps, rd_kafka_toppar_t *,
|
|
1051
|
+
rktp_txnlink, rd_kafka_toppar_topic_cmp);
|
|
1052
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
1053
|
+
|
|
1054
|
+
err = rd_kafka_txn_normalize_err(err);
|
|
1055
|
+
|
|
1056
|
+
if (actions & RD_KAFKA_ERR_ACTION_FATAL) {
|
|
1057
|
+
rd_kafka_txn_set_fatal_error(rk, RD_DO_LOCK, err,
|
|
1058
|
+
"Failed to add partitions to "
|
|
1059
|
+
"transaction: %s",
|
|
1060
|
+
rd_kafka_err2str(err));
|
|
1061
|
+
|
|
1062
|
+
} else if (actions & RD_KAFKA_ERR_ACTION_PERMANENT) {
|
|
1063
|
+
/* Treat all other permanent errors as abortable errors.
|
|
1064
|
+
* If an epoch bump is required let idempo sort it out. */
|
|
1065
|
+
if (require_bump)
|
|
1066
|
+
rd_kafka_idemp_drain_epoch_bump(
|
|
1067
|
+
rk, err,
|
|
1068
|
+
"Failed to add partition(s) to transaction "
|
|
1069
|
+
"on broker %s: %s (after %d ms)",
|
|
1070
|
+
rd_kafka_broker_name(rkb), rd_kafka_err2str(err),
|
|
1071
|
+
(int)(request->rkbuf_ts_sent / 1000));
|
|
1072
|
+
else
|
|
1073
|
+
rd_kafka_txn_set_abortable_error(
|
|
1074
|
+
rk, err,
|
|
1075
|
+
"Failed to add partition(s) to transaction "
|
|
1076
|
+
"on broker %s: %s (after %d ms)",
|
|
1077
|
+
rd_kafka_broker_name(rkb), rd_kafka_err2str(err),
|
|
1078
|
+
(int)(request->rkbuf_ts_sent / 1000));
|
|
1079
|
+
|
|
1080
|
+
} else {
|
|
1081
|
+
/* Schedule registration of any new or remaining partitions */
|
|
1082
|
+
rd_kafka_txn_schedule_register_partitions(
|
|
1083
|
+
rk, (actions & RD_KAFKA_ERR_ACTION_RETRY)
|
|
1084
|
+
? retry_backoff_ms
|
|
1085
|
+
: 1 /*immediate*/);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* @brief Send AddPartitionsToTxnRequest to the transaction coordinator.
|
|
1092
|
+
*
|
|
1093
|
+
* @locality rdkafka main thread
|
|
1094
|
+
* @locks none
|
|
1095
|
+
*/
|
|
1096
|
+
static void rd_kafka_txn_register_partitions(rd_kafka_t *rk) {
|
|
1097
|
+
char errstr[512];
|
|
1098
|
+
rd_kafka_resp_err_t err;
|
|
1099
|
+
rd_kafka_error_t *error;
|
|
1100
|
+
rd_kafka_pid_t pid;
|
|
1101
|
+
|
|
1102
|
+
/* Require operational state */
|
|
1103
|
+
rd_kafka_rdlock(rk);
|
|
1104
|
+
error =
|
|
1105
|
+
rd_kafka_txn_require_state(rk, RD_KAFKA_TXN_STATE_IN_TRANSACTION,
|
|
1106
|
+
RD_KAFKA_TXN_STATE_BEGIN_COMMIT);
|
|
1107
|
+
|
|
1108
|
+
if (unlikely(error != NULL)) {
|
|
1109
|
+
rd_kafka_rdunlock(rk);
|
|
1110
|
+
rd_kafka_dbg(rk, EOS, "ADDPARTS",
|
|
1111
|
+
"Not registering partitions: %s",
|
|
1112
|
+
rd_kafka_error_string(error));
|
|
1113
|
+
rd_kafka_error_destroy(error);
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/* Get pid, checked later */
|
|
1118
|
+
pid = rd_kafka_idemp_get_pid0(rk, RD_DONT_LOCK, rd_false);
|
|
1119
|
+
|
|
1120
|
+
rd_kafka_rdunlock(rk);
|
|
1121
|
+
|
|
1122
|
+
/* Transaction coordinator needs to be up */
|
|
1123
|
+
if (!rd_kafka_broker_is_up(rk->rk_eos.txn_coord)) {
|
|
1124
|
+
rd_kafka_dbg(rk, EOS, "ADDPARTS",
|
|
1125
|
+
"Not registering partitions: "
|
|
1126
|
+
"coordinator is not available");
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
mtx_lock(&rk->rk_eos.txn_pending_lock);
|
|
1131
|
+
if (TAILQ_EMPTY(&rk->rk_eos.txn_pending_rktps)) {
|
|
1132
|
+
/* No pending partitions to register */
|
|
1133
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if (!TAILQ_EMPTY(&rk->rk_eos.txn_waitresp_rktps)) {
|
|
1138
|
+
/* Only allow one outstanding AddPartitionsToTxnRequest */
|
|
1139
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
1140
|
+
rd_kafka_dbg(rk, EOS, "ADDPARTS",
|
|
1141
|
+
"Not registering partitions: waiting for "
|
|
1142
|
+
"previous AddPartitionsToTxn request to complete");
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* Require valid pid */
|
|
1147
|
+
if (unlikely(!rd_kafka_pid_valid(pid))) {
|
|
1148
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
1149
|
+
rd_kafka_dbg(rk, EOS, "ADDPARTS",
|
|
1150
|
+
"Not registering partitions: "
|
|
1151
|
+
"No PID available (idempotence state %s)",
|
|
1152
|
+
rd_kafka_idemp_state2str(rk->rk_eos.idemp_state));
|
|
1153
|
+
rd_dassert(!*"BUG: No PID despite proper transaction state");
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
/* Send request to coordinator */
|
|
1159
|
+
err = rd_kafka_AddPartitionsToTxnRequest(
|
|
1160
|
+
rk->rk_eos.txn_coord, rk->rk_conf.eos.transactional_id, pid,
|
|
1161
|
+
&rk->rk_eos.txn_pending_rktps, errstr, sizeof(errstr),
|
|
1162
|
+
RD_KAFKA_REPLYQ(rk->rk_ops, 0),
|
|
1163
|
+
rd_kafka_txn_handle_AddPartitionsToTxn, NULL);
|
|
1164
|
+
if (err) {
|
|
1165
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
1166
|
+
rd_kafka_dbg(rk, EOS, "ADDPARTS",
|
|
1167
|
+
"Not registering partitions: %s", errstr);
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/* Move all pending partitions to wait-response list.
|
|
1172
|
+
* No need to keep waitresp sorted. */
|
|
1173
|
+
TAILQ_CONCAT(&rk->rk_eos.txn_waitresp_rktps,
|
|
1174
|
+
&rk->rk_eos.txn_pending_rktps, rktp_txnlink);
|
|
1175
|
+
|
|
1176
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
1177
|
+
|
|
1178
|
+
rk->rk_eos.txn_req_cnt++;
|
|
1179
|
+
|
|
1180
|
+
rd_rkb_dbg(rk->rk_eos.txn_coord, EOS, "ADDPARTS",
|
|
1181
|
+
"Registering partitions with transaction");
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
static void rd_kafka_txn_register_partitions_tmr_cb(rd_kafka_timers_t *rkts,
|
|
1186
|
+
void *arg) {
|
|
1187
|
+
rd_kafka_t *rk = arg;
|
|
1188
|
+
rd_kafka_txn_register_partitions(rk);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* @brief Schedule register_partitions() as soon as possible.
|
|
1194
|
+
*
|
|
1195
|
+
* @locality any
|
|
1196
|
+
* @locks any
|
|
1197
|
+
*/
|
|
1198
|
+
void rd_kafka_txn_schedule_register_partitions(rd_kafka_t *rk, int backoff_ms) {
|
|
1199
|
+
rd_kafka_timer_start_oneshot(
|
|
1200
|
+
&rk->rk_timers, &rk->rk_eos.txn_register_parts_tmr,
|
|
1201
|
+
rd_false /*dont-restart*/,
|
|
1202
|
+
backoff_ms ? backoff_ms * 1000 : 1 /* immediate */,
|
|
1203
|
+
rd_kafka_txn_register_partitions_tmr_cb, rk);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* @brief Clears \p flag from all rktps and destroys them, emptying
|
|
1210
|
+
* and reinitializing the \p tqh.
|
|
1211
|
+
*/
|
|
1212
|
+
static void rd_kafka_txn_clear_partitions_flag(rd_kafka_toppar_tqhead_t *tqh,
|
|
1213
|
+
int flag) {
|
|
1214
|
+
rd_kafka_toppar_t *rktp, *tmp;
|
|
1215
|
+
|
|
1216
|
+
TAILQ_FOREACH_SAFE(rktp, tqh, rktp_txnlink, tmp) {
|
|
1217
|
+
rd_kafka_toppar_lock(rktp);
|
|
1218
|
+
rd_dassert(rktp->rktp_flags & flag);
|
|
1219
|
+
rktp->rktp_flags &= ~flag;
|
|
1220
|
+
rd_kafka_toppar_unlock(rktp);
|
|
1221
|
+
rd_kafka_toppar_destroy(rktp);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
TAILQ_INIT(tqh);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* @brief Clear all pending partitions.
|
|
1230
|
+
*
|
|
1231
|
+
* @locks txn_pending_lock MUST be held
|
|
1232
|
+
*/
|
|
1233
|
+
static void rd_kafka_txn_clear_pending_partitions(rd_kafka_t *rk) {
|
|
1234
|
+
rd_kafka_txn_clear_partitions_flag(&rk->rk_eos.txn_pending_rktps,
|
|
1235
|
+
RD_KAFKA_TOPPAR_F_PEND_TXN);
|
|
1236
|
+
rd_kafka_txn_clear_partitions_flag(&rk->rk_eos.txn_waitresp_rktps,
|
|
1237
|
+
RD_KAFKA_TOPPAR_F_PEND_TXN);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* @brief Clear all added partitions.
|
|
1242
|
+
*
|
|
1243
|
+
* @locks rd_kafka_wrlock(rk) MUST be held
|
|
1244
|
+
*/
|
|
1245
|
+
static void rd_kafka_txn_clear_partitions(rd_kafka_t *rk) {
|
|
1246
|
+
rd_kafka_txn_clear_partitions_flag(&rk->rk_eos.txn_rktps,
|
|
1247
|
+
RD_KAFKA_TOPPAR_F_IN_TXN);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* @brief Async handler for init_transactions()
|
|
1254
|
+
*
|
|
1255
|
+
* @locks none
|
|
1256
|
+
* @locality rdkafka main thread
|
|
1257
|
+
*/
|
|
1258
|
+
static rd_kafka_op_res_t rd_kafka_txn_op_init_transactions(rd_kafka_t *rk,
|
|
1259
|
+
rd_kafka_q_t *rkq,
|
|
1260
|
+
rd_kafka_op_t *rko) {
|
|
1261
|
+
rd_kafka_error_t *error;
|
|
1262
|
+
|
|
1263
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
1264
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
1265
|
+
|
|
1266
|
+
rd_kafka_wrlock(rk);
|
|
1267
|
+
|
|
1268
|
+
if ((error = rd_kafka_txn_require_state(
|
|
1269
|
+
rk, RD_KAFKA_TXN_STATE_INIT, RD_KAFKA_TXN_STATE_WAIT_PID,
|
|
1270
|
+
RD_KAFKA_TXN_STATE_READY_NOT_ACKED))) {
|
|
1271
|
+
rd_kafka_wrunlock(rk);
|
|
1272
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
1273
|
+
|
|
1274
|
+
} else if (rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_READY_NOT_ACKED) {
|
|
1275
|
+
/* A previous init_transactions() called finished successfully
|
|
1276
|
+
* after timeout, the application has called init_transactions()
|
|
1277
|
+
* again, we do nothin here, ack_init_transactions() will
|
|
1278
|
+
* transition the state from READY_NOT_ACKED to READY. */
|
|
1279
|
+
rd_kafka_wrunlock(rk);
|
|
1280
|
+
|
|
1281
|
+
} else {
|
|
1282
|
+
|
|
1283
|
+
/* Possibly a no-op if already in WAIT_PID state */
|
|
1284
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_WAIT_PID);
|
|
1285
|
+
|
|
1286
|
+
rk->rk_eos.txn_init_err = RD_KAFKA_RESP_ERR_NO_ERROR;
|
|
1287
|
+
|
|
1288
|
+
rd_kafka_wrunlock(rk);
|
|
1289
|
+
|
|
1290
|
+
/* Start idempotent producer to acquire PID */
|
|
1291
|
+
rd_kafka_idemp_start(rk, rd_true /*immediately*/);
|
|
1292
|
+
|
|
1293
|
+
/* Do not call curr_api_set_result, it will be triggered from
|
|
1294
|
+
* idemp_state_change() when the PID has been retrieved. */
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* @brief Async handler for the application to acknowledge
|
|
1303
|
+
* successful background completion of init_transactions().
|
|
1304
|
+
*
|
|
1305
|
+
* @locks none
|
|
1306
|
+
* @locality rdkafka main thread
|
|
1307
|
+
*/
|
|
1308
|
+
static rd_kafka_op_res_t
|
|
1309
|
+
rd_kafka_txn_op_ack_init_transactions(rd_kafka_t *rk,
|
|
1310
|
+
rd_kafka_q_t *rkq,
|
|
1311
|
+
rd_kafka_op_t *rko) {
|
|
1312
|
+
rd_kafka_error_t *error;
|
|
1313
|
+
|
|
1314
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
1315
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
1316
|
+
|
|
1317
|
+
rd_kafka_wrlock(rk);
|
|
1318
|
+
|
|
1319
|
+
if (!(error = rd_kafka_txn_require_state(
|
|
1320
|
+
rk, RD_KAFKA_TXN_STATE_READY_NOT_ACKED)))
|
|
1321
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_READY);
|
|
1322
|
+
|
|
1323
|
+
rd_kafka_wrunlock(rk);
|
|
1324
|
+
|
|
1325
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
1326
|
+
|
|
1327
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
rd_kafka_error_t *rd_kafka_init_transactions(rd_kafka_t *rk, int timeout_ms) {
|
|
1333
|
+
rd_kafka_error_t *error;
|
|
1334
|
+
rd_ts_t abs_timeout;
|
|
1335
|
+
|
|
1336
|
+
/* Cap actual timeout to transaction.timeout.ms * 2 when an infinite
|
|
1337
|
+
* timeout is provided, this is to make sure the call doesn't block
|
|
1338
|
+
* indefinitely in case a coordinator is not available.
|
|
1339
|
+
* This is only needed for init_transactions() since there is no
|
|
1340
|
+
* coordinator to time us out yet. */
|
|
1341
|
+
if (timeout_ms == RD_POLL_INFINITE &&
|
|
1342
|
+
/* Avoid overflow */
|
|
1343
|
+
rk->rk_conf.eos.transaction_timeout_ms < INT_MAX / 2)
|
|
1344
|
+
timeout_ms = rk->rk_conf.eos.transaction_timeout_ms * 2;
|
|
1345
|
+
|
|
1346
|
+
if ((error = rd_kafka_txn_curr_api_begin(rk, "init_transactions",
|
|
1347
|
+
rd_false /* no cap */,
|
|
1348
|
+
timeout_ms, &abs_timeout)))
|
|
1349
|
+
return error;
|
|
1350
|
+
|
|
1351
|
+
/* init_transactions() will continue to operate in the background
|
|
1352
|
+
* if the timeout expires, and the application may call
|
|
1353
|
+
* init_transactions() again to resume the initialization
|
|
1354
|
+
* process.
|
|
1355
|
+
* For this reason we need two states:
|
|
1356
|
+
* - TXN_STATE_READY_NOT_ACKED for when initialization is done
|
|
1357
|
+
* but the API call timed out prior to success, meaning the
|
|
1358
|
+
* application does not know initialization finished and
|
|
1359
|
+
* is thus not allowed to call sub-sequent txn APIs, e.g. begin..()
|
|
1360
|
+
* - TXN_STATE_READY for when initialization is done and this
|
|
1361
|
+
* function has returned successfully to the application.
|
|
1362
|
+
*
|
|
1363
|
+
* And due to the two states we need two calls to the rdkafka main
|
|
1364
|
+
* thread (to keep txn_state synchronization in one place). */
|
|
1365
|
+
|
|
1366
|
+
/* First call is to trigger initialization */
|
|
1367
|
+
if ((error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_init_transactions,
|
|
1368
|
+
abs_timeout))) {
|
|
1369
|
+
if (rd_kafka_error_code(error) ==
|
|
1370
|
+
RD_KAFKA_RESP_ERR__TIMED_OUT) {
|
|
1371
|
+
/* See if there's a more meaningful txn_init_err set
|
|
1372
|
+
* by idempo that we can return. */
|
|
1373
|
+
rd_kafka_resp_err_t err;
|
|
1374
|
+
rd_kafka_rdlock(rk);
|
|
1375
|
+
err =
|
|
1376
|
+
rd_kafka_txn_normalize_err(rk->rk_eos.txn_init_err);
|
|
1377
|
+
rd_kafka_rdunlock(rk);
|
|
1378
|
+
|
|
1379
|
+
if (err && err != RD_KAFKA_RESP_ERR__TIMED_OUT) {
|
|
1380
|
+
rd_kafka_error_destroy(error);
|
|
1381
|
+
error = rd_kafka_error_new_retriable(
|
|
1382
|
+
err, "Failed to initialize Producer ID: %s",
|
|
1383
|
+
rd_kafka_err2str(err));
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
return rd_kafka_txn_curr_api_return(rk, rd_true, error);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
/* Second call is to transition from READY_NOT_ACKED -> READY,
|
|
1392
|
+
* if necessary. */
|
|
1393
|
+
error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_ack_init_transactions,
|
|
1394
|
+
/* Timeout must be infinite since this is
|
|
1395
|
+
* a synchronization point.
|
|
1396
|
+
* The call is immediate though, so this
|
|
1397
|
+
* will not block. */
|
|
1398
|
+
RD_POLL_INFINITE);
|
|
1399
|
+
|
|
1400
|
+
return rd_kafka_txn_curr_api_return(rk,
|
|
1401
|
+
/* not resumable at this point */
|
|
1402
|
+
rd_false, error);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
/**
|
|
1408
|
+
* @brief Handler for begin_transaction()
|
|
1409
|
+
*
|
|
1410
|
+
* @locks none
|
|
1411
|
+
* @locality rdkafka main thread
|
|
1412
|
+
*/
|
|
1413
|
+
static rd_kafka_op_res_t rd_kafka_txn_op_begin_transaction(rd_kafka_t *rk,
|
|
1414
|
+
rd_kafka_q_t *rkq,
|
|
1415
|
+
rd_kafka_op_t *rko) {
|
|
1416
|
+
rd_kafka_error_t *error;
|
|
1417
|
+
rd_bool_t wakeup_brokers = rd_false;
|
|
1418
|
+
|
|
1419
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
1420
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
1421
|
+
|
|
1422
|
+
rd_kafka_wrlock(rk);
|
|
1423
|
+
if (!(error =
|
|
1424
|
+
rd_kafka_txn_require_state(rk, RD_KAFKA_TXN_STATE_READY))) {
|
|
1425
|
+
rd_assert(TAILQ_EMPTY(&rk->rk_eos.txn_rktps));
|
|
1426
|
+
|
|
1427
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_IN_TRANSACTION);
|
|
1428
|
+
|
|
1429
|
+
rd_assert(rk->rk_eos.txn_req_cnt == 0);
|
|
1430
|
+
rd_atomic64_set(&rk->rk_eos.txn_dr_fails, 0);
|
|
1431
|
+
rk->rk_eos.txn_err = RD_KAFKA_RESP_ERR_NO_ERROR;
|
|
1432
|
+
RD_IF_FREE(rk->rk_eos.txn_errstr, rd_free);
|
|
1433
|
+
rk->rk_eos.txn_errstr = NULL;
|
|
1434
|
+
|
|
1435
|
+
/* Wake up all broker threads (that may have messages to send
|
|
1436
|
+
* that were waiting for this transaction state.
|
|
1437
|
+
* But needs to be done below with no lock held. */
|
|
1438
|
+
wakeup_brokers = rd_true;
|
|
1439
|
+
}
|
|
1440
|
+
rd_kafka_wrunlock(rk);
|
|
1441
|
+
|
|
1442
|
+
if (wakeup_brokers)
|
|
1443
|
+
rd_kafka_all_brokers_wakeup(rk, RD_KAFKA_BROKER_STATE_INIT,
|
|
1444
|
+
"begin transaction");
|
|
1445
|
+
|
|
1446
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
1447
|
+
|
|
1448
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
rd_kafka_error_t *rd_kafka_begin_transaction(rd_kafka_t *rk) {
|
|
1453
|
+
rd_kafka_error_t *error;
|
|
1454
|
+
|
|
1455
|
+
if ((error = rd_kafka_txn_curr_api_begin(rk, "begin_transaction",
|
|
1456
|
+
rd_false, 0, NULL)))
|
|
1457
|
+
return error;
|
|
1458
|
+
|
|
1459
|
+
error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_begin_transaction,
|
|
1460
|
+
RD_POLL_INFINITE);
|
|
1461
|
+
|
|
1462
|
+
return rd_kafka_txn_curr_api_return(rk, rd_false /*not resumable*/,
|
|
1463
|
+
error);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
|
|
1467
|
+
static rd_kafka_resp_err_t
|
|
1468
|
+
rd_kafka_txn_send_TxnOffsetCommitRequest(rd_kafka_broker_t *rkb,
|
|
1469
|
+
rd_kafka_op_t *rko,
|
|
1470
|
+
rd_kafka_replyq_t replyq,
|
|
1471
|
+
rd_kafka_resp_cb_t *resp_cb,
|
|
1472
|
+
void *reply_opaque);
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* @brief Handle TxnOffsetCommitResponse
|
|
1476
|
+
*
|
|
1477
|
+
* @locality rdkafka main thread
|
|
1478
|
+
* @locks none
|
|
1479
|
+
*/
|
|
1480
|
+
static void rd_kafka_txn_handle_TxnOffsetCommit(rd_kafka_t *rk,
|
|
1481
|
+
rd_kafka_broker_t *rkb,
|
|
1482
|
+
rd_kafka_resp_err_t err,
|
|
1483
|
+
rd_kafka_buf_t *rkbuf,
|
|
1484
|
+
rd_kafka_buf_t *request,
|
|
1485
|
+
void *opaque) {
|
|
1486
|
+
const int log_decode_errors = LOG_ERR;
|
|
1487
|
+
rd_kafka_op_t *rko = opaque;
|
|
1488
|
+
int actions = 0;
|
|
1489
|
+
rd_kafka_topic_partition_list_t *partitions = NULL;
|
|
1490
|
+
char errstr[512];
|
|
1491
|
+
|
|
1492
|
+
*errstr = '\0';
|
|
1493
|
+
|
|
1494
|
+
if (err)
|
|
1495
|
+
goto done;
|
|
1496
|
+
|
|
1497
|
+
rd_kafka_buf_read_throttle_time(rkbuf);
|
|
1498
|
+
|
|
1499
|
+
const rd_kafka_topic_partition_field_t fields[] = {
|
|
1500
|
+
RD_KAFKA_TOPIC_PARTITION_FIELD_PARTITION,
|
|
1501
|
+
RD_KAFKA_TOPIC_PARTITION_FIELD_ERR,
|
|
1502
|
+
RD_KAFKA_TOPIC_PARTITION_FIELD_END};
|
|
1503
|
+
partitions = rd_kafka_buf_read_topic_partitions(
|
|
1504
|
+
rkbuf, rd_false /*don't use topic_id*/, rd_true, 0, fields);
|
|
1505
|
+
if (!partitions)
|
|
1506
|
+
goto err_parse;
|
|
1507
|
+
|
|
1508
|
+
err = rd_kafka_topic_partition_list_get_err(partitions);
|
|
1509
|
+
if (err) {
|
|
1510
|
+
char errparts[256];
|
|
1511
|
+
rd_kafka_topic_partition_list_str(partitions, errparts,
|
|
1512
|
+
sizeof(errparts),
|
|
1513
|
+
RD_KAFKA_FMT_F_ONLY_ERR);
|
|
1514
|
+
rd_snprintf(errstr, sizeof(errstr),
|
|
1515
|
+
"Failed to commit offsets to transaction on "
|
|
1516
|
+
"broker %s: %s "
|
|
1517
|
+
"(after %dms)",
|
|
1518
|
+
rd_kafka_broker_name(rkb), errparts,
|
|
1519
|
+
(int)(request->rkbuf_ts_sent / 1000));
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
goto done;
|
|
1523
|
+
|
|
1524
|
+
err_parse:
|
|
1525
|
+
err = rkbuf->rkbuf_err;
|
|
1526
|
+
|
|
1527
|
+
done:
|
|
1528
|
+
if (err) {
|
|
1529
|
+
if (!*errstr) {
|
|
1530
|
+
rd_snprintf(errstr, sizeof(errstr),
|
|
1531
|
+
"Failed to commit offsets to "
|
|
1532
|
+
"transaction on broker %s: %s "
|
|
1533
|
+
"(after %d ms)",
|
|
1534
|
+
rkb ? rd_kafka_broker_name(rkb) : "(none)",
|
|
1535
|
+
rd_kafka_err2str(err),
|
|
1536
|
+
(int)(request->rkbuf_ts_sent / 1000));
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
|
|
1541
|
+
if (partitions)
|
|
1542
|
+
rd_kafka_topic_partition_list_destroy(partitions);
|
|
1543
|
+
|
|
1544
|
+
switch (err) {
|
|
1545
|
+
case RD_KAFKA_RESP_ERR_NO_ERROR:
|
|
1546
|
+
break;
|
|
1547
|
+
|
|
1548
|
+
case RD_KAFKA_RESP_ERR__DESTROY:
|
|
1549
|
+
/* Producer is being terminated, ignore the response. */
|
|
1550
|
+
case RD_KAFKA_RESP_ERR__OUTDATED:
|
|
1551
|
+
/* Set a non-actionable actions flag so that
|
|
1552
|
+
* curr_api_set_result() is called below, without
|
|
1553
|
+
* other side-effects. */
|
|
1554
|
+
actions = RD_KAFKA_ERR_ACTION_SPECIAL;
|
|
1555
|
+
return;
|
|
1556
|
+
|
|
1557
|
+
case RD_KAFKA_RESP_ERR_NOT_COORDINATOR:
|
|
1558
|
+
case RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE:
|
|
1559
|
+
case RD_KAFKA_RESP_ERR_REQUEST_TIMED_OUT:
|
|
1560
|
+
case RD_KAFKA_RESP_ERR__TRANSPORT:
|
|
1561
|
+
case RD_KAFKA_RESP_ERR__TIMED_OUT:
|
|
1562
|
+
case RD_KAFKA_RESP_ERR__TIMED_OUT_QUEUE:
|
|
1563
|
+
/* Note: this is the group coordinator, not the
|
|
1564
|
+
* transaction coordinator. */
|
|
1565
|
+
rd_kafka_coord_cache_evict(&rk->rk_coord_cache, rkb);
|
|
1566
|
+
actions |= RD_KAFKA_ERR_ACTION_RETRY;
|
|
1567
|
+
break;
|
|
1568
|
+
|
|
1569
|
+
case RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS:
|
|
1570
|
+
case RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS:
|
|
1571
|
+
case RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART:
|
|
1572
|
+
actions |= RD_KAFKA_ERR_ACTION_RETRY;
|
|
1573
|
+
break;
|
|
1574
|
+
|
|
1575
|
+
case RD_KAFKA_RESP_ERR_TRANSACTIONAL_ID_AUTHORIZATION_FAILED:
|
|
1576
|
+
case RD_KAFKA_RESP_ERR_CLUSTER_AUTHORIZATION_FAILED:
|
|
1577
|
+
case RD_KAFKA_RESP_ERR_INVALID_PRODUCER_ID_MAPPING:
|
|
1578
|
+
case RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH:
|
|
1579
|
+
case RD_KAFKA_RESP_ERR_INVALID_TXN_STATE:
|
|
1580
|
+
case RD_KAFKA_RESP_ERR_UNSUPPORTED_FOR_MESSAGE_FORMAT:
|
|
1581
|
+
actions |= RD_KAFKA_ERR_ACTION_FATAL;
|
|
1582
|
+
break;
|
|
1583
|
+
|
|
1584
|
+
case RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED:
|
|
1585
|
+
case RD_KAFKA_RESP_ERR_GROUP_AUTHORIZATION_FAILED:
|
|
1586
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
1587
|
+
break;
|
|
1588
|
+
|
|
1589
|
+
case RD_KAFKA_RESP_ERR_ILLEGAL_GENERATION:
|
|
1590
|
+
case RD_KAFKA_RESP_ERR_UNKNOWN_MEMBER_ID:
|
|
1591
|
+
case RD_KAFKA_RESP_ERR_FENCED_INSTANCE_ID:
|
|
1592
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
1593
|
+
break;
|
|
1594
|
+
|
|
1595
|
+
default:
|
|
1596
|
+
/* Unhandled error, fail transaction */
|
|
1597
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
1598
|
+
break;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
err = rd_kafka_txn_normalize_err(err);
|
|
1602
|
+
|
|
1603
|
+
if (actions & RD_KAFKA_ERR_ACTION_FATAL) {
|
|
1604
|
+
rd_kafka_txn_set_fatal_error(rk, RD_DO_LOCK, err, "%s", errstr);
|
|
1605
|
+
|
|
1606
|
+
} else if (actions & RD_KAFKA_ERR_ACTION_RETRY) {
|
|
1607
|
+
int remains_ms = rd_timeout_remains(rko->rko_u.txn.abs_timeout);
|
|
1608
|
+
|
|
1609
|
+
if (!rd_timeout_expired(remains_ms)) {
|
|
1610
|
+
rd_kafka_coord_req(
|
|
1611
|
+
rk, RD_KAFKA_COORD_GROUP,
|
|
1612
|
+
rko->rko_u.txn.cgmetadata->group_id,
|
|
1613
|
+
rd_kafka_txn_send_TxnOffsetCommitRequest, rko,
|
|
1614
|
+
500 /* 500ms delay before retrying */,
|
|
1615
|
+
rd_timeout_remains_limit0(
|
|
1616
|
+
remains_ms, rk->rk_conf.socket_timeout_ms),
|
|
1617
|
+
RD_KAFKA_REPLYQ(rk->rk_ops, 0),
|
|
1618
|
+
rd_kafka_txn_handle_TxnOffsetCommit, rko);
|
|
1619
|
+
return;
|
|
1620
|
+
} else if (!err)
|
|
1621
|
+
err = RD_KAFKA_RESP_ERR__TIMED_OUT;
|
|
1622
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
if (actions & RD_KAFKA_ERR_ACTION_PERMANENT)
|
|
1626
|
+
rd_kafka_txn_set_abortable_error(rk, err, "%s", errstr);
|
|
1627
|
+
|
|
1628
|
+
if (err)
|
|
1629
|
+
rd_kafka_txn_curr_api_set_result(
|
|
1630
|
+
rk, actions, rd_kafka_error_new(err, "%s", errstr));
|
|
1631
|
+
else
|
|
1632
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, NULL);
|
|
1633
|
+
|
|
1634
|
+
rd_kafka_op_destroy(rko);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* @brief Construct and send TxnOffsetCommitRequest.
|
|
1641
|
+
*
|
|
1642
|
+
* @locality rdkafka main thread
|
|
1643
|
+
* @locks none
|
|
1644
|
+
*/
|
|
1645
|
+
static rd_kafka_resp_err_t
|
|
1646
|
+
rd_kafka_txn_send_TxnOffsetCommitRequest(rd_kafka_broker_t *rkb,
|
|
1647
|
+
rd_kafka_op_t *rko,
|
|
1648
|
+
rd_kafka_replyq_t replyq,
|
|
1649
|
+
rd_kafka_resp_cb_t *resp_cb,
|
|
1650
|
+
void *reply_opaque) {
|
|
1651
|
+
rd_kafka_t *rk = rkb->rkb_rk;
|
|
1652
|
+
rd_kafka_buf_t *rkbuf;
|
|
1653
|
+
int16_t ApiVersion;
|
|
1654
|
+
rd_kafka_pid_t pid;
|
|
1655
|
+
const rd_kafka_consumer_group_metadata_t *cgmetadata =
|
|
1656
|
+
rko->rko_u.txn.cgmetadata;
|
|
1657
|
+
int cnt;
|
|
1658
|
+
|
|
1659
|
+
rd_kafka_rdlock(rk);
|
|
1660
|
+
if (rk->rk_eos.txn_state != RD_KAFKA_TXN_STATE_IN_TRANSACTION) {
|
|
1661
|
+
rd_kafka_rdunlock(rk);
|
|
1662
|
+
/* Do not free the rko, it is passed as the reply_opaque
|
|
1663
|
+
* on the reply queue by coord_req_fsm() when we return
|
|
1664
|
+
* an error here. */
|
|
1665
|
+
return RD_KAFKA_RESP_ERR__STATE;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
pid = rd_kafka_idemp_get_pid0(rk, RD_DONT_LOCK, rd_false);
|
|
1669
|
+
rd_kafka_rdunlock(rk);
|
|
1670
|
+
if (!rd_kafka_pid_valid(pid)) {
|
|
1671
|
+
/* Do not free the rko, it is passed as the reply_opaque
|
|
1672
|
+
* on the reply queue by coord_req_fsm() when we return
|
|
1673
|
+
* an error here. */
|
|
1674
|
+
return RD_KAFKA_RESP_ERR__STATE;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
ApiVersion = rd_kafka_broker_ApiVersion_supported(
|
|
1678
|
+
rkb, RD_KAFKAP_TxnOffsetCommit, 0, 3, NULL);
|
|
1679
|
+
if (ApiVersion == -1) {
|
|
1680
|
+
/* Do not free the rko, it is passed as the reply_opaque
|
|
1681
|
+
* on the reply queue by coord_req_fsm() when we return
|
|
1682
|
+
* an error here. */
|
|
1683
|
+
return RD_KAFKA_RESP_ERR__UNSUPPORTED_FEATURE;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
rkbuf = rd_kafka_buf_new_flexver_request(
|
|
1687
|
+
rkb, RD_KAFKAP_TxnOffsetCommit, 1, rko->rko_u.txn.offsets->cnt * 50,
|
|
1688
|
+
ApiVersion >= 3);
|
|
1689
|
+
|
|
1690
|
+
/* transactional_id */
|
|
1691
|
+
rd_kafka_buf_write_str(rkbuf, rk->rk_conf.eos.transactional_id, -1);
|
|
1692
|
+
|
|
1693
|
+
/* group_id */
|
|
1694
|
+
rd_kafka_buf_write_str(rkbuf, rko->rko_u.txn.cgmetadata->group_id, -1);
|
|
1695
|
+
|
|
1696
|
+
/* PID */
|
|
1697
|
+
rd_kafka_buf_write_i64(rkbuf, pid.id);
|
|
1698
|
+
rd_kafka_buf_write_i16(rkbuf, pid.epoch);
|
|
1699
|
+
|
|
1700
|
+
if (ApiVersion >= 3) {
|
|
1701
|
+
/* GenerationId */
|
|
1702
|
+
rd_kafka_buf_write_i32(rkbuf, cgmetadata->generation_id);
|
|
1703
|
+
/* MemberId */
|
|
1704
|
+
rd_kafka_buf_write_str(rkbuf, cgmetadata->member_id, -1);
|
|
1705
|
+
/* GroupInstanceId */
|
|
1706
|
+
rd_kafka_buf_write_str(rkbuf, cgmetadata->group_instance_id,
|
|
1707
|
+
-1);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
/* Write per-partition offsets list */
|
|
1711
|
+
const rd_kafka_topic_partition_field_t fields[] = {
|
|
1712
|
+
RD_KAFKA_TOPIC_PARTITION_FIELD_PARTITION,
|
|
1713
|
+
RD_KAFKA_TOPIC_PARTITION_FIELD_OFFSET,
|
|
1714
|
+
ApiVersion >= 2 ? RD_KAFKA_TOPIC_PARTITION_FIELD_EPOCH
|
|
1715
|
+
: RD_KAFKA_TOPIC_PARTITION_FIELD_NOOP,
|
|
1716
|
+
RD_KAFKA_TOPIC_PARTITION_FIELD_METADATA,
|
|
1717
|
+
RD_KAFKA_TOPIC_PARTITION_FIELD_END};
|
|
1718
|
+
cnt = rd_kafka_buf_write_topic_partitions(
|
|
1719
|
+
rkbuf, rko->rko_u.txn.offsets, rd_true /*skip invalid offsets*/,
|
|
1720
|
+
rd_false /*any offset*/, rd_false /*don't use topic id*/,
|
|
1721
|
+
rd_true /*use topic name*/, fields);
|
|
1722
|
+
if (!cnt) {
|
|
1723
|
+
/* No valid partition offsets, don't commit. */
|
|
1724
|
+
rd_kafka_buf_destroy(rkbuf);
|
|
1725
|
+
/* Do not free the rko, it is passed as the reply_opaque
|
|
1726
|
+
* on the reply queue by coord_req_fsm() when we return
|
|
1727
|
+
* an error here. */
|
|
1728
|
+
return RD_KAFKA_RESP_ERR__NO_OFFSET;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
rd_kafka_buf_ApiVersion_set(rkbuf, ApiVersion, 0);
|
|
1732
|
+
|
|
1733
|
+
rkbuf->rkbuf_max_retries = RD_KAFKA_REQUEST_MAX_RETRIES;
|
|
1734
|
+
|
|
1735
|
+
rd_kafka_broker_buf_enq_replyq(rkb, rkbuf, replyq, resp_cb,
|
|
1736
|
+
reply_opaque);
|
|
1737
|
+
|
|
1738
|
+
return RD_KAFKA_RESP_ERR_NO_ERROR;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
|
|
1742
|
+
/**
|
|
1743
|
+
* @brief Handle AddOffsetsToTxnResponse
|
|
1744
|
+
*
|
|
1745
|
+
* @locality rdkafka main thread
|
|
1746
|
+
* @locks none
|
|
1747
|
+
*/
|
|
1748
|
+
static void rd_kafka_txn_handle_AddOffsetsToTxn(rd_kafka_t *rk,
|
|
1749
|
+
rd_kafka_broker_t *rkb,
|
|
1750
|
+
rd_kafka_resp_err_t err,
|
|
1751
|
+
rd_kafka_buf_t *rkbuf,
|
|
1752
|
+
rd_kafka_buf_t *request,
|
|
1753
|
+
void *opaque) {
|
|
1754
|
+
const int log_decode_errors = LOG_ERR;
|
|
1755
|
+
rd_kafka_op_t *rko = opaque;
|
|
1756
|
+
int16_t ErrorCode;
|
|
1757
|
+
int actions = 0;
|
|
1758
|
+
int remains_ms;
|
|
1759
|
+
|
|
1760
|
+
if (err == RD_KAFKA_RESP_ERR__DESTROY) {
|
|
1761
|
+
rd_kafka_op_destroy(rko);
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
if (err)
|
|
1766
|
+
goto done;
|
|
1767
|
+
|
|
1768
|
+
rd_kafka_buf_read_throttle_time(rkbuf);
|
|
1769
|
+
rd_kafka_buf_read_i16(rkbuf, &ErrorCode);
|
|
1770
|
+
|
|
1771
|
+
err = ErrorCode;
|
|
1772
|
+
goto done;
|
|
1773
|
+
|
|
1774
|
+
err_parse:
|
|
1775
|
+
err = rkbuf->rkbuf_err;
|
|
1776
|
+
|
|
1777
|
+
done:
|
|
1778
|
+
if (err) {
|
|
1779
|
+
rd_assert(rk->rk_eos.txn_req_cnt > 0);
|
|
1780
|
+
rk->rk_eos.txn_req_cnt--;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
remains_ms = rd_timeout_remains(rko->rko_u.txn.abs_timeout);
|
|
1784
|
+
if (rd_timeout_expired(remains_ms) && !err)
|
|
1785
|
+
err = RD_KAFKA_RESP_ERR__TIMED_OUT;
|
|
1786
|
+
|
|
1787
|
+
switch (err) {
|
|
1788
|
+
case RD_KAFKA_RESP_ERR_NO_ERROR:
|
|
1789
|
+
break;
|
|
1790
|
+
|
|
1791
|
+
case RD_KAFKA_RESP_ERR__DESTROY:
|
|
1792
|
+
/* Producer is being terminated, ignore the response. */
|
|
1793
|
+
case RD_KAFKA_RESP_ERR__OUTDATED:
|
|
1794
|
+
/* Set a non-actionable actions flag so that
|
|
1795
|
+
* curr_api_set_result() is called below, without
|
|
1796
|
+
* other side-effects. */
|
|
1797
|
+
actions = RD_KAFKA_ERR_ACTION_SPECIAL;
|
|
1798
|
+
break;
|
|
1799
|
+
|
|
1800
|
+
case RD_KAFKA_RESP_ERR__TRANSPORT:
|
|
1801
|
+
case RD_KAFKA_RESP_ERR__TIMED_OUT:
|
|
1802
|
+
/* For these errors we can't be sure if the
|
|
1803
|
+
* request was received by the broker or not,
|
|
1804
|
+
* so increase the txn_req_cnt back up as if
|
|
1805
|
+
* they were received so that and EndTxnRequest
|
|
1806
|
+
* is sent on abort_transaction(). */
|
|
1807
|
+
rk->rk_eos.txn_req_cnt++;
|
|
1808
|
+
/* FALLTHRU */
|
|
1809
|
+
case RD_KAFKA_RESP_ERR__TIMED_OUT_QUEUE:
|
|
1810
|
+
case RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE:
|
|
1811
|
+
case RD_KAFKA_RESP_ERR_NOT_COORDINATOR:
|
|
1812
|
+
case RD_KAFKA_RESP_ERR_REQUEST_TIMED_OUT:
|
|
1813
|
+
actions |=
|
|
1814
|
+
RD_KAFKA_ERR_ACTION_RETRY | RD_KAFKA_ERR_ACTION_REFRESH;
|
|
1815
|
+
break;
|
|
1816
|
+
|
|
1817
|
+
case RD_KAFKA_RESP_ERR_TRANSACTIONAL_ID_AUTHORIZATION_FAILED:
|
|
1818
|
+
case RD_KAFKA_RESP_ERR_CLUSTER_AUTHORIZATION_FAILED:
|
|
1819
|
+
case RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH:
|
|
1820
|
+
case RD_KAFKA_RESP_ERR_INVALID_TXN_STATE:
|
|
1821
|
+
case RD_KAFKA_RESP_ERR_UNSUPPORTED_FOR_MESSAGE_FORMAT:
|
|
1822
|
+
actions |= RD_KAFKA_ERR_ACTION_FATAL;
|
|
1823
|
+
break;
|
|
1824
|
+
|
|
1825
|
+
case RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED:
|
|
1826
|
+
case RD_KAFKA_RESP_ERR_GROUP_AUTHORIZATION_FAILED:
|
|
1827
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
1828
|
+
break;
|
|
1829
|
+
|
|
1830
|
+
case RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART:
|
|
1831
|
+
case RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS:
|
|
1832
|
+
case RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS:
|
|
1833
|
+
actions |= RD_KAFKA_ERR_ACTION_RETRY;
|
|
1834
|
+
break;
|
|
1835
|
+
|
|
1836
|
+
default:
|
|
1837
|
+
/* All unhandled errors are permanent */
|
|
1838
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
1839
|
+
break;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
err = rd_kafka_txn_normalize_err(err);
|
|
1843
|
+
|
|
1844
|
+
rd_kafka_dbg(rk, EOS, "ADDOFFSETS",
|
|
1845
|
+
"AddOffsetsToTxn response from %s: %s (%s)",
|
|
1846
|
+
rkb ? rd_kafka_broker_name(rkb) : "(none)",
|
|
1847
|
+
rd_kafka_err2name(err), rd_kafka_actions2str(actions));
|
|
1848
|
+
|
|
1849
|
+
/* All unhandled errors are considered permanent */
|
|
1850
|
+
if (err && !actions)
|
|
1851
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
1852
|
+
|
|
1853
|
+
if (actions & RD_KAFKA_ERR_ACTION_FATAL) {
|
|
1854
|
+
rd_kafka_txn_set_fatal_error(rk, RD_DO_LOCK, err,
|
|
1855
|
+
"Failed to add offsets to "
|
|
1856
|
+
"transaction: %s",
|
|
1857
|
+
rd_kafka_err2str(err));
|
|
1858
|
+
} else {
|
|
1859
|
+
if (actions & RD_KAFKA_ERR_ACTION_REFRESH)
|
|
1860
|
+
rd_kafka_txn_coord_timer_start(rk, 50);
|
|
1861
|
+
|
|
1862
|
+
if (actions & RD_KAFKA_ERR_ACTION_RETRY) {
|
|
1863
|
+
rd_rkb_dbg(
|
|
1864
|
+
rkb, EOS, "ADDOFFSETS",
|
|
1865
|
+
"Failed to add offsets to transaction on "
|
|
1866
|
+
"broker %s: %s (after %dms, %dms remains): "
|
|
1867
|
+
"error is retriable",
|
|
1868
|
+
rd_kafka_broker_name(rkb), rd_kafka_err2str(err),
|
|
1869
|
+
(int)(request->rkbuf_ts_sent / 1000), remains_ms);
|
|
1870
|
+
|
|
1871
|
+
if (!rd_timeout_expired(remains_ms) &&
|
|
1872
|
+
rd_kafka_buf_retry(rk->rk_eos.txn_coord, request)) {
|
|
1873
|
+
rk->rk_eos.txn_req_cnt++;
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
/* Propagate as retriable error through
|
|
1878
|
+
* api_reply() below */
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
if (err)
|
|
1883
|
+
rd_rkb_log(rkb, LOG_ERR, "ADDOFFSETS",
|
|
1884
|
+
"Failed to add offsets to transaction on broker %s: "
|
|
1885
|
+
"%s",
|
|
1886
|
+
rkb ? rd_kafka_broker_name(rkb) : "(none)",
|
|
1887
|
+
rd_kafka_err2str(err));
|
|
1888
|
+
|
|
1889
|
+
if (actions & RD_KAFKA_ERR_ACTION_PERMANENT)
|
|
1890
|
+
rd_kafka_txn_set_abortable_error(
|
|
1891
|
+
rk, err,
|
|
1892
|
+
"Failed to add offsets to "
|
|
1893
|
+
"transaction on broker %s: "
|
|
1894
|
+
"%s (after %dms)",
|
|
1895
|
+
rd_kafka_broker_name(rkb), rd_kafka_err2str(err),
|
|
1896
|
+
(int)(request->rkbuf_ts_sent / 1000));
|
|
1897
|
+
|
|
1898
|
+
if (!err) {
|
|
1899
|
+
/* Step 2: Commit offsets to transaction on the
|
|
1900
|
+
* group coordinator. */
|
|
1901
|
+
|
|
1902
|
+
rd_kafka_coord_req(
|
|
1903
|
+
rk, RD_KAFKA_COORD_GROUP,
|
|
1904
|
+
rko->rko_u.txn.cgmetadata->group_id,
|
|
1905
|
+
rd_kafka_txn_send_TxnOffsetCommitRequest, rko,
|
|
1906
|
+
0 /* no delay */,
|
|
1907
|
+
rd_timeout_remains_limit0(remains_ms,
|
|
1908
|
+
rk->rk_conf.socket_timeout_ms),
|
|
1909
|
+
RD_KAFKA_REPLYQ(rk->rk_ops, 0),
|
|
1910
|
+
rd_kafka_txn_handle_TxnOffsetCommit, rko);
|
|
1911
|
+
|
|
1912
|
+
} else {
|
|
1913
|
+
|
|
1914
|
+
rd_kafka_txn_curr_api_set_result(
|
|
1915
|
+
rk, actions,
|
|
1916
|
+
rd_kafka_error_new(
|
|
1917
|
+
err,
|
|
1918
|
+
"Failed to add offsets to transaction on "
|
|
1919
|
+
"broker %s: %s (after %dms)",
|
|
1920
|
+
rd_kafka_broker_name(rkb), rd_kafka_err2str(err),
|
|
1921
|
+
(int)(request->rkbuf_ts_sent / 1000)));
|
|
1922
|
+
|
|
1923
|
+
rd_kafka_op_destroy(rko);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
|
|
1928
|
+
/**
|
|
1929
|
+
* @brief Async handler for send_offsets_to_transaction()
|
|
1930
|
+
*
|
|
1931
|
+
* @locks none
|
|
1932
|
+
* @locality rdkafka main thread
|
|
1933
|
+
*/
|
|
1934
|
+
static rd_kafka_op_res_t
|
|
1935
|
+
rd_kafka_txn_op_send_offsets_to_transaction(rd_kafka_t *rk,
|
|
1936
|
+
rd_kafka_q_t *rkq,
|
|
1937
|
+
rd_kafka_op_t *rko) {
|
|
1938
|
+
rd_kafka_resp_err_t err = RD_KAFKA_RESP_ERR_NO_ERROR;
|
|
1939
|
+
char errstr[512];
|
|
1940
|
+
rd_kafka_error_t *error;
|
|
1941
|
+
rd_kafka_pid_t pid;
|
|
1942
|
+
|
|
1943
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
1944
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
1945
|
+
|
|
1946
|
+
*errstr = '\0';
|
|
1947
|
+
|
|
1948
|
+
rd_kafka_wrlock(rk);
|
|
1949
|
+
|
|
1950
|
+
if ((error = rd_kafka_txn_require_state(
|
|
1951
|
+
rk, RD_KAFKA_TXN_STATE_IN_TRANSACTION))) {
|
|
1952
|
+
rd_kafka_wrunlock(rk);
|
|
1953
|
+
goto err;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
rd_kafka_wrunlock(rk);
|
|
1957
|
+
|
|
1958
|
+
pid = rd_kafka_idemp_get_pid0(rk, RD_DONT_LOCK, rd_false);
|
|
1959
|
+
if (!rd_kafka_pid_valid(pid)) {
|
|
1960
|
+
rd_dassert(!*"BUG: No PID despite proper transaction state");
|
|
1961
|
+
error = rd_kafka_error_new_retriable(
|
|
1962
|
+
RD_KAFKA_RESP_ERR__STATE,
|
|
1963
|
+
"No PID available (idempotence state %s)",
|
|
1964
|
+
rd_kafka_idemp_state2str(rk->rk_eos.idemp_state));
|
|
1965
|
+
goto err;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
/* This is a multi-stage operation, consisting of:
|
|
1969
|
+
* 1) send AddOffsetsToTxnRequest to transaction coordinator.
|
|
1970
|
+
* 2) send TxnOffsetCommitRequest to group coordinator. */
|
|
1971
|
+
|
|
1972
|
+
err = rd_kafka_AddOffsetsToTxnRequest(
|
|
1973
|
+
rk->rk_eos.txn_coord, rk->rk_conf.eos.transactional_id, pid,
|
|
1974
|
+
rko->rko_u.txn.cgmetadata->group_id, errstr, sizeof(errstr),
|
|
1975
|
+
RD_KAFKA_REPLYQ(rk->rk_ops, 0), rd_kafka_txn_handle_AddOffsetsToTxn,
|
|
1976
|
+
rko);
|
|
1977
|
+
|
|
1978
|
+
if (err) {
|
|
1979
|
+
error = rd_kafka_error_new_retriable(err, "%s", errstr);
|
|
1980
|
+
goto err;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
rk->rk_eos.txn_req_cnt++;
|
|
1984
|
+
|
|
1985
|
+
return RD_KAFKA_OP_RES_KEEP; /* the rko is passed to AddOffsetsToTxn */
|
|
1986
|
+
|
|
1987
|
+
err:
|
|
1988
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
1989
|
+
|
|
1990
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
/**
|
|
1994
|
+
* error returns:
|
|
1995
|
+
* ERR__TRANSPORT - retryable
|
|
1996
|
+
*/
|
|
1997
|
+
rd_kafka_error_t *rd_kafka_send_offsets_to_transaction(
|
|
1998
|
+
rd_kafka_t *rk,
|
|
1999
|
+
const rd_kafka_topic_partition_list_t *offsets,
|
|
2000
|
+
const rd_kafka_consumer_group_metadata_t *cgmetadata,
|
|
2001
|
+
int timeout_ms) {
|
|
2002
|
+
rd_kafka_error_t *error;
|
|
2003
|
+
rd_kafka_op_t *rko;
|
|
2004
|
+
rd_kafka_topic_partition_list_t *valid_offsets;
|
|
2005
|
+
rd_ts_t abs_timeout;
|
|
2006
|
+
|
|
2007
|
+
if (!cgmetadata || !offsets)
|
|
2008
|
+
return rd_kafka_error_new(
|
|
2009
|
+
RD_KAFKA_RESP_ERR__INVALID_ARG,
|
|
2010
|
+
"cgmetadata and offsets are required parameters");
|
|
2011
|
+
|
|
2012
|
+
if ((error = rd_kafka_txn_curr_api_begin(
|
|
2013
|
+
rk, "send_offsets_to_transaction",
|
|
2014
|
+
/* Cap timeout to txn timeout */
|
|
2015
|
+
rd_true, timeout_ms, &abs_timeout)))
|
|
2016
|
+
return error;
|
|
2017
|
+
|
|
2018
|
+
|
|
2019
|
+
valid_offsets = rd_kafka_topic_partition_list_match(
|
|
2020
|
+
offsets, rd_kafka_topic_partition_match_valid_offset, NULL);
|
|
2021
|
+
|
|
2022
|
+
if (valid_offsets->cnt == 0) {
|
|
2023
|
+
/* No valid offsets, e.g., nothing was consumed,
|
|
2024
|
+
* this is not an error, do nothing. */
|
|
2025
|
+
rd_kafka_topic_partition_list_destroy(valid_offsets);
|
|
2026
|
+
return rd_kafka_txn_curr_api_return(rk, rd_false, NULL);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
rd_kafka_topic_partition_list_sort_by_topic(valid_offsets);
|
|
2030
|
+
|
|
2031
|
+
rko = rd_kafka_op_new_cb(rk, RD_KAFKA_OP_TXN,
|
|
2032
|
+
rd_kafka_txn_op_send_offsets_to_transaction);
|
|
2033
|
+
rko->rko_u.txn.offsets = valid_offsets;
|
|
2034
|
+
rko->rko_u.txn.cgmetadata =
|
|
2035
|
+
rd_kafka_consumer_group_metadata_dup(cgmetadata);
|
|
2036
|
+
rko->rko_u.txn.abs_timeout = abs_timeout;
|
|
2037
|
+
|
|
2038
|
+
/* Timeout is enforced by op_send_offsets_to_transaction() */
|
|
2039
|
+
error = rd_kafka_txn_op_req1(rk, rko, RD_POLL_INFINITE);
|
|
2040
|
+
|
|
2041
|
+
return rd_kafka_txn_curr_api_return(rk, rd_false, error);
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
|
|
2045
|
+
|
|
2046
|
+
/**
|
|
2047
|
+
* @brief Successfully complete the transaction.
|
|
2048
|
+
*
|
|
2049
|
+
* Current state must be either COMMIT_NOT_ACKED or ABORT_NOT_ACKED.
|
|
2050
|
+
*
|
|
2051
|
+
* @locality rdkafka main thread
|
|
2052
|
+
* @locks rd_kafka_wrlock(rk) MUST be held
|
|
2053
|
+
*/
|
|
2054
|
+
static void rd_kafka_txn_complete(rd_kafka_t *rk, rd_bool_t is_commit) {
|
|
2055
|
+
rd_kafka_dbg(rk, EOS, "TXNCOMPLETE", "Transaction successfully %s",
|
|
2056
|
+
is_commit ? "committed" : "aborted");
|
|
2057
|
+
|
|
2058
|
+
/* Clear all transaction partition state */
|
|
2059
|
+
rd_kafka_txn_clear_pending_partitions(rk);
|
|
2060
|
+
rd_kafka_txn_clear_partitions(rk);
|
|
2061
|
+
|
|
2062
|
+
rk->rk_eos.txn_requires_epoch_bump = rd_false;
|
|
2063
|
+
rk->rk_eos.txn_req_cnt = 0;
|
|
2064
|
+
|
|
2065
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_READY);
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
|
|
2069
|
+
/**
|
|
2070
|
+
* @brief EndTxn (commit or abort of transaction on the coordinator) is done,
|
|
2071
|
+
* or was skipped.
|
|
2072
|
+
* Continue with next steps (if any) before completing the local
|
|
2073
|
+
* transaction state.
|
|
2074
|
+
*
|
|
2075
|
+
* @locality rdkafka main thread
|
|
2076
|
+
* @locks_acquired rd_kafka_wrlock(rk), rk->rk_eos.txn_curr_api.lock
|
|
2077
|
+
*/
|
|
2078
|
+
static void rd_kafka_txn_endtxn_complete(rd_kafka_t *rk) {
|
|
2079
|
+
rd_bool_t is_commit;
|
|
2080
|
+
|
|
2081
|
+
mtx_lock(&rk->rk_eos.txn_curr_api.lock);
|
|
2082
|
+
is_commit = !strcmp(rk->rk_eos.txn_curr_api.name, "commit_transaction");
|
|
2083
|
+
mtx_unlock(&rk->rk_eos.txn_curr_api.lock);
|
|
2084
|
+
|
|
2085
|
+
rd_kafka_wrlock(rk);
|
|
2086
|
+
|
|
2087
|
+
/* If an epoch bump is required, let idempo handle it.
|
|
2088
|
+
* When the bump is finished we'll be notified through
|
|
2089
|
+
* idemp_state_change() and we can complete the local transaction state
|
|
2090
|
+
* and set the final API call result.
|
|
2091
|
+
* If the bumping fails a fatal error will be raised. */
|
|
2092
|
+
if (rk->rk_eos.txn_requires_epoch_bump) {
|
|
2093
|
+
rd_kafka_resp_err_t bump_err = rk->rk_eos.txn_err;
|
|
2094
|
+
rd_dassert(!is_commit);
|
|
2095
|
+
|
|
2096
|
+
rd_kafka_wrunlock(rk);
|
|
2097
|
+
|
|
2098
|
+
/* After the epoch bump is done we'll be transitioned
|
|
2099
|
+
* to the next state. */
|
|
2100
|
+
rd_kafka_idemp_drain_epoch_bump0(
|
|
2101
|
+
rk, rd_false /* don't allow txn abort */, bump_err,
|
|
2102
|
+
"Transaction aborted: %s", rd_kafka_err2str(bump_err));
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
if (is_commit)
|
|
2107
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_COMMIT_NOT_ACKED);
|
|
2108
|
+
else
|
|
2109
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_ABORT_NOT_ACKED);
|
|
2110
|
+
|
|
2111
|
+
rd_kafka_wrunlock(rk);
|
|
2112
|
+
|
|
2113
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, NULL);
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
|
|
2117
|
+
/**
|
|
2118
|
+
* @brief Handle EndTxnResponse (commit or abort)
|
|
2119
|
+
*
|
|
2120
|
+
* @locality rdkafka main thread
|
|
2121
|
+
* @locks none
|
|
2122
|
+
*/
|
|
2123
|
+
static void rd_kafka_txn_handle_EndTxn(rd_kafka_t *rk,
|
|
2124
|
+
rd_kafka_broker_t *rkb,
|
|
2125
|
+
rd_kafka_resp_err_t err,
|
|
2126
|
+
rd_kafka_buf_t *rkbuf,
|
|
2127
|
+
rd_kafka_buf_t *request,
|
|
2128
|
+
void *opaque) {
|
|
2129
|
+
const int log_decode_errors = LOG_ERR;
|
|
2130
|
+
int16_t ErrorCode;
|
|
2131
|
+
int actions = 0;
|
|
2132
|
+
rd_bool_t is_commit, may_retry = rd_false, require_bump = rd_false;
|
|
2133
|
+
|
|
2134
|
+
if (err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
2135
|
+
return;
|
|
2136
|
+
|
|
2137
|
+
is_commit = request->rkbuf_u.EndTxn.commit;
|
|
2138
|
+
|
|
2139
|
+
if (err)
|
|
2140
|
+
goto err;
|
|
2141
|
+
|
|
2142
|
+
rd_kafka_buf_read_throttle_time(rkbuf);
|
|
2143
|
+
rd_kafka_buf_read_i16(rkbuf, &ErrorCode);
|
|
2144
|
+
err = ErrorCode;
|
|
2145
|
+
goto err;
|
|
2146
|
+
|
|
2147
|
+
err_parse:
|
|
2148
|
+
err = rkbuf->rkbuf_err;
|
|
2149
|
+
/* FALLTHRU */
|
|
2150
|
+
|
|
2151
|
+
err:
|
|
2152
|
+
rd_kafka_wrlock(rk);
|
|
2153
|
+
|
|
2154
|
+
if (rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION) {
|
|
2155
|
+
may_retry = rd_true;
|
|
2156
|
+
|
|
2157
|
+
} else if (rk->rk_eos.txn_state ==
|
|
2158
|
+
RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION) {
|
|
2159
|
+
may_retry = rd_true;
|
|
2160
|
+
|
|
2161
|
+
} else if (rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_ABORTABLE_ERROR) {
|
|
2162
|
+
/* Transaction has failed locally, typically due to timeout.
|
|
2163
|
+
* Get the transaction error and return that instead of
|
|
2164
|
+
* this error.
|
|
2165
|
+
* This is a tricky state since the transaction will have
|
|
2166
|
+
* failed locally but the EndTxn(commit) may have succeeded. */
|
|
2167
|
+
|
|
2168
|
+
|
|
2169
|
+
if (err) {
|
|
2170
|
+
rd_kafka_txn_curr_api_set_result(
|
|
2171
|
+
rk, RD_KAFKA_ERR_ACTION_PERMANENT,
|
|
2172
|
+
rd_kafka_error_new(
|
|
2173
|
+
rk->rk_eos.txn_err,
|
|
2174
|
+
"EndTxn failed with %s but transaction "
|
|
2175
|
+
"had already failed due to: %s",
|
|
2176
|
+
rd_kafka_err2name(err), rk->rk_eos.txn_errstr));
|
|
2177
|
+
} else {
|
|
2178
|
+
/* If the transaction has failed locally but
|
|
2179
|
+
* this EndTxn commit succeeded we'll raise
|
|
2180
|
+
* a fatal error. */
|
|
2181
|
+
if (is_commit)
|
|
2182
|
+
rd_kafka_txn_curr_api_set_result(
|
|
2183
|
+
rk, RD_KAFKA_ERR_ACTION_FATAL,
|
|
2184
|
+
rd_kafka_error_new(
|
|
2185
|
+
rk->rk_eos.txn_err,
|
|
2186
|
+
"Transaction commit succeeded on the "
|
|
2187
|
+
"broker but the transaction "
|
|
2188
|
+
"had already failed locally due to: %s",
|
|
2189
|
+
rk->rk_eos.txn_errstr));
|
|
2190
|
+
|
|
2191
|
+
else
|
|
2192
|
+
rd_kafka_txn_curr_api_set_result(
|
|
2193
|
+
rk, RD_KAFKA_ERR_ACTION_PERMANENT,
|
|
2194
|
+
rd_kafka_error_new(
|
|
2195
|
+
rk->rk_eos.txn_err,
|
|
2196
|
+
"Transaction abort succeeded on the "
|
|
2197
|
+
"broker but the transaction"
|
|
2198
|
+
"had already failed locally due to: %s",
|
|
2199
|
+
rk->rk_eos.txn_errstr));
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
rd_kafka_wrunlock(rk);
|
|
2203
|
+
|
|
2204
|
+
|
|
2205
|
+
return;
|
|
2206
|
+
|
|
2207
|
+
} else if (!err) {
|
|
2208
|
+
/* Request is outdated */
|
|
2209
|
+
err = RD_KAFKA_RESP_ERR__OUTDATED;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
|
|
2213
|
+
rd_kafka_dbg(rk, EOS, "ENDTXN",
|
|
2214
|
+
"EndTxn returned %s in state %s (may_retry=%s)",
|
|
2215
|
+
rd_kafka_err2name(err),
|
|
2216
|
+
rd_kafka_txn_state2str(rk->rk_eos.txn_state),
|
|
2217
|
+
RD_STR_ToF(may_retry));
|
|
2218
|
+
|
|
2219
|
+
rd_kafka_wrunlock(rk);
|
|
2220
|
+
|
|
2221
|
+
switch (err) {
|
|
2222
|
+
case RD_KAFKA_RESP_ERR_NO_ERROR:
|
|
2223
|
+
break;
|
|
2224
|
+
|
|
2225
|
+
case RD_KAFKA_RESP_ERR__DESTROY:
|
|
2226
|
+
/* Producer is being terminated, ignore the response. */
|
|
2227
|
+
case RD_KAFKA_RESP_ERR__OUTDATED:
|
|
2228
|
+
/* Transactional state no longer relevant for this
|
|
2229
|
+
* outdated response. */
|
|
2230
|
+
break;
|
|
2231
|
+
case RD_KAFKA_RESP_ERR__TIMED_OUT:
|
|
2232
|
+
case RD_KAFKA_RESP_ERR__TIMED_OUT_QUEUE:
|
|
2233
|
+
/* Request timeout */
|
|
2234
|
+
/* FALLTHRU */
|
|
2235
|
+
case RD_KAFKA_RESP_ERR__TRANSPORT:
|
|
2236
|
+
actions |=
|
|
2237
|
+
RD_KAFKA_ERR_ACTION_RETRY | RD_KAFKA_ERR_ACTION_REFRESH;
|
|
2238
|
+
break;
|
|
2239
|
+
|
|
2240
|
+
case RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE:
|
|
2241
|
+
case RD_KAFKA_RESP_ERR_NOT_COORDINATOR:
|
|
2242
|
+
rd_kafka_wrlock(rk);
|
|
2243
|
+
rd_kafka_txn_coord_set(rk, NULL, "EndTxn failed: %s",
|
|
2244
|
+
rd_kafka_err2str(err));
|
|
2245
|
+
rd_kafka_wrunlock(rk);
|
|
2246
|
+
actions |= RD_KAFKA_ERR_ACTION_RETRY;
|
|
2247
|
+
break;
|
|
2248
|
+
|
|
2249
|
+
case RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS:
|
|
2250
|
+
case RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS:
|
|
2251
|
+
actions |= RD_KAFKA_ERR_ACTION_RETRY;
|
|
2252
|
+
break;
|
|
2253
|
+
|
|
2254
|
+
case RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID:
|
|
2255
|
+
case RD_KAFKA_RESP_ERR_INVALID_PRODUCER_ID_MAPPING:
|
|
2256
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
2257
|
+
require_bump = rd_true;
|
|
2258
|
+
break;
|
|
2259
|
+
|
|
2260
|
+
case RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH:
|
|
2261
|
+
case RD_KAFKA_RESP_ERR_PRODUCER_FENCED:
|
|
2262
|
+
case RD_KAFKA_RESP_ERR_TRANSACTIONAL_ID_AUTHORIZATION_FAILED:
|
|
2263
|
+
case RD_KAFKA_RESP_ERR_CLUSTER_AUTHORIZATION_FAILED:
|
|
2264
|
+
case RD_KAFKA_RESP_ERR_INVALID_TXN_STATE:
|
|
2265
|
+
actions |= RD_KAFKA_ERR_ACTION_FATAL;
|
|
2266
|
+
break;
|
|
2267
|
+
|
|
2268
|
+
default:
|
|
2269
|
+
/* All unhandled errors are permanent */
|
|
2270
|
+
actions |= RD_KAFKA_ERR_ACTION_PERMANENT;
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
err = rd_kafka_txn_normalize_err(err);
|
|
2274
|
+
|
|
2275
|
+
if (actions & RD_KAFKA_ERR_ACTION_FATAL) {
|
|
2276
|
+
rd_kafka_txn_set_fatal_error(rk, RD_DO_LOCK, err,
|
|
2277
|
+
"Failed to end transaction: %s",
|
|
2278
|
+
rd_kafka_err2str(err));
|
|
2279
|
+
} else {
|
|
2280
|
+
if (actions & RD_KAFKA_ERR_ACTION_REFRESH)
|
|
2281
|
+
rd_kafka_txn_coord_timer_start(rk, 50);
|
|
2282
|
+
|
|
2283
|
+
if (actions & RD_KAFKA_ERR_ACTION_PERMANENT) {
|
|
2284
|
+
if (require_bump && !is_commit) {
|
|
2285
|
+
/* Abort failed to due invalid PID, starting
|
|
2286
|
+
* with KIP-360 we can have idempo sort out
|
|
2287
|
+
* epoch bumping.
|
|
2288
|
+
* When the epoch has been bumped we'll detect
|
|
2289
|
+
* the idemp_state_change and complete the
|
|
2290
|
+
* current API call. */
|
|
2291
|
+
rd_kafka_idemp_drain_epoch_bump0(
|
|
2292
|
+
rk,
|
|
2293
|
+
/* don't allow txn abort */
|
|
2294
|
+
rd_false, err, "EndTxn %s failed: %s",
|
|
2295
|
+
is_commit ? "commit" : "abort",
|
|
2296
|
+
rd_kafka_err2str(err));
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
/* For aborts we need to revert the state back to
|
|
2301
|
+
* BEGIN_ABORT so that the abort can be retried from
|
|
2302
|
+
* the beginning in op_abort_transaction(). */
|
|
2303
|
+
rd_kafka_wrlock(rk);
|
|
2304
|
+
if (rk->rk_eos.txn_state ==
|
|
2305
|
+
RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION)
|
|
2306
|
+
rd_kafka_txn_set_state(
|
|
2307
|
+
rk, RD_KAFKA_TXN_STATE_BEGIN_ABORT);
|
|
2308
|
+
rd_kafka_wrunlock(rk);
|
|
2309
|
+
|
|
2310
|
+
rd_kafka_txn_set_abortable_error0(
|
|
2311
|
+
rk, err, require_bump,
|
|
2312
|
+
"Failed to end transaction: "
|
|
2313
|
+
"%s",
|
|
2314
|
+
rd_kafka_err2str(err));
|
|
2315
|
+
|
|
2316
|
+
} else if (may_retry && actions & RD_KAFKA_ERR_ACTION_RETRY &&
|
|
2317
|
+
rd_kafka_buf_retry(rkb, request))
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
if (err)
|
|
2322
|
+
rd_kafka_txn_curr_api_set_result(
|
|
2323
|
+
rk, actions,
|
|
2324
|
+
rd_kafka_error_new(err, "EndTxn %s failed: %s",
|
|
2325
|
+
is_commit ? "commit" : "abort",
|
|
2326
|
+
rd_kafka_err2str(err)));
|
|
2327
|
+
else
|
|
2328
|
+
rd_kafka_txn_endtxn_complete(rk);
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
|
|
2332
|
+
|
|
2333
|
+
/**
|
|
2334
|
+
* @brief Handler for commit_transaction()
|
|
2335
|
+
*
|
|
2336
|
+
* @locks none
|
|
2337
|
+
* @locality rdkafka main thread
|
|
2338
|
+
*/
|
|
2339
|
+
static rd_kafka_op_res_t
|
|
2340
|
+
rd_kafka_txn_op_commit_transaction(rd_kafka_t *rk,
|
|
2341
|
+
rd_kafka_q_t *rkq,
|
|
2342
|
+
rd_kafka_op_t *rko) {
|
|
2343
|
+
rd_kafka_error_t *error;
|
|
2344
|
+
rd_kafka_resp_err_t err;
|
|
2345
|
+
char errstr[512];
|
|
2346
|
+
rd_kafka_pid_t pid;
|
|
2347
|
+
int64_t dr_fails;
|
|
2348
|
+
|
|
2349
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
2350
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2351
|
+
|
|
2352
|
+
rd_kafka_wrlock(rk);
|
|
2353
|
+
|
|
2354
|
+
if ((error = rd_kafka_txn_require_state(
|
|
2355
|
+
rk, RD_KAFKA_TXN_STATE_BEGIN_COMMIT,
|
|
2356
|
+
RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION,
|
|
2357
|
+
RD_KAFKA_TXN_STATE_COMMIT_NOT_ACKED)))
|
|
2358
|
+
goto done;
|
|
2359
|
+
|
|
2360
|
+
if (rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_COMMIT_NOT_ACKED) {
|
|
2361
|
+
/* A previous call to commit_transaction() timed out but the
|
|
2362
|
+
* commit completed since then, we still
|
|
2363
|
+
* need to wait for the application to call commit_transaction()
|
|
2364
|
+
* again to resume the call, and it just did. */
|
|
2365
|
+
goto done;
|
|
2366
|
+
} else if (rk->rk_eos.txn_state ==
|
|
2367
|
+
RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION) {
|
|
2368
|
+
/* A previous call to commit_transaction() timed out but the
|
|
2369
|
+
* commit is still in progress, we still
|
|
2370
|
+
* need to wait for the application to call commit_transaction()
|
|
2371
|
+
* again to resume the call, and it just did. */
|
|
2372
|
+
rd_kafka_wrunlock(rk);
|
|
2373
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
/* If any messages failed delivery the transaction must be aborted. */
|
|
2377
|
+
dr_fails = rd_atomic64_get(&rk->rk_eos.txn_dr_fails);
|
|
2378
|
+
if (unlikely(dr_fails > 0)) {
|
|
2379
|
+
error = rd_kafka_error_new_txn_requires_abort(
|
|
2380
|
+
RD_KAFKA_RESP_ERR__INCONSISTENT,
|
|
2381
|
+
"%" PRId64
|
|
2382
|
+
" message(s) failed delivery "
|
|
2383
|
+
"(see individual delivery reports)",
|
|
2384
|
+
dr_fails);
|
|
2385
|
+
goto done;
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
if (!rk->rk_eos.txn_req_cnt) {
|
|
2389
|
+
/* If there were no messages produced, or no send_offsets,
|
|
2390
|
+
* in this transaction, simply complete the transaction
|
|
2391
|
+
* without sending anything to the transaction coordinator
|
|
2392
|
+
* (since it will not have any txn state). */
|
|
2393
|
+
rd_kafka_dbg(rk, EOS, "TXNCOMMIT",
|
|
2394
|
+
"No partitions registered: not sending EndTxn");
|
|
2395
|
+
rd_kafka_wrunlock(rk);
|
|
2396
|
+
rd_kafka_txn_endtxn_complete(rk);
|
|
2397
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
pid = rd_kafka_idemp_get_pid0(rk, RD_DONT_LOCK, rd_false);
|
|
2401
|
+
if (!rd_kafka_pid_valid(pid)) {
|
|
2402
|
+
rd_dassert(!*"BUG: No PID despite proper transaction state");
|
|
2403
|
+
error = rd_kafka_error_new_retriable(
|
|
2404
|
+
RD_KAFKA_RESP_ERR__STATE,
|
|
2405
|
+
"No PID available (idempotence state %s)",
|
|
2406
|
+
rd_kafka_idemp_state2str(rk->rk_eos.idemp_state));
|
|
2407
|
+
goto done;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
err = rd_kafka_EndTxnRequest(
|
|
2411
|
+
rk->rk_eos.txn_coord, rk->rk_conf.eos.transactional_id, pid,
|
|
2412
|
+
rd_true /* commit */, errstr, sizeof(errstr),
|
|
2413
|
+
RD_KAFKA_REPLYQ(rk->rk_ops, 0), rd_kafka_txn_handle_EndTxn, NULL);
|
|
2414
|
+
if (err) {
|
|
2415
|
+
error = rd_kafka_error_new_retriable(err, "%s", errstr);
|
|
2416
|
+
goto done;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION);
|
|
2420
|
+
|
|
2421
|
+
rd_kafka_wrunlock(rk);
|
|
2422
|
+
|
|
2423
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2424
|
+
|
|
2425
|
+
done:
|
|
2426
|
+
rd_kafka_wrunlock(rk);
|
|
2427
|
+
|
|
2428
|
+
/* If the returned error is an abortable error
|
|
2429
|
+
* also set the current transaction state accordingly. */
|
|
2430
|
+
if (rd_kafka_error_txn_requires_abort(error))
|
|
2431
|
+
rd_kafka_txn_set_abortable_error(rk, rd_kafka_error_code(error),
|
|
2432
|
+
"%s",
|
|
2433
|
+
rd_kafka_error_string(error));
|
|
2434
|
+
|
|
2435
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
2436
|
+
|
|
2437
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
|
|
2441
|
+
/**
|
|
2442
|
+
* @brief Handler for commit_transaction()'s first phase: begin commit
|
|
2443
|
+
*
|
|
2444
|
+
* @locks none
|
|
2445
|
+
* @locality rdkafka main thread
|
|
2446
|
+
*/
|
|
2447
|
+
static rd_kafka_op_res_t rd_kafka_txn_op_begin_commit(rd_kafka_t *rk,
|
|
2448
|
+
rd_kafka_q_t *rkq,
|
|
2449
|
+
rd_kafka_op_t *rko) {
|
|
2450
|
+
rd_kafka_error_t *error;
|
|
2451
|
+
|
|
2452
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
2453
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2454
|
+
|
|
2455
|
+
|
|
2456
|
+
rd_kafka_wrlock(rk);
|
|
2457
|
+
|
|
2458
|
+
error = rd_kafka_txn_require_state(
|
|
2459
|
+
rk, RD_KAFKA_TXN_STATE_IN_TRANSACTION,
|
|
2460
|
+
RD_KAFKA_TXN_STATE_BEGIN_COMMIT,
|
|
2461
|
+
RD_KAFKA_TXN_STATE_COMMITTING_TRANSACTION,
|
|
2462
|
+
RD_KAFKA_TXN_STATE_COMMIT_NOT_ACKED);
|
|
2463
|
+
|
|
2464
|
+
if (!error &&
|
|
2465
|
+
rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_IN_TRANSACTION) {
|
|
2466
|
+
/* Transition to BEGIN_COMMIT state if no error and commit not
|
|
2467
|
+
* already started. */
|
|
2468
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_BEGIN_COMMIT);
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
rd_kafka_wrunlock(rk);
|
|
2472
|
+
|
|
2473
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
2474
|
+
|
|
2475
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
|
|
2479
|
+
/**
|
|
2480
|
+
* @brief Handler for last ack of commit_transaction()
|
|
2481
|
+
*
|
|
2482
|
+
* @locks none
|
|
2483
|
+
* @locality rdkafka main thread
|
|
2484
|
+
*/
|
|
2485
|
+
static rd_kafka_op_res_t
|
|
2486
|
+
rd_kafka_txn_op_commit_transaction_ack(rd_kafka_t *rk,
|
|
2487
|
+
rd_kafka_q_t *rkq,
|
|
2488
|
+
rd_kafka_op_t *rko) {
|
|
2489
|
+
rd_kafka_error_t *error;
|
|
2490
|
+
|
|
2491
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
2492
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2493
|
+
|
|
2494
|
+
rd_kafka_wrlock(rk);
|
|
2495
|
+
|
|
2496
|
+
if (!(error = rd_kafka_txn_require_state(
|
|
2497
|
+
rk, RD_KAFKA_TXN_STATE_COMMIT_NOT_ACKED))) {
|
|
2498
|
+
rd_kafka_dbg(rk, EOS, "TXNCOMMIT",
|
|
2499
|
+
"Committed transaction now acked by application");
|
|
2500
|
+
rd_kafka_txn_complete(rk, rd_true /*is commit*/);
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
rd_kafka_wrunlock(rk);
|
|
2504
|
+
|
|
2505
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
2506
|
+
|
|
2507
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
|
|
2511
|
+
|
|
2512
|
+
rd_kafka_error_t *rd_kafka_commit_transaction(rd_kafka_t *rk, int timeout_ms) {
|
|
2513
|
+
rd_kafka_error_t *error;
|
|
2514
|
+
rd_kafka_resp_err_t err;
|
|
2515
|
+
rd_ts_t abs_timeout;
|
|
2516
|
+
|
|
2517
|
+
/* The commit is in three phases:
|
|
2518
|
+
* - begin commit: wait for outstanding messages to be produced,
|
|
2519
|
+
* disallow new messages from being produced
|
|
2520
|
+
* by application.
|
|
2521
|
+
* - commit: commit transaction.
|
|
2522
|
+
* - commit not acked: commit done, but waiting for application
|
|
2523
|
+
* to acknowledge by completing this API call.
|
|
2524
|
+
*/
|
|
2525
|
+
|
|
2526
|
+
if ((error = rd_kafka_txn_curr_api_begin(rk, "commit_transaction",
|
|
2527
|
+
rd_false /* no cap */,
|
|
2528
|
+
timeout_ms, &abs_timeout)))
|
|
2529
|
+
return error;
|
|
2530
|
+
|
|
2531
|
+
/* Begin commit */
|
|
2532
|
+
if ((error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_begin_commit,
|
|
2533
|
+
abs_timeout)))
|
|
2534
|
+
return rd_kafka_txn_curr_api_return(rk,
|
|
2535
|
+
/* not resumable yet */
|
|
2536
|
+
rd_false, error);
|
|
2537
|
+
|
|
2538
|
+
rd_kafka_dbg(rk, EOS, "TXNCOMMIT",
|
|
2539
|
+
"Flushing %d outstanding message(s) prior to commit",
|
|
2540
|
+
rd_kafka_outq_len(rk));
|
|
2541
|
+
|
|
2542
|
+
/* Wait for queued messages to be delivered, limited by
|
|
2543
|
+
* the remaining transaction lifetime. */
|
|
2544
|
+
if ((err = rd_kafka_flush(rk, rd_timeout_remains(abs_timeout)))) {
|
|
2545
|
+
rd_kafka_dbg(rk, EOS, "TXNCOMMIT",
|
|
2546
|
+
"Flush failed (with %d messages remaining): %s",
|
|
2547
|
+
rd_kafka_outq_len(rk), rd_kafka_err2str(err));
|
|
2548
|
+
|
|
2549
|
+
if (err == RD_KAFKA_RESP_ERR__TIMED_OUT)
|
|
2550
|
+
error = rd_kafka_error_new_retriable(
|
|
2551
|
+
err,
|
|
2552
|
+
"Failed to flush all outstanding messages "
|
|
2553
|
+
"within the API timeout: "
|
|
2554
|
+
"%d message(s) remaining%s",
|
|
2555
|
+
rd_kafka_outq_len(rk),
|
|
2556
|
+
/* In case event queue delivery reports
|
|
2557
|
+
* are enabled and there is no dr callback
|
|
2558
|
+
* we instruct the developer to poll
|
|
2559
|
+
* the event queue separately, since we
|
|
2560
|
+
* can't do it for them. */
|
|
2561
|
+
((rk->rk_conf.enabled_events & RD_KAFKA_EVENT_DR) &&
|
|
2562
|
+
!rk->rk_conf.dr_msg_cb && !rk->rk_conf.dr_cb)
|
|
2563
|
+
? ": the event queue must be polled "
|
|
2564
|
+
"for delivery report events in a separate "
|
|
2565
|
+
"thread or prior to calling commit"
|
|
2566
|
+
: "");
|
|
2567
|
+
else
|
|
2568
|
+
error = rd_kafka_error_new_retriable(
|
|
2569
|
+
err, "Failed to flush outstanding messages: %s",
|
|
2570
|
+
rd_kafka_err2str(err));
|
|
2571
|
+
|
|
2572
|
+
/* The commit operation is in progress in the background
|
|
2573
|
+
* and the application will need to call this API again
|
|
2574
|
+
* to resume. */
|
|
2575
|
+
return rd_kafka_txn_curr_api_return(rk, rd_true, error);
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
rd_kafka_dbg(rk, EOS, "TXNCOMMIT",
|
|
2579
|
+
"Transaction commit message flush complete");
|
|
2580
|
+
|
|
2581
|
+
/* Commit transaction */
|
|
2582
|
+
error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_commit_transaction,
|
|
2583
|
+
abs_timeout);
|
|
2584
|
+
if (error)
|
|
2585
|
+
return rd_kafka_txn_curr_api_return(rk, rd_true, error);
|
|
2586
|
+
|
|
2587
|
+
/* Last call is to transition from COMMIT_NOT_ACKED to READY */
|
|
2588
|
+
error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_commit_transaction_ack,
|
|
2589
|
+
/* Timeout must be infinite since this is
|
|
2590
|
+
* a synchronization point.
|
|
2591
|
+
* The call is immediate though, so this
|
|
2592
|
+
* will not block. */
|
|
2593
|
+
RD_POLL_INFINITE);
|
|
2594
|
+
|
|
2595
|
+
return rd_kafka_txn_curr_api_return(rk,
|
|
2596
|
+
/* not resumable at this point */
|
|
2597
|
+
rd_false, error);
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
|
|
2601
|
+
|
|
2602
|
+
/**
|
|
2603
|
+
* @brief Handler for abort_transaction()'s first phase: begin abort
|
|
2604
|
+
*
|
|
2605
|
+
* @locks none
|
|
2606
|
+
* @locality rdkafka main thread
|
|
2607
|
+
*/
|
|
2608
|
+
static rd_kafka_op_res_t rd_kafka_txn_op_begin_abort(rd_kafka_t *rk,
|
|
2609
|
+
rd_kafka_q_t *rkq,
|
|
2610
|
+
rd_kafka_op_t *rko) {
|
|
2611
|
+
rd_kafka_error_t *error;
|
|
2612
|
+
rd_bool_t clear_pending = rd_false;
|
|
2613
|
+
|
|
2614
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
2615
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2616
|
+
|
|
2617
|
+
rd_kafka_wrlock(rk);
|
|
2618
|
+
|
|
2619
|
+
error =
|
|
2620
|
+
rd_kafka_txn_require_state(rk, RD_KAFKA_TXN_STATE_IN_TRANSACTION,
|
|
2621
|
+
RD_KAFKA_TXN_STATE_BEGIN_ABORT,
|
|
2622
|
+
RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION,
|
|
2623
|
+
RD_KAFKA_TXN_STATE_ABORTABLE_ERROR,
|
|
2624
|
+
RD_KAFKA_TXN_STATE_ABORT_NOT_ACKED);
|
|
2625
|
+
|
|
2626
|
+
if (!error &&
|
|
2627
|
+
(rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_IN_TRANSACTION ||
|
|
2628
|
+
rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_ABORTABLE_ERROR)) {
|
|
2629
|
+
/* Transition to ABORTING_TRANSACTION state if no error and
|
|
2630
|
+
* abort not already started. */
|
|
2631
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_BEGIN_ABORT);
|
|
2632
|
+
clear_pending = rd_true;
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
rd_kafka_wrunlock(rk);
|
|
2636
|
+
|
|
2637
|
+
if (clear_pending) {
|
|
2638
|
+
mtx_lock(&rk->rk_eos.txn_pending_lock);
|
|
2639
|
+
rd_kafka_txn_clear_pending_partitions(rk);
|
|
2640
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
2644
|
+
|
|
2645
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
|
|
2649
|
+
/**
|
|
2650
|
+
* @brief Handler for abort_transaction()
|
|
2651
|
+
*
|
|
2652
|
+
* @locks none
|
|
2653
|
+
* @locality rdkafka main thread
|
|
2654
|
+
*/
|
|
2655
|
+
static rd_kafka_op_res_t rd_kafka_txn_op_abort_transaction(rd_kafka_t *rk,
|
|
2656
|
+
rd_kafka_q_t *rkq,
|
|
2657
|
+
rd_kafka_op_t *rko) {
|
|
2658
|
+
rd_kafka_error_t *error;
|
|
2659
|
+
rd_kafka_resp_err_t err;
|
|
2660
|
+
char errstr[512];
|
|
2661
|
+
rd_kafka_pid_t pid;
|
|
2662
|
+
|
|
2663
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
2664
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2665
|
+
|
|
2666
|
+
rd_kafka_wrlock(rk);
|
|
2667
|
+
|
|
2668
|
+
if ((error = rd_kafka_txn_require_state(
|
|
2669
|
+
rk, RD_KAFKA_TXN_STATE_BEGIN_ABORT,
|
|
2670
|
+
RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION,
|
|
2671
|
+
RD_KAFKA_TXN_STATE_ABORT_NOT_ACKED)))
|
|
2672
|
+
goto done;
|
|
2673
|
+
|
|
2674
|
+
if (rk->rk_eos.txn_state == RD_KAFKA_TXN_STATE_ABORT_NOT_ACKED) {
|
|
2675
|
+
/* A previous call to abort_transaction() timed out but
|
|
2676
|
+
* the aborting completed since then, we still need to wait
|
|
2677
|
+
* for the application to call abort_transaction() again
|
|
2678
|
+
* to synchronize state, and it just did. */
|
|
2679
|
+
goto done;
|
|
2680
|
+
} else if (rk->rk_eos.txn_state ==
|
|
2681
|
+
RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION) {
|
|
2682
|
+
/* A previous call to abort_transaction() timed out but
|
|
2683
|
+
* the abort is still in progress, we still need to wait
|
|
2684
|
+
* for the application to call abort_transaction() again
|
|
2685
|
+
* to synchronize state, and it just did. */
|
|
2686
|
+
rd_kafka_wrunlock(rk);
|
|
2687
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
if (!rk->rk_eos.txn_req_cnt) {
|
|
2691
|
+
rd_kafka_dbg(rk, EOS, "TXNABORT",
|
|
2692
|
+
"No partitions registered: not sending EndTxn");
|
|
2693
|
+
rd_kafka_wrunlock(rk);
|
|
2694
|
+
rd_kafka_txn_endtxn_complete(rk);
|
|
2695
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
/* If the underlying idempotent producer's state indicates it
|
|
2699
|
+
* is re-acquiring its PID we need to wait for that to finish
|
|
2700
|
+
* before allowing a new begin_transaction(), and since that is
|
|
2701
|
+
* not a blocking call we need to perform that wait in this
|
|
2702
|
+
* state instead.
|
|
2703
|
+
* To recover we need to request an epoch bump from the
|
|
2704
|
+
* transaction coordinator. This is handled automatically
|
|
2705
|
+
* by the idempotent producer, so we just need to wait for
|
|
2706
|
+
* the new pid to be assigned.
|
|
2707
|
+
*/
|
|
2708
|
+
if (rk->rk_eos.idemp_state != RD_KAFKA_IDEMP_STATE_ASSIGNED &&
|
|
2709
|
+
rk->rk_eos.idemp_state != RD_KAFKA_IDEMP_STATE_WAIT_TXN_ABORT) {
|
|
2710
|
+
rd_kafka_dbg(rk, EOS, "TXNABORT",
|
|
2711
|
+
"Waiting for transaction coordinator "
|
|
2712
|
+
"PID bump to complete before aborting "
|
|
2713
|
+
"transaction (idempotent producer state %s)",
|
|
2714
|
+
rd_kafka_idemp_state2str(rk->rk_eos.idemp_state));
|
|
2715
|
+
|
|
2716
|
+
rd_kafka_wrunlock(rk);
|
|
2717
|
+
|
|
2718
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
pid = rd_kafka_idemp_get_pid0(rk, RD_DONT_LOCK, rd_true);
|
|
2722
|
+
if (!rd_kafka_pid_valid(pid)) {
|
|
2723
|
+
rd_dassert(!*"BUG: No PID despite proper transaction state");
|
|
2724
|
+
error = rd_kafka_error_new_retriable(
|
|
2725
|
+
RD_KAFKA_RESP_ERR__STATE,
|
|
2726
|
+
"No PID available (idempotence state %s)",
|
|
2727
|
+
rd_kafka_idemp_state2str(rk->rk_eos.idemp_state));
|
|
2728
|
+
goto done;
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
err = rd_kafka_EndTxnRequest(
|
|
2732
|
+
rk->rk_eos.txn_coord, rk->rk_conf.eos.transactional_id, pid,
|
|
2733
|
+
rd_false /* abort */, errstr, sizeof(errstr),
|
|
2734
|
+
RD_KAFKA_REPLYQ(rk->rk_ops, 0), rd_kafka_txn_handle_EndTxn, NULL);
|
|
2735
|
+
if (err) {
|
|
2736
|
+
error = rd_kafka_error_new_retriable(err, "%s", errstr);
|
|
2737
|
+
goto done;
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
rd_kafka_txn_set_state(rk, RD_KAFKA_TXN_STATE_ABORTING_TRANSACTION);
|
|
2741
|
+
|
|
2742
|
+
rd_kafka_wrunlock(rk);
|
|
2743
|
+
|
|
2744
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2745
|
+
|
|
2746
|
+
done:
|
|
2747
|
+
rd_kafka_wrunlock(rk);
|
|
2748
|
+
|
|
2749
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
2750
|
+
|
|
2751
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
|
|
2755
|
+
/**
|
|
2756
|
+
* @brief Handler for last ack of abort_transaction()
|
|
2757
|
+
*
|
|
2758
|
+
* @locks none
|
|
2759
|
+
* @locality rdkafka main thread
|
|
2760
|
+
*/
|
|
2761
|
+
static rd_kafka_op_res_t
|
|
2762
|
+
rd_kafka_txn_op_abort_transaction_ack(rd_kafka_t *rk,
|
|
2763
|
+
rd_kafka_q_t *rkq,
|
|
2764
|
+
rd_kafka_op_t *rko) {
|
|
2765
|
+
rd_kafka_error_t *error;
|
|
2766
|
+
|
|
2767
|
+
if (rko->rko_err == RD_KAFKA_RESP_ERR__DESTROY)
|
|
2768
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2769
|
+
|
|
2770
|
+
rd_kafka_wrlock(rk);
|
|
2771
|
+
|
|
2772
|
+
if (!(error = rd_kafka_txn_require_state(
|
|
2773
|
+
rk, RD_KAFKA_TXN_STATE_ABORT_NOT_ACKED))) {
|
|
2774
|
+
rd_kafka_dbg(rk, EOS, "TXNABORT",
|
|
2775
|
+
"Aborted transaction now acked by application");
|
|
2776
|
+
rd_kafka_txn_complete(rk, rd_false /*is abort*/);
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
rd_kafka_wrunlock(rk);
|
|
2780
|
+
|
|
2781
|
+
rd_kafka_txn_curr_api_set_result(rk, 0, error);
|
|
2782
|
+
|
|
2783
|
+
return RD_KAFKA_OP_RES_HANDLED;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
|
|
2787
|
+
|
|
2788
|
+
rd_kafka_error_t *rd_kafka_abort_transaction(rd_kafka_t *rk, int timeout_ms) {
|
|
2789
|
+
rd_kafka_error_t *error;
|
|
2790
|
+
rd_kafka_resp_err_t err;
|
|
2791
|
+
rd_ts_t abs_timeout;
|
|
2792
|
+
|
|
2793
|
+
if ((error = rd_kafka_txn_curr_api_begin(rk, "abort_transaction",
|
|
2794
|
+
rd_false /* no cap */,
|
|
2795
|
+
timeout_ms, &abs_timeout)))
|
|
2796
|
+
return error;
|
|
2797
|
+
|
|
2798
|
+
/* The abort is multi-phase:
|
|
2799
|
+
* - set state to BEGIN_ABORT
|
|
2800
|
+
* - flush() outstanding messages
|
|
2801
|
+
* - send EndTxn
|
|
2802
|
+
*/
|
|
2803
|
+
|
|
2804
|
+
/* Begin abort */
|
|
2805
|
+
if ((error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_begin_abort,
|
|
2806
|
+
abs_timeout)))
|
|
2807
|
+
return rd_kafka_txn_curr_api_return(rk,
|
|
2808
|
+
/* not resumable yet */
|
|
2809
|
+
rd_false, error);
|
|
2810
|
+
|
|
2811
|
+
rd_kafka_dbg(rk, EOS, "TXNABORT",
|
|
2812
|
+
"Purging and flushing %d outstanding message(s) prior "
|
|
2813
|
+
"to abort",
|
|
2814
|
+
rd_kafka_outq_len(rk));
|
|
2815
|
+
|
|
2816
|
+
/* Purge all queued messages.
|
|
2817
|
+
* Will need to wait for messages in-flight since purging these
|
|
2818
|
+
* messages may lead to gaps in the idempotent producer sequences. */
|
|
2819
|
+
err = rd_kafka_purge(rk, RD_KAFKA_PURGE_F_QUEUE |
|
|
2820
|
+
RD_KAFKA_PURGE_F_ABORT_TXN);
|
|
2821
|
+
|
|
2822
|
+
/* Serve delivery reports for the purged messages. */
|
|
2823
|
+
if ((err = rd_kafka_flush(rk, rd_timeout_remains(abs_timeout)))) {
|
|
2824
|
+
/* FIXME: Not sure these errors matter that much */
|
|
2825
|
+
if (err == RD_KAFKA_RESP_ERR__TIMED_OUT)
|
|
2826
|
+
error = rd_kafka_error_new_retriable(
|
|
2827
|
+
err,
|
|
2828
|
+
"Failed to flush all outstanding messages "
|
|
2829
|
+
"within the API timeout: "
|
|
2830
|
+
"%d message(s) remaining%s",
|
|
2831
|
+
rd_kafka_outq_len(rk),
|
|
2832
|
+
(rk->rk_conf.enabled_events & RD_KAFKA_EVENT_DR)
|
|
2833
|
+
? ": the event queue must be polled "
|
|
2834
|
+
"for delivery report events in a separate "
|
|
2835
|
+
"thread or prior to calling abort"
|
|
2836
|
+
: "");
|
|
2837
|
+
|
|
2838
|
+
else
|
|
2839
|
+
error = rd_kafka_error_new_retriable(
|
|
2840
|
+
err, "Failed to flush outstanding messages: %s",
|
|
2841
|
+
rd_kafka_err2str(err));
|
|
2842
|
+
|
|
2843
|
+
/* The abort operation is in progress in the background
|
|
2844
|
+
* and the application will need to call this API again
|
|
2845
|
+
* to resume. */
|
|
2846
|
+
return rd_kafka_txn_curr_api_return(rk, rd_true, error);
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
rd_kafka_dbg(rk, EOS, "TXNCOMMIT",
|
|
2850
|
+
"Transaction abort message purge and flush complete");
|
|
2851
|
+
|
|
2852
|
+
error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_abort_transaction,
|
|
2853
|
+
abs_timeout);
|
|
2854
|
+
if (error)
|
|
2855
|
+
return rd_kafka_txn_curr_api_return(rk, rd_true, error);
|
|
2856
|
+
|
|
2857
|
+
/* Last call is to transition from ABORT_NOT_ACKED to READY. */
|
|
2858
|
+
error = rd_kafka_txn_op_req(rk, rd_kafka_txn_op_abort_transaction_ack,
|
|
2859
|
+
/* Timeout must be infinite since this is
|
|
2860
|
+
* a synchronization point.
|
|
2861
|
+
* The call is immediate though, so this
|
|
2862
|
+
* will not block. */
|
|
2863
|
+
RD_POLL_INFINITE);
|
|
2864
|
+
|
|
2865
|
+
return rd_kafka_txn_curr_api_return(rk,
|
|
2866
|
+
/* not resumable at this point */
|
|
2867
|
+
rd_false, error);
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
|
|
2871
|
+
|
|
2872
|
+
/**
|
|
2873
|
+
* @brief Coordinator query timer
|
|
2874
|
+
*
|
|
2875
|
+
* @locality rdkafka main thread
|
|
2876
|
+
* @locks none
|
|
2877
|
+
*/
|
|
2878
|
+
|
|
2879
|
+
static void rd_kafka_txn_coord_timer_cb(rd_kafka_timers_t *rkts, void *arg) {
|
|
2880
|
+
rd_kafka_t *rk = arg;
|
|
2881
|
+
|
|
2882
|
+
rd_kafka_wrlock(rk);
|
|
2883
|
+
rd_kafka_txn_coord_query(rk, "Coordinator query timer");
|
|
2884
|
+
rd_kafka_wrunlock(rk);
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
/**
|
|
2888
|
+
* @brief Start coord query timer if not already started.
|
|
2889
|
+
*
|
|
2890
|
+
* @locality rdkafka main thread
|
|
2891
|
+
* @locks none
|
|
2892
|
+
*/
|
|
2893
|
+
static void rd_kafka_txn_coord_timer_start(rd_kafka_t *rk, int timeout_ms) {
|
|
2894
|
+
rd_assert(rd_kafka_is_transactional(rk));
|
|
2895
|
+
rd_kafka_timer_start_oneshot(&rk->rk_timers, &rk->rk_eos.txn_coord_tmr,
|
|
2896
|
+
/* don't restart if already started */
|
|
2897
|
+
rd_false, 1000 * timeout_ms,
|
|
2898
|
+
rd_kafka_txn_coord_timer_cb, rk);
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
|
|
2902
|
+
/**
|
|
2903
|
+
* @brief Parses and handles a FindCoordinator response.
|
|
2904
|
+
*
|
|
2905
|
+
* @locality rdkafka main thread
|
|
2906
|
+
* @locks none
|
|
2907
|
+
*/
|
|
2908
|
+
static void rd_kafka_txn_handle_FindCoordinator(rd_kafka_t *rk,
|
|
2909
|
+
rd_kafka_broker_t *rkb,
|
|
2910
|
+
rd_kafka_resp_err_t err,
|
|
2911
|
+
rd_kafka_buf_t *rkbuf,
|
|
2912
|
+
rd_kafka_buf_t *request,
|
|
2913
|
+
void *opaque) {
|
|
2914
|
+
const int log_decode_errors = LOG_ERR;
|
|
2915
|
+
int16_t ErrorCode;
|
|
2916
|
+
rd_kafkap_str_t Host;
|
|
2917
|
+
int32_t NodeId, Port;
|
|
2918
|
+
char errstr[512];
|
|
2919
|
+
|
|
2920
|
+
*errstr = '\0';
|
|
2921
|
+
|
|
2922
|
+
rk->rk_eos.txn_wait_coord = rd_false;
|
|
2923
|
+
|
|
2924
|
+
if (err)
|
|
2925
|
+
goto err;
|
|
2926
|
+
|
|
2927
|
+
if (request->rkbuf_reqhdr.ApiVersion >= 1)
|
|
2928
|
+
rd_kafka_buf_read_throttle_time(rkbuf);
|
|
2929
|
+
|
|
2930
|
+
rd_kafka_buf_read_i16(rkbuf, &ErrorCode);
|
|
2931
|
+
|
|
2932
|
+
if (request->rkbuf_reqhdr.ApiVersion >= 1) {
|
|
2933
|
+
rd_kafkap_str_t ErrorMsg;
|
|
2934
|
+
rd_kafka_buf_read_str(rkbuf, &ErrorMsg);
|
|
2935
|
+
if (ErrorCode)
|
|
2936
|
+
rd_snprintf(errstr, sizeof(errstr), "%.*s",
|
|
2937
|
+
RD_KAFKAP_STR_PR(&ErrorMsg));
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
if ((err = ErrorCode))
|
|
2941
|
+
goto err;
|
|
2942
|
+
|
|
2943
|
+
rd_kafka_buf_read_i32(rkbuf, &NodeId);
|
|
2944
|
+
rd_kafka_buf_read_str(rkbuf, &Host);
|
|
2945
|
+
rd_kafka_buf_read_i32(rkbuf, &Port);
|
|
2946
|
+
|
|
2947
|
+
rd_rkb_dbg(rkb, EOS, "TXNCOORD",
|
|
2948
|
+
"FindCoordinator response: "
|
|
2949
|
+
"Transaction coordinator is broker %" PRId32 " (%.*s:%d)",
|
|
2950
|
+
NodeId, RD_KAFKAP_STR_PR(&Host), (int)Port);
|
|
2951
|
+
|
|
2952
|
+
rd_kafka_rdlock(rk);
|
|
2953
|
+
if (NodeId == -1)
|
|
2954
|
+
err = RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE;
|
|
2955
|
+
else if (!(rkb = rd_kafka_broker_find_by_nodeid(rk, NodeId))) {
|
|
2956
|
+
rd_snprintf(errstr, sizeof(errstr),
|
|
2957
|
+
"Transaction coordinator %" PRId32 " is unknown",
|
|
2958
|
+
NodeId);
|
|
2959
|
+
err = RD_KAFKA_RESP_ERR__UNKNOWN_BROKER;
|
|
2960
|
+
}
|
|
2961
|
+
if (rkb && rkb->rkb_source != RD_KAFKA_LEARNED) {
|
|
2962
|
+
rd_kafka_broker_destroy(rkb);
|
|
2963
|
+
rkb = NULL;
|
|
2964
|
+
err = RD_KAFKA_RESP_ERR__UNKNOWN_BROKER;
|
|
2965
|
+
}
|
|
2966
|
+
rd_kafka_rdunlock(rk);
|
|
2967
|
+
|
|
2968
|
+
if (err)
|
|
2969
|
+
goto err;
|
|
2970
|
+
|
|
2971
|
+
rd_kafka_wrlock(rk);
|
|
2972
|
+
rd_kafka_txn_coord_set(rk, rkb, "FindCoordinator response");
|
|
2973
|
+
rd_kafka_wrunlock(rk);
|
|
2974
|
+
|
|
2975
|
+
rd_kafka_broker_destroy(rkb);
|
|
2976
|
+
|
|
2977
|
+
return;
|
|
2978
|
+
|
|
2979
|
+
err_parse:
|
|
2980
|
+
err = rkbuf->rkbuf_err;
|
|
2981
|
+
err:
|
|
2982
|
+
|
|
2983
|
+
switch (err) {
|
|
2984
|
+
case RD_KAFKA_RESP_ERR__DESTROY:
|
|
2985
|
+
return;
|
|
2986
|
+
|
|
2987
|
+
case RD_KAFKA_RESP_ERR_TRANSACTIONAL_ID_AUTHORIZATION_FAILED:
|
|
2988
|
+
case RD_KAFKA_RESP_ERR_CLUSTER_AUTHORIZATION_FAILED:
|
|
2989
|
+
rd_kafka_wrlock(rk);
|
|
2990
|
+
rd_kafka_txn_set_fatal_error(
|
|
2991
|
+
rkb->rkb_rk, RD_DONT_LOCK, err,
|
|
2992
|
+
"Failed to find transaction coordinator: %s: %s%s%s",
|
|
2993
|
+
rd_kafka_broker_name(rkb), rd_kafka_err2str(err),
|
|
2994
|
+
*errstr ? ": " : "", errstr);
|
|
2995
|
+
|
|
2996
|
+
rd_kafka_idemp_set_state(rk, RD_KAFKA_IDEMP_STATE_FATAL_ERROR);
|
|
2997
|
+
rd_kafka_wrunlock(rk);
|
|
2998
|
+
return;
|
|
2999
|
+
|
|
3000
|
+
case RD_KAFKA_RESP_ERR__UNKNOWN_BROKER:
|
|
3001
|
+
rd_kafka_metadata_refresh_brokers(rk, NULL, errstr);
|
|
3002
|
+
break;
|
|
3003
|
+
|
|
3004
|
+
default:
|
|
3005
|
+
break;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
rd_kafka_wrlock(rk);
|
|
3009
|
+
rd_kafka_txn_coord_set(
|
|
3010
|
+
rk, NULL, "Failed to find transaction coordinator: %s: %s",
|
|
3011
|
+
rd_kafka_err2name(err), *errstr ? errstr : rd_kafka_err2str(err));
|
|
3012
|
+
rd_kafka_wrunlock(rk);
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
|
|
3016
|
+
|
|
3017
|
+
/**
|
|
3018
|
+
* @brief Query for the transaction coordinator.
|
|
3019
|
+
*
|
|
3020
|
+
* @returns true if a fatal error was raised, else false.
|
|
3021
|
+
*
|
|
3022
|
+
* @locality rdkafka main thread
|
|
3023
|
+
* @locks rd_kafka_wrlock(rk) MUST be held.
|
|
3024
|
+
*/
|
|
3025
|
+
rd_bool_t rd_kafka_txn_coord_query(rd_kafka_t *rk, const char *reason) {
|
|
3026
|
+
rd_kafka_resp_err_t err;
|
|
3027
|
+
char errstr[512];
|
|
3028
|
+
rd_kafka_broker_t *rkb;
|
|
3029
|
+
|
|
3030
|
+
rd_assert(rd_kafka_is_transactional(rk));
|
|
3031
|
+
|
|
3032
|
+
if (rk->rk_eos.txn_wait_coord) {
|
|
3033
|
+
rd_kafka_dbg(rk, EOS, "TXNCOORD",
|
|
3034
|
+
"Not sending coordinator query (%s): "
|
|
3035
|
+
"waiting for previous query to finish",
|
|
3036
|
+
reason);
|
|
3037
|
+
return rd_false;
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
/* Find usable broker to query for the txn coordinator */
|
|
3041
|
+
rkb = rd_kafka_idemp_broker_any(rk, &err, errstr, sizeof(errstr));
|
|
3042
|
+
if (!rkb) {
|
|
3043
|
+
rd_kafka_dbg(rk, EOS, "TXNCOORD",
|
|
3044
|
+
"Unable to query for transaction coordinator: "
|
|
3045
|
+
"%s: %s",
|
|
3046
|
+
reason, errstr);
|
|
3047
|
+
|
|
3048
|
+
if (rd_kafka_idemp_check_error(rk, err, errstr, rd_false))
|
|
3049
|
+
return rd_true;
|
|
3050
|
+
|
|
3051
|
+
rd_kafka_txn_coord_timer_start(rk, 500);
|
|
3052
|
+
|
|
3053
|
+
return rd_false;
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
rd_kafka_dbg(rk, EOS, "TXNCOORD",
|
|
3057
|
+
"Querying for transaction coordinator: %s", reason);
|
|
3058
|
+
|
|
3059
|
+
/* Send FindCoordinator request */
|
|
3060
|
+
err = rd_kafka_FindCoordinatorRequest(
|
|
3061
|
+
rkb, RD_KAFKA_COORD_TXN, rk->rk_conf.eos.transactional_id,
|
|
3062
|
+
RD_KAFKA_REPLYQ(rk->rk_ops, 0), rd_kafka_txn_handle_FindCoordinator,
|
|
3063
|
+
NULL);
|
|
3064
|
+
|
|
3065
|
+
if (err) {
|
|
3066
|
+
rd_snprintf(errstr, sizeof(errstr),
|
|
3067
|
+
"Failed to send coordinator query to %s: "
|
|
3068
|
+
"%s",
|
|
3069
|
+
rd_kafka_broker_name(rkb), rd_kafka_err2str(err));
|
|
3070
|
+
|
|
3071
|
+
rd_kafka_broker_destroy(rkb);
|
|
3072
|
+
|
|
3073
|
+
if (rd_kafka_idemp_check_error(rk, err, errstr, rd_false))
|
|
3074
|
+
return rd_true; /* Fatal error */
|
|
3075
|
+
|
|
3076
|
+
rd_kafka_txn_coord_timer_start(rk, 500);
|
|
3077
|
+
|
|
3078
|
+
return rd_false;
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
rd_kafka_broker_destroy(rkb);
|
|
3082
|
+
|
|
3083
|
+
rk->rk_eos.txn_wait_coord = rd_true;
|
|
3084
|
+
|
|
3085
|
+
return rd_false;
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
/**
|
|
3089
|
+
* @brief Sets or clears the current coordinator address.
|
|
3090
|
+
*
|
|
3091
|
+
* @returns true if the coordinator was changed, else false.
|
|
3092
|
+
*
|
|
3093
|
+
* @locality rdkafka main thread
|
|
3094
|
+
* @locks rd_kafka_wrlock(rk) MUST be held
|
|
3095
|
+
*/
|
|
3096
|
+
rd_bool_t rd_kafka_txn_coord_set(rd_kafka_t *rk,
|
|
3097
|
+
rd_kafka_broker_t *rkb,
|
|
3098
|
+
const char *fmt,
|
|
3099
|
+
...) {
|
|
3100
|
+
char buf[256];
|
|
3101
|
+
va_list ap;
|
|
3102
|
+
|
|
3103
|
+
va_start(ap, fmt);
|
|
3104
|
+
vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
3105
|
+
va_end(ap);
|
|
3106
|
+
|
|
3107
|
+
|
|
3108
|
+
if (rk->rk_eos.txn_curr_coord == rkb) {
|
|
3109
|
+
if (!rkb) {
|
|
3110
|
+
rd_kafka_dbg(rk, EOS, "TXNCOORD", "%s", buf);
|
|
3111
|
+
/* Keep querying for the coordinator */
|
|
3112
|
+
rd_kafka_txn_coord_timer_start(rk, 500);
|
|
3113
|
+
}
|
|
3114
|
+
return rd_false;
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
rd_kafka_dbg(rk, EOS, "TXNCOORD",
|
|
3118
|
+
"Transaction coordinator changed from %s -> %s: %s",
|
|
3119
|
+
rk->rk_eos.txn_curr_coord
|
|
3120
|
+
? rd_kafka_broker_name(rk->rk_eos.txn_curr_coord)
|
|
3121
|
+
: "(none)",
|
|
3122
|
+
rkb ? rd_kafka_broker_name(rkb) : "(none)", buf);
|
|
3123
|
+
|
|
3124
|
+
if (rk->rk_eos.txn_curr_coord)
|
|
3125
|
+
rd_kafka_broker_destroy(rk->rk_eos.txn_curr_coord);
|
|
3126
|
+
|
|
3127
|
+
rk->rk_eos.txn_curr_coord = rkb;
|
|
3128
|
+
if (rkb)
|
|
3129
|
+
rd_kafka_broker_keep(rkb);
|
|
3130
|
+
|
|
3131
|
+
rd_kafka_broker_set_nodename(rk->rk_eos.txn_coord,
|
|
3132
|
+
rk->rk_eos.txn_curr_coord);
|
|
3133
|
+
|
|
3134
|
+
if (!rkb) {
|
|
3135
|
+
/* Lost the current coordinator, query for new coordinator */
|
|
3136
|
+
rd_kafka_txn_coord_timer_start(rk, 500);
|
|
3137
|
+
} else {
|
|
3138
|
+
/* Trigger PID state machine */
|
|
3139
|
+
rd_kafka_idemp_pid_fsm(rk);
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
return rd_true;
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
|
|
3146
|
+
/**
|
|
3147
|
+
* @brief Coordinator state monitor callback.
|
|
3148
|
+
*
|
|
3149
|
+
* @locality rdkafka main thread
|
|
3150
|
+
* @locks none
|
|
3151
|
+
*/
|
|
3152
|
+
void rd_kafka_txn_coord_monitor_cb(rd_kafka_broker_t *rkb) {
|
|
3153
|
+
rd_kafka_t *rk = rkb->rkb_rk;
|
|
3154
|
+
rd_kafka_broker_state_t state = rd_kafka_broker_get_state(rkb);
|
|
3155
|
+
rd_bool_t is_up;
|
|
3156
|
+
|
|
3157
|
+
rd_assert(rk->rk_eos.txn_coord == rkb);
|
|
3158
|
+
|
|
3159
|
+
is_up = rd_kafka_broker_state_is_up(state);
|
|
3160
|
+
rd_rkb_dbg(rkb, EOS, "COORD", "Transaction coordinator is now %s",
|
|
3161
|
+
is_up ? "up" : "down");
|
|
3162
|
+
|
|
3163
|
+
if (!is_up) {
|
|
3164
|
+
/* Coordinator is down, the connection will be re-established
|
|
3165
|
+
* automatically, but we also trigger a coordinator query
|
|
3166
|
+
* to pick up on coordinator change. */
|
|
3167
|
+
rd_kafka_txn_coord_timer_start(rk, 500);
|
|
3168
|
+
|
|
3169
|
+
} else {
|
|
3170
|
+
/* Coordinator is up. */
|
|
3171
|
+
|
|
3172
|
+
rd_kafka_wrlock(rk);
|
|
3173
|
+
if (rk->rk_eos.idemp_state < RD_KAFKA_IDEMP_STATE_ASSIGNED) {
|
|
3174
|
+
/* See if a idempotence state change is warranted. */
|
|
3175
|
+
rd_kafka_idemp_pid_fsm(rk);
|
|
3176
|
+
|
|
3177
|
+
} else if (rk->rk_eos.idemp_state ==
|
|
3178
|
+
RD_KAFKA_IDEMP_STATE_ASSIGNED) {
|
|
3179
|
+
/* PID is already valid, continue transactional
|
|
3180
|
+
* operations by checking for partitions to register */
|
|
3181
|
+
rd_kafka_txn_schedule_register_partitions(rk,
|
|
3182
|
+
1 /*ASAP*/);
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
rd_kafka_wrunlock(rk);
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
|
|
3190
|
+
|
|
3191
|
+
/**
|
|
3192
|
+
* @brief Transactions manager destructor
|
|
3193
|
+
*
|
|
3194
|
+
* @locality rdkafka main thread
|
|
3195
|
+
* @locks none
|
|
3196
|
+
*/
|
|
3197
|
+
void rd_kafka_txns_term(rd_kafka_t *rk) {
|
|
3198
|
+
|
|
3199
|
+
RD_IF_FREE(rk->rk_eos.txn_errstr, rd_free);
|
|
3200
|
+
RD_IF_FREE(rk->rk_eos.txn_curr_api.error, rd_kafka_error_destroy);
|
|
3201
|
+
|
|
3202
|
+
mtx_destroy(&rk->rk_eos.txn_curr_api.lock);
|
|
3203
|
+
cnd_destroy(&rk->rk_eos.txn_curr_api.cnd);
|
|
3204
|
+
|
|
3205
|
+
rd_kafka_timer_stop(&rk->rk_timers, &rk->rk_eos.txn_coord_tmr, 1);
|
|
3206
|
+
rd_kafka_timer_stop(&rk->rk_timers, &rk->rk_eos.txn_register_parts_tmr,
|
|
3207
|
+
1);
|
|
3208
|
+
|
|
3209
|
+
if (rk->rk_eos.txn_curr_coord)
|
|
3210
|
+
rd_kafka_broker_destroy(rk->rk_eos.txn_curr_coord);
|
|
3211
|
+
|
|
3212
|
+
/* Logical coordinator */
|
|
3213
|
+
rd_kafka_broker_persistent_connection_del(
|
|
3214
|
+
rk->rk_eos.txn_coord, &rk->rk_eos.txn_coord->rkb_persistconn.coord);
|
|
3215
|
+
rd_kafka_broker_monitor_del(&rk->rk_eos.txn_coord_mon);
|
|
3216
|
+
rd_kafka_broker_destroy(rk->rk_eos.txn_coord);
|
|
3217
|
+
rk->rk_eos.txn_coord = NULL;
|
|
3218
|
+
|
|
3219
|
+
mtx_lock(&rk->rk_eos.txn_pending_lock);
|
|
3220
|
+
rd_kafka_txn_clear_pending_partitions(rk);
|
|
3221
|
+
mtx_unlock(&rk->rk_eos.txn_pending_lock);
|
|
3222
|
+
mtx_destroy(&rk->rk_eos.txn_pending_lock);
|
|
3223
|
+
|
|
3224
|
+
rd_kafka_txn_clear_partitions(rk);
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
|
|
3228
|
+
/**
|
|
3229
|
+
* @brief Initialize transactions manager.
|
|
3230
|
+
*
|
|
3231
|
+
* @locality application thread
|
|
3232
|
+
* @locks none
|
|
3233
|
+
*/
|
|
3234
|
+
void rd_kafka_txns_init(rd_kafka_t *rk) {
|
|
3235
|
+
rd_atomic32_init(&rk->rk_eos.txn_may_enq, 0);
|
|
3236
|
+
mtx_init(&rk->rk_eos.txn_pending_lock, mtx_plain);
|
|
3237
|
+
TAILQ_INIT(&rk->rk_eos.txn_pending_rktps);
|
|
3238
|
+
TAILQ_INIT(&rk->rk_eos.txn_waitresp_rktps);
|
|
3239
|
+
TAILQ_INIT(&rk->rk_eos.txn_rktps);
|
|
3240
|
+
|
|
3241
|
+
mtx_init(&rk->rk_eos.txn_curr_api.lock, mtx_plain);
|
|
3242
|
+
cnd_init(&rk->rk_eos.txn_curr_api.cnd);
|
|
3243
|
+
|
|
3244
|
+
/* Logical coordinator */
|
|
3245
|
+
rk->rk_eos.txn_coord =
|
|
3246
|
+
rd_kafka_broker_add_logical(rk, "TxnCoordinator");
|
|
3247
|
+
|
|
3248
|
+
rd_kafka_broker_monitor_add(&rk->rk_eos.txn_coord_mon,
|
|
3249
|
+
rk->rk_eos.txn_coord, rk->rk_ops,
|
|
3250
|
+
rd_kafka_txn_coord_monitor_cb);
|
|
3251
|
+
|
|
3252
|
+
rd_kafka_broker_persistent_connection_add(
|
|
3253
|
+
rk->rk_eos.txn_coord, &rk->rk_eos.txn_coord->rkb_persistconn.coord);
|
|
3254
|
+
|
|
3255
|
+
rd_atomic64_init(&rk->rk_eos.txn_dr_fails, 0);
|
|
3256
|
+
}
|