@platformatic/rdkafka 4.0.0

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 (767) hide show
  1. package/.editorconfig +5 -0
  2. package/.jshintignore +1 -0
  3. package/.jshintrc +23 -0
  4. package/CONTRIBUTING.md +237 -0
  5. package/LICENSE.txt +21 -0
  6. package/Makefile +94 -0
  7. package/README.md +630 -0
  8. package/bench/consumer-raw-rdkafka.js +76 -0
  9. package/bench/consumer-subscribe.js +77 -0
  10. package/bench/kafka-consumer-stream.js +100 -0
  11. package/bench/producer-raw-rdkafka.js +125 -0
  12. package/bench/producer-rdkafka.js +117 -0
  13. package/bench/seed.sh +38 -0
  14. package/binding.gyp +155 -0
  15. package/ci/checks/librdkafka-correct-version.js +67 -0
  16. package/ci/checks/librdkafka-exists.js +13 -0
  17. package/ci/librdkafka-defs-generator.js +193 -0
  18. package/ci/prepublish.js +3 -0
  19. package/config.d.ts +1132 -0
  20. package/configure +15 -0
  21. package/cpplint.py +6325 -0
  22. package/deploy.enc +0 -0
  23. package/deps/librdkafka/.clang-format +136 -0
  24. package/deps/librdkafka/.clang-format-cpp +103 -0
  25. package/deps/librdkafka/.dir-locals.el +10 -0
  26. package/deps/librdkafka/.formatignore +33 -0
  27. package/deps/librdkafka/.gdbmacros +19 -0
  28. package/deps/librdkafka/.semaphore/project.yml +43 -0
  29. package/deps/librdkafka/.semaphore/project_public.yml +20 -0
  30. package/deps/librdkafka/.semaphore/semaphore.yml +355 -0
  31. package/deps/librdkafka/CHANGELOG.md +1738 -0
  32. package/deps/librdkafka/CMakeLists.txt +291 -0
  33. package/deps/librdkafka/CODE_OF_CONDUCT.md +46 -0
  34. package/deps/librdkafka/CONFIGURATION.md +189 -0
  35. package/deps/librdkafka/CONTRIBUTING.md +431 -0
  36. package/deps/librdkafka/Doxyfile +2375 -0
  37. package/deps/librdkafka/INTRODUCTION.md +2173 -0
  38. package/deps/librdkafka/LICENSE +26 -0
  39. package/deps/librdkafka/LICENSE.cjson +22 -0
  40. package/deps/librdkafka/LICENSE.crc32c +28 -0
  41. package/deps/librdkafka/LICENSE.fnv1a +18 -0
  42. package/deps/librdkafka/LICENSE.hdrhistogram +27 -0
  43. package/deps/librdkafka/LICENSE.lz4 +26 -0
  44. package/deps/librdkafka/LICENSE.murmur2 +25 -0
  45. package/deps/librdkafka/LICENSE.nanopb +22 -0
  46. package/deps/librdkafka/LICENSE.opentelemetry +203 -0
  47. package/deps/librdkafka/LICENSE.pycrc +23 -0
  48. package/deps/librdkafka/LICENSE.queue +31 -0
  49. package/deps/librdkafka/LICENSE.regexp +5 -0
  50. package/deps/librdkafka/LICENSE.snappy +36 -0
  51. package/deps/librdkafka/LICENSE.tinycthread +26 -0
  52. package/deps/librdkafka/LICENSE.wingetopt +49 -0
  53. package/deps/librdkafka/LICENSES.txt +625 -0
  54. package/deps/librdkafka/Makefile +124 -0
  55. package/deps/librdkafka/README.md +199 -0
  56. package/deps/librdkafka/README.win32 +26 -0
  57. package/deps/librdkafka/STATISTICS.md +624 -0
  58. package/deps/librdkafka/configure +214 -0
  59. package/deps/librdkafka/configure.self +331 -0
  60. package/deps/librdkafka/debian/changelog +111 -0
  61. package/deps/librdkafka/debian/compat +1 -0
  62. package/deps/librdkafka/debian/control +71 -0
  63. package/deps/librdkafka/debian/copyright +99 -0
  64. package/deps/librdkafka/debian/gbp.conf +9 -0
  65. package/deps/librdkafka/debian/librdkafka++1.install +1 -0
  66. package/deps/librdkafka/debian/librdkafka-dev.examples +2 -0
  67. package/deps/librdkafka/debian/librdkafka-dev.install +9 -0
  68. package/deps/librdkafka/debian/librdkafka1.docs +5 -0
  69. package/deps/librdkafka/debian/librdkafka1.install +1 -0
  70. package/deps/librdkafka/debian/librdkafka1.symbols +135 -0
  71. package/deps/librdkafka/debian/rules +19 -0
  72. package/deps/librdkafka/debian/source/format +1 -0
  73. package/deps/librdkafka/debian/watch +2 -0
  74. package/deps/librdkafka/dev-conf.sh +123 -0
  75. package/deps/librdkafka/examples/CMakeLists.txt +79 -0
  76. package/deps/librdkafka/examples/Makefile +167 -0
  77. package/deps/librdkafka/examples/README.md +42 -0
  78. package/deps/librdkafka/examples/alter_consumer_group_offsets.c +338 -0
  79. package/deps/librdkafka/examples/consumer.c +261 -0
  80. package/deps/librdkafka/examples/delete_records.c +233 -0
  81. package/deps/librdkafka/examples/describe_cluster.c +322 -0
  82. package/deps/librdkafka/examples/describe_consumer_groups.c +436 -0
  83. package/deps/librdkafka/examples/describe_topics.c +427 -0
  84. package/deps/librdkafka/examples/elect_leaders.c +317 -0
  85. package/deps/librdkafka/examples/globals.json +11 -0
  86. package/deps/librdkafka/examples/idempotent_producer.c +344 -0
  87. package/deps/librdkafka/examples/incremental_alter_configs.c +348 -0
  88. package/deps/librdkafka/examples/kafkatest_verifiable_client.cpp +961 -0
  89. package/deps/librdkafka/examples/list_consumer_group_offsets.c +359 -0
  90. package/deps/librdkafka/examples/list_consumer_groups.c +365 -0
  91. package/deps/librdkafka/examples/list_offsets.c +327 -0
  92. package/deps/librdkafka/examples/misc.c +287 -0
  93. package/deps/librdkafka/examples/openssl_engine_example.cpp +249 -0
  94. package/deps/librdkafka/examples/producer.c +251 -0
  95. package/deps/librdkafka/examples/producer.cpp +228 -0
  96. package/deps/librdkafka/examples/rdkafka_complex_consumer_example.c +617 -0
  97. package/deps/librdkafka/examples/rdkafka_complex_consumer_example.cpp +467 -0
  98. package/deps/librdkafka/examples/rdkafka_consume_batch.cpp +264 -0
  99. package/deps/librdkafka/examples/rdkafka_example.c +853 -0
  100. package/deps/librdkafka/examples/rdkafka_example.cpp +679 -0
  101. package/deps/librdkafka/examples/rdkafka_performance.c +1781 -0
  102. package/deps/librdkafka/examples/transactions-older-broker.c +668 -0
  103. package/deps/librdkafka/examples/transactions.c +665 -0
  104. package/deps/librdkafka/examples/user_scram.c +492 -0
  105. package/deps/librdkafka/examples/win_ssl_cert_store.cpp +395 -0
  106. package/deps/librdkafka/lds-gen.py +73 -0
  107. package/deps/librdkafka/mainpage.doxy +40 -0
  108. package/deps/librdkafka/mklove/Makefile.base +329 -0
  109. package/deps/librdkafka/mklove/modules/configure.atomics +144 -0
  110. package/deps/librdkafka/mklove/modules/configure.base +2484 -0
  111. package/deps/librdkafka/mklove/modules/configure.builtin +70 -0
  112. package/deps/librdkafka/mklove/modules/configure.cc +186 -0
  113. package/deps/librdkafka/mklove/modules/configure.cxx +8 -0
  114. package/deps/librdkafka/mklove/modules/configure.fileversion +65 -0
  115. package/deps/librdkafka/mklove/modules/configure.gitversion +29 -0
  116. package/deps/librdkafka/mklove/modules/configure.good_cflags +18 -0
  117. package/deps/librdkafka/mklove/modules/configure.host +132 -0
  118. package/deps/librdkafka/mklove/modules/configure.lib +49 -0
  119. package/deps/librdkafka/mklove/modules/configure.libcurl +99 -0
  120. package/deps/librdkafka/mklove/modules/configure.libsasl2 +36 -0
  121. package/deps/librdkafka/mklove/modules/configure.libssl +147 -0
  122. package/deps/librdkafka/mklove/modules/configure.libzstd +58 -0
  123. package/deps/librdkafka/mklove/modules/configure.parseversion +95 -0
  124. package/deps/librdkafka/mklove/modules/configure.pic +16 -0
  125. package/deps/librdkafka/mklove/modules/configure.socket +20 -0
  126. package/deps/librdkafka/mklove/modules/configure.zlib +61 -0
  127. package/deps/librdkafka/mklove/modules/patches/README.md +8 -0
  128. package/deps/librdkafka/mklove/modules/patches/libcurl.0000-no-runtime-linking-check.patch +11 -0
  129. package/deps/librdkafka/mklove/modules/patches/libssl.0000-osx-rand-include-fix-OpenSSL-PR16409.patch +56 -0
  130. package/deps/librdkafka/packaging/RELEASE.md +311 -0
  131. package/deps/librdkafka/packaging/alpine/build-alpine.sh +38 -0
  132. package/deps/librdkafka/packaging/archlinux/PKGBUILD +30 -0
  133. package/deps/librdkafka/packaging/cmake/Config.cmake.in +37 -0
  134. package/deps/librdkafka/packaging/cmake/Modules/FindLZ4.cmake +38 -0
  135. package/deps/librdkafka/packaging/cmake/Modules/FindZSTD.cmake +27 -0
  136. package/deps/librdkafka/packaging/cmake/Modules/LICENSE.FindZstd +178 -0
  137. package/deps/librdkafka/packaging/cmake/README.md +38 -0
  138. package/deps/librdkafka/packaging/cmake/config.h.in +52 -0
  139. package/deps/librdkafka/packaging/cmake/parseversion.cmake +60 -0
  140. package/deps/librdkafka/packaging/cmake/rdkafka.pc.in +12 -0
  141. package/deps/librdkafka/packaging/cmake/try_compile/atomic_32_test.c +8 -0
  142. package/deps/librdkafka/packaging/cmake/try_compile/atomic_64_test.c +8 -0
  143. package/deps/librdkafka/packaging/cmake/try_compile/c11threads_test.c +14 -0
  144. package/deps/librdkafka/packaging/cmake/try_compile/crc32c_hw_test.c +27 -0
  145. package/deps/librdkafka/packaging/cmake/try_compile/dlopen_test.c +11 -0
  146. package/deps/librdkafka/packaging/cmake/try_compile/libsasl2_test.c +7 -0
  147. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_darwin_test.c +6 -0
  148. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_freebsd_test.c +7 -0
  149. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_gnu_test.c +5 -0
  150. package/deps/librdkafka/packaging/cmake/try_compile/rand_r_test.c +7 -0
  151. package/deps/librdkafka/packaging/cmake/try_compile/rdkafka_setup.cmake +122 -0
  152. package/deps/librdkafka/packaging/cmake/try_compile/regex_test.c +10 -0
  153. package/deps/librdkafka/packaging/cmake/try_compile/strndup_test.c +5 -0
  154. package/deps/librdkafka/packaging/cmake/try_compile/sync_32_test.c +8 -0
  155. package/deps/librdkafka/packaging/cmake/try_compile/sync_64_test.c +8 -0
  156. package/deps/librdkafka/packaging/cp/README.md +13 -0
  157. package/deps/librdkafka/packaging/cp/check_features.c +64 -0
  158. package/deps/librdkafka/packaging/cp/verify-deb.sh +33 -0
  159. package/deps/librdkafka/packaging/cp/verify-packages.sh +48 -0
  160. package/deps/librdkafka/packaging/cp/verify-rpm.sh +39 -0
  161. package/deps/librdkafka/packaging/debian/changelog +66 -0
  162. package/deps/librdkafka/packaging/debian/compat +1 -0
  163. package/deps/librdkafka/packaging/debian/control +49 -0
  164. package/deps/librdkafka/packaging/debian/copyright +84 -0
  165. package/deps/librdkafka/packaging/debian/gbp.conf +9 -0
  166. package/deps/librdkafka/packaging/debian/librdkafka-dev.dirs +2 -0
  167. package/deps/librdkafka/packaging/debian/librdkafka-dev.examples +2 -0
  168. package/deps/librdkafka/packaging/debian/librdkafka-dev.install +6 -0
  169. package/deps/librdkafka/packaging/debian/librdkafka-dev.substvars +1 -0
  170. package/deps/librdkafka/packaging/debian/librdkafka.dsc +16 -0
  171. package/deps/librdkafka/packaging/debian/librdkafka1-dbg.substvars +1 -0
  172. package/deps/librdkafka/packaging/debian/librdkafka1.dirs +1 -0
  173. package/deps/librdkafka/packaging/debian/librdkafka1.install +2 -0
  174. package/deps/librdkafka/packaging/debian/librdkafka1.postinst.debhelper +5 -0
  175. package/deps/librdkafka/packaging/debian/librdkafka1.postrm.debhelper +5 -0
  176. package/deps/librdkafka/packaging/debian/librdkafka1.symbols +64 -0
  177. package/deps/librdkafka/packaging/debian/rules +19 -0
  178. package/deps/librdkafka/packaging/debian/source/format +1 -0
  179. package/deps/librdkafka/packaging/debian/watch +2 -0
  180. package/deps/librdkafka/packaging/get_version.py +21 -0
  181. package/deps/librdkafka/packaging/homebrew/README.md +15 -0
  182. package/deps/librdkafka/packaging/homebrew/brew-update-pr.sh +31 -0
  183. package/deps/librdkafka/packaging/mingw-w64/configure-build-msys2-mingw-static.sh +52 -0
  184. package/deps/librdkafka/packaging/mingw-w64/configure-build-msys2-mingw.sh +21 -0
  185. package/deps/librdkafka/packaging/mingw-w64/run-tests.sh +6 -0
  186. package/deps/librdkafka/packaging/mingw-w64/semaphoreci-build.sh +38 -0
  187. package/deps/librdkafka/packaging/mingw-w64/travis-before-install.sh +20 -0
  188. package/deps/librdkafka/packaging/nuget/README.md +84 -0
  189. package/deps/librdkafka/packaging/nuget/artifact.py +177 -0
  190. package/deps/librdkafka/packaging/nuget/cleanup-s3.py +143 -0
  191. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-win32__bldtype-Release/msvcr120.zip +0 -0
  192. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-win32__bldtype-Release/msvcr140.zip +0 -0
  193. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-x64__bldtype-Release/msvcr120.zip +0 -0
  194. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-x64__bldtype-Release/msvcr140.zip +0 -0
  195. package/deps/librdkafka/packaging/nuget/nuget.sh +21 -0
  196. package/deps/librdkafka/packaging/nuget/nugetpackage.py +278 -0
  197. package/deps/librdkafka/packaging/nuget/packaging.py +448 -0
  198. package/deps/librdkafka/packaging/nuget/push-to-nuget.sh +21 -0
  199. package/deps/librdkafka/packaging/nuget/release.py +167 -0
  200. package/deps/librdkafka/packaging/nuget/requirements.txt +3 -0
  201. package/deps/librdkafka/packaging/nuget/staticpackage.py +178 -0
  202. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.nuspec +21 -0
  203. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.props +18 -0
  204. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.targets +19 -0
  205. package/deps/librdkafka/packaging/nuget/zfile/__init__.py +0 -0
  206. package/deps/librdkafka/packaging/nuget/zfile/zfile.py +98 -0
  207. package/deps/librdkafka/packaging/rpm/Makefile +92 -0
  208. package/deps/librdkafka/packaging/rpm/README.md +23 -0
  209. package/deps/librdkafka/packaging/rpm/el7-x86_64.cfg +40 -0
  210. package/deps/librdkafka/packaging/rpm/librdkafka.spec +118 -0
  211. package/deps/librdkafka/packaging/rpm/mock-on-docker.sh +96 -0
  212. package/deps/librdkafka/packaging/rpm/tests/Makefile +25 -0
  213. package/deps/librdkafka/packaging/rpm/tests/README.md +8 -0
  214. package/deps/librdkafka/packaging/rpm/tests/run-test.sh +42 -0
  215. package/deps/librdkafka/packaging/rpm/tests/test-on-docker.sh +56 -0
  216. package/deps/librdkafka/packaging/rpm/tests/test.c +77 -0
  217. package/deps/librdkafka/packaging/rpm/tests/test.cpp +34 -0
  218. package/deps/librdkafka/packaging/tools/build-configurations-checks.sh +10 -0
  219. package/deps/librdkafka/packaging/tools/build-deb-package.sh +64 -0
  220. package/deps/librdkafka/packaging/tools/build-debian.sh +65 -0
  221. package/deps/librdkafka/packaging/tools/build-manylinux.sh +68 -0
  222. package/deps/librdkafka/packaging/tools/build-release-artifacts.sh +139 -0
  223. package/deps/librdkafka/packaging/tools/distro-build.sh +38 -0
  224. package/deps/librdkafka/packaging/tools/gh-release-checksums.py +39 -0
  225. package/deps/librdkafka/packaging/tools/rdutcoverage.sh +25 -0
  226. package/deps/librdkafka/packaging/tools/requirements.txt +2 -0
  227. package/deps/librdkafka/packaging/tools/style-format.sh +148 -0
  228. package/deps/librdkafka/service.yml +18 -0
  229. package/deps/librdkafka/src/CMakeLists.txt +374 -0
  230. package/deps/librdkafka/src/Makefile +103 -0
  231. package/deps/librdkafka/src/README.lz4.md +30 -0
  232. package/deps/librdkafka/src/cJSON.c +2834 -0
  233. package/deps/librdkafka/src/cJSON.h +398 -0
  234. package/deps/librdkafka/src/crc32c.c +430 -0
  235. package/deps/librdkafka/src/crc32c.h +38 -0
  236. package/deps/librdkafka/src/generate_proto.sh +66 -0
  237. package/deps/librdkafka/src/librdkafka_cgrp_synch.png +0 -0
  238. package/deps/librdkafka/src/lz4.c +2727 -0
  239. package/deps/librdkafka/src/lz4.h +842 -0
  240. package/deps/librdkafka/src/lz4frame.c +2078 -0
  241. package/deps/librdkafka/src/lz4frame.h +692 -0
  242. package/deps/librdkafka/src/lz4frame_static.h +47 -0
  243. package/deps/librdkafka/src/lz4hc.c +1631 -0
  244. package/deps/librdkafka/src/lz4hc.h +413 -0
  245. package/deps/librdkafka/src/nanopb/pb.h +917 -0
  246. package/deps/librdkafka/src/nanopb/pb_common.c +388 -0
  247. package/deps/librdkafka/src/nanopb/pb_common.h +49 -0
  248. package/deps/librdkafka/src/nanopb/pb_decode.c +1727 -0
  249. package/deps/librdkafka/src/nanopb/pb_decode.h +193 -0
  250. package/deps/librdkafka/src/nanopb/pb_encode.c +1000 -0
  251. package/deps/librdkafka/src/nanopb/pb_encode.h +185 -0
  252. package/deps/librdkafka/src/opentelemetry/common.pb.c +32 -0
  253. package/deps/librdkafka/src/opentelemetry/common.pb.h +170 -0
  254. package/deps/librdkafka/src/opentelemetry/metrics.options +2 -0
  255. package/deps/librdkafka/src/opentelemetry/metrics.pb.c +67 -0
  256. package/deps/librdkafka/src/opentelemetry/metrics.pb.h +966 -0
  257. package/deps/librdkafka/src/opentelemetry/resource.pb.c +12 -0
  258. package/deps/librdkafka/src/opentelemetry/resource.pb.h +58 -0
  259. package/deps/librdkafka/src/queue.h +850 -0
  260. package/deps/librdkafka/src/rd.h +441 -0
  261. package/deps/librdkafka/src/rdaddr.c +255 -0
  262. package/deps/librdkafka/src/rdaddr.h +203 -0
  263. package/deps/librdkafka/src/rdatomic.h +226 -0
  264. package/deps/librdkafka/src/rdavg.h +259 -0
  265. package/deps/librdkafka/src/rdavl.c +210 -0
  266. package/deps/librdkafka/src/rdavl.h +250 -0
  267. package/deps/librdkafka/src/rdbase64.c +169 -0
  268. package/deps/librdkafka/src/rdbase64.h +41 -0
  269. package/deps/librdkafka/src/rdbuf.c +1884 -0
  270. package/deps/librdkafka/src/rdbuf.h +375 -0
  271. package/deps/librdkafka/src/rdcrc32.c +114 -0
  272. package/deps/librdkafka/src/rdcrc32.h +170 -0
  273. package/deps/librdkafka/src/rddl.c +179 -0
  274. package/deps/librdkafka/src/rddl.h +43 -0
  275. package/deps/librdkafka/src/rdendian.h +174 -0
  276. package/deps/librdkafka/src/rdfloat.h +67 -0
  277. package/deps/librdkafka/src/rdfnv1a.c +113 -0
  278. package/deps/librdkafka/src/rdfnv1a.h +35 -0
  279. package/deps/librdkafka/src/rdgz.c +120 -0
  280. package/deps/librdkafka/src/rdgz.h +46 -0
  281. package/deps/librdkafka/src/rdhdrhistogram.c +721 -0
  282. package/deps/librdkafka/src/rdhdrhistogram.h +87 -0
  283. package/deps/librdkafka/src/rdhttp.c +512 -0
  284. package/deps/librdkafka/src/rdhttp.h +83 -0
  285. package/deps/librdkafka/src/rdinterval.h +177 -0
  286. package/deps/librdkafka/src/rdkafka.c +5351 -0
  287. package/deps/librdkafka/src/rdkafka.h +10603 -0
  288. package/deps/librdkafka/src/rdkafka_admin.c +9417 -0
  289. package/deps/librdkafka/src/rdkafka_admin.h +619 -0
  290. package/deps/librdkafka/src/rdkafka_assignment.c +1010 -0
  291. package/deps/librdkafka/src/rdkafka_assignment.h +73 -0
  292. package/deps/librdkafka/src/rdkafka_assignor.c +1786 -0
  293. package/deps/librdkafka/src/rdkafka_assignor.h +402 -0
  294. package/deps/librdkafka/src/rdkafka_aux.c +409 -0
  295. package/deps/librdkafka/src/rdkafka_aux.h +174 -0
  296. package/deps/librdkafka/src/rdkafka_background.c +221 -0
  297. package/deps/librdkafka/src/rdkafka_broker.c +6186 -0
  298. package/deps/librdkafka/src/rdkafka_broker.h +686 -0
  299. package/deps/librdkafka/src/rdkafka_buf.c +540 -0
  300. package/deps/librdkafka/src/rdkafka_buf.h +1524 -0
  301. package/deps/librdkafka/src/rdkafka_cert.c +552 -0
  302. package/deps/librdkafka/src/rdkafka_cert.h +61 -0
  303. package/deps/librdkafka/src/rdkafka_cgrp.c +7262 -0
  304. package/deps/librdkafka/src/rdkafka_cgrp.h +443 -0
  305. package/deps/librdkafka/src/rdkafka_conf.c +4456 -0
  306. package/deps/librdkafka/src/rdkafka_conf.h +668 -0
  307. package/deps/librdkafka/src/rdkafka_confval.h +97 -0
  308. package/deps/librdkafka/src/rdkafka_coord.c +623 -0
  309. package/deps/librdkafka/src/rdkafka_coord.h +132 -0
  310. package/deps/librdkafka/src/rdkafka_error.c +228 -0
  311. package/deps/librdkafka/src/rdkafka_error.h +80 -0
  312. package/deps/librdkafka/src/rdkafka_event.c +502 -0
  313. package/deps/librdkafka/src/rdkafka_event.h +126 -0
  314. package/deps/librdkafka/src/rdkafka_feature.c +461 -0
  315. package/deps/librdkafka/src/rdkafka_feature.h +102 -0
  316. package/deps/librdkafka/src/rdkafka_fetcher.c +1402 -0
  317. package/deps/librdkafka/src/rdkafka_fetcher.h +44 -0
  318. package/deps/librdkafka/src/rdkafka_header.c +220 -0
  319. package/deps/librdkafka/src/rdkafka_header.h +76 -0
  320. package/deps/librdkafka/src/rdkafka_idempotence.c +807 -0
  321. package/deps/librdkafka/src/rdkafka_idempotence.h +144 -0
  322. package/deps/librdkafka/src/rdkafka_int.h +1222 -0
  323. package/deps/librdkafka/src/rdkafka_interceptor.c +819 -0
  324. package/deps/librdkafka/src/rdkafka_interceptor.h +104 -0
  325. package/deps/librdkafka/src/rdkafka_lz4.c +450 -0
  326. package/deps/librdkafka/src/rdkafka_lz4.h +49 -0
  327. package/deps/librdkafka/src/rdkafka_metadata.c +2124 -0
  328. package/deps/librdkafka/src/rdkafka_metadata.h +341 -0
  329. package/deps/librdkafka/src/rdkafka_metadata_cache.c +977 -0
  330. package/deps/librdkafka/src/rdkafka_mock.c +2885 -0
  331. package/deps/librdkafka/src/rdkafka_mock.h +491 -0
  332. package/deps/librdkafka/src/rdkafka_mock_cgrp.c +710 -0
  333. package/deps/librdkafka/src/rdkafka_mock_handlers.c +2817 -0
  334. package/deps/librdkafka/src/rdkafka_mock_int.h +591 -0
  335. package/deps/librdkafka/src/rdkafka_msg.c +2573 -0
  336. package/deps/librdkafka/src/rdkafka_msg.h +614 -0
  337. package/deps/librdkafka/src/rdkafka_msgbatch.h +62 -0
  338. package/deps/librdkafka/src/rdkafka_msgset.h +98 -0
  339. package/deps/librdkafka/src/rdkafka_msgset_reader.c +1806 -0
  340. package/deps/librdkafka/src/rdkafka_msgset_writer.c +1470 -0
  341. package/deps/librdkafka/src/rdkafka_offset.c +1537 -0
  342. package/deps/librdkafka/src/rdkafka_offset.h +150 -0
  343. package/deps/librdkafka/src/rdkafka_op.c +997 -0
  344. package/deps/librdkafka/src/rdkafka_op.h +834 -0
  345. package/deps/librdkafka/src/rdkafka_partition.c +4801 -0
  346. package/deps/librdkafka/src/rdkafka_partition.h +1173 -0
  347. package/deps/librdkafka/src/rdkafka_pattern.c +228 -0
  348. package/deps/librdkafka/src/rdkafka_pattern.h +70 -0
  349. package/deps/librdkafka/src/rdkafka_plugin.c +213 -0
  350. package/deps/librdkafka/src/rdkafka_plugin.h +41 -0
  351. package/deps/librdkafka/src/rdkafka_proto.h +733 -0
  352. package/deps/librdkafka/src/rdkafka_protocol.h +127 -0
  353. package/deps/librdkafka/src/rdkafka_queue.c +1153 -0
  354. package/deps/librdkafka/src/rdkafka_queue.h +1201 -0
  355. package/deps/librdkafka/src/rdkafka_range_assignor.c +1748 -0
  356. package/deps/librdkafka/src/rdkafka_request.c +6817 -0
  357. package/deps/librdkafka/src/rdkafka_request.h +709 -0
  358. package/deps/librdkafka/src/rdkafka_roundrobin_assignor.c +123 -0
  359. package/deps/librdkafka/src/rdkafka_sasl.c +528 -0
  360. package/deps/librdkafka/src/rdkafka_sasl.h +63 -0
  361. package/deps/librdkafka/src/rdkafka_sasl_cyrus.c +722 -0
  362. package/deps/librdkafka/src/rdkafka_sasl_int.h +89 -0
  363. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.c +1829 -0
  364. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.h +52 -0
  365. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.c +589 -0
  366. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.h +37 -0
  367. package/deps/librdkafka/src/rdkafka_sasl_plain.c +142 -0
  368. package/deps/librdkafka/src/rdkafka_sasl_scram.c +857 -0
  369. package/deps/librdkafka/src/rdkafka_sasl_win32.c +550 -0
  370. package/deps/librdkafka/src/rdkafka_ssl.c +1904 -0
  371. package/deps/librdkafka/src/rdkafka_ssl.h +64 -0
  372. package/deps/librdkafka/src/rdkafka_sticky_assignor.c +4781 -0
  373. package/deps/librdkafka/src/rdkafka_subscription.c +278 -0
  374. package/deps/librdkafka/src/rdkafka_telemetry.c +703 -0
  375. package/deps/librdkafka/src/rdkafka_telemetry.h +52 -0
  376. package/deps/librdkafka/src/rdkafka_telemetry_decode.c +1053 -0
  377. package/deps/librdkafka/src/rdkafka_telemetry_decode.h +59 -0
  378. package/deps/librdkafka/src/rdkafka_telemetry_encode.c +993 -0
  379. package/deps/librdkafka/src/rdkafka_telemetry_encode.h +301 -0
  380. package/deps/librdkafka/src/rdkafka_timer.c +402 -0
  381. package/deps/librdkafka/src/rdkafka_timer.h +117 -0
  382. package/deps/librdkafka/src/rdkafka_topic.c +2078 -0
  383. package/deps/librdkafka/src/rdkafka_topic.h +328 -0
  384. package/deps/librdkafka/src/rdkafka_transport.c +1297 -0
  385. package/deps/librdkafka/src/rdkafka_transport.h +94 -0
  386. package/deps/librdkafka/src/rdkafka_transport_int.h +100 -0
  387. package/deps/librdkafka/src/rdkafka_txnmgr.c +3251 -0
  388. package/deps/librdkafka/src/rdkafka_txnmgr.h +171 -0
  389. package/deps/librdkafka/src/rdkafka_zstd.c +226 -0
  390. package/deps/librdkafka/src/rdkafka_zstd.h +57 -0
  391. package/deps/librdkafka/src/rdlist.c +576 -0
  392. package/deps/librdkafka/src/rdlist.h +434 -0
  393. package/deps/librdkafka/src/rdlog.c +89 -0
  394. package/deps/librdkafka/src/rdlog.h +41 -0
  395. package/deps/librdkafka/src/rdmap.c +503 -0
  396. package/deps/librdkafka/src/rdmap.h +492 -0
  397. package/deps/librdkafka/src/rdmurmur2.c +167 -0
  398. package/deps/librdkafka/src/rdmurmur2.h +35 -0
  399. package/deps/librdkafka/src/rdports.c +61 -0
  400. package/deps/librdkafka/src/rdports.h +38 -0
  401. package/deps/librdkafka/src/rdposix.h +250 -0
  402. package/deps/librdkafka/src/rdrand.c +70 -0
  403. package/deps/librdkafka/src/rdrand.h +43 -0
  404. package/deps/librdkafka/src/rdregex.c +156 -0
  405. package/deps/librdkafka/src/rdregex.h +43 -0
  406. package/deps/librdkafka/src/rdsignal.h +57 -0
  407. package/deps/librdkafka/src/rdstring.c +645 -0
  408. package/deps/librdkafka/src/rdstring.h +98 -0
  409. package/deps/librdkafka/src/rdsysqueue.h +404 -0
  410. package/deps/librdkafka/src/rdtime.h +325 -0
  411. package/deps/librdkafka/src/rdtypes.h +86 -0
  412. package/deps/librdkafka/src/rdunittest.c +532 -0
  413. package/deps/librdkafka/src/rdunittest.h +230 -0
  414. package/deps/librdkafka/src/rdvarint.c +134 -0
  415. package/deps/librdkafka/src/rdvarint.h +165 -0
  416. package/deps/librdkafka/src/rdwin32.h +382 -0
  417. package/deps/librdkafka/src/rdxxhash.c +1030 -0
  418. package/deps/librdkafka/src/rdxxhash.h +328 -0
  419. package/deps/librdkafka/src/regexp.c +1347 -0
  420. package/deps/librdkafka/src/regexp.h +41 -0
  421. package/deps/librdkafka/src/snappy.c +1866 -0
  422. package/deps/librdkafka/src/snappy.h +62 -0
  423. package/deps/librdkafka/src/snappy_compat.h +138 -0
  424. package/deps/librdkafka/src/statistics_schema.json +444 -0
  425. package/deps/librdkafka/src/tinycthread.c +932 -0
  426. package/deps/librdkafka/src/tinycthread.h +503 -0
  427. package/deps/librdkafka/src/tinycthread_extra.c +175 -0
  428. package/deps/librdkafka/src/tinycthread_extra.h +208 -0
  429. package/deps/librdkafka/src/win32_config.h +58 -0
  430. package/deps/librdkafka/src-cpp/CMakeLists.txt +90 -0
  431. package/deps/librdkafka/src-cpp/ConfImpl.cpp +84 -0
  432. package/deps/librdkafka/src-cpp/ConsumerImpl.cpp +244 -0
  433. package/deps/librdkafka/src-cpp/HandleImpl.cpp +436 -0
  434. package/deps/librdkafka/src-cpp/HeadersImpl.cpp +48 -0
  435. package/deps/librdkafka/src-cpp/KafkaConsumerImpl.cpp +296 -0
  436. package/deps/librdkafka/src-cpp/Makefile +55 -0
  437. package/deps/librdkafka/src-cpp/MessageImpl.cpp +38 -0
  438. package/deps/librdkafka/src-cpp/MetadataImpl.cpp +170 -0
  439. package/deps/librdkafka/src-cpp/ProducerImpl.cpp +197 -0
  440. package/deps/librdkafka/src-cpp/QueueImpl.cpp +70 -0
  441. package/deps/librdkafka/src-cpp/README.md +16 -0
  442. package/deps/librdkafka/src-cpp/RdKafka.cpp +59 -0
  443. package/deps/librdkafka/src-cpp/TopicImpl.cpp +124 -0
  444. package/deps/librdkafka/src-cpp/TopicPartitionImpl.cpp +57 -0
  445. package/deps/librdkafka/src-cpp/rdkafkacpp.h +3771 -0
  446. package/deps/librdkafka/src-cpp/rdkafkacpp_int.h +1641 -0
  447. package/deps/librdkafka/tests/0000-unittests.c +72 -0
  448. package/deps/librdkafka/tests/0001-multiobj.c +98 -0
  449. package/deps/librdkafka/tests/0002-unkpart.c +244 -0
  450. package/deps/librdkafka/tests/0003-msgmaxsize.c +173 -0
  451. package/deps/librdkafka/tests/0004-conf.c +867 -0
  452. package/deps/librdkafka/tests/0005-order.c +133 -0
  453. package/deps/librdkafka/tests/0006-symbols.c +163 -0
  454. package/deps/librdkafka/tests/0007-autotopic.c +136 -0
  455. package/deps/librdkafka/tests/0008-reqacks.c +179 -0
  456. package/deps/librdkafka/tests/0009-mock_cluster.c +96 -0
  457. package/deps/librdkafka/tests/0011-produce_batch.c +752 -0
  458. package/deps/librdkafka/tests/0012-produce_consume.c +537 -0
  459. package/deps/librdkafka/tests/0013-null-msgs.c +473 -0
  460. package/deps/librdkafka/tests/0014-reconsume-191.c +512 -0
  461. package/deps/librdkafka/tests/0015-offset_seeks.c +172 -0
  462. package/deps/librdkafka/tests/0016-client_swname.c +166 -0
  463. package/deps/librdkafka/tests/0017-compression.c +142 -0
  464. package/deps/librdkafka/tests/0018-cgrp_term.c +333 -0
  465. package/deps/librdkafka/tests/0019-list_groups.c +289 -0
  466. package/deps/librdkafka/tests/0020-destroy_hang.c +162 -0
  467. package/deps/librdkafka/tests/0021-rkt_destroy.c +71 -0
  468. package/deps/librdkafka/tests/0022-consume_batch.c +276 -0
  469. package/deps/librdkafka/tests/0025-timers.c +147 -0
  470. package/deps/librdkafka/tests/0026-consume_pause.c +549 -0
  471. package/deps/librdkafka/tests/0028-long_topicnames.c +79 -0
  472. package/deps/librdkafka/tests/0029-assign_offset.c +204 -0
  473. package/deps/librdkafka/tests/0030-offset_commit.c +589 -0
  474. package/deps/librdkafka/tests/0031-get_offsets.c +235 -0
  475. package/deps/librdkafka/tests/0033-regex_subscribe.c +520 -0
  476. package/deps/librdkafka/tests/0034-offset_reset.c +377 -0
  477. package/deps/librdkafka/tests/0035-api_version.c +73 -0
  478. package/deps/librdkafka/tests/0036-partial_fetch.c +86 -0
  479. package/deps/librdkafka/tests/0037-destroy_hang_local.c +85 -0
  480. package/deps/librdkafka/tests/0038-performance.c +120 -0
  481. package/deps/librdkafka/tests/0039-event.c +284 -0
  482. package/deps/librdkafka/tests/0040-io_event.c +251 -0
  483. package/deps/librdkafka/tests/0041-fetch_max_bytes.c +96 -0
  484. package/deps/librdkafka/tests/0042-many_topics.c +252 -0
  485. package/deps/librdkafka/tests/0043-no_connection.c +77 -0
  486. package/deps/librdkafka/tests/0044-partition_cnt.c +93 -0
  487. package/deps/librdkafka/tests/0045-subscribe_update.c +746 -0
  488. package/deps/librdkafka/tests/0046-rkt_cache.c +65 -0
  489. package/deps/librdkafka/tests/0047-partial_buf_tmout.c +97 -0
  490. package/deps/librdkafka/tests/0048-partitioner.c +283 -0
  491. package/deps/librdkafka/tests/0049-consume_conn_close.c +162 -0
  492. package/deps/librdkafka/tests/0050-subscribe_adds.c +144 -0
  493. package/deps/librdkafka/tests/0051-assign_adds.c +125 -0
  494. package/deps/librdkafka/tests/0052-msg_timestamps.c +220 -0
  495. package/deps/librdkafka/tests/0053-stats_cb.cpp +535 -0
  496. package/deps/librdkafka/tests/0054-offset_time.cpp +236 -0
  497. package/deps/librdkafka/tests/0055-producer_latency.c +366 -0
  498. package/deps/librdkafka/tests/0056-balanced_group_mt.c +311 -0
  499. package/deps/librdkafka/tests/0057-invalid_topic.cpp +112 -0
  500. package/deps/librdkafka/tests/0058-log.cpp +123 -0
  501. package/deps/librdkafka/tests/0059-bsearch.cpp +237 -0
  502. package/deps/librdkafka/tests/0060-op_prio.cpp +163 -0
  503. package/deps/librdkafka/tests/0061-consumer_lag.cpp +275 -0
  504. package/deps/librdkafka/tests/0062-stats_event.c +126 -0
  505. package/deps/librdkafka/tests/0063-clusterid.cpp +180 -0
  506. package/deps/librdkafka/tests/0064-interceptors.c +481 -0
  507. package/deps/librdkafka/tests/0065-yield.cpp +140 -0
  508. package/deps/librdkafka/tests/0066-plugins.cpp +129 -0
  509. package/deps/librdkafka/tests/0067-empty_topic.cpp +148 -0
  510. package/deps/librdkafka/tests/0068-produce_timeout.c +138 -0
  511. package/deps/librdkafka/tests/0069-consumer_add_parts.c +123 -0
  512. package/deps/librdkafka/tests/0070-null_empty.cpp +197 -0
  513. package/deps/librdkafka/tests/0072-headers_ut.c +448 -0
  514. package/deps/librdkafka/tests/0073-headers.c +381 -0
  515. package/deps/librdkafka/tests/0074-producev.c +87 -0
  516. package/deps/librdkafka/tests/0075-retry.c +253 -0
  517. package/deps/librdkafka/tests/0076-produce_retry.c +450 -0
  518. package/deps/librdkafka/tests/0077-compaction.c +357 -0
  519. package/deps/librdkafka/tests/0078-c_from_cpp.cpp +96 -0
  520. package/deps/librdkafka/tests/0079-fork.c +93 -0
  521. package/deps/librdkafka/tests/0080-admin_ut.c +3065 -0
  522. package/deps/librdkafka/tests/0081-admin.c +5333 -0
  523. package/deps/librdkafka/tests/0082-fetch_max_bytes.cpp +133 -0
  524. package/deps/librdkafka/tests/0083-cb_event.c +228 -0
  525. package/deps/librdkafka/tests/0084-destroy_flags.c +212 -0
  526. package/deps/librdkafka/tests/0085-headers.cpp +388 -0
  527. package/deps/librdkafka/tests/0086-purge.c +335 -0
  528. package/deps/librdkafka/tests/0088-produce_metadata_timeout.c +162 -0
  529. package/deps/librdkafka/tests/0089-max_poll_interval.c +506 -0
  530. package/deps/librdkafka/tests/0090-idempotence.c +172 -0
  531. package/deps/librdkafka/tests/0091-max_poll_interval_timeout.c +297 -0
  532. package/deps/librdkafka/tests/0092-mixed_msgver.c +97 -0
  533. package/deps/librdkafka/tests/0093-holb.c +197 -0
  534. package/deps/librdkafka/tests/0094-idempotence_msg_timeout.c +230 -0
  535. package/deps/librdkafka/tests/0095-all_brokers_down.cpp +122 -0
  536. package/deps/librdkafka/tests/0097-ssl_verify.cpp +466 -0
  537. package/deps/librdkafka/tests/0098-consumer-txn.cpp +1218 -0
  538. package/deps/librdkafka/tests/0099-commit_metadata.c +194 -0
  539. package/deps/librdkafka/tests/0100-thread_interceptors.cpp +195 -0
  540. package/deps/librdkafka/tests/0101-fetch-from-follower.cpp +446 -0
  541. package/deps/librdkafka/tests/0102-static_group_rebalance.c +535 -0
  542. package/deps/librdkafka/tests/0103-transactions.c +1383 -0
  543. package/deps/librdkafka/tests/0104-fetch_from_follower_mock.c +624 -0
  544. package/deps/librdkafka/tests/0105-transactions_mock.c +3923 -0
  545. package/deps/librdkafka/tests/0106-cgrp_sess_timeout.c +297 -0
  546. package/deps/librdkafka/tests/0107-topic_recreate.c +259 -0
  547. package/deps/librdkafka/tests/0109-auto_create_topics.cpp +218 -0
  548. package/deps/librdkafka/tests/0110-batch_size.cpp +183 -0
  549. package/deps/librdkafka/tests/0111-delay_create_topics.cpp +127 -0
  550. package/deps/librdkafka/tests/0112-assign_unknown_part.c +98 -0
  551. package/deps/librdkafka/tests/0113-cooperative_rebalance.cpp +3329 -0
  552. package/deps/librdkafka/tests/0114-sticky_partitioning.cpp +176 -0
  553. package/deps/librdkafka/tests/0115-producer_auth.cpp +179 -0
  554. package/deps/librdkafka/tests/0116-kafkaconsumer_close.cpp +214 -0
  555. package/deps/librdkafka/tests/0117-mock_errors.c +321 -0
  556. package/deps/librdkafka/tests/0118-commit_rebalance.c +121 -0
  557. package/deps/librdkafka/tests/0119-consumer_auth.cpp +148 -0
  558. package/deps/librdkafka/tests/0120-asymmetric_subscription.c +180 -0
  559. package/deps/librdkafka/tests/0121-clusterid.c +115 -0
  560. package/deps/librdkafka/tests/0122-buffer_cleaning_after_rebalance.c +227 -0
  561. package/deps/librdkafka/tests/0123-connections_max_idle.c +98 -0
  562. package/deps/librdkafka/tests/0124-openssl_invalid_engine.c +69 -0
  563. package/deps/librdkafka/tests/0125-immediate_flush.c +144 -0
  564. package/deps/librdkafka/tests/0126-oauthbearer_oidc.c +213 -0
  565. package/deps/librdkafka/tests/0127-fetch_queue_backoff.cpp +165 -0
  566. package/deps/librdkafka/tests/0128-sasl_callback_queue.cpp +125 -0
  567. package/deps/librdkafka/tests/0129-fetch_aborted_msgs.c +78 -0
  568. package/deps/librdkafka/tests/0130-store_offsets.c +178 -0
  569. package/deps/librdkafka/tests/0131-connect_timeout.c +81 -0
  570. package/deps/librdkafka/tests/0132-strategy_ordering.c +171 -0
  571. package/deps/librdkafka/tests/0133-ssl_keys.c +128 -0
  572. package/deps/librdkafka/tests/0134-ssl_provider.c +92 -0
  573. package/deps/librdkafka/tests/0135-sasl_credentials.cpp +143 -0
  574. package/deps/librdkafka/tests/0136-resolve_cb.c +181 -0
  575. package/deps/librdkafka/tests/0137-barrier_batch_consume.c +609 -0
  576. package/deps/librdkafka/tests/0138-admin_mock.c +281 -0
  577. package/deps/librdkafka/tests/0139-offset_validation_mock.c +442 -0
  578. package/deps/librdkafka/tests/0140-commit_metadata.cpp +108 -0
  579. package/deps/librdkafka/tests/0142-reauthentication.c +495 -0
  580. package/deps/librdkafka/tests/0143-exponential_backoff_mock.c +553 -0
  581. package/deps/librdkafka/tests/0144-idempotence_mock.c +373 -0
  582. package/deps/librdkafka/tests/0145-pause_resume_mock.c +119 -0
  583. package/deps/librdkafka/tests/0146-metadata_mock.c +444 -0
  584. package/deps/librdkafka/tests/0150-telemetry_mock.c +648 -0
  585. package/deps/librdkafka/tests/1000-unktopic.c +164 -0
  586. package/deps/librdkafka/tests/8000-idle.cpp +60 -0
  587. package/deps/librdkafka/tests/8001-fetch_from_follower_mock_manual.c +113 -0
  588. package/deps/librdkafka/tests/CMakeLists.txt +164 -0
  589. package/deps/librdkafka/tests/LibrdkafkaTestApp.py +266 -0
  590. package/deps/librdkafka/tests/Makefile +182 -0
  591. package/deps/librdkafka/tests/README.md +509 -0
  592. package/deps/librdkafka/tests/autotest.sh +33 -0
  593. package/deps/librdkafka/tests/backtrace.gdb +30 -0
  594. package/deps/librdkafka/tests/broker_version_tests.py +315 -0
  595. package/deps/librdkafka/tests/buildbox.sh +17 -0
  596. package/deps/librdkafka/tests/cleanup-checker-tests.sh +20 -0
  597. package/deps/librdkafka/tests/cluster_testing.py +191 -0
  598. package/deps/librdkafka/tests/delete-test-topics.sh +56 -0
  599. package/deps/librdkafka/tests/fixtures/ssl/Makefile +8 -0
  600. package/deps/librdkafka/tests/fixtures/ssl/README.md +13 -0
  601. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.p12 +0 -0
  602. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.pem +109 -0
  603. package/deps/librdkafka/tests/fixtures/ssl/client2.key +34 -0
  604. package/deps/librdkafka/tests/fixtures/ssl/create_keys.sh +93 -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 +12 -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-consumer-tests.sh +16 -0
  630. package/deps/librdkafka/tests/run-producer-tests.sh +16 -0
  631. package/deps/librdkafka/tests/run-test.sh +140 -0
  632. package/deps/librdkafka/tests/rusage.c +249 -0
  633. package/deps/librdkafka/tests/sasl_test.py +289 -0
  634. package/deps/librdkafka/tests/scenarios/README.md +6 -0
  635. package/deps/librdkafka/tests/scenarios/ak23.json +6 -0
  636. package/deps/librdkafka/tests/scenarios/default.json +5 -0
  637. package/deps/librdkafka/tests/scenarios/noautocreate.json +5 -0
  638. package/deps/librdkafka/tests/sockem.c +801 -0
  639. package/deps/librdkafka/tests/sockem.h +85 -0
  640. package/deps/librdkafka/tests/sockem_ctrl.c +145 -0
  641. package/deps/librdkafka/tests/sockem_ctrl.h +61 -0
  642. package/deps/librdkafka/tests/test.c +7318 -0
  643. package/deps/librdkafka/tests/test.conf.example +27 -0
  644. package/deps/librdkafka/tests/test.h +979 -0
  645. package/deps/librdkafka/tests/testcpp.cpp +126 -0
  646. package/deps/librdkafka/tests/testcpp.h +360 -0
  647. package/deps/librdkafka/tests/testshared.h +402 -0
  648. package/deps/librdkafka/tests/tools/README.md +4 -0
  649. package/deps/librdkafka/tests/tools/stats/README.md +21 -0
  650. package/deps/librdkafka/tests/tools/stats/filter.jq +42 -0
  651. package/deps/librdkafka/tests/tools/stats/graph.py +150 -0
  652. package/deps/librdkafka/tests/tools/stats/requirements.txt +3 -0
  653. package/deps/librdkafka/tests/tools/stats/to_csv.py +124 -0
  654. package/deps/librdkafka/tests/until-fail.sh +87 -0
  655. package/deps/librdkafka/tests/xxxx-assign_partition.c +122 -0
  656. package/deps/librdkafka/tests/xxxx-metadata.cpp +159 -0
  657. package/deps/librdkafka/vcpkg.json +23 -0
  658. package/deps/librdkafka/win32/README.md +5 -0
  659. package/deps/librdkafka/win32/build-package.bat +3 -0
  660. package/deps/librdkafka/win32/build.bat +19 -0
  661. package/deps/librdkafka/win32/common.vcxproj +84 -0
  662. package/deps/librdkafka/win32/install-openssl.ps1 +33 -0
  663. package/deps/librdkafka/win32/interceptor_test/interceptor_test.vcxproj +87 -0
  664. package/deps/librdkafka/win32/librdkafka.autopkg.template +54 -0
  665. package/deps/librdkafka/win32/librdkafka.master.testing.targets +13 -0
  666. package/deps/librdkafka/win32/librdkafka.sln +226 -0
  667. package/deps/librdkafka/win32/librdkafka.vcxproj +276 -0
  668. package/deps/librdkafka/win32/librdkafkacpp/librdkafkacpp.vcxproj +104 -0
  669. package/deps/librdkafka/win32/msbuild.ps1 +15 -0
  670. package/deps/librdkafka/win32/openssl_engine_example/openssl_engine_example.vcxproj +132 -0
  671. package/deps/librdkafka/win32/package-zip.ps1 +46 -0
  672. package/deps/librdkafka/win32/packages/repositories.config +4 -0
  673. package/deps/librdkafka/win32/push-package.bat +4 -0
  674. package/deps/librdkafka/win32/rdkafka_complex_consumer_example_cpp/rdkafka_complex_consumer_example_cpp.vcxproj +67 -0
  675. package/deps/librdkafka/win32/rdkafka_example/rdkafka_example.vcxproj +97 -0
  676. package/deps/librdkafka/win32/rdkafka_performance/rdkafka_performance.vcxproj +97 -0
  677. package/deps/librdkafka/win32/setup-msys2.ps1 +31 -0
  678. package/deps/librdkafka/win32/setup-vcpkg.ps1 +12 -0
  679. package/deps/librdkafka/win32/tests/test.conf.example +25 -0
  680. package/deps/librdkafka/win32/tests/tests.vcxproj +247 -0
  681. package/deps/librdkafka/win32/win_ssl_cert_store/win_ssl_cert_store.vcxproj +132 -0
  682. package/deps/librdkafka/win32/wingetopt.c +564 -0
  683. package/deps/librdkafka/win32/wingetopt.h +100 -0
  684. package/deps/librdkafka/win32/wintime.h +33 -0
  685. package/deps/librdkafka.gyp +62 -0
  686. package/deps/windows-install.py +73 -0
  687. package/docker-compose.yml +23 -0
  688. package/e2e/admin.spec.js +187 -0
  689. package/e2e/both.spec.js +726 -0
  690. package/e2e/consumer.spec.js +386 -0
  691. package/e2e/groups.spec.js +132 -0
  692. package/e2e/listener.js +51 -0
  693. package/e2e/producer-transaction.spec.js +333 -0
  694. package/e2e/producer.spec.js +283 -0
  695. package/errors.d.ts +347 -0
  696. package/examples/consumer-flow.md +76 -0
  697. package/examples/consumer.md +45 -0
  698. package/examples/docker-alpine.md +30 -0
  699. package/examples/high-level-producer.md +32 -0
  700. package/examples/metadata.md +27 -0
  701. package/examples/oauthbearer-default-flow.md +80 -0
  702. package/examples/producer-cluster.md +103 -0
  703. package/examples/producer.md +73 -0
  704. package/index.d.ts +357 -0
  705. package/lib/admin.js +233 -0
  706. package/lib/client.js +578 -0
  707. package/lib/error.js +472 -0
  708. package/lib/index.js +34 -0
  709. package/lib/kafka-consumer-stream.js +397 -0
  710. package/lib/kafka-consumer.js +698 -0
  711. package/lib/producer/high-level-producer.js +323 -0
  712. package/lib/producer-stream.js +307 -0
  713. package/lib/producer.js +375 -0
  714. package/lib/tools/ref-counter.js +52 -0
  715. package/lib/topic-partition.js +88 -0
  716. package/lib/topic.js +42 -0
  717. package/lib/util.js +29 -0
  718. package/librdkafka.js +12 -0
  719. package/make_docs.sh +59 -0
  720. package/package.json +53 -0
  721. package/renovate.json +30 -0
  722. package/run_docker.sh +42 -0
  723. package/src/admin.cc +624 -0
  724. package/src/admin.h +78 -0
  725. package/src/binding.cc +73 -0
  726. package/src/binding.h +25 -0
  727. package/src/callbacks.cc +614 -0
  728. package/src/callbacks.h +265 -0
  729. package/src/common.cc +582 -0
  730. package/src/common.h +134 -0
  731. package/src/config.cc +167 -0
  732. package/src/config.h +45 -0
  733. package/src/connection.cc +482 -0
  734. package/src/connection.h +108 -0
  735. package/src/errors.cc +96 -0
  736. package/src/errors.h +53 -0
  737. package/src/kafka-consumer.cc +1457 -0
  738. package/src/kafka-consumer.h +136 -0
  739. package/src/per-isolate-data.cc +57 -0
  740. package/src/per-isolate-data.h +39 -0
  741. package/src/producer.cc +873 -0
  742. package/src/producer.h +125 -0
  743. package/src/topic.cc +180 -0
  744. package/src/topic.h +56 -0
  745. package/src/workers.cc +1245 -0
  746. package/src/workers.h +511 -0
  747. package/test/binding.spec.js +66 -0
  748. package/test/consumer.spec.js +91 -0
  749. package/test/error.spec.js +8 -0
  750. package/test/index.spec.js +8 -0
  751. package/test/kafka-consumer-stream.spec.js +298 -0
  752. package/test/kafka-consumer-worker.js +51 -0
  753. package/test/kafka-consumer.spec.js +86 -0
  754. package/test/mocha.opts +1 -0
  755. package/test/mock.js +55 -0
  756. package/test/producer/high-level-producer.spec.js +496 -0
  757. package/test/producer-stream.spec.js +723 -0
  758. package/test/producer-worker.js +53 -0
  759. package/test/producer.spec.js +120 -0
  760. package/test/tools/ref-counter.spec.js +53 -0
  761. package/test/topic-partition.spec.js +104 -0
  762. package/test/util.spec.js +46 -0
  763. package/util/configure.js +30 -0
  764. package/util/get-env.js +6 -0
  765. package/util/test-compile.js +11 -0
  766. package/util/test-producer-delivery.js +100 -0
  767. package/win_install.ps1 +2 -0
@@ -0,0 +1,3923 @@
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
+ #include "test.h"
30
+
31
+ #include "rdkafka.h"
32
+
33
+ #include "../src/rdkafka_proto.h"
34
+ #include "../src/rdstring.h"
35
+ #include "../src/rdunittest.h"
36
+
37
+ #include <stdarg.h>
38
+
39
+
40
+ /**
41
+ * @name Producer transaction tests using the mock cluster
42
+ *
43
+ */
44
+
45
+
46
+ static int allowed_error;
47
+
48
+ /**
49
+ * @brief Decide what error_cb's will cause the test to fail.
50
+ */
51
+ static int
52
+ error_is_fatal_cb(rd_kafka_t *rk, rd_kafka_resp_err_t err, const char *reason) {
53
+ if (err == allowed_error ||
54
+ /* If transport errors are allowed then it is likely
55
+ * that we'll also see ALL_BROKERS_DOWN. */
56
+ (allowed_error == RD_KAFKA_RESP_ERR__TRANSPORT &&
57
+ err == RD_KAFKA_RESP_ERR__ALL_BROKERS_DOWN)) {
58
+ TEST_SAY("Ignoring allowed error: %s: %s\n",
59
+ rd_kafka_err2name(err), reason);
60
+ return 0;
61
+ }
62
+ return 1;
63
+ }
64
+
65
+
66
+ static rd_kafka_resp_err_t (*on_response_received_cb)(rd_kafka_t *rk,
67
+ int sockfd,
68
+ const char *brokername,
69
+ int32_t brokerid,
70
+ int16_t ApiKey,
71
+ int16_t ApiVersion,
72
+ int32_t CorrId,
73
+ size_t size,
74
+ int64_t rtt,
75
+ rd_kafka_resp_err_t err,
76
+ void *ic_opaque);
77
+
78
+ /**
79
+ * @brief Simple on_response_received interceptor that simply calls the
80
+ * sub-test's on_response_received_cb function, if set.
81
+ */
82
+ static rd_kafka_resp_err_t
83
+ on_response_received_trampoline(rd_kafka_t *rk,
84
+ int sockfd,
85
+ const char *brokername,
86
+ int32_t brokerid,
87
+ int16_t ApiKey,
88
+ int16_t ApiVersion,
89
+ int32_t CorrId,
90
+ size_t size,
91
+ int64_t rtt,
92
+ rd_kafka_resp_err_t err,
93
+ void *ic_opaque) {
94
+ TEST_ASSERT(on_response_received_cb != NULL, "");
95
+ return on_response_received_cb(rk, sockfd, brokername, brokerid, ApiKey,
96
+ ApiVersion, CorrId, size, rtt, err,
97
+ ic_opaque);
98
+ }
99
+
100
+
101
+ /**
102
+ * @brief on_new interceptor to add an on_response_received interceptor.
103
+ */
104
+ static rd_kafka_resp_err_t on_new_producer(rd_kafka_t *rk,
105
+ const rd_kafka_conf_t *conf,
106
+ void *ic_opaque,
107
+ char *errstr,
108
+ size_t errstr_size) {
109
+ rd_kafka_resp_err_t err = RD_KAFKA_RESP_ERR_NO_ERROR;
110
+
111
+ if (on_response_received_cb)
112
+ err = rd_kafka_interceptor_add_on_response_received(
113
+ rk, "on_response_received", on_response_received_trampoline,
114
+ ic_opaque);
115
+
116
+ return err;
117
+ }
118
+
119
+
120
+ /**
121
+ * @brief Create a transactional producer and a mock cluster.
122
+ *
123
+ * The var-arg list is a NULL-terminated list of
124
+ * (const char *key, const char *value) config properties.
125
+ *
126
+ * Special keys:
127
+ * "on_response_received", "" - enable the on_response_received_cb
128
+ * interceptor,
129
+ * which must be assigned prior to
130
+ * calling create_tnx_producer().
131
+ */
132
+ static RD_SENTINEL rd_kafka_t *
133
+ create_txn_producer(rd_kafka_mock_cluster_t **mclusterp,
134
+ const char *transactional_id,
135
+ int broker_cnt,
136
+ ...) {
137
+ rd_kafka_conf_t *conf;
138
+ rd_kafka_t *rk;
139
+ char numstr[8];
140
+ va_list ap;
141
+ const char *key;
142
+ rd_bool_t add_interceptors = rd_false;
143
+
144
+ rd_snprintf(numstr, sizeof(numstr), "%d", broker_cnt);
145
+
146
+ test_conf_init(&conf, NULL, 60);
147
+
148
+ test_conf_set(conf, "transactional.id", transactional_id);
149
+ /* When mock brokers are set to down state they're still binding
150
+ * the port, just not listening to it, which makes connection attempts
151
+ * stall until socket.connection.setup.timeout.ms expires.
152
+ * To speed up detection of brokers being down we reduce this timeout
153
+ * to just a couple of seconds. */
154
+ test_conf_set(conf, "socket.connection.setup.timeout.ms", "5000");
155
+ /* Speed up reconnects */
156
+ test_conf_set(conf, "reconnect.backoff.max.ms", "2000");
157
+ test_conf_set(conf, "test.mock.num.brokers", numstr);
158
+ rd_kafka_conf_set_dr_msg_cb(conf, test_dr_msg_cb);
159
+
160
+ test_curr->ignore_dr_err = rd_false;
161
+
162
+ va_start(ap, broker_cnt);
163
+ while ((key = va_arg(ap, const char *))) {
164
+ if (!strcmp(key, "on_response_received")) {
165
+ add_interceptors = rd_true;
166
+ (void)va_arg(ap, const char *);
167
+ } else {
168
+ test_conf_set(conf, key, va_arg(ap, const char *));
169
+ }
170
+ }
171
+ va_end(ap);
172
+
173
+ /* Add an on_.. interceptors */
174
+ if (add_interceptors)
175
+ rd_kafka_conf_interceptor_add_on_new(conf, "on_new_producer",
176
+ on_new_producer, NULL);
177
+
178
+ rk = test_create_handle(RD_KAFKA_PRODUCER, conf);
179
+
180
+ if (mclusterp) {
181
+ *mclusterp = rd_kafka_handle_mock_cluster(rk);
182
+ TEST_ASSERT(*mclusterp, "failed to create mock cluster");
183
+
184
+ /* Create some of the common consumer "input" topics
185
+ * that we must be able to commit to with
186
+ * send_offsets_to_transaction().
187
+ * The number depicts the number of partitions in the topic. */
188
+ TEST_CALL_ERR__(
189
+ rd_kafka_mock_topic_create(*mclusterp, "srctopic4", 4, 1));
190
+ TEST_CALL_ERR__(rd_kafka_mock_topic_create(
191
+ *mclusterp, "srctopic64", 64, 1));
192
+ }
193
+
194
+ return rk;
195
+ }
196
+
197
+
198
+ /**
199
+ * @brief Test recoverable errors using mock broker error injections
200
+ * and code coverage checks.
201
+ */
202
+ static void do_test_txn_recoverable_errors(void) {
203
+ rd_kafka_t *rk;
204
+ rd_kafka_mock_cluster_t *mcluster;
205
+ rd_kafka_topic_partition_list_t *offsets;
206
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
207
+ const char *groupid = "myGroupId";
208
+ const char *txnid = "myTxnId";
209
+
210
+ SUB_TEST_QUICK();
211
+
212
+ rk = create_txn_producer(&mcluster, txnid, 3, "batch.num.messages", "1",
213
+ NULL);
214
+
215
+ /* Make sure transaction and group coordinators are different.
216
+ * This verifies that AddOffsetsToTxnRequest isn't sent to the
217
+ * transaction coordinator but the group coordinator. */
218
+ rd_kafka_mock_coordinator_set(mcluster, "group", groupid, 1);
219
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid, 2);
220
+
221
+ /*
222
+ * Inject som InitProducerId errors that causes retries
223
+ */
224
+ rd_kafka_mock_push_request_errors(
225
+ mcluster, RD_KAFKAP_InitProducerId, 3,
226
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
227
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
228
+ RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS);
229
+
230
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
231
+
232
+ (void)RD_UT_COVERAGE_CHECK(0); /* idemp_request_pid_failed(retry) */
233
+ (void)RD_UT_COVERAGE_CHECK(1); /* txn_idemp_state_change(READY) */
234
+
235
+ /*
236
+ * Start a transaction
237
+ */
238
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
239
+
240
+
241
+ /* Produce a message without error first */
242
+ TEST_CALL_ERR__(rd_kafka_producev(
243
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
244
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
245
+
246
+ rd_kafka_flush(rk, -1);
247
+
248
+ /*
249
+ * Produce a message, let it fail with a non-idempo/non-txn
250
+ * retryable error
251
+ */
252
+ rd_kafka_mock_push_request_errors(
253
+ mcluster, RD_KAFKAP_Produce, 1,
254
+ RD_KAFKA_RESP_ERR_NOT_ENOUGH_REPLICAS);
255
+
256
+ TEST_CALL_ERR__(rd_kafka_producev(
257
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
258
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
259
+
260
+ /* Make sure messages are produced */
261
+ rd_kafka_flush(rk, -1);
262
+
263
+ /*
264
+ * Send some arbitrary offsets, first with some failures, then
265
+ * succeed.
266
+ */
267
+ offsets = rd_kafka_topic_partition_list_new(4);
268
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
269
+ rd_kafka_topic_partition_list_add(offsets, "srctopic64", 39)->offset =
270
+ 999999111;
271
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 0)->offset =
272
+ 999;
273
+ rd_kafka_topic_partition_list_add(offsets, "srctopic64", 19)->offset =
274
+ 123456789;
275
+
276
+ rd_kafka_mock_push_request_errors(
277
+ mcluster, RD_KAFKAP_AddPartitionsToTxn, 1,
278
+ RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART);
279
+
280
+ rd_kafka_mock_push_request_errors(
281
+ mcluster, RD_KAFKAP_TxnOffsetCommit, 2,
282
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
283
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS);
284
+
285
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
286
+
287
+ TEST_CALL_ERROR__(
288
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
289
+
290
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
291
+ rd_kafka_topic_partition_list_destroy(offsets);
292
+
293
+ /*
294
+ * Commit transaction, first with som failures, then succeed.
295
+ */
296
+ rd_kafka_mock_push_request_errors(
297
+ mcluster, RD_KAFKAP_EndTxn, 3,
298
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
299
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
300
+ RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS);
301
+
302
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, 5000));
303
+
304
+ /* All done */
305
+
306
+ rd_kafka_destroy(rk);
307
+
308
+ SUB_TEST_PASS();
309
+ }
310
+
311
+
312
+ /**
313
+ * @brief KIP-360: Test that fatal idempotence errors triggers abortable
314
+ * transaction errors and that the producer can recover.
315
+ */
316
+ static void do_test_txn_fatal_idempo_errors(void) {
317
+ rd_kafka_t *rk;
318
+ rd_kafka_mock_cluster_t *mcluster;
319
+ rd_kafka_error_t *error;
320
+ const char *txnid = "myTxnId";
321
+
322
+ SUB_TEST_QUICK();
323
+
324
+ rk = create_txn_producer(&mcluster, txnid, 3, "batch.num.messages", "1",
325
+ NULL);
326
+
327
+ test_curr->ignore_dr_err = rd_true;
328
+ test_curr->is_fatal_cb = error_is_fatal_cb;
329
+ allowed_error = RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID;
330
+
331
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
332
+
333
+ /*
334
+ * Start a transaction
335
+ */
336
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
337
+
338
+
339
+ /* Produce a message without error first */
340
+ TEST_CALL_ERR__(rd_kafka_producev(
341
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
342
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
343
+
344
+ /* Produce a message, let it fail with a fatal idempo error. */
345
+ rd_kafka_mock_push_request_errors(
346
+ mcluster, RD_KAFKAP_Produce, 1,
347
+ RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID);
348
+
349
+ TEST_CALL_ERR__(rd_kafka_producev(
350
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
351
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
352
+
353
+ /* Commit the transaction, should fail */
354
+ error = rd_kafka_commit_transaction(rk, -1);
355
+ TEST_ASSERT(error != NULL, "Expected commit_transaction() to fail");
356
+
357
+ TEST_SAY("commit_transaction() failed (expectedly): %s\n",
358
+ rd_kafka_error_string(error));
359
+
360
+ TEST_ASSERT(!rd_kafka_error_is_fatal(error),
361
+ "Did not expect fatal error");
362
+ TEST_ASSERT(rd_kafka_error_txn_requires_abort(error),
363
+ "Expected abortable error");
364
+ rd_kafka_error_destroy(error);
365
+
366
+ /* Abort the transaction */
367
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
368
+
369
+ /* Run a new transaction without errors to verify that the
370
+ * producer can recover. */
371
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
372
+
373
+ TEST_CALL_ERR__(rd_kafka_producev(
374
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
375
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
376
+
377
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
378
+
379
+ /* All done */
380
+
381
+ rd_kafka_destroy(rk);
382
+
383
+ allowed_error = RD_KAFKA_RESP_ERR_NO_ERROR;
384
+
385
+ SUB_TEST_PASS();
386
+ }
387
+
388
+
389
+ /**
390
+ * @brief KIP-360: Test that fatal idempotence errors triggers abortable
391
+ * transaction errors, but let the broker-side bumping of the
392
+ * producer PID take longer than the remaining transaction timeout
393
+ * which should raise a retriable error from abort_transaction().
394
+ *
395
+ * @param with_sleep After the first abort sleep longer than it takes to
396
+ * re-init the pid so that the internal state automatically
397
+ * transitions.
398
+ */
399
+ static void do_test_txn_slow_reinit(rd_bool_t with_sleep) {
400
+ rd_kafka_t *rk;
401
+ rd_kafka_mock_cluster_t *mcluster;
402
+ rd_kafka_error_t *error;
403
+ int32_t txn_coord = 2;
404
+ const char *txnid = "myTxnId";
405
+ test_timing_t timing;
406
+
407
+ SUB_TEST("%s sleep", with_sleep ? "with" : "without");
408
+
409
+ rk = create_txn_producer(&mcluster, txnid, 3, "batch.num.messages", "1",
410
+ NULL);
411
+
412
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid,
413
+ txn_coord);
414
+
415
+ test_curr->ignore_dr_err = rd_true;
416
+ test_curr->is_fatal_cb = NULL;
417
+
418
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
419
+
420
+ /*
421
+ * Start a transaction
422
+ */
423
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
424
+
425
+
426
+ /* Produce a message without error first */
427
+ TEST_CALL_ERR__(rd_kafka_producev(
428
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
429
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
430
+
431
+ test_flush(rk, -1);
432
+
433
+ /* Set transaction coordinator latency higher than
434
+ * the abort_transaction() call timeout so that the automatic
435
+ * re-initpid takes longer than abort_transaction(). */
436
+ rd_kafka_mock_broker_push_request_error_rtts(
437
+ mcluster, txn_coord, RD_KAFKAP_InitProducerId, 1,
438
+ RD_KAFKA_RESP_ERR_NO_ERROR, 10000 /*10s*/);
439
+
440
+ /* Produce a message, let it fail with a fatal idempo error. */
441
+ rd_kafka_mock_push_request_errors(
442
+ mcluster, RD_KAFKAP_Produce, 1,
443
+ RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID);
444
+
445
+ TEST_CALL_ERR__(rd_kafka_producev(
446
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
447
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
448
+
449
+
450
+ /* Commit the transaction, should fail */
451
+ TIMING_START(&timing, "commit_transaction(-1)");
452
+ error = rd_kafka_commit_transaction(rk, -1);
453
+ TIMING_STOP(&timing);
454
+ TEST_ASSERT(error != NULL, "Expected commit_transaction() to fail");
455
+
456
+ TEST_SAY("commit_transaction() failed (expectedly): %s\n",
457
+ rd_kafka_error_string(error));
458
+
459
+ TEST_ASSERT(!rd_kafka_error_is_fatal(error),
460
+ "Did not expect fatal error");
461
+ TEST_ASSERT(rd_kafka_error_txn_requires_abort(error),
462
+ "Expected abortable error");
463
+ rd_kafka_error_destroy(error);
464
+
465
+ /* Abort the transaction, should fail with retriable (timeout) error */
466
+ TIMING_START(&timing, "abort_transaction(100)");
467
+ error = rd_kafka_abort_transaction(rk, 100);
468
+ TIMING_STOP(&timing);
469
+ TEST_ASSERT(error != NULL, "Expected abort_transaction() to fail");
470
+
471
+ TEST_SAY("First abort_transaction() failed: %s\n",
472
+ rd_kafka_error_string(error));
473
+ TEST_ASSERT(!rd_kafka_error_is_fatal(error),
474
+ "Did not expect fatal error");
475
+ TEST_ASSERT(rd_kafka_error_is_retriable(error),
476
+ "Expected retriable error");
477
+ rd_kafka_error_destroy(error);
478
+
479
+ if (with_sleep)
480
+ rd_sleep(12);
481
+
482
+ /* Retry abort, should now finish. */
483
+ TEST_SAY("Retrying abort\n");
484
+ TIMING_START(&timing, "abort_transaction(-1)");
485
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
486
+ TIMING_STOP(&timing);
487
+
488
+ /* Run a new transaction without errors to verify that the
489
+ * producer can recover. */
490
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
491
+
492
+ TEST_CALL_ERR__(rd_kafka_producev(
493
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
494
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
495
+
496
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
497
+
498
+ /* All done */
499
+
500
+ rd_kafka_destroy(rk);
501
+
502
+ allowed_error = RD_KAFKA_RESP_ERR_NO_ERROR;
503
+
504
+ SUB_TEST_PASS();
505
+ }
506
+
507
+
508
+
509
+ /**
510
+ * @brief KIP-360: Test that fatal idempotence errors triggers abortable
511
+ * transaction errors, but let the broker-side bumping of the
512
+ * producer PID fail with a fencing error.
513
+ * Should raise a fatal error.
514
+ *
515
+ * @param error_code Which error code InitProducerIdRequest should fail with.
516
+ * Either RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH (older)
517
+ * or RD_KAFKA_RESP_ERR_PRODUCER_FENCED (newer).
518
+ */
519
+ static void do_test_txn_fenced_reinit(rd_kafka_resp_err_t error_code) {
520
+ rd_kafka_t *rk;
521
+ rd_kafka_mock_cluster_t *mcluster;
522
+ rd_kafka_error_t *error;
523
+ int32_t txn_coord = 2;
524
+ const char *txnid = "myTxnId";
525
+ char errstr[512];
526
+ rd_kafka_resp_err_t fatal_err;
527
+
528
+ SUB_TEST_QUICK("With error %s", rd_kafka_err2name(error_code));
529
+
530
+ rk = create_txn_producer(&mcluster, txnid, 3, "batch.num.messages", "1",
531
+ NULL);
532
+
533
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid,
534
+ txn_coord);
535
+
536
+ test_curr->ignore_dr_err = rd_true;
537
+ test_curr->is_fatal_cb = error_is_fatal_cb;
538
+ allowed_error = RD_KAFKA_RESP_ERR__FENCED;
539
+
540
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
541
+
542
+ /*
543
+ * Start a transaction
544
+ */
545
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
546
+
547
+
548
+ /* Produce a message without error first */
549
+ TEST_CALL_ERR__(rd_kafka_producev(
550
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
551
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
552
+
553
+ test_flush(rk, -1);
554
+
555
+ /* Fail the PID reinit */
556
+ rd_kafka_mock_broker_push_request_error_rtts(
557
+ mcluster, txn_coord, RD_KAFKAP_InitProducerId, 1, error_code, 0);
558
+
559
+ /* Produce a message, let it fail with a fatal idempo error. */
560
+ rd_kafka_mock_push_request_errors(
561
+ mcluster, RD_KAFKAP_Produce, 1,
562
+ RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID);
563
+
564
+ TEST_CALL_ERR__(rd_kafka_producev(
565
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
566
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
567
+
568
+ test_flush(rk, -1);
569
+
570
+ /* Abort the transaction, should fail with a fatal error */
571
+ error = rd_kafka_abort_transaction(rk, -1);
572
+ TEST_ASSERT(error != NULL, "Expected abort_transaction() to fail");
573
+
574
+ TEST_SAY("abort_transaction() failed: %s\n",
575
+ rd_kafka_error_string(error));
576
+ TEST_ASSERT(rd_kafka_error_is_fatal(error), "Expected a fatal error");
577
+ rd_kafka_error_destroy(error);
578
+
579
+ fatal_err = rd_kafka_fatal_error(rk, errstr, sizeof(errstr));
580
+ TEST_ASSERT(fatal_err, "Expected a fatal error to have been raised");
581
+ TEST_SAY("Fatal error: %s: %s\n", rd_kafka_err2name(fatal_err), errstr);
582
+
583
+ /* All done */
584
+
585
+ rd_kafka_destroy(rk);
586
+
587
+ allowed_error = RD_KAFKA_RESP_ERR_NO_ERROR;
588
+
589
+ SUB_TEST_PASS();
590
+ }
591
+
592
+
593
+ /**
594
+ * @brief Test EndTxn errors.
595
+ */
596
+ static void do_test_txn_endtxn_errors(void) {
597
+ rd_kafka_t *rk = NULL;
598
+ rd_kafka_mock_cluster_t *mcluster = NULL;
599
+ rd_kafka_resp_err_t err;
600
+ struct {
601
+ size_t error_cnt;
602
+ rd_kafka_resp_err_t errors[4];
603
+ rd_kafka_resp_err_t exp_err;
604
+ rd_bool_t exp_retriable;
605
+ rd_bool_t exp_abortable;
606
+ rd_bool_t exp_fatal;
607
+ rd_bool_t exp_successful_abort;
608
+ } scenario[] = {
609
+ /* This list of errors is from the EndTxnResponse handler in
610
+ * AK clients/.../TransactionManager.java */
611
+ {
612
+ /* #0 */
613
+ 2,
614
+ {RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
615
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE},
616
+ /* Should auto-recover */
617
+ RD_KAFKA_RESP_ERR_NO_ERROR,
618
+ },
619
+ {
620
+ /* #1 */
621
+ 2,
622
+ {RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
623
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR},
624
+ /* Should auto-recover */
625
+ RD_KAFKA_RESP_ERR_NO_ERROR,
626
+ },
627
+ {
628
+ /* #2 */
629
+ 1,
630
+ {RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS},
631
+ /* Should auto-recover */
632
+ RD_KAFKA_RESP_ERR_NO_ERROR,
633
+ },
634
+ {
635
+ /* #3 */
636
+ 3,
637
+ {RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS,
638
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS,
639
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS},
640
+ /* Should auto-recover */
641
+ RD_KAFKA_RESP_ERR_NO_ERROR,
642
+ },
643
+ {
644
+ /* #4: the abort is auto-recovering thru epoch bump */
645
+ 1,
646
+ {RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID},
647
+ RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID,
648
+ rd_false /* !retriable */,
649
+ rd_true /* abortable */,
650
+ rd_false /* !fatal */,
651
+ rd_true /* successful abort */
652
+ },
653
+ {
654
+ /* #5: the abort is auto-recovering thru epoch bump */
655
+ 1,
656
+ {RD_KAFKA_RESP_ERR_INVALID_PRODUCER_ID_MAPPING},
657
+ RD_KAFKA_RESP_ERR_INVALID_PRODUCER_ID_MAPPING,
658
+ rd_false /* !retriable */,
659
+ rd_true /* abortable */,
660
+ rd_false /* !fatal */,
661
+ rd_true /* successful abort */
662
+ },
663
+ {
664
+ /* #6 */
665
+ 1,
666
+ {RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH},
667
+ /* This error is normalized */
668
+ RD_KAFKA_RESP_ERR__FENCED,
669
+ rd_false /* !retriable */,
670
+ rd_false /* !abortable */,
671
+ rd_true /* fatal */
672
+ },
673
+ {
674
+ /* #7 */
675
+ 1,
676
+ {RD_KAFKA_RESP_ERR_PRODUCER_FENCED},
677
+ /* This error is normalized */
678
+ RD_KAFKA_RESP_ERR__FENCED,
679
+ rd_false /* !retriable */,
680
+ rd_false /* !abortable */,
681
+ rd_true /* fatal */
682
+ },
683
+ {
684
+ /* #8 */
685
+ 1,
686
+ {RD_KAFKA_RESP_ERR_TRANSACTIONAL_ID_AUTHORIZATION_FAILED},
687
+ RD_KAFKA_RESP_ERR_TRANSACTIONAL_ID_AUTHORIZATION_FAILED,
688
+ rd_false /* !retriable */,
689
+ rd_false /* !abortable */,
690
+ rd_true /* fatal */
691
+ },
692
+ {
693
+ /* #9 */
694
+ 1,
695
+ {RD_KAFKA_RESP_ERR_GROUP_AUTHORIZATION_FAILED},
696
+ RD_KAFKA_RESP_ERR_GROUP_AUTHORIZATION_FAILED,
697
+ rd_false /* !retriable */,
698
+ rd_true /* abortable */,
699
+ rd_false /* !fatal */
700
+ },
701
+ {
702
+ /* #10 */
703
+ /* Any other error should raise a fatal error */
704
+ 1,
705
+ {RD_KAFKA_RESP_ERR_INVALID_MSG_SIZE},
706
+ RD_KAFKA_RESP_ERR_INVALID_MSG_SIZE,
707
+ rd_false /* !retriable */,
708
+ rd_true /* abortable */,
709
+ rd_false /* !fatal */,
710
+ },
711
+ {
712
+ /* #11 */
713
+ 1,
714
+ {RD_KAFKA_RESP_ERR_PRODUCER_FENCED},
715
+ /* This error is normalized */
716
+ RD_KAFKA_RESP_ERR__FENCED,
717
+ rd_false /* !retriable */,
718
+ rd_false /* !abortable */,
719
+ rd_true /* fatal */
720
+ },
721
+ {0},
722
+ };
723
+ int i;
724
+
725
+ SUB_TEST_QUICK();
726
+
727
+ for (i = 0; scenario[i].error_cnt > 0; i++) {
728
+ int j;
729
+ /* For each scenario, test:
730
+ * commit_transaction()
731
+ * flush() + commit_transaction()
732
+ * abort_transaction()
733
+ * flush() + abort_transaction()
734
+ */
735
+ for (j = 0; j < (2 + 2); j++) {
736
+ rd_bool_t commit = j < 2;
737
+ rd_bool_t with_flush = j & 1;
738
+ rd_bool_t exp_successful_abort =
739
+ !commit && scenario[i].exp_successful_abort;
740
+ const char *commit_str =
741
+ commit ? (with_flush ? "commit&flush" : "commit")
742
+ : (with_flush ? "abort&flush" : "abort");
743
+ rd_kafka_topic_partition_list_t *offsets;
744
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
745
+ rd_kafka_error_t *error;
746
+ test_timing_t t_call;
747
+
748
+ TEST_SAY("Testing scenario #%d %s with %" PRIusz
749
+ " injected erorrs, expecting %s\n",
750
+ i, commit_str, scenario[i].error_cnt,
751
+ exp_successful_abort
752
+ ? "successful abort"
753
+ : rd_kafka_err2name(scenario[i].exp_err));
754
+
755
+ if (!rk) {
756
+ const char *txnid = "myTxnId";
757
+ rk = create_txn_producer(&mcluster, txnid, 3,
758
+ NULL);
759
+ TEST_CALL_ERROR__(
760
+ rd_kafka_init_transactions(rk, 5000));
761
+ }
762
+
763
+ /*
764
+ * Start transaction
765
+ */
766
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
767
+
768
+ /* Transaction aborts will cause DR errors:
769
+ * ignore them. */
770
+ test_curr->ignore_dr_err = !commit;
771
+
772
+ /*
773
+ * Produce a message.
774
+ */
775
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
776
+ RD_KAFKA_V_VALUE("hi", 2),
777
+ RD_KAFKA_V_END);
778
+ TEST_ASSERT(!err, "produce failed: %s",
779
+ rd_kafka_err2str(err));
780
+
781
+ if (with_flush)
782
+ test_flush(rk, -1);
783
+
784
+ /*
785
+ * Send some arbitrary offsets.
786
+ */
787
+ offsets = rd_kafka_topic_partition_list_new(4);
788
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4",
789
+ 3)
790
+ ->offset = 12;
791
+ rd_kafka_topic_partition_list_add(offsets, "srctopic64",
792
+ 60)
793
+ ->offset = 99999;
794
+
795
+ cgmetadata =
796
+ rd_kafka_consumer_group_metadata_new("mygroupid");
797
+
798
+ TEST_CALL_ERROR__(rd_kafka_send_offsets_to_transaction(
799
+ rk, offsets, cgmetadata, -1));
800
+
801
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
802
+ rd_kafka_topic_partition_list_destroy(offsets);
803
+
804
+ /*
805
+ * Commit transaction, first with som failures,
806
+ * then succeed.
807
+ */
808
+ rd_kafka_mock_push_request_errors_array(
809
+ mcluster, RD_KAFKAP_EndTxn, scenario[i].error_cnt,
810
+ scenario[i].errors);
811
+
812
+ TIMING_START(&t_call, "%s", commit_str);
813
+ if (commit)
814
+ error = rd_kafka_commit_transaction(
815
+ rk, tmout_multip(5000));
816
+ else
817
+ error = rd_kafka_abort_transaction(
818
+ rk, tmout_multip(5000));
819
+ TIMING_STOP(&t_call);
820
+
821
+ if (error)
822
+ TEST_SAY(
823
+ "Scenario #%d %s failed: %s: %s "
824
+ "(retriable=%s, req_abort=%s, "
825
+ "fatal=%s)\n",
826
+ i, commit_str, rd_kafka_error_name(error),
827
+ rd_kafka_error_string(error),
828
+ RD_STR_ToF(
829
+ rd_kafka_error_is_retriable(error)),
830
+ RD_STR_ToF(
831
+ rd_kafka_error_txn_requires_abort(
832
+ error)),
833
+ RD_STR_ToF(rd_kafka_error_is_fatal(error)));
834
+ else
835
+ TEST_SAY("Scenario #%d %s succeeded\n", i,
836
+ commit_str);
837
+
838
+ if (!scenario[i].exp_err || exp_successful_abort) {
839
+ TEST_ASSERT(!error,
840
+ "Expected #%d %s to succeed, "
841
+ "got %s",
842
+ i, commit_str,
843
+ rd_kafka_error_string(error));
844
+ continue;
845
+ }
846
+
847
+
848
+ TEST_ASSERT(error != NULL, "Expected #%d %s to fail", i,
849
+ commit_str);
850
+ TEST_ASSERT(scenario[i].exp_err ==
851
+ rd_kafka_error_code(error),
852
+ "Scenario #%d: expected %s, not %s", i,
853
+ rd_kafka_err2name(scenario[i].exp_err),
854
+ rd_kafka_error_name(error));
855
+ TEST_ASSERT(
856
+ scenario[i].exp_retriable ==
857
+ (rd_bool_t)rd_kafka_error_is_retriable(error),
858
+ "Scenario #%d: retriable mismatch", i);
859
+ TEST_ASSERT(
860
+ scenario[i].exp_abortable ==
861
+ (rd_bool_t)rd_kafka_error_txn_requires_abort(
862
+ error),
863
+ "Scenario #%d: abortable mismatch", i);
864
+ TEST_ASSERT(
865
+ scenario[i].exp_fatal ==
866
+ (rd_bool_t)rd_kafka_error_is_fatal(error),
867
+ "Scenario #%d: fatal mismatch", i);
868
+
869
+ /* Handle errors according to the error flags */
870
+ if (rd_kafka_error_is_fatal(error)) {
871
+ TEST_SAY("Fatal error, destroying producer\n");
872
+ rd_kafka_error_destroy(error);
873
+ rd_kafka_destroy(rk);
874
+ rk = NULL; /* Will be re-created on the next
875
+ * loop iteration. */
876
+
877
+ } else if (rd_kafka_error_txn_requires_abort(error)) {
878
+ rd_kafka_error_destroy(error);
879
+ TEST_SAY(
880
+ "Abortable error, "
881
+ "aborting transaction\n");
882
+ TEST_CALL_ERROR__(
883
+ rd_kafka_abort_transaction(rk, -1));
884
+
885
+ } else if (rd_kafka_error_is_retriable(error)) {
886
+ rd_kafka_error_destroy(error);
887
+ TEST_SAY("Retriable error, retrying %s once\n",
888
+ commit_str);
889
+ if (commit)
890
+ TEST_CALL_ERROR__(
891
+ rd_kafka_commit_transaction(rk,
892
+ 5000));
893
+ else
894
+ TEST_CALL_ERROR__(
895
+ rd_kafka_abort_transaction(rk,
896
+ 5000));
897
+ } else {
898
+ TEST_FAIL(
899
+ "Scenario #%d %s: "
900
+ "Permanent error without enough "
901
+ "hints to proceed: %s\n",
902
+ i, commit_str,
903
+ rd_kafka_error_string(error));
904
+ }
905
+ }
906
+ }
907
+
908
+ /* All done */
909
+ if (rk)
910
+ rd_kafka_destroy(rk);
911
+
912
+ SUB_TEST_PASS();
913
+ }
914
+
915
+
916
+ /**
917
+ * @brief Test that the commit/abort works properly with infinite timeout.
918
+ */
919
+ static void do_test_txn_endtxn_infinite(void) {
920
+ rd_kafka_t *rk;
921
+ rd_kafka_mock_cluster_t *mcluster = NULL;
922
+ const char *txnid = "myTxnId";
923
+ int i;
924
+
925
+ SUB_TEST_QUICK();
926
+
927
+ rk = create_txn_producer(&mcluster, txnid, 3, NULL);
928
+
929
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
930
+
931
+ for (i = 0; i < 2; i++) {
932
+ rd_bool_t commit = i == 0;
933
+ const char *commit_str = commit ? "commit" : "abort";
934
+ rd_kafka_error_t *error;
935
+ test_timing_t t_call;
936
+
937
+ /* Messages will fail on as the transaction fails,
938
+ * ignore the DR error */
939
+ test_curr->ignore_dr_err = rd_true;
940
+
941
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
942
+
943
+ TEST_CALL_ERR__(rd_kafka_producev(
944
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_VALUE("hi", 2),
945
+ RD_KAFKA_V_END));
946
+
947
+ /*
948
+ * Commit/abort transaction, first with som retriable failures,
949
+ * then success.
950
+ */
951
+ rd_kafka_mock_push_request_errors(
952
+ mcluster, RD_KAFKAP_EndTxn, 10,
953
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
954
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
955
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
956
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
957
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
958
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
959
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
960
+ RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS,
961
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
962
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR);
963
+
964
+ rd_sleep(1);
965
+
966
+ TIMING_START(&t_call, "%s_transaction()", commit_str);
967
+ if (commit)
968
+ error = rd_kafka_commit_transaction(rk, -1);
969
+ else
970
+ error = rd_kafka_abort_transaction(rk, -1);
971
+ TIMING_STOP(&t_call);
972
+
973
+ TEST_SAY("%s returned %s\n", commit_str,
974
+ error ? rd_kafka_error_string(error) : "success");
975
+
976
+ TEST_ASSERT(!error, "Expected %s to succeed, got %s",
977
+ commit_str, rd_kafka_error_string(error));
978
+ }
979
+
980
+ /* All done */
981
+
982
+ rd_kafka_destroy(rk);
983
+
984
+ SUB_TEST_PASS();
985
+ }
986
+
987
+
988
+
989
+ /**
990
+ * @brief Test that the commit/abort user timeout is honoured.
991
+ */
992
+ static void do_test_txn_endtxn_timeout(void) {
993
+ rd_kafka_t *rk;
994
+ rd_kafka_mock_cluster_t *mcluster = NULL;
995
+ const char *txnid = "myTxnId";
996
+ int i;
997
+
998
+ SUB_TEST_QUICK();
999
+
1000
+ rk = create_txn_producer(&mcluster, txnid, 3, NULL);
1001
+
1002
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1003
+
1004
+ for (i = 0; i < 2; i++) {
1005
+ rd_bool_t commit = i == 0;
1006
+ const char *commit_str = commit ? "commit" : "abort";
1007
+ rd_kafka_error_t *error;
1008
+ test_timing_t t_call;
1009
+
1010
+ /* Messages will fail as the transaction fails,
1011
+ * ignore the DR error */
1012
+ test_curr->ignore_dr_err = rd_true;
1013
+
1014
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1015
+
1016
+ TEST_CALL_ERR__(rd_kafka_producev(
1017
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_VALUE("hi", 2),
1018
+ RD_KAFKA_V_END));
1019
+
1020
+ /*
1021
+ * Commit/abort transaction, first with some retriable failures
1022
+ * whos retries exceed the user timeout.
1023
+ */
1024
+ rd_kafka_mock_push_request_errors(
1025
+ mcluster, RD_KAFKAP_EndTxn, 10,
1026
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
1027
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
1028
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
1029
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
1030
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
1031
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
1032
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
1033
+ RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS,
1034
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR,
1035
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR);
1036
+
1037
+ rd_sleep(1);
1038
+
1039
+ TIMING_START(&t_call, "%s_transaction()", commit_str);
1040
+ if (commit)
1041
+ error = rd_kafka_commit_transaction(rk, 100);
1042
+ else
1043
+ error = rd_kafka_abort_transaction(rk, 100);
1044
+ TIMING_STOP(&t_call);
1045
+
1046
+ TEST_SAY_ERROR(error, "%s returned: ", commit_str);
1047
+ TEST_ASSERT(error != NULL, "Expected %s to fail", commit_str);
1048
+ TEST_ASSERT(
1049
+ rd_kafka_error_code(error) == RD_KAFKA_RESP_ERR__TIMED_OUT,
1050
+ "Expected %s to fail with timeout, not %s: %s", commit_str,
1051
+ rd_kafka_error_name(error), rd_kafka_error_string(error));
1052
+ TEST_ASSERT(rd_kafka_error_is_retriable(error),
1053
+ "%s failure should raise a retriable error",
1054
+ commit_str);
1055
+ rd_kafka_error_destroy(error);
1056
+
1057
+ /* Now call it again with an infinite timeout, should work. */
1058
+ TIMING_START(&t_call, "%s_transaction() nr 2", commit_str);
1059
+ if (commit)
1060
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
1061
+ else
1062
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
1063
+ TIMING_STOP(&t_call);
1064
+ }
1065
+
1066
+ /* All done */
1067
+
1068
+ rd_kafka_destroy(rk);
1069
+
1070
+ SUB_TEST_PASS();
1071
+ }
1072
+
1073
+
1074
+
1075
+ /**
1076
+ * @brief Test commit/abort inflight timeout behaviour, which should result
1077
+ * in a retriable error.
1078
+ */
1079
+ static void do_test_txn_endtxn_timeout_inflight(void) {
1080
+ rd_kafka_t *rk;
1081
+ rd_kafka_mock_cluster_t *mcluster = NULL;
1082
+ const char *txnid = "myTxnId";
1083
+ int32_t coord_id = 1;
1084
+ int i;
1085
+
1086
+ SUB_TEST();
1087
+
1088
+ allowed_error = RD_KAFKA_RESP_ERR__TIMED_OUT;
1089
+ test_curr->is_fatal_cb = error_is_fatal_cb;
1090
+
1091
+ rk = create_txn_producer(&mcluster, txnid, 1, "transaction.timeout.ms",
1092
+ "5000", NULL);
1093
+
1094
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
1095
+
1096
+ for (i = 0; i < 2; i++) {
1097
+ rd_bool_t commit = i == 0;
1098
+ const char *commit_str = commit ? "commit" : "abort";
1099
+ rd_kafka_error_t *error;
1100
+ test_timing_t t_call;
1101
+
1102
+ /* Messages will fail as the transaction fails,
1103
+ * ignore the DR error */
1104
+ test_curr->ignore_dr_err = rd_true;
1105
+
1106
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1107
+
1108
+ TEST_CALL_ERR__(rd_kafka_producev(
1109
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_VALUE("hi", 2),
1110
+ RD_KAFKA_V_END));
1111
+
1112
+ /* Let EndTxn & EndTxn retry timeout */
1113
+ rd_kafka_mock_broker_push_request_error_rtts(
1114
+ mcluster, coord_id, RD_KAFKAP_EndTxn, 2,
1115
+ RD_KAFKA_RESP_ERR_NO_ERROR, 10000,
1116
+ RD_KAFKA_RESP_ERR_NO_ERROR, 10000);
1117
+
1118
+ rd_sleep(1);
1119
+
1120
+ TIMING_START(&t_call, "%s_transaction()", commit_str);
1121
+ if (commit)
1122
+ error = rd_kafka_commit_transaction(rk, 4000);
1123
+ else
1124
+ error = rd_kafka_abort_transaction(rk, 4000);
1125
+ TIMING_STOP(&t_call);
1126
+
1127
+ TEST_SAY_ERROR(error, "%s returned: ", commit_str);
1128
+ TEST_ASSERT(error != NULL, "Expected %s to fail", commit_str);
1129
+ TEST_ASSERT(
1130
+ rd_kafka_error_code(error) == RD_KAFKA_RESP_ERR__TIMED_OUT,
1131
+ "Expected %s to fail with timeout, not %s: %s", commit_str,
1132
+ rd_kafka_error_name(error), rd_kafka_error_string(error));
1133
+ TEST_ASSERT(rd_kafka_error_is_retriable(error),
1134
+ "%s failure should raise a retriable error",
1135
+ commit_str);
1136
+ rd_kafka_error_destroy(error);
1137
+
1138
+ /* Now call it again with an infinite timeout, should work. */
1139
+ TIMING_START(&t_call, "%s_transaction() nr 2", commit_str);
1140
+ if (commit)
1141
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
1142
+ else
1143
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
1144
+ TIMING_STOP(&t_call);
1145
+ }
1146
+
1147
+ /* All done */
1148
+
1149
+ rd_kafka_destroy(rk);
1150
+
1151
+ allowed_error = RD_KAFKA_RESP_ERR_NO_ERROR;
1152
+ test_curr->is_fatal_cb = NULL;
1153
+
1154
+ SUB_TEST_PASS();
1155
+ }
1156
+
1157
+
1158
+
1159
+ /**
1160
+ * @brief Test that EndTxn is properly sent for aborted transactions
1161
+ * even if AddOffsetsToTxnRequest was retried.
1162
+ * This is a check for a txn_req_cnt bug.
1163
+ */
1164
+ static void do_test_txn_req_cnt(void) {
1165
+ rd_kafka_t *rk;
1166
+ rd_kafka_mock_cluster_t *mcluster;
1167
+ rd_kafka_topic_partition_list_t *offsets;
1168
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
1169
+ const char *txnid = "myTxnId";
1170
+
1171
+ SUB_TEST_QUICK();
1172
+
1173
+ rk = create_txn_producer(&mcluster, txnid, 3, NULL);
1174
+
1175
+ /* Messages will fail on abort(), ignore the DR error */
1176
+ test_curr->ignore_dr_err = rd_true;
1177
+
1178
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1179
+
1180
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1181
+
1182
+ /*
1183
+ * Send some arbitrary offsets, first with some failures, then
1184
+ * succeed.
1185
+ */
1186
+ offsets = rd_kafka_topic_partition_list_new(2);
1187
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
1188
+ rd_kafka_topic_partition_list_add(offsets, "srctopic64", 40)->offset =
1189
+ 999999111;
1190
+
1191
+ rd_kafka_mock_push_request_errors(mcluster, RD_KAFKAP_AddOffsetsToTxn,
1192
+ 2,
1193
+ RD_KAFKA_RESP_ERR_REQUEST_TIMED_OUT,
1194
+ RD_KAFKA_RESP_ERR_NOT_COORDINATOR);
1195
+
1196
+ rd_kafka_mock_push_request_errors(
1197
+ mcluster, RD_KAFKAP_TxnOffsetCommit, 2,
1198
+ RD_KAFKA_RESP_ERR_COORDINATOR_LOAD_IN_PROGRESS,
1199
+ RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART);
1200
+
1201
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
1202
+
1203
+ TEST_CALL_ERROR__(
1204
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
1205
+
1206
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
1207
+ rd_kafka_topic_partition_list_destroy(offsets);
1208
+
1209
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, 5000));
1210
+
1211
+ /* All done */
1212
+
1213
+ rd_kafka_destroy(rk);
1214
+
1215
+ SUB_TEST_PASS();
1216
+ }
1217
+
1218
+
1219
+ /**
1220
+ * @brief Test abortable errors using mock broker error injections
1221
+ * and code coverage checks.
1222
+ */
1223
+ static void do_test_txn_requires_abort_errors(void) {
1224
+ rd_kafka_t *rk;
1225
+ rd_kafka_mock_cluster_t *mcluster;
1226
+ rd_kafka_error_t *error;
1227
+ rd_kafka_resp_err_t err;
1228
+ rd_kafka_topic_partition_list_t *offsets;
1229
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
1230
+ int r;
1231
+
1232
+ SUB_TEST_QUICK();
1233
+
1234
+ rk = create_txn_producer(&mcluster, "txnid", 3, NULL);
1235
+
1236
+ test_curr->ignore_dr_err = rd_true;
1237
+
1238
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1239
+
1240
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1241
+
1242
+ /*
1243
+ * 1. Fail on produce
1244
+ */
1245
+ TEST_SAY("1. Fail on produce\n");
1246
+
1247
+ rd_kafka_mock_push_request_errors(
1248
+ mcluster, RD_KAFKAP_Produce, 1,
1249
+ RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED);
1250
+
1251
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
1252
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
1253
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
1254
+
1255
+ /* Wait for messages to fail */
1256
+ test_flush(rk, 5000);
1257
+
1258
+ /* Any other transactional API should now raise an error */
1259
+ offsets = rd_kafka_topic_partition_list_new(1);
1260
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
1261
+
1262
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
1263
+
1264
+ error =
1265
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1);
1266
+
1267
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
1268
+ rd_kafka_topic_partition_list_destroy(offsets);
1269
+ TEST_ASSERT(error, "expected error");
1270
+ TEST_ASSERT(rd_kafka_error_txn_requires_abort(error),
1271
+ "expected abortable error, not %s",
1272
+ rd_kafka_error_string(error));
1273
+ TEST_SAY("Error %s: %s\n", rd_kafka_error_name(error),
1274
+ rd_kafka_error_string(error));
1275
+ rd_kafka_error_destroy(error);
1276
+
1277
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
1278
+
1279
+ /*
1280
+ * 2. Restart transaction and fail on AddPartitionsToTxn
1281
+ */
1282
+ TEST_SAY("2. Fail on AddPartitionsToTxn\n");
1283
+
1284
+ /* First refresh proper Metadata to clear the topic's auth error,
1285
+ * otherwise the produce() below will fail immediately. */
1286
+ r = test_get_partition_count(rk, "mytopic", 5000);
1287
+ TEST_ASSERT(r > 0, "Expected topic %s to exist", "mytopic");
1288
+
1289
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1290
+
1291
+ rd_kafka_mock_push_request_errors(
1292
+ mcluster, RD_KAFKAP_AddPartitionsToTxn, 1,
1293
+ RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED);
1294
+
1295
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
1296
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
1297
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
1298
+
1299
+ error = rd_kafka_commit_transaction(rk, 5000);
1300
+ TEST_ASSERT(error, "commit_transaction should have failed");
1301
+ TEST_SAY("commit_transaction() error %s: %s\n",
1302
+ rd_kafka_error_name(error), rd_kafka_error_string(error));
1303
+ rd_kafka_error_destroy(error);
1304
+
1305
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
1306
+
1307
+ /*
1308
+ * 3. Restart transaction and fail on AddOffsetsToTxn
1309
+ */
1310
+ TEST_SAY("3. Fail on AddOffsetsToTxn\n");
1311
+
1312
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1313
+
1314
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
1315
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
1316
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
1317
+
1318
+ rd_kafka_mock_push_request_errors(
1319
+ mcluster, RD_KAFKAP_AddOffsetsToTxn, 1,
1320
+ RD_KAFKA_RESP_ERR_GROUP_AUTHORIZATION_FAILED);
1321
+
1322
+ offsets = rd_kafka_topic_partition_list_new(1);
1323
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
1324
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
1325
+
1326
+ error =
1327
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1);
1328
+ TEST_ASSERT(error, "Expected send_offsets..() to fail");
1329
+ TEST_ASSERT(rd_kafka_error_code(error) ==
1330
+ RD_KAFKA_RESP_ERR_GROUP_AUTHORIZATION_FAILED,
1331
+ "expected send_offsets_to_transaction() to fail with "
1332
+ "group auth error: not %s",
1333
+ rd_kafka_error_name(error));
1334
+ rd_kafka_error_destroy(error);
1335
+
1336
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
1337
+ rd_kafka_topic_partition_list_destroy(offsets);
1338
+
1339
+
1340
+ error = rd_kafka_commit_transaction(rk, 5000);
1341
+ TEST_ASSERT(error, "commit_transaction should have failed");
1342
+ rd_kafka_error_destroy(error);
1343
+
1344
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
1345
+
1346
+ /* All done */
1347
+
1348
+ rd_kafka_destroy(rk);
1349
+
1350
+ SUB_TEST_PASS();
1351
+ }
1352
+
1353
+
1354
+ /**
1355
+ * @brief Test error handling and recover for when broker goes down during
1356
+ * an ongoing transaction.
1357
+ */
1358
+ static void do_test_txn_broker_down_in_txn(rd_bool_t down_coord) {
1359
+ rd_kafka_t *rk;
1360
+ rd_kafka_mock_cluster_t *mcluster;
1361
+ int32_t coord_id, leader_id, down_id;
1362
+ const char *down_what;
1363
+ rd_kafka_resp_err_t err;
1364
+ const char *topic = "test";
1365
+ const char *transactional_id = "txnid";
1366
+ int msgcnt = 1000;
1367
+ int remains = 0;
1368
+
1369
+ /* Assign coordinator and leader to two different brokers */
1370
+ coord_id = 1;
1371
+ leader_id = 2;
1372
+ if (down_coord) {
1373
+ down_id = coord_id;
1374
+ down_what = "coordinator";
1375
+ } else {
1376
+ down_id = leader_id;
1377
+ down_what = "leader";
1378
+ }
1379
+
1380
+ SUB_TEST_QUICK("Test %s down", down_what);
1381
+
1382
+ rk = create_txn_producer(&mcluster, transactional_id, 3, NULL);
1383
+
1384
+ /* Broker down is not a test-failing error */
1385
+ allowed_error = RD_KAFKA_RESP_ERR__TRANSPORT;
1386
+ test_curr->is_fatal_cb = error_is_fatal_cb;
1387
+
1388
+ err = rd_kafka_mock_topic_create(mcluster, topic, 1, 3);
1389
+ TEST_ASSERT(!err, "Failed to create topic: %s", rd_kafka_err2str(err));
1390
+
1391
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
1392
+ coord_id);
1393
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, leader_id);
1394
+
1395
+ /* Start transactioning */
1396
+ TEST_SAY("Starting transaction\n");
1397
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1398
+
1399
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1400
+
1401
+ test_produce_msgs2_nowait(rk, topic, 0, RD_KAFKA_PARTITION_UA, 0,
1402
+ msgcnt / 2, NULL, 0, &remains);
1403
+
1404
+ TEST_SAY("Bringing down %s %" PRId32 "\n", down_what, down_id);
1405
+ rd_kafka_mock_broker_set_down(mcluster, down_id);
1406
+
1407
+ rd_kafka_flush(rk, 3000);
1408
+
1409
+ /* Produce remaining messages */
1410
+ test_produce_msgs2_nowait(rk, topic, 0, RD_KAFKA_PARTITION_UA,
1411
+ msgcnt / 2, msgcnt / 2, NULL, 0, &remains);
1412
+
1413
+ rd_sleep(2);
1414
+
1415
+ TEST_SAY("Bringing up %s %" PRId32 "\n", down_what, down_id);
1416
+ rd_kafka_mock_broker_set_up(mcluster, down_id);
1417
+
1418
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
1419
+
1420
+ TEST_ASSERT(remains == 0, "%d message(s) were not produced\n", remains);
1421
+
1422
+ rd_kafka_destroy(rk);
1423
+
1424
+ test_curr->is_fatal_cb = NULL;
1425
+
1426
+ SUB_TEST_PASS();
1427
+ }
1428
+
1429
+
1430
+
1431
+ /**
1432
+ * @brief Advance the coord_id to the next broker.
1433
+ */
1434
+ static void set_next_coord(rd_kafka_mock_cluster_t *mcluster,
1435
+ const char *transactional_id,
1436
+ int broker_cnt,
1437
+ int32_t *coord_idp) {
1438
+ int32_t new_coord_id;
1439
+
1440
+ new_coord_id = 1 + ((*coord_idp) % (broker_cnt));
1441
+ TEST_SAY("Changing transaction coordinator from %" PRId32 " to %" PRId32
1442
+ "\n",
1443
+ *coord_idp, new_coord_id);
1444
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
1445
+ new_coord_id);
1446
+
1447
+ *coord_idp = new_coord_id;
1448
+ }
1449
+
1450
+ /**
1451
+ * @brief Switch coordinator during a transaction.
1452
+ *
1453
+ */
1454
+ static void do_test_txn_switch_coordinator(void) {
1455
+ rd_kafka_t *rk;
1456
+ rd_kafka_mock_cluster_t *mcluster;
1457
+ int32_t coord_id;
1458
+ const char *topic = "test";
1459
+ const char *transactional_id = "txnid";
1460
+ const int broker_cnt = 5;
1461
+ const int iterations = 20;
1462
+ int i;
1463
+
1464
+ test_timeout_set(iterations * 10);
1465
+
1466
+ SUB_TEST("Test switching coordinators");
1467
+
1468
+ rk = create_txn_producer(&mcluster, transactional_id, broker_cnt, NULL);
1469
+
1470
+ coord_id = 1;
1471
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
1472
+ coord_id);
1473
+
1474
+ /* Start transactioning */
1475
+ TEST_SAY("Starting transaction\n");
1476
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1477
+
1478
+ for (i = 0; i < iterations; i++) {
1479
+ const int msgcnt = 100;
1480
+ int remains = 0;
1481
+
1482
+ set_next_coord(mcluster, transactional_id, broker_cnt,
1483
+ &coord_id);
1484
+
1485
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1486
+
1487
+ test_produce_msgs2(rk, topic, 0, RD_KAFKA_PARTITION_UA, 0,
1488
+ msgcnt / 2, NULL, 0);
1489
+
1490
+ if (!(i % 3))
1491
+ set_next_coord(mcluster, transactional_id, broker_cnt,
1492
+ &coord_id);
1493
+
1494
+ /* Produce remaining messages */
1495
+ test_produce_msgs2_nowait(rk, topic, 0, RD_KAFKA_PARTITION_UA,
1496
+ msgcnt / 2, msgcnt / 2, NULL, 0,
1497
+ &remains);
1498
+
1499
+ if ((i & 1) || !(i % 8))
1500
+ set_next_coord(mcluster, transactional_id, broker_cnt,
1501
+ &coord_id);
1502
+
1503
+
1504
+ if (!(i % 5)) {
1505
+ test_curr->ignore_dr_err = rd_false;
1506
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
1507
+
1508
+ } else {
1509
+ test_curr->ignore_dr_err = rd_true;
1510
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
1511
+ }
1512
+ }
1513
+
1514
+
1515
+ rd_kafka_destroy(rk);
1516
+
1517
+ SUB_TEST_PASS();
1518
+ }
1519
+
1520
+
1521
+ /**
1522
+ * @brief Switch coordinator during a transaction when AddOffsetsToTxn
1523
+ * are sent. #3571.
1524
+ */
1525
+ static void do_test_txn_switch_coordinator_refresh(void) {
1526
+ rd_kafka_t *rk;
1527
+ rd_kafka_mock_cluster_t *mcluster;
1528
+ const char *topic = "test";
1529
+ const char *transactional_id = "txnid";
1530
+ rd_kafka_topic_partition_list_t *offsets;
1531
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
1532
+
1533
+ SUB_TEST("Test switching coordinators (refresh)");
1534
+
1535
+ rk = create_txn_producer(&mcluster, transactional_id, 3, NULL);
1536
+
1537
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
1538
+ 1);
1539
+
1540
+ /* Start transactioning */
1541
+ TEST_SAY("Starting transaction\n");
1542
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1543
+
1544
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1545
+
1546
+ /* Switch the coordinator so that AddOffsetsToTxnRequest
1547
+ * will respond with NOT_COORDINATOR. */
1548
+ TEST_SAY("Switching to coordinator 2\n");
1549
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
1550
+ 2);
1551
+
1552
+ /*
1553
+ * Send some arbitrary offsets.
1554
+ */
1555
+ offsets = rd_kafka_topic_partition_list_new(4);
1556
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
1557
+ rd_kafka_topic_partition_list_add(offsets, "srctopic64", 29)->offset =
1558
+ 99999;
1559
+
1560
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
1561
+
1562
+ TEST_CALL_ERROR__(rd_kafka_send_offsets_to_transaction(
1563
+ rk, offsets, cgmetadata, 20 * 1000));
1564
+
1565
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
1566
+ rd_kafka_topic_partition_list_destroy(offsets);
1567
+
1568
+
1569
+ /* Produce some messages */
1570
+ test_produce_msgs2(rk, topic, 0, RD_KAFKA_PARTITION_UA, 0, 10, NULL, 0);
1571
+
1572
+ /* And commit the transaction */
1573
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
1574
+
1575
+ rd_kafka_destroy(rk);
1576
+
1577
+ SUB_TEST_PASS();
1578
+ }
1579
+
1580
+
1581
+ /**
1582
+ * @brief Test fatal error handling when transactions are not supported
1583
+ * by the broker.
1584
+ */
1585
+ static void do_test_txns_not_supported(void) {
1586
+ rd_kafka_t *rk;
1587
+ rd_kafka_conf_t *conf;
1588
+ rd_kafka_mock_cluster_t *mcluster;
1589
+ rd_kafka_error_t *error;
1590
+ rd_kafka_resp_err_t err;
1591
+
1592
+ SUB_TEST_QUICK();
1593
+
1594
+ test_conf_init(&conf, NULL, 10);
1595
+
1596
+ test_conf_set(conf, "transactional.id", "myxnid");
1597
+ test_conf_set(conf, "bootstrap.servers", ",");
1598
+ rd_kafka_conf_set_dr_msg_cb(conf, test_dr_msg_cb);
1599
+
1600
+ rk = test_create_handle(RD_KAFKA_PRODUCER, conf);
1601
+
1602
+ /* Create mock cluster */
1603
+ mcluster = rd_kafka_mock_cluster_new(rk, 3);
1604
+
1605
+ /* Disable InitProducerId */
1606
+ rd_kafka_mock_set_apiversion(mcluster, 22 /*InitProducerId*/, -1, -1);
1607
+
1608
+
1609
+ rd_kafka_brokers_add(rk, rd_kafka_mock_cluster_bootstraps(mcluster));
1610
+
1611
+
1612
+
1613
+ error = rd_kafka_init_transactions(rk, 5 * 1000);
1614
+ TEST_SAY("init_transactions() returned %s: %s\n",
1615
+ error ? rd_kafka_error_name(error) : "success",
1616
+ error ? rd_kafka_error_string(error) : "success");
1617
+
1618
+ TEST_ASSERT(error, "Expected init_transactions() to fail");
1619
+ TEST_ASSERT(rd_kafka_error_code(error) ==
1620
+ RD_KAFKA_RESP_ERR__UNSUPPORTED_FEATURE,
1621
+ "Expected init_transactions() to fail with %s, not %s: %s",
1622
+ rd_kafka_err2name(RD_KAFKA_RESP_ERR__UNSUPPORTED_FEATURE),
1623
+ rd_kafka_error_name(error), rd_kafka_error_string(error));
1624
+ rd_kafka_error_destroy(error);
1625
+
1626
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("test"),
1627
+ RD_KAFKA_V_KEY("test", 4), RD_KAFKA_V_END);
1628
+ TEST_ASSERT(err == RD_KAFKA_RESP_ERR__FATAL,
1629
+ "Expected producev() to fail with %s, not %s",
1630
+ rd_kafka_err2name(RD_KAFKA_RESP_ERR__FATAL),
1631
+ rd_kafka_err2name(err));
1632
+
1633
+ rd_kafka_mock_cluster_destroy(mcluster);
1634
+
1635
+ rd_kafka_destroy(rk);
1636
+
1637
+ SUB_TEST_PASS();
1638
+ }
1639
+
1640
+
1641
+ /**
1642
+ * @brief CONCURRENT_TRANSACTION on AddOffsets.. should be retried.
1643
+ */
1644
+ static void do_test_txns_send_offsets_concurrent_is_retried(void) {
1645
+ rd_kafka_t *rk;
1646
+ rd_kafka_mock_cluster_t *mcluster;
1647
+ rd_kafka_resp_err_t err;
1648
+ rd_kafka_topic_partition_list_t *offsets;
1649
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
1650
+
1651
+ SUB_TEST_QUICK();
1652
+
1653
+ rk = create_txn_producer(&mcluster, "txnid", 3, NULL);
1654
+
1655
+ test_curr->ignore_dr_err = rd_true;
1656
+
1657
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1658
+
1659
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1660
+
1661
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
1662
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
1663
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
1664
+
1665
+ /* Wait for messages to be delivered */
1666
+ test_flush(rk, 5000);
1667
+
1668
+
1669
+ /*
1670
+ * Have AddOffsetsToTxn fail but eventually succeed due to
1671
+ * infinite retries.
1672
+ */
1673
+ rd_kafka_mock_push_request_errors(
1674
+ mcluster, RD_KAFKAP_AddOffsetsToTxn,
1675
+ 1 + 5, /* first request + some retries */
1676
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS,
1677
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS,
1678
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS,
1679
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS,
1680
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS,
1681
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS);
1682
+
1683
+ offsets = rd_kafka_topic_partition_list_new(1);
1684
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
1685
+
1686
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
1687
+
1688
+ TEST_CALL_ERROR__(
1689
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
1690
+
1691
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
1692
+ rd_kafka_topic_partition_list_destroy(offsets);
1693
+
1694
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, 5000));
1695
+
1696
+ /* All done */
1697
+
1698
+ rd_kafka_destroy(rk);
1699
+
1700
+ SUB_TEST_PASS();
1701
+ }
1702
+
1703
+
1704
+ /**
1705
+ * @brief Verify that send_offsets_to_transaction() with no eligible offsets
1706
+ * is handled properly - the call should succeed immediately and be
1707
+ * repeatable.
1708
+ */
1709
+ static void do_test_txns_send_offsets_non_eligible(void) {
1710
+ rd_kafka_t *rk;
1711
+ rd_kafka_mock_cluster_t *mcluster;
1712
+ rd_kafka_resp_err_t err;
1713
+ rd_kafka_topic_partition_list_t *offsets;
1714
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
1715
+
1716
+ SUB_TEST_QUICK();
1717
+
1718
+ rk = create_txn_producer(&mcluster, "txnid", 3, NULL);
1719
+
1720
+ test_curr->ignore_dr_err = rd_true;
1721
+
1722
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1723
+
1724
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1725
+
1726
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
1727
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
1728
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
1729
+
1730
+ /* Wait for messages to be delivered */
1731
+ test_flush(rk, 5000);
1732
+
1733
+ /* Empty offsets list */
1734
+ offsets = rd_kafka_topic_partition_list_new(0);
1735
+
1736
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
1737
+
1738
+ TEST_CALL_ERROR__(
1739
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
1740
+
1741
+ /* Now call it again, should also succeed. */
1742
+ TEST_CALL_ERROR__(
1743
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
1744
+
1745
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
1746
+ rd_kafka_topic_partition_list_destroy(offsets);
1747
+
1748
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, 5000));
1749
+
1750
+ /* All done */
1751
+
1752
+ rd_kafka_destroy(rk);
1753
+
1754
+ SUB_TEST_PASS();
1755
+ }
1756
+
1757
+
1758
+ /**
1759
+ * @brief Verify that request timeouts don't cause crash (#2913).
1760
+ */
1761
+ static void do_test_txns_no_timeout_crash(void) {
1762
+ rd_kafka_t *rk;
1763
+ rd_kafka_mock_cluster_t *mcluster;
1764
+ rd_kafka_error_t *error;
1765
+ rd_kafka_resp_err_t err;
1766
+ rd_kafka_topic_partition_list_t *offsets;
1767
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
1768
+
1769
+ SUB_TEST_QUICK();
1770
+
1771
+ rk =
1772
+ create_txn_producer(&mcluster, "txnid", 3, "socket.timeout.ms",
1773
+ "1000", "transaction.timeout.ms", "5000", NULL);
1774
+
1775
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1776
+
1777
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1778
+
1779
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
1780
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
1781
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
1782
+
1783
+ test_flush(rk, -1);
1784
+
1785
+ /* Delay all broker connections */
1786
+ if ((err = rd_kafka_mock_broker_set_rtt(mcluster, 1, 2000)) ||
1787
+ (err = rd_kafka_mock_broker_set_rtt(mcluster, 2, 2000)) ||
1788
+ (err = rd_kafka_mock_broker_set_rtt(mcluster, 3, 2000)))
1789
+ TEST_FAIL("Failed to set broker RTT: %s",
1790
+ rd_kafka_err2str(err));
1791
+
1792
+ /* send_offsets..() should now time out */
1793
+ offsets = rd_kafka_topic_partition_list_new(1);
1794
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
1795
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
1796
+
1797
+ error =
1798
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1);
1799
+ TEST_ASSERT(error, "Expected send_offsets..() to fail");
1800
+ TEST_SAY("send_offsets..() failed with %serror: %s\n",
1801
+ rd_kafka_error_is_retriable(error) ? "retriable " : "",
1802
+ rd_kafka_error_string(error));
1803
+ TEST_ASSERT(rd_kafka_error_code(error) == RD_KAFKA_RESP_ERR__TIMED_OUT,
1804
+ "expected send_offsets_to_transaction() to fail with "
1805
+ "timeout, not %s",
1806
+ rd_kafka_error_name(error));
1807
+ TEST_ASSERT(rd_kafka_error_is_retriable(error),
1808
+ "expected send_offsets_to_transaction() to fail with "
1809
+ "a retriable error");
1810
+ rd_kafka_error_destroy(error);
1811
+
1812
+ /* Reset delay and try again */
1813
+ if ((err = rd_kafka_mock_broker_set_rtt(mcluster, 1, 0)) ||
1814
+ (err = rd_kafka_mock_broker_set_rtt(mcluster, 2, 0)) ||
1815
+ (err = rd_kafka_mock_broker_set_rtt(mcluster, 3, 0)))
1816
+ TEST_FAIL("Failed to reset broker RTT: %s",
1817
+ rd_kafka_err2str(err));
1818
+
1819
+ TEST_SAY("Retrying send_offsets..()\n");
1820
+ error =
1821
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1);
1822
+ TEST_ASSERT(!error, "Expected send_offsets..() to succeed, got: %s",
1823
+ rd_kafka_error_string(error));
1824
+
1825
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
1826
+ rd_kafka_topic_partition_list_destroy(offsets);
1827
+
1828
+ /* All done */
1829
+ rd_kafka_destroy(rk);
1830
+
1831
+ SUB_TEST_PASS();
1832
+ }
1833
+
1834
+
1835
+ /**
1836
+ * @brief Test auth failure handling.
1837
+ */
1838
+ static void do_test_txn_auth_failure(int16_t ApiKey,
1839
+ rd_kafka_resp_err_t ErrorCode) {
1840
+ rd_kafka_t *rk;
1841
+ rd_kafka_mock_cluster_t *mcluster;
1842
+ rd_kafka_error_t *error;
1843
+
1844
+ SUB_TEST_QUICK("ApiKey=%s ErrorCode=%s", rd_kafka_ApiKey2str(ApiKey),
1845
+ rd_kafka_err2name(ErrorCode));
1846
+
1847
+ rk = create_txn_producer(&mcluster, "txnid", 3, NULL);
1848
+
1849
+ rd_kafka_mock_push_request_errors(mcluster, ApiKey, 1, ErrorCode);
1850
+
1851
+ error = rd_kafka_init_transactions(rk, 5000);
1852
+ TEST_ASSERT(error, "Expected init_transactions() to fail");
1853
+
1854
+ TEST_SAY("init_transactions() failed: %s: %s\n",
1855
+ rd_kafka_err2name(rd_kafka_error_code(error)),
1856
+ rd_kafka_error_string(error));
1857
+ TEST_ASSERT(rd_kafka_error_code(error) == ErrorCode,
1858
+ "Expected error %s, not %s", rd_kafka_err2name(ErrorCode),
1859
+ rd_kafka_err2name(rd_kafka_error_code(error)));
1860
+ TEST_ASSERT(rd_kafka_error_is_fatal(error),
1861
+ "Expected error to be fatal");
1862
+ TEST_ASSERT(!rd_kafka_error_is_retriable(error),
1863
+ "Expected error to not be retriable");
1864
+ rd_kafka_error_destroy(error);
1865
+
1866
+ /* All done */
1867
+
1868
+ rd_kafka_destroy(rk);
1869
+
1870
+ SUB_TEST_PASS();
1871
+ }
1872
+
1873
+
1874
+ /**
1875
+ * @brief Issue #3041: Commit fails due to message flush() taking too long,
1876
+ * eventually resulting in an unabortable error and failure to
1877
+ * re-init the transactional producer.
1878
+ */
1879
+ static void do_test_txn_flush_timeout(void) {
1880
+ rd_kafka_t *rk;
1881
+ rd_kafka_mock_cluster_t *mcluster;
1882
+ rd_kafka_topic_partition_list_t *offsets;
1883
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
1884
+ rd_kafka_error_t *error;
1885
+ const char *txnid = "myTxnId";
1886
+ const char *topic = "myTopic";
1887
+ const int32_t coord_id = 2;
1888
+ int msgcounter = 0;
1889
+ rd_bool_t is_retry = rd_false;
1890
+
1891
+ SUB_TEST_QUICK();
1892
+
1893
+ rk = create_txn_producer(&mcluster, txnid, 3, "message.timeout.ms",
1894
+ "10000", "transaction.timeout.ms", "10000",
1895
+ /* Speed up coordinator reconnect */
1896
+ "reconnect.backoff.max.ms", "1000", NULL);
1897
+
1898
+
1899
+ /* Broker down is not a test-failing error */
1900
+ test_curr->is_fatal_cb = error_is_fatal_cb;
1901
+ allowed_error = RD_KAFKA_RESP_ERR__TRANSPORT;
1902
+
1903
+ rd_kafka_mock_topic_create(mcluster, topic, 2, 3);
1904
+
1905
+ /* Set coordinator so we can disconnect it later */
1906
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid, coord_id);
1907
+
1908
+ /*
1909
+ * Init transactions
1910
+ */
1911
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
1912
+
1913
+ retry:
1914
+ if (!is_retry) {
1915
+ /* First attempt should fail. */
1916
+
1917
+ test_curr->ignore_dr_err = rd_true;
1918
+ test_curr->exp_dr_err = RD_KAFKA_RESP_ERR__MSG_TIMED_OUT;
1919
+
1920
+ /* Assign invalid partition leaders for some partitions so
1921
+ * that messages will not be delivered. */
1922
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, -1);
1923
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 1, -1);
1924
+
1925
+ } else {
1926
+ /* The retry should succeed */
1927
+ test_curr->ignore_dr_err = rd_false;
1928
+ test_curr->exp_dr_err = is_retry
1929
+ ? RD_KAFKA_RESP_ERR_NO_ERROR
1930
+ : RD_KAFKA_RESP_ERR__MSG_TIMED_OUT;
1931
+
1932
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, 1);
1933
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 1, 1);
1934
+ }
1935
+
1936
+
1937
+ /*
1938
+ * Start a transaction
1939
+ */
1940
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
1941
+
1942
+ /*
1943
+ * Produce some messages to specific partitions and random.
1944
+ */
1945
+ test_produce_msgs2_nowait(rk, topic, 0, 0, 0, 100, NULL, 10,
1946
+ &msgcounter);
1947
+ test_produce_msgs2_nowait(rk, topic, 1, 0, 0, 100, NULL, 10,
1948
+ &msgcounter);
1949
+ test_produce_msgs2_nowait(rk, topic, RD_KAFKA_PARTITION_UA, 0, 0, 100,
1950
+ NULL, 10, &msgcounter);
1951
+
1952
+
1953
+ /*
1954
+ * Send some arbitrary offsets.
1955
+ */
1956
+ offsets = rd_kafka_topic_partition_list_new(4);
1957
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
1958
+ rd_kafka_topic_partition_list_add(offsets, "srctopic64", 49)->offset =
1959
+ 999999111;
1960
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 0)->offset =
1961
+ 999;
1962
+ rd_kafka_topic_partition_list_add(offsets, "srctopic64", 34)->offset =
1963
+ 123456789;
1964
+
1965
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
1966
+
1967
+ TEST_CALL_ERROR__(
1968
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
1969
+
1970
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
1971
+ rd_kafka_topic_partition_list_destroy(offsets);
1972
+
1973
+ rd_sleep(2);
1974
+
1975
+ if (!is_retry) {
1976
+ /* Now disconnect the coordinator. */
1977
+ TEST_SAY("Disconnecting transaction coordinator %" PRId32 "\n",
1978
+ coord_id);
1979
+ rd_kafka_mock_broker_set_down(mcluster, coord_id);
1980
+ }
1981
+
1982
+ /*
1983
+ * Start committing.
1984
+ */
1985
+ error = rd_kafka_commit_transaction(rk, -1);
1986
+
1987
+ if (!is_retry) {
1988
+ TEST_ASSERT(error != NULL, "Expected commit to fail");
1989
+ TEST_SAY("commit_transaction() failed (expectedly): %s\n",
1990
+ rd_kafka_error_string(error));
1991
+ rd_kafka_error_destroy(error);
1992
+
1993
+ } else {
1994
+ TEST_ASSERT(!error, "Expected commit to succeed, not: %s",
1995
+ rd_kafka_error_string(error));
1996
+ }
1997
+
1998
+ if (!is_retry) {
1999
+ /*
2000
+ * Bring the coordinator back up.
2001
+ */
2002
+ rd_kafka_mock_broker_set_up(mcluster, coord_id);
2003
+ rd_sleep(2);
2004
+
2005
+ /*
2006
+ * Abort, and try again, this time without error.
2007
+ */
2008
+ TEST_SAY("Aborting and retrying\n");
2009
+ is_retry = rd_true;
2010
+
2011
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, 60000));
2012
+ goto retry;
2013
+ }
2014
+
2015
+ /* All done */
2016
+
2017
+ rd_kafka_destroy(rk);
2018
+
2019
+ SUB_TEST_PASS();
2020
+ }
2021
+
2022
+
2023
+ /**
2024
+ * @brief ESC-4424: rko is reused in response handler after destroy in coord_req
2025
+ * sender due to bad state.
2026
+ *
2027
+ * This is somewhat of a race condition so we need to perform a couple of
2028
+ * iterations before it hits, usually 2 or 3, so we try at least 15 times.
2029
+ */
2030
+ static void do_test_txn_coord_req_destroy(void) {
2031
+ rd_kafka_t *rk;
2032
+ rd_kafka_mock_cluster_t *mcluster;
2033
+ int i;
2034
+ int errcnt = 0;
2035
+
2036
+ SUB_TEST();
2037
+
2038
+ rk = create_txn_producer(&mcluster, "txnid", 3, NULL);
2039
+
2040
+ test_curr->ignore_dr_err = rd_true;
2041
+
2042
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
2043
+
2044
+ for (i = 0; i < 15; i++) {
2045
+ rd_kafka_error_t *error;
2046
+ rd_kafka_resp_err_t err;
2047
+ rd_kafka_topic_partition_list_t *offsets;
2048
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
2049
+
2050
+ test_timeout_set(10);
2051
+
2052
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2053
+
2054
+ /*
2055
+ * Inject errors to trigger retries
2056
+ */
2057
+ rd_kafka_mock_push_request_errors(
2058
+ mcluster, RD_KAFKAP_AddPartitionsToTxn,
2059
+ 2, /* first request + number of internal retries */
2060
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS,
2061
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS);
2062
+
2063
+ rd_kafka_mock_push_request_errors(
2064
+ mcluster, RD_KAFKAP_AddOffsetsToTxn,
2065
+ 1, /* first request + number of internal retries */
2066
+ RD_KAFKA_RESP_ERR_CONCURRENT_TRANSACTIONS);
2067
+
2068
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
2069
+ RD_KAFKA_V_VALUE("hi", 2),
2070
+ RD_KAFKA_V_END);
2071
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
2072
+
2073
+ rd_kafka_mock_push_request_errors(
2074
+ mcluster, RD_KAFKAP_Produce, 4,
2075
+ RD_KAFKA_RESP_ERR_REQUEST_TIMED_OUT,
2076
+ RD_KAFKA_RESP_ERR_REQUEST_TIMED_OUT,
2077
+ RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED,
2078
+ RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED);
2079
+ /* FIXME: When KIP-360 is supported, add this error:
2080
+ * RD_KAFKA_RESP_ERR_OUT_OF_ORDER_SEQUENCE_NUMBER */
2081
+
2082
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
2083
+ RD_KAFKA_V_VALUE("hi", 2),
2084
+ RD_KAFKA_V_END);
2085
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
2086
+
2087
+
2088
+ /*
2089
+ * Send offsets to transaction
2090
+ */
2091
+
2092
+ offsets = rd_kafka_topic_partition_list_new(1);
2093
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)
2094
+ ->offset = 12;
2095
+
2096
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
2097
+
2098
+ error = rd_kafka_send_offsets_to_transaction(rk, offsets,
2099
+ cgmetadata, -1);
2100
+
2101
+ TEST_SAY("send_offsets_to_transaction() #%d: %s\n", i,
2102
+ rd_kafka_error_string(error));
2103
+
2104
+ /* As we can't control the exact timing and sequence
2105
+ * of requests this sometimes fails and sometimes succeeds,
2106
+ * but we run the test enough times to trigger at least
2107
+ * one failure. */
2108
+ if (error) {
2109
+ TEST_SAY(
2110
+ "send_offsets_to_transaction() #%d "
2111
+ "failed (expectedly): %s\n",
2112
+ i, rd_kafka_error_string(error));
2113
+ TEST_ASSERT(rd_kafka_error_txn_requires_abort(error),
2114
+ "Expected abortable error for #%d", i);
2115
+ rd_kafka_error_destroy(error);
2116
+ errcnt++;
2117
+ }
2118
+
2119
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
2120
+ rd_kafka_topic_partition_list_destroy(offsets);
2121
+
2122
+ /* Allow time for internal retries */
2123
+ rd_sleep(2);
2124
+
2125
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, 5000));
2126
+ }
2127
+
2128
+ TEST_ASSERT(errcnt > 0,
2129
+ "Expected at least one send_offets_to_transaction() "
2130
+ "failure");
2131
+
2132
+ /* All done */
2133
+
2134
+ rd_kafka_destroy(rk);
2135
+ }
2136
+
2137
+
2138
+ static rd_atomic32_t multi_find_req_cnt;
2139
+
2140
+ static rd_kafka_resp_err_t
2141
+ multi_find_on_response_received_cb(rd_kafka_t *rk,
2142
+ int sockfd,
2143
+ const char *brokername,
2144
+ int32_t brokerid,
2145
+ int16_t ApiKey,
2146
+ int16_t ApiVersion,
2147
+ int32_t CorrId,
2148
+ size_t size,
2149
+ int64_t rtt,
2150
+ rd_kafka_resp_err_t err,
2151
+ void *ic_opaque) {
2152
+ rd_kafka_mock_cluster_t *mcluster = rd_kafka_handle_mock_cluster(rk);
2153
+ rd_bool_t done = rd_atomic32_get(&multi_find_req_cnt) > 10000;
2154
+
2155
+ if (ApiKey != RD_KAFKAP_AddOffsetsToTxn || done)
2156
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2157
+
2158
+ TEST_SAY("on_response_received_cb: %s: %s: brokerid %" PRId32
2159
+ ", ApiKey %hd, CorrId %d, rtt %.2fms, %s: %s\n",
2160
+ rd_kafka_name(rk), brokername, brokerid, ApiKey, CorrId,
2161
+ rtt != -1 ? (float)rtt / 1000.0 : 0.0,
2162
+ done ? "already done" : "not done yet",
2163
+ rd_kafka_err2name(err));
2164
+
2165
+
2166
+ if (rd_atomic32_add(&multi_find_req_cnt, 1) == 1) {
2167
+ /* Trigger a broker down/up event, which in turns
2168
+ * triggers the coord_req_fsm(). */
2169
+ rd_kafka_mock_broker_set_down(mcluster, 2);
2170
+ rd_kafka_mock_broker_set_up(mcluster, 2);
2171
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2172
+ }
2173
+
2174
+ /* Trigger a broker down/up event, which in turns
2175
+ * triggers the coord_req_fsm(). */
2176
+ rd_kafka_mock_broker_set_down(mcluster, 3);
2177
+ rd_kafka_mock_broker_set_up(mcluster, 3);
2178
+
2179
+ /* Clear the downed broker's latency so that it reconnects
2180
+ * quickly, otherwise the ApiVersionRequest will be delayed and
2181
+ * this will in turn delay the -> UP transition that we need to
2182
+ * trigger the coord_reqs. */
2183
+ rd_kafka_mock_broker_set_rtt(mcluster, 3, 0);
2184
+
2185
+ /* Only do this down/up once */
2186
+ rd_atomic32_add(&multi_find_req_cnt, 10000);
2187
+
2188
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2189
+ }
2190
+
2191
+
2192
+ /**
2193
+ * @brief ESC-4444: multiple FindCoordinatorRequests are sent referencing
2194
+ * the same coord_req_t, but the first one received will destroy
2195
+ * the coord_req_t object and make the subsequent FindCoordingResponses
2196
+ * reference a freed object.
2197
+ *
2198
+ * What we want to achieve is this sequence:
2199
+ * 1. AddOffsetsToTxnRequest + Response which..
2200
+ * 2. Triggers TxnOffsetCommitRequest, but the coordinator is not known, so..
2201
+ * 3. Triggers a FindCoordinatorRequest
2202
+ * 4. FindCoordinatorResponse from 3 is received ..
2203
+ * 5. A TxnOffsetCommitRequest is sent from coord_req_fsm().
2204
+ * 6. Another broker changing state to Up triggers coord reqs again, which..
2205
+ * 7. Triggers a second TxnOffsetCommitRequest from coord_req_fsm().
2206
+ * 7. FindCoordinatorResponse from 5 is received, references the destroyed rko
2207
+ * and crashes.
2208
+ */
2209
+ static void do_test_txn_coord_req_multi_find(void) {
2210
+ rd_kafka_t *rk;
2211
+ rd_kafka_mock_cluster_t *mcluster;
2212
+ rd_kafka_error_t *error;
2213
+ rd_kafka_resp_err_t err;
2214
+ rd_kafka_topic_partition_list_t *offsets;
2215
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
2216
+ const char *txnid = "txnid", *groupid = "mygroupid", *topic = "mytopic";
2217
+ int i;
2218
+
2219
+ SUB_TEST();
2220
+
2221
+ rd_atomic32_init(&multi_find_req_cnt, 0);
2222
+
2223
+ on_response_received_cb = multi_find_on_response_received_cb;
2224
+ rk = create_txn_producer(&mcluster, txnid, 3,
2225
+ /* Need connections to all brokers so we
2226
+ * can trigger coord_req_fsm events
2227
+ * by toggling connections. */
2228
+ "enable.sparse.connections", "false",
2229
+ /* Set up on_response_received interceptor */
2230
+ "on_response_received", "", NULL);
2231
+
2232
+ /* Let broker 1 be both txn and group coordinator
2233
+ * so that the group coordinator connection is up when it is time
2234
+ * send the TxnOffsetCommitRequest. */
2235
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid, 1);
2236
+ rd_kafka_mock_coordinator_set(mcluster, "group", groupid, 1);
2237
+
2238
+ /* Set broker 1, 2, and 3 as leaders for a partition each and
2239
+ * later produce to both partitions so we know there's a connection
2240
+ * to all brokers. */
2241
+ rd_kafka_mock_topic_create(mcluster, topic, 3, 1);
2242
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, 1);
2243
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 1, 2);
2244
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 2, 3);
2245
+
2246
+ /* Broker down is not a test-failing error */
2247
+ allowed_error = RD_KAFKA_RESP_ERR__TRANSPORT;
2248
+ test_curr->is_fatal_cb = error_is_fatal_cb;
2249
+
2250
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
2251
+
2252
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2253
+
2254
+ for (i = 0; i < 3; i++) {
2255
+ err = rd_kafka_producev(
2256
+ rk, RD_KAFKA_V_TOPIC(topic), RD_KAFKA_V_PARTITION(i),
2257
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
2258
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
2259
+ }
2260
+
2261
+ test_flush(rk, 5000);
2262
+
2263
+ /*
2264
+ * send_offsets_to_transaction() will query for the group coordinator,
2265
+ * we need to make those requests slow so that multiple requests are
2266
+ * sent.
2267
+ */
2268
+ for (i = 1; i <= 3; i++)
2269
+ rd_kafka_mock_broker_set_rtt(mcluster, (int32_t)i, 4000);
2270
+
2271
+ /*
2272
+ * Send offsets to transaction
2273
+ */
2274
+
2275
+ offsets = rd_kafka_topic_partition_list_new(1);
2276
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 12;
2277
+
2278
+ cgmetadata = rd_kafka_consumer_group_metadata_new(groupid);
2279
+
2280
+ error =
2281
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1);
2282
+
2283
+ TEST_SAY("send_offsets_to_transaction() %s\n",
2284
+ rd_kafka_error_string(error));
2285
+ TEST_ASSERT(!error, "send_offsets_to_transaction() failed: %s",
2286
+ rd_kafka_error_string(error));
2287
+
2288
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
2289
+ rd_kafka_topic_partition_list_destroy(offsets);
2290
+
2291
+ /* Clear delay */
2292
+ for (i = 1; i <= 3; i++)
2293
+ rd_kafka_mock_broker_set_rtt(mcluster, (int32_t)i, 0);
2294
+
2295
+ rd_sleep(5);
2296
+
2297
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, 5000));
2298
+
2299
+ /* All done */
2300
+
2301
+ TEST_ASSERT(rd_atomic32_get(&multi_find_req_cnt) > 10000,
2302
+ "on_request_sent interceptor did not trigger properly");
2303
+
2304
+ rd_kafka_destroy(rk);
2305
+
2306
+ on_response_received_cb = NULL;
2307
+
2308
+ SUB_TEST_PASS();
2309
+ }
2310
+
2311
+
2312
+ /**
2313
+ * @brief ESC-4410: adding producer partitions gradually will trigger multiple
2314
+ * AddPartitionsToTxn requests. Due to a bug the third partition to be
2315
+ * registered would hang in PEND_TXN state.
2316
+ *
2317
+ * Trigger this behaviour by having two outstanding AddPartitionsToTxn requests
2318
+ * at the same time, followed by a need for a third:
2319
+ *
2320
+ * 1. Set coordinator broker rtt high (to give us time to produce).
2321
+ * 2. Produce to partition 0, will trigger first AddPartitionsToTxn.
2322
+ * 3. Produce to partition 1, will trigger second AddPartitionsToTxn.
2323
+ * 4. Wait for second AddPartitionsToTxn response.
2324
+ * 5. Produce to partition 2, should trigger AddPartitionsToTxn, but bug
2325
+ * causes it to be stale in pending state.
2326
+ */
2327
+
2328
+ static rd_atomic32_t multi_addparts_resp_cnt;
2329
+ static rd_kafka_resp_err_t
2330
+ multi_addparts_response_received_cb(rd_kafka_t *rk,
2331
+ int sockfd,
2332
+ const char *brokername,
2333
+ int32_t brokerid,
2334
+ int16_t ApiKey,
2335
+ int16_t ApiVersion,
2336
+ int32_t CorrId,
2337
+ size_t size,
2338
+ int64_t rtt,
2339
+ rd_kafka_resp_err_t err,
2340
+ void *ic_opaque) {
2341
+
2342
+ if (ApiKey == RD_KAFKAP_AddPartitionsToTxn) {
2343
+ TEST_SAY("on_response_received_cb: %s: %s: brokerid %" PRId32
2344
+ ", ApiKey %hd, CorrId %d, rtt %.2fms, count %" PRId32
2345
+ ": %s\n",
2346
+ rd_kafka_name(rk), brokername, brokerid, ApiKey,
2347
+ CorrId, rtt != -1 ? (float)rtt / 1000.0 : 0.0,
2348
+ rd_atomic32_get(&multi_addparts_resp_cnt),
2349
+ rd_kafka_err2name(err));
2350
+
2351
+ rd_atomic32_add(&multi_addparts_resp_cnt, 1);
2352
+ }
2353
+
2354
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2355
+ }
2356
+
2357
+
2358
+ static void do_test_txn_addparts_req_multi(void) {
2359
+ rd_kafka_t *rk;
2360
+ rd_kafka_mock_cluster_t *mcluster;
2361
+ const char *txnid = "txnid", *topic = "mytopic";
2362
+ int32_t txn_coord = 2;
2363
+
2364
+ SUB_TEST();
2365
+
2366
+ rd_atomic32_init(&multi_addparts_resp_cnt, 0);
2367
+
2368
+ on_response_received_cb = multi_addparts_response_received_cb;
2369
+ rk = create_txn_producer(&mcluster, txnid, 3, "linger.ms", "0",
2370
+ "message.timeout.ms", "9000",
2371
+ /* Set up on_response_received interceptor */
2372
+ "on_response_received", "", NULL);
2373
+
2374
+ /* Let broker 1 be txn coordinator. */
2375
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid,
2376
+ txn_coord);
2377
+
2378
+ rd_kafka_mock_topic_create(mcluster, topic, 3, 1);
2379
+
2380
+ /* Set partition leaders to non-txn-coord broker so they wont
2381
+ * be affected by rtt delay */
2382
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, 1);
2383
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 1, 1);
2384
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 2, 1);
2385
+
2386
+
2387
+
2388
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
2389
+
2390
+ /*
2391
+ * Run one transaction first to let the client familiarize with
2392
+ * the topic, this avoids metadata lookups, etc, when the real
2393
+ * test is run.
2394
+ */
2395
+ TEST_SAY("Running seed transaction\n");
2396
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2397
+ TEST_CALL_ERR__(rd_kafka_producev(rk, RD_KAFKA_V_TOPIC(topic),
2398
+ RD_KAFKA_V_VALUE("seed", 4),
2399
+ RD_KAFKA_V_END));
2400
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, 5000));
2401
+
2402
+
2403
+ /*
2404
+ * Now perform test transaction with rtt delays
2405
+ */
2406
+ TEST_SAY("Running test transaction\n");
2407
+
2408
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2409
+
2410
+ /* Reset counter */
2411
+ rd_atomic32_set(&multi_addparts_resp_cnt, 0);
2412
+
2413
+ /* Add latency to txn coordinator so we can pace our produce() calls */
2414
+ rd_kafka_mock_broker_set_rtt(mcluster, txn_coord, 1000);
2415
+
2416
+ /* Produce to partition 0 */
2417
+ TEST_CALL_ERR__(rd_kafka_producev(
2418
+ rk, RD_KAFKA_V_TOPIC(topic), RD_KAFKA_V_PARTITION(0),
2419
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2420
+
2421
+ rd_usleep(500 * 1000, NULL);
2422
+
2423
+ /* Produce to partition 1 */
2424
+ TEST_CALL_ERR__(rd_kafka_producev(
2425
+ rk, RD_KAFKA_V_TOPIC(topic), RD_KAFKA_V_PARTITION(1),
2426
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2427
+
2428
+ TEST_SAY("Waiting for two AddPartitionsToTxnResponse\n");
2429
+ while (rd_atomic32_get(&multi_addparts_resp_cnt) < 2)
2430
+ rd_usleep(10 * 1000, NULL);
2431
+
2432
+ TEST_SAY("%" PRId32 " AddPartitionsToTxnResponses seen\n",
2433
+ rd_atomic32_get(&multi_addparts_resp_cnt));
2434
+
2435
+ /* Produce to partition 2, this message will hang in
2436
+ * queue if the bug is not fixed. */
2437
+ TEST_CALL_ERR__(rd_kafka_producev(
2438
+ rk, RD_KAFKA_V_TOPIC(topic), RD_KAFKA_V_PARTITION(2),
2439
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2440
+
2441
+ /* Allow some extra time for things to settle before committing
2442
+ * transaction. */
2443
+ rd_usleep(1000 * 1000, NULL);
2444
+
2445
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, 10 * 1000));
2446
+
2447
+ /* All done */
2448
+ rd_kafka_destroy(rk);
2449
+
2450
+ on_response_received_cb = NULL;
2451
+
2452
+ SUB_TEST_PASS();
2453
+ }
2454
+
2455
+
2456
+
2457
+ /**
2458
+ * @brief Test handling of OffsetFetchRequest returning UNSTABLE_OFFSET_COMMIT.
2459
+ *
2460
+ * There are two things to test;
2461
+ * - OffsetFetch triggered by committed() (and similar code paths)
2462
+ * - OffsetFetch triggered by assign()
2463
+ */
2464
+ static void do_test_unstable_offset_commit(void) {
2465
+ rd_kafka_t *rk, *c;
2466
+ rd_kafka_conf_t *c_conf;
2467
+ rd_kafka_mock_cluster_t *mcluster;
2468
+ rd_kafka_topic_partition_list_t *offsets;
2469
+ const char *topic = "srctopic4";
2470
+ const int msgcnt = 100;
2471
+ const int64_t offset_to_commit = msgcnt / 2;
2472
+ int i;
2473
+ int remains = 0;
2474
+
2475
+ SUB_TEST_QUICK();
2476
+
2477
+ rk = create_txn_producer(&mcluster, "txnid", 3, NULL);
2478
+
2479
+ test_conf_init(&c_conf, NULL, 0);
2480
+ test_conf_set(c_conf, "security.protocol", "PLAINTEXT");
2481
+ test_conf_set(c_conf, "bootstrap.servers",
2482
+ rd_kafka_mock_cluster_bootstraps(mcluster));
2483
+ test_conf_set(c_conf, "enable.partition.eof", "true");
2484
+ test_conf_set(c_conf, "auto.offset.reset", "error");
2485
+ c = test_create_consumer("mygroup", NULL, c_conf, NULL);
2486
+
2487
+ rd_kafka_mock_topic_create(mcluster, topic, 2, 3);
2488
+
2489
+ /* Produce some messages to the topic so that the consumer has
2490
+ * something to read. */
2491
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
2492
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2493
+ test_produce_msgs2_nowait(rk, topic, 0, 0, 0, msgcnt, NULL, 0,
2494
+ &remains);
2495
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
2496
+
2497
+
2498
+ /* Commit offset */
2499
+ offsets = rd_kafka_topic_partition_list_new(1);
2500
+ rd_kafka_topic_partition_list_add(offsets, topic, 0)->offset =
2501
+ offset_to_commit;
2502
+ TEST_CALL_ERR__(rd_kafka_commit(c, offsets, 0 /*sync*/));
2503
+ rd_kafka_topic_partition_list_destroy(offsets);
2504
+
2505
+ /* Retrieve offsets by calling committed().
2506
+ *
2507
+ * Have OffsetFetch fail and retry, on the first iteration
2508
+ * the API timeout is higher than the amount of time the retries will
2509
+ * take and thus succeed, and on the second iteration the timeout
2510
+ * will be lower and thus fail. */
2511
+ for (i = 0; i < 2; i++) {
2512
+ rd_kafka_resp_err_t err;
2513
+ rd_kafka_resp_err_t exp_err =
2514
+ i == 0 ? RD_KAFKA_RESP_ERR_NO_ERROR
2515
+ : RD_KAFKA_RESP_ERR__TIMED_OUT;
2516
+ int timeout_ms = exp_err ? 200 : 5 * 1000;
2517
+
2518
+ rd_kafka_mock_push_request_errors(
2519
+ mcluster, RD_KAFKAP_OffsetFetch,
2520
+ 1 + 5, /* first request + some retries */
2521
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2522
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2523
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2524
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2525
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2526
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT);
2527
+
2528
+ offsets = rd_kafka_topic_partition_list_new(1);
2529
+ rd_kafka_topic_partition_list_add(offsets, topic, 0);
2530
+
2531
+ err = rd_kafka_committed(c, offsets, timeout_ms);
2532
+
2533
+ TEST_SAY("#%d: committed() returned %s (expected %s)\n", i,
2534
+ rd_kafka_err2name(err), rd_kafka_err2name(exp_err));
2535
+
2536
+ TEST_ASSERT(err == exp_err,
2537
+ "#%d: Expected committed() to return %s, not %s", i,
2538
+ rd_kafka_err2name(exp_err), rd_kafka_err2name(err));
2539
+ TEST_ASSERT(offsets->cnt == 1,
2540
+ "Expected 1 committed offset, not %d",
2541
+ offsets->cnt);
2542
+ if (!exp_err)
2543
+ TEST_ASSERT(offsets->elems[0].offset ==
2544
+ offset_to_commit,
2545
+ "Expected committed offset %" PRId64
2546
+ ", "
2547
+ "not %" PRId64,
2548
+ offset_to_commit, offsets->elems[0].offset);
2549
+ else
2550
+ TEST_ASSERT(offsets->elems[0].offset < 0,
2551
+ "Expected no committed offset, "
2552
+ "not %" PRId64,
2553
+ offsets->elems[0].offset);
2554
+
2555
+ rd_kafka_topic_partition_list_destroy(offsets);
2556
+ }
2557
+
2558
+ TEST_SAY("Phase 2: OffsetFetch lookup through assignment\n");
2559
+ offsets = rd_kafka_topic_partition_list_new(1);
2560
+ rd_kafka_topic_partition_list_add(offsets, topic, 0)->offset =
2561
+ RD_KAFKA_OFFSET_STORED;
2562
+
2563
+ rd_kafka_mock_push_request_errors(
2564
+ mcluster, RD_KAFKAP_OffsetFetch,
2565
+ 1 + 5, /* first request + some retries */
2566
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2567
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2568
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2569
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2570
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT,
2571
+ RD_KAFKA_RESP_ERR_UNSTABLE_OFFSET_COMMIT);
2572
+
2573
+ test_consumer_incremental_assign("assign", c, offsets);
2574
+ rd_kafka_topic_partition_list_destroy(offsets);
2575
+
2576
+ test_consumer_poll_exact("consume", c, 0, 1 /*eof*/, 0, msgcnt / 2,
2577
+ rd_true /*exact counts*/, NULL);
2578
+
2579
+ /* All done */
2580
+ rd_kafka_destroy(c);
2581
+ rd_kafka_destroy(rk);
2582
+
2583
+ SUB_TEST_PASS();
2584
+ }
2585
+
2586
+
2587
+ /**
2588
+ * @brief If a message times out locally before being attempted to send
2589
+ * and commit_transaction() is called, the transaction must not succeed.
2590
+ * https://github.com/confluentinc/confluent-kafka-dotnet/issues/1568
2591
+ */
2592
+ static void do_test_commit_after_msg_timeout(void) {
2593
+ rd_kafka_t *rk;
2594
+ rd_kafka_mock_cluster_t *mcluster;
2595
+ int32_t coord_id, leader_id;
2596
+ rd_kafka_resp_err_t err;
2597
+ rd_kafka_error_t *error;
2598
+ const char *topic = "test";
2599
+ const char *transactional_id = "txnid";
2600
+ int remains = 0;
2601
+
2602
+ SUB_TEST_QUICK();
2603
+
2604
+ /* Assign coordinator and leader to two different brokers */
2605
+ coord_id = 1;
2606
+ leader_id = 2;
2607
+
2608
+ rk = create_txn_producer(&mcluster, transactional_id, 3,
2609
+ "message.timeout.ms", "5000",
2610
+ "transaction.timeout.ms", "10000", NULL);
2611
+
2612
+ /* Broker down is not a test-failing error */
2613
+ allowed_error = RD_KAFKA_RESP_ERR__TRANSPORT;
2614
+ test_curr->is_fatal_cb = error_is_fatal_cb;
2615
+ test_curr->exp_dr_err = RD_KAFKA_RESP_ERR__MSG_TIMED_OUT;
2616
+
2617
+ err = rd_kafka_mock_topic_create(mcluster, topic, 1, 3);
2618
+ TEST_ASSERT(!err, "Failed to create topic: %s", rd_kafka_err2str(err));
2619
+
2620
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
2621
+ coord_id);
2622
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, leader_id);
2623
+
2624
+ /* Start transactioning */
2625
+ TEST_SAY("Starting transaction\n");
2626
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
2627
+
2628
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2629
+
2630
+ TEST_SAY("Bringing down %" PRId32 "\n", leader_id);
2631
+ rd_kafka_mock_broker_set_down(mcluster, leader_id);
2632
+ rd_kafka_mock_broker_set_down(mcluster, coord_id);
2633
+
2634
+ test_produce_msgs2_nowait(rk, topic, 0, 0, 0, 1, NULL, 0, &remains);
2635
+
2636
+ error = rd_kafka_commit_transaction(rk, -1);
2637
+ TEST_ASSERT(error != NULL, "expected commit_transaciton() to fail");
2638
+ TEST_SAY_ERROR(error, "commit_transaction() failed (as expected): ");
2639
+ TEST_ASSERT(rd_kafka_error_txn_requires_abort(error),
2640
+ "Expected txn_requires_abort error");
2641
+ rd_kafka_error_destroy(error);
2642
+
2643
+ /* Bring the brokers up so the abort can complete */
2644
+ rd_kafka_mock_broker_set_up(mcluster, coord_id);
2645
+ rd_kafka_mock_broker_set_up(mcluster, leader_id);
2646
+
2647
+ TEST_SAY("Aborting transaction\n");
2648
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
2649
+
2650
+ TEST_ASSERT(remains == 0, "%d message(s) were not flushed\n", remains);
2651
+
2652
+ TEST_SAY("Attempting second transaction, which should succeed\n");
2653
+ test_curr->is_fatal_cb = error_is_fatal_cb;
2654
+ test_curr->exp_dr_err = RD_KAFKA_RESP_ERR_NO_ERROR;
2655
+
2656
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2657
+ test_produce_msgs2_nowait(rk, topic, 0, 0, 0, 1, NULL, 0, &remains);
2658
+
2659
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
2660
+
2661
+ TEST_ASSERT(remains == 0, "%d message(s) were not produced\n", remains);
2662
+
2663
+ rd_kafka_destroy(rk);
2664
+
2665
+ allowed_error = RD_KAFKA_RESP_ERR_NO_ERROR;
2666
+ test_curr->is_fatal_cb = NULL;
2667
+
2668
+ SUB_TEST_PASS();
2669
+ }
2670
+
2671
+
2672
+ /**
2673
+ * @brief #3575: Verify that OUT_OF_ORDER_SEQ does not trigger an epoch bump
2674
+ * during an ongoing transaction.
2675
+ * The transaction should instead enter the abortable state.
2676
+ */
2677
+ static void do_test_out_of_order_seq(void) {
2678
+ rd_kafka_t *rk;
2679
+ rd_kafka_mock_cluster_t *mcluster;
2680
+ rd_kafka_error_t *error;
2681
+ int32_t txn_coord = 1, leader = 2;
2682
+ const char *txnid = "myTxnId";
2683
+ test_timing_t timing;
2684
+ rd_kafka_resp_err_t err;
2685
+
2686
+ SUB_TEST_QUICK();
2687
+
2688
+ rk = create_txn_producer(&mcluster, txnid, 3, "batch.num.messages", "1",
2689
+ NULL);
2690
+
2691
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid,
2692
+ txn_coord);
2693
+
2694
+ rd_kafka_mock_partition_set_leader(mcluster, "mytopic", 0, leader);
2695
+
2696
+ test_curr->ignore_dr_err = rd_true;
2697
+ test_curr->is_fatal_cb = NULL;
2698
+
2699
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
2700
+
2701
+ /*
2702
+ * Start a transaction
2703
+ */
2704
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2705
+
2706
+
2707
+
2708
+ /* Produce one seeding message first to get the leader up and running */
2709
+ TEST_CALL_ERR__(rd_kafka_producev(
2710
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
2711
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2712
+ test_flush(rk, -1);
2713
+
2714
+ /* Let partition leader have a latency of 2 seconds
2715
+ * so that we can have multiple messages in-flight. */
2716
+ rd_kafka_mock_broker_set_rtt(mcluster, leader, 2 * 1000);
2717
+
2718
+ /* Produce a message, let it fail with with different errors,
2719
+ * ending with OUT_OF_ORDER which previously triggered an
2720
+ * Epoch bump. */
2721
+ rd_kafka_mock_push_request_errors(
2722
+ mcluster, RD_KAFKAP_Produce, 3,
2723
+ RD_KAFKA_RESP_ERR_NOT_LEADER_FOR_PARTITION,
2724
+ RD_KAFKA_RESP_ERR_NOT_LEADER_FOR_PARTITION,
2725
+ RD_KAFKA_RESP_ERR_OUT_OF_ORDER_SEQUENCE_NUMBER);
2726
+
2727
+ /* Produce three messages that will be delayed
2728
+ * and have errors injected.*/
2729
+ TEST_CALL_ERR__(rd_kafka_producev(
2730
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
2731
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2732
+ TEST_CALL_ERR__(rd_kafka_producev(
2733
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
2734
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2735
+ TEST_CALL_ERR__(rd_kafka_producev(
2736
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
2737
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2738
+
2739
+ /* Now sleep a short while so that the messages are processed
2740
+ * by the broker and errors are returned. */
2741
+ TEST_SAY("Sleeping..\n");
2742
+ rd_sleep(5);
2743
+
2744
+ rd_kafka_mock_broker_set_rtt(mcluster, leader, 0);
2745
+
2746
+ /* Produce a fifth message, should fail with ERR__STATE since
2747
+ * the transaction should have entered the abortable state. */
2748
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
2749
+ RD_KAFKA_V_PARTITION(0),
2750
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
2751
+ TEST_ASSERT(err == RD_KAFKA_RESP_ERR__STATE,
2752
+ "Expected produce() to fail with ERR__STATE, not %s",
2753
+ rd_kafka_err2name(err));
2754
+ TEST_SAY("produce() failed as expected: %s\n", rd_kafka_err2str(err));
2755
+
2756
+ /* Commit the transaction, should fail with abortable error. */
2757
+ TIMING_START(&timing, "commit_transaction(-1)");
2758
+ error = rd_kafka_commit_transaction(rk, -1);
2759
+ TIMING_STOP(&timing);
2760
+ TEST_ASSERT(error != NULL, "Expected commit_transaction() to fail");
2761
+
2762
+ TEST_SAY("commit_transaction() failed (expectedly): %s\n",
2763
+ rd_kafka_error_string(error));
2764
+
2765
+ TEST_ASSERT(!rd_kafka_error_is_fatal(error),
2766
+ "Did not expect fatal error");
2767
+ TEST_ASSERT(rd_kafka_error_txn_requires_abort(error),
2768
+ "Expected abortable error");
2769
+ rd_kafka_error_destroy(error);
2770
+
2771
+ /* Abort the transaction */
2772
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
2773
+
2774
+ /* Run a new transaction without errors to verify that the
2775
+ * producer can recover. */
2776
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2777
+
2778
+ TEST_CALL_ERR__(rd_kafka_producev(
2779
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
2780
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2781
+
2782
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
2783
+
2784
+ rd_kafka_destroy(rk);
2785
+
2786
+ SUB_TEST_PASS();
2787
+ }
2788
+
2789
+
2790
+ /**
2791
+ * @brief Verify lossless delivery if topic disappears from Metadata for awhile.
2792
+ *
2793
+ * If a topic is removed from metadata inbetween transactions, the producer
2794
+ * will remove its partition state for the topic's partitions.
2795
+ * If later the same topic comes back (same topic instance, not a new creation)
2796
+ * then the producer must restore the previously used msgid/BaseSequence
2797
+ * in case the same Epoch is still used, or messages will be silently lost
2798
+ * as they would seem like legit duplicates to the broker.
2799
+ *
2800
+ * Reproduction:
2801
+ * 1. produce msgs to topic, commit transaction.
2802
+ * 2. remove topic from metadata
2803
+ * 3. make sure client updates its metadata, which removes the partition
2804
+ * objects.
2805
+ * 4. restore the topic in metadata
2806
+ * 5. produce new msgs to topic, commit transaction.
2807
+ * 6. consume topic. All messages should be accounted for.
2808
+ */
2809
+ static void do_test_topic_disappears_for_awhile(void) {
2810
+ rd_kafka_t *rk, *c;
2811
+ rd_kafka_conf_t *c_conf;
2812
+ rd_kafka_mock_cluster_t *mcluster;
2813
+ const char *topic = "mytopic";
2814
+ const char *txnid = "myTxnId";
2815
+ test_timing_t timing;
2816
+ int i;
2817
+ int msgcnt = 0;
2818
+ const int partition_cnt = 10;
2819
+
2820
+ SUB_TEST_QUICK();
2821
+
2822
+ rk = create_txn_producer(
2823
+ &mcluster, txnid, 1, "batch.num.messages", "3", "linger.ms", "100",
2824
+ "topic.metadata.refresh.interval.ms", "2000", NULL);
2825
+
2826
+ rd_kafka_mock_topic_create(mcluster, topic, partition_cnt, 1);
2827
+
2828
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
2829
+
2830
+ for (i = 0; i < 2; i++) {
2831
+ int cnt = 3 * 2 * partition_cnt;
2832
+ rd_bool_t remove_topic = (i % 2) == 0;
2833
+ /*
2834
+ * Start a transaction
2835
+ */
2836
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2837
+
2838
+
2839
+ while (cnt-- >= 0) {
2840
+ TEST_CALL_ERR__(rd_kafka_producev(
2841
+ rk, RD_KAFKA_V_TOPIC(topic),
2842
+ RD_KAFKA_V_PARTITION(cnt % partition_cnt),
2843
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2844
+ msgcnt++;
2845
+ }
2846
+
2847
+ /* Commit the transaction */
2848
+ TIMING_START(&timing, "commit_transaction(-1)");
2849
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
2850
+ TIMING_STOP(&timing);
2851
+
2852
+
2853
+
2854
+ if (remove_topic) {
2855
+ /* Make it seem the topic is removed, refresh metadata,
2856
+ * and then make the topic available again. */
2857
+ const rd_kafka_metadata_t *md;
2858
+
2859
+ TEST_SAY("Marking topic as non-existent\n");
2860
+
2861
+ rd_kafka_mock_topic_set_error(
2862
+ mcluster, topic,
2863
+ RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART);
2864
+
2865
+ TEST_CALL_ERR__(rd_kafka_metadata(rk, 0, NULL, &md,
2866
+ tmout_multip(5000)));
2867
+
2868
+ rd_kafka_metadata_destroy(md);
2869
+
2870
+ rd_sleep(2);
2871
+
2872
+ TEST_SAY("Bringing topic back to life\n");
2873
+ rd_kafka_mock_topic_set_error(
2874
+ mcluster, topic, RD_KAFKA_RESP_ERR_NO_ERROR);
2875
+ }
2876
+ }
2877
+
2878
+ TEST_SAY("Verifying messages by consumtion\n");
2879
+ test_conf_init(&c_conf, NULL, 0);
2880
+ test_conf_set(c_conf, "security.protocol", "PLAINTEXT");
2881
+ test_conf_set(c_conf, "bootstrap.servers",
2882
+ rd_kafka_mock_cluster_bootstraps(mcluster));
2883
+ test_conf_set(c_conf, "enable.partition.eof", "true");
2884
+ test_conf_set(c_conf, "auto.offset.reset", "earliest");
2885
+ c = test_create_consumer("mygroup", NULL, c_conf, NULL);
2886
+
2887
+ test_consumer_subscribe(c, topic);
2888
+ test_consumer_poll_exact("consume", c, 0, partition_cnt, 0, msgcnt,
2889
+ rd_true /*exact*/, NULL);
2890
+ rd_kafka_destroy(c);
2891
+
2892
+
2893
+ rd_kafka_destroy(rk);
2894
+
2895
+ SUB_TEST_PASS();
2896
+ }
2897
+
2898
+
2899
+ /**
2900
+ * @brief Test that group coordinator requests can handle an
2901
+ * untimely disconnect.
2902
+ *
2903
+ * The transaction manager makes use of librdkafka coord_req to commit
2904
+ * transaction offsets to the group coordinator.
2905
+ * If the connection to the given group coordinator is not up the
2906
+ * coord_req code will request a connection once, but if this connection fails
2907
+ * there will be no new attempts and the coord_req will idle until either
2908
+ * destroyed or the connection is retried for other reasons.
2909
+ * This in turn stalls the send_offsets_to_transaction() call until the
2910
+ * transaction times out.
2911
+ *
2912
+ * There are two variants to this test based on switch_coord:
2913
+ * - True - Switches the coordinator during the downtime.
2914
+ * The client should detect this and send the request to the
2915
+ * new coordinator.
2916
+ * - False - The coordinator remains on the down broker. Client will reconnect
2917
+ * when down broker comes up again.
2918
+ */
2919
+ struct some_state {
2920
+ rd_kafka_mock_cluster_t *mcluster;
2921
+ rd_bool_t switch_coord;
2922
+ int32_t broker_id;
2923
+ const char *grpid;
2924
+ };
2925
+
2926
+ static int delayed_up_cb(void *arg) {
2927
+ struct some_state *state = arg;
2928
+ rd_sleep(3);
2929
+ if (state->switch_coord) {
2930
+ TEST_SAY("Switching group coordinator to %" PRId32 "\n",
2931
+ state->broker_id);
2932
+ rd_kafka_mock_coordinator_set(state->mcluster, "group",
2933
+ state->grpid, state->broker_id);
2934
+ } else {
2935
+ TEST_SAY("Bringing up group coordinator %" PRId32 "..\n",
2936
+ state->broker_id);
2937
+ rd_kafka_mock_broker_set_up(state->mcluster, state->broker_id);
2938
+ }
2939
+ return 0;
2940
+ }
2941
+
2942
+ static void do_test_disconnected_group_coord(rd_bool_t switch_coord) {
2943
+ const char *topic = "mytopic";
2944
+ const char *txnid = "myTxnId";
2945
+ const char *grpid = "myGrpId";
2946
+ const int partition_cnt = 1;
2947
+ rd_kafka_t *rk;
2948
+ rd_kafka_mock_cluster_t *mcluster;
2949
+ rd_kafka_topic_partition_list_t *offsets;
2950
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
2951
+ struct some_state state = RD_ZERO_INIT;
2952
+ test_timing_t timing;
2953
+ thrd_t thrd;
2954
+ int ret;
2955
+
2956
+ SUB_TEST_QUICK("switch_coord=%s", RD_STR_ToF(switch_coord));
2957
+
2958
+ test_curr->is_fatal_cb = error_is_fatal_cb;
2959
+ allowed_error = RD_KAFKA_RESP_ERR__TRANSPORT;
2960
+
2961
+ rk = create_txn_producer(&mcluster, txnid, 3, NULL);
2962
+
2963
+ rd_kafka_mock_topic_create(mcluster, topic, partition_cnt, 1);
2964
+
2965
+ /* Broker 1: txn coordinator
2966
+ * Broker 2: group coordinator
2967
+ * Broker 3: partition leader & backup coord if switch_coord=true */
2968
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid, 1);
2969
+ rd_kafka_mock_coordinator_set(mcluster, "group", grpid, 2);
2970
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, 3);
2971
+
2972
+ /* Bring down group coordinator so there are no undesired
2973
+ * connections to it. */
2974
+ rd_kafka_mock_broker_set_down(mcluster, 2);
2975
+
2976
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
2977
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
2978
+ TEST_CALL_ERR__(rd_kafka_producev(
2979
+ rk, RD_KAFKA_V_TOPIC(topic), RD_KAFKA_V_PARTITION(0),
2980
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
2981
+ test_flush(rk, -1);
2982
+
2983
+ rd_sleep(1);
2984
+
2985
+ /* Run a background thread that after 3s, which should be enough
2986
+ * to perform the first failed connection attempt, makes the
2987
+ * group coordinator available again. */
2988
+ state.switch_coord = switch_coord;
2989
+ state.mcluster = mcluster;
2990
+ state.grpid = grpid;
2991
+ state.broker_id = switch_coord ? 3 : 2;
2992
+ if (thrd_create(&thrd, delayed_up_cb, &state) != thrd_success)
2993
+ TEST_FAIL("Failed to create thread");
2994
+
2995
+ TEST_SAY("Calling send_offsets_to_transaction()\n");
2996
+ offsets = rd_kafka_topic_partition_list_new(1);
2997
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 0)->offset = 1;
2998
+ cgmetadata = rd_kafka_consumer_group_metadata_new(grpid);
2999
+
3000
+ TIMING_START(&timing, "send_offsets_to_transaction(-1)");
3001
+ TEST_CALL_ERROR__(
3002
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
3003
+ TIMING_STOP(&timing);
3004
+ TIMING_ASSERT(&timing, 0, 10 * 1000 /*10s*/);
3005
+
3006
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
3007
+ rd_kafka_topic_partition_list_destroy(offsets);
3008
+ thrd_join(thrd, &ret);
3009
+
3010
+ /* Commit the transaction */
3011
+ TIMING_START(&timing, "commit_transaction(-1)");
3012
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
3013
+ TIMING_STOP(&timing);
3014
+
3015
+ rd_kafka_destroy(rk);
3016
+
3017
+ allowed_error = RD_KAFKA_RESP_ERR_NO_ERROR;
3018
+ test_curr->is_fatal_cb = NULL;
3019
+
3020
+ SUB_TEST_PASS();
3021
+ }
3022
+
3023
+
3024
+ /**
3025
+ * @brief Test that a NULL coordinator is not fatal when
3026
+ * the transactional producer reconnects to the txn coordinator
3027
+ * and the first thing it does is a FindCoordinatorRequest that
3028
+ * fails with COORDINATOR_NOT_AVAILABLE, setting coordinator to NULL.
3029
+ */
3030
+ static void do_test_txn_coordinator_null_not_fatal(void) {
3031
+ rd_kafka_t *rk;
3032
+ rd_kafka_mock_cluster_t *mcluster;
3033
+ rd_kafka_error_t *error;
3034
+ rd_kafka_resp_err_t err;
3035
+ int32_t coord_id = 1;
3036
+ const char *topic = "test";
3037
+ const char *transactional_id = "txnid";
3038
+ int msgcnt = 1;
3039
+ int remains = 0;
3040
+
3041
+ SUB_TEST_QUICK();
3042
+
3043
+ /* Broker down is not a test-failing error */
3044
+ allowed_error = RD_KAFKA_RESP_ERR__TRANSPORT;
3045
+ test_curr->is_fatal_cb = error_is_fatal_cb;
3046
+ test_curr->exp_dr_err = RD_KAFKA_RESP_ERR__MSG_TIMED_OUT;
3047
+
3048
+ /* One second is the minimum transaction timeout */
3049
+ rk = create_txn_producer(&mcluster, transactional_id, 1,
3050
+ "transaction.timeout.ms", "1000", NULL);
3051
+
3052
+ err = rd_kafka_mock_topic_create(mcluster, topic, 1, 1);
3053
+ TEST_ASSERT(!err, "Failed to create topic: %s", rd_kafka_err2str(err));
3054
+
3055
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
3056
+ coord_id);
3057
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, coord_id);
3058
+
3059
+ /* Start transactioning */
3060
+ TEST_SAY("Starting transaction\n");
3061
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
3062
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
3063
+
3064
+ /* Makes the produce request timeout. */
3065
+ rd_kafka_mock_broker_push_request_error_rtts(
3066
+ mcluster, coord_id, RD_KAFKAP_Produce, 1,
3067
+ RD_KAFKA_RESP_ERR_NO_ERROR, 3000);
3068
+
3069
+ test_produce_msgs2_nowait(rk, topic, 0, RD_KAFKA_PARTITION_UA, 0,
3070
+ msgcnt, NULL, 0, &remains);
3071
+
3072
+ /* This value is linked to transaction.timeout.ms, needs enough time
3073
+ * so the message times out and a DrainBump sequence is started. */
3074
+ rd_kafka_flush(rk, 1000);
3075
+
3076
+ /* To trigger the error the COORDINATOR_NOT_AVAILABLE response
3077
+ * must come AFTER idempotent state has changed to WaitTransport
3078
+ * but BEFORE it changes to WaitPID. To make it more likely
3079
+ * rd_kafka_txn_coord_timer_start timeout can be changed to 5 ms
3080
+ * in rd_kafka_txn_coord_query, when unable to query for
3081
+ * transaction coordinator.
3082
+ */
3083
+ rd_kafka_mock_broker_push_request_error_rtts(
3084
+ mcluster, coord_id, RD_KAFKAP_FindCoordinator, 1,
3085
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE, 10);
3086
+
3087
+ /* Coordinator down starts the FindCoordinatorRequest loop. */
3088
+ TEST_SAY("Bringing down coordinator %" PRId32 "\n", coord_id);
3089
+ rd_kafka_mock_broker_set_down(mcluster, coord_id);
3090
+
3091
+ /* Coordinator down for some time. */
3092
+ rd_usleep(100 * 1000, NULL);
3093
+
3094
+ /* When it comes up, the error is triggered, if the preconditions
3095
+ * happen. */
3096
+ TEST_SAY("Bringing up coordinator %" PRId32 "\n", coord_id);
3097
+ rd_kafka_mock_broker_set_up(mcluster, coord_id);
3098
+
3099
+ /* Make sure DRs are received */
3100
+ rd_kafka_flush(rk, 1000);
3101
+
3102
+ error = rd_kafka_commit_transaction(rk, -1);
3103
+
3104
+ TEST_ASSERT(remains == 0, "%d message(s) were not produced\n", remains);
3105
+ TEST_ASSERT(error != NULL, "Expected commit_transaction() to fail");
3106
+ TEST_SAY("commit_transaction() failed (expectedly): %s\n",
3107
+ rd_kafka_error_string(error));
3108
+ rd_kafka_error_destroy(error);
3109
+
3110
+ /* Needs to wait some time before closing to make sure it doesn't go
3111
+ * into TERMINATING state before error is triggered. */
3112
+ rd_usleep(1000 * 1000, NULL);
3113
+ rd_kafka_destroy(rk);
3114
+
3115
+ allowed_error = RD_KAFKA_RESP_ERR_NO_ERROR;
3116
+ test_curr->exp_dr_err = RD_KAFKA_RESP_ERR_NO_ERROR;
3117
+ test_curr->is_fatal_cb = NULL;
3118
+
3119
+ SUB_TEST_PASS();
3120
+ }
3121
+
3122
+
3123
+
3124
+ /**
3125
+ * @brief Simple test to make sure the init_transactions() timeout is honoured
3126
+ * and also not infinite.
3127
+ */
3128
+ static void do_test_txn_resumable_init(void) {
3129
+ rd_kafka_t *rk;
3130
+ const char *transactional_id = "txnid";
3131
+ rd_kafka_error_t *error;
3132
+ test_timing_t duration;
3133
+
3134
+ SUB_TEST();
3135
+
3136
+ rd_kafka_conf_t *conf;
3137
+
3138
+ test_conf_init(&conf, NULL, 20);
3139
+ test_conf_set(conf, "bootstrap.servers", "");
3140
+ test_conf_set(conf, "transactional.id", transactional_id);
3141
+ test_conf_set(conf, "transaction.timeout.ms", "4000");
3142
+
3143
+ rk = test_create_handle(RD_KAFKA_PRODUCER, conf);
3144
+
3145
+ /* First make sure a lower timeout is honoured. */
3146
+ TIMING_START(&duration, "init_transactions(1000)");
3147
+ error = rd_kafka_init_transactions(rk, 1000);
3148
+ TIMING_STOP(&duration);
3149
+
3150
+ if (error)
3151
+ TEST_SAY("First init_transactions failed (as expected): %s\n",
3152
+ rd_kafka_error_string(error));
3153
+ TEST_ASSERT(rd_kafka_error_code(error) == RD_KAFKA_RESP_ERR__TIMED_OUT,
3154
+ "Expected _TIMED_OUT, not %s",
3155
+ error ? rd_kafka_error_string(error) : "success");
3156
+ rd_kafka_error_destroy(error);
3157
+
3158
+ TIMING_ASSERT(&duration, 900, 1500);
3159
+
3160
+ TEST_SAY(
3161
+ "Performing second init_transactions() call now with an "
3162
+ "infinite timeout: "
3163
+ "should time out in 2 x transaction.timeout.ms\n");
3164
+
3165
+ TIMING_START(&duration, "init_transactions(infinite)");
3166
+ error = rd_kafka_init_transactions(rk, -1);
3167
+ TIMING_STOP(&duration);
3168
+
3169
+ if (error)
3170
+ TEST_SAY("Second init_transactions failed (as expected): %s\n",
3171
+ rd_kafka_error_string(error));
3172
+ TEST_ASSERT(rd_kafka_error_code(error) == RD_KAFKA_RESP_ERR__TIMED_OUT,
3173
+ "Expected _TIMED_OUT, not %s",
3174
+ error ? rd_kafka_error_string(error) : "success");
3175
+ rd_kafka_error_destroy(error);
3176
+
3177
+ TIMING_ASSERT(&duration, 2 * 4000 - 500, 2 * 4000 + 500);
3178
+
3179
+ rd_kafka_destroy(rk);
3180
+
3181
+ SUB_TEST_PASS();
3182
+ }
3183
+
3184
+
3185
+ /**
3186
+ * @brief Retries a transaction call until it succeeds or returns a
3187
+ * non-retriable error - which will cause the test to fail.
3188
+ *
3189
+ * @param intermed_calls Is a block of code that will be called after each
3190
+ * retriable failure of \p call.
3191
+ */
3192
+ #define RETRY_TXN_CALL__(call, intermed_calls) \
3193
+ do { \
3194
+ rd_kafka_error_t *_error = call; \
3195
+ if (!_error) \
3196
+ break; \
3197
+ TEST_SAY_ERROR(_error, "%s: ", "" #call); \
3198
+ TEST_ASSERT(rd_kafka_error_is_retriable(_error), \
3199
+ "Expected retriable error"); \
3200
+ TEST_SAY("%s failed, retrying in 1 second\n", "" #call); \
3201
+ rd_kafka_error_destroy(_error); \
3202
+ intermed_calls; \
3203
+ rd_sleep(1); \
3204
+ } while (1)
3205
+
3206
+ /**
3207
+ * @brief Call \p call and expect it to fail with \p exp_err_code.
3208
+ */
3209
+ #define TXN_CALL_EXPECT_ERROR__(call, exp_err_code) \
3210
+ do { \
3211
+ rd_kafka_error_t *_error = call; \
3212
+ TEST_ASSERT(_error != NULL, \
3213
+ "%s: Expected %s error, got success", "" #call, \
3214
+ rd_kafka_err2name(exp_err_code)); \
3215
+ TEST_SAY_ERROR(_error, "%s: ", "" #call); \
3216
+ TEST_ASSERT(rd_kafka_error_code(_error) == exp_err_code, \
3217
+ "%s: Expected %s error, got %s", "" #call, \
3218
+ rd_kafka_err2name(exp_err_code), \
3219
+ rd_kafka_error_name(_error)); \
3220
+ rd_kafka_error_destroy(_error); \
3221
+ } while (0)
3222
+
3223
+
3224
+ /**
3225
+ * @brief Simple test to make sure short API timeouts can be safely resumed
3226
+ * by calling the same API again.
3227
+ *
3228
+ * @param do_commit Commit transaction if true, else abort transaction.
3229
+ */
3230
+ static void do_test_txn_resumable_calls_timeout(rd_bool_t do_commit) {
3231
+ rd_kafka_t *rk;
3232
+ rd_kafka_mock_cluster_t *mcluster;
3233
+ rd_kafka_resp_err_t err;
3234
+ rd_kafka_topic_partition_list_t *offsets;
3235
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
3236
+ int32_t coord_id = 1;
3237
+ const char *topic = "test";
3238
+ const char *transactional_id = "txnid";
3239
+ int msgcnt = 1;
3240
+ int remains = 0;
3241
+
3242
+ SUB_TEST("%s_transaction", do_commit ? "commit" : "abort");
3243
+
3244
+ rk = create_txn_producer(&mcluster, transactional_id, 1, NULL);
3245
+
3246
+ err = rd_kafka_mock_topic_create(mcluster, topic, 1, 1);
3247
+ TEST_ASSERT(!err, "Failed to create topic: %s", rd_kafka_err2str(err));
3248
+
3249
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
3250
+ coord_id);
3251
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, coord_id);
3252
+
3253
+ TEST_SAY("Starting transaction\n");
3254
+ TEST_SAY("Delaying first two InitProducerIdRequests by 500ms\n");
3255
+ rd_kafka_mock_broker_push_request_error_rtts(
3256
+ mcluster, coord_id, RD_KAFKAP_InitProducerId, 2,
3257
+ RD_KAFKA_RESP_ERR_NO_ERROR, 500, RD_KAFKA_RESP_ERR_NO_ERROR, 500);
3258
+
3259
+ RETRY_TXN_CALL__(
3260
+ rd_kafka_init_transactions(rk, 100),
3261
+ TXN_CALL_EXPECT_ERROR__(rd_kafka_abort_transaction(rk, -1),
3262
+ RD_KAFKA_RESP_ERR__CONFLICT));
3263
+
3264
+ RETRY_TXN_CALL__(rd_kafka_begin_transaction(rk), /*none*/);
3265
+
3266
+
3267
+ TEST_SAY("Delaying ProduceRequests by 3000ms\n");
3268
+ rd_kafka_mock_broker_push_request_error_rtts(
3269
+ mcluster, coord_id, RD_KAFKAP_Produce, 1,
3270
+ RD_KAFKA_RESP_ERR_NO_ERROR, 3000);
3271
+
3272
+ test_produce_msgs2_nowait(rk, topic, 0, RD_KAFKA_PARTITION_UA, 0,
3273
+ msgcnt, NULL, 0, &remains);
3274
+
3275
+
3276
+ TEST_SAY("Delaying SendOffsetsToTransaction by 400ms\n");
3277
+ rd_kafka_mock_broker_push_request_error_rtts(
3278
+ mcluster, coord_id, RD_KAFKAP_AddOffsetsToTxn, 1,
3279
+ RD_KAFKA_RESP_ERR_NO_ERROR, 400);
3280
+ offsets = rd_kafka_topic_partition_list_new(1);
3281
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 0)->offset = 12;
3282
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
3283
+
3284
+ /* This is not a resumable call on timeout */
3285
+ TEST_CALL_ERROR__(
3286
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
3287
+
3288
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
3289
+ rd_kafka_topic_partition_list_destroy(offsets);
3290
+
3291
+
3292
+ TEST_SAY("Delaying EndTxnRequests by 1200ms\n");
3293
+ rd_kafka_mock_broker_push_request_error_rtts(
3294
+ mcluster, coord_id, RD_KAFKAP_EndTxn, 1, RD_KAFKA_RESP_ERR_NO_ERROR,
3295
+ 1200);
3296
+
3297
+ /* Committing/aborting the transaction will also be delayed by the
3298
+ * previous accumulated remaining delays. */
3299
+
3300
+ if (do_commit) {
3301
+ TEST_SAY("Committing transaction\n");
3302
+
3303
+ RETRY_TXN_CALL__(
3304
+ rd_kafka_commit_transaction(rk, 100),
3305
+ TXN_CALL_EXPECT_ERROR__(rd_kafka_abort_transaction(rk, -1),
3306
+ RD_KAFKA_RESP_ERR__CONFLICT));
3307
+ } else {
3308
+ TEST_SAY("Aborting transaction\n");
3309
+
3310
+ RETRY_TXN_CALL__(
3311
+ rd_kafka_abort_transaction(rk, 100),
3312
+ TXN_CALL_EXPECT_ERROR__(rd_kafka_commit_transaction(rk, -1),
3313
+ RD_KAFKA_RESP_ERR__CONFLICT));
3314
+ }
3315
+
3316
+ rd_kafka_destroy(rk);
3317
+
3318
+ SUB_TEST_PASS();
3319
+ }
3320
+
3321
+
3322
+ /**
3323
+ * @brief Verify that resuming timed out calls that after the timeout, but
3324
+ * before the resuming call, would error out.
3325
+ */
3326
+ static void do_test_txn_resumable_calls_timeout_error(rd_bool_t do_commit) {
3327
+ rd_kafka_t *rk;
3328
+ rd_kafka_mock_cluster_t *mcluster;
3329
+ rd_kafka_resp_err_t err;
3330
+ int32_t coord_id = 1;
3331
+ const char *topic = "test";
3332
+ const char *transactional_id = "txnid";
3333
+ int msgcnt = 1;
3334
+ int remains = 0;
3335
+ rd_kafka_error_t *error;
3336
+
3337
+ SUB_TEST_QUICK("%s_transaction", do_commit ? "commit" : "abort");
3338
+
3339
+ rk = create_txn_producer(&mcluster, transactional_id, 1, NULL);
3340
+
3341
+ err = rd_kafka_mock_topic_create(mcluster, topic, 1, 1);
3342
+ TEST_ASSERT(!err, "Failed to create topic: %s", rd_kafka_err2str(err));
3343
+
3344
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", transactional_id,
3345
+ coord_id);
3346
+ rd_kafka_mock_partition_set_leader(mcluster, topic, 0, coord_id);
3347
+
3348
+ TEST_SAY("Starting transaction\n");
3349
+
3350
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
3351
+
3352
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
3353
+
3354
+ test_produce_msgs2_nowait(rk, topic, 0, RD_KAFKA_PARTITION_UA, 0,
3355
+ msgcnt, NULL, 0, &remains);
3356
+
3357
+
3358
+ TEST_SAY("Fail EndTxn fatally after 2000ms\n");
3359
+ rd_kafka_mock_broker_push_request_error_rtts(
3360
+ mcluster, coord_id, RD_KAFKAP_EndTxn, 1,
3361
+ RD_KAFKA_RESP_ERR_INVALID_TXN_STATE, 2000);
3362
+
3363
+ if (do_commit) {
3364
+ TEST_SAY("Committing transaction\n");
3365
+
3366
+ TXN_CALL_EXPECT_ERROR__(rd_kafka_commit_transaction(rk, 500),
3367
+ RD_KAFKA_RESP_ERR__TIMED_OUT);
3368
+
3369
+ /* Sleep so that the background EndTxn fails locally and sets
3370
+ * an error result. */
3371
+ rd_sleep(3);
3372
+
3373
+ error = rd_kafka_commit_transaction(rk, -1);
3374
+
3375
+ } else {
3376
+ TEST_SAY("Aborting transaction\n");
3377
+
3378
+ TXN_CALL_EXPECT_ERROR__(rd_kafka_commit_transaction(rk, 500),
3379
+ RD_KAFKA_RESP_ERR__TIMED_OUT);
3380
+
3381
+ /* Sleep so that the background EndTxn fails locally and sets
3382
+ * an error result. */
3383
+ rd_sleep(3);
3384
+
3385
+ error = rd_kafka_commit_transaction(rk, -1);
3386
+ }
3387
+
3388
+ TEST_ASSERT(error != NULL && rd_kafka_error_is_fatal(error),
3389
+ "Expected fatal error, not %s",
3390
+ rd_kafka_error_string(error));
3391
+ TEST_ASSERT(rd_kafka_error_code(error) ==
3392
+ RD_KAFKA_RESP_ERR_INVALID_TXN_STATE,
3393
+ "Expected error INVALID_TXN_STATE, got %s",
3394
+ rd_kafka_error_name(error));
3395
+ rd_kafka_error_destroy(error);
3396
+
3397
+ rd_kafka_destroy(rk);
3398
+
3399
+ SUB_TEST_PASS();
3400
+ }
3401
+
3402
+
3403
+ /**
3404
+ * @brief Concurrent transaction API calls are not permitted.
3405
+ * This test makes sure they're properly enforced.
3406
+ *
3407
+ * For each transactional API, call it with a 5s timeout, and during that time
3408
+ * from another thread call transactional APIs, one by one, and verify that
3409
+ * we get an ERR__CONFLICT error back in the second thread.
3410
+ *
3411
+ * We use a mutex for synchronization, the main thread will hold the lock
3412
+ * when not calling an API but release it just prior to calling.
3413
+ * The other thread will acquire the lock, sleep, and hold the lock while
3414
+ * calling the concurrent API that should fail immediately, releasing the lock
3415
+ * when done.
3416
+ *
3417
+ */
3418
+
3419
+ struct _txn_concurrent_state {
3420
+ const char *api;
3421
+ mtx_t lock;
3422
+ rd_kafka_t *rk;
3423
+ struct test *test;
3424
+ };
3425
+
3426
+ static int txn_concurrent_thread_main(void *arg) {
3427
+ struct _txn_concurrent_state *state = arg;
3428
+ static const char *apis[] = {
3429
+ "init_transactions", "begin_transaction",
3430
+ "send_offsets_to_transaction", "commit_transaction",
3431
+ "abort_transaction", NULL};
3432
+ rd_kafka_t *rk = state->rk;
3433
+ const char *main_api = NULL;
3434
+ int i;
3435
+
3436
+ /* Update TLS variable so TEST_..() macros work */
3437
+ test_curr = state->test;
3438
+
3439
+ while (1) {
3440
+ const char *api = NULL;
3441
+ const int timeout_ms = 10000;
3442
+ rd_kafka_error_t *error = NULL;
3443
+ rd_kafka_resp_err_t exp_err;
3444
+ test_timing_t duration;
3445
+
3446
+ /* Wait for other thread's txn call to start, then sleep a bit
3447
+ * to increase the chance of that call has really begun. */
3448
+ mtx_lock(&state->lock);
3449
+
3450
+ if (state->api && state->api == main_api) {
3451
+ /* Main thread is still blocking on the last API call */
3452
+ TEST_SAY("Waiting for main thread to finish %s()\n",
3453
+ main_api);
3454
+ mtx_unlock(&state->lock);
3455
+ rd_sleep(1);
3456
+ continue;
3457
+ } else if (!(main_api = state->api)) {
3458
+ mtx_unlock(&state->lock);
3459
+ break;
3460
+ }
3461
+
3462
+ rd_sleep(1);
3463
+
3464
+ for (i = 0; (api = apis[i]) != NULL; i++) {
3465
+ TEST_SAY(
3466
+ "Triggering concurrent %s() call while "
3467
+ "main is in %s() call\n",
3468
+ api, main_api);
3469
+ TIMING_START(&duration, "%s", api);
3470
+
3471
+ if (!strcmp(api, "init_transactions"))
3472
+ error =
3473
+ rd_kafka_init_transactions(rk, timeout_ms);
3474
+ else if (!strcmp(api, "begin_transaction"))
3475
+ error = rd_kafka_begin_transaction(rk);
3476
+ else if (!strcmp(api, "send_offsets_to_transaction")) {
3477
+ rd_kafka_topic_partition_list_t *offsets =
3478
+ rd_kafka_topic_partition_list_new(1);
3479
+ rd_kafka_consumer_group_metadata_t *cgmetadata =
3480
+ rd_kafka_consumer_group_metadata_new(
3481
+ "mygroupid");
3482
+ rd_kafka_topic_partition_list_add(
3483
+ offsets, "srctopic4", 0)
3484
+ ->offset = 12;
3485
+
3486
+ error = rd_kafka_send_offsets_to_transaction(
3487
+ rk, offsets, cgmetadata, -1);
3488
+ rd_kafka_consumer_group_metadata_destroy(
3489
+ cgmetadata);
3490
+ rd_kafka_topic_partition_list_destroy(offsets);
3491
+ } else if (!strcmp(api, "commit_transaction"))
3492
+ error =
3493
+ rd_kafka_commit_transaction(rk, timeout_ms);
3494
+ else if (!strcmp(api, "abort_transaction"))
3495
+ error =
3496
+ rd_kafka_abort_transaction(rk, timeout_ms);
3497
+ else
3498
+ TEST_FAIL("Unknown API: %s", api);
3499
+
3500
+ TIMING_STOP(&duration);
3501
+
3502
+ TEST_SAY_ERROR(error, "Conflicting %s() call: ", api);
3503
+ TEST_ASSERT(error,
3504
+ "Expected conflicting %s() call to fail",
3505
+ api);
3506
+
3507
+ exp_err = !strcmp(api, main_api)
3508
+ ? RD_KAFKA_RESP_ERR__PREV_IN_PROGRESS
3509
+ : RD_KAFKA_RESP_ERR__CONFLICT;
3510
+
3511
+ TEST_ASSERT(rd_kafka_error_code(error) == exp_err,
3512
+
3513
+ "Conflicting %s(): Expected %s, not %s",
3514
+ api, rd_kafka_err2str(exp_err),
3515
+ rd_kafka_error_name(error));
3516
+ TEST_ASSERT(
3517
+ rd_kafka_error_is_retriable(error),
3518
+ "Conflicting %s(): Expected retriable error", api);
3519
+ rd_kafka_error_destroy(error);
3520
+ /* These calls should fail immediately */
3521
+ TIMING_ASSERT(&duration, 0, 100);
3522
+ }
3523
+
3524
+ mtx_unlock(&state->lock);
3525
+ }
3526
+
3527
+ return 0;
3528
+ }
3529
+
3530
+ static void do_test_txn_concurrent_operations(rd_bool_t do_commit) {
3531
+ rd_kafka_t *rk;
3532
+ rd_kafka_mock_cluster_t *mcluster;
3533
+ int32_t coord_id = 1;
3534
+ rd_kafka_resp_err_t err;
3535
+ const char *topic = "test";
3536
+ const char *transactional_id = "txnid";
3537
+ int remains = 0;
3538
+ thrd_t thrd;
3539
+ struct _txn_concurrent_state state = RD_ZERO_INIT;
3540
+ rd_kafka_topic_partition_list_t *offsets;
3541
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
3542
+
3543
+ SUB_TEST("%s", do_commit ? "commit" : "abort");
3544
+
3545
+ test_timeout_set(90);
3546
+
3547
+ /* We need to override the value of socket.connection.setup.timeout.ms
3548
+ * to be at least 2*RTT of the mock broker. This is because the first
3549
+ * ApiVersion request will fail, since we make the request with v3, and
3550
+ * the mock broker's MaxVersion is 2, so the request is retried with v0.
3551
+ * We use the value 3*RTT to add some buffer.
3552
+ */
3553
+ rk = create_txn_producer(&mcluster, transactional_id, 1,
3554
+ "socket.connection.setup.timeout.ms", "15000",
3555
+ NULL);
3556
+
3557
+ /* Set broker RTT to 3.5s so that the background thread has ample
3558
+ * time to call its conflicting APIs.
3559
+ * This value must be less than socket.connection.setup.timeout.ms/2. */
3560
+ rd_kafka_mock_broker_set_rtt(mcluster, coord_id, 3500);
3561
+
3562
+ err = rd_kafka_mock_topic_create(mcluster, topic, 1, 1);
3563
+ TEST_ASSERT(!err, "Failed to create topic: %s", rd_kafka_err2str(err));
3564
+
3565
+ /* Set up shared state between us and the concurrent thread */
3566
+ mtx_init(&state.lock, mtx_plain);
3567
+ state.test = test_curr;
3568
+ state.rk = rk;
3569
+
3570
+ /* We release the lock only while calling the TXN API */
3571
+ mtx_lock(&state.lock);
3572
+
3573
+ /* Spin up concurrent thread */
3574
+ if (thrd_create(&thrd, txn_concurrent_thread_main, (void *)&state) !=
3575
+ thrd_success)
3576
+ TEST_FAIL("Failed to create thread");
3577
+
3578
+ #define _start_call(callname) \
3579
+ do { \
3580
+ state.api = callname; \
3581
+ mtx_unlock(&state.lock); \
3582
+ } while (0)
3583
+ #define _end_call() mtx_lock(&state.lock)
3584
+
3585
+ _start_call("init_transactions");
3586
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
3587
+ _end_call();
3588
+
3589
+ /* This call doesn't block, so can't really be tested concurrently. */
3590
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
3591
+
3592
+ test_produce_msgs2_nowait(rk, topic, 0, RD_KAFKA_PARTITION_UA, 0, 10,
3593
+ NULL, 0, &remains);
3594
+
3595
+ _start_call("send_offsets_to_transaction");
3596
+ offsets = rd_kafka_topic_partition_list_new(1);
3597
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 0)->offset = 12;
3598
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
3599
+
3600
+ TEST_CALL_ERROR__(
3601
+ rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata, -1));
3602
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
3603
+ rd_kafka_topic_partition_list_destroy(offsets);
3604
+ _end_call();
3605
+
3606
+ if (do_commit) {
3607
+ _start_call("commit_transaction");
3608
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(rk, -1));
3609
+ _end_call();
3610
+ } else {
3611
+ _start_call("abort_transaction");
3612
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(rk, -1));
3613
+ _end_call();
3614
+ }
3615
+
3616
+ /* Signal completion to background thread */
3617
+ state.api = NULL;
3618
+
3619
+ mtx_unlock(&state.lock);
3620
+
3621
+ thrd_join(thrd, NULL);
3622
+
3623
+ rd_kafka_destroy(rk);
3624
+
3625
+ mtx_destroy(&state.lock);
3626
+
3627
+ SUB_TEST_PASS();
3628
+ }
3629
+
3630
+
3631
+ /**
3632
+ * @brief KIP-360: Test that fatal idempotence errors triggers abortable
3633
+ * transaction errors, but let the broker-side abort of the
3634
+ * transaction fail with a fencing error.
3635
+ * Should raise a fatal error.
3636
+ *
3637
+ * @param error_code Which error code EndTxn should fail with.
3638
+ * Either RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH (older)
3639
+ * or RD_KAFKA_RESP_ERR_PRODUCER_FENCED (newer).
3640
+ */
3641
+ static void do_test_txn_fenced_abort(rd_kafka_resp_err_t error_code) {
3642
+ rd_kafka_t *rk;
3643
+ rd_kafka_mock_cluster_t *mcluster;
3644
+ rd_kafka_error_t *error;
3645
+ int32_t txn_coord = 2;
3646
+ const char *txnid = "myTxnId";
3647
+ char errstr[512];
3648
+ rd_kafka_resp_err_t fatal_err;
3649
+ size_t errors_cnt;
3650
+
3651
+ SUB_TEST_QUICK("With error %s", rd_kafka_err2name(error_code));
3652
+
3653
+ rk = create_txn_producer(&mcluster, txnid, 3, "batch.num.messages", "1",
3654
+ NULL);
3655
+
3656
+ rd_kafka_mock_coordinator_set(mcluster, "transaction", txnid,
3657
+ txn_coord);
3658
+
3659
+ test_curr->ignore_dr_err = rd_true;
3660
+ test_curr->is_fatal_cb = error_is_fatal_cb;
3661
+ allowed_error = RD_KAFKA_RESP_ERR__FENCED;
3662
+
3663
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
3664
+
3665
+ /*
3666
+ * Start a transaction
3667
+ */
3668
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
3669
+
3670
+
3671
+ /* Produce a message without error first */
3672
+ TEST_CALL_ERR__(rd_kafka_producev(
3673
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
3674
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
3675
+
3676
+ test_flush(rk, -1);
3677
+
3678
+ /* Fail abort transaction */
3679
+ rd_kafka_mock_broker_push_request_error_rtts(
3680
+ mcluster, txn_coord, RD_KAFKAP_EndTxn, 1, error_code, 0);
3681
+
3682
+ /* Fail the PID reinit */
3683
+ rd_kafka_mock_broker_push_request_error_rtts(
3684
+ mcluster, txn_coord, RD_KAFKAP_InitProducerId, 1, error_code, 0);
3685
+
3686
+ /* Produce a message, let it fail with a fatal idempo error. */
3687
+ rd_kafka_mock_push_request_errors(
3688
+ mcluster, RD_KAFKAP_Produce, 1,
3689
+ RD_KAFKA_RESP_ERR_UNKNOWN_PRODUCER_ID);
3690
+
3691
+ TEST_CALL_ERR__(rd_kafka_producev(
3692
+ rk, RD_KAFKA_V_TOPIC("mytopic"), RD_KAFKA_V_PARTITION(0),
3693
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END));
3694
+
3695
+ test_flush(rk, -1);
3696
+
3697
+ /* Abort the transaction, should fail with a fatal error */
3698
+ error = rd_kafka_abort_transaction(rk, -1);
3699
+ TEST_ASSERT(error != NULL, "Expected abort_transaction() to fail");
3700
+
3701
+ TEST_SAY_ERROR(error, "abort_transaction() failed: ");
3702
+ TEST_ASSERT(rd_kafka_error_is_fatal(error), "Expected a fatal error");
3703
+ rd_kafka_error_destroy(error);
3704
+
3705
+ fatal_err = rd_kafka_fatal_error(rk, errstr, sizeof(errstr));
3706
+ TEST_ASSERT(fatal_err, "Expected a fatal error to have been raised");
3707
+ TEST_SAY("Fatal error: %s: %s\n", rd_kafka_err2name(fatal_err), errstr);
3708
+
3709
+ /* Verify that the producer sent the expected number of EndTxn requests
3710
+ * by inspecting the mock broker error stack,
3711
+ * which should now be empty. */
3712
+ if (rd_kafka_mock_broker_error_stack_cnt(
3713
+ mcluster, txn_coord, RD_KAFKAP_EndTxn, &errors_cnt)) {
3714
+ TEST_FAIL(
3715
+ "Broker error count should succeed for API %s"
3716
+ " on broker %" PRId32,
3717
+ rd_kafka_ApiKey2str(RD_KAFKAP_EndTxn), txn_coord);
3718
+ }
3719
+ /* Checks all the RD_KAFKAP_EndTxn responses have been consumed */
3720
+ TEST_ASSERT(errors_cnt == 0,
3721
+ "Expected error count 0 for API %s, found %zu",
3722
+ rd_kafka_ApiKey2str(RD_KAFKAP_EndTxn), errors_cnt);
3723
+
3724
+ if (rd_kafka_mock_broker_error_stack_cnt(
3725
+ mcluster, txn_coord, RD_KAFKAP_InitProducerId, &errors_cnt)) {
3726
+ TEST_FAIL(
3727
+ "Broker error count should succeed for API %s"
3728
+ " on broker %" PRId32,
3729
+ rd_kafka_ApiKey2str(RD_KAFKAP_InitProducerId), txn_coord);
3730
+ }
3731
+ /* Checks none of the RD_KAFKAP_InitProducerId responses have been
3732
+ * consumed
3733
+ */
3734
+ TEST_ASSERT(errors_cnt == 1,
3735
+ "Expected error count 1 for API %s, found %zu",
3736
+ rd_kafka_ApiKey2str(RD_KAFKAP_InitProducerId), errors_cnt);
3737
+
3738
+ /* All done */
3739
+ rd_kafka_destroy(rk);
3740
+
3741
+ allowed_error = RD_KAFKA_RESP_ERR_NO_ERROR;
3742
+
3743
+ SUB_TEST_PASS();
3744
+ }
3745
+
3746
+
3747
+ /**
3748
+ * @brief Test that the TxnOffsetCommit op doesn't retry without waiting
3749
+ * if the coordinator is found but not available, causing too frequent retries.
3750
+ */
3751
+ static void
3752
+ do_test_txn_offset_commit_doesnt_retry_too_quickly(rd_bool_t times_out) {
3753
+ rd_kafka_t *rk;
3754
+ rd_kafka_mock_cluster_t *mcluster;
3755
+ rd_kafka_resp_err_t err;
3756
+ rd_kafka_topic_partition_list_t *offsets;
3757
+ rd_kafka_consumer_group_metadata_t *cgmetadata;
3758
+ rd_kafka_error_t *error;
3759
+ int timeout;
3760
+
3761
+ SUB_TEST_QUICK("times_out=%s", RD_STR_ToF(times_out));
3762
+
3763
+ rk = create_txn_producer(&mcluster, "txnid", 3, NULL);
3764
+
3765
+ test_curr->ignore_dr_err = rd_true;
3766
+
3767
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, 5000));
3768
+
3769
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(rk));
3770
+
3771
+ err = rd_kafka_producev(rk, RD_KAFKA_V_TOPIC("mytopic"),
3772
+ RD_KAFKA_V_VALUE("hi", 2), RD_KAFKA_V_END);
3773
+ TEST_ASSERT(!err, "produce failed: %s", rd_kafka_err2str(err));
3774
+
3775
+ /* Wait for messages to be delivered */
3776
+ test_flush(rk, 5000);
3777
+
3778
+ /*
3779
+ * Fail TxnOffsetCommit with COORDINATOR_NOT_AVAILABLE
3780
+ * repeatedly.
3781
+ */
3782
+ rd_kafka_mock_push_request_errors(
3783
+ mcluster, RD_KAFKAP_TxnOffsetCommit, 4,
3784
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
3785
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
3786
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
3787
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE);
3788
+
3789
+ offsets = rd_kafka_topic_partition_list_new(1);
3790
+ rd_kafka_topic_partition_list_add(offsets, "srctopic4", 3)->offset = 1;
3791
+
3792
+ cgmetadata = rd_kafka_consumer_group_metadata_new("mygroupid");
3793
+
3794
+ /* The retry delay is 500ms, with 4 retries it should take at least
3795
+ * 2000ms for this call to succeed. */
3796
+ timeout = times_out ? 500 : 4000;
3797
+ error = rd_kafka_send_offsets_to_transaction(rk, offsets, cgmetadata,
3798
+ timeout);
3799
+ rd_kafka_consumer_group_metadata_destroy(cgmetadata);
3800
+ rd_kafka_topic_partition_list_destroy(offsets);
3801
+
3802
+ if (times_out) {
3803
+ TEST_ASSERT(rd_kafka_error_code(error) ==
3804
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE,
3805
+ "expected %s, got: %s",
3806
+ rd_kafka_err2name(
3807
+ RD_KAFKA_RESP_ERR_COORDINATOR_NOT_AVAILABLE),
3808
+ rd_kafka_err2str(rd_kafka_error_code(error)));
3809
+ } else {
3810
+ TEST_ASSERT(rd_kafka_error_code(error) ==
3811
+ RD_KAFKA_RESP_ERR_NO_ERROR,
3812
+ "expected \"Success\", found: %s",
3813
+ rd_kafka_err2str(rd_kafka_error_code(error)));
3814
+ }
3815
+ rd_kafka_error_destroy(error);
3816
+
3817
+ /* All done */
3818
+ rd_kafka_destroy(rk);
3819
+
3820
+ SUB_TEST_PASS();
3821
+ }
3822
+
3823
+
3824
+ int main_0105_transactions_mock(int argc, char **argv) {
3825
+ TEST_SKIP_MOCK_CLUSTER(0);
3826
+
3827
+ do_test_txn_recoverable_errors();
3828
+
3829
+ do_test_txn_fatal_idempo_errors();
3830
+
3831
+ do_test_txn_fenced_reinit(RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH);
3832
+ do_test_txn_fenced_reinit(RD_KAFKA_RESP_ERR_PRODUCER_FENCED);
3833
+
3834
+ do_test_txn_req_cnt();
3835
+
3836
+ do_test_txn_requires_abort_errors();
3837
+
3838
+ do_test_txn_slow_reinit(rd_false);
3839
+ do_test_txn_slow_reinit(rd_true);
3840
+
3841
+ /* Just do a subset of tests in quick mode */
3842
+ if (test_quick)
3843
+ return 0;
3844
+
3845
+ do_test_txn_endtxn_errors();
3846
+
3847
+ do_test_txn_endtxn_infinite();
3848
+
3849
+ do_test_txn_endtxn_timeout();
3850
+
3851
+ do_test_txn_endtxn_timeout_inflight();
3852
+
3853
+ /* Bring down the coordinator */
3854
+ do_test_txn_broker_down_in_txn(rd_true);
3855
+
3856
+ /* Bring down partition leader */
3857
+ do_test_txn_broker_down_in_txn(rd_false);
3858
+
3859
+ do_test_txns_not_supported();
3860
+
3861
+ do_test_txns_send_offsets_concurrent_is_retried();
3862
+
3863
+ do_test_txns_send_offsets_non_eligible();
3864
+
3865
+ do_test_txn_coord_req_destroy();
3866
+
3867
+ do_test_txn_coord_req_multi_find();
3868
+
3869
+ do_test_txn_addparts_req_multi();
3870
+
3871
+ do_test_txns_no_timeout_crash();
3872
+
3873
+ do_test_txn_auth_failure(
3874
+ RD_KAFKAP_InitProducerId,
3875
+ RD_KAFKA_RESP_ERR_CLUSTER_AUTHORIZATION_FAILED);
3876
+
3877
+ do_test_txn_auth_failure(
3878
+ RD_KAFKAP_FindCoordinator,
3879
+ RD_KAFKA_RESP_ERR_CLUSTER_AUTHORIZATION_FAILED);
3880
+
3881
+ do_test_txn_flush_timeout();
3882
+
3883
+ do_test_unstable_offset_commit();
3884
+
3885
+ do_test_commit_after_msg_timeout();
3886
+
3887
+ do_test_txn_switch_coordinator();
3888
+
3889
+ do_test_txn_switch_coordinator_refresh();
3890
+
3891
+ do_test_out_of_order_seq();
3892
+
3893
+ do_test_topic_disappears_for_awhile();
3894
+
3895
+ do_test_disconnected_group_coord(rd_false);
3896
+
3897
+ do_test_disconnected_group_coord(rd_true);
3898
+
3899
+ do_test_txn_coordinator_null_not_fatal();
3900
+
3901
+ do_test_txn_resumable_calls_timeout(rd_true);
3902
+
3903
+ do_test_txn_resumable_calls_timeout(rd_false);
3904
+
3905
+ do_test_txn_resumable_calls_timeout_error(rd_true);
3906
+
3907
+ do_test_txn_resumable_calls_timeout_error(rd_false);
3908
+ do_test_txn_resumable_init();
3909
+
3910
+ do_test_txn_concurrent_operations(rd_true /*commit*/);
3911
+
3912
+ do_test_txn_concurrent_operations(rd_false /*abort*/);
3913
+
3914
+ do_test_txn_fenced_abort(RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH);
3915
+
3916
+ do_test_txn_fenced_abort(RD_KAFKA_RESP_ERR_PRODUCER_FENCED);
3917
+
3918
+ do_test_txn_offset_commit_doesnt_retry_too_quickly(rd_true);
3919
+
3920
+ do_test_txn_offset_commit_doesnt_retry_too_quickly(rd_false);
3921
+
3922
+ return 0;
3923
+ }