@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.
Files changed (707) hide show
  1. package/LICENSE.txt +20 -0
  2. package/README.md +636 -0
  3. package/binding.gyp +154 -0
  4. package/deps/librdkafka/.clang-format +136 -0
  5. package/deps/librdkafka/.clang-format-cpp +103 -0
  6. package/deps/librdkafka/.dir-locals.el +10 -0
  7. package/deps/librdkafka/.formatignore +33 -0
  8. package/deps/librdkafka/.gdbmacros +19 -0
  9. package/deps/librdkafka/.github/CODEOWNERS +1 -0
  10. package/deps/librdkafka/.github/ISSUE_TEMPLATE +34 -0
  11. package/deps/librdkafka/.semaphore/run-all-tests.yml +77 -0
  12. package/deps/librdkafka/.semaphore/semaphore-integration.yml +250 -0
  13. package/deps/librdkafka/.semaphore/semaphore.yml +378 -0
  14. package/deps/librdkafka/.semaphore/verify-linux-packages.yml +41 -0
  15. package/deps/librdkafka/CHANGELOG.md +2208 -0
  16. package/deps/librdkafka/CMakeLists.txt +291 -0
  17. package/deps/librdkafka/CODE_OF_CONDUCT.md +46 -0
  18. package/deps/librdkafka/CONFIGURATION.md +209 -0
  19. package/deps/librdkafka/CONTRIBUTING.md +431 -0
  20. package/deps/librdkafka/Doxyfile +2375 -0
  21. package/deps/librdkafka/INTRODUCTION.md +2481 -0
  22. package/deps/librdkafka/LICENSE +26 -0
  23. package/deps/librdkafka/LICENSE.cjson +22 -0
  24. package/deps/librdkafka/LICENSE.crc32c +28 -0
  25. package/deps/librdkafka/LICENSE.fnv1a +18 -0
  26. package/deps/librdkafka/LICENSE.hdrhistogram +27 -0
  27. package/deps/librdkafka/LICENSE.lz4 +26 -0
  28. package/deps/librdkafka/LICENSE.murmur2 +25 -0
  29. package/deps/librdkafka/LICENSE.nanopb +22 -0
  30. package/deps/librdkafka/LICENSE.opentelemetry +203 -0
  31. package/deps/librdkafka/LICENSE.pycrc +23 -0
  32. package/deps/librdkafka/LICENSE.queue +31 -0
  33. package/deps/librdkafka/LICENSE.regexp +5 -0
  34. package/deps/librdkafka/LICENSE.snappy +36 -0
  35. package/deps/librdkafka/LICENSE.tinycthread +26 -0
  36. package/deps/librdkafka/LICENSE.wingetopt +49 -0
  37. package/deps/librdkafka/LICENSES.txt +625 -0
  38. package/deps/librdkafka/Makefile +125 -0
  39. package/deps/librdkafka/README.md +199 -0
  40. package/deps/librdkafka/README.win32 +26 -0
  41. package/deps/librdkafka/STATISTICS.md +624 -0
  42. package/deps/librdkafka/configure +214 -0
  43. package/deps/librdkafka/configure.self +331 -0
  44. package/deps/librdkafka/debian/changelog +111 -0
  45. package/deps/librdkafka/debian/compat +1 -0
  46. package/deps/librdkafka/debian/control +71 -0
  47. package/deps/librdkafka/debian/copyright +99 -0
  48. package/deps/librdkafka/debian/gbp.conf +9 -0
  49. package/deps/librdkafka/debian/librdkafka++1.install +1 -0
  50. package/deps/librdkafka/debian/librdkafka-dev.examples +2 -0
  51. package/deps/librdkafka/debian/librdkafka-dev.install +9 -0
  52. package/deps/librdkafka/debian/librdkafka1.docs +5 -0
  53. package/deps/librdkafka/debian/librdkafka1.install +1 -0
  54. package/deps/librdkafka/debian/librdkafka1.symbols +135 -0
  55. package/deps/librdkafka/debian/rules +19 -0
  56. package/deps/librdkafka/debian/source/format +1 -0
  57. package/deps/librdkafka/debian/watch +2 -0
  58. package/deps/librdkafka/dev-conf.sh +123 -0
  59. package/deps/librdkafka/examples/CMakeLists.txt +79 -0
  60. package/deps/librdkafka/examples/Makefile +167 -0
  61. package/deps/librdkafka/examples/README.md +42 -0
  62. package/deps/librdkafka/examples/alter_consumer_group_offsets.c +338 -0
  63. package/deps/librdkafka/examples/consumer.c +271 -0
  64. package/deps/librdkafka/examples/delete_records.c +233 -0
  65. package/deps/librdkafka/examples/describe_cluster.c +322 -0
  66. package/deps/librdkafka/examples/describe_consumer_groups.c +455 -0
  67. package/deps/librdkafka/examples/describe_topics.c +427 -0
  68. package/deps/librdkafka/examples/elect_leaders.c +317 -0
  69. package/deps/librdkafka/examples/globals.json +11 -0
  70. package/deps/librdkafka/examples/idempotent_producer.c +344 -0
  71. package/deps/librdkafka/examples/incremental_alter_configs.c +347 -0
  72. package/deps/librdkafka/examples/kafkatest_verifiable_client.cpp +945 -0
  73. package/deps/librdkafka/examples/list_consumer_group_offsets.c +359 -0
  74. package/deps/librdkafka/examples/list_consumer_groups.c +365 -0
  75. package/deps/librdkafka/examples/list_offsets.c +327 -0
  76. package/deps/librdkafka/examples/misc.c +287 -0
  77. package/deps/librdkafka/examples/openssl_engine_example.cpp +248 -0
  78. package/deps/librdkafka/examples/producer.c +251 -0
  79. package/deps/librdkafka/examples/producer.cpp +228 -0
  80. package/deps/librdkafka/examples/rdkafka_complex_consumer_example.c +617 -0
  81. package/deps/librdkafka/examples/rdkafka_complex_consumer_example.cpp +467 -0
  82. package/deps/librdkafka/examples/rdkafka_consume_batch.cpp +264 -0
  83. package/deps/librdkafka/examples/rdkafka_example.c +853 -0
  84. package/deps/librdkafka/examples/rdkafka_example.cpp +679 -0
  85. package/deps/librdkafka/examples/rdkafka_performance.c +1781 -0
  86. package/deps/librdkafka/examples/transactions-older-broker.c +668 -0
  87. package/deps/librdkafka/examples/transactions.c +665 -0
  88. package/deps/librdkafka/examples/user_scram.c +491 -0
  89. package/deps/librdkafka/examples/win_ssl_cert_store.cpp +396 -0
  90. package/deps/librdkafka/lds-gen.py +73 -0
  91. package/deps/librdkafka/mainpage.doxy +40 -0
  92. package/deps/librdkafka/mklove/Makefile.base +329 -0
  93. package/deps/librdkafka/mklove/modules/configure.atomics +144 -0
  94. package/deps/librdkafka/mklove/modules/configure.base +2484 -0
  95. package/deps/librdkafka/mklove/modules/configure.builtin +70 -0
  96. package/deps/librdkafka/mklove/modules/configure.cc +186 -0
  97. package/deps/librdkafka/mklove/modules/configure.cxx +8 -0
  98. package/deps/librdkafka/mklove/modules/configure.fileversion +65 -0
  99. package/deps/librdkafka/mklove/modules/configure.gitversion +29 -0
  100. package/deps/librdkafka/mklove/modules/configure.good_cflags +18 -0
  101. package/deps/librdkafka/mklove/modules/configure.host +132 -0
  102. package/deps/librdkafka/mklove/modules/configure.lib +49 -0
  103. package/deps/librdkafka/mklove/modules/configure.libcurl +99 -0
  104. package/deps/librdkafka/mklove/modules/configure.libsasl2 +36 -0
  105. package/deps/librdkafka/mklove/modules/configure.libssl +147 -0
  106. package/deps/librdkafka/mklove/modules/configure.libzstd +58 -0
  107. package/deps/librdkafka/mklove/modules/configure.parseversion +95 -0
  108. package/deps/librdkafka/mklove/modules/configure.pic +16 -0
  109. package/deps/librdkafka/mklove/modules/configure.socket +20 -0
  110. package/deps/librdkafka/mklove/modules/configure.zlib +61 -0
  111. package/deps/librdkafka/mklove/modules/patches/README.md +8 -0
  112. package/deps/librdkafka/mklove/modules/patches/libcurl.0000-no-runtime-linking-check.patch +11 -0
  113. package/deps/librdkafka/mklove/modules/patches/libssl.0000-osx-rand-include-fix-OpenSSL-PR16409.patch +56 -0
  114. package/deps/librdkafka/packaging/RELEASE.md +319 -0
  115. package/deps/librdkafka/packaging/alpine/build-alpine.sh +38 -0
  116. package/deps/librdkafka/packaging/archlinux/PKGBUILD +30 -0
  117. package/deps/librdkafka/packaging/cmake/Config.cmake.in +37 -0
  118. package/deps/librdkafka/packaging/cmake/Modules/FindLZ4.cmake +38 -0
  119. package/deps/librdkafka/packaging/cmake/Modules/FindZSTD.cmake +27 -0
  120. package/deps/librdkafka/packaging/cmake/Modules/LICENSE.FindZstd +178 -0
  121. package/deps/librdkafka/packaging/cmake/README.md +38 -0
  122. package/deps/librdkafka/packaging/cmake/config.h.in +52 -0
  123. package/deps/librdkafka/packaging/cmake/parseversion.cmake +60 -0
  124. package/deps/librdkafka/packaging/cmake/rdkafka.pc.in +12 -0
  125. package/deps/librdkafka/packaging/cmake/try_compile/atomic_32_test.c +8 -0
  126. package/deps/librdkafka/packaging/cmake/try_compile/atomic_64_test.c +8 -0
  127. package/deps/librdkafka/packaging/cmake/try_compile/c11threads_test.c +14 -0
  128. package/deps/librdkafka/packaging/cmake/try_compile/crc32c_hw_test.c +27 -0
  129. package/deps/librdkafka/packaging/cmake/try_compile/dlopen_test.c +11 -0
  130. package/deps/librdkafka/packaging/cmake/try_compile/libsasl2_test.c +7 -0
  131. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_darwin_test.c +6 -0
  132. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_freebsd_test.c +7 -0
  133. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_gnu_test.c +5 -0
  134. package/deps/librdkafka/packaging/cmake/try_compile/rand_r_test.c +7 -0
  135. package/deps/librdkafka/packaging/cmake/try_compile/rdkafka_setup.cmake +122 -0
  136. package/deps/librdkafka/packaging/cmake/try_compile/regex_test.c +10 -0
  137. package/deps/librdkafka/packaging/cmake/try_compile/strndup_test.c +5 -0
  138. package/deps/librdkafka/packaging/cmake/try_compile/sync_32_test.c +8 -0
  139. package/deps/librdkafka/packaging/cmake/try_compile/sync_64_test.c +8 -0
  140. package/deps/librdkafka/packaging/cp/README.md +16 -0
  141. package/deps/librdkafka/packaging/cp/check_features.c +72 -0
  142. package/deps/librdkafka/packaging/cp/verify-deb.sh +33 -0
  143. package/deps/librdkafka/packaging/cp/verify-packages.sh +69 -0
  144. package/deps/librdkafka/packaging/cp/verify-rpm.sh +32 -0
  145. package/deps/librdkafka/packaging/debian/changelog +66 -0
  146. package/deps/librdkafka/packaging/debian/compat +1 -0
  147. package/deps/librdkafka/packaging/debian/control +49 -0
  148. package/deps/librdkafka/packaging/debian/copyright +84 -0
  149. package/deps/librdkafka/packaging/debian/docs +5 -0
  150. package/deps/librdkafka/packaging/debian/gbp.conf +9 -0
  151. package/deps/librdkafka/packaging/debian/librdkafka-dev.dirs +2 -0
  152. package/deps/librdkafka/packaging/debian/librdkafka-dev.examples +2 -0
  153. package/deps/librdkafka/packaging/debian/librdkafka-dev.install +6 -0
  154. package/deps/librdkafka/packaging/debian/librdkafka-dev.substvars +1 -0
  155. package/deps/librdkafka/packaging/debian/librdkafka.dsc +16 -0
  156. package/deps/librdkafka/packaging/debian/librdkafka1-dbg.substvars +1 -0
  157. package/deps/librdkafka/packaging/debian/librdkafka1.dirs +1 -0
  158. package/deps/librdkafka/packaging/debian/librdkafka1.install +2 -0
  159. package/deps/librdkafka/packaging/debian/librdkafka1.postinst.debhelper +5 -0
  160. package/deps/librdkafka/packaging/debian/librdkafka1.postrm.debhelper +5 -0
  161. package/deps/librdkafka/packaging/debian/librdkafka1.symbols +64 -0
  162. package/deps/librdkafka/packaging/debian/rules +19 -0
  163. package/deps/librdkafka/packaging/debian/source/format +1 -0
  164. package/deps/librdkafka/packaging/debian/watch +2 -0
  165. package/deps/librdkafka/packaging/get_version.py +21 -0
  166. package/deps/librdkafka/packaging/homebrew/README.md +15 -0
  167. package/deps/librdkafka/packaging/homebrew/brew-update-pr.sh +31 -0
  168. package/deps/librdkafka/packaging/mingw-w64/configure-build-msys2-mingw-static.sh +52 -0
  169. package/deps/librdkafka/packaging/mingw-w64/configure-build-msys2-mingw.sh +21 -0
  170. package/deps/librdkafka/packaging/mingw-w64/export-variables.sh +13 -0
  171. package/deps/librdkafka/packaging/mingw-w64/run-tests.sh +6 -0
  172. package/deps/librdkafka/packaging/mingw-w64/semaphoreci-build.sh +38 -0
  173. package/deps/librdkafka/packaging/nuget/README.md +84 -0
  174. package/deps/librdkafka/packaging/nuget/artifact.py +177 -0
  175. package/deps/librdkafka/packaging/nuget/cleanup-s3.py +143 -0
  176. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-win32__bldtype-Release/msvcr120.zip +0 -0
  177. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-win32__bldtype-Release/msvcr140.zip +0 -0
  178. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-x64__bldtype-Release/msvcr120.zip +0 -0
  179. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-x64__bldtype-Release/msvcr140.zip +0 -0
  180. package/deps/librdkafka/packaging/nuget/nuget.sh +21 -0
  181. package/deps/librdkafka/packaging/nuget/nugetpackage.py +278 -0
  182. package/deps/librdkafka/packaging/nuget/packaging.py +448 -0
  183. package/deps/librdkafka/packaging/nuget/push-to-nuget.sh +21 -0
  184. package/deps/librdkafka/packaging/nuget/release.py +167 -0
  185. package/deps/librdkafka/packaging/nuget/requirements.txt +3 -0
  186. package/deps/librdkafka/packaging/nuget/staticpackage.py +178 -0
  187. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.nuspec +21 -0
  188. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.props +18 -0
  189. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.targets +19 -0
  190. package/deps/librdkafka/packaging/nuget/zfile/__init__.py +0 -0
  191. package/deps/librdkafka/packaging/nuget/zfile/zfile.py +98 -0
  192. package/deps/librdkafka/packaging/rpm/Makefile +92 -0
  193. package/deps/librdkafka/packaging/rpm/README.md +23 -0
  194. package/deps/librdkafka/packaging/rpm/el7-x86_64.cfg +40 -0
  195. package/deps/librdkafka/packaging/rpm/librdkafka.spec +118 -0
  196. package/deps/librdkafka/packaging/rpm/mock-on-docker.sh +96 -0
  197. package/deps/librdkafka/packaging/rpm/tests/Makefile +25 -0
  198. package/deps/librdkafka/packaging/rpm/tests/README.md +8 -0
  199. package/deps/librdkafka/packaging/rpm/tests/run-test.sh +42 -0
  200. package/deps/librdkafka/packaging/rpm/tests/test-on-docker.sh +56 -0
  201. package/deps/librdkafka/packaging/rpm/tests/test.c +77 -0
  202. package/deps/librdkafka/packaging/rpm/tests/test.cpp +34 -0
  203. package/deps/librdkafka/packaging/tools/Dockerfile +31 -0
  204. package/deps/librdkafka/packaging/tools/build-configurations-checks.sh +12 -0
  205. package/deps/librdkafka/packaging/tools/build-deb-package.sh +64 -0
  206. package/deps/librdkafka/packaging/tools/build-debian.sh +65 -0
  207. package/deps/librdkafka/packaging/tools/build-manylinux.sh +68 -0
  208. package/deps/librdkafka/packaging/tools/build-release-artifacts.sh +139 -0
  209. package/deps/librdkafka/packaging/tools/distro-build.sh +38 -0
  210. package/deps/librdkafka/packaging/tools/gh-release-checksums.py +39 -0
  211. package/deps/librdkafka/packaging/tools/rdutcoverage.sh +25 -0
  212. package/deps/librdkafka/packaging/tools/requirements.txt +2 -0
  213. package/deps/librdkafka/packaging/tools/run-in-docker.sh +28 -0
  214. package/deps/librdkafka/packaging/tools/run-integration-tests.sh +31 -0
  215. package/deps/librdkafka/packaging/tools/run-style-check.sh +4 -0
  216. package/deps/librdkafka/packaging/tools/style-format.sh +149 -0
  217. package/deps/librdkafka/packaging/tools/update_rpcs_max_versions.py +100 -0
  218. package/deps/librdkafka/service.yml +172 -0
  219. package/deps/librdkafka/src/CMakeLists.txt +374 -0
  220. package/deps/librdkafka/src/Makefile +103 -0
  221. package/deps/librdkafka/src/README.lz4.md +30 -0
  222. package/deps/librdkafka/src/cJSON.c +2834 -0
  223. package/deps/librdkafka/src/cJSON.h +398 -0
  224. package/deps/librdkafka/src/crc32c.c +430 -0
  225. package/deps/librdkafka/src/crc32c.h +38 -0
  226. package/deps/librdkafka/src/generate_proto.sh +66 -0
  227. package/deps/librdkafka/src/librdkafka_cgrp_synch.png +0 -0
  228. package/deps/librdkafka/src/lz4.c +2727 -0
  229. package/deps/librdkafka/src/lz4.h +842 -0
  230. package/deps/librdkafka/src/lz4frame.c +2078 -0
  231. package/deps/librdkafka/src/lz4frame.h +692 -0
  232. package/deps/librdkafka/src/lz4frame_static.h +47 -0
  233. package/deps/librdkafka/src/lz4hc.c +1631 -0
  234. package/deps/librdkafka/src/lz4hc.h +413 -0
  235. package/deps/librdkafka/src/nanopb/pb.h +917 -0
  236. package/deps/librdkafka/src/nanopb/pb_common.c +388 -0
  237. package/deps/librdkafka/src/nanopb/pb_common.h +49 -0
  238. package/deps/librdkafka/src/nanopb/pb_decode.c +1727 -0
  239. package/deps/librdkafka/src/nanopb/pb_decode.h +193 -0
  240. package/deps/librdkafka/src/nanopb/pb_encode.c +1000 -0
  241. package/deps/librdkafka/src/nanopb/pb_encode.h +185 -0
  242. package/deps/librdkafka/src/opentelemetry/common.pb.c +32 -0
  243. package/deps/librdkafka/src/opentelemetry/common.pb.h +170 -0
  244. package/deps/librdkafka/src/opentelemetry/metrics.options +2 -0
  245. package/deps/librdkafka/src/opentelemetry/metrics.pb.c +67 -0
  246. package/deps/librdkafka/src/opentelemetry/metrics.pb.h +966 -0
  247. package/deps/librdkafka/src/opentelemetry/resource.pb.c +12 -0
  248. package/deps/librdkafka/src/opentelemetry/resource.pb.h +58 -0
  249. package/deps/librdkafka/src/queue.h +850 -0
  250. package/deps/librdkafka/src/rd.h +584 -0
  251. package/deps/librdkafka/src/rdaddr.c +255 -0
  252. package/deps/librdkafka/src/rdaddr.h +202 -0
  253. package/deps/librdkafka/src/rdatomic.h +230 -0
  254. package/deps/librdkafka/src/rdavg.h +260 -0
  255. package/deps/librdkafka/src/rdavl.c +210 -0
  256. package/deps/librdkafka/src/rdavl.h +250 -0
  257. package/deps/librdkafka/src/rdbase64.c +200 -0
  258. package/deps/librdkafka/src/rdbase64.h +43 -0
  259. package/deps/librdkafka/src/rdbuf.c +1884 -0
  260. package/deps/librdkafka/src/rdbuf.h +375 -0
  261. package/deps/librdkafka/src/rdcrc32.c +114 -0
  262. package/deps/librdkafka/src/rdcrc32.h +170 -0
  263. package/deps/librdkafka/src/rddl.c +179 -0
  264. package/deps/librdkafka/src/rddl.h +43 -0
  265. package/deps/librdkafka/src/rdendian.h +175 -0
  266. package/deps/librdkafka/src/rdfloat.h +67 -0
  267. package/deps/librdkafka/src/rdfnv1a.c +113 -0
  268. package/deps/librdkafka/src/rdfnv1a.h +35 -0
  269. package/deps/librdkafka/src/rdgz.c +120 -0
  270. package/deps/librdkafka/src/rdgz.h +46 -0
  271. package/deps/librdkafka/src/rdhdrhistogram.c +721 -0
  272. package/deps/librdkafka/src/rdhdrhistogram.h +87 -0
  273. package/deps/librdkafka/src/rdhttp.c +830 -0
  274. package/deps/librdkafka/src/rdhttp.h +101 -0
  275. package/deps/librdkafka/src/rdinterval.h +177 -0
  276. package/deps/librdkafka/src/rdkafka.c +5505 -0
  277. package/deps/librdkafka/src/rdkafka.h +10686 -0
  278. package/deps/librdkafka/src/rdkafka_admin.c +9794 -0
  279. package/deps/librdkafka/src/rdkafka_admin.h +661 -0
  280. package/deps/librdkafka/src/rdkafka_assignment.c +1010 -0
  281. package/deps/librdkafka/src/rdkafka_assignment.h +73 -0
  282. package/deps/librdkafka/src/rdkafka_assignor.c +1786 -0
  283. package/deps/librdkafka/src/rdkafka_assignor.h +402 -0
  284. package/deps/librdkafka/src/rdkafka_aux.c +409 -0
  285. package/deps/librdkafka/src/rdkafka_aux.h +174 -0
  286. package/deps/librdkafka/src/rdkafka_background.c +221 -0
  287. package/deps/librdkafka/src/rdkafka_broker.c +6337 -0
  288. package/deps/librdkafka/src/rdkafka_broker.h +744 -0
  289. package/deps/librdkafka/src/rdkafka_buf.c +543 -0
  290. package/deps/librdkafka/src/rdkafka_buf.h +1525 -0
  291. package/deps/librdkafka/src/rdkafka_cert.c +576 -0
  292. package/deps/librdkafka/src/rdkafka_cert.h +62 -0
  293. package/deps/librdkafka/src/rdkafka_cgrp.c +7587 -0
  294. package/deps/librdkafka/src/rdkafka_cgrp.h +477 -0
  295. package/deps/librdkafka/src/rdkafka_conf.c +4880 -0
  296. package/deps/librdkafka/src/rdkafka_conf.h +732 -0
  297. package/deps/librdkafka/src/rdkafka_confval.h +97 -0
  298. package/deps/librdkafka/src/rdkafka_coord.c +623 -0
  299. package/deps/librdkafka/src/rdkafka_coord.h +132 -0
  300. package/deps/librdkafka/src/rdkafka_error.c +228 -0
  301. package/deps/librdkafka/src/rdkafka_error.h +80 -0
  302. package/deps/librdkafka/src/rdkafka_event.c +502 -0
  303. package/deps/librdkafka/src/rdkafka_event.h +126 -0
  304. package/deps/librdkafka/src/rdkafka_feature.c +898 -0
  305. package/deps/librdkafka/src/rdkafka_feature.h +104 -0
  306. package/deps/librdkafka/src/rdkafka_fetcher.c +1422 -0
  307. package/deps/librdkafka/src/rdkafka_fetcher.h +44 -0
  308. package/deps/librdkafka/src/rdkafka_header.c +220 -0
  309. package/deps/librdkafka/src/rdkafka_header.h +76 -0
  310. package/deps/librdkafka/src/rdkafka_idempotence.c +807 -0
  311. package/deps/librdkafka/src/rdkafka_idempotence.h +144 -0
  312. package/deps/librdkafka/src/rdkafka_int.h +1260 -0
  313. package/deps/librdkafka/src/rdkafka_interceptor.c +819 -0
  314. package/deps/librdkafka/src/rdkafka_interceptor.h +104 -0
  315. package/deps/librdkafka/src/rdkafka_lz4.c +450 -0
  316. package/deps/librdkafka/src/rdkafka_lz4.h +49 -0
  317. package/deps/librdkafka/src/rdkafka_metadata.c +2209 -0
  318. package/deps/librdkafka/src/rdkafka_metadata.h +345 -0
  319. package/deps/librdkafka/src/rdkafka_metadata_cache.c +1183 -0
  320. package/deps/librdkafka/src/rdkafka_mock.c +3661 -0
  321. package/deps/librdkafka/src/rdkafka_mock.h +610 -0
  322. package/deps/librdkafka/src/rdkafka_mock_cgrp.c +1876 -0
  323. package/deps/librdkafka/src/rdkafka_mock_handlers.c +3113 -0
  324. package/deps/librdkafka/src/rdkafka_mock_int.h +710 -0
  325. package/deps/librdkafka/src/rdkafka_msg.c +2589 -0
  326. package/deps/librdkafka/src/rdkafka_msg.h +614 -0
  327. package/deps/librdkafka/src/rdkafka_msgbatch.h +62 -0
  328. package/deps/librdkafka/src/rdkafka_msgset.h +98 -0
  329. package/deps/librdkafka/src/rdkafka_msgset_reader.c +1806 -0
  330. package/deps/librdkafka/src/rdkafka_msgset_writer.c +1474 -0
  331. package/deps/librdkafka/src/rdkafka_offset.c +1565 -0
  332. package/deps/librdkafka/src/rdkafka_offset.h +150 -0
  333. package/deps/librdkafka/src/rdkafka_op.c +997 -0
  334. package/deps/librdkafka/src/rdkafka_op.h +858 -0
  335. package/deps/librdkafka/src/rdkafka_partition.c +4896 -0
  336. package/deps/librdkafka/src/rdkafka_partition.h +1182 -0
  337. package/deps/librdkafka/src/rdkafka_pattern.c +228 -0
  338. package/deps/librdkafka/src/rdkafka_pattern.h +70 -0
  339. package/deps/librdkafka/src/rdkafka_plugin.c +213 -0
  340. package/deps/librdkafka/src/rdkafka_plugin.h +41 -0
  341. package/deps/librdkafka/src/rdkafka_proto.h +736 -0
  342. package/deps/librdkafka/src/rdkafka_protocol.h +128 -0
  343. package/deps/librdkafka/src/rdkafka_queue.c +1230 -0
  344. package/deps/librdkafka/src/rdkafka_queue.h +1220 -0
  345. package/deps/librdkafka/src/rdkafka_range_assignor.c +1748 -0
  346. package/deps/librdkafka/src/rdkafka_request.c +7089 -0
  347. package/deps/librdkafka/src/rdkafka_request.h +732 -0
  348. package/deps/librdkafka/src/rdkafka_roundrobin_assignor.c +123 -0
  349. package/deps/librdkafka/src/rdkafka_sasl.c +530 -0
  350. package/deps/librdkafka/src/rdkafka_sasl.h +63 -0
  351. package/deps/librdkafka/src/rdkafka_sasl_cyrus.c +722 -0
  352. package/deps/librdkafka/src/rdkafka_sasl_int.h +89 -0
  353. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.c +1833 -0
  354. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.h +52 -0
  355. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.c +1666 -0
  356. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.h +47 -0
  357. package/deps/librdkafka/src/rdkafka_sasl_plain.c +142 -0
  358. package/deps/librdkafka/src/rdkafka_sasl_scram.c +858 -0
  359. package/deps/librdkafka/src/rdkafka_sasl_win32.c +550 -0
  360. package/deps/librdkafka/src/rdkafka_ssl.c +2129 -0
  361. package/deps/librdkafka/src/rdkafka_ssl.h +86 -0
  362. package/deps/librdkafka/src/rdkafka_sticky_assignor.c +4785 -0
  363. package/deps/librdkafka/src/rdkafka_subscription.c +278 -0
  364. package/deps/librdkafka/src/rdkafka_telemetry.c +760 -0
  365. package/deps/librdkafka/src/rdkafka_telemetry.h +52 -0
  366. package/deps/librdkafka/src/rdkafka_telemetry_decode.c +1053 -0
  367. package/deps/librdkafka/src/rdkafka_telemetry_decode.h +59 -0
  368. package/deps/librdkafka/src/rdkafka_telemetry_encode.c +997 -0
  369. package/deps/librdkafka/src/rdkafka_telemetry_encode.h +301 -0
  370. package/deps/librdkafka/src/rdkafka_timer.c +402 -0
  371. package/deps/librdkafka/src/rdkafka_timer.h +117 -0
  372. package/deps/librdkafka/src/rdkafka_topic.c +2161 -0
  373. package/deps/librdkafka/src/rdkafka_topic.h +334 -0
  374. package/deps/librdkafka/src/rdkafka_transport.c +1309 -0
  375. package/deps/librdkafka/src/rdkafka_transport.h +99 -0
  376. package/deps/librdkafka/src/rdkafka_transport_int.h +100 -0
  377. package/deps/librdkafka/src/rdkafka_txnmgr.c +3256 -0
  378. package/deps/librdkafka/src/rdkafka_txnmgr.h +171 -0
  379. package/deps/librdkafka/src/rdkafka_zstd.c +226 -0
  380. package/deps/librdkafka/src/rdkafka_zstd.h +57 -0
  381. package/deps/librdkafka/src/rdlist.c +576 -0
  382. package/deps/librdkafka/src/rdlist.h +434 -0
  383. package/deps/librdkafka/src/rdlog.c +89 -0
  384. package/deps/librdkafka/src/rdlog.h +41 -0
  385. package/deps/librdkafka/src/rdmap.c +508 -0
  386. package/deps/librdkafka/src/rdmap.h +492 -0
  387. package/deps/librdkafka/src/rdmurmur2.c +167 -0
  388. package/deps/librdkafka/src/rdmurmur2.h +35 -0
  389. package/deps/librdkafka/src/rdports.c +61 -0
  390. package/deps/librdkafka/src/rdports.h +38 -0
  391. package/deps/librdkafka/src/rdposix.h +250 -0
  392. package/deps/librdkafka/src/rdrand.c +80 -0
  393. package/deps/librdkafka/src/rdrand.h +43 -0
  394. package/deps/librdkafka/src/rdregex.c +156 -0
  395. package/deps/librdkafka/src/rdregex.h +43 -0
  396. package/deps/librdkafka/src/rdsignal.h +57 -0
  397. package/deps/librdkafka/src/rdstring.c +645 -0
  398. package/deps/librdkafka/src/rdstring.h +98 -0
  399. package/deps/librdkafka/src/rdsysqueue.h +404 -0
  400. package/deps/librdkafka/src/rdtime.h +356 -0
  401. package/deps/librdkafka/src/rdtypes.h +86 -0
  402. package/deps/librdkafka/src/rdunittest.c +549 -0
  403. package/deps/librdkafka/src/rdunittest.h +232 -0
  404. package/deps/librdkafka/src/rdvarint.c +134 -0
  405. package/deps/librdkafka/src/rdvarint.h +165 -0
  406. package/deps/librdkafka/src/rdwin32.h +382 -0
  407. package/deps/librdkafka/src/rdxxhash.c +1030 -0
  408. package/deps/librdkafka/src/rdxxhash.h +328 -0
  409. package/deps/librdkafka/src/regexp.c +1352 -0
  410. package/deps/librdkafka/src/regexp.h +41 -0
  411. package/deps/librdkafka/src/snappy.c +1866 -0
  412. package/deps/librdkafka/src/snappy.h +62 -0
  413. package/deps/librdkafka/src/snappy_compat.h +138 -0
  414. package/deps/librdkafka/src/statistics_schema.json +444 -0
  415. package/deps/librdkafka/src/tinycthread.c +932 -0
  416. package/deps/librdkafka/src/tinycthread.h +503 -0
  417. package/deps/librdkafka/src/tinycthread_extra.c +199 -0
  418. package/deps/librdkafka/src/tinycthread_extra.h +212 -0
  419. package/deps/librdkafka/src/win32_config.h +58 -0
  420. package/deps/librdkafka/src-cpp/CMakeLists.txt +90 -0
  421. package/deps/librdkafka/src-cpp/ConfImpl.cpp +84 -0
  422. package/deps/librdkafka/src-cpp/ConsumerImpl.cpp +244 -0
  423. package/deps/librdkafka/src-cpp/HandleImpl.cpp +436 -0
  424. package/deps/librdkafka/src-cpp/HeadersImpl.cpp +48 -0
  425. package/deps/librdkafka/src-cpp/KafkaConsumerImpl.cpp +296 -0
  426. package/deps/librdkafka/src-cpp/Makefile +55 -0
  427. package/deps/librdkafka/src-cpp/MessageImpl.cpp +38 -0
  428. package/deps/librdkafka/src-cpp/MetadataImpl.cpp +170 -0
  429. package/deps/librdkafka/src-cpp/ProducerImpl.cpp +197 -0
  430. package/deps/librdkafka/src-cpp/QueueImpl.cpp +70 -0
  431. package/deps/librdkafka/src-cpp/README.md +16 -0
  432. package/deps/librdkafka/src-cpp/RdKafka.cpp +59 -0
  433. package/deps/librdkafka/src-cpp/TopicImpl.cpp +124 -0
  434. package/deps/librdkafka/src-cpp/TopicPartitionImpl.cpp +57 -0
  435. package/deps/librdkafka/src-cpp/rdkafkacpp.h +3797 -0
  436. package/deps/librdkafka/src-cpp/rdkafkacpp_int.h +1641 -0
  437. package/deps/librdkafka/tests/0000-unittests.c +72 -0
  438. package/deps/librdkafka/tests/0001-multiobj.c +102 -0
  439. package/deps/librdkafka/tests/0002-unkpart.c +244 -0
  440. package/deps/librdkafka/tests/0003-msgmaxsize.c +173 -0
  441. package/deps/librdkafka/tests/0004-conf.c +934 -0
  442. package/deps/librdkafka/tests/0005-order.c +133 -0
  443. package/deps/librdkafka/tests/0006-symbols.c +163 -0
  444. package/deps/librdkafka/tests/0007-autotopic.c +136 -0
  445. package/deps/librdkafka/tests/0008-reqacks.c +179 -0
  446. package/deps/librdkafka/tests/0009-mock_cluster.c +97 -0
  447. package/deps/librdkafka/tests/0011-produce_batch.c +753 -0
  448. package/deps/librdkafka/tests/0012-produce_consume.c +537 -0
  449. package/deps/librdkafka/tests/0013-null-msgs.c +473 -0
  450. package/deps/librdkafka/tests/0014-reconsume-191.c +512 -0
  451. package/deps/librdkafka/tests/0015-offset_seeks.c +172 -0
  452. package/deps/librdkafka/tests/0016-client_swname.c +181 -0
  453. package/deps/librdkafka/tests/0017-compression.c +140 -0
  454. package/deps/librdkafka/tests/0018-cgrp_term.c +338 -0
  455. package/deps/librdkafka/tests/0019-list_groups.c +289 -0
  456. package/deps/librdkafka/tests/0020-destroy_hang.c +162 -0
  457. package/deps/librdkafka/tests/0021-rkt_destroy.c +72 -0
  458. package/deps/librdkafka/tests/0022-consume_batch.c +279 -0
  459. package/deps/librdkafka/tests/0025-timers.c +147 -0
  460. package/deps/librdkafka/tests/0026-consume_pause.c +547 -0
  461. package/deps/librdkafka/tests/0028-long_topicnames.c +79 -0
  462. package/deps/librdkafka/tests/0029-assign_offset.c +202 -0
  463. package/deps/librdkafka/tests/0030-offset_commit.c +589 -0
  464. package/deps/librdkafka/tests/0031-get_offsets.c +235 -0
  465. package/deps/librdkafka/tests/0033-regex_subscribe.c +536 -0
  466. package/deps/librdkafka/tests/0034-offset_reset.c +398 -0
  467. package/deps/librdkafka/tests/0035-api_version.c +73 -0
  468. package/deps/librdkafka/tests/0036-partial_fetch.c +87 -0
  469. package/deps/librdkafka/tests/0037-destroy_hang_local.c +85 -0
  470. package/deps/librdkafka/tests/0038-performance.c +121 -0
  471. package/deps/librdkafka/tests/0039-event.c +284 -0
  472. package/deps/librdkafka/tests/0040-io_event.c +257 -0
  473. package/deps/librdkafka/tests/0041-fetch_max_bytes.c +97 -0
  474. package/deps/librdkafka/tests/0042-many_topics.c +252 -0
  475. package/deps/librdkafka/tests/0043-no_connection.c +77 -0
  476. package/deps/librdkafka/tests/0044-partition_cnt.c +94 -0
  477. package/deps/librdkafka/tests/0045-subscribe_update.c +1010 -0
  478. package/deps/librdkafka/tests/0046-rkt_cache.c +65 -0
  479. package/deps/librdkafka/tests/0047-partial_buf_tmout.c +98 -0
  480. package/deps/librdkafka/tests/0048-partitioner.c +283 -0
  481. package/deps/librdkafka/tests/0049-consume_conn_close.c +162 -0
  482. package/deps/librdkafka/tests/0050-subscribe_adds.c +145 -0
  483. package/deps/librdkafka/tests/0051-assign_adds.c +126 -0
  484. package/deps/librdkafka/tests/0052-msg_timestamps.c +238 -0
  485. package/deps/librdkafka/tests/0053-stats_cb.cpp +527 -0
  486. package/deps/librdkafka/tests/0054-offset_time.cpp +236 -0
  487. package/deps/librdkafka/tests/0055-producer_latency.c +539 -0
  488. package/deps/librdkafka/tests/0056-balanced_group_mt.c +315 -0
  489. package/deps/librdkafka/tests/0057-invalid_topic.cpp +112 -0
  490. package/deps/librdkafka/tests/0058-log.cpp +123 -0
  491. package/deps/librdkafka/tests/0059-bsearch.cpp +241 -0
  492. package/deps/librdkafka/tests/0060-op_prio.cpp +163 -0
  493. package/deps/librdkafka/tests/0061-consumer_lag.cpp +295 -0
  494. package/deps/librdkafka/tests/0062-stats_event.c +126 -0
  495. package/deps/librdkafka/tests/0063-clusterid.cpp +180 -0
  496. package/deps/librdkafka/tests/0064-interceptors.c +481 -0
  497. package/deps/librdkafka/tests/0065-yield.cpp +140 -0
  498. package/deps/librdkafka/tests/0066-plugins.cpp +129 -0
  499. package/deps/librdkafka/tests/0067-empty_topic.cpp +151 -0
  500. package/deps/librdkafka/tests/0068-produce_timeout.c +136 -0
  501. package/deps/librdkafka/tests/0069-consumer_add_parts.c +119 -0
  502. package/deps/librdkafka/tests/0070-null_empty.cpp +197 -0
  503. package/deps/librdkafka/tests/0072-headers_ut.c +448 -0
  504. package/deps/librdkafka/tests/0073-headers.c +381 -0
  505. package/deps/librdkafka/tests/0074-producev.c +87 -0
  506. package/deps/librdkafka/tests/0075-retry.c +290 -0
  507. package/deps/librdkafka/tests/0076-produce_retry.c +452 -0
  508. package/deps/librdkafka/tests/0077-compaction.c +363 -0
  509. package/deps/librdkafka/tests/0078-c_from_cpp.cpp +96 -0
  510. package/deps/librdkafka/tests/0079-fork.c +93 -0
  511. package/deps/librdkafka/tests/0080-admin_ut.c +3095 -0
  512. package/deps/librdkafka/tests/0081-admin.c +5633 -0
  513. package/deps/librdkafka/tests/0082-fetch_max_bytes.cpp +137 -0
  514. package/deps/librdkafka/tests/0083-cb_event.c +233 -0
  515. package/deps/librdkafka/tests/0084-destroy_flags.c +208 -0
  516. package/deps/librdkafka/tests/0085-headers.cpp +392 -0
  517. package/deps/librdkafka/tests/0086-purge.c +368 -0
  518. package/deps/librdkafka/tests/0088-produce_metadata_timeout.c +162 -0
  519. package/deps/librdkafka/tests/0089-max_poll_interval.c +511 -0
  520. package/deps/librdkafka/tests/0090-idempotence.c +171 -0
  521. package/deps/librdkafka/tests/0091-max_poll_interval_timeout.c +295 -0
  522. package/deps/librdkafka/tests/0092-mixed_msgver.c +103 -0
  523. package/deps/librdkafka/tests/0093-holb.c +200 -0
  524. package/deps/librdkafka/tests/0094-idempotence_msg_timeout.c +231 -0
  525. package/deps/librdkafka/tests/0095-all_brokers_down.cpp +122 -0
  526. package/deps/librdkafka/tests/0097-ssl_verify.cpp +658 -0
  527. package/deps/librdkafka/tests/0098-consumer-txn.cpp +1218 -0
  528. package/deps/librdkafka/tests/0099-commit_metadata.c +194 -0
  529. package/deps/librdkafka/tests/0100-thread_interceptors.cpp +195 -0
  530. package/deps/librdkafka/tests/0101-fetch-from-follower.cpp +446 -0
  531. package/deps/librdkafka/tests/0102-static_group_rebalance.c +836 -0
  532. package/deps/librdkafka/tests/0103-transactions.c +1383 -0
  533. package/deps/librdkafka/tests/0104-fetch_from_follower_mock.c +625 -0
  534. package/deps/librdkafka/tests/0105-transactions_mock.c +3930 -0
  535. package/deps/librdkafka/tests/0106-cgrp_sess_timeout.c +318 -0
  536. package/deps/librdkafka/tests/0107-topic_recreate.c +259 -0
  537. package/deps/librdkafka/tests/0109-auto_create_topics.cpp +278 -0
  538. package/deps/librdkafka/tests/0110-batch_size.cpp +182 -0
  539. package/deps/librdkafka/tests/0111-delay_create_topics.cpp +127 -0
  540. package/deps/librdkafka/tests/0112-assign_unknown_part.c +87 -0
  541. package/deps/librdkafka/tests/0113-cooperative_rebalance.cpp +3473 -0
  542. package/deps/librdkafka/tests/0114-sticky_partitioning.cpp +176 -0
  543. package/deps/librdkafka/tests/0115-producer_auth.cpp +182 -0
  544. package/deps/librdkafka/tests/0116-kafkaconsumer_close.cpp +216 -0
  545. package/deps/librdkafka/tests/0117-mock_errors.c +331 -0
  546. package/deps/librdkafka/tests/0118-commit_rebalance.c +154 -0
  547. package/deps/librdkafka/tests/0119-consumer_auth.cpp +167 -0
  548. package/deps/librdkafka/tests/0120-asymmetric_subscription.c +185 -0
  549. package/deps/librdkafka/tests/0121-clusterid.c +115 -0
  550. package/deps/librdkafka/tests/0122-buffer_cleaning_after_rebalance.c +227 -0
  551. package/deps/librdkafka/tests/0123-connections_max_idle.c +98 -0
  552. package/deps/librdkafka/tests/0124-openssl_invalid_engine.c +69 -0
  553. package/deps/librdkafka/tests/0125-immediate_flush.c +144 -0
  554. package/deps/librdkafka/tests/0126-oauthbearer_oidc.c +528 -0
  555. package/deps/librdkafka/tests/0127-fetch_queue_backoff.cpp +165 -0
  556. package/deps/librdkafka/tests/0128-sasl_callback_queue.cpp +125 -0
  557. package/deps/librdkafka/tests/0129-fetch_aborted_msgs.c +79 -0
  558. package/deps/librdkafka/tests/0130-store_offsets.c +178 -0
  559. package/deps/librdkafka/tests/0131-connect_timeout.c +81 -0
  560. package/deps/librdkafka/tests/0132-strategy_ordering.c +179 -0
  561. package/deps/librdkafka/tests/0133-ssl_keys.c +150 -0
  562. package/deps/librdkafka/tests/0134-ssl_provider.c +92 -0
  563. package/deps/librdkafka/tests/0135-sasl_credentials.cpp +143 -0
  564. package/deps/librdkafka/tests/0136-resolve_cb.c +181 -0
  565. package/deps/librdkafka/tests/0137-barrier_batch_consume.c +619 -0
  566. package/deps/librdkafka/tests/0138-admin_mock.c +281 -0
  567. package/deps/librdkafka/tests/0139-offset_validation_mock.c +950 -0
  568. package/deps/librdkafka/tests/0140-commit_metadata.cpp +108 -0
  569. package/deps/librdkafka/tests/0142-reauthentication.c +515 -0
  570. package/deps/librdkafka/tests/0143-exponential_backoff_mock.c +552 -0
  571. package/deps/librdkafka/tests/0144-idempotence_mock.c +373 -0
  572. package/deps/librdkafka/tests/0145-pause_resume_mock.c +119 -0
  573. package/deps/librdkafka/tests/0146-metadata_mock.c +505 -0
  574. package/deps/librdkafka/tests/0147-consumer_group_consumer_mock.c +952 -0
  575. package/deps/librdkafka/tests/0148-offset_fetch_commit_error_mock.c +563 -0
  576. package/deps/librdkafka/tests/0149-broker-same-host-port.c +140 -0
  577. package/deps/librdkafka/tests/0150-telemetry_mock.c +651 -0
  578. package/deps/librdkafka/tests/0151-purge-brokers.c +566 -0
  579. package/deps/librdkafka/tests/0152-rebootstrap.c +59 -0
  580. package/deps/librdkafka/tests/0153-memberid.c +128 -0
  581. package/deps/librdkafka/tests/1000-unktopic.c +164 -0
  582. package/deps/librdkafka/tests/8000-idle.cpp +60 -0
  583. package/deps/librdkafka/tests/8001-fetch_from_follower_mock_manual.c +113 -0
  584. package/deps/librdkafka/tests/CMakeLists.txt +170 -0
  585. package/deps/librdkafka/tests/LibrdkafkaTestApp.py +291 -0
  586. package/deps/librdkafka/tests/Makefile +182 -0
  587. package/deps/librdkafka/tests/README.md +509 -0
  588. package/deps/librdkafka/tests/autotest.sh +33 -0
  589. package/deps/librdkafka/tests/backtrace.gdb +30 -0
  590. package/deps/librdkafka/tests/broker_version_tests.py +315 -0
  591. package/deps/librdkafka/tests/buildbox.sh +17 -0
  592. package/deps/librdkafka/tests/cleanup-checker-tests.sh +20 -0
  593. package/deps/librdkafka/tests/cluster_testing.py +191 -0
  594. package/deps/librdkafka/tests/delete-test-topics.sh +56 -0
  595. package/deps/librdkafka/tests/fixtures/oauthbearer/jwt_assertion_template.json +10 -0
  596. package/deps/librdkafka/tests/fixtures/ssl/Makefile +8 -0
  597. package/deps/librdkafka/tests/fixtures/ssl/README.md +13 -0
  598. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.intermediate.p12 +0 -0
  599. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.p12 +0 -0
  600. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.intermediate.pem +72 -0
  601. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.pem +50 -0
  602. package/deps/librdkafka/tests/fixtures/ssl/client2.intermediate.key +46 -0
  603. package/deps/librdkafka/tests/fixtures/ssl/client2.key +46 -0
  604. package/deps/librdkafka/tests/fixtures/ssl/create_keys.sh +168 -0
  605. package/deps/librdkafka/tests/fuzzers/Makefile +12 -0
  606. package/deps/librdkafka/tests/fuzzers/README.md +31 -0
  607. package/deps/librdkafka/tests/fuzzers/fuzz_regex.c +74 -0
  608. package/deps/librdkafka/tests/fuzzers/helpers.h +90 -0
  609. package/deps/librdkafka/tests/gen-ssl-certs.sh +165 -0
  610. package/deps/librdkafka/tests/interactive_broker_version.py +170 -0
  611. package/deps/librdkafka/tests/interceptor_test/CMakeLists.txt +16 -0
  612. package/deps/librdkafka/tests/interceptor_test/Makefile +22 -0
  613. package/deps/librdkafka/tests/interceptor_test/interceptor_test.c +314 -0
  614. package/deps/librdkafka/tests/interceptor_test/interceptor_test.h +54 -0
  615. package/deps/librdkafka/tests/java/IncrementalRebalanceCli.java +97 -0
  616. package/deps/librdkafka/tests/java/Makefile +13 -0
  617. package/deps/librdkafka/tests/java/Murmur2Cli.java +46 -0
  618. package/deps/librdkafka/tests/java/README.md +14 -0
  619. package/deps/librdkafka/tests/java/TransactionProducerCli.java +162 -0
  620. package/deps/librdkafka/tests/java/run-class.sh +11 -0
  621. package/deps/librdkafka/tests/librdkafka.suppressions +483 -0
  622. package/deps/librdkafka/tests/lz4_manual_test.sh +59 -0
  623. package/deps/librdkafka/tests/multi-broker-version-test.sh +50 -0
  624. package/deps/librdkafka/tests/parse-refcnt.sh +43 -0
  625. package/deps/librdkafka/tests/performance_plot.py +115 -0
  626. package/deps/librdkafka/tests/plugin_test/Makefile +19 -0
  627. package/deps/librdkafka/tests/plugin_test/plugin_test.c +58 -0
  628. package/deps/librdkafka/tests/requirements.txt +2 -0
  629. package/deps/librdkafka/tests/run-all-tests.sh +79 -0
  630. package/deps/librdkafka/tests/run-consumer-tests.sh +16 -0
  631. package/deps/librdkafka/tests/run-producer-tests.sh +16 -0
  632. package/deps/librdkafka/tests/run-test-batches.py +157 -0
  633. package/deps/librdkafka/tests/run-test.sh +140 -0
  634. package/deps/librdkafka/tests/rusage.c +249 -0
  635. package/deps/librdkafka/tests/sasl_test.py +289 -0
  636. package/deps/librdkafka/tests/scenarios/README.md +6 -0
  637. package/deps/librdkafka/tests/scenarios/ak23.json +6 -0
  638. package/deps/librdkafka/tests/scenarios/default.json +5 -0
  639. package/deps/librdkafka/tests/scenarios/noautocreate.json +5 -0
  640. package/deps/librdkafka/tests/sockem.c +801 -0
  641. package/deps/librdkafka/tests/sockem.h +85 -0
  642. package/deps/librdkafka/tests/sockem_ctrl.c +145 -0
  643. package/deps/librdkafka/tests/sockem_ctrl.h +61 -0
  644. package/deps/librdkafka/tests/test.c +7778 -0
  645. package/deps/librdkafka/tests/test.conf.example +27 -0
  646. package/deps/librdkafka/tests/test.h +1028 -0
  647. package/deps/librdkafka/tests/testcpp.cpp +131 -0
  648. package/deps/librdkafka/tests/testcpp.h +388 -0
  649. package/deps/librdkafka/tests/testshared.h +416 -0
  650. package/deps/librdkafka/tests/tools/README.md +4 -0
  651. package/deps/librdkafka/tests/tools/stats/README.md +21 -0
  652. package/deps/librdkafka/tests/tools/stats/filter.jq +42 -0
  653. package/deps/librdkafka/tests/tools/stats/graph.py +150 -0
  654. package/deps/librdkafka/tests/tools/stats/requirements.txt +3 -0
  655. package/deps/librdkafka/tests/tools/stats/to_csv.py +124 -0
  656. package/deps/librdkafka/tests/trivup/trivup-0.14.0.tar.gz +0 -0
  657. package/deps/librdkafka/tests/until-fail.sh +87 -0
  658. package/deps/librdkafka/tests/xxxx-assign_partition.c +122 -0
  659. package/deps/librdkafka/tests/xxxx-metadata.cpp +159 -0
  660. package/deps/librdkafka/vcpkg.json +23 -0
  661. package/deps/librdkafka/win32/README.md +5 -0
  662. package/deps/librdkafka/win32/build-package.bat +3 -0
  663. package/deps/librdkafka/win32/build.bat +19 -0
  664. package/deps/librdkafka/win32/common.vcxproj +84 -0
  665. package/deps/librdkafka/win32/interceptor_test/interceptor_test.vcxproj +87 -0
  666. package/deps/librdkafka/win32/librdkafka.autopkg.template +54 -0
  667. package/deps/librdkafka/win32/librdkafka.master.testing.targets +13 -0
  668. package/deps/librdkafka/win32/librdkafka.sln +226 -0
  669. package/deps/librdkafka/win32/librdkafka.vcxproj +276 -0
  670. package/deps/librdkafka/win32/librdkafkacpp/librdkafkacpp.vcxproj +104 -0
  671. package/deps/librdkafka/win32/msbuild.ps1 +15 -0
  672. package/deps/librdkafka/win32/openssl_engine_example/openssl_engine_example.vcxproj +132 -0
  673. package/deps/librdkafka/win32/package-zip.ps1 +46 -0
  674. package/deps/librdkafka/win32/packages/repositories.config +4 -0
  675. package/deps/librdkafka/win32/push-package.bat +4 -0
  676. package/deps/librdkafka/win32/rdkafka_complex_consumer_example_cpp/rdkafka_complex_consumer_example_cpp.vcxproj +67 -0
  677. package/deps/librdkafka/win32/rdkafka_example/rdkafka_example.vcxproj +97 -0
  678. package/deps/librdkafka/win32/rdkafka_performance/rdkafka_performance.vcxproj +97 -0
  679. package/deps/librdkafka/win32/setup-msys2.ps1 +47 -0
  680. package/deps/librdkafka/win32/setup-vcpkg.ps1 +34 -0
  681. package/deps/librdkafka/win32/tests/test.conf.example +25 -0
  682. package/deps/librdkafka/win32/tests/tests.vcxproj +253 -0
  683. package/deps/librdkafka/win32/win_ssl_cert_store/win_ssl_cert_store.vcxproj +132 -0
  684. package/deps/librdkafka/win32/wingetopt.c +564 -0
  685. package/deps/librdkafka/win32/wingetopt.h +101 -0
  686. package/deps/librdkafka/win32/wintime.h +33 -0
  687. package/deps/librdkafka.gyp +62 -0
  688. package/lib/admin.js +233 -0
  689. package/lib/client.js +573 -0
  690. package/lib/error.js +500 -0
  691. package/lib/index.js +34 -0
  692. package/lib/kafka-consumer-stream.js +397 -0
  693. package/lib/kafka-consumer.js +698 -0
  694. package/lib/producer/high-level-producer.js +323 -0
  695. package/lib/producer-stream.js +307 -0
  696. package/lib/producer.js +375 -0
  697. package/lib/tools/ref-counter.js +52 -0
  698. package/lib/topic-partition.js +88 -0
  699. package/lib/topic.js +42 -0
  700. package/lib/util.js +29 -0
  701. package/package.json +61 -0
  702. package/prebuilds/darwin-arm64/@point3+node-rdkafka.node +0 -0
  703. package/prebuilds/linux-x64/@point3+node-rdkafka.node +0 -0
  704. package/util/configure.js +30 -0
  705. package/util/get-env.js +6 -0
  706. package/util/test-compile.js +11 -0
  707. 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
+ }