@ohos-ports/confluentinc-kafka-javascript 1.10.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (628) hide show
  1. package/CHANGELOG.md +457 -0
  2. package/LICENSE.kafkajs +31 -0
  3. package/LICENSE.node-rdkafka +25 -0
  4. package/LICENSE.txt +20 -0
  5. package/README.md +133 -0
  6. package/binding.gyp +184 -0
  7. package/build/Release/confluent-kafka-javascript.node +0 -0
  8. package/build/deps/librdkafka++.so.1 +0 -0
  9. package/build/deps/librdkafka.Makefile +6 -0
  10. package/build/deps/librdkafka.so.1 +0 -0
  11. package/build/deps/librdkafka.target.mk +55 -0
  12. package/deps/librdkafka/.clang-format +136 -0
  13. package/deps/librdkafka/.clang-format-cpp +103 -0
  14. package/deps/librdkafka/.dir-locals.el +10 -0
  15. package/deps/librdkafka/.formatignore +35 -0
  16. package/deps/librdkafka/.gdbmacros +19 -0
  17. package/deps/librdkafka/CHANGELOG.md +2477 -0
  18. package/deps/librdkafka/CMakeLists.txt +291 -0
  19. package/deps/librdkafka/CODE_OF_CONDUCT.md +46 -0
  20. package/deps/librdkafka/CONFIGURATION.md +212 -0
  21. package/deps/librdkafka/CONTRIBUTING.md +431 -0
  22. package/deps/librdkafka/Doxyfile +2375 -0
  23. package/deps/librdkafka/INTRODUCTION.md +2752 -0
  24. package/deps/librdkafka/LICENSE +26 -0
  25. package/deps/librdkafka/LICENSE.cjson +22 -0
  26. package/deps/librdkafka/LICENSE.crc32c +28 -0
  27. package/deps/librdkafka/LICENSE.fnv1a +18 -0
  28. package/deps/librdkafka/LICENSE.hdrhistogram +27 -0
  29. package/deps/librdkafka/LICENSE.lz4 +26 -0
  30. package/deps/librdkafka/LICENSE.murmur2 +25 -0
  31. package/deps/librdkafka/LICENSE.nanopb +22 -0
  32. package/deps/librdkafka/LICENSE.opentelemetry +203 -0
  33. package/deps/librdkafka/LICENSE.pycrc +23 -0
  34. package/deps/librdkafka/LICENSE.queue +31 -0
  35. package/deps/librdkafka/LICENSE.regexp +5 -0
  36. package/deps/librdkafka/LICENSE.snappy +36 -0
  37. package/deps/librdkafka/LICENSE.tinycthread +26 -0
  38. package/deps/librdkafka/LICENSE.wingetopt +49 -0
  39. package/deps/librdkafka/LICENSES.txt +625 -0
  40. package/deps/librdkafka/Makefile +125 -0
  41. package/deps/librdkafka/README.md +201 -0
  42. package/deps/librdkafka/README.win32 +26 -0
  43. package/deps/librdkafka/STATISTICS.md +624 -0
  44. package/deps/librdkafka/configure +214 -0
  45. package/deps/librdkafka/configure.self +341 -0
  46. package/deps/librdkafka/dev-conf.sh +123 -0
  47. package/deps/librdkafka/lds-gen.py +73 -0
  48. package/deps/librdkafka/mainpage.doxy +40 -0
  49. package/deps/librdkafka/mklove/Makefile.base +329 -0
  50. package/deps/librdkafka/mklove/modules/configure.atomics +144 -0
  51. package/deps/librdkafka/mklove/modules/configure.base +2484 -0
  52. package/deps/librdkafka/mklove/modules/configure.builtin +70 -0
  53. package/deps/librdkafka/mklove/modules/configure.cc +186 -0
  54. package/deps/librdkafka/mklove/modules/configure.cxx +8 -0
  55. package/deps/librdkafka/mklove/modules/configure.fileversion +65 -0
  56. package/deps/librdkafka/mklove/modules/configure.gitversion +29 -0
  57. package/deps/librdkafka/mklove/modules/configure.good_cflags +18 -0
  58. package/deps/librdkafka/mklove/modules/configure.host +132 -0
  59. package/deps/librdkafka/mklove/modules/configure.lib +49 -0
  60. package/deps/librdkafka/mklove/modules/configure.libcurl +99 -0
  61. package/deps/librdkafka/mklove/modules/configure.libsasl2 +36 -0
  62. package/deps/librdkafka/mklove/modules/configure.libssl +147 -0
  63. package/deps/librdkafka/mklove/modules/configure.libzstd +58 -0
  64. package/deps/librdkafka/mklove/modules/configure.parseversion +95 -0
  65. package/deps/librdkafka/mklove/modules/configure.pic +16 -0
  66. package/deps/librdkafka/mklove/modules/configure.socket +20 -0
  67. package/deps/librdkafka/mklove/modules/configure.zlib +61 -0
  68. package/deps/librdkafka/mklove/modules/patches/README.md +8 -0
  69. package/deps/librdkafka/mklove/modules/patches/libcurl.0000-no-runtime-linking-check.patch +11 -0
  70. package/deps/librdkafka/mklove/modules/patches/libssl.0000-osx-rand-include-fix-OpenSSL-PR16409.patch +56 -0
  71. package/deps/librdkafka/service.yml +182 -0
  72. package/deps/librdkafka/src/CMakeLists.txt +379 -0
  73. package/deps/librdkafka/src/Makefile +105 -0
  74. package/deps/librdkafka/src/README.lz4.md +30 -0
  75. package/deps/librdkafka/src/cJSON.c +3191 -0
  76. package/deps/librdkafka/src/cJSON.h +306 -0
  77. package/deps/librdkafka/src/crc32c.c +430 -0
  78. package/deps/librdkafka/src/crc32c.h +38 -0
  79. package/deps/librdkafka/src/generate_proto.sh +66 -0
  80. package/deps/librdkafka/src/librdkafka_cgrp_synch.png +0 -0
  81. package/deps/librdkafka/src/lz4.c +2727 -0
  82. package/deps/librdkafka/src/lz4.h +842 -0
  83. package/deps/librdkafka/src/lz4frame.c +2078 -0
  84. package/deps/librdkafka/src/lz4frame.h +692 -0
  85. package/deps/librdkafka/src/lz4frame_static.h +47 -0
  86. package/deps/librdkafka/src/lz4hc.c +1631 -0
  87. package/deps/librdkafka/src/lz4hc.h +413 -0
  88. package/deps/librdkafka/src/nanopb/pb.h +917 -0
  89. package/deps/librdkafka/src/nanopb/pb_common.c +388 -0
  90. package/deps/librdkafka/src/nanopb/pb_common.h +49 -0
  91. package/deps/librdkafka/src/nanopb/pb_decode.c +1727 -0
  92. package/deps/librdkafka/src/nanopb/pb_decode.h +193 -0
  93. package/deps/librdkafka/src/nanopb/pb_encode.c +1000 -0
  94. package/deps/librdkafka/src/nanopb/pb_encode.h +185 -0
  95. package/deps/librdkafka/src/opentelemetry/common.pb.c +32 -0
  96. package/deps/librdkafka/src/opentelemetry/common.pb.h +170 -0
  97. package/deps/librdkafka/src/opentelemetry/metrics.options +2 -0
  98. package/deps/librdkafka/src/opentelemetry/metrics.pb.c +67 -0
  99. package/deps/librdkafka/src/opentelemetry/metrics.pb.h +966 -0
  100. package/deps/librdkafka/src/opentelemetry/resource.pb.c +12 -0
  101. package/deps/librdkafka/src/opentelemetry/resource.pb.h +58 -0
  102. package/deps/librdkafka/src/queue.h +850 -0
  103. package/deps/librdkafka/src/rd.h +592 -0
  104. package/deps/librdkafka/src/rdaddr.c +255 -0
  105. package/deps/librdkafka/src/rdaddr.h +202 -0
  106. package/deps/librdkafka/src/rdatomic.h +262 -0
  107. package/deps/librdkafka/src/rdavg.h +260 -0
  108. package/deps/librdkafka/src/rdavl.c +210 -0
  109. package/deps/librdkafka/src/rdavl.h +250 -0
  110. package/deps/librdkafka/src/rdbase64.c +200 -0
  111. package/deps/librdkafka/src/rdbase64.h +43 -0
  112. package/deps/librdkafka/src/rdbuf.c +1884 -0
  113. package/deps/librdkafka/src/rdbuf.h +375 -0
  114. package/deps/librdkafka/src/rdcrc32.c +114 -0
  115. package/deps/librdkafka/src/rdcrc32.h +170 -0
  116. package/deps/librdkafka/src/rddl.c +179 -0
  117. package/deps/librdkafka/src/rddl.h +43 -0
  118. package/deps/librdkafka/src/rdendian.h +175 -0
  119. package/deps/librdkafka/src/rdfloat.h +67 -0
  120. package/deps/librdkafka/src/rdfnv1a.c +113 -0
  121. package/deps/librdkafka/src/rdfnv1a.h +35 -0
  122. package/deps/librdkafka/src/rdgz.c +120 -0
  123. package/deps/librdkafka/src/rdgz.h +46 -0
  124. package/deps/librdkafka/src/rdhdrhistogram.c +721 -0
  125. package/deps/librdkafka/src/rdhdrhistogram.h +87 -0
  126. package/deps/librdkafka/src/rdhttp.c +831 -0
  127. package/deps/librdkafka/src/rdhttp.h +101 -0
  128. package/deps/librdkafka/src/rdinterval.h +177 -0
  129. package/deps/librdkafka/src/rdkafka.c +7287 -0
  130. package/deps/librdkafka/src/rdkafka.h +11795 -0
  131. package/deps/librdkafka/src/rdkafka_admin.c +10165 -0
  132. package/deps/librdkafka/src/rdkafka_admin.h +658 -0
  133. package/deps/librdkafka/src/rdkafka_assignment.c +1282 -0
  134. package/deps/librdkafka/src/rdkafka_assignment.h +73 -0
  135. package/deps/librdkafka/src/rdkafka_assignor.c +1800 -0
  136. package/deps/librdkafka/src/rdkafka_assignor.h +402 -0
  137. package/deps/librdkafka/src/rdkafka_aux.c +409 -0
  138. package/deps/librdkafka/src/rdkafka_aux.h +174 -0
  139. package/deps/librdkafka/src/rdkafka_background.c +222 -0
  140. package/deps/librdkafka/src/rdkafka_broker.c +6736 -0
  141. package/deps/librdkafka/src/rdkafka_broker.h +851 -0
  142. package/deps/librdkafka/src/rdkafka_buf.c +543 -0
  143. package/deps/librdkafka/src/rdkafka_buf.h +1525 -0
  144. package/deps/librdkafka/src/rdkafka_cert.c +576 -0
  145. package/deps/librdkafka/src/rdkafka_cert.h +62 -0
  146. package/deps/librdkafka/src/rdkafka_cgrp.c +8310 -0
  147. package/deps/librdkafka/src/rdkafka_cgrp.h +589 -0
  148. package/deps/librdkafka/src/rdkafka_conf.c +5177 -0
  149. package/deps/librdkafka/src/rdkafka_conf.h +746 -0
  150. package/deps/librdkafka/src/rdkafka_confval.h +97 -0
  151. package/deps/librdkafka/src/rdkafka_coord.c +623 -0
  152. package/deps/librdkafka/src/rdkafka_coord.h +132 -0
  153. package/deps/librdkafka/src/rdkafka_error.c +228 -0
  154. package/deps/librdkafka/src/rdkafka_error.h +80 -0
  155. package/deps/librdkafka/src/rdkafka_event.c +502 -0
  156. package/deps/librdkafka/src/rdkafka_event.h +126 -0
  157. package/deps/librdkafka/src/rdkafka_feature.c +898 -0
  158. package/deps/librdkafka/src/rdkafka_feature.h +104 -0
  159. package/deps/librdkafka/src/rdkafka_fetcher.c +3820 -0
  160. package/deps/librdkafka/src/rdkafka_fetcher.h +62 -0
  161. package/deps/librdkafka/src/rdkafka_header.c +220 -0
  162. package/deps/librdkafka/src/rdkafka_header.h +76 -0
  163. package/deps/librdkafka/src/rdkafka_idempotence.c +807 -0
  164. package/deps/librdkafka/src/rdkafka_idempotence.h +144 -0
  165. package/deps/librdkafka/src/rdkafka_int.h +1505 -0
  166. package/deps/librdkafka/src/rdkafka_interceptor.c +842 -0
  167. package/deps/librdkafka/src/rdkafka_interceptor.h +104 -0
  168. package/deps/librdkafka/src/rdkafka_lz4.c +450 -0
  169. package/deps/librdkafka/src/rdkafka_lz4.h +49 -0
  170. package/deps/librdkafka/src/rdkafka_metadata.c +2347 -0
  171. package/deps/librdkafka/src/rdkafka_metadata.h +350 -0
  172. package/deps/librdkafka/src/rdkafka_metadata_cache.c +1183 -0
  173. package/deps/librdkafka/src/rdkafka_mock.c +4514 -0
  174. package/deps/librdkafka/src/rdkafka_mock.h +867 -0
  175. package/deps/librdkafka/src/rdkafka_mock_cgrp.c +1877 -0
  176. package/deps/librdkafka/src/rdkafka_mock_handlers.c +5255 -0
  177. package/deps/librdkafka/src/rdkafka_mock_int.h +1005 -0
  178. package/deps/librdkafka/src/rdkafka_mock_sharegrp.c +1207 -0
  179. package/deps/librdkafka/src/rdkafka_msg.c +2619 -0
  180. package/deps/librdkafka/src/rdkafka_msg.h +629 -0
  181. package/deps/librdkafka/src/rdkafka_msgbatch.h +62 -0
  182. package/deps/librdkafka/src/rdkafka_msgset.h +105 -0
  183. package/deps/librdkafka/src/rdkafka_msgset_reader.c +2099 -0
  184. package/deps/librdkafka/src/rdkafka_msgset_writer.c +1474 -0
  185. package/deps/librdkafka/src/rdkafka_offset.c +1565 -0
  186. package/deps/librdkafka/src/rdkafka_offset.h +150 -0
  187. package/deps/librdkafka/src/rdkafka_op.c +1219 -0
  188. package/deps/librdkafka/src/rdkafka_op.h +1065 -0
  189. package/deps/librdkafka/src/rdkafka_partition.c +5109 -0
  190. package/deps/librdkafka/src/rdkafka_partition.h +1215 -0
  191. package/deps/librdkafka/src/rdkafka_pattern.c +228 -0
  192. package/deps/librdkafka/src/rdkafka_pattern.h +70 -0
  193. package/deps/librdkafka/src/rdkafka_plugin.c +213 -0
  194. package/deps/librdkafka/src/rdkafka_plugin.h +41 -0
  195. package/deps/librdkafka/src/rdkafka_proto.h +739 -0
  196. package/deps/librdkafka/src/rdkafka_protocol.h +131 -0
  197. package/deps/librdkafka/src/rdkafka_queue.c +1351 -0
  198. package/deps/librdkafka/src/rdkafka_queue.h +1235 -0
  199. package/deps/librdkafka/src/rdkafka_range_assignor.c +1748 -0
  200. package/deps/librdkafka/src/rdkafka_request.c +7298 -0
  201. package/deps/librdkafka/src/rdkafka_request.h +761 -0
  202. package/deps/librdkafka/src/rdkafka_roundrobin_assignor.c +123 -0
  203. package/deps/librdkafka/src/rdkafka_sasl.c +589 -0
  204. package/deps/librdkafka/src/rdkafka_sasl.h +63 -0
  205. package/deps/librdkafka/src/rdkafka_sasl_cyrus.c +722 -0
  206. package/deps/librdkafka/src/rdkafka_sasl_int.h +89 -0
  207. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.c +1833 -0
  208. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.h +52 -0
  209. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.c +1827 -0
  210. package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.h +52 -0
  211. package/deps/librdkafka/src/rdkafka_sasl_plain.c +142 -0
  212. package/deps/librdkafka/src/rdkafka_sasl_scram.c +858 -0
  213. package/deps/librdkafka/src/rdkafka_sasl_win32.c +550 -0
  214. package/deps/librdkafka/src/rdkafka_share_acknowledgement.c +1445 -0
  215. package/deps/librdkafka/src/rdkafka_share_acknowledgement.h +373 -0
  216. package/deps/librdkafka/src/rdkafka_ssl.c +2235 -0
  217. package/deps/librdkafka/src/rdkafka_ssl.h +86 -0
  218. package/deps/librdkafka/src/rdkafka_sticky_assignor.c +4785 -0
  219. package/deps/librdkafka/src/rdkafka_subscription.c +411 -0
  220. package/deps/librdkafka/src/rdkafka_telemetry.c +761 -0
  221. package/deps/librdkafka/src/rdkafka_telemetry.h +52 -0
  222. package/deps/librdkafka/src/rdkafka_telemetry_decode.c +1307 -0
  223. package/deps/librdkafka/src/rdkafka_telemetry_decode.h +59 -0
  224. package/deps/librdkafka/src/rdkafka_telemetry_encode.c +1309 -0
  225. package/deps/librdkafka/src/rdkafka_telemetry_encode.h +459 -0
  226. package/deps/librdkafka/src/rdkafka_timer.c +407 -0
  227. package/deps/librdkafka/src/rdkafka_timer.h +117 -0
  228. package/deps/librdkafka/src/rdkafka_topic.c +2382 -0
  229. package/deps/librdkafka/src/rdkafka_topic.h +369 -0
  230. package/deps/librdkafka/src/rdkafka_transport.c +1309 -0
  231. package/deps/librdkafka/src/rdkafka_transport.h +99 -0
  232. package/deps/librdkafka/src/rdkafka_transport_int.h +100 -0
  233. package/deps/librdkafka/src/rdkafka_txnmgr.c +3256 -0
  234. package/deps/librdkafka/src/rdkafka_txnmgr.h +171 -0
  235. package/deps/librdkafka/src/rdkafka_zstd.c +226 -0
  236. package/deps/librdkafka/src/rdkafka_zstd.h +57 -0
  237. package/deps/librdkafka/src/rdlist.c +593 -0
  238. package/deps/librdkafka/src/rdlist.h +444 -0
  239. package/deps/librdkafka/src/rdlog.c +89 -0
  240. package/deps/librdkafka/src/rdlog.h +41 -0
  241. package/deps/librdkafka/src/rdmap.c +508 -0
  242. package/deps/librdkafka/src/rdmap.h +492 -0
  243. package/deps/librdkafka/src/rdmurmur2.c +167 -0
  244. package/deps/librdkafka/src/rdmurmur2.h +35 -0
  245. package/deps/librdkafka/src/rdports.c +61 -0
  246. package/deps/librdkafka/src/rdports.h +38 -0
  247. package/deps/librdkafka/src/rdposix.h +250 -0
  248. package/deps/librdkafka/src/rdrand.c +151 -0
  249. package/deps/librdkafka/src/rdrand.h +72 -0
  250. package/deps/librdkafka/src/rdregex.c +156 -0
  251. package/deps/librdkafka/src/rdregex.h +43 -0
  252. package/deps/librdkafka/src/rdsignal.h +57 -0
  253. package/deps/librdkafka/src/rdstring.c +645 -0
  254. package/deps/librdkafka/src/rdstring.h +98 -0
  255. package/deps/librdkafka/src/rdsysqueue.h +404 -0
  256. package/deps/librdkafka/src/rdtime.h +356 -0
  257. package/deps/librdkafka/src/rdtypes.h +86 -0
  258. package/deps/librdkafka/src/rdunittest.c +564 -0
  259. package/deps/librdkafka/src/rdunittest.h +232 -0
  260. package/deps/librdkafka/src/rdunittest_acknowledge.c +1177 -0
  261. package/deps/librdkafka/src/rdunittest_fetcher.c +1211 -0
  262. package/deps/librdkafka/src/rdunittest_msgset_errors.c +1521 -0
  263. package/deps/librdkafka/src/rdvarint.c +134 -0
  264. package/deps/librdkafka/src/rdvarint.h +165 -0
  265. package/deps/librdkafka/src/rdwin32.h +382 -0
  266. package/deps/librdkafka/src/rdxxhash.c +1030 -0
  267. package/deps/librdkafka/src/rdxxhash.h +328 -0
  268. package/deps/librdkafka/src/regexp.c +1352 -0
  269. package/deps/librdkafka/src/regexp.h +41 -0
  270. package/deps/librdkafka/src/snappy.c +1866 -0
  271. package/deps/librdkafka/src/snappy.h +62 -0
  272. package/deps/librdkafka/src/snappy_compat.h +138 -0
  273. package/deps/librdkafka/src/statistics_schema.json +444 -0
  274. package/deps/librdkafka/src/tinycthread.c +932 -0
  275. package/deps/librdkafka/src/tinycthread.h +503 -0
  276. package/deps/librdkafka/src/tinycthread_extra.c +210 -0
  277. package/deps/librdkafka/src/tinycthread_extra.h +218 -0
  278. package/deps/librdkafka/src/win32_config.h +58 -0
  279. package/deps/librdkafka/src-cpp/CMakeLists.txt +90 -0
  280. package/deps/librdkafka/src-cpp/ConfImpl.cpp +84 -0
  281. package/deps/librdkafka/src-cpp/ConsumerImpl.cpp +244 -0
  282. package/deps/librdkafka/src-cpp/HandleImpl.cpp +436 -0
  283. package/deps/librdkafka/src-cpp/HeadersImpl.cpp +48 -0
  284. package/deps/librdkafka/src-cpp/KafkaConsumerImpl.cpp +296 -0
  285. package/deps/librdkafka/src-cpp/Makefile +55 -0
  286. package/deps/librdkafka/src-cpp/MessageImpl.cpp +38 -0
  287. package/deps/librdkafka/src-cpp/MetadataImpl.cpp +170 -0
  288. package/deps/librdkafka/src-cpp/ProducerImpl.cpp +197 -0
  289. package/deps/librdkafka/src-cpp/QueueImpl.cpp +70 -0
  290. package/deps/librdkafka/src-cpp/README.md +16 -0
  291. package/deps/librdkafka/src-cpp/RdKafka.cpp +59 -0
  292. package/deps/librdkafka/src-cpp/TopicImpl.cpp +124 -0
  293. package/deps/librdkafka/src-cpp/TopicPartitionImpl.cpp +57 -0
  294. package/deps/librdkafka/src-cpp/rdkafkacpp.h +3797 -0
  295. package/deps/librdkafka/src-cpp/rdkafkacpp_int.h +1641 -0
  296. package/deps/librdkafka/tests/0000-unittests.c +72 -0
  297. package/deps/librdkafka/tests/0001-multiobj.c +102 -0
  298. package/deps/librdkafka/tests/0002-unkpart.c +244 -0
  299. package/deps/librdkafka/tests/0003-msgmaxsize.c +173 -0
  300. package/deps/librdkafka/tests/0004-conf.c +934 -0
  301. package/deps/librdkafka/tests/0005-order.c +133 -0
  302. package/deps/librdkafka/tests/0006-symbols.c +163 -0
  303. package/deps/librdkafka/tests/0007-autotopic.c +136 -0
  304. package/deps/librdkafka/tests/0008-reqacks.c +179 -0
  305. package/deps/librdkafka/tests/0009-mock_cluster.c +272 -0
  306. package/deps/librdkafka/tests/0011-produce_batch.c +753 -0
  307. package/deps/librdkafka/tests/0012-produce_consume.c +537 -0
  308. package/deps/librdkafka/tests/0013-null-msgs.c +473 -0
  309. package/deps/librdkafka/tests/0014-reconsume-191.c +512 -0
  310. package/deps/librdkafka/tests/0015-offset_seeks.c +172 -0
  311. package/deps/librdkafka/tests/0016-client_swname.c +181 -0
  312. package/deps/librdkafka/tests/0017-compression.c +140 -0
  313. package/deps/librdkafka/tests/0018-cgrp_term.c +444 -0
  314. package/deps/librdkafka/tests/0019-list_groups.c +289 -0
  315. package/deps/librdkafka/tests/0020-destroy_hang.c +162 -0
  316. package/deps/librdkafka/tests/0021-rkt_destroy.c +72 -0
  317. package/deps/librdkafka/tests/0022-consume_batch.c +331 -0
  318. package/deps/librdkafka/tests/0025-timers.c +147 -0
  319. package/deps/librdkafka/tests/0026-consume_pause.c +547 -0
  320. package/deps/librdkafka/tests/0028-long_topicnames.c +79 -0
  321. package/deps/librdkafka/tests/0029-assign_offset.c +202 -0
  322. package/deps/librdkafka/tests/0030-offset_commit.c +589 -0
  323. package/deps/librdkafka/tests/0031-get_offsets.c +235 -0
  324. package/deps/librdkafka/tests/0033-regex_subscribe.c +536 -0
  325. package/deps/librdkafka/tests/0034-offset_reset.c +398 -0
  326. package/deps/librdkafka/tests/0035-api_version.c +73 -0
  327. package/deps/librdkafka/tests/0036-partial_fetch.c +87 -0
  328. package/deps/librdkafka/tests/0037-destroy_hang_local.c +85 -0
  329. package/deps/librdkafka/tests/0038-performance.c +121 -0
  330. package/deps/librdkafka/tests/0039-event.c +284 -0
  331. package/deps/librdkafka/tests/0040-io_event.c +257 -0
  332. package/deps/librdkafka/tests/0041-fetch_max_bytes.c +97 -0
  333. package/deps/librdkafka/tests/0042-many_topics.c +252 -0
  334. package/deps/librdkafka/tests/0043-no_connection.c +77 -0
  335. package/deps/librdkafka/tests/0044-partition_cnt.c +94 -0
  336. package/deps/librdkafka/tests/0045-subscribe_update.c +1010 -0
  337. package/deps/librdkafka/tests/0046-rkt_cache.c +65 -0
  338. package/deps/librdkafka/tests/0047-partial_buf_tmout.c +98 -0
  339. package/deps/librdkafka/tests/0048-partitioner.c +283 -0
  340. package/deps/librdkafka/tests/0049-consume_conn_close.c +162 -0
  341. package/deps/librdkafka/tests/0050-subscribe_adds.c +145 -0
  342. package/deps/librdkafka/tests/0051-assign_adds.c +126 -0
  343. package/deps/librdkafka/tests/0052-msg_timestamps.c +238 -0
  344. package/deps/librdkafka/tests/0053-stats_cb.cpp +527 -0
  345. package/deps/librdkafka/tests/0054-offset_time.cpp +236 -0
  346. package/deps/librdkafka/tests/0055-producer_latency.c +539 -0
  347. package/deps/librdkafka/tests/0056-balanced_group_mt.c +315 -0
  348. package/deps/librdkafka/tests/0057-invalid_topic.cpp +112 -0
  349. package/deps/librdkafka/tests/0058-log.cpp +123 -0
  350. package/deps/librdkafka/tests/0059-bsearch.cpp +241 -0
  351. package/deps/librdkafka/tests/0060-op_prio.cpp +163 -0
  352. package/deps/librdkafka/tests/0061-consumer_lag.cpp +295 -0
  353. package/deps/librdkafka/tests/0062-stats_event.c +126 -0
  354. package/deps/librdkafka/tests/0063-clusterid.cpp +180 -0
  355. package/deps/librdkafka/tests/0064-interceptors.c +529 -0
  356. package/deps/librdkafka/tests/0065-yield.cpp +140 -0
  357. package/deps/librdkafka/tests/0066-plugins.cpp +143 -0
  358. package/deps/librdkafka/tests/0067-empty_topic.cpp +151 -0
  359. package/deps/librdkafka/tests/0068-produce_timeout.c +136 -0
  360. package/deps/librdkafka/tests/0069-consumer_add_parts.c +119 -0
  361. package/deps/librdkafka/tests/0070-null_empty.cpp +197 -0
  362. package/deps/librdkafka/tests/0072-headers_ut.c +448 -0
  363. package/deps/librdkafka/tests/0073-headers.c +381 -0
  364. package/deps/librdkafka/tests/0074-producev.c +87 -0
  365. package/deps/librdkafka/tests/0075-retry.c +290 -0
  366. package/deps/librdkafka/tests/0076-produce_retry.c +452 -0
  367. package/deps/librdkafka/tests/0077-compaction.c +366 -0
  368. package/deps/librdkafka/tests/0078-c_from_cpp.cpp +96 -0
  369. package/deps/librdkafka/tests/0079-fork.c +93 -0
  370. package/deps/librdkafka/tests/0080-admin_ut.c +3095 -0
  371. package/deps/librdkafka/tests/0081-admin.c +5633 -0
  372. package/deps/librdkafka/tests/0082-fetch_max_bytes.cpp +137 -0
  373. package/deps/librdkafka/tests/0083-cb_event.c +233 -0
  374. package/deps/librdkafka/tests/0084-destroy_flags.c +208 -0
  375. package/deps/librdkafka/tests/0085-headers.cpp +392 -0
  376. package/deps/librdkafka/tests/0086-purge.c +368 -0
  377. package/deps/librdkafka/tests/0088-produce_metadata_timeout.c +162 -0
  378. package/deps/librdkafka/tests/0089-max_poll_interval.c +511 -0
  379. package/deps/librdkafka/tests/0090-idempotence.c +171 -0
  380. package/deps/librdkafka/tests/0091-max_poll_interval_timeout.c +295 -0
  381. package/deps/librdkafka/tests/0092-mixed_msgver.c +103 -0
  382. package/deps/librdkafka/tests/0093-holb.c +200 -0
  383. package/deps/librdkafka/tests/0094-idempotence_msg_timeout.c +231 -0
  384. package/deps/librdkafka/tests/0095-all_brokers_down.cpp +122 -0
  385. package/deps/librdkafka/tests/0097-ssl_verify.cpp +674 -0
  386. package/deps/librdkafka/tests/0098-consumer-txn.cpp +1218 -0
  387. package/deps/librdkafka/tests/0099-commit_metadata.c +194 -0
  388. package/deps/librdkafka/tests/0100-thread_interceptors.cpp +195 -0
  389. package/deps/librdkafka/tests/0101-fetch-from-follower.cpp +446 -0
  390. package/deps/librdkafka/tests/0102-static_group_rebalance.c +836 -0
  391. package/deps/librdkafka/tests/0103-transactions.c +1383 -0
  392. package/deps/librdkafka/tests/0104-fetch_from_follower_mock.c +625 -0
  393. package/deps/librdkafka/tests/0105-transactions_mock.c +3930 -0
  394. package/deps/librdkafka/tests/0106-cgrp_sess_timeout.c +318 -0
  395. package/deps/librdkafka/tests/0107-topic_recreate.c +259 -0
  396. package/deps/librdkafka/tests/0109-auto_create_topics.cpp +278 -0
  397. package/deps/librdkafka/tests/0110-batch_size.cpp +182 -0
  398. package/deps/librdkafka/tests/0111-delay_create_topics.cpp +127 -0
  399. package/deps/librdkafka/tests/0112-assign_unknown_part.c +87 -0
  400. package/deps/librdkafka/tests/0113-cooperative_rebalance.cpp +3473 -0
  401. package/deps/librdkafka/tests/0114-sticky_partitioning.cpp +176 -0
  402. package/deps/librdkafka/tests/0115-producer_auth.cpp +182 -0
  403. package/deps/librdkafka/tests/0116-kafkaconsumer_close.cpp +216 -0
  404. package/deps/librdkafka/tests/0117-mock_errors.c +331 -0
  405. package/deps/librdkafka/tests/0118-commit_rebalance.c +154 -0
  406. package/deps/librdkafka/tests/0119-consumer_auth.cpp +167 -0
  407. package/deps/librdkafka/tests/0120-asymmetric_subscription.c +185 -0
  408. package/deps/librdkafka/tests/0121-clusterid.c +115 -0
  409. package/deps/librdkafka/tests/0122-buffer_cleaning_after_rebalance.c +227 -0
  410. package/deps/librdkafka/tests/0123-connections_max_idle.c +98 -0
  411. package/deps/librdkafka/tests/0124-openssl_invalid_engine.c +69 -0
  412. package/deps/librdkafka/tests/0125-immediate_flush.c +144 -0
  413. package/deps/librdkafka/tests/0126-oauthbearer_oidc.c +1164 -0
  414. package/deps/librdkafka/tests/0127-fetch_queue_backoff.cpp +165 -0
  415. package/deps/librdkafka/tests/0128-sasl_callback_queue.cpp +217 -0
  416. package/deps/librdkafka/tests/0129-fetch_aborted_msgs.c +79 -0
  417. package/deps/librdkafka/tests/0130-store_offsets.c +178 -0
  418. package/deps/librdkafka/tests/0131-connect_timeout.c +81 -0
  419. package/deps/librdkafka/tests/0132-strategy_ordering.c +179 -0
  420. package/deps/librdkafka/tests/0133-ssl_keys.c +150 -0
  421. package/deps/librdkafka/tests/0134-ssl_provider.c +92 -0
  422. package/deps/librdkafka/tests/0135-sasl_credentials.cpp +279 -0
  423. package/deps/librdkafka/tests/0136-resolve_cb.c +181 -0
  424. package/deps/librdkafka/tests/0137-barrier_batch_consume.c +752 -0
  425. package/deps/librdkafka/tests/0138-admin_mock.c +281 -0
  426. package/deps/librdkafka/tests/0139-offset_validation_mock.c +950 -0
  427. package/deps/librdkafka/tests/0140-commit_metadata.cpp +108 -0
  428. package/deps/librdkafka/tests/0142-reauthentication.c +931 -0
  429. package/deps/librdkafka/tests/0143-exponential_backoff_mock.c +552 -0
  430. package/deps/librdkafka/tests/0144-idempotence_mock.c +373 -0
  431. package/deps/librdkafka/tests/0145-pause_resume_mock.c +119 -0
  432. package/deps/librdkafka/tests/0146-metadata_mock.c +505 -0
  433. package/deps/librdkafka/tests/0147-consumer_group_consumer_mock.c +1210 -0
  434. package/deps/librdkafka/tests/0148-offset_fetch_commit_error_mock.c +569 -0
  435. package/deps/librdkafka/tests/0149-broker-same-host-port.c +140 -0
  436. package/deps/librdkafka/tests/0150-telemetry_mock.c +651 -0
  437. package/deps/librdkafka/tests/0151-purge-brokers.c +566 -0
  438. package/deps/librdkafka/tests/0152-rebootstrap.c +59 -0
  439. package/deps/librdkafka/tests/0153-memberid.c +128 -0
  440. package/deps/librdkafka/tests/0155-share_group_heartbeat_mock.c +2695 -0
  441. package/deps/librdkafka/tests/0156-share_consumer_fetch_mock.c +1551 -0
  442. package/deps/librdkafka/tests/0157-share_consumer_ack_mock.c +1788 -0
  443. package/deps/librdkafka/tests/0158-share_consumer_transactions_mock.c +467 -0
  444. package/deps/librdkafka/tests/0170-share_consumer_subscription.c +2192 -0
  445. package/deps/librdkafka/tests/0171-share_consumer_consume.c +2150 -0
  446. package/deps/librdkafka/tests/0172-share_consumer_acknowledge.c +1919 -0
  447. package/deps/librdkafka/tests/0173-share_consumer_commit_async.c +2735 -0
  448. package/deps/librdkafka/tests/0174-share_consumer_concurrency.c +1434 -0
  449. package/deps/librdkafka/tests/0175-share_consumer_groups.c +574 -0
  450. package/deps/librdkafka/tests/0176-share_consumer_commit_sync.c +2986 -0
  451. package/deps/librdkafka/tests/0177-share_consumer_transactions.c +1815 -0
  452. package/deps/librdkafka/tests/0178-share_consumer_close.c +2102 -0
  453. package/deps/librdkafka/tests/0179-share_consumer_destroy.c +2482 -0
  454. package/deps/librdkafka/tests/0180-share_consumer_config.c +1677 -0
  455. package/deps/librdkafka/tests/0181-share_consumer_topic_delete.c +354 -0
  456. package/deps/librdkafka/tests/0182-share_consumer_error_handling_mock.c +3358 -0
  457. package/deps/librdkafka/tests/0183-share_consumer_leader_change_mock.c +1234 -0
  458. package/deps/librdkafka/tests/0184-share_consumer_topic_recreate.c +1356 -0
  459. package/deps/librdkafka/tests/0185-share_consumer_max_poll_interval.c +557 -0
  460. package/deps/librdkafka/tests/0186-share_consumer_fatal_error.c +360 -0
  461. package/deps/librdkafka/tests/0190-share_consumer_telemetry.c +387 -0
  462. package/deps/librdkafka/tests/1000-unktopic.c +164 -0
  463. package/deps/librdkafka/tests/8000-idle.cpp +60 -0
  464. package/deps/librdkafka/tests/8001-fetch_from_follower_mock_manual.c +113 -0
  465. package/deps/librdkafka/tests/CMakeLists.txt +193 -0
  466. package/deps/librdkafka/tests/LibrdkafkaTestApp.py +291 -0
  467. package/deps/librdkafka/tests/Makefile +182 -0
  468. package/deps/librdkafka/tests/README.md +514 -0
  469. package/deps/librdkafka/tests/autotest.sh +33 -0
  470. package/deps/librdkafka/tests/backtrace.gdb +30 -0
  471. package/deps/librdkafka/tests/broker_version_tests.py +315 -0
  472. package/deps/librdkafka/tests/buildbox.sh +17 -0
  473. package/deps/librdkafka/tests/cleanup-checker-tests.sh +20 -0
  474. package/deps/librdkafka/tests/cluster_testing.py +203 -0
  475. package/deps/librdkafka/tests/delete-test-topics.sh +56 -0
  476. package/deps/librdkafka/tests/fixtures/oauthbearer/jwt_assertion_template.json +10 -0
  477. package/deps/librdkafka/tests/fixtures/ssl/Makefile +8 -0
  478. package/deps/librdkafka/tests/fixtures/ssl/README.md +13 -0
  479. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.intermediate.p12 +0 -0
  480. package/deps/librdkafka/tests/fixtures/ssl/client.keystore.p12 +0 -0
  481. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.intermediate.pem +72 -0
  482. package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.pem +50 -0
  483. package/deps/librdkafka/tests/fixtures/ssl/client2.intermediate.key +46 -0
  484. package/deps/librdkafka/tests/fixtures/ssl/client2.key +46 -0
  485. package/deps/librdkafka/tests/fixtures/ssl/create_keys.sh +168 -0
  486. package/deps/librdkafka/tests/fuzzers/Makefile +12 -0
  487. package/deps/librdkafka/tests/fuzzers/README.md +31 -0
  488. package/deps/librdkafka/tests/fuzzers/fuzz_regex.c +74 -0
  489. package/deps/librdkafka/tests/fuzzers/helpers.h +90 -0
  490. package/deps/librdkafka/tests/gen-ssl-certs.sh +165 -0
  491. package/deps/librdkafka/tests/interactive_broker_version.py +170 -0
  492. package/deps/librdkafka/tests/interceptor_test/CMakeLists.txt +16 -0
  493. package/deps/librdkafka/tests/interceptor_test/Makefile +22 -0
  494. package/deps/librdkafka/tests/interceptor_test/interceptor_test.c +314 -0
  495. package/deps/librdkafka/tests/interceptor_test/interceptor_test.h +54 -0
  496. package/deps/librdkafka/tests/java/IncrementalRebalanceCli.java +97 -0
  497. package/deps/librdkafka/tests/java/Makefile +13 -0
  498. package/deps/librdkafka/tests/java/Murmur2Cli.java +46 -0
  499. package/deps/librdkafka/tests/java/README.md +14 -0
  500. package/deps/librdkafka/tests/java/TransactionProducerCli.java +162 -0
  501. package/deps/librdkafka/tests/java/run-class.sh +11 -0
  502. package/deps/librdkafka/tests/librdkafka.suppressions +483 -0
  503. package/deps/librdkafka/tests/lz4_manual_test.sh +59 -0
  504. package/deps/librdkafka/tests/multi-broker-version-test.sh +50 -0
  505. package/deps/librdkafka/tests/parse-refcnt.sh +43 -0
  506. package/deps/librdkafka/tests/performance_plot.py +115 -0
  507. package/deps/librdkafka/tests/plugin_test/Makefile +19 -0
  508. package/deps/librdkafka/tests/plugin_test/plugin_test.c +58 -0
  509. package/deps/librdkafka/tests/requirements.txt +2 -0
  510. package/deps/librdkafka/tests/run-all-tests.sh +81 -0
  511. package/deps/librdkafka/tests/run-consumer-tests.sh +16 -0
  512. package/deps/librdkafka/tests/run-producer-tests.sh +16 -0
  513. package/deps/librdkafka/tests/run-test-batches.py +157 -0
  514. package/deps/librdkafka/tests/run-test.sh +141 -0
  515. package/deps/librdkafka/tests/rusage.c +249 -0
  516. package/deps/librdkafka/tests/sasl_test.py +289 -0
  517. package/deps/librdkafka/tests/scenarios/README.md +6 -0
  518. package/deps/librdkafka/tests/scenarios/ak23.json +6 -0
  519. package/deps/librdkafka/tests/scenarios/default.json +5 -0
  520. package/deps/librdkafka/tests/scenarios/noautocreate.json +5 -0
  521. package/deps/librdkafka/tests/share_telemetry_verify/share-otel-collector-config.yaml +28 -0
  522. package/deps/librdkafka/tests/sockem.c +801 -0
  523. package/deps/librdkafka/tests/sockem.h +85 -0
  524. package/deps/librdkafka/tests/sockem_ctrl.c +145 -0
  525. package/deps/librdkafka/tests/sockem_ctrl.h +61 -0
  526. package/deps/librdkafka/tests/test.c +8376 -0
  527. package/deps/librdkafka/tests/test.conf.example +27 -0
  528. package/deps/librdkafka/tests/test.h +1150 -0
  529. package/deps/librdkafka/tests/testcpp.cpp +131 -0
  530. package/deps/librdkafka/tests/testcpp.h +388 -0
  531. package/deps/librdkafka/tests/testshared.h +416 -0
  532. package/deps/librdkafka/tests/tools/README.md +4 -0
  533. package/deps/librdkafka/tests/tools/stats/README.md +21 -0
  534. package/deps/librdkafka/tests/tools/stats/filter.jq +42 -0
  535. package/deps/librdkafka/tests/tools/stats/graph.py +150 -0
  536. package/deps/librdkafka/tests/tools/stats/requirements.txt +3 -0
  537. package/deps/librdkafka/tests/tools/stats/to_csv.py +124 -0
  538. package/deps/librdkafka/tests/trivup/trivup-0.15.0.tar.gz +0 -0
  539. package/deps/librdkafka/tests/until-fail.sh +87 -0
  540. package/deps/librdkafka/tests/xxxx-assign_partition.c +122 -0
  541. package/deps/librdkafka/tests/xxxx-metadata.cpp +159 -0
  542. package/deps/librdkafka/vcpkg.json +23 -0
  543. package/deps/librdkafka/win32/README.md +5 -0
  544. package/deps/librdkafka/win32/build-package.bat +3 -0
  545. package/deps/librdkafka/win32/build.bat +19 -0
  546. package/deps/librdkafka/win32/common.vcxproj +84 -0
  547. package/deps/librdkafka/win32/interceptor_test/interceptor_test.vcxproj +87 -0
  548. package/deps/librdkafka/win32/librdkafka.autopkg.template +54 -0
  549. package/deps/librdkafka/win32/librdkafka.master.testing.targets +13 -0
  550. package/deps/librdkafka/win32/librdkafka.sln +226 -0
  551. package/deps/librdkafka/win32/librdkafka.vcxproj +282 -0
  552. package/deps/librdkafka/win32/librdkafkacpp/librdkafkacpp.vcxproj +104 -0
  553. package/deps/librdkafka/win32/msbuild.ps1 +58 -0
  554. package/deps/librdkafka/win32/openssl_engine_example/openssl_engine_example.vcxproj +132 -0
  555. package/deps/librdkafka/win32/package-zip.ps1 +46 -0
  556. package/deps/librdkafka/win32/packages/repositories.config +4 -0
  557. package/deps/librdkafka/win32/push-package.bat +4 -0
  558. package/deps/librdkafka/win32/rdkafka_complex_consumer_example_cpp/rdkafka_complex_consumer_example_cpp.vcxproj +67 -0
  559. package/deps/librdkafka/win32/rdkafka_example/rdkafka_example.vcxproj +97 -0
  560. package/deps/librdkafka/win32/rdkafka_performance/rdkafka_performance.vcxproj +97 -0
  561. package/deps/librdkafka/win32/setup-msys2.ps1 +47 -0
  562. package/deps/librdkafka/win32/setup-vcpkg.ps1 +34 -0
  563. package/deps/librdkafka/win32/tests/test.conf.example +25 -0
  564. package/deps/librdkafka/win32/tests/tests.vcxproj +278 -0
  565. package/deps/librdkafka/win32/win_ssl_cert_store/win_ssl_cert_store.vcxproj +132 -0
  566. package/deps/librdkafka/win32/wingetopt.c +564 -0
  567. package/deps/librdkafka/win32/wingetopt.h +101 -0
  568. package/deps/librdkafka/win32/wintime.h +33 -0
  569. package/deps/librdkafka.gyp +83 -0
  570. package/deps/windows-install.py +80 -0
  571. package/index.d.ts +3 -0
  572. package/lib/admin.js +732 -0
  573. package/lib/client.js +679 -0
  574. package/lib/error.js +501 -0
  575. package/lib/index.js +8 -0
  576. package/lib/kafka-consumer-stream.js +399 -0
  577. package/lib/kafka-consumer.js +798 -0
  578. package/lib/kafkajs/_admin.js +1052 -0
  579. package/lib/kafkajs/_common.js +887 -0
  580. package/lib/kafkajs/_consumer.js +2156 -0
  581. package/lib/kafkajs/_consumer_cache.js +310 -0
  582. package/lib/kafkajs/_error.js +298 -0
  583. package/lib/kafkajs/_kafka.js +125 -0
  584. package/lib/kafkajs/_linked-list.js +220 -0
  585. package/lib/kafkajs/_producer.js +863 -0
  586. package/lib/kafkajs/index.js +5 -0
  587. package/lib/producer/high-level-producer.js +400 -0
  588. package/lib/producer-stream.js +337 -0
  589. package/lib/producer.js +405 -0
  590. package/lib/rdkafka.js +44 -0
  591. package/lib/tools/ref-counter.js +53 -0
  592. package/lib/topic-partition.js +91 -0
  593. package/lib/topic.js +43 -0
  594. package/lib/util.js +55 -0
  595. package/librdkafka.js +12 -0
  596. package/package.json +85 -0
  597. package/shim/source_location +54 -0
  598. package/src/admin.cc +1597 -0
  599. package/src/admin.h +112 -0
  600. package/src/binding.cc +76 -0
  601. package/src/binding.h +25 -0
  602. package/src/callbacks.cc +676 -0
  603. package/src/callbacks.h +302 -0
  604. package/src/common.cc +1628 -0
  605. package/src/common.h +192 -0
  606. package/src/config.cc +219 -0
  607. package/src/config.h +61 -0
  608. package/src/connection.cc +703 -0
  609. package/src/connection.h +117 -0
  610. package/src/errors.cc +119 -0
  611. package/src/errors.h +60 -0
  612. package/src/kafka-consumer.cc +1539 -0
  613. package/src/kafka-consumer.h +147 -0
  614. package/src/producer.cc +917 -0
  615. package/src/producer.h +124 -0
  616. package/src/topic.cc +173 -0
  617. package/src/topic.h +54 -0
  618. package/src/workers.cc +1714 -0
  619. package/src/workers.h +690 -0
  620. package/types/config.d.ts +1269 -0
  621. package/types/errors.d.ts +373 -0
  622. package/types/kafkajs.d.ts +568 -0
  623. package/types/rdkafka.d.ts +571 -0
  624. package/util/configure.js +32 -0
  625. package/util/generate-docs.sh +18 -0
  626. package/util/get-env.js +6 -0
  627. package/util/test-compile.js +11 -0
  628. package/util/test-producer-delivery.js +100 -0
@@ -0,0 +1,2150 @@
1
+ /*
2
+ * librdkafka - Apache Kafka C library
3
+ *
4
+ * Copyright (c) 2025, Confluent Inc.
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * 1. Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ * this list of conditions and the following disclaimer in the documentation
14
+ * and/or other materials provided with the distribution.
15
+ *
16
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ * POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ #include "test.h"
30
+
31
+ /**
32
+ * @brief Maximum supported values for test configuration
33
+ */
34
+ #define MAX_CONSUMERS 16
35
+ #define MAX_TOPICS 16
36
+ #define MAX_PARTITIONS 32
37
+ #define BATCH_SIZE 10000
38
+
39
+ /** Common producer reused across all tests. */
40
+ static rd_kafka_t *common_producer;
41
+
42
+ /** Common admin client reused across all tests. */
43
+ static rd_kafka_t *common_admin;
44
+
45
+ /**
46
+ * @brief Test configuration structure
47
+ *
48
+ * This structure defines all parameters for a share consumer test scenario.
49
+ *
50
+ * Example configurations:
51
+ *
52
+ * 1. Multiple consumers, single topic, multiple partitions:
53
+ * { .consumer_cnt = 2, .topic_cnt = 1, .partitions = {3},
54
+ * .msgs_per_partition = 100, .group_name = "test-group" }
55
+ *
56
+ * 2. Single consumer, multiple topics, single partition each:
57
+ * { .consumer_cnt = 1, .topic_cnt = 2, .partitions = {1, 1},
58
+ * .msgs_per_partition = 100, .group_name = "test-group" }
59
+ *
60
+ * 3. Multiple consumers, multiple topics, multiple partitions:
61
+ * { .consumer_cnt = 3, .topic_cnt = 2, .partitions = {3, 2},
62
+ * .msgs_per_partition = 50, .group_name = "test-group" }
63
+ */
64
+ typedef struct {
65
+ int consumer_cnt; /**< Number of consumers to create */
66
+ int topic_cnt; /**< Number of topics to create */
67
+ int partitions[MAX_TOPICS]; /**< Partition count for each topic */
68
+ int msgs_per_partition; /**< Messages to produce per partition */
69
+ const char *group_name; /**< Share group name */
70
+ const char *test_name; /**< Test description for logging */
71
+ int poll_timeout_ms; /**< Timeout for each poll (default: 3000) */
72
+ int max_attempts; /**< Max poll attempts (default: auto-calculated) */
73
+ } share_test_config_t;
74
+
75
+ /**
76
+ * @brief Test state/results structure
77
+ */
78
+ typedef struct {
79
+ rd_kafka_share_t *consumers[MAX_CONSUMERS];
80
+ char *topic_names[MAX_TOPICS];
81
+ int per_consumer_count[MAX_CONSUMERS];
82
+ int total_consumed;
83
+ int total_expected;
84
+ } share_test_state_t;
85
+
86
+ /**
87
+ * @brief Create share consumers
88
+ */
89
+ static void create_share_consumers(share_test_config_t *config,
90
+ share_test_state_t *state) {
91
+ int i;
92
+
93
+ for (i = 0; i < config->consumer_cnt; i++) {
94
+ state->consumers[i] =
95
+ test_create_share_consumer(config->group_name, NULL);
96
+ }
97
+
98
+ TEST_SAY("Created %d share consumer(s)\n", config->consumer_cnt);
99
+ }
100
+
101
+ /**
102
+ * @brief Create topics and produce messages
103
+ */
104
+ static void setup_topics_and_produce(share_test_config_t *config,
105
+ share_test_state_t *state) {
106
+ int t, p;
107
+ int total_partitions = 0;
108
+
109
+ state->total_expected = 0;
110
+
111
+ for (t = 0; t < config->topic_cnt; t++) {
112
+ /* Generate unique topic name */
113
+ state->topic_names[t] =
114
+ rd_strdup(test_mk_topic_name("0171-share-test", 1));
115
+
116
+ /* Create topic with specified partitions */
117
+ test_create_topic_wait_exists(NULL, state->topic_names[t],
118
+ config->partitions[t], -1,
119
+ 60 * 1000);
120
+
121
+ /* Produce messages to each partition */
122
+ for (p = 0; p < config->partitions[t]; p++) {
123
+ test_produce_msgs_simple(common_producer,
124
+ state->topic_names[t], p,
125
+ config->msgs_per_partition);
126
+ state->total_expected += config->msgs_per_partition;
127
+ }
128
+
129
+ total_partitions += config->partitions[t];
130
+ TEST_SAY("Topic '%s': %d partition(s), %d msgs/partition\n",
131
+ state->topic_names[t], config->partitions[t],
132
+ config->msgs_per_partition);
133
+ }
134
+
135
+ TEST_SAY(
136
+ "Setup complete: %d topic(s), %d total partition(s), "
137
+ "%d total messages\n",
138
+ config->topic_cnt, total_partitions, state->total_expected);
139
+ }
140
+
141
+ /**
142
+ * @brief Subscribe consumers to topics
143
+ */
144
+ static void subscribe_consumers(share_test_config_t *config,
145
+ share_test_state_t *state) {
146
+ rd_kafka_topic_partition_list_t *subs;
147
+ int t, i;
148
+
149
+ /* Set group config using a dedicated admin client */
150
+ test_share_set_auto_offset_reset(config->group_name, "earliest");
151
+
152
+ /* Build subscription list */
153
+ subs = rd_kafka_topic_partition_list_new(config->topic_cnt);
154
+ for (t = 0; t < config->topic_cnt; t++) {
155
+ rd_kafka_topic_partition_list_add(subs, state->topic_names[t],
156
+ RD_KAFKA_PARTITION_UA);
157
+ }
158
+
159
+ /* Subscribe all consumers */
160
+ for (i = 0; i < config->consumer_cnt; i++) {
161
+ rd_kafka_share_subscribe(state->consumers[i], subs);
162
+ }
163
+
164
+ rd_kafka_topic_partition_list_destroy(subs);
165
+
166
+ TEST_SAY("Subscribed %d consumer(s) to %d topic(s)\n",
167
+ config->consumer_cnt, config->topic_cnt);
168
+ }
169
+
170
+ /**
171
+ * @brief Consume messages function
172
+ */
173
+ static void consume_messages(share_test_config_t *config,
174
+ share_test_state_t *state) {
175
+ rd_kafka_messages_t *batch = NULL;
176
+ int attempts;
177
+ int poll_timeout;
178
+ int i;
179
+
180
+ /* Initialize per-consumer counts */
181
+ for (i = 0; i < config->consumer_cnt; i++) {
182
+ state->per_consumer_count[i] = 0;
183
+ }
184
+ state->total_consumed = 0;
185
+
186
+ /* Calculate defaults if not specified */
187
+ poll_timeout =
188
+ config->poll_timeout_ms > 0 ? config->poll_timeout_ms : 3000;
189
+
190
+ /* Auto-calculate attempts based on expected messages and consumers */
191
+ if (config->max_attempts > 0) {
192
+ attempts = config->max_attempts;
193
+ } else {
194
+ /* Heuristic: at least 10 attempts, or based on message count */
195
+ attempts = (state->total_expected / (BATCH_SIZE / 2)) + 10;
196
+ if (attempts < 20)
197
+ attempts = 20;
198
+ }
199
+
200
+ TEST_SAY(
201
+ "Starting consumption: expecting %d messages, "
202
+ "max %d attempts, %dms timeout\n",
203
+ state->total_expected, attempts, poll_timeout);
204
+
205
+ while (state->total_consumed < state->total_expected &&
206
+ attempts-- > 0) {
207
+ /* Round-robin poll across all consumers */
208
+ for (i = 0; i < config->consumer_cnt; i++) {
209
+ size_t rcvd = 0;
210
+ size_t m;
211
+ rd_kafka_error_t *err;
212
+
213
+ rd_kafka_messages_destroy(batch);
214
+ batch = NULL;
215
+ err = rd_kafka_share_poll(state->consumers[i],
216
+ poll_timeout, &batch);
217
+
218
+ if (err) {
219
+ rd_kafka_error_destroy(err);
220
+ continue;
221
+ }
222
+
223
+ rcvd = rd_kafka_messages_count(batch);
224
+ for (m = 0; m < rcvd; m++) {
225
+ rd_kafka_message_t *msg =
226
+ rd_kafka_messages_get(batch, m);
227
+ if (!msg->err) {
228
+ state->per_consumer_count[i]++;
229
+ state->total_consumed++;
230
+ }
231
+ }
232
+
233
+ /* Early exit if we've consumed everything */
234
+ if (state->total_consumed >= state->total_expected)
235
+ break;
236
+ }
237
+
238
+ /* Progress logging */
239
+ if (config->consumer_cnt == 1) {
240
+ TEST_SAY("Progress: %d/%d\n", state->total_consumed,
241
+ state->total_expected);
242
+ } else {
243
+ /* Build distribution string for multiple consumers */
244
+ char dist[256] = {0};
245
+ int pos = 0;
246
+ for (i = 0; i < config->consumer_cnt && pos < 250;
247
+ i++) {
248
+ pos += rd_snprintf(
249
+ dist + pos, sizeof(dist) - pos, "c%d=%d ",
250
+ i, state->per_consumer_count[i]);
251
+ }
252
+ TEST_SAY("Progress: %d/%d (%s)\n",
253
+ state->total_consumed, state->total_expected,
254
+ dist);
255
+ }
256
+ }
257
+
258
+ rd_kafka_messages_destroy(batch);
259
+ batch = NULL;
260
+ }
261
+
262
+ /**
263
+ * @brief Cleanup function
264
+ */
265
+ static void cleanup_test(share_test_config_t *config,
266
+ share_test_state_t *state) {
267
+ int t, i;
268
+
269
+ for (t = 0; t < config->topic_cnt; t++) {
270
+ if (state->topic_names[t]) {
271
+ rd_free(state->topic_names[t]);
272
+ state->topic_names[t] = NULL;
273
+ }
274
+ }
275
+
276
+ /* Destroy consumers */
277
+ for (i = 0; i < config->consumer_cnt; i++) {
278
+ if (state->consumers[i]) {
279
+ test_share_consumer_close(state->consumers[i]);
280
+ test_share_destroy(state->consumers[i]);
281
+ state->consumers[i] = NULL;
282
+ }
283
+ }
284
+
285
+ TEST_SAY("Cleanup complete\n");
286
+ }
287
+
288
+ /**
289
+ * @brief Test Runner function
290
+ *
291
+ * This function handles:
292
+ * 1. Create consumers
293
+ * 2. Create topics and produce messages
294
+ * 3. Subscribe consumers
295
+ * 4. Consume and verify messages
296
+ * 5. Cleanup
297
+ *
298
+ * @param config Test configuration
299
+ * @returns 0 on success, -1 on failure
300
+ */
301
+ static int run_share_consumer_test(share_test_config_t *config) {
302
+ share_test_state_t state = {0};
303
+ int i;
304
+ char dist_str[512] = {0};
305
+ int pos = 0;
306
+ char unique_suffix[64];
307
+ char unique_group[128];
308
+ char unique_test_name[256];
309
+
310
+ /* Validate config */
311
+ TEST_ASSERT(config->consumer_cnt > 0 &&
312
+ config->consumer_cnt <= MAX_CONSUMERS,
313
+ "consumer_cnt must be 1-%d", MAX_CONSUMERS);
314
+ TEST_ASSERT(config->topic_cnt > 0 && config->topic_cnt <= MAX_TOPICS,
315
+ "topic_cnt must be 1-%d", MAX_TOPICS);
316
+ TEST_ASSERT(config->group_name != NULL, "group_name is required");
317
+
318
+ /* Append a per-invocation unique suffix to the group name and
319
+ * displayed test name so re-runs / parallel runs on the same
320
+ * cluster can't collide on share-group state. */
321
+ rd_snprintf(unique_suffix, sizeof(unique_suffix), "rnd%" PRIx64,
322
+ test_id_generate());
323
+ rd_snprintf(unique_group, sizeof(unique_group), "%s-%s",
324
+ config->group_name, unique_suffix);
325
+ rd_snprintf(unique_test_name, sizeof(unique_test_name), "%s [%s]",
326
+ config->test_name ? config->test_name
327
+ : "Share Consumer Test",
328
+ unique_suffix);
329
+ config->group_name = unique_group;
330
+ config->test_name = unique_test_name;
331
+
332
+ /* Print test header */
333
+ TEST_SAY("\n");
334
+ TEST_SAY(
335
+ "============================================================\n");
336
+ TEST_SAY("=== %s ===\n", config->test_name);
337
+ TEST_SAY("=== Consumers: %d, Topics: %d, Partitions: [",
338
+ config->consumer_cnt, config->topic_cnt);
339
+ for (i = 0; i < config->topic_cnt; i++) {
340
+ TEST_SAY("%d%s", config->partitions[i],
341
+ i < config->topic_cnt - 1 ? ", " : "");
342
+ }
343
+ TEST_SAY("], Msgs/Partition: %d ===\n", config->msgs_per_partition);
344
+ TEST_SAY(
345
+ "============================================================\n");
346
+
347
+ /* Execute test phases */
348
+ create_share_consumers(config, &state);
349
+ setup_topics_and_produce(config, &state);
350
+ subscribe_consumers(config, &state);
351
+ consume_messages(config, &state);
352
+
353
+ /* Verify results */
354
+ TEST_ASSERT(state.total_consumed == state.total_expected,
355
+ "Expected %d messages, consumed %d", state.total_expected,
356
+ state.total_consumed);
357
+
358
+ /* Build distribution string */
359
+ for (i = 0; i < config->consumer_cnt && pos < 500; i++) {
360
+ pos += rd_snprintf(dist_str + pos, sizeof(dist_str) - pos,
361
+ "c%d=%d ", i, state.per_consumer_count[i]);
362
+ }
363
+
364
+ TEST_SAY("✓ SUCCESS: Consumed %d/%d messages (distribution: %s)\n",
365
+ state.total_consumed, state.total_expected, dist_str);
366
+
367
+ /* Cleanup */
368
+ cleanup_test(config, &state);
369
+
370
+ return 0;
371
+ }
372
+
373
+
374
+ /**
375
+ * @brief Single consumer, single topic, single partition
376
+ */
377
+ static void do_test_single_consumer_single_topic_single_partition(void) {
378
+ share_test_config_t config = {
379
+ .consumer_cnt = 1,
380
+ .topic_cnt = 1,
381
+ .partitions = {1},
382
+ .msgs_per_partition = 1000,
383
+ .group_name = "share-1c-1t-1p",
384
+ .test_name = "Single consumer, single topic, single partition"};
385
+ SUB_TEST();
386
+ run_share_consumer_test(&config);
387
+ SUB_TEST_PASS();
388
+ }
389
+
390
+ /**
391
+ * @brief Single consumer, single topic, multiple partitions
392
+ */
393
+ static void do_test_single_consumer_single_topic_multiple_partitions(void) {
394
+ share_test_config_t config = {
395
+ .consumer_cnt = 1,
396
+ .topic_cnt = 1,
397
+ .partitions = {3},
398
+ .msgs_per_partition = 500,
399
+ .group_name = "share-1c-1t-3p",
400
+ .test_name = "Single consumer, single topic, 3 partitions"};
401
+ SUB_TEST();
402
+ run_share_consumer_test(&config);
403
+ SUB_TEST_PASS();
404
+ }
405
+
406
+ /**
407
+ * @brief Single consumer, multiple topics, single partition each
408
+ */
409
+ static void do_test_single_consumer_multiple_topic_single_partition(void) {
410
+ share_test_config_t config = {
411
+ .consumer_cnt = 1,
412
+ .topic_cnt = 2,
413
+ .partitions = {1, 1},
414
+ .msgs_per_partition = 500,
415
+ .group_name = "share-1c-2t-1p",
416
+ .test_name = "Single consumer, 2 topics, 1 partition each"};
417
+ SUB_TEST();
418
+ run_share_consumer_test(&config);
419
+ SUB_TEST_PASS();
420
+ }
421
+
422
+ /**
423
+ * @brief Single consumer, multiple topics, multiple partitions
424
+ */
425
+ static void do_test_single_consumer_multiple_topic_multiple_partitions(void) {
426
+ share_test_config_t config = {
427
+ .consumer_cnt = 1,
428
+ .topic_cnt = 3,
429
+ .partitions = {2, 2, 2},
430
+ .msgs_per_partition = 500,
431
+ .group_name = "share-1c-3t-2p",
432
+ .test_name = "Single consumer, 3 topics, 2 partitions each"};
433
+ SUB_TEST();
434
+ run_share_consumer_test(&config);
435
+ SUB_TEST_PASS();
436
+ }
437
+
438
+ /**
439
+ * @brief Multiple consumers, single topic, single partition
440
+ */
441
+ static void do_test_multiple_consumers_single_topic_single_partition(void) {
442
+ share_test_config_t config = {
443
+ .consumer_cnt = 2,
444
+ .topic_cnt = 1,
445
+ .partitions = {1},
446
+ .msgs_per_partition = 1000,
447
+ .group_name = "share-2c-1t-1p",
448
+ .test_name = "2 consumers, single topic, single partition"};
449
+ SUB_TEST();
450
+ run_share_consumer_test(&config);
451
+ SUB_TEST_PASS();
452
+ }
453
+
454
+ /**
455
+ * @brief Multiple consumers, single topic, multiple partitions
456
+ */
457
+ static void do_test_multiple_consumers_single_topic_multiple_partitions(void) {
458
+ share_test_config_t config = {
459
+ .consumer_cnt = 2,
460
+ .topic_cnt = 1,
461
+ .partitions = {4},
462
+ .msgs_per_partition = 500,
463
+ .group_name = "share-2c-1t-4p",
464
+ .test_name = "2 consumers, single topic, 4 partitions"};
465
+ SUB_TEST();
466
+ run_share_consumer_test(&config);
467
+ SUB_TEST_PASS();
468
+ }
469
+
470
+ /**
471
+ * @brief Multiple consumers, multiple topics, multiple partitions
472
+ */
473
+ static void
474
+ do_test_multiple_consumers_multiple_topics_multiple_partitions(void) {
475
+ share_test_config_t config = {
476
+ .consumer_cnt = 3,
477
+ .topic_cnt = 2,
478
+ .partitions = {3, 3},
479
+ .msgs_per_partition = 500,
480
+ .group_name = "share-3c-2t-3p",
481
+ .test_name = "3 consumers, 2 topics, 3 partitions each"};
482
+ SUB_TEST();
483
+ run_share_consumer_test(&config);
484
+ SUB_TEST_PASS();
485
+ }
486
+
487
+ /***************************************************************************
488
+ * High Volume Tests
489
+ ***************************************************************************/
490
+
491
+ /**
492
+ * @brief High volume test with 10k messages on single partition
493
+ */
494
+ static void do_test_high_volume_10k_messages(void) {
495
+ share_test_config_t config = {
496
+ .consumer_cnt = 1,
497
+ .topic_cnt = 1,
498
+ .partitions = {1},
499
+ .msgs_per_partition = 10000,
500
+ .group_name = "share-highvol-10k",
501
+ .test_name = "High volume: 10k messages, 1 partition",
502
+ .max_attempts = 100};
503
+ SUB_TEST();
504
+ run_share_consumer_test(&config);
505
+ SUB_TEST_PASS();
506
+ }
507
+
508
+ /**
509
+ * @brief High volume test with 50k messages across 5 partitions
510
+ */
511
+ static void do_test_high_volume_50k_multi_partition(void) {
512
+ share_test_config_t config = {
513
+ .consumer_cnt = 1,
514
+ .topic_cnt = 1,
515
+ .partitions = {5},
516
+ .msgs_per_partition = 10000,
517
+ .group_name = "share-highvol-50k",
518
+ .test_name = "High volume: 50k messages, 5 partitions",
519
+ .max_attempts = 150};
520
+ SUB_TEST();
521
+ run_share_consumer_test(&config);
522
+ SUB_TEST_PASS();
523
+ }
524
+
525
+ /***************************************************************************
526
+ * Multi-Topic Tests (Triggers Multiple Fetch Responses)
527
+ ***************************************************************************/
528
+
529
+ /**
530
+ * @brief 15 topics to trigger multiple ShareFetch responses
531
+ */
532
+ static void do_test_many_topics_15(void) {
533
+ share_test_config_t config = {
534
+ .consumer_cnt = 1,
535
+ .topic_cnt = 15,
536
+ .partitions = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
537
+ .msgs_per_partition = 100,
538
+ .group_name = "share-15topics",
539
+ .test_name = "15 topics, 1 partition each (1500 msgs)",
540
+ .max_attempts = 100};
541
+ SUB_TEST();
542
+ run_share_consumer_test(&config);
543
+ SUB_TEST_PASS();
544
+ }
545
+
546
+ /**
547
+ * @brief 10 topics with 2 partitions each
548
+ */
549
+ static void do_test_many_topics_10_multi_partition(void) {
550
+ share_test_config_t config = {
551
+ .consumer_cnt = 1,
552
+ .topic_cnt = 10,
553
+ .partitions = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
554
+ .msgs_per_partition = 100,
555
+ .group_name = "share-10topics-2p",
556
+ .test_name = "10 topics, 2 partitions each (2000 msgs)",
557
+ .max_attempts = 100};
558
+ SUB_TEST();
559
+ run_share_consumer_test(&config);
560
+ SUB_TEST_PASS();
561
+ }
562
+
563
+ /***************************************************************************
564
+ * Rapid Produce/Consume Tests
565
+ ***************************************************************************/
566
+
567
+ /**
568
+ * @brief Rapid produce/consume cycles - 20 rounds of 500 messages each
569
+ */
570
+ static void do_test_rapid_produce_consume_cycles(void) {
571
+ rd_kafka_share_t *consumer;
572
+ rd_kafka_messages_t *batch = NULL;
573
+ const char *topic;
574
+ const char *group = "share-rapid-cycles";
575
+ rd_kafka_topic_partition_list_t *subs;
576
+ int round, total_consumed = 0;
577
+ const int rounds = 20;
578
+ const int msgs_per_round = 500;
579
+ const int total_expected = rounds * msgs_per_round;
580
+
581
+ SUB_TEST();
582
+
583
+ TEST_SAY("\n");
584
+ TEST_SAY("=== Rapid produce/consume cycles: %d rounds x %d msgs ===\n",
585
+ rounds, msgs_per_round);
586
+
587
+ /* Create consumer and topic */
588
+ consumer = test_create_share_consumer(group, NULL);
589
+ topic = test_mk_topic_name("0171-rapid-cycles", 1);
590
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
591
+
592
+ /* Configure group */
593
+ test_share_set_auto_offset_reset(group, "earliest");
594
+
595
+ /* Subscribe */
596
+ subs = rd_kafka_topic_partition_list_new(1);
597
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
598
+ rd_kafka_share_subscribe(consumer, subs);
599
+ rd_kafka_topic_partition_list_destroy(subs);
600
+
601
+ /* Rapid cycles */
602
+ for (round = 0; round < rounds; round++) {
603
+ int round_consumed = 0;
604
+ int attempts = 100;
605
+
606
+ /* Produce */
607
+ test_produce_msgs_simple(common_producer, topic, 0,
608
+ msgs_per_round);
609
+
610
+ /* Consume */
611
+ while (round_consumed < msgs_per_round && attempts-- > 0) {
612
+ size_t rcvd = 0;
613
+ size_t m;
614
+ rd_kafka_error_t *err;
615
+
616
+ rd_kafka_messages_destroy(batch);
617
+ batch = NULL;
618
+ err = rd_kafka_share_poll(consumer, 1000, &batch);
619
+ if (err) {
620
+ rd_kafka_error_destroy(err);
621
+ continue;
622
+ }
623
+
624
+ rcvd = rd_kafka_messages_count(batch);
625
+ for (m = 0; m < rcvd; m++) {
626
+ rd_kafka_message_t *msg =
627
+ rd_kafka_messages_get(batch, m);
628
+ if (!msg->err)
629
+ round_consumed++;
630
+ }
631
+ }
632
+
633
+ total_consumed += round_consumed;
634
+ TEST_SAY("Round %d: consumed %d/%d (total: %d/%d)\n", round + 1,
635
+ round_consumed, msgs_per_round, total_consumed,
636
+ (round + 1) * msgs_per_round);
637
+ }
638
+
639
+ rd_kafka_messages_destroy(batch);
640
+ batch = NULL;
641
+
642
+ TEST_ASSERT(total_consumed == total_expected,
643
+ "Expected %d messages, consumed %d", total_expected,
644
+ total_consumed);
645
+
646
+ TEST_SAY("SUCCESS: Rapid cycles completed - %d messages\n",
647
+ total_consumed);
648
+
649
+ test_share_consumer_close(consumer);
650
+ test_share_destroy(consumer);
651
+
652
+ SUB_TEST_PASS();
653
+ }
654
+
655
+ /**
656
+ * @brief Empty topic then produce - verify consumer handles transition
657
+ */
658
+ static void do_test_empty_then_produce(void) {
659
+ rd_kafka_share_t *consumer;
660
+ rd_kafka_messages_t *batch = NULL;
661
+ const char *topic;
662
+ const char *group = "share-empty-then-produce";
663
+ rd_kafka_topic_partition_list_t *subs;
664
+ int consumed = 0, attempts;
665
+
666
+ SUB_TEST();
667
+
668
+ TEST_SAY("\n");
669
+ TEST_SAY("=== Empty topic then produce test ===\n");
670
+
671
+ /* Create consumer and empty topic */
672
+ consumer = test_create_share_consumer(group, NULL);
673
+ topic = test_mk_topic_name("0171-empty-then-produce", 1);
674
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
675
+
676
+ /* Configure and subscribe */
677
+ test_share_set_auto_offset_reset(group, "earliest");
678
+ subs = rd_kafka_topic_partition_list_new(1);
679
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
680
+ rd_kafka_share_subscribe(consumer, subs);
681
+ rd_kafka_topic_partition_list_destroy(subs);
682
+
683
+ /* Poll empty topic - should return 0 */
684
+ TEST_SAY("Polling empty topic...\n");
685
+ for (attempts = 0; attempts < 5; attempts++) {
686
+ size_t rcvd = 0;
687
+ rd_kafka_error_t *err;
688
+
689
+ rd_kafka_messages_destroy(batch);
690
+ batch = NULL;
691
+ err = rd_kafka_share_poll(consumer, 1000, &batch);
692
+ if (err)
693
+ rd_kafka_error_destroy(err);
694
+ rcvd = rd_kafka_messages_count(batch);
695
+ TEST_SAY("Empty poll %d: received %zu\n", attempts + 1, rcvd);
696
+ }
697
+
698
+ /* Now produce messages */
699
+ TEST_SAY("Producing 100 messages...\n");
700
+ test_produce_msgs_simple(common_producer, topic, 0, 100);
701
+
702
+ /* Consume - should get messages now */
703
+ attempts = 100;
704
+ while (consumed < 100 && attempts-- > 0) {
705
+ size_t rcvd = 0;
706
+ size_t m;
707
+ rd_kafka_error_t *err;
708
+
709
+ rd_kafka_messages_destroy(batch);
710
+ batch = NULL;
711
+ err = rd_kafka_share_poll(consumer, 1000, &batch);
712
+ if (err) {
713
+ rd_kafka_error_destroy(err);
714
+ continue;
715
+ }
716
+
717
+ rcvd = rd_kafka_messages_count(batch);
718
+ for (m = 0; m < rcvd; m++) {
719
+ rd_kafka_message_t *msg =
720
+ rd_kafka_messages_get(batch, m);
721
+ if (!msg->err)
722
+ consumed++;
723
+ }
724
+ }
725
+
726
+ rd_kafka_messages_destroy(batch);
727
+ batch = NULL;
728
+
729
+ TEST_ASSERT(consumed == 100, "Expected 100 messages, consumed %d",
730
+ consumed);
731
+ TEST_SAY("SUCCESS: Empty then produce - consumed %d messages\n",
732
+ consumed);
733
+
734
+ test_share_consumer_close(consumer);
735
+ test_share_destroy(consumer);
736
+
737
+ SUB_TEST_PASS();
738
+ }
739
+
740
+ /**
741
+ * @brief Sparse partitions - produce only to some partitions
742
+ */
743
+ static void do_test_sparse_partitions(void) {
744
+ rd_kafka_share_t *consumer;
745
+ rd_kafka_messages_t *batch = NULL;
746
+ const char *topic;
747
+ const char *group = "share-sparse-partitions";
748
+ rd_kafka_topic_partition_list_t *subs;
749
+ int consumed = 0, attempts;
750
+ const int msgs_per_partition = 100;
751
+ const int expected = 3 * msgs_per_partition; /* partitions 0,2,4 */
752
+
753
+ SUB_TEST();
754
+
755
+ TEST_SAY("\n");
756
+ TEST_SAY(
757
+ "=== Sparse partitions test (5 partitions, produce to 0,2,4) "
758
+ "===\n");
759
+
760
+ /* Create consumer and topic with 5 partitions */
761
+ consumer = test_create_share_consumer(group, NULL);
762
+ topic = test_mk_topic_name("0171-sparse-partitions", 1);
763
+ test_create_topic_wait_exists(NULL, topic, 5, -1, 60 * 1000);
764
+
765
+ /* Configure and subscribe */
766
+ test_share_set_auto_offset_reset(group, "earliest");
767
+ subs = rd_kafka_topic_partition_list_new(1);
768
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
769
+ rd_kafka_share_subscribe(consumer, subs);
770
+ rd_kafka_topic_partition_list_destroy(subs);
771
+
772
+ /* Produce only to partitions 0, 2, 4 (skip 1, 3) */
773
+ test_produce_msgs_simple(common_producer, topic, 0, msgs_per_partition);
774
+ test_produce_msgs_simple(common_producer, topic, 2, msgs_per_partition);
775
+ test_produce_msgs_simple(common_producer, topic, 4, msgs_per_partition);
776
+
777
+ TEST_SAY("Produced %d messages to partitions 0, 2, 4\n", expected);
778
+
779
+ /* Consume all */
780
+ attempts = 100;
781
+ while (consumed < expected && attempts-- > 0) {
782
+ size_t rcvd = 0;
783
+ size_t m;
784
+ rd_kafka_error_t *err;
785
+
786
+ rd_kafka_messages_destroy(batch);
787
+ batch = NULL;
788
+ err = rd_kafka_share_poll(consumer, 1000, &batch);
789
+ if (err) {
790
+ rd_kafka_error_destroy(err);
791
+ continue;
792
+ }
793
+
794
+ rcvd = rd_kafka_messages_count(batch);
795
+ for (m = 0; m < rcvd; m++) {
796
+ rd_kafka_message_t *msg =
797
+ rd_kafka_messages_get(batch, m);
798
+ if (!msg->err)
799
+ consumed++;
800
+ }
801
+
802
+ TEST_SAY("Progress: %d/%d\n", consumed, expected);
803
+ }
804
+
805
+ rd_kafka_messages_destroy(batch);
806
+ batch = NULL;
807
+
808
+ TEST_ASSERT(consumed == expected, "Expected %d messages, consumed %d",
809
+ expected, consumed);
810
+ TEST_SAY("SUCCESS: Sparse partitions - consumed %d messages\n",
811
+ consumed);
812
+
813
+ test_share_consumer_close(consumer);
814
+ test_share_destroy(consumer);
815
+
816
+ SUB_TEST_PASS();
817
+ }
818
+
819
+
820
+ /**
821
+ * @brief Poll callback - piggybacked acks on ShareFetch.
822
+ *
823
+ * In implicit mode, acks are automatically sent with the next poll().
824
+ * This test verifies that the callback is invoked when acks are
825
+ * piggybacked on ShareFetch responses.
826
+ */
827
+ static void do_test_poll_callback_piggybacked_acks(void) {
828
+ rd_kafka_share_t *consumer;
829
+ rd_kafka_messages_t *batch = NULL;
830
+ const char *topic;
831
+ const char *group = "share-poll-callback";
832
+ rd_kafka_topic_partition_list_t *subs;
833
+ int consumed = 0, attempts;
834
+ test_ack_cb_state_t state = {0};
835
+
836
+ SUB_TEST();
837
+
838
+ TEST_SAY("\n");
839
+ TEST_SAY(
840
+ "=== Poll callback test (piggybacked acks on ShareFetch) ===\n");
841
+
842
+ consumer =
843
+ test_create_share_consumer_with_cb(group, "implicit", &state, NULL);
844
+
845
+ /* Create topic and produce messages */
846
+ topic = test_mk_topic_name("0171-poll-cb", 1);
847
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
848
+ test_produce_msgs_simple(common_producer, topic, 0, 100);
849
+
850
+ /* Configure group */
851
+ test_share_set_auto_offset_reset(group, "earliest");
852
+
853
+ /* Subscribe */
854
+ subs = rd_kafka_topic_partition_list_new(1);
855
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
856
+ rd_kafka_share_subscribe(consumer, subs);
857
+ rd_kafka_topic_partition_list_destroy(subs);
858
+
859
+ /* First poll: consume messages (implicit mode auto-acks) */
860
+ attempts = 50;
861
+ while (consumed < 50 && attempts-- > 0) {
862
+ size_t rcvd = 0;
863
+ size_t m;
864
+ rd_kafka_error_t *err;
865
+
866
+ rd_kafka_messages_destroy(batch);
867
+ batch = NULL;
868
+ err = rd_kafka_share_poll(consumer, 2000, &batch);
869
+ if (err) {
870
+ rd_kafka_error_destroy(err);
871
+ continue;
872
+ }
873
+
874
+ rcvd = rd_kafka_messages_count(batch);
875
+ for (m = 0; m < rcvd; m++) {
876
+ rd_kafka_message_t *msg =
877
+ rd_kafka_messages_get(batch, m);
878
+ if (!msg->err)
879
+ consumed++;
880
+ }
881
+ }
882
+
883
+ rd_kafka_messages_destroy(batch);
884
+ batch = NULL;
885
+
886
+ TEST_SAY("Consumed %d messages\n", consumed);
887
+ TEST_ASSERT(consumed > 0, "Expected to consume some messages");
888
+
889
+ /* Second poll: this sends piggybacked acks from first batch */
890
+ test_wait_for_cb_with_poll(&state, consumer, 1, 20000);
891
+
892
+ TEST_SAY("Callback count=%d, total_offsets=%zu\n", state.callback_cnt,
893
+ state.total_offsets);
894
+
895
+ TEST_ASSERT(
896
+ state.callback_cnt >= 1,
897
+ "Expected at least 1 callback from piggybacked acks, got %d",
898
+ state.callback_cnt);
899
+ TEST_ASSERT(state.total_offsets == (size_t)consumed,
900
+ "Expected %d offsets in callback, got %zu", consumed,
901
+ state.total_offsets);
902
+
903
+ TEST_SAY("SUCCESS: Poll callback received %d callbacks, %zu offsets\n",
904
+ state.callback_cnt, state.total_offsets);
905
+
906
+ /* Cleanup */
907
+ test_share_consumer_close(consumer);
908
+ test_share_destroy(consumer);
909
+ test_ack_cb_state_destroy(&state);
910
+
911
+ SUB_TEST_PASS();
912
+ }
913
+
914
+
915
+ /**
916
+ * @brief Worker-thread argument for the gate-conflict test.
917
+ *
918
+ * @c entering is set to 1 by the worker thread just before it calls
919
+ * share_consume_batch, so the main thread can wait for the worker to
920
+ * have started before probing. (entering=1 doesn't yet prove the gate
921
+ * is held — there's still a window between the atomic flip and the
922
+ * gate acquire inside consume_batch — so the main thread also retries
923
+ * its probe in a short polling loop below.)
924
+ */
925
+ typedef struct {
926
+ rd_kafka_share_t *consumer;
927
+ rd_atomic32_t entering;
928
+ } blocker_arg_t;
929
+
930
+ static int consume_blocker_thread(void *arg) {
931
+ blocker_arg_t *ba = arg;
932
+ rd_kafka_messages_t *batch = NULL;
933
+ rd_kafka_error_t *err;
934
+
935
+ rd_atomic32_set(&ba->entering, 1);
936
+
937
+ /* Blocks for ~5s waiting for messages that never arrive.
938
+ * The share access gate is held for the full duration. */
939
+ err = rd_kafka_share_poll(ba->consumer, 5000, &batch);
940
+ if (err)
941
+ rd_kafka_error_destroy(err);
942
+ rd_kafka_messages_destroy(batch);
943
+ return 0;
944
+ }
945
+
946
+ /**
947
+ * @brief Verify the share consumer's single-thread access gate rejects
948
+ * concurrent calls from a second thread, and that ownership is
949
+ * released after the holder returns so a later call succeeds.
950
+ */
951
+ static void do_test_concurrent_thread_access_rejected(void) {
952
+ rd_kafka_share_t *consumer;
953
+ const char *topic;
954
+ const char *group = "share-concurrent-thread";
955
+ rd_kafka_topic_partition_list_t *subs;
956
+ thrd_t blocker;
957
+ blocker_arg_t ba;
958
+ rd_kafka_messages_t *batch = NULL;
959
+ rd_kafka_error_t *err = NULL;
960
+ int probe_attempt;
961
+ const int probe_max_attempts = 50; /* 50 × 100ms = 5 s budget */
962
+
963
+ SUB_TEST();
964
+
965
+ /* Skip under Valgrind: Valgrind's heavy thread serialization
966
+ * makes the blocker thread's 5s consume_batch timeout
967
+ * unreliable, which can leave thrd_join / share consumer close
968
+ * waiting indefinitely (see logs.txt: subtest ran ~4000s
969
+ * before the suite watchdog killed it). */
970
+ if (!strcmp(test_mode, "valgrind")) {
971
+ SUB_TEST_SKIP(
972
+ "Concurrent-thread gate test is scheduling-sensitive "
973
+ "under Valgrind\n");
974
+ return;
975
+ }
976
+
977
+ TEST_SAY("\n");
978
+ TEST_SAY("=== Concurrent thread access rejected ===\n");
979
+
980
+ consumer = test_create_share_consumer(group, NULL);
981
+ topic = test_mk_topic_name("0171-concurrent-thread", 1);
982
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
983
+
984
+ test_share_set_auto_offset_reset(group, "earliest");
985
+
986
+ subs = rd_kafka_topic_partition_list_new(1);
987
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
988
+ rd_kafka_share_subscribe(consumer, subs);
989
+ rd_kafka_topic_partition_list_destroy(subs);
990
+
991
+ /* Spawn the blocker. It enters consume_batch, takes the gate,
992
+ * and stays inside for ~5s on the empty topic. */
993
+ ba.consumer = consumer;
994
+ rd_atomic32_init(&ba.entering, 0);
995
+ if (thrd_create(&blocker, consume_blocker_thread, &ba) != thrd_success)
996
+ TEST_FAIL("thrd_create failed");
997
+
998
+ /* Wait until the blocker thread has at least reached the point
999
+ * just before its consume_batch call. */
1000
+ while (rd_atomic32_get(&ba.entering) == 0)
1001
+ rd_usleep(10 * 1000, NULL);
1002
+
1003
+ /* The blocker has flipped its `entering` flag but may not yet
1004
+ * have completed the gate acquire inside consume_batch. Probe
1005
+ * in a short polling loop until we either observe __CONFLICT
1006
+ * (gate is held by the blocker — the success case) or exhaust
1007
+ * the budget. Without the loop, slow thread scheduling (e.g.,
1008
+ * Windows CI) lets the main thread win the gate first and the
1009
+ * single-shot assertion flakes. */
1010
+ for (probe_attempt = 0; probe_attempt < probe_max_attempts;
1011
+ probe_attempt++) {
1012
+ rd_kafka_messages_destroy(batch);
1013
+ batch = NULL;
1014
+ err = rd_kafka_share_poll(consumer, 0, &batch);
1015
+ if (err &&
1016
+ rd_kafka_error_code(err) == RD_KAFKA_RESP_ERR__CONFLICT)
1017
+ break; /* observed the conflict — done */
1018
+ if (err) {
1019
+ rd_kafka_error_destroy(err);
1020
+ err = NULL;
1021
+ }
1022
+ rd_usleep(100 * 1000, NULL);
1023
+ }
1024
+
1025
+ TEST_ASSERT(
1026
+ err && rd_kafka_error_code(err) == RD_KAFKA_RESP_ERR__CONFLICT,
1027
+ "Expected __CONFLICT from concurrent share_poll while blocker "
1028
+ "thread holds the gate after %d probe attempts; got: %s",
1029
+ probe_attempt + 1,
1030
+ err ? rd_kafka_err2name(rd_kafka_error_code(err)) : "no error");
1031
+ rd_kafka_error_destroy(err);
1032
+
1033
+ TEST_SAY("Gate correctly rejected concurrent caller\n");
1034
+
1035
+ /* Wait for blocker to return (releases the gate). */
1036
+ thrd_join(blocker, NULL);
1037
+
1038
+ /* Sequential ownership transfer: gate is now free, the same
1039
+ * main thread should be able to call share_poll without
1040
+ * hitting the gate. */
1041
+ rd_kafka_messages_destroy(batch);
1042
+ batch = NULL;
1043
+ err = rd_kafka_share_poll(consumer, 0, &batch);
1044
+ TEST_ASSERT(!err, "After blocker returned, gate should be free; got %s",
1045
+ err ? rd_kafka_err2str(rd_kafka_error_code(err))
1046
+ : "no error");
1047
+ if (err)
1048
+ rd_kafka_error_destroy(err);
1049
+ rd_kafka_messages_destroy(batch);
1050
+ batch = NULL;
1051
+
1052
+ TEST_SAY("Sequential ownership transfer works after release\n");
1053
+
1054
+ test_share_consumer_close(consumer);
1055
+ test_share_destroy(consumer);
1056
+
1057
+ TEST_SAY("SUCCESS: Concurrent thread access correctly rejected\n");
1058
+
1059
+ SUB_TEST_PASS();
1060
+ }
1061
+
1062
+
1063
+ /**
1064
+ * @brief Test that record headers are preserved end-to-end through the
1065
+ * share consumer.
1066
+ *
1067
+ * Produce a single record with three headers and verify the share consumer
1068
+ * receives all of them with the correct values.
1069
+ */
1070
+ static void do_test_headers_preserved(void) {
1071
+ rd_kafka_share_t *consumer;
1072
+ rd_kafka_messages_t *batch = NULL;
1073
+ rd_kafka_message_t *msg0;
1074
+ const char *topic;
1075
+ const char *group = "share-headers";
1076
+ rd_kafka_topic_partition_list_t *subs;
1077
+ rd_kafka_resp_err_t err;
1078
+ rd_kafka_headers_t *hdrs = NULL;
1079
+ rd_kafka_resp_err_t herr;
1080
+ const void *val;
1081
+ size_t vsz;
1082
+ size_t rcvd = 0;
1083
+ int attempts;
1084
+
1085
+ SUB_TEST();
1086
+
1087
+ TEST_SAY("\n");
1088
+ TEST_SAY("=== Headers preserved through share consumer ===\n");
1089
+
1090
+ consumer = test_create_share_consumer(group, NULL);
1091
+ topic = test_mk_topic_name("0171-headers", 1);
1092
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
1093
+
1094
+ /* Produce a single record with three headers */
1095
+ err = rd_kafka_producev(
1096
+ common_producer, RD_KAFKA_V_TOPIC(topic),
1097
+ RD_KAFKA_V_KEY("hdr-key", 7), RD_KAFKA_V_VALUE("hdr-value", 9),
1098
+ RD_KAFKA_V_HEADER("h1", "v1", 2), RD_KAFKA_V_HEADER("h2", "v2", 2),
1099
+ RD_KAFKA_V_HEADER("h3", "v3", 2), RD_KAFKA_V_END);
1100
+ TEST_ASSERT(!err, "producev failed: %s", rd_kafka_err2name(err));
1101
+ rd_kafka_flush(common_producer, 10 * 1000);
1102
+
1103
+ test_share_set_auto_offset_reset(group, "earliest");
1104
+
1105
+ subs = rd_kafka_topic_partition_list_new(1);
1106
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1107
+ rd_kafka_share_subscribe(consumer, subs);
1108
+ rd_kafka_topic_partition_list_destroy(subs);
1109
+
1110
+ attempts = 30;
1111
+ while (rcvd < 1 && attempts-- > 0) {
1112
+ rd_kafka_error_t *e;
1113
+
1114
+ rd_kafka_messages_destroy(batch);
1115
+ batch = NULL;
1116
+ e = rd_kafka_share_poll(consumer, 2000, &batch);
1117
+ if (e)
1118
+ rd_kafka_error_destroy(e);
1119
+ rcvd = rd_kafka_messages_count(batch);
1120
+ }
1121
+ TEST_ASSERT(rcvd == 1, "Expected 1 record, got %zu", rcvd);
1122
+
1123
+ msg0 = rd_kafka_messages_get(batch, 0);
1124
+ herr = rd_kafka_message_headers(msg0, &hdrs);
1125
+ TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR && hdrs,
1126
+ "Failed to get headers: %s", rd_kafka_err2name(herr));
1127
+
1128
+ herr = rd_kafka_header_get_last(hdrs, "h1", &val, &vsz);
1129
+ TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR, "h1 not present: %s",
1130
+ rd_kafka_err2name(herr));
1131
+ TEST_ASSERT(vsz == 2 && memcmp(val, "v1", 2) == 0, "h1 value mismatch");
1132
+
1133
+ herr = rd_kafka_header_get_last(hdrs, "h2", &val, &vsz);
1134
+ TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR, "h2 not present: %s",
1135
+ rd_kafka_err2name(herr));
1136
+ TEST_ASSERT(vsz == 2 && memcmp(val, "v2", 2) == 0, "h2 value mismatch");
1137
+
1138
+ herr = rd_kafka_header_get_last(hdrs, "h3", &val, &vsz);
1139
+ TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR, "h3 not present: %s",
1140
+ rd_kafka_err2name(herr));
1141
+ TEST_ASSERT(vsz == 2 && memcmp(val, "v3", 2) == 0, "h3 value mismatch");
1142
+
1143
+ rd_kafka_messages_destroy(batch);
1144
+ batch = NULL;
1145
+
1146
+ test_share_consumer_close(consumer);
1147
+ test_share_destroy(consumer);
1148
+
1149
+ TEST_SAY("SUCCESS: Headers preserved through share consumer\n");
1150
+
1151
+ SUB_TEST_PASS();
1152
+ }
1153
+
1154
+
1155
+ /**
1156
+ * @brief Test that a share consumer with a small session-level fetch.max.bytes
1157
+ * receives records one by one per fetch.
1158
+ *
1159
+ * - With fetch.max.bytes=1500 and 1000-byte records each in their own
1160
+ * RecordBatch (custom producer, batch.num.messages=1), every
1161
+ * ShareFetch response can hold at most one record.
1162
+ * - All 100 records are received (consumer correctly loops across
1163
+ * multiple ShareFetch RPCs).
1164
+ * - Every non-empty consume_batch returns exactly one record (one batch
1165
+ * per ShareFetch response, due to the cap).
1166
+ */
1167
+ static void do_test_fetch_max_bytes_small(void) {
1168
+ const char *group = "share-small-fetch";
1169
+ const char *topic;
1170
+ rd_kafka_share_t *consumer;
1171
+ rd_kafka_conf_t *conf;
1172
+ rd_kafka_conf_t *prod_conf;
1173
+ rd_kafka_t *prod;
1174
+ rd_kafka_topic_partition_list_t *subs;
1175
+ char errstr[512];
1176
+ const int msgcnt = 10;
1177
+ const int msgsize = 1000;
1178
+ int consumed = 0;
1179
+ int attempts;
1180
+
1181
+ SUB_TEST();
1182
+
1183
+ TEST_SAY("\n");
1184
+ TEST_SAY(
1185
+ "=== Small session-level fetch.max.bytes (1500) with 1000-byte "
1186
+ "msgs ===\n");
1187
+
1188
+ topic = test_mk_topic_name("0171-small-fetch", 1);
1189
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
1190
+
1191
+ /* Custom producer that emits one RecordBatch per message so the
1192
+ * broker has many small batches to choose from when responding to
1193
+ * ShareFetch */
1194
+ test_conf_init(&prod_conf, NULL, 60);
1195
+ rd_kafka_conf_set_dr_msg_cb(prod_conf, test_dr_msg_cb);
1196
+ rd_kafka_conf_set(prod_conf, "linger.ms", "0", errstr, sizeof(errstr));
1197
+ rd_kafka_conf_set(prod_conf, "batch.num.messages", "1", errstr,
1198
+ sizeof(errstr));
1199
+ prod = test_create_handle(RD_KAFKA_PRODUCER, prod_conf);
1200
+
1201
+ test_produce_msgs2(prod, topic, 0, 0, 0, msgcnt, NULL, msgsize);
1202
+ rd_kafka_flush(prod, 30 * 1000);
1203
+ rd_kafka_destroy(prod);
1204
+
1205
+ /* Create share consumer with a small session-level fetch cap.
1206
+ * For share consumers `message.max.bytes` does not floor
1207
+ * `fetch.max.bytes` (and is rejected), so set `fetch.max.bytes`
1208
+ * directly to 1500. */
1209
+ test_conf_init(&conf, NULL, 60);
1210
+ rd_kafka_conf_set(conf, "group.id", group, errstr, sizeof(errstr));
1211
+ rd_kafka_conf_set(conf, "fetch.max.bytes", "1500", errstr,
1212
+ sizeof(errstr));
1213
+
1214
+ consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
1215
+ TEST_ASSERT(consumer, "Failed to create share consumer: %s", errstr);
1216
+
1217
+ test_share_set_auto_offset_reset(group, "earliest");
1218
+
1219
+ subs = rd_kafka_topic_partition_list_new(1);
1220
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1221
+ rd_kafka_share_subscribe(consumer, subs);
1222
+ rd_kafka_topic_partition_list_destroy(subs);
1223
+
1224
+ /* All 100 records must arrive one by one per fetch */
1225
+ attempts = 30;
1226
+ while (consumed < msgcnt && attempts-- > 0) {
1227
+ int batch_cnt = 0;
1228
+ test_share_poll(consumer, 2000, NULL, 0, &batch_cnt);
1229
+ consumed += batch_cnt;
1230
+ if (batch_cnt > 0) {
1231
+ TEST_ASSERT(batch_cnt == 1,
1232
+ "Expected 1 record per batch with small "
1233
+ "fetch.max.bytes, "
1234
+ "got %d",
1235
+ batch_cnt);
1236
+ }
1237
+ }
1238
+
1239
+ TEST_ASSERT(consumed == msgcnt,
1240
+ "Expected %d records with small fetch.max.bytes, "
1241
+ "consumed %d",
1242
+ msgcnt, consumed);
1243
+
1244
+ TEST_SAY("SUCCESS: small fetch.max.bytes - consumed %d records\n",
1245
+ consumed);
1246
+
1247
+ test_share_consumer_close(consumer);
1248
+ test_share_destroy(consumer);
1249
+
1250
+ SUB_TEST_PASS();
1251
+ }
1252
+
1253
+
1254
+ /**
1255
+ * @brief Verify the share consumer receives records that individually
1256
+ * exceed fetch.max.bytes, the broker doesnt stall consumption.
1257
+ *
1258
+ * What is tested:
1259
+ * - Produce a mix of 10 KB and 500 KB records on a topic.
1260
+ * - Configure the consumer with fetch.max.bytes=1500 — well below every
1261
+ * record's on-wire size.
1262
+ * - All records must still arrive. The broker is required to return the
1263
+ * first batch in a non-empty partition even when it exceeds the cap
1264
+ * to guarantee consumer progress; the client side must accept these
1265
+ * oversized responses.
1266
+ */
1267
+ static void do_test_record_larger_than_fetch_max_bytes(void) {
1268
+ const char *group = "share-large-record";
1269
+ const char *topic;
1270
+ rd_kafka_share_t *consumer;
1271
+ rd_kafka_conf_t *conf;
1272
+ rd_kafka_topic_partition_list_t *subs;
1273
+ char errstr[512];
1274
+ const int MAX_BYTES = 100000;
1275
+ const int small_cnt = 5;
1276
+ const int large_cnt = 2;
1277
+ const int total = small_cnt + large_cnt;
1278
+ int consumed = 0;
1279
+ int attempts;
1280
+
1281
+ SUB_TEST();
1282
+
1283
+ TEST_SAY("\n");
1284
+ TEST_SAY("=== Records larger than fetch.message.max.bytes (%d) ===\n",
1285
+ MAX_BYTES);
1286
+
1287
+ topic = test_mk_topic_name("0171-large-record", 1);
1288
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
1289
+
1290
+ /* Produce small messages then large ones */
1291
+ test_produce_msgs2(common_producer, topic, 0, 0, 0, small_cnt, NULL,
1292
+ MAX_BYTES / 10);
1293
+ test_produce_msgs2(common_producer, topic, 0, 0, small_cnt, large_cnt,
1294
+ NULL, MAX_BYTES * 5);
1295
+ rd_kafka_flush(common_producer, 60 * 1000);
1296
+
1297
+ test_conf_init(&conf, NULL, 60);
1298
+ rd_kafka_conf_set(conf, "group.id", group, errstr, sizeof(errstr));
1299
+ rd_kafka_conf_set(conf, "fetch.max.bytes", "1500", errstr,
1300
+ sizeof(errstr));
1301
+
1302
+ consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
1303
+ TEST_ASSERT(consumer, "Failed to create share consumer: %s", errstr);
1304
+
1305
+ test_share_set_auto_offset_reset(group, "earliest");
1306
+
1307
+ subs = rd_kafka_topic_partition_list_new(1);
1308
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1309
+ rd_kafka_share_subscribe(consumer, subs);
1310
+ rd_kafka_topic_partition_list_destroy(subs);
1311
+
1312
+ attempts = 30;
1313
+ while (consumed < total && attempts-- > 0) {
1314
+ int batch_cnt = 0;
1315
+ test_share_poll(consumer, 3000, NULL, 0, &batch_cnt);
1316
+ consumed += batch_cnt;
1317
+ if (batch_cnt > 0)
1318
+ TEST_SAY("Progress: %d/%d\n", consumed, total);
1319
+ }
1320
+
1321
+ TEST_ASSERT(consumed == total,
1322
+ "Expected %d records (incl. oversized), consumed %d", total,
1323
+ consumed);
1324
+
1325
+ TEST_SAY("SUCCESS: oversized record - consumed %d records\n", consumed);
1326
+
1327
+ test_share_consumer_close(consumer);
1328
+ test_share_destroy(consumer);
1329
+
1330
+ SUB_TEST_PASS();
1331
+ }
1332
+
1333
+
1334
+ /**
1335
+ * @brief Test that records held by a consumer that closes mid-flight
1336
+ * are released back to other consumers in the group.
1337
+ *
1338
+ * Create 4 share consumers in the same group, produce a known set of
1339
+ * records, have consumer 0 fetch a batch without acknowledging and then
1340
+ * close. The other consumers should eventually receive every record
1341
+ * (including the ones released by consumer 0).
1342
+ */
1343
+ static void do_test_consumer_close_releases_to_group(void) {
1344
+ const char *group = "share-close-releases";
1345
+ const char *topic;
1346
+ rd_kafka_share_t *consumers[4];
1347
+ rd_kafka_topic_partition_list_t *subs;
1348
+ rd_kafka_messages_t *batch = NULL;
1349
+ const int total_msgs = 200;
1350
+ int c0_fetched = 0;
1351
+ int total_consumed = 0;
1352
+ int per_consumer[4] = {0};
1353
+ int attempts;
1354
+ int i;
1355
+
1356
+ SUB_TEST();
1357
+
1358
+ TEST_SAY("\n");
1359
+ TEST_SAY(
1360
+ "=== Failing consumer releases unacked records to group ===\n");
1361
+
1362
+ topic = test_mk_topic_name("0171-close-release", 1);
1363
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
1364
+ test_produce_msgs_simple(common_producer, topic, 0, total_msgs);
1365
+
1366
+ test_share_set_auto_offset_reset(group, "earliest");
1367
+
1368
+ for (i = 0; i < 4; i++)
1369
+ consumers[i] = test_create_share_consumer(group, NULL);
1370
+
1371
+ subs = rd_kafka_topic_partition_list_new(1);
1372
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1373
+ for (i = 0; i < 4; i++)
1374
+ rd_kafka_share_subscribe(consumers[i], subs);
1375
+ rd_kafka_topic_partition_list_destroy(subs);
1376
+
1377
+ /* Consumer 0 fetches a batch WITHOUT acknowledging, then closes. */
1378
+ attempts = 30;
1379
+ while (c0_fetched == 0 && attempts-- > 0) {
1380
+ size_t rcvd = 0;
1381
+ size_t m;
1382
+ rd_kafka_error_t *err;
1383
+
1384
+ rd_kafka_messages_destroy(batch);
1385
+ batch = NULL;
1386
+ err = rd_kafka_share_poll(consumers[0], 2000, &batch);
1387
+ if (err) {
1388
+ rd_kafka_error_destroy(err);
1389
+ continue;
1390
+ }
1391
+
1392
+ rcvd = rd_kafka_messages_count(batch);
1393
+ for (m = 0; m < rcvd; m++) {
1394
+ rd_kafka_message_t *msg =
1395
+ rd_kafka_messages_get(batch, m);
1396
+ if (!msg->err)
1397
+ c0_fetched++;
1398
+ }
1399
+ }
1400
+
1401
+ rd_kafka_messages_destroy(batch);
1402
+ batch = NULL;
1403
+ TEST_ASSERT(c0_fetched > 0,
1404
+ "Consumer 0 should have fetched some records, got %d",
1405
+ c0_fetched);
1406
+
1407
+ TEST_SAY("Consumer 0 fetched %d records (unacked), closing now\n",
1408
+ c0_fetched);
1409
+ /* Close without acknowledging - records should be released */
1410
+ test_share_consumer_close(consumers[0]);
1411
+ test_share_destroy(consumers[0]);
1412
+ consumers[0] = NULL;
1413
+
1414
+ /* Drain the rest with consumers 1..3 */
1415
+ attempts = 200;
1416
+ while (total_consumed < total_msgs && attempts-- > 0) {
1417
+ for (i = 1; i < 4; i++) {
1418
+ size_t rcvd = 0;
1419
+ size_t m;
1420
+ rd_kafka_error_t *err;
1421
+
1422
+ rd_kafka_messages_destroy(batch);
1423
+ batch = NULL;
1424
+ err = rd_kafka_share_poll(consumers[i], 1000, &batch);
1425
+ if (err) {
1426
+ rd_kafka_error_destroy(err);
1427
+ continue;
1428
+ }
1429
+
1430
+ rcvd = rd_kafka_messages_count(batch);
1431
+ for (m = 0; m < rcvd; m++) {
1432
+ rd_kafka_message_t *msg =
1433
+ rd_kafka_messages_get(batch, m);
1434
+ if (!msg->err) {
1435
+ per_consumer[i]++;
1436
+ total_consumed++;
1437
+ }
1438
+ }
1439
+ }
1440
+ }
1441
+
1442
+ rd_kafka_messages_destroy(batch);
1443
+ batch = NULL;
1444
+
1445
+ TEST_SAY(
1446
+ "Distribution: c0=%d (lost on close), c1=%d, c2=%d, c3=%d "
1447
+ "(total %d)\n",
1448
+ c0_fetched, per_consumer[1], per_consumer[2], per_consumer[3],
1449
+ total_consumed);
1450
+
1451
+ TEST_ASSERT(total_consumed == total_msgs,
1452
+ "Expected exactly %d records on the survivors after "
1453
+ "consumer 0's records were released, got %d",
1454
+ total_msgs, total_consumed);
1455
+
1456
+ for (i = 1; i < 4; i++) {
1457
+ test_share_consumer_close(consumers[i]);
1458
+ test_share_destroy(consumers[i]);
1459
+ }
1460
+
1461
+ TEST_SAY("SUCCESS: closed consumer's records redelivered to group\n");
1462
+
1463
+ SUB_TEST_PASS();
1464
+ }
1465
+
1466
+
1467
+ /**
1468
+ * @brief Partition count increased during consume.
1469
+ *
1470
+ * 1 consumer, topic starts with 2 partitions, grown to 5.
1471
+ * Produce N records to {0,1} -> consume -> grow to 5 -> produce N to
1472
+ * {2,3,4} -> continue consuming -> expect 5*N total.
1473
+ */
1474
+ static void do_test_partition_increase_during_consume(void) {
1475
+ const char *group = "share-partition-increase";
1476
+ const char *topic;
1477
+ rd_kafka_share_t *consumer;
1478
+ rd_kafka_conf_t *conf;
1479
+ rd_kafka_conf_t *prod_conf;
1480
+ rd_kafka_t *producer;
1481
+ rd_kafka_topic_partition_list_t *subs;
1482
+ rd_kafka_messages_t *batch = NULL;
1483
+ const int msgs_per_partition = 200;
1484
+ const int initial_partitions = 2;
1485
+ const int grown_partitions = 5;
1486
+ const int initial_expected = initial_partitions * msgs_per_partition;
1487
+ const int total_expected = grown_partitions * msgs_per_partition;
1488
+ int consumed = 0;
1489
+ int attempts;
1490
+ int p;
1491
+ rd_kafka_resp_err_t cp_err;
1492
+ char errstr[512];
1493
+
1494
+ SUB_TEST();
1495
+
1496
+ TEST_SAY("\n");
1497
+ TEST_SAY("=== Partition count increase during consume (%d -> %d) ===\n",
1498
+ initial_partitions, grown_partitions);
1499
+
1500
+ topic = test_mk_topic_name("0171-partition-increase", 1);
1501
+ test_create_topic_wait_exists(NULL, topic, initial_partitions, -1,
1502
+ 60 * 1000);
1503
+
1504
+ /* Custom producer with a short metadata refresh interval so its
1505
+ * cache picks up the post-CreatePartitions partition count on its
1506
+ * own — avoids the explicit test_wait_metadata_update probe. */
1507
+ test_conf_init(&prod_conf, NULL, 60);
1508
+ rd_kafka_conf_set_dr_msg_cb(prod_conf, test_dr_msg_cb);
1509
+ rd_kafka_conf_set(prod_conf, "topic.metadata.refresh.interval.ms",
1510
+ "500", errstr, sizeof(errstr));
1511
+ producer = test_create_handle(RD_KAFKA_PRODUCER, prod_conf);
1512
+
1513
+ /* Produce to initial partitions only */
1514
+ for (p = 0; p < initial_partitions; p++)
1515
+ test_produce_msgs_simple(producer, topic, p,
1516
+ msgs_per_partition);
1517
+
1518
+ /* Lower the consumer's metadata refresh interval (default 5 min) so
1519
+ * it picks up the new partition count quickly without a long
1520
+ * hardcoded sleep below. */
1521
+ test_conf_init(&conf, NULL, 60);
1522
+ rd_kafka_conf_set(conf, "group.id", group, errstr, sizeof(errstr));
1523
+ rd_kafka_conf_set(conf, "topic.metadata.refresh.interval.ms", "500",
1524
+ errstr, sizeof(errstr));
1525
+ consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
1526
+ TEST_ASSERT(consumer, "Failed to create share consumer: %s", errstr);
1527
+ test_share_set_auto_offset_reset(group, "earliest");
1528
+
1529
+ subs = rd_kafka_topic_partition_list_new(1);
1530
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1531
+ rd_kafka_share_subscribe(consumer, subs);
1532
+ rd_kafka_topic_partition_list_destroy(subs);
1533
+
1534
+ /* Consume from the initial partitions */
1535
+ attempts = 100;
1536
+ while (consumed < initial_expected && attempts-- > 0) {
1537
+ size_t rcvd = 0;
1538
+ size_t m;
1539
+ rd_kafka_error_t *err;
1540
+
1541
+ rd_kafka_messages_destroy(batch);
1542
+ batch = NULL;
1543
+ err = rd_kafka_share_poll(consumer, 1000, &batch);
1544
+ if (err) {
1545
+ rd_kafka_error_destroy(err);
1546
+ continue;
1547
+ }
1548
+
1549
+ rcvd = rd_kafka_messages_count(batch);
1550
+ for (m = 0; m < rcvd; m++) {
1551
+ rd_kafka_message_t *msg =
1552
+ rd_kafka_messages_get(batch, m);
1553
+ if (!msg->err)
1554
+ consumed++;
1555
+ }
1556
+ }
1557
+ TEST_SAY("Pre-grow consumed: %d/%d\n", consumed, initial_expected);
1558
+ TEST_ASSERT(consumed == initial_expected,
1559
+ "Expected >= %d msgs from initial partitions, got %d",
1560
+ initial_expected, consumed);
1561
+
1562
+ /* Grow topic partitions from 2 -> 5 */
1563
+ TEST_SAY("Growing partitions to %d via CreatePartitions\n",
1564
+ grown_partitions);
1565
+ cp_err = test_CreatePartitions_simple(common_admin, NULL, topic,
1566
+ grown_partitions, NULL);
1567
+ TEST_ASSERT(!cp_err, "CreatePartitions failed: %s",
1568
+ rd_kafka_err2str(cp_err));
1569
+
1570
+ /* Both producer and consumer have topic.metadata.refresh.interval
1571
+ * .ms=500, so their local metadata caches pick up the new partition
1572
+ * count within ~500 ms via the background refresh. One second is
1573
+ * plenty for both: the producer can route to the new partitions,
1574
+ * and the consumer can be assigned them on the next
1575
+ * ShareGroupHeartbeat cycle. */
1576
+ rd_sleep(1);
1577
+
1578
+ /* Produce to the newly added partitions */
1579
+ for (p = initial_partitions; p < grown_partitions; p++)
1580
+ test_produce_msgs_simple(producer, topic, p,
1581
+ msgs_per_partition);
1582
+
1583
+ /* Continue consuming, expecting records from new partitions */
1584
+ attempts = 200;
1585
+ while (consumed < total_expected && attempts-- > 0) {
1586
+ size_t rcvd = 0;
1587
+ size_t m;
1588
+ rd_kafka_error_t *err;
1589
+
1590
+ rd_kafka_messages_destroy(batch);
1591
+ batch = NULL;
1592
+ err = rd_kafka_share_poll(consumer, 1000, &batch);
1593
+ if (err) {
1594
+ rd_kafka_error_destroy(err);
1595
+ continue;
1596
+ }
1597
+
1598
+ rcvd = rd_kafka_messages_count(batch);
1599
+ for (m = 0; m < rcvd; m++) {
1600
+ rd_kafka_message_t *msg =
1601
+ rd_kafka_messages_get(batch, m);
1602
+ if (!msg->err)
1603
+ consumed++;
1604
+ }
1605
+ }
1606
+
1607
+ rd_kafka_messages_destroy(batch);
1608
+ batch = NULL;
1609
+
1610
+ TEST_ASSERT(consumed == total_expected,
1611
+ "Expected exactly %d total records after partition grow, "
1612
+ "got %d",
1613
+ total_expected, consumed);
1614
+
1615
+ TEST_SAY("SUCCESS: partition count grow %d->%d, consumed %d records\n",
1616
+ initial_partitions, grown_partitions, consumed);
1617
+
1618
+ test_share_consumer_close(consumer);
1619
+ test_share_destroy(consumer);
1620
+ rd_kafka_destroy(producer);
1621
+
1622
+ SUB_TEST_PASS();
1623
+ }
1624
+
1625
+
1626
+ /**
1627
+ * @brief Zero-byte payload and zero-byte key.
1628
+ *
1629
+ * Verifies that the share consumer correctly preserves zero-length
1630
+ * key/value buffers (len == 0, pointer non-NULL) and NULL key/value.
1631
+ */
1632
+ static void do_test_zero_byte_payload_and_key(void) {
1633
+ const char *group = "share-zero-byte";
1634
+ const char *topic;
1635
+ rd_kafka_share_t *consumer;
1636
+ rd_kafka_topic_partition_list_t *subs;
1637
+ rd_kafka_messages_t *batch = NULL;
1638
+ rd_kafka_resp_err_t err;
1639
+ size_t rcvd = 0;
1640
+ int attempts;
1641
+ int saw_empty_kv = 0;
1642
+ int saw_key_only = 0;
1643
+ int saw_value_only = 0;
1644
+ int saw_null_kv = 0;
1645
+ size_t i;
1646
+ rd_bool_t key_null;
1647
+ rd_bool_t key_empty;
1648
+ rd_bool_t val_null;
1649
+ rd_bool_t val_empty;
1650
+ rd_bool_t key_is_empty_or_null;
1651
+ rd_bool_t val_is_empty_or_null;
1652
+
1653
+ SUB_TEST();
1654
+
1655
+ TEST_SAY("\n");
1656
+ TEST_SAY("=== Zero-byte payload and key ===\n");
1657
+
1658
+ consumer = test_create_share_consumer(group, NULL);
1659
+ topic = test_mk_topic_name("0171-zero-byte", 1);
1660
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
1661
+
1662
+ /* Empty key, empty value (zero-length, non-NULL pointers) */
1663
+ err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
1664
+ RD_KAFKA_V_KEY("", 0), RD_KAFKA_V_VALUE("", 0),
1665
+ RD_KAFKA_V_END);
1666
+ TEST_ASSERT(!err, "produce empty kv failed: %s",
1667
+ rd_kafka_err2name(err));
1668
+
1669
+ /* Non-empty key, empty value */
1670
+ err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
1671
+ RD_KAFKA_V_KEY("k", 1), RD_KAFKA_V_VALUE("", 0),
1672
+ RD_KAFKA_V_END);
1673
+ TEST_ASSERT(!err, "produce key-only failed: %s",
1674
+ rd_kafka_err2name(err));
1675
+
1676
+ /* Empty key, non-empty value */
1677
+ err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
1678
+ RD_KAFKA_V_KEY("", 0), RD_KAFKA_V_VALUE("v", 1),
1679
+ RD_KAFKA_V_END);
1680
+ TEST_ASSERT(!err, "produce value-only failed: %s",
1681
+ rd_kafka_err2name(err));
1682
+
1683
+ /* NULL key, NULL value */
1684
+ err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
1685
+ RD_KAFKA_V_KEY(NULL, 0),
1686
+ RD_KAFKA_V_VALUE(NULL, 0), RD_KAFKA_V_END);
1687
+ TEST_ASSERT(!err, "produce null kv failed: %s", rd_kafka_err2name(err));
1688
+
1689
+ rd_kafka_flush(common_producer, 10 * 1000);
1690
+
1691
+ test_share_set_auto_offset_reset(group, "earliest");
1692
+
1693
+ subs = rd_kafka_topic_partition_list_new(1);
1694
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1695
+ rd_kafka_share_subscribe(consumer, subs);
1696
+ rd_kafka_topic_partition_list_destroy(subs);
1697
+
1698
+ attempts = 30;
1699
+ while (rcvd < 4 && attempts-- > 0) {
1700
+ size_t batch_rcvd = 0;
1701
+ rd_kafka_error_t *e;
1702
+
1703
+ rd_kafka_messages_destroy(batch);
1704
+ batch = NULL;
1705
+ e = rd_kafka_share_poll(consumer, 2000, &batch);
1706
+ if (e)
1707
+ rd_kafka_error_destroy(e);
1708
+ batch_rcvd = rd_kafka_messages_count(batch);
1709
+
1710
+ for (i = 0; i < batch_rcvd; i++) {
1711
+ rd_kafka_message_t *msg =
1712
+ rd_kafka_messages_get(batch, i);
1713
+
1714
+ key_null = msg->key == NULL;
1715
+ key_empty = msg->key_len == 0 && !key_null;
1716
+ val_null = msg->payload == NULL;
1717
+ val_empty = msg->len == 0 && !val_null;
1718
+
1719
+ /* Brokers may normalize empty buffers to NULL; accept
1720
+ * either. */
1721
+ key_is_empty_or_null = key_null || key_empty;
1722
+ val_is_empty_or_null = val_null || val_empty;
1723
+
1724
+ if (key_is_empty_or_null && val_is_empty_or_null) {
1725
+ /* Could be "empty kv" or "null kv" record -
1726
+ * count both as the "no content" class. */
1727
+ if (key_null && val_null)
1728
+ saw_null_kv++;
1729
+ else
1730
+ saw_empty_kv++;
1731
+ } else if (!key_is_empty_or_null &&
1732
+ val_is_empty_or_null) {
1733
+ TEST_ASSERT(msg->key_len == 1 &&
1734
+ memcmp(msg->key, "k", 1) == 0,
1735
+ "key-only record: key mismatch");
1736
+ saw_key_only++;
1737
+ } else if (key_is_empty_or_null &&
1738
+ !val_is_empty_or_null) {
1739
+ TEST_ASSERT(
1740
+ msg->len == 1 &&
1741
+ memcmp(msg->payload, "v", 1) == 0,
1742
+ "value-only record: value mismatch");
1743
+ saw_value_only++;
1744
+ }
1745
+ }
1746
+ rcvd += batch_rcvd;
1747
+ }
1748
+
1749
+ rd_kafka_messages_destroy(batch);
1750
+ batch = NULL;
1751
+
1752
+ TEST_ASSERT(rcvd == 4, "Expected 4 records, got %zu", rcvd);
1753
+
1754
+ TEST_SAY("Counts: empty_kv=%d null_kv=%d key_only=%d value_only=%d\n",
1755
+ saw_empty_kv, saw_null_kv, saw_key_only, saw_value_only);
1756
+
1757
+ TEST_ASSERT(saw_empty_kv + saw_null_kv == 2,
1758
+ "Expected 2 'no content' records, got %d",
1759
+ saw_empty_kv + saw_null_kv);
1760
+ TEST_ASSERT(saw_key_only == 1, "Expected 1 key-only record, got %d",
1761
+ saw_key_only);
1762
+ TEST_ASSERT(saw_value_only == 1, "Expected 1 value-only record, got %d",
1763
+ saw_value_only);
1764
+
1765
+ test_share_consumer_close(consumer);
1766
+ test_share_destroy(consumer);
1767
+
1768
+ TEST_SAY("SUCCESS: zero-byte payload/key preserved\n");
1769
+
1770
+ SUB_TEST_PASS();
1771
+ }
1772
+
1773
+
1774
+ /**
1775
+ * @brief Consume + ack a round of messages produced with the given
1776
+ * compression codec.
1777
+ */
1778
+ static void do_test_compression_codec(const char *codec) {
1779
+ const char *group_prefix = "share-codec-";
1780
+ char group[128];
1781
+ const char *topic;
1782
+ rd_kafka_share_t *consumer;
1783
+ rd_kafka_conf_t *conf;
1784
+ rd_kafka_t *producer;
1785
+ rd_kafka_topic_partition_list_t *subs;
1786
+ rd_kafka_messages_t *batch = NULL;
1787
+ const int msg_cnt = 200;
1788
+ const int msg_sz = 256;
1789
+ int consumed = 0;
1790
+ int attempts;
1791
+
1792
+ TEST_SAY("\n");
1793
+ TEST_SAY("=== Compression codec: %s ===\n", codec);
1794
+
1795
+ rd_snprintf(group, sizeof(group), "%s%s", group_prefix, codec);
1796
+ topic = test_mk_topic_name(codec, 1);
1797
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
1798
+
1799
+ /* Producer configured with the requested compression codec.
1800
+ * test_produce_msgs2 → test_wait_delivery uses a counter that's
1801
+ * decremented inside test_dr_msg_cb, so the dr callback must
1802
+ * be wired up explicitly when we create our own producer (only
1803
+ * test_create_producer() sets it for us). */
1804
+ test_conf_init(&conf, NULL, 60);
1805
+ test_conf_set(conf, "compression.type", codec);
1806
+ rd_kafka_conf_set_dr_msg_cb(conf, test_dr_msg_cb);
1807
+ producer = test_create_handle(RD_KAFKA_PRODUCER, conf);
1808
+
1809
+ test_produce_msgs2(producer, topic, 0, 0, 0, msg_cnt, NULL, msg_sz);
1810
+ rd_kafka_flush(producer, 30 * 1000);
1811
+ rd_kafka_destroy(producer);
1812
+
1813
+ consumer = test_create_share_consumer(group, NULL);
1814
+ test_share_set_auto_offset_reset(group, "earliest");
1815
+
1816
+ subs = rd_kafka_topic_partition_list_new(1);
1817
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1818
+ rd_kafka_share_subscribe(consumer, subs);
1819
+ rd_kafka_topic_partition_list_destroy(subs);
1820
+
1821
+ attempts = 100;
1822
+ while (consumed < msg_cnt && attempts-- > 0) {
1823
+ size_t rcvd = 0;
1824
+ size_t m;
1825
+ rd_kafka_error_t *err;
1826
+
1827
+ rd_kafka_messages_destroy(batch);
1828
+ batch = NULL;
1829
+ err = rd_kafka_share_poll(consumer, 2000, &batch);
1830
+ if (err) {
1831
+ rd_kafka_error_destroy(err);
1832
+ continue;
1833
+ }
1834
+
1835
+ rcvd = rd_kafka_messages_count(batch);
1836
+ for (m = 0; m < rcvd; m++) {
1837
+ rd_kafka_message_t *msg =
1838
+ rd_kafka_messages_get(batch, m);
1839
+ if (!msg->err) {
1840
+ TEST_ASSERT(msg->len == (size_t)msg_sz,
1841
+ "[%s] msg size %zu != expected %d",
1842
+ codec, msg->len, msg_sz);
1843
+ consumed++;
1844
+ }
1845
+ }
1846
+ }
1847
+
1848
+ rd_kafka_messages_destroy(batch);
1849
+ batch = NULL;
1850
+
1851
+ TEST_ASSERT(consumed == msg_cnt,
1852
+ "[%s] expected %d records, consumed %d", codec, msg_cnt,
1853
+ consumed);
1854
+
1855
+ TEST_SAY("SUCCESS: codec %s - consumed %d records\n", codec, consumed);
1856
+
1857
+ test_share_consumer_close(consumer);
1858
+ test_share_destroy(consumer);
1859
+ }
1860
+
1861
+
1862
+ /**
1863
+ * @brief Run consume+ack roundtrip for every supported producer
1864
+ * compression codec.
1865
+ */
1866
+ static void do_test_all_compression_codecs(void) {
1867
+ SUB_TEST();
1868
+ do_test_compression_codec("none");
1869
+ do_test_compression_codec("gzip");
1870
+ do_test_compression_codec("snappy");
1871
+ do_test_compression_codec("lz4");
1872
+ do_test_compression_codec("zstd");
1873
+ SUB_TEST_PASS();
1874
+ }
1875
+
1876
+
1877
+ /**
1878
+ * @brief Records with 100 headers and one very large header value.
1879
+ *
1880
+ * Verifies the share consumer preserves a large header count and large
1881
+ * per-header value sizes end-to-end.
1882
+ */
1883
+ static void do_test_many_and_large_headers(void) {
1884
+ const char *group = "share-many-headers";
1885
+ const char *topic;
1886
+ rd_kafka_share_t *consumer;
1887
+ rd_kafka_topic_partition_list_t *subs;
1888
+ rd_kafka_messages_t *batch = NULL;
1889
+ rd_kafka_resp_err_t err;
1890
+ const int hdr_cnt = 100;
1891
+ const size_t big_hdr_size = 64 * 1024;
1892
+ char *big_hdr_value;
1893
+ size_t rcvd = 0;
1894
+ int attempts;
1895
+ size_t i;
1896
+ rd_bool_t verified_many = rd_false;
1897
+ rd_bool_t verified_big = rd_false;
1898
+ rd_kafka_headers_t *hdrs;
1899
+ char name[32];
1900
+ char value[32];
1901
+ rd_kafka_headers_t *msg_hdrs = NULL;
1902
+ rd_kafka_resp_err_t herr;
1903
+ size_t n;
1904
+ size_t j;
1905
+ char expected[32];
1906
+ const void *val;
1907
+ size_t vsz;
1908
+ size_t k;
1909
+ const char *bytes;
1910
+ char expected_byte;
1911
+
1912
+ SUB_TEST();
1913
+
1914
+ TEST_SAY("\n");
1915
+ TEST_SAY(
1916
+ "=== Many headers (%d) and one large header value (%zu B) ===\n",
1917
+ hdr_cnt, big_hdr_size);
1918
+
1919
+ consumer = test_create_share_consumer(group, NULL);
1920
+ topic = test_mk_topic_name("0171-many-headers", 1);
1921
+ test_create_topic_wait_exists(NULL, topic, 1, -1, 60 * 1000);
1922
+
1923
+ /* Build the producev arg list for a record with `hdr_cnt` headers.
1924
+ * Each header name is "h-<i>" and each value is "v-<i>". */
1925
+ hdrs = rd_kafka_headers_new(hdr_cnt);
1926
+ for (i = 0; i < (size_t)hdr_cnt; i++) {
1927
+ rd_snprintf(name, sizeof(name), "h-%zu", i);
1928
+ rd_snprintf(value, sizeof(value), "v-%zu", i);
1929
+ rd_kafka_header_add(hdrs, name, -1, value, -1);
1930
+ }
1931
+
1932
+ err = rd_kafka_producev(common_producer, RD_KAFKA_V_TOPIC(topic),
1933
+ RD_KAFKA_V_KEY("many", 4),
1934
+ RD_KAFKA_V_VALUE("many-payload", 12),
1935
+ RD_KAFKA_V_HEADERS(hdrs), RD_KAFKA_V_END);
1936
+ TEST_ASSERT(!err, "producev many headers failed: %s",
1937
+ rd_kafka_err2name(err));
1938
+
1939
+ /* Record with one very large header value. */
1940
+ big_hdr_value = rd_malloc(big_hdr_size);
1941
+ for (i = 0; i < big_hdr_size; i++)
1942
+ big_hdr_value[i] = (char)('A' + (i % 26));
1943
+
1944
+ err = rd_kafka_producev(
1945
+ common_producer, RD_KAFKA_V_TOPIC(topic), RD_KAFKA_V_KEY("big", 3),
1946
+ RD_KAFKA_V_VALUE("big-payload", 11),
1947
+ RD_KAFKA_V_HEADER("big-hdr", big_hdr_value, big_hdr_size),
1948
+ RD_KAFKA_V_END);
1949
+ TEST_ASSERT(!err, "producev big header failed: %s",
1950
+ rd_kafka_err2name(err));
1951
+ rd_free(big_hdr_value);
1952
+
1953
+ rd_kafka_flush(common_producer, 30 * 1000);
1954
+
1955
+ test_share_set_auto_offset_reset(group, "earliest");
1956
+
1957
+ subs = rd_kafka_topic_partition_list_new(1);
1958
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1959
+ rd_kafka_share_subscribe(consumer, subs);
1960
+ rd_kafka_topic_partition_list_destroy(subs);
1961
+
1962
+ attempts = 30;
1963
+ while (rcvd < 2 && attempts-- > 0) {
1964
+ size_t batch_rcvd = 0;
1965
+ rd_kafka_error_t *e;
1966
+
1967
+ rd_kafka_messages_destroy(batch);
1968
+ batch = NULL;
1969
+ e = rd_kafka_share_poll(consumer, 2000, &batch);
1970
+ if (e)
1971
+ rd_kafka_error_destroy(e);
1972
+ batch_rcvd = rd_kafka_messages_count(batch);
1973
+
1974
+ for (i = 0; i < batch_rcvd; i++) {
1975
+ rd_kafka_message_t *msg =
1976
+ rd_kafka_messages_get(batch, i);
1977
+ msg_hdrs = NULL;
1978
+ herr = rd_kafka_message_headers(msg, &msg_hdrs);
1979
+ TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR &&
1980
+ msg_hdrs,
1981
+ "headers not available: %s",
1982
+ rd_kafka_err2name(herr));
1983
+
1984
+ if (msg->key_len == 4 &&
1985
+ memcmp(msg->key, "many", 4) == 0) {
1986
+ n = rd_kafka_header_cnt(msg_hdrs);
1987
+
1988
+ TEST_ASSERT(n == (size_t)hdr_cnt,
1989
+ "many-headers record: expected %d "
1990
+ "headers, got %zu",
1991
+ hdr_cnt, n);
1992
+
1993
+ /* Verify every header name+value present. */
1994
+ for (j = 0; j < (size_t)hdr_cnt; j++) {
1995
+ rd_snprintf(name, sizeof(name), "h-%zu",
1996
+ j);
1997
+ rd_snprintf(expected, sizeof(expected),
1998
+ "v-%zu", j);
1999
+ herr = rd_kafka_header_get_last(
2000
+ msg_hdrs, name, &val, &vsz);
2001
+ TEST_ASSERT(
2002
+ herr == RD_KAFKA_RESP_ERR_NO_ERROR,
2003
+ "%s missing: %s", name,
2004
+ rd_kafka_err2name(herr));
2005
+ TEST_ASSERT(
2006
+ vsz == strlen(expected) &&
2007
+ memcmp(val, expected, vsz) == 0,
2008
+ "%s value mismatch", name);
2009
+ }
2010
+ verified_many = rd_true;
2011
+ } else if (msg->key_len == 3 &&
2012
+ memcmp(msg->key, "big", 3) == 0) {
2013
+ herr = rd_kafka_header_get_last(
2014
+ msg_hdrs, "big-hdr", &val, &vsz);
2015
+ TEST_ASSERT(herr == RD_KAFKA_RESP_ERR_NO_ERROR,
2016
+ "big-hdr missing: %s",
2017
+ rd_kafka_err2name(herr));
2018
+ TEST_ASSERT(vsz == big_hdr_size,
2019
+ "big-hdr size mismatch: expected "
2020
+ "%zu, got %zu",
2021
+ big_hdr_size, vsz);
2022
+
2023
+ /* Spot-check the content pattern at a few
2024
+ * offsets. */
2025
+ bytes = (const char *)val;
2026
+ for (k = 0; k < vsz; k += vsz / 16) {
2027
+ expected_byte = (char)('A' + (k % 26));
2028
+ TEST_ASSERT(
2029
+ bytes[k] == expected_byte,
2030
+ "big-hdr byte %zu: expected %c, "
2031
+ "got %c",
2032
+ k, expected_byte, bytes[k]);
2033
+ }
2034
+ verified_big = rd_true;
2035
+ }
2036
+ }
2037
+ rcvd += batch_rcvd;
2038
+ }
2039
+
2040
+ rd_kafka_messages_destroy(batch);
2041
+ batch = NULL;
2042
+
2043
+ TEST_ASSERT(rcvd == 2, "Expected 2 records, got %zu", rcvd);
2044
+
2045
+ TEST_ASSERT(verified_many, "many-headers record was not verified");
2046
+ TEST_ASSERT(verified_big, "big-header record was not verified");
2047
+
2048
+ test_share_consumer_close(consumer);
2049
+ test_share_destroy(consumer);
2050
+
2051
+ TEST_SAY("SUCCESS: many headers + large header value preserved\n");
2052
+
2053
+ SUB_TEST_PASS();
2054
+ }
2055
+
2056
+
2057
+ int main_0171_share_consumer_consume(int argc, char **argv) {
2058
+ /* Create common handles for all tests */
2059
+ common_producer = test_create_producer();
2060
+ common_admin = test_create_producer();
2061
+
2062
+ test_timeout_set(600);
2063
+
2064
+ /* Single-consumer tests */
2065
+ do_test_single_consumer_single_topic_single_partition(); /* Single
2066
+ consumer,
2067
+ single topic,
2068
+ single
2069
+ partition */
2070
+ do_test_single_consumer_single_topic_multiple_partitions(); /* Single
2071
+ consumer,
2072
+ single
2073
+ topic, multi
2074
+ partitions
2075
+ */
2076
+ do_test_single_consumer_multiple_topic_single_partition(); /* Single
2077
+ consumer,
2078
+ multi topic,
2079
+ single
2080
+ partition
2081
+ each */
2082
+ do_test_single_consumer_multiple_topic_multiple_partitions(); /* Single
2083
+ consumer,
2084
+ multi
2085
+ topics,
2086
+ multi
2087
+ partitions
2088
+ each */
2089
+
2090
+ /* Multi-consumer tests */
2091
+ do_test_multiple_consumers_single_topic_single_partition(); /* Multi
2092
+ consumer
2093
+ sharing
2094
+ single
2095
+ partition */
2096
+ do_test_multiple_consumers_single_topic_multiple_partitions(); /* Multi
2097
+ consumer,
2098
+ multi
2099
+ partition
2100
+ */
2101
+ do_test_multiple_consumers_multiple_topics_multiple_partitions(); /* Full
2102
+ matrix:
2103
+ multi
2104
+ everything
2105
+ */
2106
+
2107
+ /* High volume tests */
2108
+ do_test_high_volume_10k_messages();
2109
+ do_test_high_volume_50k_multi_partition();
2110
+
2111
+ /* Multi-topic tests (triggers multiple fetch responses) */
2112
+ do_test_many_topics_15();
2113
+ do_test_many_topics_10_multi_partition();
2114
+
2115
+ /* Rapid produce/consume tests */
2116
+ do_test_rapid_produce_consume_cycles();
2117
+
2118
+ /* Edge case tests */
2119
+ do_test_empty_then_produce();
2120
+ do_test_sparse_partitions();
2121
+
2122
+ /* Callback tests */
2123
+ do_test_poll_callback_piggybacked_acks();
2124
+
2125
+ /* Concurrency tests */
2126
+ do_test_concurrent_thread_access_rejected();
2127
+
2128
+ /* Headers and fetch byte-limit tests */
2129
+ do_test_headers_preserved();
2130
+ do_test_fetch_max_bytes_small();
2131
+ do_test_record_larger_than_fetch_max_bytes();
2132
+
2133
+ /* Failing consumer releases to group */
2134
+ do_test_consumer_close_releases_to_group();
2135
+
2136
+ /* Topology change: grow partitions while consuming */
2137
+ do_test_partition_increase_during_consume();
2138
+
2139
+ /* Payload edge cases */
2140
+ do_test_zero_byte_payload_and_key();
2141
+ do_test_all_compression_codecs();
2142
+ do_test_many_and_large_headers();
2143
+
2144
+
2145
+ /* Cleanup common handles */
2146
+ rd_kafka_destroy(common_admin);
2147
+ rd_kafka_destroy(common_producer);
2148
+
2149
+ return 0;
2150
+ }