@point3/node-rdkafka 3.6.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (707) hide show
  1. package/LICENSE.txt +20 -0
  2. package/README.md +636 -0
  3. package/binding.gyp +154 -0
  4. package/deps/librdkafka/.clang-format +136 -0
  5. package/deps/librdkafka/.clang-format-cpp +103 -0
  6. package/deps/librdkafka/.dir-locals.el +10 -0
  7. package/deps/librdkafka/.formatignore +33 -0
  8. package/deps/librdkafka/.gdbmacros +19 -0
  9. package/deps/librdkafka/.github/CODEOWNERS +1 -0
  10. package/deps/librdkafka/.github/ISSUE_TEMPLATE +34 -0
  11. package/deps/librdkafka/.semaphore/run-all-tests.yml +77 -0
  12. package/deps/librdkafka/.semaphore/semaphore-integration.yml +250 -0
  13. package/deps/librdkafka/.semaphore/semaphore.yml +378 -0
  14. package/deps/librdkafka/.semaphore/verify-linux-packages.yml +41 -0
  15. package/deps/librdkafka/CHANGELOG.md +2208 -0
  16. package/deps/librdkafka/CMakeLists.txt +291 -0
  17. package/deps/librdkafka/CODE_OF_CONDUCT.md +46 -0
  18. package/deps/librdkafka/CONFIGURATION.md +209 -0
  19. package/deps/librdkafka/CONTRIBUTING.md +431 -0
  20. package/deps/librdkafka/Doxyfile +2375 -0
  21. package/deps/librdkafka/INTRODUCTION.md +2481 -0
  22. package/deps/librdkafka/LICENSE +26 -0
  23. package/deps/librdkafka/LICENSE.cjson +22 -0
  24. package/deps/librdkafka/LICENSE.crc32c +28 -0
  25. package/deps/librdkafka/LICENSE.fnv1a +18 -0
  26. package/deps/librdkafka/LICENSE.hdrhistogram +27 -0
  27. package/deps/librdkafka/LICENSE.lz4 +26 -0
  28. package/deps/librdkafka/LICENSE.murmur2 +25 -0
  29. package/deps/librdkafka/LICENSE.nanopb +22 -0
  30. package/deps/librdkafka/LICENSE.opentelemetry +203 -0
  31. package/deps/librdkafka/LICENSE.pycrc +23 -0
  32. package/deps/librdkafka/LICENSE.queue +31 -0
  33. package/deps/librdkafka/LICENSE.regexp +5 -0
  34. package/deps/librdkafka/LICENSE.snappy +36 -0
  35. package/deps/librdkafka/LICENSE.tinycthread +26 -0
  36. package/deps/librdkafka/LICENSE.wingetopt +49 -0
  37. package/deps/librdkafka/LICENSES.txt +625 -0
  38. package/deps/librdkafka/Makefile +125 -0
  39. package/deps/librdkafka/README.md +199 -0
  40. package/deps/librdkafka/README.win32 +26 -0
  41. package/deps/librdkafka/STATISTICS.md +624 -0
  42. package/deps/librdkafka/configure +214 -0
  43. package/deps/librdkafka/configure.self +331 -0
  44. package/deps/librdkafka/debian/changelog +111 -0
  45. package/deps/librdkafka/debian/compat +1 -0
  46. package/deps/librdkafka/debian/control +71 -0
  47. package/deps/librdkafka/debian/copyright +99 -0
  48. package/deps/librdkafka/debian/gbp.conf +9 -0
  49. package/deps/librdkafka/debian/librdkafka++1.install +1 -0
  50. package/deps/librdkafka/debian/librdkafka-dev.examples +2 -0
  51. package/deps/librdkafka/debian/librdkafka-dev.install +9 -0
  52. package/deps/librdkafka/debian/librdkafka1.docs +5 -0
  53. package/deps/librdkafka/debian/librdkafka1.install +1 -0
  54. package/deps/librdkafka/debian/librdkafka1.symbols +135 -0
  55. package/deps/librdkafka/debian/rules +19 -0
  56. package/deps/librdkafka/debian/source/format +1 -0
  57. package/deps/librdkafka/debian/watch +2 -0
  58. package/deps/librdkafka/dev-conf.sh +123 -0
  59. package/deps/librdkafka/examples/CMakeLists.txt +79 -0
  60. package/deps/librdkafka/examples/Makefile +167 -0
  61. package/deps/librdkafka/examples/README.md +42 -0
  62. package/deps/librdkafka/examples/alter_consumer_group_offsets.c +338 -0
  63. package/deps/librdkafka/examples/consumer.c +271 -0
  64. package/deps/librdkafka/examples/delete_records.c +233 -0
  65. package/deps/librdkafka/examples/describe_cluster.c +322 -0
  66. package/deps/librdkafka/examples/describe_consumer_groups.c +455 -0
  67. package/deps/librdkafka/examples/describe_topics.c +427 -0
  68. package/deps/librdkafka/examples/elect_leaders.c +317 -0
  69. package/deps/librdkafka/examples/globals.json +11 -0
  70. package/deps/librdkafka/examples/idempotent_producer.c +344 -0
  71. package/deps/librdkafka/examples/incremental_alter_configs.c +347 -0
  72. package/deps/librdkafka/examples/kafkatest_verifiable_client.cpp +945 -0
  73. package/deps/librdkafka/examples/list_consumer_group_offsets.c +359 -0
  74. package/deps/librdkafka/examples/list_consumer_groups.c +365 -0
  75. package/deps/librdkafka/examples/list_offsets.c +327 -0
  76. package/deps/librdkafka/examples/misc.c +287 -0
  77. package/deps/librdkafka/examples/openssl_engine_example.cpp +248 -0
  78. package/deps/librdkafka/examples/producer.c +251 -0
  79. package/deps/librdkafka/examples/producer.cpp +228 -0
  80. package/deps/librdkafka/examples/rdkafka_complex_consumer_example.c +617 -0
  81. package/deps/librdkafka/examples/rdkafka_complex_consumer_example.cpp +467 -0
  82. package/deps/librdkafka/examples/rdkafka_consume_batch.cpp +264 -0
  83. package/deps/librdkafka/examples/rdkafka_example.c +853 -0
  84. package/deps/librdkafka/examples/rdkafka_example.cpp +679 -0
  85. package/deps/librdkafka/examples/rdkafka_performance.c +1781 -0
  86. package/deps/librdkafka/examples/transactions-older-broker.c +668 -0
  87. package/deps/librdkafka/examples/transactions.c +665 -0
  88. package/deps/librdkafka/examples/user_scram.c +491 -0
  89. package/deps/librdkafka/examples/win_ssl_cert_store.cpp +396 -0
  90. package/deps/librdkafka/lds-gen.py +73 -0
  91. package/deps/librdkafka/mainpage.doxy +40 -0
  92. package/deps/librdkafka/mklove/Makefile.base +329 -0
  93. package/deps/librdkafka/mklove/modules/configure.atomics +144 -0
  94. package/deps/librdkafka/mklove/modules/configure.base +2484 -0
  95. package/deps/librdkafka/mklove/modules/configure.builtin +70 -0
  96. package/deps/librdkafka/mklove/modules/configure.cc +186 -0
  97. package/deps/librdkafka/mklove/modules/configure.cxx +8 -0
  98. package/deps/librdkafka/mklove/modules/configure.fileversion +65 -0
  99. package/deps/librdkafka/mklove/modules/configure.gitversion +29 -0
  100. package/deps/librdkafka/mklove/modules/configure.good_cflags +18 -0
  101. package/deps/librdkafka/mklove/modules/configure.host +132 -0
  102. package/deps/librdkafka/mklove/modules/configure.lib +49 -0
  103. package/deps/librdkafka/mklove/modules/configure.libcurl +99 -0
  104. package/deps/librdkafka/mklove/modules/configure.libsasl2 +36 -0
  105. package/deps/librdkafka/mklove/modules/configure.libssl +147 -0
  106. package/deps/librdkafka/mklove/modules/configure.libzstd +58 -0
  107. package/deps/librdkafka/mklove/modules/configure.parseversion +95 -0
  108. package/deps/librdkafka/mklove/modules/configure.pic +16 -0
  109. package/deps/librdkafka/mklove/modules/configure.socket +20 -0
  110. package/deps/librdkafka/mklove/modules/configure.zlib +61 -0
  111. package/deps/librdkafka/mklove/modules/patches/README.md +8 -0
  112. package/deps/librdkafka/mklove/modules/patches/libcurl.0000-no-runtime-linking-check.patch +11 -0
  113. package/deps/librdkafka/mklove/modules/patches/libssl.0000-osx-rand-include-fix-OpenSSL-PR16409.patch +56 -0
  114. package/deps/librdkafka/packaging/RELEASE.md +319 -0
  115. package/deps/librdkafka/packaging/alpine/build-alpine.sh +38 -0
  116. package/deps/librdkafka/packaging/archlinux/PKGBUILD +30 -0
  117. package/deps/librdkafka/packaging/cmake/Config.cmake.in +37 -0
  118. package/deps/librdkafka/packaging/cmake/Modules/FindLZ4.cmake +38 -0
  119. package/deps/librdkafka/packaging/cmake/Modules/FindZSTD.cmake +27 -0
  120. package/deps/librdkafka/packaging/cmake/Modules/LICENSE.FindZstd +178 -0
  121. package/deps/librdkafka/packaging/cmake/README.md +38 -0
  122. package/deps/librdkafka/packaging/cmake/config.h.in +52 -0
  123. package/deps/librdkafka/packaging/cmake/parseversion.cmake +60 -0
  124. package/deps/librdkafka/packaging/cmake/rdkafka.pc.in +12 -0
  125. package/deps/librdkafka/packaging/cmake/try_compile/atomic_32_test.c +8 -0
  126. package/deps/librdkafka/packaging/cmake/try_compile/atomic_64_test.c +8 -0
  127. package/deps/librdkafka/packaging/cmake/try_compile/c11threads_test.c +14 -0
  128. package/deps/librdkafka/packaging/cmake/try_compile/crc32c_hw_test.c +27 -0
  129. package/deps/librdkafka/packaging/cmake/try_compile/dlopen_test.c +11 -0
  130. package/deps/librdkafka/packaging/cmake/try_compile/libsasl2_test.c +7 -0
  131. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_darwin_test.c +6 -0
  132. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_freebsd_test.c +7 -0
  133. package/deps/librdkafka/packaging/cmake/try_compile/pthread_setname_gnu_test.c +5 -0
  134. package/deps/librdkafka/packaging/cmake/try_compile/rand_r_test.c +7 -0
  135. package/deps/librdkafka/packaging/cmake/try_compile/rdkafka_setup.cmake +122 -0
  136. package/deps/librdkafka/packaging/cmake/try_compile/regex_test.c +10 -0
  137. package/deps/librdkafka/packaging/cmake/try_compile/strndup_test.c +5 -0
  138. package/deps/librdkafka/packaging/cmake/try_compile/sync_32_test.c +8 -0
  139. package/deps/librdkafka/packaging/cmake/try_compile/sync_64_test.c +8 -0
  140. package/deps/librdkafka/packaging/cp/README.md +16 -0
  141. package/deps/librdkafka/packaging/cp/check_features.c +72 -0
  142. package/deps/librdkafka/packaging/cp/verify-deb.sh +33 -0
  143. package/deps/librdkafka/packaging/cp/verify-packages.sh +69 -0
  144. package/deps/librdkafka/packaging/cp/verify-rpm.sh +32 -0
  145. package/deps/librdkafka/packaging/debian/changelog +66 -0
  146. package/deps/librdkafka/packaging/debian/compat +1 -0
  147. package/deps/librdkafka/packaging/debian/control +49 -0
  148. package/deps/librdkafka/packaging/debian/copyright +84 -0
  149. package/deps/librdkafka/packaging/debian/docs +5 -0
  150. package/deps/librdkafka/packaging/debian/gbp.conf +9 -0
  151. package/deps/librdkafka/packaging/debian/librdkafka-dev.dirs +2 -0
  152. package/deps/librdkafka/packaging/debian/librdkafka-dev.examples +2 -0
  153. package/deps/librdkafka/packaging/debian/librdkafka-dev.install +6 -0
  154. package/deps/librdkafka/packaging/debian/librdkafka-dev.substvars +1 -0
  155. package/deps/librdkafka/packaging/debian/librdkafka.dsc +16 -0
  156. package/deps/librdkafka/packaging/debian/librdkafka1-dbg.substvars +1 -0
  157. package/deps/librdkafka/packaging/debian/librdkafka1.dirs +1 -0
  158. package/deps/librdkafka/packaging/debian/librdkafka1.install +2 -0
  159. package/deps/librdkafka/packaging/debian/librdkafka1.postinst.debhelper +5 -0
  160. package/deps/librdkafka/packaging/debian/librdkafka1.postrm.debhelper +5 -0
  161. package/deps/librdkafka/packaging/debian/librdkafka1.symbols +64 -0
  162. package/deps/librdkafka/packaging/debian/rules +19 -0
  163. package/deps/librdkafka/packaging/debian/source/format +1 -0
  164. package/deps/librdkafka/packaging/debian/watch +2 -0
  165. package/deps/librdkafka/packaging/get_version.py +21 -0
  166. package/deps/librdkafka/packaging/homebrew/README.md +15 -0
  167. package/deps/librdkafka/packaging/homebrew/brew-update-pr.sh +31 -0
  168. package/deps/librdkafka/packaging/mingw-w64/configure-build-msys2-mingw-static.sh +52 -0
  169. package/deps/librdkafka/packaging/mingw-w64/configure-build-msys2-mingw.sh +21 -0
  170. package/deps/librdkafka/packaging/mingw-w64/export-variables.sh +13 -0
  171. package/deps/librdkafka/packaging/mingw-w64/run-tests.sh +6 -0
  172. package/deps/librdkafka/packaging/mingw-w64/semaphoreci-build.sh +38 -0
  173. package/deps/librdkafka/packaging/nuget/README.md +84 -0
  174. package/deps/librdkafka/packaging/nuget/artifact.py +177 -0
  175. package/deps/librdkafka/packaging/nuget/cleanup-s3.py +143 -0
  176. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-win32__bldtype-Release/msvcr120.zip +0 -0
  177. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-win32__bldtype-Release/msvcr140.zip +0 -0
  178. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-x64__bldtype-Release/msvcr120.zip +0 -0
  179. package/deps/librdkafka/packaging/nuget/common/p-common__plat-windows__arch-x64__bldtype-Release/msvcr140.zip +0 -0
  180. package/deps/librdkafka/packaging/nuget/nuget.sh +21 -0
  181. package/deps/librdkafka/packaging/nuget/nugetpackage.py +278 -0
  182. package/deps/librdkafka/packaging/nuget/packaging.py +448 -0
  183. package/deps/librdkafka/packaging/nuget/push-to-nuget.sh +21 -0
  184. package/deps/librdkafka/packaging/nuget/release.py +167 -0
  185. package/deps/librdkafka/packaging/nuget/requirements.txt +3 -0
  186. package/deps/librdkafka/packaging/nuget/staticpackage.py +178 -0
  187. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.nuspec +21 -0
  188. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.props +18 -0
  189. package/deps/librdkafka/packaging/nuget/templates/librdkafka.redist.targets +19 -0
  190. package/deps/librdkafka/packaging/nuget/zfile/__init__.py +0 -0
  191. package/deps/librdkafka/packaging/nuget/zfile/zfile.py +98 -0
  192. package/deps/librdkafka/packaging/rpm/Makefile +92 -0
  193. package/deps/librdkafka/packaging/rpm/README.md +23 -0
  194. package/deps/librdkafka/packaging/rpm/el7-x86_64.cfg +40 -0
  195. package/deps/librdkafka/packaging/rpm/librdkafka.spec +118 -0
  196. package/deps/librdkafka/packaging/rpm/mock-on-docker.sh +96 -0
  197. package/deps/librdkafka/packaging/rpm/tests/Makefile +25 -0
  198. package/deps/librdkafka/packaging/rpm/tests/README.md +8 -0
  199. package/deps/librdkafka/packaging/rpm/tests/run-test.sh +42 -0
  200. package/deps/librdkafka/packaging/rpm/tests/test-on-docker.sh +56 -0
  201. package/deps/librdkafka/packaging/rpm/tests/test.c +77 -0
  202. package/deps/librdkafka/packaging/rpm/tests/test.cpp +34 -0
  203. package/deps/librdkafka/packaging/tools/Dockerfile +31 -0
  204. package/deps/librdkafka/packaging/tools/build-configurations-checks.sh +12 -0
  205. package/deps/librdkafka/packaging/tools/build-deb-package.sh +64 -0
  206. package/deps/librdkafka/packaging/tools/build-debian.sh +65 -0
  207. package/deps/librdkafka/packaging/tools/build-manylinux.sh +68 -0
  208. package/deps/librdkafka/packaging/tools/build-release-artifacts.sh +139 -0
  209. package/deps/librdkafka/packaging/tools/distro-build.sh +38 -0
  210. package/deps/librdkafka/packaging/tools/gh-release-checksums.py +39 -0
  211. package/deps/librdkafka/packaging/tools/rdutcoverage.sh +25 -0
  212. package/deps/librdkafka/packaging/tools/requirements.txt +2 -0
  213. package/deps/librdkafka/packaging/tools/run-in-docker.sh +28 -0
  214. package/deps/librdkafka/packaging/tools/run-integration-tests.sh +31 -0
  215. package/deps/librdkafka/packaging/tools/run-style-check.sh +4 -0
  216. package/deps/librdkafka/packaging/tools/style-format.sh +149 -0
  217. package/deps/librdkafka/packaging/tools/update_rpcs_max_versions.py +100 -0
  218. package/deps/librdkafka/service.yml +172 -0
  219. package/deps/librdkafka/src/CMakeLists.txt +374 -0
  220. package/deps/librdkafka/src/Makefile +103 -0
  221. package/deps/librdkafka/src/README.lz4.md +30 -0
  222. package/deps/librdkafka/src/cJSON.c +2834 -0
  223. package/deps/librdkafka/src/cJSON.h +398 -0
  224. package/deps/librdkafka/src/crc32c.c +430 -0
  225. package/deps/librdkafka/src/crc32c.h +38 -0
  226. package/deps/librdkafka/src/generate_proto.sh +66 -0
  227. package/deps/librdkafka/src/librdkafka_cgrp_synch.png +0 -0
  228. package/deps/librdkafka/src/lz4.c +2727 -0
  229. package/deps/librdkafka/src/lz4.h +842 -0
  230. package/deps/librdkafka/src/lz4frame.c +2078 -0
  231. package/deps/librdkafka/src/lz4frame.h +692 -0
  232. package/deps/librdkafka/src/lz4frame_static.h +47 -0
  233. package/deps/librdkafka/src/lz4hc.c +1631 -0
  234. package/deps/librdkafka/src/lz4hc.h +413 -0
  235. package/deps/librdkafka/src/nanopb/pb.h +917 -0
  236. package/deps/librdkafka/src/nanopb/pb_common.c +388 -0
  237. package/deps/librdkafka/src/nanopb/pb_common.h +49 -0
  238. package/deps/librdkafka/src/nanopb/pb_decode.c +1727 -0
  239. package/deps/librdkafka/src/nanopb/pb_decode.h +193 -0
  240. package/deps/librdkafka/src/nanopb/pb_encode.c +1000 -0
  241. package/deps/librdkafka/src/nanopb/pb_encode.h +185 -0
  242. package/deps/librdkafka/src/opentelemetry/common.pb.c +32 -0
  243. package/deps/librdkafka/src/opentelemetry/common.pb.h +170 -0
  244. package/deps/librdkafka/src/opentelemetry/metrics.options +2 -0
  245. package/deps/librdkafka/src/opentelemetry/metrics.pb.c +67 -0
  246. package/deps/librdkafka/src/opentelemetry/metrics.pb.h +966 -0
  247. package/deps/librdkafka/src/opentelemetry/resource.pb.c +12 -0
  248. package/deps/librdkafka/src/opentelemetry/resource.pb.h +58 -0
  249. package/deps/librdkafka/src/queue.h +850 -0
  250. package/deps/librdkafka/src/rd.h +584 -0
  251. package/deps/librdkafka/src/rdaddr.c +255 -0
  252. package/deps/librdkafka/src/rdaddr.h +202 -0
  253. package/deps/librdkafka/src/rdatomic.h +230 -0
  254. package/deps/librdkafka/src/rdavg.h +260 -0
  255. package/deps/librdkafka/src/rdavl.c +210 -0
  256. package/deps/librdkafka/src/rdavl.h +250 -0
  257. package/deps/librdkafka/src/rdbase64.c +200 -0
  258. package/deps/librdkafka/src/rdbase64.h +43 -0
  259. package/deps/librdkafka/src/rdbuf.c +1884 -0
  260. package/deps/librdkafka/src/rdbuf.h +375 -0
  261. package/deps/librdkafka/src/rdcrc32.c +114 -0
  262. package/deps/librdkafka/src/rdcrc32.h +170 -0
  263. package/deps/librdkafka/src/rddl.c +179 -0
  264. package/deps/librdkafka/src/rddl.h +43 -0
  265. package/deps/librdkafka/src/rdendian.h +175 -0
  266. package/deps/librdkafka/src/rdfloat.h +67 -0
  267. package/deps/librdkafka/src/rdfnv1a.c +113 -0
  268. package/deps/librdkafka/src/rdfnv1a.h +35 -0
  269. package/deps/librdkafka/src/rdgz.c +120 -0
  270. package/deps/librdkafka/src/rdgz.h +46 -0
  271. package/deps/librdkafka/src/rdhdrhistogram.c +721 -0
  272. package/deps/librdkafka/src/rdhdrhistogram.h +87 -0
  273. package/deps/librdkafka/src/rdhttp.c +830 -0
  274. package/deps/librdkafka/src/rdhttp.h +101 -0
  275. package/deps/librdkafka/src/rdinterval.h +177 -0
  276. package/deps/librdkafka/src/rdkafka.c +5505 -0
  277. package/deps/librdkafka/src/rdkafka.h +10686 -0
  278. package/deps/librdkafka/src/rdkafka_admin.c +9794 -0
  279. package/deps/librdkafka/src/rdkafka_admin.h +661 -0
  280. package/deps/librdkafka/src/rdkafka_assignment.c +1010 -0
  281. package/deps/librdkafka/src/rdkafka_assignment.h +73 -0
  282. package/deps/librdkafka/src/rdkafka_assignor.c +1786 -0
  283. package/deps/librdkafka/src/rdkafka_assignor.h +402 -0
  284. package/deps/librdkafka/src/rdkafka_aux.c +409 -0
  285. package/deps/librdkafka/src/rdkafka_aux.h +174 -0
  286. package/deps/librdkafka/src/rdkafka_background.c +221 -0
  287. package/deps/librdkafka/src/rdkafka_broker.c +6337 -0
  288. package/deps/librdkafka/src/rdkafka_broker.h +744 -0
  289. package/deps/librdkafka/src/rdkafka_buf.c +543 -0
  290. package/deps/librdkafka/src/rdkafka_buf.h +1525 -0
  291. package/deps/librdkafka/src/rdkafka_cert.c +576 -0
  292. package/deps/librdkafka/src/rdkafka_cert.h +62 -0
  293. package/deps/librdkafka/src/rdkafka_cgrp.c +7587 -0
  294. package/deps/librdkafka/src/rdkafka_cgrp.h +477 -0
  295. package/deps/librdkafka/src/rdkafka_conf.c +4880 -0
  296. package/deps/librdkafka/src/rdkafka_conf.h +732 -0
  297. package/deps/librdkafka/src/rdkafka_confval.h +97 -0
  298. package/deps/librdkafka/src/rdkafka_coord.c +623 -0
  299. package/deps/librdkafka/src/rdkafka_coord.h +132 -0
  300. package/deps/librdkafka/src/rdkafka_error.c +228 -0
  301. package/deps/librdkafka/src/rdkafka_error.h +80 -0
  302. package/deps/librdkafka/src/rdkafka_event.c +502 -0
  303. package/deps/librdkafka/src/rdkafka_event.h +126 -0
  304. package/deps/librdkafka/src/rdkafka_feature.c +898 -0
  305. package/deps/librdkafka/src/rdkafka_feature.h +104 -0
  306. package/deps/librdkafka/src/rdkafka_fetcher.c +1422 -0
  307. package/deps/librdkafka/src/rdkafka_fetcher.h +44 -0
  308. package/deps/librdkafka/src/rdkafka_header.c +220 -0
  309. package/deps/librdkafka/src/rdkafka_header.h +76 -0
  310. package/deps/librdkafka/src/rdkafka_idempotence.c +807 -0
  311. package/deps/librdkafka/src/rdkafka_idempotence.h +144 -0
  312. package/deps/librdkafka/src/rdkafka_int.h +1260 -0
  313. package/deps/librdkafka/src/rdkafka_interceptor.c +819 -0
  314. package/deps/librdkafka/src/rdkafka_interceptor.h +104 -0
  315. package/deps/librdkafka/src/rdkafka_lz4.c +450 -0
  316. package/deps/librdkafka/src/rdkafka_lz4.h +49 -0
  317. package/deps/librdkafka/src/rdkafka_metadata.c +2209 -0
  318. package/deps/librdkafka/src/rdkafka_metadata.h +345 -0
  319. package/deps/librdkafka/src/rdkafka_metadata_cache.c +1183 -0
  320. package/deps/librdkafka/src/rdkafka_mock.c +3661 -0
  321. package/deps/librdkafka/src/rdkafka_mock.h +610 -0
  322. package/deps/librdkafka/src/rdkafka_mock_cgrp.c +1876 -0
  323. package/deps/librdkafka/src/rdkafka_mock_handlers.c +3113 -0
  324. package/deps/librdkafka/src/rdkafka_mock_int.h +710 -0
  325. package/deps/librdkafka/src/rdkafka_msg.c +2589 -0
  326. package/deps/librdkafka/src/rdkafka_msg.h +614 -0
  327. package/deps/librdkafka/src/rdkafka_msgbatch.h +62 -0
  328. package/deps/librdkafka/src/rdkafka_msgset.h +98 -0
  329. package/deps/librdkafka/src/rdkafka_msgset_reader.c +1806 -0
  330. package/deps/librdkafka/src/rdkafka_msgset_writer.c +1474 -0
  331. package/deps/librdkafka/src/rdkafka_offset.c +1565 -0
  332. package/deps/librdkafka/src/rdkafka_offset.h +150 -0
  333. package/deps/librdkafka/src/rdkafka_op.c +997 -0
  334. package/deps/librdkafka/src/rdkafka_op.h +858 -0
  335. package/deps/librdkafka/src/rdkafka_partition.c +4896 -0
  336. package/deps/librdkafka/src/rdkafka_partition.h +1182 -0
  337. package/deps/librdkafka/src/rdkafka_pattern.c +228 -0
  338. package/deps/librdkafka/src/rdkafka_pattern.h +70 -0
  339. package/deps/librdkafka/src/rdkafka_plugin.c +213 -0
  340. package/deps/librdkafka/src/rdkafka_plugin.h +41 -0
  341. package/deps/librdkafka/src/rdkafka_proto.h +736 -0
  342. package/deps/librdkafka/src/rdkafka_protocol.h +128 -0
  343. package/deps/librdkafka/src/rdkafka_queue.c +1230 -0
  344. package/deps/librdkafka/src/rdkafka_queue.h +1220 -0
  345. package/deps/librdkafka/src/rdkafka_range_assignor.c +1748 -0
  346. package/deps/librdkafka/src/rdkafka_request.c +7089 -0
  347. package/deps/librdkafka/src/rdkafka_request.h +732 -0
  348. package/deps/librdkafka/src/rdkafka_roundrobin_assignor.c +123 -0
  349. package/deps/librdkafka/src/rdkafka_sasl.c +530 -0
  350. package/deps/librdkafka/src/rdkafka_sasl.h +63 -0
  351. package/deps/librdkafka/src/rdkafka_sasl_cyrus.c +722 -0
  352. package/deps/librdkafka/src/rdkafka_sasl_int.h +89 -0
  353. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.c +1833 -0
  354. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.h +52 -0
  355. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.c +1666 -0
  356. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.h +47 -0
  357. package/deps/librdkafka/src/rdkafka_sasl_plain.c +142 -0
  358. package/deps/librdkafka/src/rdkafka_sasl_scram.c +858 -0
  359. package/deps/librdkafka/src/rdkafka_sasl_win32.c +550 -0
  360. package/deps/librdkafka/src/rdkafka_ssl.c +2129 -0
  361. package/deps/librdkafka/src/rdkafka_ssl.h +86 -0
  362. package/deps/librdkafka/src/rdkafka_sticky_assignor.c +4785 -0
  363. package/deps/librdkafka/src/rdkafka_subscription.c +278 -0
  364. package/deps/librdkafka/src/rdkafka_telemetry.c +760 -0
  365. package/deps/librdkafka/src/rdkafka_telemetry.h +52 -0
  366. package/deps/librdkafka/src/rdkafka_telemetry_decode.c +1053 -0
  367. package/deps/librdkafka/src/rdkafka_telemetry_decode.h +59 -0
  368. package/deps/librdkafka/src/rdkafka_telemetry_encode.c +997 -0
  369. package/deps/librdkafka/src/rdkafka_telemetry_encode.h +301 -0
  370. package/deps/librdkafka/src/rdkafka_timer.c +402 -0
  371. package/deps/librdkafka/src/rdkafka_timer.h +117 -0
  372. package/deps/librdkafka/src/rdkafka_topic.c +2161 -0
  373. package/deps/librdkafka/src/rdkafka_topic.h +334 -0
  374. package/deps/librdkafka/src/rdkafka_transport.c +1309 -0
  375. package/deps/librdkafka/src/rdkafka_transport.h +99 -0
  376. package/deps/librdkafka/src/rdkafka_transport_int.h +100 -0
  377. package/deps/librdkafka/src/rdkafka_txnmgr.c +3256 -0
  378. package/deps/librdkafka/src/rdkafka_txnmgr.h +171 -0
  379. package/deps/librdkafka/src/rdkafka_zstd.c +226 -0
  380. package/deps/librdkafka/src/rdkafka_zstd.h +57 -0
  381. package/deps/librdkafka/src/rdlist.c +576 -0
  382. package/deps/librdkafka/src/rdlist.h +434 -0
  383. package/deps/librdkafka/src/rdlog.c +89 -0
  384. package/deps/librdkafka/src/rdlog.h +41 -0
  385. package/deps/librdkafka/src/rdmap.c +508 -0
  386. package/deps/librdkafka/src/rdmap.h +492 -0
  387. package/deps/librdkafka/src/rdmurmur2.c +167 -0
  388. package/deps/librdkafka/src/rdmurmur2.h +35 -0
  389. package/deps/librdkafka/src/rdports.c +61 -0
  390. package/deps/librdkafka/src/rdports.h +38 -0
  391. package/deps/librdkafka/src/rdposix.h +250 -0
  392. package/deps/librdkafka/src/rdrand.c +80 -0
  393. package/deps/librdkafka/src/rdrand.h +43 -0
  394. package/deps/librdkafka/src/rdregex.c +156 -0
  395. package/deps/librdkafka/src/rdregex.h +43 -0
  396. package/deps/librdkafka/src/rdsignal.h +57 -0
  397. package/deps/librdkafka/src/rdstring.c +645 -0
  398. package/deps/librdkafka/src/rdstring.h +98 -0
  399. package/deps/librdkafka/src/rdsysqueue.h +404 -0
  400. package/deps/librdkafka/src/rdtime.h +356 -0
  401. package/deps/librdkafka/src/rdtypes.h +86 -0
  402. package/deps/librdkafka/src/rdunittest.c +549 -0
  403. package/deps/librdkafka/src/rdunittest.h +232 -0
  404. package/deps/librdkafka/src/rdvarint.c +134 -0
  405. package/deps/librdkafka/src/rdvarint.h +165 -0
  406. package/deps/librdkafka/src/rdwin32.h +382 -0
  407. package/deps/librdkafka/src/rdxxhash.c +1030 -0
  408. package/deps/librdkafka/src/rdxxhash.h +328 -0
  409. package/deps/librdkafka/src/regexp.c +1352 -0
  410. package/deps/librdkafka/src/regexp.h +41 -0
  411. package/deps/librdkafka/src/snappy.c +1866 -0
  412. package/deps/librdkafka/src/snappy.h +62 -0
  413. package/deps/librdkafka/src/snappy_compat.h +138 -0
  414. package/deps/librdkafka/src/statistics_schema.json +444 -0
  415. package/deps/librdkafka/src/tinycthread.c +932 -0
  416. package/deps/librdkafka/src/tinycthread.h +503 -0
  417. package/deps/librdkafka/src/tinycthread_extra.c +199 -0
  418. package/deps/librdkafka/src/tinycthread_extra.h +212 -0
  419. package/deps/librdkafka/src/win32_config.h +58 -0
  420. package/deps/librdkafka/src-cpp/CMakeLists.txt +90 -0
  421. package/deps/librdkafka/src-cpp/ConfImpl.cpp +84 -0
  422. package/deps/librdkafka/src-cpp/ConsumerImpl.cpp +244 -0
  423. package/deps/librdkafka/src-cpp/HandleImpl.cpp +436 -0
  424. package/deps/librdkafka/src-cpp/HeadersImpl.cpp +48 -0
  425. package/deps/librdkafka/src-cpp/KafkaConsumerImpl.cpp +296 -0
  426. package/deps/librdkafka/src-cpp/Makefile +55 -0
  427. package/deps/librdkafka/src-cpp/MessageImpl.cpp +38 -0
  428. package/deps/librdkafka/src-cpp/MetadataImpl.cpp +170 -0
  429. package/deps/librdkafka/src-cpp/ProducerImpl.cpp +197 -0
  430. package/deps/librdkafka/src-cpp/QueueImpl.cpp +70 -0
  431. package/deps/librdkafka/src-cpp/README.md +16 -0
  432. package/deps/librdkafka/src-cpp/RdKafka.cpp +59 -0
  433. package/deps/librdkafka/src-cpp/TopicImpl.cpp +124 -0
  434. package/deps/librdkafka/src-cpp/TopicPartitionImpl.cpp +57 -0
  435. package/deps/librdkafka/src-cpp/rdkafkacpp.h +3797 -0
  436. package/deps/librdkafka/src-cpp/rdkafkacpp_int.h +1641 -0
  437. package/deps/librdkafka/tests/0000-unittests.c +72 -0
  438. package/deps/librdkafka/tests/0001-multiobj.c +102 -0
  439. package/deps/librdkafka/tests/0002-unkpart.c +244 -0
  440. package/deps/librdkafka/tests/0003-msgmaxsize.c +173 -0
  441. package/deps/librdkafka/tests/0004-conf.c +934 -0
  442. package/deps/librdkafka/tests/0005-order.c +133 -0
  443. package/deps/librdkafka/tests/0006-symbols.c +163 -0
  444. package/deps/librdkafka/tests/0007-autotopic.c +136 -0
  445. package/deps/librdkafka/tests/0008-reqacks.c +179 -0
  446. package/deps/librdkafka/tests/0009-mock_cluster.c +97 -0
  447. package/deps/librdkafka/tests/0011-produce_batch.c +753 -0
  448. package/deps/librdkafka/tests/0012-produce_consume.c +537 -0
  449. package/deps/librdkafka/tests/0013-null-msgs.c +473 -0
  450. package/deps/librdkafka/tests/0014-reconsume-191.c +512 -0
  451. package/deps/librdkafka/tests/0015-offset_seeks.c +172 -0
  452. package/deps/librdkafka/tests/0016-client_swname.c +181 -0
  453. package/deps/librdkafka/tests/0017-compression.c +140 -0
  454. package/deps/librdkafka/tests/0018-cgrp_term.c +338 -0
  455. package/deps/librdkafka/tests/0019-list_groups.c +289 -0
  456. package/deps/librdkafka/tests/0020-destroy_hang.c +162 -0
  457. package/deps/librdkafka/tests/0021-rkt_destroy.c +72 -0
  458. package/deps/librdkafka/tests/0022-consume_batch.c +279 -0
  459. package/deps/librdkafka/tests/0025-timers.c +147 -0
  460. package/deps/librdkafka/tests/0026-consume_pause.c +547 -0
  461. package/deps/librdkafka/tests/0028-long_topicnames.c +79 -0
  462. package/deps/librdkafka/tests/0029-assign_offset.c +202 -0
  463. package/deps/librdkafka/tests/0030-offset_commit.c +589 -0
  464. package/deps/librdkafka/tests/0031-get_offsets.c +235 -0
  465. package/deps/librdkafka/tests/0033-regex_subscribe.c +536 -0
  466. package/deps/librdkafka/tests/0034-offset_reset.c +398 -0
  467. package/deps/librdkafka/tests/0035-api_version.c +73 -0
  468. package/deps/librdkafka/tests/0036-partial_fetch.c +87 -0
  469. package/deps/librdkafka/tests/0037-destroy_hang_local.c +85 -0
  470. package/deps/librdkafka/tests/0038-performance.c +121 -0
  471. package/deps/librdkafka/tests/0039-event.c +284 -0
  472. package/deps/librdkafka/tests/0040-io_event.c +257 -0
  473. package/deps/librdkafka/tests/0041-fetch_max_bytes.c +97 -0
  474. package/deps/librdkafka/tests/0042-many_topics.c +252 -0
  475. package/deps/librdkafka/tests/0043-no_connection.c +77 -0
  476. package/deps/librdkafka/tests/0044-partition_cnt.c +94 -0
  477. package/deps/librdkafka/tests/0045-subscribe_update.c +1010 -0
  478. package/deps/librdkafka/tests/0046-rkt_cache.c +65 -0
  479. package/deps/librdkafka/tests/0047-partial_buf_tmout.c +98 -0
  480. package/deps/librdkafka/tests/0048-partitioner.c +283 -0
  481. package/deps/librdkafka/tests/0049-consume_conn_close.c +162 -0
  482. package/deps/librdkafka/tests/0050-subscribe_adds.c +145 -0
  483. package/deps/librdkafka/tests/0051-assign_adds.c +126 -0
  484. package/deps/librdkafka/tests/0052-msg_timestamps.c +238 -0
  485. package/deps/librdkafka/tests/0053-stats_cb.cpp +527 -0
  486. package/deps/librdkafka/tests/0054-offset_time.cpp +236 -0
  487. package/deps/librdkafka/tests/0055-producer_latency.c +539 -0
  488. package/deps/librdkafka/tests/0056-balanced_group_mt.c +315 -0
  489. package/deps/librdkafka/tests/0057-invalid_topic.cpp +112 -0
  490. package/deps/librdkafka/tests/0058-log.cpp +123 -0
  491. package/deps/librdkafka/tests/0059-bsearch.cpp +241 -0
  492. package/deps/librdkafka/tests/0060-op_prio.cpp +163 -0
  493. package/deps/librdkafka/tests/0061-consumer_lag.cpp +295 -0
  494. package/deps/librdkafka/tests/0062-stats_event.c +126 -0
  495. package/deps/librdkafka/tests/0063-clusterid.cpp +180 -0
  496. package/deps/librdkafka/tests/0064-interceptors.c +481 -0
  497. package/deps/librdkafka/tests/0065-yield.cpp +140 -0
  498. package/deps/librdkafka/tests/0066-plugins.cpp +129 -0
  499. package/deps/librdkafka/tests/0067-empty_topic.cpp +151 -0
  500. package/deps/librdkafka/tests/0068-produce_timeout.c +136 -0
  501. package/deps/librdkafka/tests/0069-consumer_add_parts.c +119 -0
  502. package/deps/librdkafka/tests/0070-null_empty.cpp +197 -0
  503. package/deps/librdkafka/tests/0072-headers_ut.c +448 -0
  504. package/deps/librdkafka/tests/0073-headers.c +381 -0
  505. package/deps/librdkafka/tests/0074-producev.c +87 -0
  506. package/deps/librdkafka/tests/0075-retry.c +290 -0
  507. package/deps/librdkafka/tests/0076-produce_retry.c +452 -0
  508. package/deps/librdkafka/tests/0077-compaction.c +363 -0
  509. package/deps/librdkafka/tests/0078-c_from_cpp.cpp +96 -0
  510. package/deps/librdkafka/tests/0079-fork.c +93 -0
  511. package/deps/librdkafka/tests/0080-admin_ut.c +3095 -0
  512. package/deps/librdkafka/tests/0081-admin.c +5633 -0
  513. package/deps/librdkafka/tests/0082-fetch_max_bytes.cpp +137 -0
  514. package/deps/librdkafka/tests/0083-cb_event.c +233 -0
  515. package/deps/librdkafka/tests/0084-destroy_flags.c +208 -0
  516. package/deps/librdkafka/tests/0085-headers.cpp +392 -0
  517. package/deps/librdkafka/tests/0086-purge.c +368 -0
  518. package/deps/librdkafka/tests/0088-produce_metadata_timeout.c +162 -0
  519. package/deps/librdkafka/tests/0089-max_poll_interval.c +511 -0
  520. package/deps/librdkafka/tests/0090-idempotence.c +171 -0
  521. package/deps/librdkafka/tests/0091-max_poll_interval_timeout.c +295 -0
  522. package/deps/librdkafka/tests/0092-mixed_msgver.c +103 -0
  523. package/deps/librdkafka/tests/0093-holb.c +200 -0
  524. package/deps/librdkafka/tests/0094-idempotence_msg_timeout.c +231 -0
  525. package/deps/librdkafka/tests/0095-all_brokers_down.cpp +122 -0
  526. package/deps/librdkafka/tests/0097-ssl_verify.cpp +658 -0
  527. package/deps/librdkafka/tests/0098-consumer-txn.cpp +1218 -0
  528. package/deps/librdkafka/tests/0099-commit_metadata.c +194 -0
  529. package/deps/librdkafka/tests/0100-thread_interceptors.cpp +195 -0
  530. package/deps/librdkafka/tests/0101-fetch-from-follower.cpp +446 -0
  531. package/deps/librdkafka/tests/0102-static_group_rebalance.c +836 -0
  532. package/deps/librdkafka/tests/0103-transactions.c +1383 -0
  533. package/deps/librdkafka/tests/0104-fetch_from_follower_mock.c +625 -0
  534. package/deps/librdkafka/tests/0105-transactions_mock.c +3930 -0
  535. package/deps/librdkafka/tests/0106-cgrp_sess_timeout.c +318 -0
  536. package/deps/librdkafka/tests/0107-topic_recreate.c +259 -0
  537. package/deps/librdkafka/tests/0109-auto_create_topics.cpp +278 -0
  538. package/deps/librdkafka/tests/0110-batch_size.cpp +182 -0
  539. package/deps/librdkafka/tests/0111-delay_create_topics.cpp +127 -0
  540. package/deps/librdkafka/tests/0112-assign_unknown_part.c +87 -0
  541. package/deps/librdkafka/tests/0113-cooperative_rebalance.cpp +3473 -0
  542. package/deps/librdkafka/tests/0114-sticky_partitioning.cpp +176 -0
  543. package/deps/librdkafka/tests/0115-producer_auth.cpp +182 -0
  544. package/deps/librdkafka/tests/0116-kafkaconsumer_close.cpp +216 -0
  545. package/deps/librdkafka/tests/0117-mock_errors.c +331 -0
  546. package/deps/librdkafka/tests/0118-commit_rebalance.c +154 -0
  547. package/deps/librdkafka/tests/0119-consumer_auth.cpp +167 -0
  548. package/deps/librdkafka/tests/0120-asymmetric_subscription.c +185 -0
  549. package/deps/librdkafka/tests/0121-clusterid.c +115 -0
  550. package/deps/librdkafka/tests/0122-buffer_cleaning_after_rebalance.c +227 -0
  551. package/deps/librdkafka/tests/0123-connections_max_idle.c +98 -0
  552. package/deps/librdkafka/tests/0124-openssl_invalid_engine.c +69 -0
  553. package/deps/librdkafka/tests/0125-immediate_flush.c +144 -0
  554. package/deps/librdkafka/tests/0126-oauthbearer_oidc.c +528 -0
  555. package/deps/librdkafka/tests/0127-fetch_queue_backoff.cpp +165 -0
  556. package/deps/librdkafka/tests/0128-sasl_callback_queue.cpp +125 -0
  557. package/deps/librdkafka/tests/0129-fetch_aborted_msgs.c +79 -0
  558. package/deps/librdkafka/tests/0130-store_offsets.c +178 -0
  559. package/deps/librdkafka/tests/0131-connect_timeout.c +81 -0
  560. package/deps/librdkafka/tests/0132-strategy_ordering.c +179 -0
  561. package/deps/librdkafka/tests/0133-ssl_keys.c +150 -0
  562. package/deps/librdkafka/tests/0134-ssl_provider.c +92 -0
  563. package/deps/librdkafka/tests/0135-sasl_credentials.cpp +143 -0
  564. package/deps/librdkafka/tests/0136-resolve_cb.c +181 -0
  565. package/deps/librdkafka/tests/0137-barrier_batch_consume.c +619 -0
  566. package/deps/librdkafka/tests/0138-admin_mock.c +281 -0
  567. package/deps/librdkafka/tests/0139-offset_validation_mock.c +950 -0
  568. package/deps/librdkafka/tests/0140-commit_metadata.cpp +108 -0
  569. package/deps/librdkafka/tests/0142-reauthentication.c +515 -0
  570. package/deps/librdkafka/tests/0143-exponential_backoff_mock.c +552 -0
  571. package/deps/librdkafka/tests/0144-idempotence_mock.c +373 -0
  572. package/deps/librdkafka/tests/0145-pause_resume_mock.c +119 -0
  573. package/deps/librdkafka/tests/0146-metadata_mock.c +505 -0
  574. package/deps/librdkafka/tests/0147-consumer_group_consumer_mock.c +952 -0
  575. package/deps/librdkafka/tests/0148-offset_fetch_commit_error_mock.c +563 -0
  576. package/deps/librdkafka/tests/0149-broker-same-host-port.c +140 -0
  577. package/deps/librdkafka/tests/0150-telemetry_mock.c +651 -0
  578. package/deps/librdkafka/tests/0151-purge-brokers.c +566 -0
  579. package/deps/librdkafka/tests/0152-rebootstrap.c +59 -0
  580. package/deps/librdkafka/tests/0153-memberid.c +128 -0
  581. package/deps/librdkafka/tests/1000-unktopic.c +164 -0
  582. package/deps/librdkafka/tests/8000-idle.cpp +60 -0
  583. package/deps/librdkafka/tests/8001-fetch_from_follower_mock_manual.c +113 -0
  584. package/deps/librdkafka/tests/CMakeLists.txt +170 -0
  585. package/deps/librdkafka/tests/LibrdkafkaTestApp.py +291 -0
  586. package/deps/librdkafka/tests/Makefile +182 -0
  587. package/deps/librdkafka/tests/README.md +509 -0
  588. package/deps/librdkafka/tests/autotest.sh +33 -0
  589. package/deps/librdkafka/tests/backtrace.gdb +30 -0
  590. package/deps/librdkafka/tests/broker_version_tests.py +315 -0
  591. package/deps/librdkafka/tests/buildbox.sh +17 -0
  592. package/deps/librdkafka/tests/cleanup-checker-tests.sh +20 -0
  593. package/deps/librdkafka/tests/cluster_testing.py +191 -0
  594. package/deps/librdkafka/tests/delete-test-topics.sh +56 -0
  595. package/deps/librdkafka/tests/fixtures/oauthbearer/jwt_assertion_template.json +10 -0
  596. package/deps/librdkafka/tests/fixtures/ssl/Makefile +8 -0
  597. package/deps/librdkafka/tests/fixtures/ssl/README.md +13 -0
  598. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.intermediate.p12 +0 -0
  599. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.p12 +0 -0
  600. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.intermediate.pem +72 -0
  601. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.pem +50 -0
  602. package/deps/librdkafka/tests/fixtures/ssl/client2.intermediate.key +46 -0
  603. package/deps/librdkafka/tests/fixtures/ssl/client2.key +46 -0
  604. package/deps/librdkafka/tests/fixtures/ssl/create_keys.sh +168 -0
  605. package/deps/librdkafka/tests/fuzzers/Makefile +12 -0
  606. package/deps/librdkafka/tests/fuzzers/README.md +31 -0
  607. package/deps/librdkafka/tests/fuzzers/fuzz_regex.c +74 -0
  608. package/deps/librdkafka/tests/fuzzers/helpers.h +90 -0
  609. package/deps/librdkafka/tests/gen-ssl-certs.sh +165 -0
  610. package/deps/librdkafka/tests/interactive_broker_version.py +170 -0
  611. package/deps/librdkafka/tests/interceptor_test/CMakeLists.txt +16 -0
  612. package/deps/librdkafka/tests/interceptor_test/Makefile +22 -0
  613. package/deps/librdkafka/tests/interceptor_test/interceptor_test.c +314 -0
  614. package/deps/librdkafka/tests/interceptor_test/interceptor_test.h +54 -0
  615. package/deps/librdkafka/tests/java/IncrementalRebalanceCli.java +97 -0
  616. package/deps/librdkafka/tests/java/Makefile +13 -0
  617. package/deps/librdkafka/tests/java/Murmur2Cli.java +46 -0
  618. package/deps/librdkafka/tests/java/README.md +14 -0
  619. package/deps/librdkafka/tests/java/TransactionProducerCli.java +162 -0
  620. package/deps/librdkafka/tests/java/run-class.sh +11 -0
  621. package/deps/librdkafka/tests/librdkafka.suppressions +483 -0
  622. package/deps/librdkafka/tests/lz4_manual_test.sh +59 -0
  623. package/deps/librdkafka/tests/multi-broker-version-test.sh +50 -0
  624. package/deps/librdkafka/tests/parse-refcnt.sh +43 -0
  625. package/deps/librdkafka/tests/performance_plot.py +115 -0
  626. package/deps/librdkafka/tests/plugin_test/Makefile +19 -0
  627. package/deps/librdkafka/tests/plugin_test/plugin_test.c +58 -0
  628. package/deps/librdkafka/tests/requirements.txt +2 -0
  629. package/deps/librdkafka/tests/run-all-tests.sh +79 -0
  630. package/deps/librdkafka/tests/run-consumer-tests.sh +16 -0
  631. package/deps/librdkafka/tests/run-producer-tests.sh +16 -0
  632. package/deps/librdkafka/tests/run-test-batches.py +157 -0
  633. package/deps/librdkafka/tests/run-test.sh +140 -0
  634. package/deps/librdkafka/tests/rusage.c +249 -0
  635. package/deps/librdkafka/tests/sasl_test.py +289 -0
  636. package/deps/librdkafka/tests/scenarios/README.md +6 -0
  637. package/deps/librdkafka/tests/scenarios/ak23.json +6 -0
  638. package/deps/librdkafka/tests/scenarios/default.json +5 -0
  639. package/deps/librdkafka/tests/scenarios/noautocreate.json +5 -0
  640. package/deps/librdkafka/tests/sockem.c +801 -0
  641. package/deps/librdkafka/tests/sockem.h +85 -0
  642. package/deps/librdkafka/tests/sockem_ctrl.c +145 -0
  643. package/deps/librdkafka/tests/sockem_ctrl.h +61 -0
  644. package/deps/librdkafka/tests/test.c +7778 -0
  645. package/deps/librdkafka/tests/test.conf.example +27 -0
  646. package/deps/librdkafka/tests/test.h +1028 -0
  647. package/deps/librdkafka/tests/testcpp.cpp +131 -0
  648. package/deps/librdkafka/tests/testcpp.h +388 -0
  649. package/deps/librdkafka/tests/testshared.h +416 -0
  650. package/deps/librdkafka/tests/tools/README.md +4 -0
  651. package/deps/librdkafka/tests/tools/stats/README.md +21 -0
  652. package/deps/librdkafka/tests/tools/stats/filter.jq +42 -0
  653. package/deps/librdkafka/tests/tools/stats/graph.py +150 -0
  654. package/deps/librdkafka/tests/tools/stats/requirements.txt +3 -0
  655. package/deps/librdkafka/tests/tools/stats/to_csv.py +124 -0
  656. package/deps/librdkafka/tests/trivup/trivup-0.14.0.tar.gz +0 -0
  657. package/deps/librdkafka/tests/until-fail.sh +87 -0
  658. package/deps/librdkafka/tests/xxxx-assign_partition.c +122 -0
  659. package/deps/librdkafka/tests/xxxx-metadata.cpp +159 -0
  660. package/deps/librdkafka/vcpkg.json +23 -0
  661. package/deps/librdkafka/win32/README.md +5 -0
  662. package/deps/librdkafka/win32/build-package.bat +3 -0
  663. package/deps/librdkafka/win32/build.bat +19 -0
  664. package/deps/librdkafka/win32/common.vcxproj +84 -0
  665. package/deps/librdkafka/win32/interceptor_test/interceptor_test.vcxproj +87 -0
  666. package/deps/librdkafka/win32/librdkafka.autopkg.template +54 -0
  667. package/deps/librdkafka/win32/librdkafka.master.testing.targets +13 -0
  668. package/deps/librdkafka/win32/librdkafka.sln +226 -0
  669. package/deps/librdkafka/win32/librdkafka.vcxproj +276 -0
  670. package/deps/librdkafka/win32/librdkafkacpp/librdkafkacpp.vcxproj +104 -0
  671. package/deps/librdkafka/win32/msbuild.ps1 +15 -0
  672. package/deps/librdkafka/win32/openssl_engine_example/openssl_engine_example.vcxproj +132 -0
  673. package/deps/librdkafka/win32/package-zip.ps1 +46 -0
  674. package/deps/librdkafka/win32/packages/repositories.config +4 -0
  675. package/deps/librdkafka/win32/push-package.bat +4 -0
  676. package/deps/librdkafka/win32/rdkafka_complex_consumer_example_cpp/rdkafka_complex_consumer_example_cpp.vcxproj +67 -0
  677. package/deps/librdkafka/win32/rdkafka_example/rdkafka_example.vcxproj +97 -0
  678. package/deps/librdkafka/win32/rdkafka_performance/rdkafka_performance.vcxproj +97 -0
  679. package/deps/librdkafka/win32/setup-msys2.ps1 +47 -0
  680. package/deps/librdkafka/win32/setup-vcpkg.ps1 +34 -0
  681. package/deps/librdkafka/win32/tests/test.conf.example +25 -0
  682. package/deps/librdkafka/win32/tests/tests.vcxproj +253 -0
  683. package/deps/librdkafka/win32/win_ssl_cert_store/win_ssl_cert_store.vcxproj +132 -0
  684. package/deps/librdkafka/win32/wingetopt.c +564 -0
  685. package/deps/librdkafka/win32/wingetopt.h +101 -0
  686. package/deps/librdkafka/win32/wintime.h +33 -0
  687. package/deps/librdkafka.gyp +62 -0
  688. package/lib/admin.js +233 -0
  689. package/lib/client.js +573 -0
  690. package/lib/error.js +500 -0
  691. package/lib/index.js +34 -0
  692. package/lib/kafka-consumer-stream.js +397 -0
  693. package/lib/kafka-consumer.js +698 -0
  694. package/lib/producer/high-level-producer.js +323 -0
  695. package/lib/producer-stream.js +307 -0
  696. package/lib/producer.js +375 -0
  697. package/lib/tools/ref-counter.js +52 -0
  698. package/lib/topic-partition.js +88 -0
  699. package/lib/topic.js +42 -0
  700. package/lib/util.js +29 -0
  701. package/package.json +61 -0
  702. package/prebuilds/darwin-arm64/@point3+node-rdkafka.node +0 -0
  703. package/prebuilds/linux-x64/@point3+node-rdkafka.node +0 -0
  704. package/util/configure.js +30 -0
  705. package/util/get-env.js +6 -0
  706. package/util/test-compile.js +11 -0
  707. package/util/test-producer-delivery.js +100 -0
@@ -0,0 +1,3661 @@
1
+ /*
2
+ * librdkafka - Apache Kafka C library
3
+ *
4
+ * Copyright (c) 2019-2022, Magnus Edenhill
5
+ * 2023, Confluent Inc.
6
+ * All rights reserved.
7
+ *
8
+ * Redistribution and use in source and binary forms, with or without
9
+ * modification, are permitted provided that the following conditions are met:
10
+ *
11
+ * 1. Redistributions of source code must retain the above copyright notice,
12
+ * this list of conditions and the following disclaimer.
13
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
14
+ * this list of conditions and the following disclaimer in the documentation
15
+ * and/or other materials provided with the distribution.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ * POSSIBILITY OF SUCH DAMAGE.
28
+ */
29
+
30
+ /**
31
+ * Mocks
32
+ *
33
+ */
34
+
35
+ #include "rdkafka_int.h"
36
+ #include "rdbuf.h"
37
+ #include "rdrand.h"
38
+ #include "rdkafka_interceptor.h"
39
+ #include "rdkafka_mock_int.h"
40
+ #include "rdkafka_transport_int.h"
41
+ #include "rdkafka_mock.h"
42
+ #include "rdunittest.h"
43
+
44
+ #include <stdarg.h>
45
+
46
+ typedef struct rd_kafka_mock_request_s rd_kafka_mock_request_t;
47
+
48
+ static void rd_kafka_mock_cluster_destroy0(rd_kafka_mock_cluster_t *mcluster);
49
+ static rd_kafka_mock_request_t *
50
+ rd_kafka_mock_request_new(int32_t id, int16_t api_key, int64_t timestamp_us);
51
+ static void rd_kafka_mock_request_free(void *element);
52
+ static void rd_kafka_mock_coord_remove(rd_kafka_mock_cluster_t *mcluster,
53
+ int32_t broker_id);
54
+
55
+ static rd_kafka_mock_broker_t *
56
+ rd_kafka_mock_broker_find(const rd_kafka_mock_cluster_t *mcluster,
57
+ int32_t broker_id) {
58
+ const rd_kafka_mock_broker_t *mrkb;
59
+
60
+ TAILQ_FOREACH(mrkb, &mcluster->brokers, link)
61
+ if (mrkb->id == broker_id)
62
+ return (rd_kafka_mock_broker_t *)mrkb;
63
+
64
+ return NULL;
65
+ }
66
+
67
+
68
+
69
+ /**
70
+ * @brief Unlink and free message set.
71
+ */
72
+ static void rd_kafka_mock_msgset_destroy(rd_kafka_mock_partition_t *mpart,
73
+ rd_kafka_mock_msgset_t *mset) {
74
+ const rd_kafka_mock_msgset_t *next = TAILQ_NEXT(mset, link);
75
+
76
+ /* Removing last messageset */
77
+ if (!next)
78
+ mpart->start_offset = mpart->end_offset;
79
+ else if (mset == TAILQ_FIRST(&mpart->msgsets))
80
+ /* Removing first messageset */
81
+ mpart->start_offset = next->first_offset;
82
+
83
+ if (mpart->update_follower_start_offset)
84
+ mpart->follower_start_offset = mpart->start_offset;
85
+
86
+ rd_assert(mpart->cnt > 0);
87
+ mpart->cnt--;
88
+ mpart->size -= RD_KAFKAP_BYTES_LEN(&mset->bytes);
89
+ TAILQ_REMOVE(&mpart->msgsets, mset, link);
90
+ rd_free(mset);
91
+ }
92
+
93
+
94
+ /**
95
+ * @brief Create a new msgset object with a copy of \p bytes
96
+ * and appends it to the partition log.
97
+ */
98
+ static rd_kafka_mock_msgset_t *
99
+ rd_kafka_mock_msgset_new(rd_kafka_mock_partition_t *mpart,
100
+ const rd_kafkap_bytes_t *bytes,
101
+ size_t msgcnt) {
102
+ rd_kafka_mock_msgset_t *mset;
103
+ size_t totsize = sizeof(*mset) + RD_KAFKAP_BYTES_LEN(bytes);
104
+ int64_t BaseOffset;
105
+ int32_t PartitionLeaderEpoch;
106
+ int64_t orig_start_offset = mpart->start_offset;
107
+
108
+ rd_assert(!RD_KAFKAP_BYTES_IS_NULL(bytes));
109
+
110
+ mset = rd_malloc(totsize);
111
+ rd_assert(mset != NULL);
112
+
113
+ mset->first_offset = mpart->end_offset;
114
+ mset->last_offset = mset->first_offset + msgcnt - 1;
115
+ mpart->end_offset = mset->last_offset + 1;
116
+ if (mpart->update_follower_end_offset)
117
+ mpart->follower_end_offset = mpart->end_offset;
118
+ mpart->cnt++;
119
+
120
+ mset->bytes.len = bytes->len;
121
+ mset->leader_epoch = mpart->leader_epoch;
122
+
123
+
124
+ mset->bytes.data = (void *)(mset + 1);
125
+ memcpy((void *)mset->bytes.data, bytes->data, mset->bytes.len);
126
+ mpart->size += mset->bytes.len;
127
+
128
+ /* Update the base Offset in the MessageSet with the
129
+ * actual absolute log offset. */
130
+ BaseOffset = htobe64(mset->first_offset);
131
+ memcpy((void *)mset->bytes.data, &BaseOffset, sizeof(BaseOffset));
132
+ /* Update the base PartitionLeaderEpoch in the MessageSet with the
133
+ * actual partition leader epoch. */
134
+ PartitionLeaderEpoch = htobe32(mset->leader_epoch);
135
+ memcpy(((char *)mset->bytes.data) + 12, &PartitionLeaderEpoch,
136
+ sizeof(PartitionLeaderEpoch));
137
+
138
+ /* Remove old msgsets until within limits */
139
+ while (mpart->cnt > 1 &&
140
+ (mpart->cnt > mpart->max_cnt || mpart->size > mpart->max_size))
141
+ rd_kafka_mock_msgset_destroy(mpart,
142
+ TAILQ_FIRST(&mpart->msgsets));
143
+
144
+ TAILQ_INSERT_TAIL(&mpart->msgsets, mset, link);
145
+
146
+ rd_kafka_dbg(mpart->topic->cluster->rk, MOCK, "MOCK",
147
+ "Broker %" PRId32 ": Log append %s [%" PRId32
148
+ "] "
149
+ "%" PRIusz " messages, %" PRId32
150
+ " bytes at offset %" PRId64 " (log now %" PRId64
151
+ "..%" PRId64
152
+ ", "
153
+ "original start %" PRId64 ")",
154
+ mpart->leader->id, mpart->topic->name, mpart->id, msgcnt,
155
+ RD_KAFKAP_BYTES_LEN(&mset->bytes), mset->first_offset,
156
+ mpart->start_offset, mpart->end_offset, orig_start_offset);
157
+
158
+ return mset;
159
+ }
160
+
161
+ /**
162
+ * @brief Find message set containing \p offset
163
+ */
164
+ const rd_kafka_mock_msgset_t *
165
+ rd_kafka_mock_msgset_find(const rd_kafka_mock_partition_t *mpart,
166
+ int64_t offset,
167
+ rd_bool_t on_follower) {
168
+ const rd_kafka_mock_msgset_t *mset;
169
+
170
+ if (!on_follower &&
171
+ (offset < mpart->start_offset || offset > mpart->end_offset))
172
+ return NULL;
173
+
174
+ if (on_follower && (offset < mpart->follower_start_offset ||
175
+ offset > mpart->follower_end_offset))
176
+ return NULL;
177
+
178
+ /* FIXME: Maintain an index */
179
+
180
+ TAILQ_FOREACH(mset, &mpart->msgsets, link) {
181
+ if (mset->first_offset <= offset && offset <= mset->last_offset)
182
+ return mset;
183
+ }
184
+
185
+ return NULL;
186
+ }
187
+
188
+
189
+ /**
190
+ * @brief Looks up or creates a new pidstate for the given partition and PID.
191
+ *
192
+ * The pidstate is used to verify per-partition per-producer BaseSequences
193
+ * for the idempotent/txn producer.
194
+ */
195
+ static rd_kafka_mock_pid_t *
196
+ rd_kafka_mock_partition_pidstate_get(rd_kafka_mock_partition_t *mpart,
197
+ const rd_kafka_mock_pid_t *mpid) {
198
+ rd_kafka_mock_pid_t *pidstate;
199
+ size_t tidlen;
200
+
201
+ pidstate = rd_list_find(&mpart->pidstates, mpid, rd_kafka_mock_pid_cmp);
202
+ if (pidstate)
203
+ return pidstate;
204
+
205
+ tidlen = strlen(mpid->TransactionalId);
206
+ pidstate = rd_malloc(sizeof(*pidstate) + tidlen);
207
+ pidstate->pid = mpid->pid;
208
+ memcpy(pidstate->TransactionalId, mpid->TransactionalId, tidlen);
209
+ pidstate->TransactionalId[tidlen] = '\0';
210
+
211
+ pidstate->lo = pidstate->hi = pidstate->window = 0;
212
+ memset(pidstate->seq, 0, sizeof(pidstate->seq));
213
+
214
+ rd_list_add(&mpart->pidstates, pidstate);
215
+
216
+ return pidstate;
217
+ }
218
+
219
+
220
+ /**
221
+ * @brief Validate ProduceRequest records in \p rkbuf.
222
+ *
223
+ * @warning The \p rkbuf must not be read, just peek()ed.
224
+ *
225
+ * This is a very selective validation, currently only:
226
+ * - verify idempotency TransactionalId,PID,Epoch,Seq
227
+ */
228
+ static rd_kafka_resp_err_t
229
+ rd_kafka_mock_validate_records(rd_kafka_mock_partition_t *mpart,
230
+ rd_kafka_buf_t *rkbuf,
231
+ size_t RecordCount,
232
+ const rd_kafkap_str_t *TransactionalId,
233
+ rd_bool_t *is_dupd) {
234
+ const int log_decode_errors = LOG_ERR;
235
+ rd_kafka_mock_cluster_t *mcluster = mpart->topic->cluster;
236
+ rd_kafka_mock_pid_t *mpid;
237
+ rd_kafka_mock_pid_t *mpidstate = NULL;
238
+ rd_kafka_pid_t pid;
239
+ int32_t expected_BaseSequence = -1, BaseSequence = -1;
240
+ rd_kafka_resp_err_t err;
241
+
242
+ *is_dupd = rd_false;
243
+
244
+ if (!TransactionalId || RD_KAFKAP_STR_LEN(TransactionalId) < 1)
245
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
246
+
247
+ rd_kafka_buf_peek_i64(rkbuf, RD_KAFKAP_MSGSET_V2_OF_ProducerId,
248
+ &pid.id);
249
+ rd_kafka_buf_peek_i16(rkbuf, RD_KAFKAP_MSGSET_V2_OF_ProducerEpoch,
250
+ &pid.epoch);
251
+ rd_kafka_buf_peek_i32(rkbuf, RD_KAFKAP_MSGSET_V2_OF_BaseSequence,
252
+ &BaseSequence);
253
+
254
+ mtx_lock(&mcluster->lock);
255
+ err = rd_kafka_mock_pid_find(mcluster, TransactionalId, pid, &mpid);
256
+ mtx_unlock(&mcluster->lock);
257
+
258
+ if (likely(!err)) {
259
+
260
+ if (mpid->pid.epoch != pid.epoch)
261
+ err = RD_KAFKA_RESP_ERR_INVALID_PRODUCER_EPOCH;
262
+
263
+ /* Each partition tracks the 5 last Produce requests per PID.*/
264
+ mpidstate = rd_kafka_mock_partition_pidstate_get(mpart, mpid);
265
+
266
+ expected_BaseSequence = mpidstate->seq[mpidstate->hi];
267
+
268
+ /* A BaseSequence within the range of the last 5 requests is
269
+ * considered a legal duplicate and will be successfully acked
270
+ * but not written to the log. */
271
+ if (BaseSequence < mpidstate->seq[mpidstate->lo])
272
+ err = RD_KAFKA_RESP_ERR_DUPLICATE_SEQUENCE_NUMBER;
273
+ else if (BaseSequence > mpidstate->seq[mpidstate->hi])
274
+ err = RD_KAFKA_RESP_ERR_OUT_OF_ORDER_SEQUENCE_NUMBER;
275
+ else if (BaseSequence != expected_BaseSequence)
276
+ *is_dupd = rd_true;
277
+ }
278
+
279
+ if (unlikely(err)) {
280
+ rd_kafka_dbg(mcluster->rk, MOCK, "MOCK",
281
+ "Broker %" PRId32 ": Log append %s [%" PRId32
282
+ "] failed: PID mismatch: TransactionalId=%.*s "
283
+ "expected %s BaseSeq %" PRId32
284
+ ", not %s BaseSeq %" PRId32 ": %s",
285
+ mpart->leader->id, mpart->topic->name, mpart->id,
286
+ RD_KAFKAP_STR_PR(TransactionalId),
287
+ mpid ? rd_kafka_pid2str(mpid->pid) : "n/a",
288
+ expected_BaseSequence, rd_kafka_pid2str(pid),
289
+ BaseSequence, rd_kafka_err2name(err));
290
+ return err;
291
+ }
292
+
293
+ /* Update BaseSequence window */
294
+ if (unlikely(mpidstate->window < 5))
295
+ mpidstate->window++;
296
+ else
297
+ mpidstate->lo = (mpidstate->lo + 1) % mpidstate->window;
298
+ mpidstate->hi = (mpidstate->hi + 1) % mpidstate->window;
299
+ mpidstate->seq[mpidstate->hi] = (int32_t)(BaseSequence + RecordCount);
300
+
301
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
302
+
303
+ err_parse:
304
+ return rkbuf->rkbuf_err;
305
+ }
306
+
307
+ /**
308
+ * @brief Append the MessageSets in \p bytes to the \p mpart partition log.
309
+ *
310
+ * @param BaseOffset will contain the first assigned offset of the message set.
311
+ */
312
+ rd_kafka_resp_err_t
313
+ rd_kafka_mock_partition_log_append(rd_kafka_mock_partition_t *mpart,
314
+ const rd_kafkap_bytes_t *records,
315
+ const rd_kafkap_str_t *TransactionalId,
316
+ int64_t *BaseOffset) {
317
+ const int log_decode_errors = LOG_ERR;
318
+ rd_kafka_buf_t *rkbuf;
319
+ rd_kafka_resp_err_t err = RD_KAFKA_RESP_ERR_NO_ERROR;
320
+ int8_t MagicByte;
321
+ int32_t RecordCount;
322
+ int16_t Attributes;
323
+ rd_kafka_mock_msgset_t *mset;
324
+ rd_bool_t is_dup = rd_false;
325
+
326
+ /* Partially parse the MessageSet in \p bytes to get
327
+ * the message count. */
328
+ rkbuf = rd_kafka_buf_new_shadow(records->data,
329
+ RD_KAFKAP_BYTES_LEN(records), NULL);
330
+
331
+ rd_kafka_buf_peek_i8(rkbuf, RD_KAFKAP_MSGSET_V2_OF_MagicByte,
332
+ &MagicByte);
333
+ if (MagicByte != 2) {
334
+ /* We only support MsgVersion 2 for now */
335
+ err = RD_KAFKA_RESP_ERR_UNSUPPORTED_VERSION;
336
+ goto err;
337
+ }
338
+
339
+ rd_kafka_buf_peek_i32(rkbuf, RD_KAFKAP_MSGSET_V2_OF_RecordCount,
340
+ &RecordCount);
341
+ rd_kafka_buf_peek_i16(rkbuf, RD_KAFKAP_MSGSET_V2_OF_Attributes,
342
+ &Attributes);
343
+
344
+ if (RecordCount < 1 ||
345
+ (!(Attributes & RD_KAFKA_MSG_ATTR_COMPRESSION_MASK) &&
346
+ (size_t)RecordCount > RD_KAFKAP_BYTES_LEN(records) /
347
+ RD_KAFKAP_MESSAGE_V2_MIN_OVERHEAD)) {
348
+ err = RD_KAFKA_RESP_ERR_INVALID_MSG_SIZE;
349
+ goto err;
350
+ }
351
+
352
+ if ((err = rd_kafka_mock_validate_records(
353
+ mpart, rkbuf, (size_t)RecordCount, TransactionalId, &is_dup)))
354
+ goto err;
355
+
356
+ /* If this is a legit duplicate, don't write it to the log. */
357
+ if (is_dup)
358
+ goto err;
359
+
360
+ rd_kafka_buf_destroy(rkbuf);
361
+
362
+ mset = rd_kafka_mock_msgset_new(mpart, records, (size_t)RecordCount);
363
+
364
+ *BaseOffset = mset->first_offset;
365
+
366
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
367
+
368
+ err_parse:
369
+ err = rkbuf->rkbuf_err;
370
+ err:
371
+ rd_kafka_buf_destroy(rkbuf);
372
+ return err;
373
+ }
374
+
375
+
376
+ /**
377
+ * @brief Set the partition leader, or NULL for leader-less.
378
+ */
379
+ static void
380
+ rd_kafka_mock_partition_set_leader0(rd_kafka_mock_partition_t *mpart,
381
+ rd_kafka_mock_broker_t *mrkb) {
382
+ mpart->leader = mrkb;
383
+ mpart->leader_epoch++;
384
+ }
385
+
386
+
387
+ /**
388
+ * @brief Verifies that the client-provided leader_epoch matches that of the
389
+ * partition, else returns the appropriate error.
390
+ */
391
+ rd_kafka_resp_err_t rd_kafka_mock_partition_leader_epoch_check(
392
+ const rd_kafka_mock_partition_t *mpart,
393
+ int32_t leader_epoch) {
394
+ if (likely(leader_epoch == -1 || mpart->leader_epoch == leader_epoch))
395
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
396
+ else if (mpart->leader_epoch < leader_epoch)
397
+ return RD_KAFKA_RESP_ERR_UNKNOWN_LEADER_EPOCH;
398
+ else if (mpart->leader_epoch > leader_epoch)
399
+ return RD_KAFKA_RESP_ERR_FENCED_LEADER_EPOCH;
400
+
401
+ /* NOTREACHED, but avoids warning */
402
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
403
+ }
404
+
405
+ /**
406
+ * @brief Returns the end offset (last offset + 1)
407
+ * for the passed leader epoch in the mock partition.
408
+ *
409
+ * @param mpart The mock partition
410
+ * @param leader_epoch The leader epoch
411
+ *
412
+ * @return The end offset for the passed \p leader_epoch in \p mpart
413
+ */
414
+ int64_t rd_kafka_mock_partition_offset_for_leader_epoch(
415
+ const rd_kafka_mock_partition_t *mpart,
416
+ int32_t leader_epoch) {
417
+ const rd_kafka_mock_msgset_t *mset = NULL;
418
+
419
+ if (leader_epoch < 0)
420
+ return -1;
421
+
422
+ TAILQ_FOREACH_REVERSE(mset, &mpart->msgsets,
423
+ rd_kafka_mock_msgset_tailq_s, link) {
424
+ if (mset->leader_epoch == leader_epoch)
425
+ return mset->last_offset + 1;
426
+ }
427
+
428
+ return -1;
429
+ }
430
+
431
+
432
+ /**
433
+ * @brief Automatically assign replicas for partition
434
+ */
435
+ static void
436
+ rd_kafka_mock_partition_assign_replicas(rd_kafka_mock_partition_t *mpart,
437
+ int replication_factor) {
438
+ rd_kafka_mock_cluster_t *mcluster = mpart->topic->cluster;
439
+ int replica_cnt = RD_MIN(replication_factor, mcluster->broker_cnt);
440
+ rd_kafka_mock_broker_t *mrkb;
441
+ int i = 0;
442
+ int first_replica;
443
+ int skipped = 0;
444
+
445
+ if (mpart->replicas)
446
+ rd_free(mpart->replicas);
447
+
448
+ mpart->replicas = replica_cnt
449
+ ? rd_calloc(replica_cnt, sizeof(*mpart->replicas))
450
+ : NULL;
451
+ mpart->replica_cnt = replica_cnt;
452
+ if (replica_cnt == 0) {
453
+ rd_kafka_mock_partition_set_leader0(mpart, NULL);
454
+ return;
455
+ }
456
+
457
+ first_replica = (mpart->id * replication_factor) % mcluster->broker_cnt;
458
+
459
+ /* Use a predictable, determininistic order on a per-topic basis.
460
+ *
461
+ * Two loops are needed for wraparound. */
462
+ TAILQ_FOREACH(mrkb, &mcluster->brokers, link) {
463
+ if (skipped < first_replica) {
464
+ skipped++;
465
+ continue;
466
+ }
467
+ if (i == mpart->replica_cnt)
468
+ break;
469
+ mpart->replicas[i++] = mrkb;
470
+ }
471
+ TAILQ_FOREACH(mrkb, &mcluster->brokers, link) {
472
+ if (i == mpart->replica_cnt)
473
+ break;
474
+ mpart->replicas[i++] = mrkb;
475
+ }
476
+
477
+ /* Select a random leader */
478
+ rd_kafka_mock_partition_set_leader0(
479
+ mpart, mpart->replicas[rd_jitter(0, replica_cnt - 1)]);
480
+ }
481
+
482
+ /**
483
+ * @brief Push a partition leader response to passed \p mpart .
484
+ */
485
+ static void
486
+ rd_kafka_mock_partition_push_leader_response0(rd_kafka_mock_partition_t *mpart,
487
+ int32_t leader_id,
488
+ int32_t leader_epoch) {
489
+ rd_kafka_mock_partition_leader_t *leader_response;
490
+
491
+ leader_response = rd_calloc(1, sizeof(*leader_response));
492
+ leader_response->leader_id = leader_id;
493
+ leader_response->leader_epoch = leader_epoch;
494
+ TAILQ_INSERT_TAIL(&mpart->leader_responses, leader_response, link);
495
+ }
496
+
497
+ /**
498
+ * @brief Return the first mocked partition leader response in \p mpart ,
499
+ * if available.
500
+ */
501
+ rd_kafka_mock_partition_leader_t *
502
+ rd_kafka_mock_partition_next_leader_response(rd_kafka_mock_partition_t *mpart) {
503
+ return TAILQ_FIRST(&mpart->leader_responses);
504
+ }
505
+
506
+ /**
507
+ * @brief Unlink and destroy a partition leader response
508
+ */
509
+ void rd_kafka_mock_partition_leader_destroy(
510
+ rd_kafka_mock_partition_t *mpart,
511
+ rd_kafka_mock_partition_leader_t *mpart_leader) {
512
+ TAILQ_REMOVE(&mpart->leader_responses, mpart_leader, link);
513
+ rd_free(mpart_leader);
514
+ }
515
+
516
+ /**
517
+ * @brief Unlink and destroy committed offset
518
+ */
519
+ static void
520
+ rd_kafka_mock_committed_offset_destroy(rd_kafka_mock_partition_t *mpart,
521
+ rd_kafka_mock_committed_offset_t *coff) {
522
+ rd_kafkap_str_destroy(coff->metadata);
523
+ TAILQ_REMOVE(&mpart->committed_offsets, coff, link);
524
+ rd_free(coff);
525
+ }
526
+
527
+
528
+ /**
529
+ * @brief Find previously committed offset for group.
530
+ */
531
+ rd_kafka_mock_committed_offset_t *
532
+ rd_kafka_mock_committed_offset_find(const rd_kafka_mock_partition_t *mpart,
533
+ const rd_kafkap_str_t *group) {
534
+ const rd_kafka_mock_committed_offset_t *coff;
535
+
536
+ TAILQ_FOREACH(coff, &mpart->committed_offsets, link) {
537
+ if (!rd_kafkap_str_cmp_str(group, coff->group))
538
+ return (rd_kafka_mock_committed_offset_t *)coff;
539
+ }
540
+
541
+ return NULL;
542
+ }
543
+
544
+
545
+ /**
546
+ * @brief Commit offset for group
547
+ */
548
+ rd_kafka_mock_committed_offset_t *
549
+ rd_kafka_mock_commit_offset(rd_kafka_mock_partition_t *mpart,
550
+ const rd_kafkap_str_t *group,
551
+ int64_t offset,
552
+ const rd_kafkap_str_t *metadata) {
553
+ rd_kafka_mock_committed_offset_t *coff;
554
+
555
+ if (!(coff = rd_kafka_mock_committed_offset_find(mpart, group))) {
556
+ size_t slen = (size_t)RD_KAFKAP_STR_LEN(group);
557
+
558
+ coff = rd_malloc(sizeof(*coff) + slen + 1);
559
+
560
+ coff->group = (char *)(coff + 1);
561
+ memcpy(coff->group, group->str, slen);
562
+ coff->group[slen] = '\0';
563
+
564
+ coff->metadata = NULL;
565
+
566
+ TAILQ_INSERT_HEAD(&mpart->committed_offsets, coff, link);
567
+ }
568
+
569
+ if (coff->metadata)
570
+ rd_kafkap_str_destroy(coff->metadata);
571
+
572
+ coff->metadata = rd_kafkap_str_copy(metadata);
573
+
574
+ coff->offset = offset;
575
+
576
+ rd_kafka_dbg(mpart->topic->cluster->rk, MOCK, "MOCK",
577
+ "Topic %s [%" PRId32 "] committing offset %" PRId64
578
+ " for group %.*s",
579
+ mpart->topic->name, mpart->id, offset,
580
+ RD_KAFKAP_STR_PR(group));
581
+
582
+ return coff;
583
+ }
584
+
585
+ /**
586
+ * @brief Destroy resources for partition, but the \p mpart itself is not freed.
587
+ */
588
+ static void rd_kafka_mock_partition_destroy(rd_kafka_mock_partition_t *mpart) {
589
+ rd_kafka_mock_msgset_t *mset, *tmp;
590
+ rd_kafka_mock_committed_offset_t *coff, *tmpcoff;
591
+ rd_kafka_mock_partition_leader_t *mpart_leader, *tmp_mpart_leader;
592
+
593
+ TAILQ_FOREACH_SAFE(mset, &mpart->msgsets, link, tmp)
594
+ rd_kafka_mock_msgset_destroy(mpart, mset);
595
+
596
+ TAILQ_FOREACH_SAFE(coff, &mpart->committed_offsets, link, tmpcoff)
597
+ rd_kafka_mock_committed_offset_destroy(mpart, coff);
598
+
599
+ TAILQ_FOREACH_SAFE(mpart_leader, &mpart->leader_responses, link,
600
+ tmp_mpart_leader)
601
+ rd_kafka_mock_partition_leader_destroy(mpart, mpart_leader);
602
+
603
+ rd_list_destroy(&mpart->pidstates);
604
+
605
+ rd_free(mpart->replicas);
606
+ }
607
+
608
+
609
+ static void rd_kafka_mock_partition_init(rd_kafka_mock_topic_t *mtopic,
610
+ rd_kafka_mock_partition_t *mpart,
611
+ int id,
612
+ int replication_factor) {
613
+ mpart->topic = mtopic;
614
+ mpart->id = id;
615
+
616
+ mpart->follower_id = -1;
617
+ mpart->leader_epoch = -1; /* Start at -1 since assign_replicas() will
618
+ * bump it right away to 0. */
619
+
620
+ TAILQ_INIT(&mpart->msgsets);
621
+
622
+ mpart->max_size = 1024 * 1024 * 5;
623
+ mpart->max_cnt = 100000;
624
+
625
+ mpart->update_follower_start_offset = rd_true;
626
+ mpart->update_follower_end_offset = rd_true;
627
+
628
+ TAILQ_INIT(&mpart->committed_offsets);
629
+ TAILQ_INIT(&mpart->leader_responses);
630
+
631
+ rd_list_init(&mpart->pidstates, 0, rd_free);
632
+
633
+ rd_kafka_mock_partition_assign_replicas(mpart, replication_factor);
634
+ }
635
+
636
+ rd_kafka_mock_partition_t *
637
+ rd_kafka_mock_partition_find(const rd_kafka_mock_topic_t *mtopic,
638
+ int32_t partition) {
639
+ if (!mtopic || partition < 0 || partition >= mtopic->partition_cnt)
640
+ return NULL;
641
+
642
+ return (rd_kafka_mock_partition_t *)&mtopic->partitions[partition];
643
+ }
644
+
645
+
646
+ static void rd_kafka_mock_topic_destroy(rd_kafka_mock_topic_t *mtopic) {
647
+ int i;
648
+
649
+ for (i = 0; i < mtopic->partition_cnt; i++)
650
+ rd_kafka_mock_partition_destroy(&mtopic->partitions[i]);
651
+
652
+ TAILQ_REMOVE(&mtopic->cluster->topics, mtopic, link);
653
+ mtopic->cluster->topic_cnt--;
654
+
655
+ rd_free(mtopic->partitions);
656
+ rd_free(mtopic->name);
657
+ rd_free(mtopic);
658
+ }
659
+
660
+
661
+ static rd_kafka_mock_topic_t *
662
+ rd_kafka_mock_topic_new(rd_kafka_mock_cluster_t *mcluster,
663
+ const char *topic,
664
+ int partition_cnt,
665
+ int replication_factor) {
666
+ rd_kafka_mock_topic_t *mtopic;
667
+ int i;
668
+
669
+ mtopic = rd_calloc(1, sizeof(*mtopic));
670
+ /* Assign random topic id */
671
+ mtopic->id = rd_kafka_Uuid_random();
672
+ mtopic->name = rd_strdup(topic);
673
+ mtopic->cluster = mcluster;
674
+
675
+ mtopic->partition_cnt = partition_cnt;
676
+ mtopic->partitions =
677
+ rd_calloc(partition_cnt, sizeof(*mtopic->partitions));
678
+
679
+ for (i = 0; i < partition_cnt; i++)
680
+ rd_kafka_mock_partition_init(mtopic, &mtopic->partitions[i], i,
681
+ replication_factor);
682
+
683
+ TAILQ_INSERT_TAIL(&mcluster->topics, mtopic, link);
684
+ mcluster->topic_cnt++;
685
+
686
+ rd_kafka_dbg(mcluster->rk, MOCK, "MOCK",
687
+ "Created topic \"%s\" with %d partition(s) and "
688
+ "replication-factor %d",
689
+ mtopic->name, mtopic->partition_cnt, replication_factor);
690
+
691
+ return mtopic;
692
+ }
693
+
694
+
695
+ rd_kafka_mock_topic_t *
696
+ rd_kafka_mock_topic_find(const rd_kafka_mock_cluster_t *mcluster,
697
+ const char *name) {
698
+ const rd_kafka_mock_topic_t *mtopic;
699
+
700
+ TAILQ_FOREACH(mtopic, &mcluster->topics, link) {
701
+ if (!strcmp(mtopic->name, name))
702
+ return (rd_kafka_mock_topic_t *)mtopic;
703
+ }
704
+
705
+ return NULL;
706
+ }
707
+
708
+
709
+ rd_kafka_mock_topic_t *
710
+ rd_kafka_mock_topic_find_by_kstr(const rd_kafka_mock_cluster_t *mcluster,
711
+ const rd_kafkap_str_t *kname) {
712
+ const rd_kafka_mock_topic_t *mtopic;
713
+
714
+ TAILQ_FOREACH(mtopic, &mcluster->topics, link) {
715
+ if (!strncmp(mtopic->name, kname->str,
716
+ RD_KAFKAP_STR_LEN(kname)) &&
717
+ mtopic->name[RD_KAFKAP_STR_LEN(kname)] == '\0')
718
+ return (rd_kafka_mock_topic_t *)mtopic;
719
+ }
720
+
721
+ return NULL;
722
+ }
723
+
724
+ /**
725
+ * @brief Find a mock topic by id.
726
+ *
727
+ * @param mcluster Cluster to search in.
728
+ * @param id Topic id to find.
729
+ * @return Found topic or NULL.
730
+ *
731
+ * @locks mcluster->lock MUST be held.
732
+ */
733
+ rd_kafka_mock_topic_t *
734
+ rd_kafka_mock_topic_find_by_id(const rd_kafka_mock_cluster_t *mcluster,
735
+ rd_kafka_Uuid_t id) {
736
+ const rd_kafka_mock_topic_t *mtopic;
737
+
738
+ TAILQ_FOREACH(mtopic, &mcluster->topics, link) {
739
+ if (!rd_kafka_Uuid_cmp(mtopic->id, id))
740
+ return (rd_kafka_mock_topic_t *)mtopic;
741
+ }
742
+
743
+ return NULL;
744
+ }
745
+
746
+
747
+ /**
748
+ * @brief Create a topic using default settings.
749
+ * The topic must not already exist.
750
+ *
751
+ * @param errp will be set to an error code that is consistent with
752
+ * new topics on real clusters.
753
+ */
754
+ rd_kafka_mock_topic_t *
755
+ rd_kafka_mock_topic_auto_create(rd_kafka_mock_cluster_t *mcluster,
756
+ const char *topic,
757
+ int partition_cnt,
758
+ rd_kafka_resp_err_t *errp) {
759
+ rd_assert(!rd_kafka_mock_topic_find(mcluster, topic));
760
+ *errp = 0; // FIXME? RD_KAFKA_RESP_ERR_LEADER_NOT_AVAILABLE;
761
+ return rd_kafka_mock_topic_new(mcluster, topic,
762
+ partition_cnt == -1
763
+ ? mcluster->defaults.partition_cnt
764
+ : partition_cnt,
765
+ mcluster->defaults.replication_factor);
766
+ }
767
+
768
+
769
+ /**
770
+ * @brief Find or create topic.
771
+ *
772
+ * @param partition_cnt If not -1 and the topic does not exist, the automatic
773
+ * topic creation will create this number of topics.
774
+ * Otherwise use the default.
775
+ */
776
+ rd_kafka_mock_topic_t *
777
+ rd_kafka_mock_topic_get(rd_kafka_mock_cluster_t *mcluster,
778
+ const char *topic,
779
+ int partition_cnt) {
780
+ rd_kafka_mock_topic_t *mtopic;
781
+ rd_kafka_resp_err_t err;
782
+
783
+ if ((mtopic = rd_kafka_mock_topic_find(mcluster, topic)))
784
+ return mtopic;
785
+
786
+ return rd_kafka_mock_topic_auto_create(mcluster, topic, partition_cnt,
787
+ &err);
788
+ }
789
+
790
+ /**
791
+ * @brief Find or create a partition.
792
+ *
793
+ * @returns NULL if topic already exists and partition is out of range.
794
+ */
795
+ static rd_kafka_mock_partition_t *
796
+ rd_kafka_mock_partition_get(rd_kafka_mock_cluster_t *mcluster,
797
+ const char *topic,
798
+ int32_t partition) {
799
+ rd_kafka_mock_topic_t *mtopic;
800
+ rd_kafka_resp_err_t err;
801
+
802
+ if (!(mtopic = rd_kafka_mock_topic_find(mcluster, topic)))
803
+ mtopic = rd_kafka_mock_topic_auto_create(mcluster, topic,
804
+ partition + 1, &err);
805
+
806
+ if (partition >= mtopic->partition_cnt)
807
+ return NULL;
808
+
809
+ return &mtopic->partitions[partition];
810
+ }
811
+
812
+
813
+ /**
814
+ * @brief Set IO events for fd
815
+ */
816
+ static void
817
+ rd_kafka_mock_cluster_io_set_events(rd_kafka_mock_cluster_t *mcluster,
818
+ rd_socket_t fd,
819
+ int events) {
820
+ int i;
821
+
822
+ for (i = 0; i < mcluster->fd_cnt; i++) {
823
+ if (mcluster->fds[i].fd == fd) {
824
+ mcluster->fds[i].events |= events;
825
+ return;
826
+ }
827
+ }
828
+
829
+ rd_assert(!*"mock_cluster_io_set_events: fd not found");
830
+ }
831
+
832
+ /**
833
+ * @brief Set or clear single IO events for fd
834
+ */
835
+ static void
836
+ rd_kafka_mock_cluster_io_set_event(rd_kafka_mock_cluster_t *mcluster,
837
+ rd_socket_t fd,
838
+ rd_bool_t set,
839
+ int event) {
840
+ int i;
841
+
842
+ for (i = 0; i < mcluster->fd_cnt; i++) {
843
+ if (mcluster->fds[i].fd == fd) {
844
+ if (set)
845
+ mcluster->fds[i].events |= event;
846
+ else
847
+ mcluster->fds[i].events &= ~event;
848
+ return;
849
+ }
850
+ }
851
+
852
+ rd_assert(!*"mock_cluster_io_set_event: fd not found");
853
+ }
854
+
855
+
856
+ /**
857
+ * @brief Clear IO events for fd
858
+ */
859
+ static void
860
+ rd_kafka_mock_cluster_io_clear_events(rd_kafka_mock_cluster_t *mcluster,
861
+ rd_socket_t fd,
862
+ int events) {
863
+ int i;
864
+
865
+ for (i = 0; i < mcluster->fd_cnt; i++) {
866
+ if (mcluster->fds[i].fd == fd) {
867
+ mcluster->fds[i].events &= ~events;
868
+ return;
869
+ }
870
+ }
871
+
872
+ rd_assert(!*"mock_cluster_io_set_events: fd not found");
873
+ }
874
+
875
+
876
+ static void rd_kafka_mock_cluster_io_del(rd_kafka_mock_cluster_t *mcluster,
877
+ rd_socket_t fd) {
878
+ int i;
879
+
880
+ for (i = 0; i < mcluster->fd_cnt; i++) {
881
+ if (mcluster->fds[i].fd == fd) {
882
+ if (i + 1 < mcluster->fd_cnt) {
883
+ memmove(&mcluster->fds[i],
884
+ &mcluster->fds[i + 1],
885
+ sizeof(*mcluster->fds) *
886
+ (mcluster->fd_cnt - i));
887
+ memmove(&mcluster->handlers[i],
888
+ &mcluster->handlers[i + 1],
889
+ sizeof(*mcluster->handlers) *
890
+ (mcluster->fd_cnt - i));
891
+ }
892
+
893
+ mcluster->fd_cnt--;
894
+ return;
895
+ }
896
+ }
897
+
898
+ rd_assert(!*"mock_cluster_io_del: fd not found");
899
+ }
900
+
901
+
902
+ /**
903
+ * @brief Add \p fd to IO poll with initial desired events (POLLIN, et.al).
904
+ */
905
+ static void rd_kafka_mock_cluster_io_add(rd_kafka_mock_cluster_t *mcluster,
906
+ rd_socket_t fd,
907
+ int events,
908
+ rd_kafka_mock_io_handler_t handler,
909
+ void *opaque) {
910
+
911
+ if (mcluster->fd_cnt + 1 >= mcluster->fd_size) {
912
+ mcluster->fd_size += 8;
913
+
914
+ mcluster->fds = rd_realloc(
915
+ mcluster->fds, sizeof(*mcluster->fds) * mcluster->fd_size);
916
+ mcluster->handlers =
917
+ rd_realloc(mcluster->handlers,
918
+ sizeof(*mcluster->handlers) * mcluster->fd_size);
919
+ }
920
+
921
+ memset(&mcluster->fds[mcluster->fd_cnt], 0,
922
+ sizeof(mcluster->fds[mcluster->fd_cnt]));
923
+ mcluster->fds[mcluster->fd_cnt].fd = fd;
924
+ mcluster->fds[mcluster->fd_cnt].events = events;
925
+ mcluster->fds[mcluster->fd_cnt].revents = 0;
926
+ mcluster->handlers[mcluster->fd_cnt].cb = handler;
927
+ mcluster->handlers[mcluster->fd_cnt].opaque = opaque;
928
+ mcluster->fd_cnt++;
929
+ }
930
+
931
+ /**
932
+ * @brief Reassign partition replicas to broker, after deleting or
933
+ * adding a new one.
934
+ */
935
+ static void
936
+ rd_kafka_mock_cluster_reassign_partitions(rd_kafka_mock_cluster_t *mcluster) {
937
+ rd_kafka_mock_topic_t *mtopic;
938
+ TAILQ_FOREACH(mtopic, &mcluster->topics, link) {
939
+ int i;
940
+ for (i = 0; i < mtopic->partition_cnt; i++) {
941
+ rd_kafka_mock_partition_t *mpart =
942
+ &mtopic->partitions[i];
943
+ rd_kafka_mock_partition_assign_replicas(
944
+ mpart, mpart->replica_cnt);
945
+ }
946
+ }
947
+ }
948
+
949
+ static void rd_kafka_mock_connection_close(rd_kafka_mock_connection_t *mconn,
950
+ const char *reason) {
951
+ rd_kafka_buf_t *rkbuf;
952
+
953
+ rd_kafka_dbg(mconn->broker->cluster->rk, MOCK, "MOCK",
954
+ "Broker %" PRId32 ": Connection from %s closed: %s",
955
+ mconn->broker->id,
956
+ rd_sockaddr2str(&mconn->peer, RD_SOCKADDR2STR_F_PORT),
957
+ reason);
958
+
959
+ rd_kafka_mock_cgrps_connection_closed(mconn->broker->cluster, mconn);
960
+
961
+ rd_kafka_timer_stop(&mconn->broker->cluster->timers, &mconn->write_tmr,
962
+ rd_true);
963
+
964
+ while ((rkbuf = TAILQ_FIRST(&mconn->outbufs.rkbq_bufs))) {
965
+ rd_kafka_bufq_deq(&mconn->outbufs, rkbuf);
966
+ rd_kafka_buf_destroy(rkbuf);
967
+ }
968
+
969
+ if (mconn->rxbuf)
970
+ rd_kafka_buf_destroy(mconn->rxbuf);
971
+
972
+ rd_kafka_mock_cluster_io_del(mconn->broker->cluster,
973
+ mconn->transport->rktrans_s);
974
+ TAILQ_REMOVE(&mconn->broker->connections, mconn, link);
975
+ rd_kafka_transport_close(mconn->transport);
976
+ rd_free(mconn);
977
+ }
978
+
979
+ void rd_kafka_mock_connection_send_response0(rd_kafka_mock_connection_t *mconn,
980
+ rd_kafka_buf_t *resp,
981
+ rd_bool_t tags_written) {
982
+
983
+ if (!tags_written && (resp->rkbuf_flags & RD_KAFKA_OP_F_FLEXVER)) {
984
+ /* Empty struct tags */
985
+ rd_kafka_buf_write_i8(resp, 0);
986
+ }
987
+
988
+ /* rkbuf_ts_sent might be initialized with a RTT delay, else 0. */
989
+ resp->rkbuf_ts_sent += rd_clock();
990
+
991
+ resp->rkbuf_reshdr.Size =
992
+ (int32_t)(rd_buf_write_pos(&resp->rkbuf_buf) - 4);
993
+
994
+ rd_kafka_buf_update_i32(resp, 0, resp->rkbuf_reshdr.Size);
995
+
996
+ rd_kafka_dbg(mconn->broker->cluster->rk, MOCK, "MOCK",
997
+ "Broker %" PRId32 ": Sending %sResponseV%hd to %s",
998
+ mconn->broker->id,
999
+ rd_kafka_ApiKey2str(resp->rkbuf_reqhdr.ApiKey),
1000
+ resp->rkbuf_reqhdr.ApiVersion,
1001
+ rd_sockaddr2str(&mconn->peer, RD_SOCKADDR2STR_F_PORT));
1002
+
1003
+ /* Set up a buffer reader for sending the buffer. */
1004
+ rd_slice_init_full(&resp->rkbuf_reader, &resp->rkbuf_buf);
1005
+
1006
+ rd_kafka_bufq_enq(&mconn->outbufs, resp);
1007
+
1008
+ rd_kafka_mock_cluster_io_set_events(
1009
+ mconn->broker->cluster, mconn->transport->rktrans_s, POLLOUT);
1010
+ }
1011
+
1012
+
1013
+ /**
1014
+ * @returns 1 if a complete request is available in which case \p slicep
1015
+ * is set to a new slice containing the data,
1016
+ * 0 if a complete request is not yet available,
1017
+ * -1 on error.
1018
+ */
1019
+ static int
1020
+ rd_kafka_mock_connection_read_request(rd_kafka_mock_connection_t *mconn,
1021
+ rd_kafka_buf_t **rkbufp) {
1022
+ rd_kafka_mock_cluster_t *mcluster = mconn->broker->cluster;
1023
+ rd_kafka_t *rk = mcluster->rk;
1024
+ const rd_bool_t log_decode_errors = rd_true;
1025
+ rd_kafka_buf_t *rkbuf;
1026
+ char errstr[128];
1027
+ ssize_t r;
1028
+
1029
+ if (!(rkbuf = mconn->rxbuf)) {
1030
+ /* Initial read for a protocol request.
1031
+ * Allocate enough room for the protocol header
1032
+ * (where the total size is located). */
1033
+ rkbuf = mconn->rxbuf =
1034
+ rd_kafka_buf_new(2, RD_KAFKAP_REQHDR_SIZE);
1035
+
1036
+ /* Protocol parsing code needs the rkb for logging */
1037
+ rkbuf->rkbuf_rkb = mconn->broker->cluster->dummy_rkb;
1038
+ rd_kafka_broker_keep(rkbuf->rkbuf_rkb);
1039
+
1040
+ /* Make room for request header */
1041
+ rd_buf_write_ensure(&rkbuf->rkbuf_buf, RD_KAFKAP_REQHDR_SIZE,
1042
+ RD_KAFKAP_REQHDR_SIZE);
1043
+ }
1044
+
1045
+ /* Read as much data as possible from the socket into the
1046
+ * connection receive buffer. */
1047
+ r = rd_kafka_transport_recv(mconn->transport, &rkbuf->rkbuf_buf, errstr,
1048
+ sizeof(errstr));
1049
+ if (r == -1) {
1050
+ rd_kafka_dbg(
1051
+ rk, MOCK, "MOCK",
1052
+ "Broker %" PRId32
1053
+ ": Connection %s: "
1054
+ "receive failed: %s",
1055
+ mconn->broker->id,
1056
+ rd_sockaddr2str(&mconn->peer, RD_SOCKADDR2STR_F_PORT),
1057
+ errstr);
1058
+ return -1;
1059
+ } else if (r == 0) {
1060
+ return 0; /* Need more data */
1061
+ }
1062
+
1063
+ if (rd_buf_write_pos(&rkbuf->rkbuf_buf) == RD_KAFKAP_REQHDR_SIZE) {
1064
+ /* Received the full header, now check full request
1065
+ * size and allocate the buffer accordingly. */
1066
+
1067
+ /* Initialize reader */
1068
+ rd_slice_init(&rkbuf->rkbuf_reader, &rkbuf->rkbuf_buf, 0,
1069
+ RD_KAFKAP_REQHDR_SIZE);
1070
+
1071
+ rd_kafka_buf_read_i32(rkbuf, &rkbuf->rkbuf_reqhdr.Size);
1072
+ rd_kafka_buf_read_i16(rkbuf, &rkbuf->rkbuf_reqhdr.ApiKey);
1073
+ rd_kafka_buf_read_i16(rkbuf, &rkbuf->rkbuf_reqhdr.ApiVersion);
1074
+
1075
+ if (rkbuf->rkbuf_reqhdr.ApiKey < 0 ||
1076
+ rkbuf->rkbuf_reqhdr.ApiKey >= RD_KAFKAP__NUM) {
1077
+ rd_kafka_buf_parse_fail(
1078
+ rkbuf, "Invalid ApiKey %hd from %s",
1079
+ rkbuf->rkbuf_reqhdr.ApiKey,
1080
+ rd_sockaddr2str(&mconn->peer,
1081
+ RD_SOCKADDR2STR_F_PORT));
1082
+ RD_NOTREACHED();
1083
+ }
1084
+
1085
+ /* Check if request version has flexible fields (KIP-482) */
1086
+ if (mcluster->api_handlers[rkbuf->rkbuf_reqhdr.ApiKey]
1087
+ .FlexVersion != -1 &&
1088
+ rkbuf->rkbuf_reqhdr.ApiVersion >=
1089
+ mcluster->api_handlers[rkbuf->rkbuf_reqhdr.ApiKey]
1090
+ .FlexVersion)
1091
+ rkbuf->rkbuf_flags |= RD_KAFKA_OP_F_FLEXVER;
1092
+
1093
+
1094
+ rd_kafka_buf_read_i32(rkbuf, &rkbuf->rkbuf_reqhdr.CorrId);
1095
+
1096
+ rkbuf->rkbuf_totlen = rkbuf->rkbuf_reqhdr.Size + 4;
1097
+
1098
+ if (rkbuf->rkbuf_totlen < RD_KAFKAP_REQHDR_SIZE + 2 ||
1099
+ rkbuf->rkbuf_totlen >
1100
+ (size_t)rk->rk_conf.recv_max_msg_size) {
1101
+ rd_kafka_buf_parse_fail(
1102
+ rkbuf, "Invalid request size %" PRId32 " from %s",
1103
+ rkbuf->rkbuf_reqhdr.Size,
1104
+ rd_sockaddr2str(&mconn->peer,
1105
+ RD_SOCKADDR2STR_F_PORT));
1106
+ RD_NOTREACHED();
1107
+ }
1108
+
1109
+ /* Now adjust totlen to skip the header */
1110
+ rkbuf->rkbuf_totlen -= RD_KAFKAP_REQHDR_SIZE;
1111
+
1112
+ if (!rkbuf->rkbuf_totlen) {
1113
+ /* Empty request (valid) */
1114
+ *rkbufp = rkbuf;
1115
+ mconn->rxbuf = NULL;
1116
+ return 1;
1117
+ }
1118
+
1119
+ /* Allocate space for the request payload */
1120
+ rd_buf_write_ensure(&rkbuf->rkbuf_buf, rkbuf->rkbuf_totlen,
1121
+ rkbuf->rkbuf_totlen);
1122
+
1123
+ } else if (rd_buf_write_pos(&rkbuf->rkbuf_buf) -
1124
+ RD_KAFKAP_REQHDR_SIZE ==
1125
+ rkbuf->rkbuf_totlen) {
1126
+ /* The full request is now read into the buffer. */
1127
+
1128
+ /* Set up response reader slice starting past the
1129
+ * request header */
1130
+ rd_slice_init(&rkbuf->rkbuf_reader, &rkbuf->rkbuf_buf,
1131
+ RD_KAFKAP_REQHDR_SIZE,
1132
+ rd_buf_len(&rkbuf->rkbuf_buf) -
1133
+ RD_KAFKAP_REQHDR_SIZE);
1134
+
1135
+ /* For convenience, shave off the ClientId */
1136
+ rd_kafka_buf_skip_str_no_flexver(rkbuf);
1137
+
1138
+ /* And the flexible versions header tags, if any */
1139
+ rd_kafka_buf_skip_tags(rkbuf);
1140
+
1141
+ /* Return the buffer to the caller */
1142
+ *rkbufp = rkbuf;
1143
+ mconn->rxbuf = NULL;
1144
+ return 1;
1145
+ }
1146
+
1147
+ return 0;
1148
+
1149
+
1150
+ err_parse:
1151
+ return -1;
1152
+ }
1153
+
1154
+ rd_kafka_buf_t *rd_kafka_mock_buf_new_response(const rd_kafka_buf_t *request) {
1155
+ rd_kafka_buf_t *rkbuf = rd_kafka_buf_new(1, 100);
1156
+
1157
+ /* Copy request header so the ApiVersion remains known */
1158
+ rkbuf->rkbuf_reqhdr = request->rkbuf_reqhdr;
1159
+
1160
+ /* Size, updated later */
1161
+ rd_kafka_buf_write_i32(rkbuf, 0);
1162
+
1163
+ /* CorrId */
1164
+ rd_kafka_buf_write_i32(rkbuf, request->rkbuf_reqhdr.CorrId);
1165
+
1166
+ if (request->rkbuf_flags & RD_KAFKA_OP_F_FLEXVER) {
1167
+ rkbuf->rkbuf_flags |= RD_KAFKA_OP_F_FLEXVER;
1168
+ /* Write empty response header tags, unless this is the
1169
+ * ApiVersionResponse which needs to be backwards compatible. */
1170
+ if (request->rkbuf_reqhdr.ApiKey != RD_KAFKAP_ApiVersion)
1171
+ rd_kafka_buf_write_i8(rkbuf, 0);
1172
+ }
1173
+
1174
+ return rkbuf;
1175
+ }
1176
+
1177
+
1178
+
1179
+ /**
1180
+ * @brief Parse protocol request.
1181
+ *
1182
+ * @returns 0 on success, -1 on parse error.
1183
+ */
1184
+ static int
1185
+ rd_kafka_mock_connection_parse_request(rd_kafka_mock_connection_t *mconn,
1186
+ rd_kafka_buf_t *rkbuf) {
1187
+ rd_kafka_mock_cluster_t *mcluster = mconn->broker->cluster;
1188
+ rd_kafka_t *rk = mcluster->rk;
1189
+
1190
+ if (rkbuf->rkbuf_reqhdr.ApiKey < 0 ||
1191
+ rkbuf->rkbuf_reqhdr.ApiKey >= RD_KAFKAP__NUM ||
1192
+ !mcluster->api_handlers[rkbuf->rkbuf_reqhdr.ApiKey].cb) {
1193
+ rd_kafka_log(
1194
+ rk, LOG_ERR, "MOCK",
1195
+ "Broker %" PRId32
1196
+ ": unsupported %sRequestV%hd "
1197
+ "from %s",
1198
+ mconn->broker->id,
1199
+ rd_kafka_ApiKey2str(rkbuf->rkbuf_reqhdr.ApiKey),
1200
+ rkbuf->rkbuf_reqhdr.ApiVersion,
1201
+ rd_sockaddr2str(&mconn->peer, RD_SOCKADDR2STR_F_PORT));
1202
+ return -1;
1203
+ }
1204
+
1205
+ /* ApiVersionRequest handles future versions, for everything else
1206
+ * make sure the ApiVersion is supported. */
1207
+ if (rkbuf->rkbuf_reqhdr.ApiKey != RD_KAFKAP_ApiVersion &&
1208
+ !rd_kafka_mock_cluster_ApiVersion_check(
1209
+ mcluster, rkbuf->rkbuf_reqhdr.ApiKey,
1210
+ rkbuf->rkbuf_reqhdr.ApiVersion)) {
1211
+ rd_kafka_log(
1212
+ rk, LOG_ERR, "MOCK",
1213
+ "Broker %" PRId32
1214
+ ": unsupported %sRequest "
1215
+ "version %hd from %s",
1216
+ mconn->broker->id,
1217
+ rd_kafka_ApiKey2str(rkbuf->rkbuf_reqhdr.ApiKey),
1218
+ rkbuf->rkbuf_reqhdr.ApiVersion,
1219
+ rd_sockaddr2str(&mconn->peer, RD_SOCKADDR2STR_F_PORT));
1220
+ return -1;
1221
+ }
1222
+
1223
+ mtx_lock(&mcluster->lock);
1224
+ if (mcluster->track_requests) {
1225
+ rd_list_add(&mcluster->request_list,
1226
+ rd_kafka_mock_request_new(
1227
+ mconn->broker->id, rkbuf->rkbuf_reqhdr.ApiKey,
1228
+ rd_clock()));
1229
+ }
1230
+ mtx_unlock(&mcluster->lock);
1231
+
1232
+ rd_kafka_dbg(rk, MOCK, "MOCK",
1233
+ "Broker %" PRId32 ": Received %sRequestV%hd from %s",
1234
+ mconn->broker->id,
1235
+ rd_kafka_ApiKey2str(rkbuf->rkbuf_reqhdr.ApiKey),
1236
+ rkbuf->rkbuf_reqhdr.ApiVersion,
1237
+ rd_sockaddr2str(&mconn->peer, RD_SOCKADDR2STR_F_PORT));
1238
+
1239
+ return mcluster->api_handlers[rkbuf->rkbuf_reqhdr.ApiKey].cb(mconn,
1240
+ rkbuf);
1241
+ }
1242
+
1243
+
1244
+ /**
1245
+ * @brief Timer callback to set the POLLOUT flag for a connection after
1246
+ * the delay has expired.
1247
+ */
1248
+ static void rd_kafka_mock_connection_write_out_tmr_cb(rd_kafka_timers_t *rkts,
1249
+ void *arg) {
1250
+ rd_kafka_mock_connection_t *mconn = arg;
1251
+
1252
+ rd_kafka_mock_cluster_io_set_events(
1253
+ mconn->broker->cluster, mconn->transport->rktrans_s, POLLOUT);
1254
+ }
1255
+
1256
+
1257
+ /**
1258
+ * @brief Send as many bytes as possible from the output buffer.
1259
+ *
1260
+ * @returns 1 if all buffers were sent, 0 if more buffers need to be sent, or
1261
+ * -1 on error.
1262
+ */
1263
+ static ssize_t
1264
+ rd_kafka_mock_connection_write_out(rd_kafka_mock_connection_t *mconn) {
1265
+ rd_kafka_buf_t *rkbuf;
1266
+ rd_ts_t now = rd_clock();
1267
+ rd_ts_t rtt = mconn->broker->rtt;
1268
+
1269
+ while ((rkbuf = TAILQ_FIRST(&mconn->outbufs.rkbq_bufs))) {
1270
+ ssize_t r;
1271
+ char errstr[128];
1272
+ rd_ts_t ts_delay = 0;
1273
+
1274
+ /* Connection delay/rtt is set. */
1275
+ if (rkbuf->rkbuf_ts_sent + rtt > now)
1276
+ ts_delay = rkbuf->rkbuf_ts_sent + rtt;
1277
+
1278
+ /* Response is being delayed */
1279
+ if (rkbuf->rkbuf_ts_retry && rkbuf->rkbuf_ts_retry > now)
1280
+ ts_delay = rkbuf->rkbuf_ts_retry + rtt;
1281
+
1282
+ if (ts_delay) {
1283
+ /* Delay response */
1284
+ rd_kafka_timer_start_oneshot(
1285
+ &mconn->broker->cluster->timers, &mconn->write_tmr,
1286
+ rd_false, ts_delay - now,
1287
+ rd_kafka_mock_connection_write_out_tmr_cb, mconn);
1288
+ break;
1289
+ }
1290
+
1291
+ if ((r = rd_kafka_transport_send(mconn->transport,
1292
+ &rkbuf->rkbuf_reader, errstr,
1293
+ sizeof(errstr))) == -1)
1294
+ return -1;
1295
+
1296
+ if (rd_slice_remains(&rkbuf->rkbuf_reader) > 0)
1297
+ return 0; /* Partial send, continue next time */
1298
+
1299
+ /* Entire buffer sent, unlink and free */
1300
+ rd_kafka_bufq_deq(&mconn->outbufs, rkbuf);
1301
+
1302
+ rd_kafka_buf_destroy(rkbuf);
1303
+ }
1304
+
1305
+ rd_kafka_mock_cluster_io_clear_events(
1306
+ mconn->broker->cluster, mconn->transport->rktrans_s, POLLOUT);
1307
+
1308
+ return 1;
1309
+ }
1310
+
1311
+
1312
+ /**
1313
+ * @brief Call connection_write_out() for all the broker's connections.
1314
+ *
1315
+ * Use to check if any responses should be sent when RTT has changed.
1316
+ */
1317
+ static void
1318
+ rd_kafka_mock_broker_connections_write_out(rd_kafka_mock_broker_t *mrkb) {
1319
+ rd_kafka_mock_connection_t *mconn, *tmp;
1320
+
1321
+ /* Need a safe loop since connections may be removed on send error */
1322
+ TAILQ_FOREACH_SAFE(mconn, &mrkb->connections, link, tmp) {
1323
+ rd_kafka_mock_connection_write_out(mconn);
1324
+ }
1325
+ }
1326
+
1327
+
1328
+ /**
1329
+ * @brief Per-Connection IO handler
1330
+ */
1331
+ static void rd_kafka_mock_connection_io(rd_kafka_mock_cluster_t *mcluster,
1332
+ rd_socket_t fd,
1333
+ int events,
1334
+ void *opaque) {
1335
+ rd_kafka_mock_connection_t *mconn = opaque;
1336
+
1337
+ if (events & POLLIN) {
1338
+ rd_kafka_buf_t *rkbuf;
1339
+ int r;
1340
+
1341
+ while (1) {
1342
+ /* Read full request */
1343
+ r = rd_kafka_mock_connection_read_request(mconn,
1344
+ &rkbuf);
1345
+ if (r == 0)
1346
+ break; /* Need more data */
1347
+ else if (r == -1) {
1348
+ rd_kafka_mock_connection_close(mconn,
1349
+ "Read error");
1350
+ return;
1351
+ }
1352
+
1353
+ /* Parse and handle request */
1354
+ r = rd_kafka_mock_connection_parse_request(mconn,
1355
+ rkbuf);
1356
+ rd_kafka_buf_destroy(rkbuf);
1357
+ if (r == -1) {
1358
+ rd_kafka_mock_connection_close(mconn,
1359
+ "Parse error");
1360
+ return;
1361
+ }
1362
+ }
1363
+ }
1364
+
1365
+ if (events & POLLERR) {
1366
+ rd_kafka_mock_connection_close(mconn,
1367
+ "Disconnected: "
1368
+ "Error condition");
1369
+ return;
1370
+ }
1371
+ if (events & POLLHUP) {
1372
+ rd_kafka_mock_connection_close(mconn, "Disconnected: Hang up");
1373
+ return;
1374
+ }
1375
+
1376
+ if (events & POLLOUT) {
1377
+ if (rd_kafka_mock_connection_write_out(mconn) == -1) {
1378
+ rd_kafka_mock_connection_close(mconn, "Write error");
1379
+ return;
1380
+ }
1381
+ }
1382
+ }
1383
+
1384
+
1385
+ /**
1386
+ * @brief Set connection as blocking, POLLIN will not be served.
1387
+ */
1388
+ void rd_kafka_mock_connection_set_blocking(rd_kafka_mock_connection_t *mconn,
1389
+ rd_bool_t blocking) {
1390
+ rd_kafka_mock_cluster_io_set_event(mconn->broker->cluster,
1391
+ mconn->transport->rktrans_s,
1392
+ !blocking, POLLIN);
1393
+ }
1394
+
1395
+
1396
+ static rd_kafka_mock_connection_t *
1397
+ rd_kafka_mock_connection_new(rd_kafka_mock_broker_t *mrkb,
1398
+ rd_socket_t fd,
1399
+ const struct sockaddr_in *peer) {
1400
+ rd_kafka_mock_connection_t *mconn;
1401
+ rd_kafka_transport_t *rktrans;
1402
+ char errstr[128];
1403
+
1404
+ if (!mrkb->up) {
1405
+ rd_socket_close(fd);
1406
+ return NULL;
1407
+ }
1408
+
1409
+ rktrans = rd_kafka_transport_new(mrkb->cluster->dummy_rkb, fd, errstr,
1410
+ sizeof(errstr));
1411
+ if (!rktrans) {
1412
+ rd_kafka_log(mrkb->cluster->rk, LOG_ERR, "MOCK",
1413
+ "Failed to create transport for new "
1414
+ "mock connection: %s",
1415
+ errstr);
1416
+ rd_socket_close(fd);
1417
+ return NULL;
1418
+ }
1419
+
1420
+ rd_kafka_transport_post_connect_setup(rktrans);
1421
+
1422
+ mconn = rd_calloc(1, sizeof(*mconn));
1423
+ mconn->broker = mrkb;
1424
+ mconn->transport = rktrans;
1425
+ mconn->peer = *peer;
1426
+ rd_kafka_bufq_init(&mconn->outbufs);
1427
+
1428
+ TAILQ_INSERT_TAIL(&mrkb->connections, mconn, link);
1429
+
1430
+ rd_kafka_mock_cluster_io_add(mrkb->cluster, mconn->transport->rktrans_s,
1431
+ POLLIN, rd_kafka_mock_connection_io,
1432
+ mconn);
1433
+
1434
+ rd_kafka_dbg(mrkb->cluster->rk, MOCK, "MOCK",
1435
+ "Broker %" PRId32 ": New connection from %s", mrkb->id,
1436
+ rd_sockaddr2str(&mconn->peer, RD_SOCKADDR2STR_F_PORT));
1437
+
1438
+ return mconn;
1439
+ }
1440
+
1441
+
1442
+
1443
+ static void rd_kafka_mock_cluster_op_io(rd_kafka_mock_cluster_t *mcluster,
1444
+ rd_socket_t fd,
1445
+ int events,
1446
+ void *opaque) {
1447
+ /* Read wake-up fd data and throw away, just used for wake-ups*/
1448
+ char buf[1024];
1449
+ while (rd_socket_read(fd, buf, sizeof(buf)) > 0)
1450
+ ; /* Read all buffered signalling bytes */
1451
+ }
1452
+
1453
+
1454
+ static int rd_kafka_mock_cluster_io_poll(rd_kafka_mock_cluster_t *mcluster,
1455
+ int timeout_ms) {
1456
+ int r;
1457
+ int i;
1458
+
1459
+ r = rd_socket_poll(mcluster->fds, mcluster->fd_cnt, timeout_ms);
1460
+ if (r == RD_SOCKET_ERROR) {
1461
+ rd_kafka_log(mcluster->rk, LOG_CRIT, "MOCK",
1462
+ "Mock cluster failed to poll %d fds: %d: %s",
1463
+ mcluster->fd_cnt, r,
1464
+ rd_socket_strerror(rd_socket_errno));
1465
+ return -1;
1466
+ }
1467
+
1468
+ /* Serve ops, if any */
1469
+ rd_kafka_q_serve(mcluster->ops, RD_POLL_NOWAIT, 0,
1470
+ RD_KAFKA_Q_CB_CALLBACK, NULL, NULL);
1471
+
1472
+ /* Handle IO events, if any, and if not terminating */
1473
+ for (i = 0; mcluster->run && r > 0 && i < mcluster->fd_cnt; i++) {
1474
+ if (!mcluster->fds[i].revents)
1475
+ continue;
1476
+
1477
+ /* Call IO handler */
1478
+ mcluster->handlers[i].cb(mcluster, mcluster->fds[i].fd,
1479
+ mcluster->fds[i].revents,
1480
+ mcluster->handlers[i].opaque);
1481
+ r--;
1482
+ }
1483
+
1484
+ return 0;
1485
+ }
1486
+
1487
+
1488
+ static int rd_kafka_mock_cluster_thread_main(void *arg) {
1489
+ rd_kafka_mock_cluster_t *mcluster = arg;
1490
+
1491
+ rd_kafka_set_thread_name("mock");
1492
+ rd_kafka_set_thread_sysname("rdk:mock");
1493
+ rd_kafka_interceptors_on_thread_start(mcluster->rk,
1494
+ RD_KAFKA_THREAD_BACKGROUND);
1495
+ rd_atomic32_add(&rd_kafka_thread_cnt_curr, 1);
1496
+
1497
+ /* Op wakeup fd */
1498
+ rd_kafka_mock_cluster_io_add(mcluster, mcluster->wakeup_fds[0], POLLIN,
1499
+ rd_kafka_mock_cluster_op_io, NULL);
1500
+
1501
+ mcluster->run = rd_true;
1502
+
1503
+ while (mcluster->run) {
1504
+ int sleeptime = (int)((rd_kafka_timers_next(&mcluster->timers,
1505
+ 1000 * 1000 /*1s*/,
1506
+ 1 /*lock*/) +
1507
+ 999) /
1508
+ 1000);
1509
+
1510
+ if (rd_kafka_mock_cluster_io_poll(mcluster, sleeptime) == -1)
1511
+ break;
1512
+
1513
+ rd_kafka_timers_run(&mcluster->timers, RD_POLL_NOWAIT);
1514
+ }
1515
+
1516
+ rd_kafka_mock_cluster_io_del(mcluster, mcluster->wakeup_fds[0]);
1517
+
1518
+
1519
+ rd_kafka_interceptors_on_thread_exit(mcluster->rk,
1520
+ RD_KAFKA_THREAD_BACKGROUND);
1521
+ rd_atomic32_sub(&rd_kafka_thread_cnt_curr, 1);
1522
+
1523
+ rd_kafka_mock_cluster_destroy0(mcluster);
1524
+
1525
+ return 0;
1526
+ }
1527
+
1528
+
1529
+
1530
+ static void rd_kafka_mock_broker_listen_io(rd_kafka_mock_cluster_t *mcluster,
1531
+ rd_socket_t fd,
1532
+ int events,
1533
+ void *opaque) {
1534
+ rd_kafka_mock_broker_t *mrkb = opaque;
1535
+
1536
+ if (events & (POLLERR | POLLHUP))
1537
+ rd_assert(!*"Mock broker listen socket error");
1538
+
1539
+ if (events & POLLIN) {
1540
+ rd_socket_t new_s;
1541
+ struct sockaddr_in peer;
1542
+ socklen_t peer_size = sizeof(peer);
1543
+
1544
+ new_s = accept(mrkb->listen_s, (struct sockaddr *)&peer,
1545
+ &peer_size);
1546
+ if (new_s == RD_SOCKET_ERROR) {
1547
+ rd_kafka_log(mcluster->rk, LOG_ERR, "MOCK",
1548
+ "Failed to accept mock broker socket: %s",
1549
+ rd_socket_strerror(rd_socket_errno));
1550
+ return;
1551
+ }
1552
+
1553
+ rd_kafka_mock_connection_new(mrkb, new_s, &peer);
1554
+ }
1555
+ }
1556
+
1557
+
1558
+ /**
1559
+ * @brief Close all connections to broker.
1560
+ */
1561
+ static void rd_kafka_mock_broker_close_all(rd_kafka_mock_broker_t *mrkb,
1562
+ const char *reason) {
1563
+ rd_kafka_mock_connection_t *mconn;
1564
+
1565
+ while ((mconn = TAILQ_FIRST(&mrkb->connections)))
1566
+ rd_kafka_mock_connection_close(mconn, reason);
1567
+ }
1568
+
1569
+ /**
1570
+ * @brief Destroy error stack, must be unlinked.
1571
+ */
1572
+ static void
1573
+ rd_kafka_mock_error_stack_destroy(rd_kafka_mock_error_stack_t *errstack) {
1574
+ if (errstack->errs)
1575
+ rd_free(errstack->errs);
1576
+ rd_free(errstack);
1577
+ }
1578
+
1579
+
1580
+ static void rd_kafka_mock_broker_destroy(rd_kafka_mock_broker_t *mrkb) {
1581
+ rd_kafka_mock_error_stack_t *errstack;
1582
+
1583
+ rd_kafka_mock_broker_close_all(mrkb, "Destroying broker");
1584
+
1585
+ if (mrkb->listen_s != -1) {
1586
+ if (mrkb->up)
1587
+ rd_kafka_mock_cluster_io_del(mrkb->cluster,
1588
+ mrkb->listen_s);
1589
+ rd_socket_close(mrkb->listen_s);
1590
+ }
1591
+
1592
+ while ((errstack = TAILQ_FIRST(&mrkb->errstacks))) {
1593
+ TAILQ_REMOVE(&mrkb->errstacks, errstack, link);
1594
+ rd_kafka_mock_error_stack_destroy(errstack);
1595
+ }
1596
+
1597
+ if (mrkb->rack)
1598
+ rd_free(mrkb->rack);
1599
+
1600
+ rd_kafka_mock_coord_remove(mrkb->cluster, mrkb->id);
1601
+
1602
+ TAILQ_REMOVE(&mrkb->cluster->brokers, mrkb, link);
1603
+ mrkb->cluster->broker_cnt--;
1604
+
1605
+ rd_free(mrkb);
1606
+ }
1607
+
1608
+
1609
+ rd_kafka_resp_err_t
1610
+ rd_kafka_mock_broker_decommission(rd_kafka_mock_cluster_t *mcluster,
1611
+ int32_t broker_id) {
1612
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
1613
+
1614
+ rko->rko_u.mock.broker_id = broker_id;
1615
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_BROKER_DECOMMISSION;
1616
+
1617
+ return rd_kafka_op_err_destroy(
1618
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
1619
+ }
1620
+
1621
+ rd_kafka_resp_err_t rd_kafka_mock_broker_add(rd_kafka_mock_cluster_t *mcluster,
1622
+ int32_t broker_id) {
1623
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
1624
+
1625
+ rko->rko_u.mock.broker_id = broker_id;
1626
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_BROKER_ADD;
1627
+
1628
+ return rd_kafka_op_err_destroy(
1629
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
1630
+ }
1631
+
1632
+
1633
+ /**
1634
+ * @brief Starts listening on the mock broker socket.
1635
+ *
1636
+ * @returns 0 on success or -1 on error (logged).
1637
+ */
1638
+ static int rd_kafka_mock_broker_start_listener(rd_kafka_mock_broker_t *mrkb) {
1639
+ rd_assert(mrkb->listen_s != -1);
1640
+
1641
+ if (listen(mrkb->listen_s, 5) == RD_SOCKET_ERROR) {
1642
+ rd_kafka_log(mrkb->cluster->rk, LOG_CRIT, "MOCK",
1643
+ "Failed to listen on mock broker socket: %s",
1644
+ rd_socket_strerror(rd_socket_errno));
1645
+ return -1;
1646
+ }
1647
+
1648
+ rd_kafka_mock_cluster_io_add(mrkb->cluster, mrkb->listen_s, POLLIN,
1649
+ rd_kafka_mock_broker_listen_io, mrkb);
1650
+
1651
+ return 0;
1652
+ }
1653
+
1654
+
1655
+ /**
1656
+ * @brief Creates a new listener socket for \p mrkb but does NOT starts
1657
+ * listening.
1658
+ *
1659
+ * @param sin is the address and port to bind. If the port is zero a random
1660
+ * port will be assigned (by the kernel) and the address and port
1661
+ * will be returned in this pointer.
1662
+ *
1663
+ * @returns listener socket on success or -1 on error (errors are logged).
1664
+ */
1665
+ static int rd_kafka_mock_broker_new_listener(rd_kafka_mock_cluster_t *mcluster,
1666
+ struct sockaddr_in *sinp) {
1667
+ struct sockaddr_in sin = *sinp;
1668
+ socklen_t sin_len = sizeof(sin);
1669
+ int listen_s;
1670
+ int on = 1;
1671
+
1672
+ if (!sin.sin_family)
1673
+ sin.sin_family = AF_INET;
1674
+
1675
+ /*
1676
+ * Create and bind socket to any loopback port
1677
+ */
1678
+ listen_s =
1679
+ rd_kafka_socket_cb_linux(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL);
1680
+ if (listen_s == RD_SOCKET_ERROR) {
1681
+ rd_kafka_log(mcluster->rk, LOG_CRIT, "MOCK",
1682
+ "Unable to create mock broker listen socket: %s",
1683
+ rd_socket_strerror(rd_socket_errno));
1684
+ return -1;
1685
+ }
1686
+
1687
+ if (setsockopt(listen_s, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
1688
+ sizeof(on)) == -1) {
1689
+ rd_kafka_log(mcluster->rk, LOG_CRIT, "MOCK",
1690
+ "Failed to set SO_REUSEADDR on mock broker "
1691
+ "listen socket: %s",
1692
+ rd_socket_strerror(rd_socket_errno));
1693
+ rd_socket_close(listen_s);
1694
+ return -1;
1695
+ }
1696
+
1697
+ if (bind(listen_s, (struct sockaddr *)&sin, sizeof(sin)) ==
1698
+ RD_SOCKET_ERROR) {
1699
+ rd_kafka_log(mcluster->rk, LOG_CRIT, "MOCK",
1700
+ "Failed to bind mock broker socket to %s: %s",
1701
+ rd_socket_strerror(rd_socket_errno),
1702
+ rd_sockaddr2str(&sin, RD_SOCKADDR2STR_F_PORT));
1703
+ rd_socket_close(listen_s);
1704
+ return -1;
1705
+ }
1706
+
1707
+ if (getsockname(listen_s, (struct sockaddr *)&sin, &sin_len) ==
1708
+ RD_SOCKET_ERROR) {
1709
+ rd_kafka_log(mcluster->rk, LOG_CRIT, "MOCK",
1710
+ "Failed to get mock broker socket name: %s",
1711
+ rd_socket_strerror(rd_socket_errno));
1712
+ rd_socket_close(listen_s);
1713
+ return -1;
1714
+ }
1715
+ rd_assert(sin.sin_family == AF_INET);
1716
+ /* If a filled in sinp was passed make sure nothing changed. */
1717
+ rd_assert(!sinp->sin_port || !memcmp(sinp, &sin, sizeof(sin)));
1718
+
1719
+ *sinp = sin;
1720
+
1721
+ return listen_s;
1722
+ }
1723
+
1724
+
1725
+ static rd_kafka_mock_broker_t *
1726
+ rd_kafka_mock_broker_new(rd_kafka_mock_cluster_t *mcluster,
1727
+ int32_t broker_id,
1728
+ rd_kafka_resp_err_t *err) {
1729
+ rd_kafka_mock_broker_t *mrkb;
1730
+ rd_socket_t listen_s;
1731
+ struct sockaddr_in sin = {
1732
+ .sin_family = AF_INET,
1733
+ .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)}};
1734
+
1735
+ if (rd_kafka_mock_broker_find(mcluster, broker_id)) {
1736
+ if (err)
1737
+ *err = RD_KAFKA_RESP_ERR__INVALID_ARG;
1738
+ /* A broker with this id already exists. */
1739
+ return NULL;
1740
+ }
1741
+
1742
+ listen_s = rd_kafka_mock_broker_new_listener(mcluster, &sin);
1743
+ if (listen_s == -1) {
1744
+ if (err)
1745
+ *err = RD_KAFKA_RESP_ERR__TRANSPORT;
1746
+ return NULL;
1747
+ }
1748
+
1749
+ /*
1750
+ * Create mock broker object
1751
+ */
1752
+ mrkb = rd_calloc(1, sizeof(*mrkb));
1753
+
1754
+ mrkb->id = broker_id;
1755
+ mrkb->cluster = mcluster;
1756
+ mrkb->up = rd_true;
1757
+ mrkb->listen_s = listen_s;
1758
+ mrkb->sin = sin;
1759
+ mrkb->port = ntohs(sin.sin_port);
1760
+ rd_snprintf(mrkb->advertised_listener,
1761
+ sizeof(mrkb->advertised_listener), "%s",
1762
+ rd_sockaddr2str(&sin, 0));
1763
+
1764
+ TAILQ_INIT(&mrkb->connections);
1765
+ TAILQ_INIT(&mrkb->errstacks);
1766
+
1767
+ TAILQ_INSERT_TAIL(&mcluster->brokers, mrkb, link);
1768
+ mcluster->broker_cnt++;
1769
+
1770
+ if (rd_kafka_mock_broker_start_listener(mrkb) == -1) {
1771
+ rd_kafka_mock_broker_destroy(mrkb);
1772
+ if (err)
1773
+ *err = RD_KAFKA_RESP_ERR__TRANSPORT;
1774
+ return NULL;
1775
+ }
1776
+
1777
+ return mrkb;
1778
+ }
1779
+
1780
+
1781
+ /**
1782
+ * @returns the coordtype_t for a coord type string, or -1 on error.
1783
+ */
1784
+ static rd_kafka_coordtype_t rd_kafka_mock_coord_str2type(const char *str) {
1785
+ if (!strcmp(str, "transaction"))
1786
+ return RD_KAFKA_COORD_TXN;
1787
+ else if (!strcmp(str, "group"))
1788
+ return RD_KAFKA_COORD_GROUP;
1789
+ else
1790
+ return (rd_kafka_coordtype_t)-1;
1791
+ }
1792
+
1793
+
1794
+ /**
1795
+ * @brief Unlink and destroy coordinator.
1796
+ */
1797
+ static void rd_kafka_mock_coord_destroy(rd_kafka_mock_cluster_t *mcluster,
1798
+ rd_kafka_mock_coord_t *mcoord) {
1799
+ TAILQ_REMOVE(&mcluster->coords, mcoord, link);
1800
+ rd_free(mcoord->key);
1801
+ rd_free(mcoord);
1802
+ }
1803
+
1804
+ /**
1805
+ * @brief Find coordinator by type and key.
1806
+ */
1807
+ static rd_kafka_mock_coord_t *
1808
+ rd_kafka_mock_coord_find(rd_kafka_mock_cluster_t *mcluster,
1809
+ rd_kafka_coordtype_t type,
1810
+ const char *key) {
1811
+ rd_kafka_mock_coord_t *mcoord;
1812
+
1813
+ TAILQ_FOREACH(mcoord, &mcluster->coords, link) {
1814
+ if (mcoord->type == type && !strcmp(mcoord->key, key))
1815
+ return mcoord;
1816
+ }
1817
+
1818
+ return NULL;
1819
+ }
1820
+
1821
+
1822
+ /**
1823
+ * @returns the coordinator for KeyType,Key (e.g., GROUP,mygroup).
1824
+ */
1825
+ rd_kafka_mock_broker_t *
1826
+ rd_kafka_mock_cluster_get_coord(rd_kafka_mock_cluster_t *mcluster,
1827
+ rd_kafka_coordtype_t KeyType,
1828
+ const rd_kafkap_str_t *Key) {
1829
+ rd_kafka_mock_broker_t *mrkb;
1830
+ rd_kafka_mock_coord_t *mcoord;
1831
+ char *key;
1832
+ rd_crc32_t hash;
1833
+ int idx;
1834
+
1835
+ /* Try the explicit coord list first */
1836
+ RD_KAFKAP_STR_DUPA(&key, Key);
1837
+ if ((mcoord = rd_kafka_mock_coord_find(mcluster, KeyType, key)))
1838
+ return rd_kafka_mock_broker_find(mcluster, mcoord->broker_id);
1839
+
1840
+ /* Else hash the key to select an available broker. */
1841
+ hash = rd_crc32(Key->str, RD_KAFKAP_STR_LEN(Key));
1842
+ idx = (int)(hash % mcluster->broker_cnt);
1843
+
1844
+ /* Use the broker index in the list */
1845
+ TAILQ_FOREACH(mrkb, &mcluster->brokers, link)
1846
+ if (idx-- == 0)
1847
+ return mrkb;
1848
+
1849
+ RD_NOTREACHED();
1850
+ return NULL;
1851
+ }
1852
+
1853
+
1854
+ /**
1855
+ * @brief Explicitly set coordinator for \p key_type ("transaction", "group")
1856
+ * and \p key.
1857
+ */
1858
+ static rd_kafka_mock_coord_t *
1859
+ rd_kafka_mock_coord_set(rd_kafka_mock_cluster_t *mcluster,
1860
+ const char *key_type,
1861
+ const char *key,
1862
+ int32_t broker_id) {
1863
+ rd_kafka_mock_coord_t *mcoord;
1864
+ rd_kafka_coordtype_t type;
1865
+
1866
+ if ((int)(type = rd_kafka_mock_coord_str2type(key_type)) == -1)
1867
+ return NULL;
1868
+
1869
+ if ((mcoord = rd_kafka_mock_coord_find(mcluster, type, key)))
1870
+ rd_kafka_mock_coord_destroy(mcluster, mcoord);
1871
+
1872
+ mcoord = rd_calloc(1, sizeof(*mcoord));
1873
+ mcoord->type = type;
1874
+ mcoord->key = rd_strdup(key);
1875
+ mcoord->broker_id = broker_id;
1876
+
1877
+ TAILQ_INSERT_TAIL(&mcluster->coords, mcoord, link);
1878
+
1879
+ return mcoord;
1880
+ }
1881
+
1882
+ /**
1883
+ * @brief Remove coordinator by broker id.
1884
+ */
1885
+ void rd_kafka_mock_coord_remove(rd_kafka_mock_cluster_t *mcluster,
1886
+ int32_t broker_id) {
1887
+ rd_kafka_mock_coord_t *mcoord, *tmp;
1888
+
1889
+ TAILQ_FOREACH_SAFE(mcoord, &mcluster->coords, link, tmp) {
1890
+ if (mcoord->broker_id == broker_id) {
1891
+ rd_kafka_mock_coord_destroy(mcluster, mcoord);
1892
+ }
1893
+ }
1894
+ }
1895
+
1896
+
1897
+ /**
1898
+ * @brief Remove and return the next error, or RD_KAFKA_RESP_ERR_NO_ERROR
1899
+ * if no error.
1900
+ */
1901
+ static rd_kafka_mock_error_rtt_t
1902
+ rd_kafka_mock_error_stack_next(rd_kafka_mock_error_stack_t *errstack) {
1903
+ rd_kafka_mock_error_rtt_t err_rtt = {RD_KAFKA_RESP_ERR_NO_ERROR, 0};
1904
+
1905
+ if (likely(errstack->cnt == 0))
1906
+ return err_rtt;
1907
+
1908
+ err_rtt = errstack->errs[0];
1909
+ errstack->cnt--;
1910
+ if (errstack->cnt > 0)
1911
+ memmove(errstack->errs, &errstack->errs[1],
1912
+ sizeof(*errstack->errs) * errstack->cnt);
1913
+
1914
+ return err_rtt;
1915
+ }
1916
+
1917
+
1918
+ /**
1919
+ * @brief Find an error stack based on \p ApiKey
1920
+ */
1921
+ static rd_kafka_mock_error_stack_t *
1922
+ rd_kafka_mock_error_stack_find(const rd_kafka_mock_error_stack_head_t *shead,
1923
+ int16_t ApiKey) {
1924
+ const rd_kafka_mock_error_stack_t *errstack;
1925
+
1926
+ TAILQ_FOREACH(errstack, shead, link)
1927
+ if (errstack->ApiKey == ApiKey)
1928
+ return (rd_kafka_mock_error_stack_t *)errstack;
1929
+
1930
+ return NULL;
1931
+ }
1932
+
1933
+
1934
+
1935
+ /**
1936
+ * @brief Find or create an error stack based on \p ApiKey
1937
+ */
1938
+ static rd_kafka_mock_error_stack_t *
1939
+ rd_kafka_mock_error_stack_get(rd_kafka_mock_error_stack_head_t *shead,
1940
+ int16_t ApiKey) {
1941
+ rd_kafka_mock_error_stack_t *errstack;
1942
+
1943
+ if ((errstack = rd_kafka_mock_error_stack_find(shead, ApiKey)))
1944
+ return errstack;
1945
+
1946
+ errstack = rd_calloc(1, sizeof(*errstack));
1947
+
1948
+ errstack->ApiKey = ApiKey;
1949
+ TAILQ_INSERT_TAIL(shead, errstack, link);
1950
+
1951
+ return errstack;
1952
+ }
1953
+
1954
+
1955
+
1956
+ /**
1957
+ * @brief Removes and returns the next request error for response's ApiKey.
1958
+ *
1959
+ * If the error stack has a corresponding rtt/delay it is set on the
1960
+ * provided response \p resp buffer.
1961
+ */
1962
+ rd_kafka_resp_err_t
1963
+ rd_kafka_mock_next_request_error(rd_kafka_mock_connection_t *mconn,
1964
+ rd_kafka_buf_t *resp) {
1965
+ rd_kafka_mock_cluster_t *mcluster = mconn->broker->cluster;
1966
+ rd_kafka_mock_error_stack_t *errstack;
1967
+ rd_kafka_mock_error_rtt_t err_rtt;
1968
+
1969
+ mtx_lock(&mcluster->lock);
1970
+
1971
+ errstack = rd_kafka_mock_error_stack_find(&mconn->broker->errstacks,
1972
+ resp->rkbuf_reqhdr.ApiKey);
1973
+ if (likely(!errstack)) {
1974
+ errstack = rd_kafka_mock_error_stack_find(
1975
+ &mcluster->errstacks, resp->rkbuf_reqhdr.ApiKey);
1976
+ if (likely(!errstack)) {
1977
+ mtx_unlock(&mcluster->lock);
1978
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
1979
+ }
1980
+ }
1981
+
1982
+ err_rtt = rd_kafka_mock_error_stack_next(errstack);
1983
+ resp->rkbuf_ts_sent = err_rtt.rtt;
1984
+
1985
+ mtx_unlock(&mcluster->lock);
1986
+
1987
+ /* If the error is ERR__TRANSPORT (a librdkafka-specific error code
1988
+ * that will never be returned by a broker), we close the connection.
1989
+ * This allows closing the connection as soon as a certain
1990
+ * request is seen.
1991
+ * The handler code in rdkafka_mock_handlers.c does not need to
1992
+ * handle this case specifically and will generate a response and
1993
+ * enqueue it, but the connection will be down by the time it will
1994
+ * be sent.
1995
+ * Note: Delayed disconnects (rtt-based) are not supported. */
1996
+ if (err_rtt.err == RD_KAFKA_RESP_ERR__TRANSPORT) {
1997
+ rd_kafka_dbg(
1998
+ mcluster->rk, MOCK, "MOCK",
1999
+ "Broker %" PRId32
2000
+ ": Forcing close of connection "
2001
+ "from %s",
2002
+ mconn->broker->id,
2003
+ rd_sockaddr2str(&mconn->peer, RD_SOCKADDR2STR_F_PORT));
2004
+ rd_kafka_transport_shutdown(mconn->transport);
2005
+ }
2006
+
2007
+
2008
+ return err_rtt.err;
2009
+ }
2010
+
2011
+
2012
+ void rd_kafka_mock_clear_request_errors(rd_kafka_mock_cluster_t *mcluster,
2013
+ int16_t ApiKey) {
2014
+ rd_kafka_mock_error_stack_t *errstack;
2015
+
2016
+ mtx_lock(&mcluster->lock);
2017
+
2018
+ errstack = rd_kafka_mock_error_stack_find(&mcluster->errstacks, ApiKey);
2019
+ if (errstack)
2020
+ errstack->cnt = 0;
2021
+
2022
+ mtx_unlock(&mcluster->lock);
2023
+ }
2024
+
2025
+
2026
+ void rd_kafka_mock_push_request_errors_array(
2027
+ rd_kafka_mock_cluster_t *mcluster,
2028
+ int16_t ApiKey,
2029
+ size_t cnt,
2030
+ const rd_kafka_resp_err_t *errors) {
2031
+ rd_kafka_mock_error_stack_t *errstack;
2032
+ size_t totcnt;
2033
+ size_t i;
2034
+
2035
+ mtx_lock(&mcluster->lock);
2036
+
2037
+ errstack = rd_kafka_mock_error_stack_get(&mcluster->errstacks, ApiKey);
2038
+
2039
+ totcnt = errstack->cnt + cnt;
2040
+
2041
+ if (totcnt > errstack->size) {
2042
+ errstack->size = totcnt + 4;
2043
+ errstack->errs = rd_realloc(
2044
+ errstack->errs, errstack->size * sizeof(*errstack->errs));
2045
+ }
2046
+
2047
+ for (i = 0; i < cnt; i++) {
2048
+ errstack->errs[errstack->cnt].err = errors[i];
2049
+ errstack->errs[errstack->cnt++].rtt = 0;
2050
+ }
2051
+
2052
+ mtx_unlock(&mcluster->lock);
2053
+ }
2054
+
2055
+ void rd_kafka_mock_push_request_errors(rd_kafka_mock_cluster_t *mcluster,
2056
+ int16_t ApiKey,
2057
+ size_t cnt,
2058
+ ...) {
2059
+ va_list ap;
2060
+ rd_kafka_resp_err_t *errors = rd_alloca(sizeof(*errors) * cnt);
2061
+ size_t i;
2062
+
2063
+ va_start(ap, cnt);
2064
+ for (i = 0; i < cnt; i++)
2065
+ errors[i] = va_arg(ap, rd_kafka_resp_err_t);
2066
+ va_end(ap);
2067
+
2068
+ rd_kafka_mock_push_request_errors_array(mcluster, ApiKey, cnt, errors);
2069
+ }
2070
+
2071
+
2072
+ rd_kafka_resp_err_t
2073
+ rd_kafka_mock_broker_push_request_error_rtts(rd_kafka_mock_cluster_t *mcluster,
2074
+ int32_t broker_id,
2075
+ int16_t ApiKey,
2076
+ size_t cnt,
2077
+ ...) {
2078
+ rd_kafka_mock_broker_t *mrkb;
2079
+ va_list ap;
2080
+ rd_kafka_mock_error_stack_t *errstack;
2081
+ size_t totcnt;
2082
+
2083
+ mtx_lock(&mcluster->lock);
2084
+
2085
+ if (!(mrkb = rd_kafka_mock_broker_find(mcluster, broker_id))) {
2086
+ mtx_unlock(&mcluster->lock);
2087
+ return RD_KAFKA_RESP_ERR__UNKNOWN_BROKER;
2088
+ }
2089
+
2090
+ errstack = rd_kafka_mock_error_stack_get(&mrkb->errstacks, ApiKey);
2091
+
2092
+ totcnt = errstack->cnt + cnt;
2093
+
2094
+ if (totcnt > errstack->size) {
2095
+ errstack->size = totcnt + 4;
2096
+ errstack->errs = rd_realloc(
2097
+ errstack->errs, errstack->size * sizeof(*errstack->errs));
2098
+ }
2099
+
2100
+ va_start(ap, cnt);
2101
+ while (cnt-- > 0) {
2102
+ errstack->errs[errstack->cnt].err =
2103
+ va_arg(ap, rd_kafka_resp_err_t);
2104
+ errstack->errs[errstack->cnt++].rtt =
2105
+ ((rd_ts_t)va_arg(ap, int)) * 1000;
2106
+ }
2107
+ va_end(ap);
2108
+
2109
+ mtx_unlock(&mcluster->lock);
2110
+
2111
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2112
+ }
2113
+
2114
+
2115
+ rd_kafka_resp_err_t
2116
+ rd_kafka_mock_broker_error_stack_cnt(rd_kafka_mock_cluster_t *mcluster,
2117
+ int32_t broker_id,
2118
+ int16_t ApiKey,
2119
+ size_t *cntp) {
2120
+ rd_kafka_mock_broker_t *mrkb;
2121
+ rd_kafka_mock_error_stack_t *errstack;
2122
+
2123
+ if (!mcluster || !cntp)
2124
+ return RD_KAFKA_RESP_ERR__INVALID_ARG;
2125
+
2126
+ mtx_lock(&mcluster->lock);
2127
+
2128
+ if (!(mrkb = rd_kafka_mock_broker_find(mcluster, broker_id))) {
2129
+ mtx_unlock(&mcluster->lock);
2130
+ return RD_KAFKA_RESP_ERR__UNKNOWN_BROKER;
2131
+ }
2132
+
2133
+ if ((errstack =
2134
+ rd_kafka_mock_error_stack_find(&mrkb->errstacks, ApiKey)))
2135
+ *cntp = errstack->cnt;
2136
+
2137
+ mtx_unlock(&mcluster->lock);
2138
+
2139
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2140
+ }
2141
+
2142
+
2143
+ void rd_kafka_mock_topic_set_error(rd_kafka_mock_cluster_t *mcluster,
2144
+ const char *topic,
2145
+ rd_kafka_resp_err_t err) {
2146
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2147
+
2148
+ rko->rko_u.mock.name = rd_strdup(topic);
2149
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_TOPIC_SET_ERROR;
2150
+ rko->rko_u.mock.err = err;
2151
+
2152
+ rko = rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE);
2153
+ if (rko)
2154
+ rd_kafka_op_destroy(rko);
2155
+ }
2156
+
2157
+
2158
+ rd_kafka_resp_err_t
2159
+ rd_kafka_mock_topic_create(rd_kafka_mock_cluster_t *mcluster,
2160
+ const char *topic,
2161
+ int partition_cnt,
2162
+ int replication_factor) {
2163
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2164
+
2165
+ rko->rko_u.mock.name = rd_strdup(topic);
2166
+ rko->rko_u.mock.lo = partition_cnt;
2167
+ rko->rko_u.mock.hi = replication_factor;
2168
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_TOPIC_CREATE;
2169
+
2170
+ return rd_kafka_op_err_destroy(
2171
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2172
+ }
2173
+
2174
+ rd_kafka_resp_err_t
2175
+ rd_kafka_mock_partition_set_leader(rd_kafka_mock_cluster_t *mcluster,
2176
+ const char *topic,
2177
+ int32_t partition,
2178
+ int32_t broker_id) {
2179
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2180
+
2181
+ rko->rko_u.mock.name = rd_strdup(topic);
2182
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_PART_SET_LEADER;
2183
+ rko->rko_u.mock.partition = partition;
2184
+ rko->rko_u.mock.broker_id = broker_id;
2185
+
2186
+ return rd_kafka_op_err_destroy(
2187
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2188
+ }
2189
+
2190
+ rd_kafka_resp_err_t
2191
+ rd_kafka_mock_partition_set_follower(rd_kafka_mock_cluster_t *mcluster,
2192
+ const char *topic,
2193
+ int32_t partition,
2194
+ int32_t broker_id) {
2195
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2196
+
2197
+ rko->rko_u.mock.name = rd_strdup(topic);
2198
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_PART_SET_FOLLOWER;
2199
+ rko->rko_u.mock.partition = partition;
2200
+ rko->rko_u.mock.broker_id = broker_id;
2201
+
2202
+ return rd_kafka_op_err_destroy(
2203
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2204
+ }
2205
+
2206
+ rd_kafka_resp_err_t
2207
+ rd_kafka_mock_partition_set_follower_wmarks(rd_kafka_mock_cluster_t *mcluster,
2208
+ const char *topic,
2209
+ int32_t partition,
2210
+ int64_t lo,
2211
+ int64_t hi) {
2212
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2213
+
2214
+ rko->rko_u.mock.name = rd_strdup(topic);
2215
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_PART_SET_FOLLOWER_WMARKS;
2216
+ rko->rko_u.mock.partition = partition;
2217
+ rko->rko_u.mock.lo = lo;
2218
+ rko->rko_u.mock.hi = hi;
2219
+
2220
+ return rd_kafka_op_err_destroy(
2221
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2222
+ }
2223
+
2224
+ rd_kafka_resp_err_t
2225
+ rd_kafka_mock_partition_push_leader_response(rd_kafka_mock_cluster_t *mcluster,
2226
+ const char *topic,
2227
+ int partition,
2228
+ int32_t leader_id,
2229
+ int32_t leader_epoch) {
2230
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2231
+ rko->rko_u.mock.name = rd_strdup(topic);
2232
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_PART_PUSH_LEADER_RESPONSE;
2233
+ rko->rko_u.mock.partition = partition;
2234
+ rko->rko_u.mock.leader_id = leader_id;
2235
+ rko->rko_u.mock.leader_epoch = leader_epoch;
2236
+
2237
+ return rd_kafka_op_err_destroy(
2238
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2239
+ }
2240
+
2241
+ rd_kafka_resp_err_t
2242
+ rd_kafka_mock_broker_set_down(rd_kafka_mock_cluster_t *mcluster,
2243
+ int32_t broker_id) {
2244
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2245
+
2246
+ rko->rko_u.mock.broker_id = broker_id;
2247
+ rko->rko_u.mock.lo = rd_false;
2248
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_BROKER_SET_UPDOWN;
2249
+
2250
+ return rd_kafka_op_err_destroy(
2251
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2252
+ }
2253
+
2254
+ rd_kafka_resp_err_t
2255
+ rd_kafka_mock_broker_set_up(rd_kafka_mock_cluster_t *mcluster,
2256
+ int32_t broker_id) {
2257
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2258
+
2259
+ rko->rko_u.mock.broker_id = broker_id;
2260
+ rko->rko_u.mock.lo = rd_true;
2261
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_BROKER_SET_UPDOWN;
2262
+
2263
+ return rd_kafka_op_err_destroy(
2264
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2265
+ }
2266
+
2267
+ rd_kafka_resp_err_t
2268
+ rd_kafka_mock_broker_set_rtt(rd_kafka_mock_cluster_t *mcluster,
2269
+ int32_t broker_id,
2270
+ int rtt_ms) {
2271
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2272
+
2273
+ rko->rko_u.mock.broker_id = broker_id;
2274
+ rko->rko_u.mock.lo = rtt_ms;
2275
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_BROKER_SET_RTT;
2276
+
2277
+ return rd_kafka_op_err_destroy(
2278
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2279
+ }
2280
+
2281
+ rd_kafka_resp_err_t
2282
+ rd_kafka_mock_broker_set_rack(rd_kafka_mock_cluster_t *mcluster,
2283
+ int32_t broker_id,
2284
+ const char *rack) {
2285
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2286
+
2287
+ rko->rko_u.mock.broker_id = broker_id;
2288
+ rko->rko_u.mock.name = rd_strdup(rack);
2289
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_BROKER_SET_RACK;
2290
+
2291
+ return rd_kafka_op_err_destroy(
2292
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2293
+ }
2294
+
2295
+ void rd_kafka_mock_broker_set_host_port(rd_kafka_mock_cluster_t *cluster,
2296
+ int32_t broker_id,
2297
+ const char *host,
2298
+ int port) {
2299
+ rd_kafka_mock_broker_t *mrkb;
2300
+
2301
+ mtx_lock(&cluster->lock);
2302
+ TAILQ_FOREACH(mrkb, &cluster->brokers, link) {
2303
+ if (mrkb->id == broker_id) {
2304
+ rd_kafka_dbg(
2305
+ cluster->rk, MOCK, "MOCK",
2306
+ "Broker %" PRId32
2307
+ ": Setting advertised listener from %s:%d to %s:%d",
2308
+ broker_id, mrkb->advertised_listener, mrkb->port,
2309
+ host, port);
2310
+ rd_snprintf(mrkb->advertised_listener,
2311
+ sizeof(mrkb->advertised_listener), "%s",
2312
+ host);
2313
+ mrkb->port = port;
2314
+ break;
2315
+ }
2316
+ }
2317
+ mtx_unlock(&cluster->lock);
2318
+ }
2319
+
2320
+ rd_kafka_resp_err_t
2321
+ rd_kafka_mock_coordinator_set(rd_kafka_mock_cluster_t *mcluster,
2322
+ const char *key_type,
2323
+ const char *key,
2324
+ int32_t broker_id) {
2325
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2326
+
2327
+ rko->rko_u.mock.name = rd_strdup(key_type);
2328
+ rko->rko_u.mock.str = rd_strdup(key);
2329
+ rko->rko_u.mock.broker_id = broker_id;
2330
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_COORD_SET;
2331
+
2332
+ return rd_kafka_op_err_destroy(
2333
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2334
+ }
2335
+
2336
+ rd_kafka_resp_err_t
2337
+ rd_kafka_mock_set_apiversion(rd_kafka_mock_cluster_t *mcluster,
2338
+ int16_t ApiKey,
2339
+ int16_t MinVersion,
2340
+ int16_t MaxVersion) {
2341
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2342
+
2343
+ rko->rko_u.mock.partition = ApiKey;
2344
+ rko->rko_u.mock.lo = MinVersion;
2345
+ rko->rko_u.mock.hi = MaxVersion;
2346
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_APIVERSION_SET;
2347
+
2348
+ return rd_kafka_op_err_destroy(
2349
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2350
+ }
2351
+
2352
+ rd_kafka_resp_err_t
2353
+ rd_kafka_mock_telemetry_set_requested_metrics(rd_kafka_mock_cluster_t *mcluster,
2354
+ char **metrics,
2355
+ size_t metrics_cnt) {
2356
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2357
+
2358
+ rko->rko_u.mock.hi = metrics_cnt;
2359
+ rko->rko_u.mock.metrics = NULL;
2360
+ if (metrics_cnt) {
2361
+ size_t i;
2362
+ rko->rko_u.mock.metrics =
2363
+ rd_calloc(metrics_cnt, sizeof(char *));
2364
+ for (i = 0; i < metrics_cnt; i++)
2365
+ rko->rko_u.mock.metrics[i] = rd_strdup(metrics[i]);
2366
+ }
2367
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_REQUESTED_METRICS_SET;
2368
+
2369
+ return rd_kafka_op_err_destroy(
2370
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2371
+ }
2372
+
2373
+ rd_kafka_resp_err_t
2374
+ rd_kafka_mock_telemetry_set_push_interval(rd_kafka_mock_cluster_t *mcluster,
2375
+ int64_t push_interval_ms) {
2376
+ rd_kafka_op_t *rko = rd_kafka_op_new(RD_KAFKA_OP_MOCK);
2377
+
2378
+ rko->rko_u.mock.hi = push_interval_ms;
2379
+ rko->rko_u.mock.cmd = RD_KAFKA_MOCK_CMD_TELEMETRY_PUSH_INTERVAL_SET;
2380
+
2381
+ return rd_kafka_op_err_destroy(
2382
+ rd_kafka_op_req(mcluster->ops, rko, RD_POLL_INFINITE));
2383
+ }
2384
+
2385
+
2386
+ /**
2387
+ * @brief Apply command to specific broker.
2388
+ *
2389
+ * @locality mcluster thread
2390
+ */
2391
+ static rd_kafka_resp_err_t
2392
+ rd_kafka_mock_broker_cmd(rd_kafka_mock_cluster_t *mcluster,
2393
+ rd_kafka_mock_broker_t *mrkb,
2394
+ rd_kafka_op_t *rko) {
2395
+ switch (rko->rko_u.mock.cmd) {
2396
+ case RD_KAFKA_MOCK_CMD_BROKER_SET_UPDOWN:
2397
+ if ((rd_bool_t)rko->rko_u.mock.lo == mrkb->up)
2398
+ break;
2399
+
2400
+ mrkb->up = (rd_bool_t)rko->rko_u.mock.lo;
2401
+
2402
+ if (!mrkb->up) {
2403
+ rd_kafka_mock_cluster_io_del(mcluster, mrkb->listen_s);
2404
+ rd_socket_close(mrkb->listen_s);
2405
+ /* Re-create the listener right away so we retain the
2406
+ * same port. The listener is not started until
2407
+ * the broker is set up (below). */
2408
+ mrkb->listen_s = rd_kafka_mock_broker_new_listener(
2409
+ mcluster, &mrkb->sin);
2410
+ rd_assert(mrkb->listen_s != -1 ||
2411
+ !*"Failed to-create mock broker listener");
2412
+
2413
+ rd_kafka_mock_broker_close_all(mrkb, "Broker down");
2414
+
2415
+ } else {
2416
+ int r;
2417
+ rd_assert(mrkb->listen_s != -1);
2418
+ r = rd_kafka_mock_broker_start_listener(mrkb);
2419
+ rd_assert(r == 0 || !*"broker_start_listener() failed");
2420
+ }
2421
+ break;
2422
+
2423
+ case RD_KAFKA_MOCK_CMD_BROKER_SET_RTT:
2424
+ mrkb->rtt = (rd_ts_t)rko->rko_u.mock.lo * 1000;
2425
+
2426
+ /* Check if there is anything to send now that the RTT
2427
+ * has changed or if a timer is to be started. */
2428
+ rd_kafka_mock_broker_connections_write_out(mrkb);
2429
+ break;
2430
+
2431
+ case RD_KAFKA_MOCK_CMD_BROKER_SET_RACK:
2432
+ if (mrkb->rack)
2433
+ rd_free(mrkb->rack);
2434
+
2435
+ if (rko->rko_u.mock.name)
2436
+ mrkb->rack = rd_strdup(rko->rko_u.mock.name);
2437
+ else
2438
+ mrkb->rack = NULL;
2439
+ break;
2440
+
2441
+ case RD_KAFKA_MOCK_CMD_BROKER_DECOMMISSION:
2442
+ rd_kafka_mock_broker_destroy(mrkb);
2443
+ rd_kafka_mock_cluster_reassign_partitions(mcluster);
2444
+ break;
2445
+
2446
+ default:
2447
+ RD_BUG("Unhandled mock cmd %d", rko->rko_u.mock.cmd);
2448
+ break;
2449
+ }
2450
+
2451
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2452
+ }
2453
+
2454
+
2455
+ /**
2456
+ * @brief Apply command to to one or all brokers, depending on the value of
2457
+ * broker_id, where -1 means all, and != -1 means a specific broker.
2458
+ *
2459
+ * @locality mcluster thread
2460
+ */
2461
+ static rd_kafka_resp_err_t
2462
+ rd_kafka_mock_brokers_cmd(rd_kafka_mock_cluster_t *mcluster,
2463
+ rd_kafka_op_t *rko) {
2464
+ rd_kafka_mock_broker_t *mrkb;
2465
+
2466
+ if (rko->rko_u.mock.broker_id != -1) {
2467
+ /* Specific broker */
2468
+ mrkb = rd_kafka_mock_broker_find(mcluster,
2469
+ rko->rko_u.mock.broker_id);
2470
+ if (!mrkb)
2471
+ return RD_KAFKA_RESP_ERR_BROKER_NOT_AVAILABLE;
2472
+
2473
+ return rd_kafka_mock_broker_cmd(mcluster, mrkb, rko);
2474
+ }
2475
+
2476
+ /* All brokers */
2477
+ TAILQ_FOREACH(mrkb, &mcluster->brokers, link) {
2478
+ rd_kafka_resp_err_t err;
2479
+
2480
+ if ((err = rd_kafka_mock_broker_cmd(mcluster, mrkb, rko)))
2481
+ return err;
2482
+ }
2483
+
2484
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2485
+ }
2486
+
2487
+
2488
+ /**
2489
+ * @brief Handle command op
2490
+ *
2491
+ * @locality mcluster thread
2492
+ */
2493
+ static rd_kafka_resp_err_t
2494
+ rd_kafka_mock_cluster_cmd(rd_kafka_mock_cluster_t *mcluster,
2495
+ rd_kafka_op_t *rko) {
2496
+ rd_kafka_mock_topic_t *mtopic;
2497
+ rd_kafka_mock_partition_t *mpart;
2498
+ rd_kafka_mock_broker_t *mrkb;
2499
+ size_t i;
2500
+ rd_kafka_resp_err_t err;
2501
+
2502
+ switch (rko->rko_u.mock.cmd) {
2503
+ case RD_KAFKA_MOCK_CMD_TOPIC_CREATE:
2504
+ if (rd_kafka_mock_topic_find(mcluster, rko->rko_u.mock.name))
2505
+ return RD_KAFKA_RESP_ERR_TOPIC_ALREADY_EXISTS;
2506
+
2507
+ if (!rd_kafka_mock_topic_new(mcluster, rko->rko_u.mock.name,
2508
+ /* partition_cnt */
2509
+ (int)rko->rko_u.mock.lo,
2510
+ /* replication_factor */
2511
+ (int)rko->rko_u.mock.hi))
2512
+ return RD_KAFKA_RESP_ERR_TOPIC_EXCEPTION;
2513
+ break;
2514
+
2515
+ case RD_KAFKA_MOCK_CMD_TOPIC_SET_ERROR:
2516
+ mtopic =
2517
+ rd_kafka_mock_topic_get(mcluster, rko->rko_u.mock.name, -1);
2518
+ mtopic->err = rko->rko_u.mock.err;
2519
+ break;
2520
+
2521
+ case RD_KAFKA_MOCK_CMD_PART_SET_LEADER:
2522
+ mpart = rd_kafka_mock_partition_get(
2523
+ mcluster, rko->rko_u.mock.name, rko->rko_u.mock.partition);
2524
+ if (!mpart)
2525
+ return RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART;
2526
+
2527
+ if (rko->rko_u.mock.broker_id != -1) {
2528
+ mrkb = rd_kafka_mock_broker_find(
2529
+ mcluster, rko->rko_u.mock.broker_id);
2530
+ if (!mrkb)
2531
+ return RD_KAFKA_RESP_ERR_BROKER_NOT_AVAILABLE;
2532
+ } else {
2533
+ mrkb = NULL;
2534
+ }
2535
+
2536
+ rd_kafka_dbg(mcluster->rk, MOCK, "MOCK",
2537
+ "Set %s [%" PRId32 "] leader to %" PRId32,
2538
+ rko->rko_u.mock.name, rko->rko_u.mock.partition,
2539
+ rko->rko_u.mock.broker_id);
2540
+
2541
+ rd_kafka_mock_partition_set_leader0(mpart, mrkb);
2542
+ break;
2543
+
2544
+ case RD_KAFKA_MOCK_CMD_PART_SET_FOLLOWER:
2545
+ mpart = rd_kafka_mock_partition_get(
2546
+ mcluster, rko->rko_u.mock.name, rko->rko_u.mock.partition);
2547
+ if (!mpart)
2548
+ return RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART;
2549
+
2550
+ rd_kafka_dbg(mcluster->rk, MOCK, "MOCK",
2551
+ "Set %s [%" PRId32
2552
+ "] preferred follower "
2553
+ "to %" PRId32,
2554
+ rko->rko_u.mock.name, rko->rko_u.mock.partition,
2555
+ rko->rko_u.mock.broker_id);
2556
+
2557
+ mpart->follower_id = rko->rko_u.mock.broker_id;
2558
+ break;
2559
+
2560
+ case RD_KAFKA_MOCK_CMD_PART_SET_FOLLOWER_WMARKS:
2561
+ mpart = rd_kafka_mock_partition_get(
2562
+ mcluster, rko->rko_u.mock.name, rko->rko_u.mock.partition);
2563
+ if (!mpart)
2564
+ return RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART;
2565
+
2566
+ rd_kafka_dbg(mcluster->rk, MOCK, "MOCK",
2567
+ "Set %s [%" PRId32
2568
+ "] follower "
2569
+ "watermark offsets to %" PRId64 "..%" PRId64,
2570
+ rko->rko_u.mock.name, rko->rko_u.mock.partition,
2571
+ rko->rko_u.mock.lo, rko->rko_u.mock.hi);
2572
+
2573
+ if (rko->rko_u.mock.lo == -1) {
2574
+ mpart->follower_start_offset = mpart->start_offset;
2575
+ mpart->update_follower_start_offset = rd_true;
2576
+ } else {
2577
+ mpart->follower_start_offset = rko->rko_u.mock.lo;
2578
+ mpart->update_follower_start_offset = rd_false;
2579
+ }
2580
+
2581
+ if (rko->rko_u.mock.hi == -1) {
2582
+ mpart->follower_end_offset = mpart->end_offset;
2583
+ mpart->update_follower_end_offset = rd_true;
2584
+ } else {
2585
+ mpart->follower_end_offset = rko->rko_u.mock.hi;
2586
+ mpart->update_follower_end_offset = rd_false;
2587
+ }
2588
+ break;
2589
+ case RD_KAFKA_MOCK_CMD_PART_PUSH_LEADER_RESPONSE:
2590
+ mpart = rd_kafka_mock_partition_get(
2591
+ mcluster, rko->rko_u.mock.name, rko->rko_u.mock.partition);
2592
+ if (!mpart)
2593
+ return RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART;
2594
+
2595
+ rd_kafka_dbg(mcluster->rk, MOCK, "MOCK",
2596
+ "Push %s [%" PRId32 "] leader response: (%" PRId32
2597
+ ", %" PRId32 ")",
2598
+ rko->rko_u.mock.name, rko->rko_u.mock.partition,
2599
+ rko->rko_u.mock.leader_id,
2600
+ rko->rko_u.mock.leader_epoch);
2601
+
2602
+ rd_kafka_mock_partition_push_leader_response0(
2603
+ mpart, rko->rko_u.mock.leader_id,
2604
+ rko->rko_u.mock.leader_epoch);
2605
+ break;
2606
+
2607
+ /* Broker commands */
2608
+ case RD_KAFKA_MOCK_CMD_BROKER_SET_UPDOWN:
2609
+ case RD_KAFKA_MOCK_CMD_BROKER_SET_RTT:
2610
+ case RD_KAFKA_MOCK_CMD_BROKER_SET_RACK:
2611
+ case RD_KAFKA_MOCK_CMD_BROKER_DECOMMISSION:
2612
+ return rd_kafka_mock_brokers_cmd(mcluster, rko);
2613
+
2614
+ case RD_KAFKA_MOCK_CMD_BROKER_ADD:
2615
+ if (!rd_kafka_mock_broker_new(mcluster,
2616
+ rko->rko_u.mock.broker_id, &err))
2617
+ return err;
2618
+
2619
+ rd_kafka_mock_cluster_reassign_partitions(mcluster);
2620
+ break;
2621
+ case RD_KAFKA_MOCK_CMD_COORD_SET:
2622
+ if (!rd_kafka_mock_coord_set(mcluster, rko->rko_u.mock.name,
2623
+ rko->rko_u.mock.str,
2624
+ rko->rko_u.mock.broker_id))
2625
+ return RD_KAFKA_RESP_ERR__INVALID_ARG;
2626
+ break;
2627
+
2628
+ case RD_KAFKA_MOCK_CMD_APIVERSION_SET:
2629
+ if (rko->rko_u.mock.partition < 0 ||
2630
+ rko->rko_u.mock.partition >= RD_KAFKAP__NUM)
2631
+ return RD_KAFKA_RESP_ERR__INVALID_ARG;
2632
+
2633
+ mcluster->api_handlers[(int)rko->rko_u.mock.partition]
2634
+ .MinVersion = (int16_t)rko->rko_u.mock.lo;
2635
+ mcluster->api_handlers[(int)rko->rko_u.mock.partition]
2636
+ .MaxVersion = (int16_t)rko->rko_u.mock.hi;
2637
+ break;
2638
+
2639
+ case RD_KAFKA_MOCK_CMD_REQUESTED_METRICS_SET:
2640
+ mcluster->metrics_cnt = rko->rko_u.mock.hi;
2641
+ if (!mcluster->metrics_cnt)
2642
+ break;
2643
+
2644
+ mcluster->metrics =
2645
+ rd_calloc(mcluster->metrics_cnt, sizeof(char *));
2646
+ for (i = 0; i < mcluster->metrics_cnt; i++)
2647
+ mcluster->metrics[i] =
2648
+ rd_strdup(rko->rko_u.mock.metrics[i]);
2649
+ break;
2650
+
2651
+ case RD_KAFKA_MOCK_CMD_TELEMETRY_PUSH_INTERVAL_SET:
2652
+ mcluster->telemetry_push_interval_ms = rko->rko_u.mock.hi;
2653
+ break;
2654
+
2655
+ default:
2656
+ rd_assert(!*"unknown mock cmd");
2657
+ break;
2658
+ }
2659
+
2660
+ return RD_KAFKA_RESP_ERR_NO_ERROR;
2661
+ }
2662
+
2663
+ void rd_kafka_mock_group_initial_rebalance_delay_ms(
2664
+ rd_kafka_mock_cluster_t *mcluster,
2665
+ int32_t delay_ms) {
2666
+ mtx_lock(&mcluster->lock);
2667
+ mcluster->defaults.group_initial_rebalance_delay_ms = delay_ms;
2668
+ mtx_unlock(&mcluster->lock);
2669
+ }
2670
+
2671
+
2672
+ static rd_kafka_op_res_t
2673
+ rd_kafka_mock_cluster_op_serve(rd_kafka_t *rk,
2674
+ rd_kafka_q_t *rkq,
2675
+ rd_kafka_op_t *rko,
2676
+ rd_kafka_q_cb_type_t cb_type,
2677
+ void *opaque) {
2678
+ rd_kafka_mock_cluster_t *mcluster = opaque;
2679
+ rd_kafka_resp_err_t err = RD_KAFKA_RESP_ERR_NO_ERROR;
2680
+
2681
+ switch ((int)rko->rko_type) {
2682
+ case RD_KAFKA_OP_TERMINATE:
2683
+ mcluster->run = rd_false;
2684
+ break;
2685
+
2686
+ case RD_KAFKA_OP_MOCK:
2687
+ err = rd_kafka_mock_cluster_cmd(mcluster, rko);
2688
+ break;
2689
+
2690
+ default:
2691
+ rd_assert(!"*unhandled op");
2692
+ break;
2693
+ }
2694
+
2695
+ rd_kafka_op_reply(rko, err);
2696
+
2697
+ return RD_KAFKA_OP_RES_HANDLED;
2698
+ }
2699
+
2700
+
2701
+ /**
2702
+ * @brief Destroy cluster (internal)
2703
+ */
2704
+ static void rd_kafka_mock_cluster_destroy0(rd_kafka_mock_cluster_t *mcluster) {
2705
+ rd_kafka_mock_topic_t *mtopic;
2706
+ rd_kafka_mock_broker_t *mrkb;
2707
+ rd_kafka_mock_cgrp_classic_t *mcgrp_classic;
2708
+ rd_kafka_mock_cgrp_consumer_t *mcgrp_consumer;
2709
+ rd_kafka_mock_coord_t *mcoord;
2710
+ rd_kafka_mock_error_stack_t *errstack;
2711
+ thrd_t dummy_rkb_thread;
2712
+ int ret;
2713
+ size_t i;
2714
+
2715
+ while ((mtopic = TAILQ_FIRST(&mcluster->topics)))
2716
+ rd_kafka_mock_topic_destroy(mtopic);
2717
+
2718
+ while ((mrkb = TAILQ_FIRST(&mcluster->brokers)))
2719
+ rd_kafka_mock_broker_destroy(mrkb);
2720
+
2721
+ while ((mcgrp_classic = TAILQ_FIRST(&mcluster->cgrps_classic)))
2722
+ rd_kafka_mock_cgrp_classic_destroy(mcgrp_classic);
2723
+
2724
+ while ((mcgrp_consumer = TAILQ_FIRST(&mcluster->cgrps_consumer)))
2725
+ rd_kafka_mock_cgrp_consumer_destroy(mcgrp_consumer);
2726
+
2727
+ while ((mcoord = TAILQ_FIRST(&mcluster->coords)))
2728
+ rd_kafka_mock_coord_destroy(mcluster, mcoord);
2729
+
2730
+ rd_list_destroy(&mcluster->pids);
2731
+
2732
+ while ((errstack = TAILQ_FIRST(&mcluster->errstacks))) {
2733
+ TAILQ_REMOVE(&mcluster->errstacks, errstack, link);
2734
+ rd_kafka_mock_error_stack_destroy(errstack);
2735
+ }
2736
+
2737
+ rd_list_destroy(&mcluster->request_list);
2738
+
2739
+ dummy_rkb_thread = mcluster->dummy_rkb->rkb_thread;
2740
+
2741
+ /*
2742
+ * Destroy dummy broker.
2743
+ * WARNING: This is last time we can read
2744
+ * from dummy_rkb in this thread!
2745
+ */
2746
+ rd_kafka_q_enq(mcluster->dummy_rkb->rkb_ops,
2747
+ rd_kafka_op_new(RD_KAFKA_OP_TERMINATE));
2748
+
2749
+ if (thrd_join(dummy_rkb_thread, &ret) != thrd_success)
2750
+ rd_assert(!*"failed to join mock dummy broker thread");
2751
+
2752
+
2753
+ rd_kafka_q_destroy_owner(mcluster->ops);
2754
+
2755
+ rd_kafka_timers_destroy(&mcluster->timers);
2756
+
2757
+ if (mcluster->fd_size > 0) {
2758
+ rd_free(mcluster->fds);
2759
+ rd_free(mcluster->handlers);
2760
+ }
2761
+
2762
+ mtx_destroy(&mcluster->lock);
2763
+
2764
+ rd_free(mcluster->bootstraps);
2765
+
2766
+ rd_socket_close(mcluster->wakeup_fds[0]);
2767
+ rd_socket_close(mcluster->wakeup_fds[1]);
2768
+
2769
+ if (mcluster->metrics) {
2770
+ for (i = 0; i < mcluster->metrics_cnt; i++) {
2771
+ rd_free(mcluster->metrics[i]);
2772
+ }
2773
+ rd_free(mcluster->metrics);
2774
+ }
2775
+ }
2776
+
2777
+
2778
+
2779
+ void rd_kafka_mock_cluster_destroy(rd_kafka_mock_cluster_t *mcluster) {
2780
+ int res;
2781
+ rd_kafka_op_t *rko;
2782
+
2783
+ rd_kafka_dbg(mcluster->rk, MOCK, "MOCK", "Destroying cluster");
2784
+
2785
+ rd_assert(rd_atomic32_get(&mcluster->rk->rk_mock.cluster_cnt) > 0);
2786
+ rd_atomic32_sub(&mcluster->rk->rk_mock.cluster_cnt, 1);
2787
+
2788
+ rko = rd_kafka_op_req2(mcluster->ops, RD_KAFKA_OP_TERMINATE);
2789
+
2790
+ if (rko)
2791
+ rd_kafka_op_destroy(rko);
2792
+
2793
+ if (thrd_join(mcluster->thread, &res) != thrd_success)
2794
+ rd_assert(!*"failed to join mock thread");
2795
+
2796
+ rd_free(mcluster);
2797
+ }
2798
+
2799
+
2800
+
2801
+ rd_kafka_mock_cluster_t *rd_kafka_mock_cluster_new(rd_kafka_t *rk,
2802
+ int broker_cnt) {
2803
+ rd_kafka_mock_cluster_t *mcluster;
2804
+ rd_kafka_mock_broker_t *mrkb;
2805
+ int i, r;
2806
+ size_t bootstraps_len = 0;
2807
+ size_t of;
2808
+
2809
+ mcluster = rd_calloc(1, sizeof(*mcluster));
2810
+ mcluster->rk = rk;
2811
+
2812
+ mcluster->dummy_rkb =
2813
+ rd_kafka_broker_add(rk, RD_KAFKA_INTERNAL, RD_KAFKA_PROTO_PLAINTEXT,
2814
+ "mock", 0, RD_KAFKA_NODEID_UA);
2815
+ rd_snprintf(mcluster->id, sizeof(mcluster->id), "mockCluster%lx",
2816
+ (intptr_t)mcluster >> 2);
2817
+
2818
+ TAILQ_INIT(&mcluster->brokers);
2819
+
2820
+ for (i = 1; i <= broker_cnt; i++) {
2821
+ if (!(mrkb = rd_kafka_mock_broker_new(mcluster, i, NULL))) {
2822
+ rd_kafka_mock_cluster_destroy(mcluster);
2823
+ return NULL;
2824
+ }
2825
+
2826
+ /* advertised listener + ":port" + "," */
2827
+ bootstraps_len += strlen(mrkb->advertised_listener) + 6 + 1;
2828
+ }
2829
+
2830
+ mtx_init(&mcluster->lock, mtx_plain);
2831
+
2832
+ TAILQ_INIT(&mcluster->topics);
2833
+ mcluster->defaults.partition_cnt = 4;
2834
+ mcluster->defaults.replication_factor = RD_MIN(3, broker_cnt);
2835
+ mcluster->defaults.group_initial_rebalance_delay_ms = 3000;
2836
+ mcluster->track_requests = rd_false;
2837
+ mcluster->defaults.group_consumer_session_timeout_ms = 30000;
2838
+ mcluster->defaults.group_consumer_heartbeat_interval_ms = 3000;
2839
+
2840
+ TAILQ_INIT(&mcluster->cgrps_classic);
2841
+
2842
+ TAILQ_INIT(&mcluster->cgrps_consumer);
2843
+
2844
+ TAILQ_INIT(&mcluster->coords);
2845
+
2846
+ rd_list_init(&mcluster->pids, 16, rd_free);
2847
+
2848
+ TAILQ_INIT(&mcluster->errstacks);
2849
+
2850
+ memcpy(mcluster->api_handlers, rd_kafka_mock_api_handlers,
2851
+ sizeof(mcluster->api_handlers));
2852
+
2853
+ rd_list_init(&mcluster->request_list, 0, rd_kafka_mock_request_free);
2854
+
2855
+ /* Use an op queue for controlling the cluster in
2856
+ * a thread-safe manner without locking. */
2857
+ mcluster->ops = rd_kafka_q_new(rk);
2858
+ mcluster->ops->rkq_serve = rd_kafka_mock_cluster_op_serve;
2859
+ mcluster->ops->rkq_opaque = mcluster;
2860
+
2861
+ rd_kafka_timers_init(&mcluster->timers, rk, mcluster->ops);
2862
+
2863
+ if ((r = rd_pipe_nonblocking(mcluster->wakeup_fds)) == -1) {
2864
+ rd_kafka_log(rk, LOG_ERR, "MOCK",
2865
+ "Failed to setup mock cluster wake-up fds: %s",
2866
+ rd_socket_strerror(r));
2867
+ } else {
2868
+ const char onebyte = 1;
2869
+ rd_kafka_q_io_event_enable(mcluster->ops,
2870
+ mcluster->wakeup_fds[1], &onebyte,
2871
+ sizeof(onebyte));
2872
+ }
2873
+
2874
+
2875
+ if (thrd_create(&mcluster->thread, rd_kafka_mock_cluster_thread_main,
2876
+ mcluster) != thrd_success) {
2877
+ rd_kafka_log(rk, LOG_CRIT, "MOCK",
2878
+ "Failed to create mock cluster thread: %s",
2879
+ rd_strerror(errno));
2880
+ rd_kafka_mock_cluster_destroy(mcluster);
2881
+ return NULL;
2882
+ }
2883
+
2884
+
2885
+ /* Construct bootstrap.servers list */
2886
+ mcluster->bootstraps = rd_malloc(bootstraps_len + 1);
2887
+ of = 0;
2888
+ TAILQ_FOREACH(mrkb, &mcluster->brokers, link) {
2889
+ r = rd_snprintf(&mcluster->bootstraps[of], bootstraps_len - of,
2890
+ "%s%s:%hu", of > 0 ? "," : "",
2891
+ mrkb->advertised_listener, mrkb->port);
2892
+ of += r;
2893
+ rd_assert(of < bootstraps_len);
2894
+ }
2895
+ mcluster->bootstraps[of] = '\0';
2896
+
2897
+ rd_kafka_dbg(rk, MOCK, "MOCK", "Mock cluster %s bootstrap.servers=%s",
2898
+ mcluster->id, mcluster->bootstraps);
2899
+
2900
+ rd_atomic32_add(&rk->rk_mock.cluster_cnt, 1);
2901
+
2902
+ return mcluster;
2903
+ }
2904
+
2905
+
2906
+ rd_kafka_t *
2907
+ rd_kafka_mock_cluster_handle(const rd_kafka_mock_cluster_t *mcluster) {
2908
+ return (rd_kafka_t *)mcluster->rk;
2909
+ }
2910
+
2911
+ rd_kafka_mock_cluster_t *rd_kafka_handle_mock_cluster(const rd_kafka_t *rk) {
2912
+ return (rd_kafka_mock_cluster_t *)rk->rk_mock.cluster;
2913
+ }
2914
+
2915
+
2916
+ const char *
2917
+ rd_kafka_mock_cluster_bootstraps(const rd_kafka_mock_cluster_t *mcluster) {
2918
+ return mcluster->bootstraps;
2919
+ }
2920
+
2921
+ /**
2922
+ * @struct Represents a request to the mock cluster along with a timestamp.
2923
+ */
2924
+ struct rd_kafka_mock_request_s {
2925
+ int32_t id; /**< Broker id */
2926
+ int16_t api_key; /**< API Key of request */
2927
+ rd_ts_t timestamp /**< Timestamp at which request was received */;
2928
+ };
2929
+
2930
+ /**
2931
+ * @brief Allocate and initialize a rd_kafka_mock_request_t *
2932
+ */
2933
+ static rd_kafka_mock_request_t *
2934
+ rd_kafka_mock_request_new(int32_t id, int16_t api_key, int64_t timestamp_us) {
2935
+ rd_kafka_mock_request_t *request;
2936
+ request = rd_malloc(sizeof(*request));
2937
+ request->id = id;
2938
+ request->api_key = api_key;
2939
+ request->timestamp = timestamp_us;
2940
+ return request;
2941
+ }
2942
+
2943
+ static rd_kafka_mock_request_t *
2944
+ rd_kafka_mock_request_copy(rd_kafka_mock_request_t *mrequest) {
2945
+ rd_kafka_mock_request_t *request;
2946
+ request = rd_malloc(sizeof(*request));
2947
+ request->id = mrequest->id;
2948
+ request->api_key = mrequest->api_key;
2949
+ request->timestamp = mrequest->timestamp;
2950
+ return request;
2951
+ }
2952
+
2953
+ void rd_kafka_mock_request_destroy(rd_kafka_mock_request_t *mrequest) {
2954
+ rd_free(mrequest);
2955
+ }
2956
+
2957
+ void rd_kafka_mock_request_destroy_array(rd_kafka_mock_request_t **mrequests,
2958
+ size_t mrequest_cnt) {
2959
+ size_t i;
2960
+ for (i = 0; i < mrequest_cnt; i++)
2961
+ rd_kafka_mock_request_destroy(mrequests[i]);
2962
+ rd_free(mrequests);
2963
+ }
2964
+
2965
+ static void rd_kafka_mock_request_free(void *element) {
2966
+ rd_kafka_mock_request_destroy(element);
2967
+ }
2968
+
2969
+ void rd_kafka_mock_start_request_tracking(rd_kafka_mock_cluster_t *mcluster) {
2970
+ mtx_lock(&mcluster->lock);
2971
+ mcluster->track_requests = rd_true;
2972
+ rd_list_clear(&mcluster->request_list);
2973
+ mtx_unlock(&mcluster->lock);
2974
+ }
2975
+
2976
+ void rd_kafka_mock_stop_request_tracking(rd_kafka_mock_cluster_t *mcluster) {
2977
+ mtx_lock(&mcluster->lock);
2978
+ mcluster->track_requests = rd_false;
2979
+ rd_list_clear(&mcluster->request_list);
2980
+ mtx_unlock(&mcluster->lock);
2981
+ }
2982
+
2983
+ rd_kafka_mock_request_t **
2984
+ rd_kafka_mock_get_requests(rd_kafka_mock_cluster_t *mcluster, size_t *cntp) {
2985
+ size_t i;
2986
+ rd_kafka_mock_request_t **ret = NULL;
2987
+
2988
+ mtx_lock(&mcluster->lock);
2989
+ *cntp = rd_list_cnt(&mcluster->request_list);
2990
+ if (*cntp > 0) {
2991
+ ret = rd_calloc(*cntp, sizeof(rd_kafka_mock_request_t *));
2992
+ for (i = 0; i < *cntp; i++) {
2993
+ rd_kafka_mock_request_t *mreq =
2994
+ rd_list_elem(&mcluster->request_list, i);
2995
+ ret[i] = rd_kafka_mock_request_copy(mreq);
2996
+ }
2997
+ }
2998
+
2999
+ mtx_unlock(&mcluster->lock);
3000
+ return ret;
3001
+ }
3002
+
3003
+ void rd_kafka_mock_clear_requests(rd_kafka_mock_cluster_t *mcluster) {
3004
+ mtx_lock(&mcluster->lock);
3005
+ rd_list_clear(&mcluster->request_list);
3006
+ mtx_unlock(&mcluster->lock);
3007
+ }
3008
+
3009
+ int32_t rd_kafka_mock_request_id(rd_kafka_mock_request_t *mreq) {
3010
+ return mreq->id;
3011
+ }
3012
+
3013
+ int16_t rd_kafka_mock_request_api_key(rd_kafka_mock_request_t *mreq) {
3014
+ return mreq->api_key;
3015
+ }
3016
+
3017
+ rd_ts_t rd_kafka_mock_request_timestamp(rd_kafka_mock_request_t *mreq) {
3018
+ return mreq->timestamp;
3019
+ }
3020
+
3021
+ /* Unit tests */
3022
+
3023
+ /**
3024
+ * @brief Create a topic-partition list with vararg arguments.
3025
+ *
3026
+ * @param cnt Number of topic-partitions.
3027
+ * @param ...vararg is a tuple of:
3028
+ * const char *topic_name
3029
+ * int32_t partition
3030
+ *
3031
+ * @remark The returned pointer ownership is transferred to the caller.
3032
+ */
3033
+ static rd_kafka_topic_partition_list_t *ut_topic_partitions(int cnt, ...) {
3034
+ va_list ap;
3035
+ const char *topic_name;
3036
+ int i = 0;
3037
+
3038
+ rd_kafka_topic_partition_list_t *rktparlist =
3039
+ rd_kafka_topic_partition_list_new(cnt);
3040
+ va_start(ap, cnt);
3041
+ while (i < cnt) {
3042
+ topic_name = va_arg(ap, const char *);
3043
+ int32_t partition = va_arg(ap, int32_t);
3044
+
3045
+ rd_kafka_topic_partition_list_add(rktparlist, topic_name,
3046
+ partition);
3047
+ i++;
3048
+ }
3049
+ va_end(ap);
3050
+
3051
+ return rktparlist;
3052
+ }
3053
+
3054
+ /**
3055
+ * @brief Assert \p expected partition list is equal to \p actual.
3056
+ *
3057
+ * @param expected Expected partition list.
3058
+ * @param actual Actual partition list.
3059
+ * @return Comparation result.
3060
+ */
3061
+ static int ut_assert_topic_partitions(rd_kafka_topic_partition_list_t *expected,
3062
+ rd_kafka_topic_partition_list_t *actual) {
3063
+ rd_bool_t equal;
3064
+ char expected_str[256] = "";
3065
+ char actual_str[256] = "";
3066
+
3067
+ if (expected)
3068
+ RD_UT_ASSERT(actual, "list should be not-NULL, but it's NULL");
3069
+ else
3070
+ RD_UT_ASSERT(!actual, "list should be NULL, but it's not-NULL");
3071
+
3072
+
3073
+ if (!expected)
3074
+ return 0;
3075
+
3076
+ equal = !rd_kafka_topic_partition_list_cmp(
3077
+ actual, expected, rd_kafka_topic_partition_cmp);
3078
+
3079
+ if (!equal) {
3080
+ rd_kafka_topic_partition_list_str(expected, expected_str,
3081
+ sizeof(expected_str),
3082
+ RD_KAFKA_FMT_F_NO_ERR);
3083
+ rd_kafka_topic_partition_list_str(actual, actual_str,
3084
+ sizeof(actual_str),
3085
+ RD_KAFKA_FMT_F_NO_ERR);
3086
+ }
3087
+
3088
+ RD_UT_ASSERT(equal, "list should be equal. Expected: %s, got: %s",
3089
+ expected_str, actual_str);
3090
+ return 0;
3091
+ }
3092
+
3093
+ /**
3094
+ * @struct Fixture used for testing next assignment calculation.
3095
+ */
3096
+ struct cgrp_consumer_member_next_assignment_fixture {
3097
+ /** Current member epoch (after calling next assignment). */
3098
+ int32_t current_member_epoch;
3099
+ /** Current consumer assignment, if changed. */
3100
+ rd_kafka_topic_partition_list_t *current_assignment;
3101
+ /** Returned assignment, if expected. */
3102
+ rd_kafka_topic_partition_list_t *returned_assignment;
3103
+ /** Target assignment, if changed. */
3104
+ rd_kafka_topic_partition_list_t *target_assignment;
3105
+ /** Should simulate a disconnection and reconnection. */
3106
+ rd_bool_t reconnected;
3107
+ /** Should simulate a session time out. */
3108
+ rd_bool_t session_timed_out;
3109
+ /** Comment to log. */
3110
+ const char *comment;
3111
+ };
3112
+
3113
+ /**
3114
+ * @brief Test next assignment calculation using passed \p fixtures.
3115
+ * using a new cluster with a topic with name \p topic and
3116
+ * \p partitions partitions.
3117
+ *
3118
+ * @param topic Topic name to create.
3119
+ * @param partitions Topic partition.
3120
+ * @param fixtures Array of fixtures for this test.
3121
+ * @param fixtures_cnt Number of elements in \p fixtures.
3122
+ * @return Number of occurred errors.
3123
+ */
3124
+ static int ut_cgrp_consumer_member_next_assignment0(
3125
+ const char *topic,
3126
+ int partitions,
3127
+ struct cgrp_consumer_member_next_assignment_fixture *fixtures,
3128
+ size_t fixtures_cnt) {
3129
+ int failures = 0;
3130
+ int32_t current_member_epoch = 0;
3131
+ size_t i;
3132
+ rd_kafka_t *rk;
3133
+ rd_kafka_mock_cluster_t *mcluster;
3134
+ static rd_kafka_mock_topic_t *mtopic;
3135
+ rd_kafka_mock_cgrp_consumer_t *mcgrp;
3136
+ rd_kafka_mock_cgrp_consumer_member_t *member;
3137
+ char errstr[512];
3138
+ rd_kafkap_str_t GroupId = {.str = "group", .len = 5};
3139
+ rd_kafkap_str_t MemberId = {.str = "A", .len = 1};
3140
+ rd_kafkap_str_t InstanceId = {.len = -1};
3141
+ rd_kafkap_str_t SubscribedTopic = {.str = topic, .len = strlen(topic)};
3142
+ rd_kafkap_str_t SubscribedTopicRegex = RD_KAFKAP_STR_INITIALIZER_EMPTY;
3143
+ struct rd_kafka_mock_connection_s *conn =
3144
+ (struct rd_kafka_mock_connection_s
3145
+ *)1; /* fake connection instance */
3146
+
3147
+ rk = rd_kafka_new(RD_KAFKA_CONSUMER, NULL, errstr, sizeof(errstr));
3148
+ mcluster = rd_kafka_mock_cluster_new(rk, 1);
3149
+ mcgrp = rd_kafka_mock_cgrp_consumer_get(mcluster, &GroupId);
3150
+ member = rd_kafka_mock_cgrp_consumer_member_add(
3151
+ mcgrp, conn, &MemberId, &InstanceId, &SubscribedTopic, 1,
3152
+ &SubscribedTopicRegex);
3153
+ mtopic = rd_kafka_mock_topic_new(mcluster, topic, partitions, 1);
3154
+
3155
+ for (i = 0; i < fixtures_cnt; i++) {
3156
+ int j;
3157
+ rd_kafka_topic_partition_list_t *current_assignment,
3158
+ *member_target_assignment, *next_assignment,
3159
+ *returned_assignment;
3160
+
3161
+ RD_UT_SAY("test fixture %" PRIusz ": %s", i,
3162
+ fixtures[i].comment);
3163
+
3164
+ if (fixtures[i].session_timed_out) {
3165
+ rd_kafka_mock_cgrp_consumer_member_leave(mcgrp, member,
3166
+ rd_false);
3167
+ member = rd_kafka_mock_cgrp_consumer_member_add(
3168
+ mcgrp, conn, &MemberId, &InstanceId,
3169
+ &SubscribedTopic, 1, &SubscribedTopicRegex);
3170
+ }
3171
+
3172
+ if (fixtures[i].reconnected) {
3173
+ rd_kafka_mock_cgrps_connection_closed(mcluster, conn);
3174
+ conn++;
3175
+ member = rd_kafka_mock_cgrp_consumer_member_add(
3176
+ mcgrp, conn, &MemberId, &InstanceId,
3177
+ &SubscribedTopic, 1, &SubscribedTopicRegex);
3178
+ }
3179
+
3180
+ member_target_assignment = fixtures[i].target_assignment;
3181
+ if (member_target_assignment) {
3182
+ rd_kafka_mock_cgrp_consumer_target_assignment_t
3183
+ *target_assignment;
3184
+
3185
+ target_assignment =
3186
+ rd_kafka_mock_cgrp_consumer_target_assignment_new(
3187
+ (char **)&MemberId.str, 1,
3188
+ &member_target_assignment);
3189
+
3190
+ rd_kafka_mock_cgrp_consumer_target_assignment(
3191
+ mcluster, GroupId.str, target_assignment);
3192
+ rd_kafka_mock_cgrp_consumer_target_assignment_destroy(
3193
+ target_assignment);
3194
+ rd_kafka_topic_partition_list_destroy(
3195
+ member_target_assignment);
3196
+ }
3197
+
3198
+ current_assignment = fixtures[i].current_assignment;
3199
+ if (current_assignment) {
3200
+ /* Set topic id */
3201
+ for (j = 0; j < current_assignment->cnt; j++) {
3202
+ rd_kafka_topic_partition_set_topic_id(
3203
+ &current_assignment->elems[j], mtopic->id);
3204
+ }
3205
+ }
3206
+
3207
+ next_assignment =
3208
+ rd_kafka_mock_cgrp_consumer_member_next_assignment(
3209
+ member, current_assignment, &current_member_epoch);
3210
+ RD_IF_FREE(current_assignment,
3211
+ rd_kafka_topic_partition_list_destroy);
3212
+ RD_UT_ASSERT(
3213
+ current_member_epoch == fixtures[i].current_member_epoch,
3214
+ "current member epoch after call. Expected: %" PRId32
3215
+ ", got: %" PRId32,
3216
+ fixtures[i].current_member_epoch, current_member_epoch);
3217
+
3218
+ returned_assignment = fixtures[i].returned_assignment;
3219
+ failures += ut_assert_topic_partitions(returned_assignment,
3220
+ next_assignment);
3221
+
3222
+ RD_IF_FREE(next_assignment,
3223
+ rd_kafka_topic_partition_list_destroy);
3224
+ RD_IF_FREE(returned_assignment,
3225
+ rd_kafka_topic_partition_list_destroy);
3226
+ }
3227
+
3228
+ rd_kafka_mock_cluster_destroy(mcluster);
3229
+ rd_kafka_destroy(rk);
3230
+ return failures;
3231
+ }
3232
+
3233
+ /**
3234
+ * @brief Test case where multiple revocations are acked.
3235
+ * Only when they're acked member epoch is bumped
3236
+ * and a new partition is returned to the member.
3237
+ *
3238
+ * @return Number of occurred errors.
3239
+ */
3240
+ static int ut_cgrp_consumer_member_next_assignment1(void) {
3241
+ RD_UT_SAY("Case 1: multiple revocations acked");
3242
+
3243
+ const char *topic = "topic";
3244
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3245
+ {
3246
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3247
+ .current_member_epoch = 3,
3248
+ .current_assignment = NULL,
3249
+ .target_assignment =
3250
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3251
+ .returned_assignment =
3252
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3253
+ },
3254
+ {
3255
+ .comment = "Current assignment empty",
3256
+ .current_member_epoch = 3,
3257
+ .current_assignment = ut_topic_partitions(0),
3258
+ .returned_assignment = NULL,
3259
+ },
3260
+ {
3261
+ .comment = "Empty heartbeat",
3262
+ .current_member_epoch = 3,
3263
+ .current_assignment = NULL,
3264
+ .returned_assignment = NULL,
3265
+ },
3266
+ {
3267
+ .comment = "Current assignment 0",
3268
+ .current_member_epoch = 3,
3269
+ .current_assignment = ut_topic_partitions(1, topic, 0),
3270
+ .returned_assignment = NULL,
3271
+ },
3272
+ {
3273
+ .comment = "Empty heartbeat",
3274
+ .current_member_epoch = 3,
3275
+ .current_assignment = NULL,
3276
+ .returned_assignment = NULL,
3277
+ },
3278
+ {
3279
+ .comment = "Current assignment 0,1",
3280
+ .current_member_epoch = 3,
3281
+ .current_assignment =
3282
+ ut_topic_partitions(2, topic, 0, topic, 1),
3283
+ .returned_assignment = NULL,
3284
+ },
3285
+ {
3286
+ .comment = "Empty heartbeat",
3287
+ .current_member_epoch = 3,
3288
+ .current_assignment = NULL,
3289
+ .returned_assignment = NULL,
3290
+ },
3291
+ {
3292
+ .comment = "Current assignment 0,1,2",
3293
+ .current_member_epoch = 3,
3294
+ .current_assignment =
3295
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3296
+ .returned_assignment = NULL,
3297
+ },
3298
+ {
3299
+ .comment = "Target assignment 0,1,3. Returned assignment 0,1",
3300
+ .current_member_epoch = 3,
3301
+ .target_assignment =
3302
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3303
+ .current_assignment = NULL,
3304
+ .returned_assignment =
3305
+ ut_topic_partitions(2, topic, 0, topic, 1),
3306
+ },
3307
+ {
3308
+ .comment = "Target assignment 0,3. Returned assignment 0",
3309
+ .current_member_epoch = 3,
3310
+ .target_assignment = ut_topic_partitions(2, topic, 0, topic, 3),
3311
+ .current_assignment = NULL,
3312
+ .returned_assignment = ut_topic_partitions(1, topic, 0),
3313
+ },
3314
+ {
3315
+ .comment = "Empty heartbeat",
3316
+ .current_member_epoch = 3,
3317
+ .current_assignment = NULL,
3318
+ .returned_assignment = NULL,
3319
+ },
3320
+ {
3321
+ .comment = "Current assignment 0,1",
3322
+ .current_member_epoch = 3,
3323
+ .current_assignment =
3324
+ ut_topic_partitions(2, topic, 0, topic, 1),
3325
+ .returned_assignment = NULL,
3326
+ },
3327
+ {
3328
+ .comment = "Empty heartbeat",
3329
+ .current_member_epoch = 3,
3330
+ .current_assignment = NULL,
3331
+ .returned_assignment = NULL,
3332
+ },
3333
+ {
3334
+ .comment = "Current assignment 0. Returned assignment 0,3. "
3335
+ "Epoch 3 -> 5",
3336
+ .current_member_epoch = 5,
3337
+ .current_assignment = ut_topic_partitions(1, topic, 0),
3338
+ .returned_assignment =
3339
+ ut_topic_partitions(2, topic, 0, topic, 3),
3340
+ },
3341
+ {
3342
+ .comment = "Empty heartbeat",
3343
+ .current_member_epoch = 5,
3344
+ .current_assignment = NULL,
3345
+ .returned_assignment = NULL,
3346
+ },
3347
+ {
3348
+ .comment = "Current assignment 0,3",
3349
+ .current_member_epoch = 5,
3350
+ .current_assignment =
3351
+ ut_topic_partitions(2, topic, 0, topic, 3),
3352
+ .returned_assignment = NULL,
3353
+ },
3354
+ };
3355
+ return ut_cgrp_consumer_member_next_assignment0(
3356
+ topic, 4, fixtures, RD_ARRAY_SIZE(fixtures));
3357
+ }
3358
+
3359
+ /**
3360
+ * @brief Test case where multiple revocations happen.
3361
+ * Only the first revocation is acked and after that
3362
+ * there's a reassignment and epoch bump.
3363
+ *
3364
+ * @return Number of occurred errors.
3365
+ */
3366
+ static int ut_cgrp_consumer_member_next_assignment2(void) {
3367
+ RD_UT_SAY(
3368
+ "Case 2: reassignment of revoked partition, partial revocation "
3369
+ "acknowledge");
3370
+
3371
+ const char *topic = "topic";
3372
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3373
+ {
3374
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3375
+ .current_member_epoch = 3,
3376
+ .current_assignment = NULL,
3377
+ .target_assignment =
3378
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3379
+ .returned_assignment =
3380
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3381
+ },
3382
+ {
3383
+ .comment = "Current assignment 0,1,2",
3384
+ .current_member_epoch = 3,
3385
+ .current_assignment =
3386
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3387
+ .returned_assignment = NULL,
3388
+ },
3389
+ {
3390
+ .comment = "Target assignment 0,1,3. Returned assignment 0,1",
3391
+ .current_member_epoch = 3,
3392
+ .target_assignment =
3393
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3394
+ .current_assignment = NULL,
3395
+ .returned_assignment =
3396
+ ut_topic_partitions(2, topic, 0, topic, 1),
3397
+ },
3398
+ {
3399
+ .comment = "Target assignment 0,3. Returned assignment 0",
3400
+ .current_member_epoch = 3,
3401
+ .target_assignment = ut_topic_partitions(2, topic, 0, topic, 3),
3402
+ .current_assignment = NULL,
3403
+ .returned_assignment = ut_topic_partitions(1, topic, 0),
3404
+ },
3405
+ {
3406
+ .comment = "Empty heartbeat",
3407
+ .current_member_epoch = 3,
3408
+ .current_assignment = NULL,
3409
+ .returned_assignment = NULL,
3410
+ },
3411
+ {
3412
+ .comment = "Current assignment 0,1",
3413
+ .current_member_epoch = 3,
3414
+ .current_assignment =
3415
+ ut_topic_partitions(2, topic, 0, topic, 1),
3416
+ .returned_assignment = NULL,
3417
+ },
3418
+ {
3419
+ .comment = "Empty heartbeat",
3420
+ .current_member_epoch = 3,
3421
+ .current_assignment = NULL,
3422
+ .returned_assignment = NULL,
3423
+ },
3424
+ {
3425
+ .comment = "Target+Returned assignment 0,1,3. Epoch 3 -> 6",
3426
+ .current_member_epoch = 6,
3427
+ .target_assignment =
3428
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3429
+ .current_assignment = NULL,
3430
+ .returned_assignment =
3431
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3432
+ },
3433
+ {
3434
+ .comment = "Empty heartbeat",
3435
+ .current_member_epoch = 6,
3436
+ .current_assignment = NULL,
3437
+ .returned_assignment = NULL,
3438
+ },
3439
+ {
3440
+ .comment = "Current assignment 0,1,3",
3441
+ .current_member_epoch = 6,
3442
+ .current_assignment =
3443
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3444
+ .returned_assignment = NULL,
3445
+ },
3446
+ };
3447
+ return ut_cgrp_consumer_member_next_assignment0(
3448
+ topic, 4, fixtures, RD_ARRAY_SIZE(fixtures));
3449
+ }
3450
+
3451
+ /**
3452
+ * @brief Test case where multiple revocations happen.
3453
+ * They aren't acked but then a
3454
+ * reassignment of all the revoked partition happens, bumping the epoch.
3455
+ *
3456
+ * @return Number of occurred errors.
3457
+ */
3458
+ static int ut_cgrp_consumer_member_next_assignment3(void) {
3459
+ RD_UT_SAY(
3460
+ "Case 3: reassignment of revoked partition and new partition, no "
3461
+ "revocation acknowledge");
3462
+
3463
+ const char *topic = "topic";
3464
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3465
+ {
3466
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3467
+ .current_member_epoch = 3,
3468
+ .current_assignment = NULL,
3469
+ .target_assignment =
3470
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3471
+ .returned_assignment =
3472
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3473
+ },
3474
+ {
3475
+ .comment = "Current assignment 0,1,2",
3476
+ .current_member_epoch = 3,
3477
+ .current_assignment =
3478
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3479
+ .returned_assignment = NULL,
3480
+ },
3481
+ {
3482
+ .comment = "Target assignment 0,1,3. Returned assignment 0,1",
3483
+ .current_member_epoch = 3,
3484
+ .target_assignment =
3485
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 3),
3486
+ .current_assignment = NULL,
3487
+ .returned_assignment =
3488
+ ut_topic_partitions(2, topic, 0, topic, 1),
3489
+ },
3490
+ {
3491
+ .comment = "Target assignment 0,3. Returned assignment 0",
3492
+ .current_member_epoch = 3,
3493
+ .target_assignment = ut_topic_partitions(2, topic, 0, topic, 3),
3494
+ .current_assignment = NULL,
3495
+ .returned_assignment = ut_topic_partitions(1, topic, 0),
3496
+ },
3497
+ {
3498
+ .comment = "Empty heartbeat",
3499
+ .current_member_epoch = 3,
3500
+ .current_assignment = NULL,
3501
+ .returned_assignment = NULL,
3502
+ },
3503
+ {
3504
+ .comment = "Target+Returned assignment 0,1,2,3. Epoch 3 -> 6",
3505
+ .current_member_epoch = 6,
3506
+ .target_assignment = ut_topic_partitions(
3507
+ 3, topic, 0, topic, 1, topic, 2, topic, 3, NULL),
3508
+ .returned_assignment = ut_topic_partitions(
3509
+ 3, topic, 0, topic, 1, topic, 2, topic, 3, NULL),
3510
+ },
3511
+ {
3512
+ .comment = "Empty heartbeat",
3513
+ .current_member_epoch = 6,
3514
+ .current_assignment = NULL,
3515
+ .returned_assignment = NULL,
3516
+ },
3517
+ {
3518
+ .comment = "Current assignment 0,1,2,3",
3519
+ .current_member_epoch = 6,
3520
+ .current_assignment = ut_topic_partitions(
3521
+ 3, topic, 0, topic, 1, topic, 2, topic, 3, NULL),
3522
+ .returned_assignment = NULL,
3523
+ },
3524
+ };
3525
+ return ut_cgrp_consumer_member_next_assignment0(
3526
+ topic, 4, fixtures, RD_ARRAY_SIZE(fixtures));
3527
+ }
3528
+
3529
+ /**
3530
+ * @brief Test case where a disconnection happens and after that
3531
+ * the client send its assignment again, with same member epoch,
3532
+ * and receives back the returned assignment, even if the same.
3533
+ *
3534
+ * @return Number of occurred errors.
3535
+ */
3536
+ static int ut_cgrp_consumer_member_next_assignment4(void) {
3537
+ RD_UT_SAY("Case 4: reconciliation after disconnection");
3538
+
3539
+ const char *topic = "topic";
3540
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3541
+ {
3542
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3543
+ .current_member_epoch = 3,
3544
+ .current_assignment = NULL,
3545
+ .target_assignment =
3546
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3547
+ .returned_assignment =
3548
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3549
+ },
3550
+ {
3551
+ .comment = "Current assignment empty",
3552
+ .current_member_epoch = 3,
3553
+ .current_assignment = ut_topic_partitions(0),
3554
+ .returned_assignment = NULL,
3555
+ },
3556
+ {
3557
+ .comment = "Disconnected, resends current assignment. Returns "
3558
+ "assignment again",
3559
+ .reconnected = rd_true,
3560
+ .current_member_epoch = 3,
3561
+ .current_assignment = ut_topic_partitions(0),
3562
+ .returned_assignment =
3563
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3564
+ },
3565
+ {
3566
+ .comment = "Empty heartbeat",
3567
+ .current_member_epoch = 3,
3568
+ .current_assignment = NULL,
3569
+ .returned_assignment = NULL,
3570
+ },
3571
+ {
3572
+ .comment = "Current assignment 0,1,2",
3573
+ .current_member_epoch = 3,
3574
+ .current_assignment =
3575
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3576
+ .returned_assignment = NULL,
3577
+ },
3578
+ };
3579
+ return ut_cgrp_consumer_member_next_assignment0(
3580
+ topic, 3, fixtures, RD_ARRAY_SIZE(fixtures));
3581
+ }
3582
+
3583
+ /**
3584
+ * @brief Test case where a session timeout happens and then
3585
+ * the client receives a FENCED_MEMBER_EPOCH error,
3586
+ * revokes all of its partitions and rejoins with epoch 0.
3587
+ *
3588
+ * @return Number of occurred errors.
3589
+ */
3590
+ static int ut_cgrp_consumer_member_next_assignment5(void) {
3591
+ RD_UT_SAY("Case 5: fenced consumer");
3592
+
3593
+ const char *topic = "topic";
3594
+ struct cgrp_consumer_member_next_assignment_fixture fixtures[] = {
3595
+ {
3596
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 3",
3597
+ .current_member_epoch = 3,
3598
+ .current_assignment = NULL,
3599
+ .target_assignment =
3600
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3601
+ .returned_assignment =
3602
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3603
+ },
3604
+ {
3605
+ .comment = "Session times out, receives FENCED_MEMBER_EPOCH. "
3606
+ "Epoch 3 -> 0",
3607
+ .session_timed_out = rd_true,
3608
+ .current_member_epoch = -1,
3609
+ .current_assignment = NULL,
3610
+ .returned_assignment = NULL,
3611
+ },
3612
+ {
3613
+ .comment = "Target+Returned assignment 0,1,2. Epoch 0 -> 6",
3614
+ .target_assignment =
3615
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3616
+ .current_member_epoch = 4,
3617
+ .current_assignment = NULL,
3618
+ .returned_assignment =
3619
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3620
+ },
3621
+ {
3622
+ .comment = "Current assignment 0,1,2",
3623
+ .current_member_epoch = 4,
3624
+ .current_assignment =
3625
+ ut_topic_partitions(3, topic, 0, topic, 1, topic, 2),
3626
+ .returned_assignment = NULL,
3627
+ },
3628
+ };
3629
+ return ut_cgrp_consumer_member_next_assignment0(
3630
+ topic, 3, fixtures, RD_ARRAY_SIZE(fixtures));
3631
+ }
3632
+
3633
+ /**
3634
+ * @brief Test all next assignment calculation cases,
3635
+ * for KIP-848 consumer group type and collect
3636
+ * number of errors.
3637
+ *
3638
+ * @return Number of occurred errors.
3639
+ */
3640
+ static int ut_cgrp_consumer_member_next_assignment(void) {
3641
+ RD_UT_BEGIN();
3642
+ int failures = 0;
3643
+
3644
+ failures += ut_cgrp_consumer_member_next_assignment1();
3645
+ failures += ut_cgrp_consumer_member_next_assignment2();
3646
+ failures += ut_cgrp_consumer_member_next_assignment3();
3647
+ failures += ut_cgrp_consumer_member_next_assignment4();
3648
+ failures += ut_cgrp_consumer_member_next_assignment5();
3649
+
3650
+ RD_UT_ASSERT(!failures, "some tests failed");
3651
+ RD_UT_PASS();
3652
+ }
3653
+
3654
+ /**
3655
+ * @brief Mock cluster unit tests
3656
+ */
3657
+ int unittest_mock_cluster(void) {
3658
+ int fails = 0;
3659
+ fails += ut_cgrp_consumer_member_next_assignment();
3660
+ return fails;
3661
+ }