@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,2102 @@
1
+ /*
2
+ * librdkafka - Apache Kafka C library
3
+ *
4
+ * Copyright (c) 2026, 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
+ #include "rdkafka.h"
31
+ #include "rdkafka_protocol.h"
32
+
33
+ #define MAX_TOPICS 16
34
+ #define MAX_PARTITIONS 32
35
+ #define BATCH_SIZE 10000
36
+ #define MAX_CONSUME_ATTEMPTS 30
37
+
38
+ /** Common producer reused across all real-broker tests. */
39
+ static rd_kafka_t *common_producer;
40
+
41
+ /** Common admin client reused across all real-broker tests. */
42
+ static rd_kafka_t *common_admin;
43
+
44
+ /**
45
+ * @brief Commit mode for test scenarios
46
+ */
47
+ typedef enum {
48
+ COMMIT_MODE_NONE, /**< No commit before close */
49
+ COMMIT_MODE_ASYNC, /**< Call commitAsync before close */
50
+ COMMIT_MODE_SYNC /**< Call commitSync before close */
51
+ } commit_mode_t;
52
+
53
+ /**
54
+ * @brief Generic test context for topic and message tracking
55
+ */
56
+ typedef struct {
57
+ char *topic_names[MAX_TOPICS];
58
+ int topic_cnt;
59
+ char *group_id;
60
+ int total_msgs_produced;
61
+ } test_context_t;
62
+
63
+ /**
64
+ * @brief Tracked message info for verification
65
+ */
66
+ typedef struct {
67
+ char *topic;
68
+ int32_t partition;
69
+ int64_t offset;
70
+ } tracked_msg_t;
71
+
72
+ /**
73
+ * @brief Sink that collects ack receipts from both the share-ack callback
74
+ * and synchronous commit_sync results.
75
+ *
76
+ * Each (topic, partition, offset) is recorded at most once (idempotent add),
77
+ * so callback + commit_sync overlaps are harmless.
78
+ */
79
+ typedef struct {
80
+ tracked_msg_t *receipts;
81
+ int receipt_cnt;
82
+ int receipt_capacity;
83
+ int callback_invocations; /* diagnostic */
84
+ rd_kafka_resp_err_t last_cb_err;
85
+ mtx_t lock;
86
+ } ack_receipts_t;
87
+
88
+ static void ack_receipts_init(ack_receipts_t *r) {
89
+ memset(r, 0, sizeof(*r));
90
+ mtx_init(&r->lock, mtx_plain);
91
+ }
92
+
93
+ static void ack_receipts_destroy(ack_receipts_t *r) {
94
+ int i;
95
+ for (i = 0; i < r->receipt_cnt; i++)
96
+ rd_free(r->receipts[i].topic);
97
+ if (r->receipts)
98
+ rd_free(r->receipts);
99
+ mtx_destroy(&r->lock);
100
+ }
101
+
102
+ /* Idempotent: dedupes on (topic, partition, offset). */
103
+ static void ack_receipts_add(ack_receipts_t *r,
104
+ const char *topic,
105
+ int32_t partition,
106
+ int64_t offset) {
107
+ int i;
108
+ mtx_lock(&r->lock);
109
+ for (i = 0; i < r->receipt_cnt; i++) {
110
+ if (r->receipts[i].partition == partition &&
111
+ r->receipts[i].offset == offset &&
112
+ strcmp(r->receipts[i].topic, topic) == 0) {
113
+ mtx_unlock(&r->lock);
114
+ return;
115
+ }
116
+ }
117
+ if (r->receipt_cnt == r->receipt_capacity) {
118
+ int new_cap =
119
+ r->receipt_capacity ? r->receipt_capacity * 2 : 16;
120
+ r->receipts =
121
+ rd_realloc(r->receipts, new_cap * sizeof(*r->receipts));
122
+ r->receipt_capacity = new_cap;
123
+ }
124
+ r->receipts[r->receipt_cnt].topic = rd_strdup(topic);
125
+ r->receipts[r->receipt_cnt].partition = partition;
126
+ r->receipts[r->receipt_cnt].offset = offset;
127
+ r->receipt_cnt++;
128
+ mtx_unlock(&r->lock);
129
+ }
130
+
131
+ /**
132
+ * @brief Share-ack callback that funnels every reported offset into
133
+ * the provided ack_receipts_t (passed as opaque).
134
+ */
135
+ static void test_0178_ack_cb(rd_kafka_share_t *rkshare,
136
+ rd_kafka_share_partition_offsets_list_t *parts,
137
+ rd_kafka_resp_err_t err,
138
+ void *opaque) {
139
+ ack_receipts_t *r = opaque;
140
+ size_t pcnt, p;
141
+
142
+ (void)rkshare;
143
+
144
+ mtx_lock(&r->lock);
145
+ r->callback_invocations++;
146
+ r->last_cb_err = err;
147
+ mtx_unlock(&r->lock);
148
+
149
+ pcnt = rd_kafka_share_partition_offsets_list_count(parts);
150
+ TEST_SAY("ack_cb: invocation=%d err=%s partitions=%zu\n",
151
+ r->callback_invocations, rd_kafka_err2name(err), pcnt);
152
+
153
+ if (err != RD_KAFKA_RESP_ERR_NO_ERROR)
154
+ return;
155
+
156
+ for (p = 0; p < pcnt; p++) {
157
+ const rd_kafka_share_partition_offsets_t *entry =
158
+ rd_kafka_share_partition_offsets_list_get(parts, p);
159
+ const rd_kafka_topic_partition_t *tp;
160
+ const int64_t *offsets;
161
+ int ocnt, o;
162
+
163
+ if (!entry)
164
+ continue;
165
+
166
+ tp = rd_kafka_share_partition_offsets_partition(entry);
167
+ offsets = rd_kafka_share_partition_offsets_offsets(entry);
168
+ ocnt = rd_kafka_share_partition_offsets_offsets_cnt(entry);
169
+
170
+ for (o = 0; o < ocnt; o++)
171
+ ack_receipts_add(r, tp->topic, tp->partition,
172
+ offsets[o]);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * @brief Build a share consumer with our ack callback wired in.
178
+ *
179
+ * Mirrors what test_create_share_consumer_with_cb does, but uses
180
+ * @p receipts as the opaque so test_0178_ack_cb can record into it.
181
+ */
182
+ static rd_kafka_share_t *
183
+ create_share_consumer_with_receipts(const char *group_id,
184
+ const char *ack_mode,
185
+ ack_receipts_t *receipts) {
186
+ rd_kafka_share_t *rkshare;
187
+ rd_kafka_conf_t *conf;
188
+ char errstr[512];
189
+
190
+ test_conf_init(&conf, NULL, 60);
191
+ rd_kafka_conf_set(conf, "group.id", group_id, errstr, sizeof(errstr));
192
+ rd_kafka_conf_set(conf, "share.acknowledgement.mode", ack_mode, errstr,
193
+ sizeof(errstr));
194
+
195
+ rkshare = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
196
+ TEST_ASSERT(rkshare, "Failed to create share consumer: %s", errstr);
197
+
198
+ rd_kafka_error_t *error = rd_kafka_share_set_acknowledgement_commit_cb(
199
+ rkshare, test_0178_ack_cb, receipts);
200
+ TEST_ASSERT(error == NULL,
201
+ "Failed to set acknowledgement commit callback: %s",
202
+ rd_kafka_error_string(error));
203
+ return rkshare;
204
+ }
205
+
206
+ /**
207
+ * @brief Assert that @p r contains a receipt for every entry in
208
+ * @p expected[0..expected_cnt). Extra receipts trigger a warning.
209
+ */
210
+ static void verify_receipts_match(ack_receipts_t *r,
211
+ const tracked_msg_t *expected,
212
+ int expected_cnt,
213
+ const char *context_label) {
214
+ rd_bool_t *seen =
215
+ expected_cnt > 0 ? rd_calloc(expected_cnt, sizeof(*seen)) : NULL;
216
+ int matched = 0;
217
+ int i, j;
218
+
219
+ mtx_lock(&r->lock);
220
+
221
+ for (i = 0; i < r->receipt_cnt; i++) {
222
+ rd_bool_t expected_hit = rd_false;
223
+ for (j = 0; j < expected_cnt; j++) {
224
+ if (seen[j])
225
+ continue;
226
+ if (r->receipts[i].partition == expected[j].partition &&
227
+ r->receipts[i].offset == expected[j].offset &&
228
+ strcmp(r->receipts[i].topic, expected[j].topic) ==
229
+ 0) {
230
+ seen[j] = rd_true;
231
+ expected_hit = rd_true;
232
+ matched++;
233
+ break;
234
+ }
235
+ }
236
+ if (!expected_hit) {
237
+ TEST_WARN(
238
+ "ack receipts: unexpected receipt for %s [%d] "
239
+ "@ offset %" PRId64 " (context: %s)\n",
240
+ r->receipts[i].topic, r->receipts[i].partition,
241
+ r->receipts[i].offset, context_label);
242
+ }
243
+ }
244
+
245
+ for (j = 0; j < expected_cnt; j++) {
246
+ if (!seen[j]) {
247
+ mtx_unlock(&r->lock);
248
+ TEST_FAIL(
249
+ "ack receipts: missing receipt for %s [%d] "
250
+ "@ offset %" PRId64
251
+ " (context: %s, "
252
+ "matched=%d/%d, total receipts=%d, cb invocations="
253
+ "%d)",
254
+ expected[j].topic, expected[j].partition,
255
+ expected[j].offset, context_label, matched,
256
+ expected_cnt, r->receipt_cnt,
257
+ r->callback_invocations);
258
+ }
259
+ }
260
+
261
+ TEST_SAY(
262
+ "ack receipts: matched %d/%d expected (context: %s, total "
263
+ "receipts=%d, cb invocations=%d)\n",
264
+ matched, expected_cnt, context_label, r->receipt_cnt,
265
+ r->callback_invocations);
266
+
267
+ mtx_unlock(&r->lock);
268
+ if (seen)
269
+ rd_free(seen);
270
+ }
271
+
272
+ /**
273
+ * @brief Get string representation of commit mode
274
+ */
275
+ static const char *commit_mode_str(commit_mode_t mode) {
276
+ switch (mode) {
277
+ case COMMIT_MODE_NONE:
278
+ return "no-commit";
279
+ case COMMIT_MODE_ASYNC:
280
+ return "commit-async";
281
+ case COMMIT_MODE_SYNC:
282
+ return "commit-sync";
283
+ default:
284
+ return "unknown";
285
+ }
286
+ }
287
+
288
+ /**
289
+ * @brief Setup topics and produce messages (modular helper)
290
+ *
291
+ * @param ctx Test context to populate
292
+ * @param topic_cnt Number of topics to create
293
+ * @param partitions Array of partition counts per topic
294
+ * @param msgs_per_partition Messages to produce per partition
295
+ *
296
+ * @returns Total number of messages produced
297
+ */
298
+ static int setup_topics_and_produce(test_context_t *ctx,
299
+ int topic_cnt,
300
+ const int *partitions,
301
+ int msgs_per_partition) {
302
+ int t, p;
303
+ int total_msgs = 0;
304
+
305
+ ctx->topic_cnt = topic_cnt;
306
+
307
+ for (t = 0; t < topic_cnt; t++) {
308
+ ctx->topic_names[t] =
309
+ rd_strdup(test_mk_topic_name("0178-close-test", 1));
310
+
311
+ TEST_SAY("Creating topic %s with %d partition(s)\n",
312
+ ctx->topic_names[t], partitions[t]);
313
+
314
+ test_create_topic_wait_exists(common_admin, ctx->topic_names[t],
315
+ partitions[t], -1, 60 * 1000);
316
+
317
+ for (p = 0; p < partitions[t]; p++) {
318
+ test_produce_msgs_simple(common_producer,
319
+ ctx->topic_names[t], p,
320
+ msgs_per_partition);
321
+ total_msgs += msgs_per_partition;
322
+ }
323
+
324
+ TEST_SAY(
325
+ "Topic %s: produced %d messages (%d partitions * %d"
326
+ "msgs)\n",
327
+ ctx->topic_names[t], partitions[t] * msgs_per_partition,
328
+ partitions[t], msgs_per_partition);
329
+ }
330
+
331
+ ctx->total_msgs_produced = total_msgs;
332
+ TEST_SAY("Total messages produced: %d\n", total_msgs);
333
+ return total_msgs;
334
+ }
335
+
336
+ /**
337
+ * @brief Subscribe consumer to topics
338
+ *
339
+ * @param rkshare Consumer handle
340
+ * @param topic_names Array of topic names to subscribe to
341
+ * @param topic_cnt Number of topics
342
+ */
343
+ static void subscribe_consumer(rd_kafka_share_t *rkshare,
344
+ char **topic_names,
345
+ int topic_cnt) {
346
+ rd_kafka_topic_partition_list_t *subs;
347
+ rd_kafka_resp_err_t err;
348
+ int t;
349
+
350
+ /* Build subscription list */
351
+ subs = rd_kafka_topic_partition_list_new(topic_cnt);
352
+ for (t = 0; t < topic_cnt; t++) {
353
+ rd_kafka_topic_partition_list_add(subs, topic_names[t],
354
+ RD_KAFKA_PARTITION_UA);
355
+ }
356
+
357
+ /* Subscribe */
358
+ err = rd_kafka_share_subscribe(rkshare, subs);
359
+ if (err) {
360
+ TEST_FAIL("Subscribe failed: %s", rd_kafka_err2str(err));
361
+ }
362
+
363
+ rd_kafka_topic_partition_list_destroy(subs);
364
+ }
365
+
366
+ /**
367
+ * @brief Consume messages, track them, and optionally ACCEPT-acknowledge.
368
+ *
369
+ * In ack mode (do_acknowledge=true), messages are acknowledged with ACCEPT;
370
+ * any remaining messages in a batch beyond target_count are RELEASE'd so they
371
+ * can be redelivered. In no-ack mode (do_acknowledge=false), messages are
372
+ * tracked and destroyed without acknowledging — close() should release them.
373
+ *
374
+ * @param rkshare Consumer handle
375
+ * @param consumer_name Name for logging (e.g., "C1", "C2")
376
+ * @param target_count Number of messages to track (and ACCEPT in ack mode)
377
+ * @param do_acknowledge If true, ACCEPT each tracked message and RELEASE the
378
+ * rest of the batch; if false, track-and-destroy only.
379
+ * @param tracked_msgs If non-NULL, track messages here
380
+ * @param tracked_cnt If non-NULL, store count of tracked messages here
381
+ *
382
+ * @returns Number of messages tracked
383
+ */
384
+ static int consume_and_track(rd_kafka_share_t *rkshare,
385
+ const char *consumer_name,
386
+ int target_count,
387
+ rd_bool_t do_acknowledge,
388
+ tracked_msg_t *tracked_msgs,
389
+ int *tracked_cnt) {
390
+ rd_kafka_messages_t *batch = NULL;
391
+ int attempt;
392
+ rd_kafka_error_t *error;
393
+ int tracked = 0;
394
+
395
+ TEST_SAY("%s: Consuming and %s %d messages (max %d attempts)...\n",
396
+ consumer_name,
397
+ do_acknowledge ? "acknowledging" : "tracking (no ack)",
398
+ target_count, MAX_CONSUME_ATTEMPTS);
399
+
400
+ for (attempt = 0;
401
+ attempt < MAX_CONSUME_ATTEMPTS && tracked < target_count;
402
+ attempt++) {
403
+ size_t rcvd_msgs = 0;
404
+ int i;
405
+
406
+ error = rd_kafka_share_poll(rkshare, 3000, &batch);
407
+
408
+ if (error) {
409
+ TEST_SAY("%s: Attempt %d/%d: error: %s\n",
410
+ consumer_name, attempt + 1,
411
+ MAX_CONSUME_ATTEMPTS,
412
+ rd_kafka_error_string(error));
413
+ rd_kafka_error_destroy(error);
414
+ continue;
415
+ }
416
+
417
+ rcvd_msgs = rd_kafka_messages_count(batch);
418
+
419
+ if (rcvd_msgs == 0) {
420
+ rd_kafka_messages_destroy(batch);
421
+ batch = NULL;
422
+ continue;
423
+ }
424
+
425
+ TEST_SAY("%s: Attempt %d/%d: Received %d messages\n",
426
+ consumer_name, attempt + 1, MAX_CONSUME_ATTEMPTS,
427
+ (int)rcvd_msgs);
428
+
429
+ for (i = 0; i < (int)rcvd_msgs && tracked < target_count; i++) {
430
+ rd_kafka_message_t *rkm =
431
+ rd_kafka_messages_get(batch, i);
432
+
433
+ if (rkm->err) {
434
+ TEST_SAY(
435
+ "%s: Skipping message %d with error: %s\n",
436
+ consumer_name, i,
437
+ rd_kafka_message_errstr(rkm));
438
+ continue;
439
+ }
440
+
441
+ if (tracked_msgs && tracked < BATCH_SIZE) {
442
+ tracked_msgs[tracked].topic =
443
+ rd_strdup(rd_kafka_topic_name(rkm->rkt));
444
+ tracked_msgs[tracked].partition =
445
+ rkm->partition;
446
+ tracked_msgs[tracked].offset = rkm->offset;
447
+ }
448
+
449
+ TEST_SAY("%s: %s message %d: %s [%d] @ offset %" PRId64
450
+ "\n",
451
+ consumer_name,
452
+ do_acknowledge ? "Acking" : "Tracking",
453
+ tracked, rd_kafka_topic_name(rkm->rkt),
454
+ rkm->partition, rkm->offset);
455
+
456
+ if (do_acknowledge)
457
+ rd_kafka_share_acknowledge(rkshare, rkm);
458
+ tracked++;
459
+ }
460
+
461
+ rd_kafka_messages_destroy(batch);
462
+ batch = NULL;
463
+
464
+ if (tracked >= target_count) {
465
+ TEST_SAY("%s: Reached target of %d tracked messages\n",
466
+ consumer_name, target_count);
467
+ break;
468
+ }
469
+ }
470
+
471
+ TEST_SAY("%s: Finished - tracked %d messages\n", consumer_name,
472
+ tracked);
473
+
474
+ if (tracked < target_count) {
475
+ TEST_FAIL("%s: Expected to track %d messages, got %d",
476
+ consumer_name, target_count, tracked);
477
+ }
478
+
479
+ if (tracked_cnt)
480
+ *tracked_cnt = tracked;
481
+
482
+ return tracked;
483
+ }
484
+
485
+ /**
486
+ * @brief Execute commit based on mode (modular helper)
487
+ *
488
+ * @param rkshare Consumer handle
489
+ * @param consumer_name Name for logging
490
+ * @param mode Commit mode to execute
491
+ */
492
+ static void perform_commit(rd_kafka_share_t *rkshare,
493
+ const char *consumer_name,
494
+ commit_mode_t mode,
495
+ ack_receipts_t *receipts) {
496
+ rd_kafka_error_t *error;
497
+
498
+ switch (mode) {
499
+ case COMMIT_MODE_NONE:
500
+ TEST_SAY("%s: Skipping commit (mode: %s)\n", consumer_name,
501
+ commit_mode_str(mode));
502
+ break;
503
+
504
+ case COMMIT_MODE_ASYNC:
505
+ TEST_SAY("%s: Calling commitAsync\n", consumer_name);
506
+ error = rd_kafka_share_commit_async(rkshare);
507
+ if (error) {
508
+ TEST_FAIL("%s: commitAsync failed: %s", consumer_name,
509
+ rd_kafka_error_string(error));
510
+ rd_kafka_error_destroy(error);
511
+ }
512
+ /* Give async commit some time to process */
513
+ rd_sleep(1);
514
+ break;
515
+
516
+ case COMMIT_MODE_SYNC: {
517
+ rd_kafka_topic_partition_list_t *partitions = NULL;
518
+ int p;
519
+ TEST_SAY("%s: Calling commitSync\n", consumer_name);
520
+ error = rd_kafka_share_commit_sync(rkshare, 30000, &partitions);
521
+ if (error) {
522
+ TEST_FAIL("%s: commitSync failed: %s", consumer_name,
523
+ rd_kafka_error_string(error));
524
+ rd_kafka_error_destroy(error);
525
+ }
526
+ /* Honour synchronous per-partition results. IN_PROGRESS means
527
+ * the result will arrive via the ack callback later, so skip
528
+ * — the callback path will fill the receipt. */
529
+ if (receipts && partitions) {
530
+ for (p = 0; p < partitions->cnt; p++) {
531
+ rd_kafka_topic_partition_t *tp =
532
+ &partitions->elems[p];
533
+ if (tp->err == RD_KAFKA_RESP_ERR__IN_PROGRESS)
534
+ continue;
535
+ if (tp->err != RD_KAFKA_RESP_ERR_NO_ERROR)
536
+ TEST_FAIL(
537
+ "%s: commitSync per-partition "
538
+ "error for %s [%d]: %s",
539
+ consumer_name, tp->topic,
540
+ tp->partition,
541
+ rd_kafka_err2str(tp->err));
542
+ ack_receipts_add(receipts, tp->topic,
543
+ tp->partition, tp->offset);
544
+ }
545
+ }
546
+ if (partitions)
547
+ rd_kafka_topic_partition_list_destroy(partitions);
548
+ TEST_SAY("%s: commitSync completed successfully\n",
549
+ consumer_name);
550
+ break;
551
+ }
552
+
553
+ default:
554
+ TEST_FAIL("Unknown commit mode: %d", mode);
555
+ }
556
+ }
557
+
558
+ /**
559
+ * @brief Verify whether tracked messages are (or are not) redelivered.
560
+ *
561
+ * Consumes up to @p max_total_msgs messages and compares each against the
562
+ * @p tracked_msgs list. Behavior depends on @p expect_redelivery:
563
+ * - false: fails if any tracked message is received (post-ack scenario);
564
+ * also asserts the total received count equals @p max_total_msgs.
565
+ * - true: fails unless every tracked message is received (post-release
566
+ * scenario); stops early once all tracked messages have been
567
+ * matched.
568
+ *
569
+ * @param rkshare Consumer handle to check
570
+ * @param consumer_name Name for logging
571
+ * @param max_total_msgs Number of messages to consume (also the exact-count
572
+ * assertion in the not-redelivered case)
573
+ * @param tracked_msgs Array of tracked messages to check against
574
+ * @param tracked_cnt Number of tracked messages
575
+ * @param expect_redelivery true = expect all tracked redelivered; false =
576
+ * expect none redelivered
577
+ * @param context_label Free-form label included in failure messages
578
+ * (e.g., commit mode name)
579
+ */
580
+ static void verify_tracked_messages(rd_kafka_share_t *rkshare,
581
+ const char *consumer_name,
582
+ int max_total_msgs,
583
+ const tracked_msg_t *tracked_msgs,
584
+ int tracked_cnt,
585
+ rd_bool_t expect_redelivery,
586
+ const char *context_label) {
587
+ rd_kafka_messages_t *batch = NULL;
588
+ size_t total_rcvd = 0;
589
+ int attempt;
590
+ rd_kafka_error_t *error;
591
+ int matched_count = 0;
592
+ rd_bool_t *tracked_seen =
593
+ tracked_cnt > 0 ? rd_calloc(tracked_cnt, sizeof(*tracked_seen))
594
+ : NULL;
595
+
596
+ TEST_SAY(
597
+ "%s: Consuming for verification (up to %d messages, max %d "
598
+ "attempts, expect_redelivery=%s)...\n",
599
+ consumer_name, max_total_msgs, MAX_CONSUME_ATTEMPTS,
600
+ expect_redelivery ? "true" : "false");
601
+
602
+ for (attempt = 0; attempt < MAX_CONSUME_ATTEMPTS &&
603
+ (int)total_rcvd < max_total_msgs &&
604
+ (!expect_redelivery || matched_count < tracked_cnt);
605
+ attempt++) {
606
+ size_t rcvd_msgs = 0;
607
+ size_t i;
608
+
609
+ error = rd_kafka_share_poll(rkshare, 3000, &batch);
610
+
611
+ if (error) {
612
+ TEST_SAY("%s: Attempt %d/%d: error: %s\n",
613
+ consumer_name, attempt + 1,
614
+ MAX_CONSUME_ATTEMPTS,
615
+ rd_kafka_error_string(error));
616
+ rd_kafka_error_destroy(error);
617
+ continue;
618
+ }
619
+
620
+ rcvd_msgs = rd_kafka_messages_count(batch);
621
+
622
+ if (rcvd_msgs == 0) {
623
+ rd_kafka_messages_destroy(batch);
624
+ batch = NULL;
625
+ continue;
626
+ }
627
+
628
+ TEST_SAY(
629
+ "%s: Attempt %d/%d: Received %d messages (total: %d)\n",
630
+ consumer_name, attempt + 1, MAX_CONSUME_ATTEMPTS,
631
+ (int)rcvd_msgs, (int)(total_rcvd + rcvd_msgs));
632
+
633
+ /* Match each new message against the tracked list */
634
+ for (i = 0; i < rcvd_msgs; i++) {
635
+ rd_kafka_message_t *rkm =
636
+ rd_kafka_messages_get(batch, i);
637
+ const char *topic;
638
+ int32_t partition;
639
+ int64_t offset;
640
+ int j;
641
+
642
+ if (!rkm || rkm->err)
643
+ continue;
644
+
645
+ topic = rd_kafka_topic_name(rkm->rkt);
646
+ partition = rkm->partition;
647
+ offset = rkm->offset;
648
+
649
+ for (j = 0; j < tracked_cnt; j++) {
650
+ if (tracked_seen && tracked_seen[j])
651
+ continue;
652
+ if (strcmp(topic, tracked_msgs[j].topic) == 0 &&
653
+ partition == tracked_msgs[j].partition &&
654
+ offset == tracked_msgs[j].offset) {
655
+ if (tracked_seen)
656
+ tracked_seen[j] = rd_true;
657
+ matched_count++;
658
+ if (expect_redelivery) {
659
+ TEST_SAY(
660
+ "%s: Matched tracked "
661
+ "message %d/%d: %s [%d] "
662
+ "@ offset %" PRId64 "\n",
663
+ consumer_name,
664
+ matched_count, tracked_cnt,
665
+ topic, partition, offset);
666
+ } else {
667
+ TEST_WARN(
668
+ "%s: Received previously "
669
+ "acknowledged message: "
670
+ "%s [%d] @ offset %" PRId64
671
+ "\n",
672
+ consumer_name, topic,
673
+ partition, offset);
674
+ }
675
+ break;
676
+ }
677
+ }
678
+ }
679
+
680
+ total_rcvd += rcvd_msgs;
681
+ rd_kafka_messages_destroy(batch);
682
+ batch = NULL;
683
+ }
684
+
685
+ TEST_SAY("%s: Received %d messages, matched %d/%d tracked\n",
686
+ consumer_name, (int)total_rcvd, matched_count, tracked_cnt);
687
+
688
+ if (expect_redelivery) {
689
+ int j;
690
+ if (matched_count != tracked_cnt) {
691
+ for (j = 0; j < tracked_cnt; j++) {
692
+ if (!tracked_seen[j]) {
693
+ TEST_FAIL(
694
+ "%s: Did not receive tracked "
695
+ "message: %s [%d] @ offset "
696
+ "%" PRId64 " (context: %s)",
697
+ consumer_name,
698
+ tracked_msgs[j].topic,
699
+ tracked_msgs[j].partition,
700
+ tracked_msgs[j].offset,
701
+ context_label);
702
+ }
703
+ }
704
+ }
705
+ TEST_SAY(
706
+ "%s: Verification passed - all %d tracked messages "
707
+ "redelivered\n",
708
+ consumer_name, matched_count);
709
+ } else {
710
+ if ((int)total_rcvd != max_total_msgs) {
711
+ TEST_FAIL("%s: Expected to receive %d messages, got %d",
712
+ consumer_name, max_total_msgs,
713
+ (int)total_rcvd);
714
+ }
715
+ if (matched_count > 0) {
716
+ TEST_FAIL(
717
+ "%s received %d messages that were acknowledged "
718
+ "(context: %s)",
719
+ consumer_name, matched_count, context_label);
720
+ }
721
+ TEST_SAY(
722
+ "%s: Verification passed - no acknowledged messages "
723
+ "were redelivered\n",
724
+ consumer_name);
725
+ }
726
+
727
+ if (tracked_seen)
728
+ rd_free(tracked_seen);
729
+ }
730
+
731
+ /**
732
+ * @brief Free tracked messages (modular helper)
733
+ *
734
+ * @param tracked_msgs Array of tracked messages
735
+ * @param tracked_cnt Number of tracked messages
736
+ */
737
+ static void free_tracked_messages(tracked_msg_t *tracked_msgs,
738
+ int tracked_cnt) {
739
+ for (int i = 0; i < tracked_cnt; i++) {
740
+ if (tracked_msgs[i].topic)
741
+ rd_free(tracked_msgs[i].topic);
742
+ }
743
+ }
744
+
745
+ /**
746
+ * @brief Helper: assert the given rd_kafka_error_t* signals closed/closing.
747
+ */
748
+ static void assert_state_error(rd_kafka_error_t *error, const char *api_name) {
749
+ TEST_ASSERT(error != NULL, "%s: expected error, got NULL", api_name);
750
+ TEST_ASSERT(rd_kafka_error_code(error) == RD_KAFKA_RESP_ERR__STATE,
751
+ "%s: expected __STATE, got %s", api_name,
752
+ rd_kafka_err2name(rd_kafka_error_code(error)));
753
+ TEST_ASSERT(strstr(rd_kafka_error_string(error), "closed") != NULL,
754
+ "%s: expected error string to contain 'closed', got '%s'",
755
+ api_name, rd_kafka_error_string(error));
756
+ rd_kafka_error_destroy(error);
757
+ }
758
+
759
+ /**
760
+ * @brief Invoke every share-consumer API and assert each returns a
761
+ * closed state error.
762
+ *
763
+ * @param consumer Share consumer (already closed or closing).
764
+ * @param topic Topic name (for subscribe/acknowledge_offset).
765
+ */
766
+ static void verify_all_apis_return_error(rd_kafka_share_t *consumer,
767
+ const char *topic) {
768
+ rd_kafka_error_t *error;
769
+ rd_kafka_resp_err_t err;
770
+ rd_kafka_messages_t *batch = NULL;
771
+ rd_kafka_topic_partition_list_t *subs, *sub_result = NULL;
772
+ rd_kafka_topic_partition_list_t *commit_results = NULL;
773
+ rd_kafka_queue_t *queue = NULL;
774
+
775
+ /* 1. share_poll */
776
+ error = rd_kafka_share_poll(consumer, 100, &batch);
777
+ assert_state_error(error, "share_poll");
778
+ if (batch) {
779
+ rd_kafka_messages_destroy(batch);
780
+ batch = NULL;
781
+ }
782
+
783
+ /* 2. commit_async */
784
+ error = rd_kafka_share_commit_async(consumer);
785
+ assert_state_error(error, "commit_async");
786
+
787
+ /* 3. commit_sync */
788
+ error = rd_kafka_share_commit_sync(consumer, 1000, &commit_results);
789
+ assert_state_error(error, "commit_sync");
790
+ TEST_ASSERT(commit_results == NULL,
791
+ "commit_sync: expected no partitions on error");
792
+
793
+ /* 4. acknowledge_offset */
794
+ err = rd_kafka_share_acknowledge_offset(
795
+ consumer, topic, 0, 0, RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
796
+ TEST_ASSERT(err == RD_KAFKA_RESP_ERR__STATE,
797
+ "acknowledge_offset: expected __STATE, got %s",
798
+ rd_kafka_err2name(err));
799
+
800
+ /* 5. subscribe */
801
+ subs = rd_kafka_topic_partition_list_new(1);
802
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
803
+ err = rd_kafka_share_subscribe(consumer, subs);
804
+ TEST_ASSERT(err == RD_KAFKA_RESP_ERR__STATE,
805
+ "subscribe: expected __STATE, got %s",
806
+ rd_kafka_err2name(err));
807
+ rd_kafka_topic_partition_list_destroy(subs);
808
+
809
+ /* 6. unsubscribe */
810
+ err = rd_kafka_share_unsubscribe(consumer);
811
+ TEST_ASSERT(err == RD_KAFKA_RESP_ERR__STATE,
812
+ "unsubscribe: expected __STATE, got %s",
813
+ rd_kafka_err2name(err));
814
+
815
+ /* 7. subscription */
816
+ err = rd_kafka_share_subscription(consumer, &sub_result);
817
+ TEST_ASSERT(err == RD_KAFKA_RESP_ERR__STATE,
818
+ "subscription: expected __STATE, got %s",
819
+ rd_kafka_err2name(err));
820
+ TEST_ASSERT(sub_result == NULL, "subscription: expected NULL result");
821
+
822
+ /* 8. close */
823
+ error = rd_kafka_share_consumer_close(consumer);
824
+ assert_state_error(error, "close");
825
+
826
+ /* 9. async close */
827
+ queue = rd_kafka_queue_new(test_share_consumer_get_rk(consumer));
828
+ error = rd_kafka_share_consumer_close_queue(consumer, queue);
829
+ rd_kafka_queue_destroy(queue);
830
+ assert_state_error(error, "close_queue");
831
+ }
832
+
833
+ /**
834
+ * @brief Common setup for 3-broker mock close-tests.
835
+ *
836
+ * Creates a 3-broker mock cluster, a topic with one partition per broker,
837
+ * produces 10 messages to each partition, then creates a share consumer
838
+ * (with optional socket.timeout.ms), subscribes, and consumes all
839
+ * @p partition_cnt * @p msgs_per_partition messages so the share session
840
+ * is established with every partition leader.
841
+ *
842
+ * @param test_name Used to derive unique topic and group names.
843
+ * @param socket_timeout_ms Pass 0 to leave socket.timeout.ms at default,
844
+ * or a positive value to override it.
845
+ * @param out_mcluster Output: created cluster (caller destroys).
846
+ * @param out_consumer Output: subscribed share consumer (caller destroys).
847
+ */
848
+ static void setup_3broker_share_consumer(const char *test_name,
849
+ int socket_timeout_ms,
850
+ rd_kafka_mock_cluster_t **out_mcluster,
851
+ rd_kafka_share_t **out_consumer) {
852
+ const int partition_cnt = 3;
853
+ const int msgs_per_partition = 10;
854
+ const int total_msgs = partition_cnt * msgs_per_partition;
855
+ rd_kafka_mock_cluster_t *mcluster;
856
+ const char *bootstraps;
857
+ rd_kafka_share_t *consumer;
858
+ rd_kafka_conf_t *conf;
859
+ rd_kafka_topic_partition_list_t *subs;
860
+ rd_kafka_messages_t *batch = NULL;
861
+ rd_kafka_error_t *error;
862
+ char topic[64], group[64], errstr[512];
863
+ int p, consumed = 0, attempts = 0;
864
+ size_t i, rcvd;
865
+
866
+ rd_snprintf(topic, sizeof(topic), "mock-%s", test_name);
867
+ rd_snprintf(group, sizeof(group), "sg-%s", test_name);
868
+
869
+ mcluster = test_mock_cluster_new(3, &bootstraps);
870
+ rd_kafka_mock_sharegroup_set_auto_offset_reset(mcluster, 1);
871
+
872
+ TEST_ASSERT(rd_kafka_mock_topic_create(mcluster, topic, partition_cnt,
873
+ 1) == RD_KAFKA_RESP_ERR_NO_ERROR,
874
+ "Failed to create mock topic");
875
+
876
+ /* Produce msgs_per_partition msgs to each partition via test helper. */
877
+ for (p = 0; p < partition_cnt; p++) {
878
+ test_produce_msgs_easy_v(topic, 0, p, 0, msgs_per_partition, 16,
879
+ "bootstrap.servers", bootstraps, NULL);
880
+ }
881
+
882
+ /* Consumer */
883
+ test_conf_init(&conf, NULL, 0);
884
+ test_conf_set(conf, "bootstrap.servers", bootstraps);
885
+ test_conf_set(conf, "group.id", group);
886
+ if (socket_timeout_ms > 0) {
887
+ rd_snprintf(errstr, sizeof(errstr), "%d", socket_timeout_ms);
888
+ test_conf_set(conf, "socket.timeout.ms", errstr);
889
+ }
890
+
891
+ consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
892
+ TEST_ASSERT(consumer, "Failed to create consumer: %s", errstr);
893
+
894
+ subs = rd_kafka_topic_partition_list_new(1);
895
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
896
+ TEST_ASSERT(!rd_kafka_share_subscribe(consumer, subs),
897
+ "Subscribe failed");
898
+ rd_kafka_topic_partition_list_destroy(subs);
899
+
900
+ /* Consume all messages so share session is established with every
901
+ * partition leader (i.e. all 3 brokers). */
902
+ TEST_SAY("Consuming %d messages across %d partitions...\n", total_msgs,
903
+ partition_cnt);
904
+ while (consumed < total_msgs && attempts++ < 30) {
905
+ error = rd_kafka_share_poll(consumer, 3000, &batch);
906
+ if (error) {
907
+ rd_kafka_error_destroy(error);
908
+ continue;
909
+ }
910
+ rcvd = rd_kafka_messages_count(batch);
911
+ for (i = 0; i < rcvd; i++) {
912
+ rd_kafka_message_t *rkm =
913
+ rd_kafka_messages_get(batch, i);
914
+ if (rkm && !rkm->err)
915
+ consumed++;
916
+ }
917
+ rd_kafka_messages_destroy(batch);
918
+ batch = NULL;
919
+ }
920
+
921
+ TEST_ASSERT(consumed == total_msgs, "Expected %d messages, got %d",
922
+ total_msgs, consumed);
923
+ TEST_SAY("Consumed all %d messages\n", consumed);
924
+
925
+ *out_mcluster = mcluster;
926
+ *out_consumer = consumer;
927
+ }
928
+
929
+ /** is_fatal_cb hook scoped to test_close_with_broker_down: ignores
930
+ * the transport error and cascading ALL_BROKERS_DOWN that result from
931
+ * taking the mock broker down mid-close. */
932
+ static int test_close_with_broker_down_is_fatal_cb(rd_kafka_t *rk,
933
+ rd_kafka_resp_err_t err,
934
+ const char *reason) {
935
+ if (err == RD_KAFKA_RESP_ERR__TRANSPORT ||
936
+ err == RD_KAFKA_RESP_ERR__ALL_BROKERS_DOWN) {
937
+ TEST_SAY("Ignoring expected error: %s: %s\n",
938
+ rd_kafka_err2name(err), reason);
939
+ return 0;
940
+ }
941
+ return 1;
942
+ }
943
+
944
+ /*#################################################################################*/
945
+
946
+ /**
947
+ * @brief Close with acknowledge test scenarios
948
+ *
949
+ * Tests consumer close behavior with different commit modes and topologies.
950
+ * Verifies that acknowledged messages are not redelivered to a second
951
+ consumer.
952
+ */
953
+ static void do_test_close_with_acknowledge(void) {
954
+ /**
955
+ * @brief Test configuration for close with acknowledge scenarios
956
+ */
957
+ typedef struct {
958
+ const char *test_name;
959
+ int topic_cnt;
960
+ int partitions[MAX_TOPICS];
961
+ int msgs_per_partition;
962
+ commit_mode_t commit_mode;
963
+ int ack_count;
964
+ } close_ack_test_config_t;
965
+
966
+ /* Test matrix: 3 commit modes × 4 topologies = 12 tests */
967
+ close_ack_test_config_t tests[] = {
968
+ /* 1 topic, 1 partition */
969
+ {"close-1t1p-no-commit", 1, {1}, 20, COMMIT_MODE_NONE, 10},
970
+ {"close-1t1p-commit-async", 1, {1}, 20, COMMIT_MODE_ASYNC, 10},
971
+ {"close-1t1p-commit-sync", 1, {1}, 20, COMMIT_MODE_SYNC, 10},
972
+
973
+ /* 1 topic, multiple partitions */
974
+ {"close-1t3p-no-commit", 1, {3}, 10, COMMIT_MODE_NONE, 20},
975
+ {"close-1t3p-commit-async", 1, {3}, 10, COMMIT_MODE_ASYNC, 20},
976
+ {"close-1t3p-commit-sync", 1, {3}, 10, COMMIT_MODE_SYNC, 15},
977
+
978
+ /* Multiple topics, 1 partition each */
979
+ {"close-3t1p-no-commit", 3, {1, 1, 1}, 10, COMMIT_MODE_NONE, 20},
980
+ {"close-3t1p-commit-async",
981
+ 3,
982
+ {1, 1, 1},
983
+ 10,
984
+ COMMIT_MODE_ASYNC,
985
+ 20},
986
+ {"close-3t1p-commit-sync", 3, {1, 1, 1}, 10, COMMIT_MODE_SYNC, 20},
987
+
988
+ /* Multiple topics, multiple partitions */
989
+ {"close-2t2p-no-commit", 2, {2, 2}, 10, COMMIT_MODE_NONE, 20},
990
+ {"close-2t2p-commit-async", 2, {2, 2}, 10, COMMIT_MODE_ASYNC, 20},
991
+ {"close-2t2p-commit-sync", 2, {2, 2}, 10, COMMIT_MODE_SYNC, 20},
992
+ };
993
+
994
+ SUB_TEST();
995
+
996
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
997
+ close_ack_test_config_t *config = &tests[i];
998
+ test_context_t ctx = {0};
999
+ rd_kafka_share_t *c1, *c2;
1000
+ tracked_msg_t tracked_msgs[BATCH_SIZE];
1001
+ int tracked_cnt = 0;
1002
+ ack_receipts_t receipts;
1003
+
1004
+ ack_receipts_init(&receipts);
1005
+
1006
+ TEST_SAY("\n========================================\n");
1007
+ TEST_SAY("Test: %s\n", config->test_name);
1008
+ TEST_SAY("Topology: %d topic(s), partitions: [",
1009
+ config->topic_cnt);
1010
+ for (int j = 0; j < config->topic_cnt; j++) {
1011
+ TEST_SAY("%d%s", config->partitions[j],
1012
+ j < config->topic_cnt - 1 ? ", " : "");
1013
+ }
1014
+ TEST_SAY("]\n");
1015
+ TEST_SAY("Commit mode: %s\n",
1016
+ commit_mode_str(config->commit_mode));
1017
+ TEST_SAY("========================================\n\n");
1018
+
1019
+ ctx.group_id = "0178-group";
1020
+ setup_topics_and_produce(&ctx, config->topic_cnt,
1021
+ config->partitions,
1022
+ config->msgs_per_partition);
1023
+
1024
+ /* Set group offset to earliest */
1025
+ test_share_set_auto_offset_reset(ctx.group_id, "earliest");
1026
+
1027
+ /* Create C1 with explicit ack mode (will call acknowledge()
1028
+ * explicitly). C2 will be created after C1 closes. */
1029
+ c1 = create_share_consumer_with_receipts(ctx.group_id,
1030
+ "explicit", &receipts);
1031
+
1032
+ /* Subscribe C1 only - C2 will subscribe after C1 closes */
1033
+ subscribe_consumer(c1, ctx.topic_names, ctx.topic_cnt);
1034
+
1035
+ /* C1: Consume and acknowledge */
1036
+ consume_and_track(c1, "C1", config->ack_count, rd_true,
1037
+ tracked_msgs, &tracked_cnt);
1038
+
1039
+ /* Execute commit */
1040
+ perform_commit(c1, "C1", config->commit_mode, &receipts);
1041
+
1042
+ /* Close C1 - drains any outstanding ack callbacks
1043
+ * synchronously, so receipts are settled by the time close
1044
+ * returns. */
1045
+ TEST_SAY("C1: Closing consumer\n");
1046
+ rd_kafka_error_t *c1_close_err =
1047
+ rd_kafka_share_consumer_close(c1);
1048
+ TEST_ASSERT(c1_close_err == NULL,
1049
+ "C1: close returned error: %s",
1050
+ rd_kafka_error_string(c1_close_err));
1051
+ TEST_SAY("C1: Closed successfully\n");
1052
+
1053
+ /* Verify the union of callback + sync receipts equals the
1054
+ * set of messages C1 acknowledged. */
1055
+ verify_receipts_match(&receipts, tracked_msgs, tracked_cnt,
1056
+ commit_mode_str(config->commit_mode));
1057
+
1058
+ /* Create C2 with implicit ack mode after C1 is fully destroyed
1059
+ */
1060
+ TEST_SAY("C2: Creating and subscribing after C1 close\n");
1061
+ c2 = test_create_share_consumer(ctx.group_id, "implicit");
1062
+ subscribe_consumer(c2, ctx.topic_names, ctx.topic_cnt);
1063
+
1064
+ /* C2: Consume and verify no redelivery */
1065
+ verify_tracked_messages(c2, "C2",
1066
+ ctx.total_msgs_produced - tracked_cnt,
1067
+ tracked_msgs, tracked_cnt, rd_false,
1068
+ commit_mode_str(config->commit_mode));
1069
+
1070
+ /* Close C2 */
1071
+ TEST_SAY("C2: Closing consumer\n");
1072
+ rd_kafka_share_consumer_close(c2);
1073
+ TEST_SAY("C2: Closed successfully\n");
1074
+ test_share_destroy(c2);
1075
+
1076
+
1077
+ /* Cleanup */
1078
+ free_tracked_messages(tracked_msgs, tracked_cnt);
1079
+ ack_receipts_destroy(&receipts);
1080
+
1081
+ for (int t = 0; t < ctx.topic_cnt; t++) {
1082
+ rd_free(ctx.topic_names[t]);
1083
+ }
1084
+
1085
+ TEST_SAY("Test %s completed successfully\n\n",
1086
+ config->test_name);
1087
+ test_share_destroy(c1);
1088
+ }
1089
+
1090
+ SUB_TEST_PASS();
1091
+ }
1092
+
1093
+ /**
1094
+ * @brief Test close without acknowledge
1095
+ *
1096
+ * Verifies that when a consumer closes without acknowledging consumed records,
1097
+ * those records are released back to the share group and can be consumed by
1098
+ * another consumer.
1099
+ *
1100
+ * Test flow:
1101
+ * 1. C1 consumes records but does NOT acknowledge them
1102
+ * 2. C1 closes (should release the unacknowledged records)
1103
+ * 3. C2 should be able to consume those same records
1104
+ *
1105
+ * Tests multiple topologies: 1t1p, 1t3p, 3t1p, 2t2p
1106
+ */
1107
+ static void do_test_close_without_acknowledge() {
1108
+ /**
1109
+ * @brief Test configuration for close without acknowledge scenarios
1110
+ */
1111
+ typedef struct {
1112
+ const char *test_name;
1113
+ int topic_cnt;
1114
+ int partitions[MAX_TOPICS];
1115
+ int msgs_per_partition;
1116
+ int track_count;
1117
+ } close_no_ack_test_config_t;
1118
+
1119
+ /* Test matrix: various topologies */
1120
+ close_no_ack_test_config_t tests[] = {
1121
+ /* 1 topic, 1 partition */
1122
+ {"close-no-ack-1t1p", 1, {1}, 20, 5},
1123
+
1124
+ /* 1 topic, multiple partitions */
1125
+ {"close-no-ack-1t3p", 1, {3}, 10, 5},
1126
+
1127
+ /* Multiple topics, 1 partition each */
1128
+ {"close-no-ack-3t1p", 3, {1, 1, 1}, 10, 5},
1129
+
1130
+ /* Multiple topics, multiple partitions */
1131
+ {"close-no-ack-2t2p", 2, {2, 2}, 10, 5},
1132
+ };
1133
+
1134
+ SUB_TEST();
1135
+
1136
+ for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
1137
+ close_no_ack_test_config_t *config = &tests[i];
1138
+ test_context_t ctx = {0};
1139
+ rd_kafka_share_t *c1, *c2;
1140
+ tracked_msg_t tracked_msgs[BATCH_SIZE];
1141
+ int tracked_cnt = 0;
1142
+ char group_id[64];
1143
+ ack_receipts_t receipts;
1144
+
1145
+ ack_receipts_init(&receipts);
1146
+
1147
+ TEST_SAY("\n========================================\n");
1148
+ TEST_SAY("Topology: %d topic(s), partitions: [",
1149
+ config->topic_cnt);
1150
+ for (int j = 0; j < config->topic_cnt; j++) {
1151
+ TEST_SAY("%d%s", config->partitions[j],
1152
+ j < config->topic_cnt - 1 ? ", " : "");
1153
+ }
1154
+ TEST_SAY("]\n");
1155
+ TEST_SAY("========================================\n\n");
1156
+
1157
+ rd_snprintf(group_id, sizeof(group_id),
1158
+ "0178-group-no-ack-%" PRIu64, test_id_generate());
1159
+ ctx.group_id = group_id;
1160
+ setup_topics_and_produce(&ctx, config->topic_cnt,
1161
+ config->partitions,
1162
+ config->msgs_per_partition);
1163
+
1164
+ test_share_set_auto_offset_reset(ctx.group_id, "earliest");
1165
+
1166
+ c1 = create_share_consumer_with_receipts(ctx.group_id,
1167
+ "implicit", &receipts);
1168
+ subscribe_consumer(c1, ctx.topic_names, ctx.topic_cnt);
1169
+
1170
+ /* C1: Consume and track without acknowledging. */
1171
+ consume_and_track(c1, "C1", config->track_count, rd_false,
1172
+ tracked_msgs, &tracked_cnt);
1173
+
1174
+ /* Close C1 - unacked records must be released back to the
1175
+ * share group. No acks were issued, so we expect zero
1176
+ * receipts after close completes. */
1177
+ TEST_SAY("C1: Closing without acknowledging\n");
1178
+ rd_kafka_error_t *c1_close_err =
1179
+ rd_kafka_share_consumer_close(c1);
1180
+ TEST_ASSERT(c1_close_err == NULL,
1181
+ "C1: close returned error: %s",
1182
+ rd_kafka_error_string(c1_close_err));
1183
+
1184
+ /* No acks were issued, so the receipts sink must be empty. */
1185
+ verify_receipts_match(&receipts, NULL, 0, "no-ack-close");
1186
+
1187
+ c2 = test_create_share_consumer(ctx.group_id, "implicit");
1188
+ subscribe_consumer(c2, ctx.topic_names, ctx.topic_cnt);
1189
+
1190
+ /* C2: Verify every tracked message is redelivered. Cap the
1191
+ * scan at total produced so we don't loop forever if redelivery
1192
+ * is broken. */
1193
+ verify_tracked_messages(c2, "C2", ctx.total_msgs_produced,
1194
+ tracked_msgs, tracked_cnt, rd_true,
1195
+ "no-ack-close");
1196
+
1197
+ TEST_SAY("C2: Closing consumer\n");
1198
+ rd_kafka_share_consumer_close(c2);
1199
+ test_share_destroy(c2);
1200
+
1201
+ free_tracked_messages(tracked_msgs, tracked_cnt);
1202
+ ack_receipts_destroy(&receipts);
1203
+
1204
+ for (int t = 0; t < ctx.topic_cnt; t++) {
1205
+ rd_free(ctx.topic_names[t]);
1206
+ }
1207
+
1208
+ TEST_SAY("Test %s completed successfully\n\n",
1209
+ config->test_name);
1210
+ test_share_destroy(c1);
1211
+ }
1212
+
1213
+ SUB_TEST_PASS();
1214
+ }
1215
+
1216
+ /**
1217
+ * @brief Test close with slow broker response
1218
+ *
1219
+ * Verifies that close() waits for the broker to respond to the
1220
+ * session close request (sent to partition leader), even when
1221
+ * the broker is slow to respond.
1222
+ *
1223
+ * Tests 3 scenarios with a 3-broker setup:
1224
+ * 1. Only broker 1 has delayed response (10s)
1225
+ * 2. Brokers 1 and 2 have delayed responses (10s each)
1226
+ * 3. All 3 brokers have delayed responses (10s each)
1227
+ *
1228
+ * In each case, close() should wait for the response and complete
1229
+ * successfully after the delay.
1230
+ */
1231
+ static void do_test_close_with_slow_broker_response(void) {
1232
+ typedef struct {
1233
+ const char *test_name;
1234
+ int delayed_broker_cnt;
1235
+ int32_t delayed_brokers[3];
1236
+ } delay_test_config_t;
1237
+
1238
+ delay_test_config_t tests[] = {
1239
+ {"1-broker-delayed", 1, {1, 0, 0}},
1240
+ {"2-brokers-delayed", 2, {1, 2, 0}},
1241
+ {"3-brokers-delayed", 3, {1, 2, 3}},
1242
+ };
1243
+
1244
+ for (size_t test_idx = 0; test_idx < sizeof(tests) / sizeof(tests[0]);
1245
+ test_idx++) {
1246
+ delay_test_config_t *config = &tests[test_idx];
1247
+ rd_kafka_mock_cluster_t *mcluster;
1248
+ rd_kafka_share_t *consumer;
1249
+ const int rtt_delay_ms = 10000; /* 10 seconds */
1250
+ rd_ts_t t_start, t_elapsed_ms;
1251
+ int i;
1252
+
1253
+ TEST_SAY("\n========================================\n");
1254
+ TEST_SAY("Test: %s\n", config->test_name);
1255
+ TEST_SAY("Delayed brokers: %d\n", config->delayed_broker_cnt);
1256
+ TEST_SAY("========================================\n\n");
1257
+
1258
+ SUB_TEST("%s", config->test_name);
1259
+
1260
+ setup_3broker_share_consumer(config->test_name, 0, &mcluster,
1261
+ &consumer);
1262
+
1263
+ /* Inject RTT delay for ShareAck request on specified brokers;
1264
+ * the session-close request */
1265
+ for (i = 0; i < config->delayed_broker_cnt; i++) {
1266
+ TEST_ASSERT(
1267
+ rd_kafka_mock_broker_push_request_error_rtts(
1268
+ mcluster, config->delayed_brokers[i],
1269
+ RD_KAFKAP_ShareAcknowledge, 1,
1270
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1271
+ rtt_delay_ms) == RD_KAFKA_RESP_ERR_NO_ERROR,
1272
+ "Failed to inject ShareAcknowledge delay");
1273
+ }
1274
+
1275
+ TEST_SAY(
1276
+ "Calling close() with delayed broker response(s)...\n");
1277
+ t_start = test_clock();
1278
+ rd_kafka_share_consumer_close(consumer);
1279
+ t_elapsed_ms = (test_clock() - t_start) / 1000;
1280
+
1281
+ TEST_SAY("Close completed after %" PRId64 " ms\n",
1282
+ t_elapsed_ms);
1283
+
1284
+ /* close() should wait ~rtt_delay_ms for the slowest delayed
1285
+ * broker. Allow (rtt - 1s) to (rtt + 2s). */
1286
+ int64_t min_expected_ms = rtt_delay_ms - 1000;
1287
+ int64_t max_expected_ms = rtt_delay_ms + 2000;
1288
+
1289
+ TEST_ASSERT(t_elapsed_ms >= min_expected_ms &&
1290
+ t_elapsed_ms <= max_expected_ms,
1291
+ "Close took %" PRId64
1292
+ " ms, expected between %" PRId64 " and %" PRId64
1293
+ " ms (with %d broker(s) delayed by %dms)",
1294
+ t_elapsed_ms, min_expected_ms, max_expected_ms,
1295
+ config->delayed_broker_cnt, rtt_delay_ms);
1296
+
1297
+ TEST_SAY(
1298
+ "SUCCESS: Close waited approximately %dms for "
1299
+ "delayed broker response(s)\n",
1300
+ rtt_delay_ms);
1301
+
1302
+ test_share_destroy(consumer);
1303
+ test_mock_cluster_destroy(mcluster);
1304
+
1305
+ SUB_TEST_PASS();
1306
+ }
1307
+ }
1308
+
1309
+ /**
1310
+ * @brief Test close respects socket timeout with very slow broker(s).
1311
+ *
1312
+ * Verifies that when broker(s) take longer than socket.timeout.ms to
1313
+ * respond to the session close request, close() times out at
1314
+ * socket.timeout.ms and does not wait for the full broker delay.
1315
+ *
1316
+ * Tests 3 scenarios with a 3-broker setup:
1317
+ * 1. Only broker 1 has delayed response (40s, exceeds 20s socket timeout)
1318
+ * 2. Brokers 1 and 2 have delayed responses
1319
+ * 3. All 3 brokers have delayed responses
1320
+ */
1321
+ static void do_test_close_respects_socket_timeout(void) {
1322
+ typedef struct {
1323
+ const char *test_name;
1324
+ int delayed_broker_cnt;
1325
+ int32_t delayed_brokers[3];
1326
+ } delay_test_config_t;
1327
+
1328
+ delay_test_config_t tests[] = {
1329
+ {"close-timeout-1-broker", 1, {1, 0, 0}},
1330
+ {"close-timeout-2-brokers", 2, {1, 2, 0}},
1331
+ {"close-timeout-3-brokers", 3, {1, 2, 3}},
1332
+ };
1333
+
1334
+ const int rtt_delay_ms = 40000; /* 40s — exceeds socket.timeout */
1335
+ const int socket_timeout_ms = 20000; /* 20s */
1336
+
1337
+ for (size_t test_idx = 0; test_idx < sizeof(tests) / sizeof(tests[0]);
1338
+ test_idx++) {
1339
+ delay_test_config_t *config = &tests[test_idx];
1340
+ rd_kafka_mock_cluster_t *mcluster;
1341
+ rd_kafka_share_t *consumer;
1342
+ rd_ts_t t_start, t_elapsed_ms;
1343
+ int i;
1344
+
1345
+ TEST_SAY("\n========================================\n");
1346
+ TEST_SAY("Test: %s\n", config->test_name);
1347
+ TEST_SAY(
1348
+ "Delayed brokers: %d, RTT: %dms, Socket timeout: %dms\n",
1349
+ config->delayed_broker_cnt, rtt_delay_ms,
1350
+ socket_timeout_ms);
1351
+ TEST_SAY("========================================\n\n");
1352
+
1353
+ SUB_TEST("%s", config->test_name);
1354
+
1355
+ setup_3broker_share_consumer(
1356
+ config->test_name, socket_timeout_ms, &mcluster, &consumer);
1357
+
1358
+ for (i = 0; i < config->delayed_broker_cnt; i++) {
1359
+ TEST_SAY(
1360
+ "Injecting %dms RTT delay on broker %d "
1361
+ "(exceeds socket timeout %dms)\n",
1362
+ rtt_delay_ms, config->delayed_brokers[i],
1363
+ socket_timeout_ms);
1364
+ rd_kafka_mock_broker_set_rtt(
1365
+ mcluster, config->delayed_brokers[i], rtt_delay_ms);
1366
+ }
1367
+
1368
+ TEST_SAY("Calling close() - should timeout around %dms...\n",
1369
+ socket_timeout_ms);
1370
+ t_start = test_clock();
1371
+ rd_kafka_share_consumer_close(consumer);
1372
+ t_elapsed_ms = (test_clock() - t_start) / 1000;
1373
+
1374
+ TEST_SAY("Close completed after %" PRId64 " ms\n",
1375
+ t_elapsed_ms);
1376
+
1377
+ /* close() should timeout around socket.timeout.ms.
1378
+ * Allow ±5s margin. */
1379
+ int64_t min_expected_ms = socket_timeout_ms - 5000;
1380
+ int64_t max_expected_ms = socket_timeout_ms + 5000;
1381
+
1382
+ TEST_ASSERT(t_elapsed_ms >= min_expected_ms &&
1383
+ t_elapsed_ms <= max_expected_ms,
1384
+ "Close took %" PRId64
1385
+ " ms, expected ~%dms (between %" PRId64
1386
+ " and %" PRId64
1387
+ " ms). Should NOT wait for full broker delay "
1388
+ "of %dms",
1389
+ t_elapsed_ms, socket_timeout_ms, min_expected_ms,
1390
+ max_expected_ms, rtt_delay_ms);
1391
+
1392
+ /* Defensively verify it did not wait the full broker delay. */
1393
+ TEST_ASSERT(t_elapsed_ms < (rtt_delay_ms - 10000),
1394
+ "Close took %" PRId64
1395
+ " ms — too close to broker delay %dms. "
1396
+ "Should have timed out at socket.timeout.ms=%dms",
1397
+ t_elapsed_ms, rtt_delay_ms, socket_timeout_ms);
1398
+
1399
+ TEST_SAY(
1400
+ "SUCCESS: Close respected socket timeout (%dms) with "
1401
+ "%d delayed broker(s)\n",
1402
+ socket_timeout_ms, config->delayed_broker_cnt);
1403
+
1404
+ test_share_destroy(consumer);
1405
+ test_mock_cluster_destroy(mcluster);
1406
+
1407
+ SUB_TEST_PASS();
1408
+ }
1409
+ }
1410
+
1411
+ /**
1412
+ * @brief Test close with broker error response.
1413
+ *
1414
+ * Verifies that close() handles error responses from the broker
1415
+ * gracefully. When the broker(s) respond with an error to the
1416
+ * ShareAcknowledge request (session close request), close() should
1417
+ * complete without retrying.
1418
+ *
1419
+ * Tests 3 scenarios with a 3-broker setup:
1420
+ * 1. Only broker 1 returns LEADER_NOT_AVAILABLE
1421
+ * 2. Brokers 1 and 2 return the error
1422
+ * 3. All 3 brokers return the error
1423
+ */
1424
+ static void do_test_close_with_broker_error_response(void) {
1425
+ typedef struct {
1426
+ const char *test_name;
1427
+ int erroring_broker_cnt;
1428
+ int32_t erroring_brokers[3];
1429
+ } error_test_config_t;
1430
+
1431
+ error_test_config_t tests[] = {
1432
+ {"close-error-1-broker", 1, {1, 0, 0}},
1433
+ {"close-error-2-brokers", 2, {1, 2, 0}},
1434
+ {"close-error-3-brokers", 3, {1, 2, 3}},
1435
+ };
1436
+
1437
+ const rd_kafka_resp_err_t error_code =
1438
+ RD_KAFKA_RESP_ERR_LEADER_NOT_AVAILABLE;
1439
+
1440
+ for (size_t test_idx = 0; test_idx < sizeof(tests) / sizeof(tests[0]);
1441
+ test_idx++) {
1442
+ error_test_config_t *config = &tests[test_idx];
1443
+ rd_kafka_mock_cluster_t *mcluster;
1444
+ rd_kafka_share_t *consumer;
1445
+ int i;
1446
+
1447
+ TEST_SAY("\n========================================\n");
1448
+ TEST_SAY("Test: %s\n", config->test_name);
1449
+ TEST_SAY("Erroring brokers: %d, error code: %s\n",
1450
+ config->erroring_broker_cnt,
1451
+ rd_kafka_err2str(error_code));
1452
+ TEST_SAY("========================================\n\n");
1453
+
1454
+ SUB_TEST("%s", config->test_name);
1455
+
1456
+ setup_3broker_share_consumer(config->test_name, 0, &mcluster,
1457
+ &consumer);
1458
+
1459
+ /* Push the error onto the specified brokers' next
1460
+ * ShareAcknowledge request. */
1461
+ for (i = 0; i < config->erroring_broker_cnt; i++) {
1462
+ TEST_SAY("Injecting %s error on broker %d\n",
1463
+ rd_kafka_err2str(error_code),
1464
+ config->erroring_brokers[i]);
1465
+ rd_kafka_mock_broker_push_request_error_rtts(
1466
+ mcluster, config->erroring_brokers[i],
1467
+ RD_KAFKAP_ShareAcknowledge, 1, error_code, 0);
1468
+ }
1469
+
1470
+ TEST_SAY(
1471
+ "Calling close() - should complete despite error(s)...\n");
1472
+ rd_kafka_share_consumer_close(consumer);
1473
+ TEST_SAY("Close completed\n");
1474
+
1475
+ TEST_SAY(
1476
+ "SUCCESS: Close handled error %s gracefully on %d "
1477
+ "broker(s) (no retry)\n",
1478
+ rd_kafka_err2str(error_code), config->erroring_broker_cnt);
1479
+
1480
+ test_share_destroy(consumer);
1481
+ test_mock_cluster_destroy(mcluster);
1482
+
1483
+ SUB_TEST_PASS();
1484
+ }
1485
+ }
1486
+
1487
+
1488
+ /**
1489
+ * @brief Test close() when brokers have pending acks queued.
1490
+ *
1491
+ * Setup:
1492
+ * - 3-broker mock cluster, topic with 3 partitions (one leader per broker).
1493
+ * - Inject 5s RTT delay on ShareAcknowledge for all brokers — this makes
1494
+ * every ack the consumer sends take 5s broker-side.
1495
+ * - Produce 30 msgs (10/partition), consume them all with an implicit-ack
1496
+ * consumer. Implicit-ack piggybacks acks on the next ShareFetch, so by
1497
+ * the end of the first round there are still acks in flight or queued.
1498
+ * - Repeat: produce another 30, consume them all (same consumer).
1499
+ * - Call close(). close() should drain the in-flight ShareAcknowledges
1500
+ * and send the session-leave; with the 5s delay this should take
1501
+ * roughly 5s (remaining ack RTT) + 5s (leave RTT) = ~10s.
1502
+ * - Remove the RTT delay, start a second consumer (also implicit ack)
1503
+ * and verify it receives 0 messages over up to 5 fetch attempts. If
1504
+ * any message is delivered, close() failed to send the acks.
1505
+ */
1506
+ static void do_test_close_with_broker_busy(void) {
1507
+ const char *test_name = "close-broker-busy";
1508
+ const int partition_cnt = 3;
1509
+ const int msgs_per_partition = 10;
1510
+ const int total_msgs = partition_cnt * msgs_per_partition;
1511
+ const int rtt_delay_ms = 5000;
1512
+ rd_kafka_mock_cluster_t *mcluster;
1513
+ const char *bootstraps;
1514
+ rd_kafka_share_t *consumer, *c2;
1515
+ rd_kafka_conf_t *conf;
1516
+ rd_kafka_topic_partition_list_t *subs;
1517
+ rd_kafka_messages_t *batch = NULL;
1518
+ rd_kafka_error_t *error, *close_err;
1519
+ char topic[64], group[64], errstr[512];
1520
+ int iter, p, i;
1521
+ size_t rcvd;
1522
+ rd_ts_t t_start, t_elapsed_ms;
1523
+
1524
+ SUB_TEST("%s", test_name);
1525
+
1526
+ rd_snprintf(topic, sizeof(topic), "mock-%s", test_name);
1527
+ rd_snprintf(group, sizeof(group), "sg-%s", test_name);
1528
+
1529
+ TEST_SAY("\n========================================\n");
1530
+ TEST_SAY("Test: %s\n", test_name);
1531
+ TEST_SAY("ShareAck RTT: %dms on all brokers\n", rtt_delay_ms);
1532
+ TEST_SAY("========================================\n\n");
1533
+
1534
+ /* Cluster + APIs + topic */
1535
+ mcluster = test_mock_cluster_new(3, &bootstraps);
1536
+ rd_kafka_mock_sharegroup_set_auto_offset_reset(mcluster, 1);
1537
+
1538
+ TEST_ASSERT(rd_kafka_mock_topic_create(mcluster, topic, partition_cnt,
1539
+ 1) == RD_KAFKA_RESP_ERR_NO_ERROR,
1540
+ "Failed to create mock topic");
1541
+
1542
+ /* Implicit-ack consumer */
1543
+ test_conf_init(&conf, NULL, 0);
1544
+ test_conf_set(conf, "bootstrap.servers", bootstraps);
1545
+ test_conf_set(conf, "group.id", group);
1546
+ test_conf_set(conf, "share.acknowledgement.mode", "implicit");
1547
+ consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
1548
+ TEST_ASSERT(consumer, "Failed to create consumer: %s", errstr);
1549
+
1550
+ subs = rd_kafka_topic_partition_list_new(1);
1551
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1552
+ TEST_ASSERT(!rd_kafka_share_subscribe(consumer, subs),
1553
+ "Subscribe failed");
1554
+ rd_kafka_topic_partition_list_destroy(subs);
1555
+
1556
+ /* Inject 5s RTT delay on the next 20 ShareAcknowledge responses on
1557
+ * each broker (no error, just delay) */
1558
+ TEST_SAY(
1559
+ "Injecting %dms RTT delay on ShareAcknowledge "
1560
+ "(20 entries/broker)\n",
1561
+ rtt_delay_ms);
1562
+ for (i = 1; i <= 3; i++) {
1563
+ int j;
1564
+ for (j = 0; j < 20; j++) {
1565
+ TEST_ASSERT(
1566
+ rd_kafka_mock_broker_push_request_error_rtts(
1567
+ mcluster, i, RD_KAFKAP_ShareAcknowledge, 1,
1568
+ (rd_kafka_resp_err_t)0,
1569
+ rtt_delay_ms) == RD_KAFKA_RESP_ERR_NO_ERROR,
1570
+ "Failed to push RTT delay on broker %d", i);
1571
+ }
1572
+ }
1573
+
1574
+ /* Two rounds of produce+consume so that by the end there are acks
1575
+ * queued/in-flight on every broker. */
1576
+ for (iter = 1; iter <= 2; iter++) {
1577
+ int consumed = 0, attempts = 0;
1578
+ TEST_SAY(
1579
+ "Iteration %d: producing %d msgs across %d "
1580
+ "partitions\n",
1581
+ iter, total_msgs, partition_cnt);
1582
+ for (p = 0; p < partition_cnt; p++) {
1583
+ test_produce_msgs_easy_v(
1584
+ topic, 0, p, iter * msgs_per_partition,
1585
+ msgs_per_partition, 16, "bootstrap.servers",
1586
+ bootstraps, NULL);
1587
+ }
1588
+
1589
+ TEST_SAY("Iteration %d: consuming %d msgs\n", iter, total_msgs);
1590
+ while (consumed < total_msgs && attempts++ < 30) {
1591
+ error = rd_kafka_share_poll(consumer, 3000, &batch);
1592
+ if (error) {
1593
+ rd_kafka_error_destroy(error);
1594
+ continue;
1595
+ }
1596
+ rcvd = rd_kafka_messages_count(batch);
1597
+ for (i = 0; i < (int)rcvd; i++) {
1598
+ rd_kafka_message_t *rkm =
1599
+ rd_kafka_messages_get(batch, i);
1600
+ if (rkm && !rkm->err)
1601
+ consumed++;
1602
+ }
1603
+ rd_kafka_messages_destroy(batch);
1604
+ batch = NULL;
1605
+ TEST_ASSERT(rd_kafka_share_commit_async(consumer) ==
1606
+ NULL,
1607
+ "fail to call commit async");
1608
+ }
1609
+ TEST_ASSERT(consumed == total_msgs,
1610
+ "Iteration %d: expected %d msgs, got %d", iter,
1611
+ total_msgs, consumed);
1612
+ TEST_SAY("Iteration %d: consumed all %d msgs\n", iter,
1613
+ consumed);
1614
+ }
1615
+
1616
+ /* Call close. Expect ~10s: ~5s for in-flight ack to drain, then
1617
+ * ~5s for the session-leave ShareAcknowledge to come back. */
1618
+ TEST_SAY("Calling close() - expecting ~10s\n");
1619
+ t_start = test_clock();
1620
+ close_err = rd_kafka_share_consumer_close(consumer);
1621
+ t_elapsed_ms = (test_clock() - t_start) / 1000;
1622
+
1623
+ TEST_SAY("Close completed after %" PRId64 " ms\n", t_elapsed_ms);
1624
+ TEST_ASSERT(close_err == NULL, "Close returned error: %s",
1625
+ rd_kafka_error_string(close_err));
1626
+ TEST_ASSERT(t_elapsed_ms >= 8000 && t_elapsed_ms <= 12000,
1627
+ "Close took %" PRId64 " ms, expected 8000-12000 ms",
1628
+ t_elapsed_ms);
1629
+
1630
+ test_share_destroy(consumer);
1631
+
1632
+ /* Verify nothing was redelivered: c2 should consume 0 messages
1633
+ * because c1's close() flushed all acks. */
1634
+ test_conf_init(&conf, NULL, 0);
1635
+ test_conf_set(conf, "bootstrap.servers", bootstraps);
1636
+ test_conf_set(conf, "group.id", group);
1637
+ test_conf_set(conf, "share.acknowledgement.mode", "implicit");
1638
+ c2 = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
1639
+ TEST_ASSERT(c2, "Failed to create c2: %s", errstr);
1640
+
1641
+ subs = rd_kafka_topic_partition_list_new(1);
1642
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1643
+ TEST_ASSERT(!rd_kafka_share_subscribe(c2, subs), "c2 subscribe failed");
1644
+ rd_kafka_topic_partition_list_destroy(subs);
1645
+
1646
+ TEST_SAY("c2: verifying no msgs redelivered (5 fetch attempts)\n");
1647
+ for (i = 0; i < 5; i++) {
1648
+ error = rd_kafka_share_poll(c2, 2000, &batch);
1649
+ if (error) {
1650
+ TEST_SAY("c2 attempt %d: error: %s\n", i + 1,
1651
+ rd_kafka_error_string(error));
1652
+ rd_kafka_error_destroy(error);
1653
+ continue;
1654
+ }
1655
+ rcvd = rd_kafka_messages_count(batch);
1656
+ rd_kafka_messages_destroy(batch);
1657
+ batch = NULL;
1658
+ TEST_ASSERT(
1659
+ rcvd == 0,
1660
+ "c2 attempt %d: received %d msgs (expected 0 — close "
1661
+ "did not flush acks)",
1662
+ i + 1, (int)rcvd);
1663
+ }
1664
+
1665
+ TEST_SAY("SUCCESS: close() flushed acks; c2 received no msgs\n");
1666
+
1667
+ test_share_destroy(c2);
1668
+ test_mock_cluster_destroy(mcluster);
1669
+
1670
+ SUB_TEST_PASS();
1671
+ }
1672
+
1673
+ /**
1674
+ * @brief Test close() when the broker is down.
1675
+ *
1676
+ * Verifies that close() returns immediately without an error
1677
+ * when the broker is down
1678
+ *
1679
+ * Setup:
1680
+ * - Consumer subscribes, consumes at least one batch, then we set the
1681
+ * broker down and call close().
1682
+ * - close() should return immediately and return NULL (no error).
1683
+ */
1684
+ static void do_test_close_with_broker_down(void) {
1685
+ rd_kafka_mock_cluster_t *mcluster;
1686
+ const char *bootstraps;
1687
+ rd_kafka_share_t *consumer;
1688
+ rd_kafka_conf_t *conf;
1689
+ const char *topic = "mock-close-broker-down";
1690
+ const char *group = "sg-close-broker-down";
1691
+ const int msgcnt = 10;
1692
+ const int socket_timeout_ms = 20000;
1693
+ rd_kafka_messages_t *batch = NULL;
1694
+ rd_kafka_error_t *error;
1695
+ rd_kafka_error_t *close_err;
1696
+ size_t rcvd;
1697
+ int attempts = 0;
1698
+ rd_bool_t got_msgs = rd_false;
1699
+ rd_ts_t t_start, t_elapsed_ms;
1700
+ char errstr[512];
1701
+
1702
+ SUB_TEST("close-with-broker-down");
1703
+
1704
+ /* Suppress the transport-error / all-brokers-down events that the
1705
+ * test framework's default error_cb would otherwise fail on once we
1706
+ * set the broker down. Scoped to this test only. */
1707
+ test_curr->is_fatal_cb = test_close_with_broker_down_is_fatal_cb;
1708
+
1709
+ TEST_SAY("\n========================================\n");
1710
+ TEST_SAY("Test: close with broker down\n");
1711
+ TEST_SAY("Socket timeout: %dms\n", socket_timeout_ms);
1712
+ TEST_SAY("========================================\n\n");
1713
+
1714
+ mcluster = test_mock_cluster_new(1, &bootstraps);
1715
+ rd_kafka_mock_sharegroup_set_auto_offset_reset(mcluster, 1);
1716
+
1717
+ TEST_ASSERT(rd_kafka_mock_topic_create(mcluster, topic, 1, 1) ==
1718
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1719
+ "Failed to create mock topic");
1720
+
1721
+ /* Produce msgcnt messages via test helper. */
1722
+ test_produce_msgs_easy_v(topic, 0, 0, 0, msgcnt, 16,
1723
+ "bootstrap.servers", bootstraps, NULL);
1724
+
1725
+ /* Consumer with implicit ack mode and explicit socket timeout. */
1726
+ test_conf_init(&conf, NULL, 0);
1727
+ test_conf_set(conf, "bootstrap.servers", bootstraps);
1728
+ test_conf_set(conf, "group.id", group);
1729
+ test_conf_set(conf, "share.acknowledgement.mode", "implicit");
1730
+
1731
+ consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
1732
+ TEST_ASSERT(consumer, "Failed to create consumer: %s", errstr);
1733
+
1734
+ rd_kafka_topic_partition_list_t *subs =
1735
+ rd_kafka_topic_partition_list_new(1);
1736
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1737
+ TEST_ASSERT(!rd_kafka_share_subscribe(consumer, subs),
1738
+ "Subscribe failed");
1739
+ rd_kafka_topic_partition_list_destroy(subs);
1740
+
1741
+ /* Consume until we get at least one batch of messages, then break.
1742
+ */
1743
+ TEST_SAY("Consuming until first non-empty batch...\n");
1744
+ while (!got_msgs && attempts++ < 30) {
1745
+ error = rd_kafka_share_poll(consumer, 3000, &batch);
1746
+ if (error) {
1747
+ rd_kafka_error_destroy(error);
1748
+ continue;
1749
+ }
1750
+
1751
+ rcvd = rd_kafka_messages_count(batch);
1752
+ if (rcvd > 0) {
1753
+ TEST_SAY("Received %d messages\n", (int)rcvd);
1754
+ rd_kafka_messages_destroy(batch);
1755
+ batch = NULL;
1756
+ got_msgs = rd_true;
1757
+ break;
1758
+ }
1759
+ rd_kafka_messages_destroy(batch);
1760
+ batch = NULL;
1761
+ }
1762
+
1763
+ TEST_ASSERT(got_msgs, "Expected to receive at least one message");
1764
+
1765
+ /* Bring the broker down before calling close(). */
1766
+ TEST_SAY("Setting broker 1 down\n");
1767
+ TEST_ASSERT(rd_kafka_mock_broker_set_down(mcluster, 1) ==
1768
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1769
+ "Failed to set broker down");
1770
+
1771
+ /* close() should fail fast (broker connection was closed) */
1772
+ TEST_SAY("Calling close() with broker down\n");
1773
+ t_start = test_clock();
1774
+ close_err = rd_kafka_share_consumer_close(consumer);
1775
+ t_elapsed_ms = (test_clock() - t_start) / 1000;
1776
+
1777
+ TEST_SAY("Close completed after %" PRId64 " ms (err=%s)\n",
1778
+ t_elapsed_ms,
1779
+ close_err ? rd_kafka_error_string(close_err) : "NULL");
1780
+
1781
+ if (close_err)
1782
+ rd_kafka_error_destroy(close_err);
1783
+
1784
+ TEST_SAY("SUCCESS: close() returned in %" PRId64
1785
+ "ms after broker down\n",
1786
+ t_elapsed_ms);
1787
+
1788
+ /* Bring the broker back up so the consumer's network threads can
1789
+ * finish their shutdown handshake cleanly during destroy. */
1790
+ TEST_ASSERT(rd_kafka_mock_broker_set_up(mcluster, 1) ==
1791
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1792
+ "Failed to set broker up");
1793
+
1794
+ test_share_destroy(consumer);
1795
+ test_mock_cluster_destroy(mcluster);
1796
+
1797
+ /* Restore default error-fatal behavior for subsequent tests. */
1798
+ test_curr->is_fatal_cb = NULL;
1799
+
1800
+ SUB_TEST_PASS();
1801
+ }
1802
+
1803
+ /**
1804
+ * @brief Test: calling share-consumer APIs after close() completes.
1805
+ *
1806
+ * Verifies every guarded API returns RD_KAFKA_RESP_ERR__STATE with
1807
+ * "closed" in the error string.
1808
+ */
1809
+ static void do_test_api_calls_on_closed_consumer(void) {
1810
+ rd_kafka_mock_cluster_t *mcluster;
1811
+ const char *bootstraps;
1812
+ rd_kafka_conf_t *conf;
1813
+ rd_kafka_share_t *consumer;
1814
+ const char *topic = "0178-api-after-close";
1815
+ const char *group = "grp-0178-api-after-close";
1816
+ char errstr[512];
1817
+ rd_kafka_error_t *close_error;
1818
+
1819
+ SUB_TEST_QUICK();
1820
+
1821
+ mcluster = test_mock_cluster_new(1, &bootstraps);
1822
+
1823
+ TEST_ASSERT(rd_kafka_mock_topic_create(mcluster, topic, 1, 1) ==
1824
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1825
+ "Create mock topic");
1826
+
1827
+ /* Produce a single message via test helper (creates an internal
1828
+ * producer for the duration of the call). */
1829
+ test_produce_msgs_easy_v(topic, 0, 0, 0, 1, 3, "bootstrap.servers",
1830
+ bootstraps, NULL);
1831
+
1832
+ /* Consumer */
1833
+ test_conf_init(&conf, NULL, 0);
1834
+ test_conf_set(conf, "bootstrap.servers", bootstraps);
1835
+ test_conf_set(conf, "group.id", group);
1836
+ consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
1837
+ TEST_ASSERT(consumer, "consumer: %s", errstr);
1838
+
1839
+ rd_kafka_topic_partition_list_t *subs =
1840
+ rd_kafka_topic_partition_list_new(1);
1841
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1842
+ TEST_ASSERT(!rd_kafka_share_subscribe(consumer, subs), "subscribe");
1843
+ rd_kafka_topic_partition_list_destroy(subs);
1844
+
1845
+ /* Close (blocking), then verify all APIs reject further calls. */
1846
+ TEST_SAY("Calling close() and waiting for completion\n");
1847
+ close_error = rd_kafka_share_consumer_close(consumer);
1848
+ TEST_ASSERT(close_error == NULL, "initial close: %s",
1849
+ close_error ? rd_kafka_error_string(close_error) : "");
1850
+ TEST_ASSERT(rd_kafka_share_consumer_closed(consumer),
1851
+ "consumer should report closed");
1852
+
1853
+ TEST_SAY("Exercising APIs on closed consumer\n");
1854
+ verify_all_apis_return_error(consumer, topic);
1855
+
1856
+ test_share_destroy(consumer);
1857
+ test_mock_cluster_destroy(mcluster);
1858
+
1859
+ SUB_TEST_PASS();
1860
+ }
1861
+
1862
+ /**
1863
+ * @brief Test: once close_queue() returns successfully, all subsequent API
1864
+ * calls must be rejected with RD_KAFKA_RESP_ERR__STATE.
1865
+ *
1866
+ * A 5s RTT delay is injected on ShareAcknowledge so the consumer remains
1867
+ * in the "closing" state (close_queue returns immediately, actual close
1868
+ * completes later), giving us a deterministic window to exercise every
1869
+ * guarded API.
1870
+ */
1871
+ static void do_test_api_calls_during_closing(void) {
1872
+ rd_kafka_mock_cluster_t *mcluster;
1873
+ const char *bootstraps;
1874
+ rd_kafka_conf_t *conf;
1875
+ rd_kafka_share_t *consumer;
1876
+ const char *topic = "0178-api-during-closing";
1877
+ const char *group = "grp-0178-api-during-closing";
1878
+ char errstr[512];
1879
+ rd_kafka_queue_t *queue;
1880
+ rd_kafka_error_t *close_error;
1881
+ const int rtt_delay_ms = 5000;
1882
+
1883
+ SUB_TEST_QUICK();
1884
+
1885
+ mcluster = test_mock_cluster_new(1, &bootstraps);
1886
+
1887
+ TEST_ASSERT(rd_kafka_mock_topic_create(mcluster, topic, 1, 1) ==
1888
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1889
+ "Create mock topic");
1890
+
1891
+ /* Produce one message via test helper. */
1892
+ test_produce_msgs_easy_v(topic, 0, 0, 0, 1, 3, "bootstrap.servers",
1893
+ bootstraps, NULL);
1894
+
1895
+ test_conf_init(&conf, NULL, 0);
1896
+ test_conf_set(conf, "bootstrap.servers", bootstraps);
1897
+ test_conf_set(conf, "group.id", group);
1898
+ consumer = rd_kafka_share_consumer_new(conf, errstr, sizeof(errstr));
1899
+ TEST_ASSERT(consumer, "consumer: %s", errstr);
1900
+
1901
+ rd_kafka_topic_partition_list_t *subs =
1902
+ rd_kafka_topic_partition_list_new(1);
1903
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
1904
+ TEST_ASSERT(!rd_kafka_share_subscribe(consumer, subs), "subscribe");
1905
+ rd_kafka_topic_partition_list_destroy(subs);
1906
+
1907
+ /* Consume one batch so there's state to flush on close. */
1908
+ {
1909
+ rd_kafka_messages_t *batch = NULL;
1910
+ size_t rcvd = 0;
1911
+ int attempts = 0;
1912
+ rd_kafka_error_t *cerr = NULL;
1913
+ while (attempts++ < 20 && rcvd == 0) {
1914
+ cerr = rd_kafka_share_poll(consumer, 500, &batch);
1915
+ if (cerr) {
1916
+ rd_kafka_error_destroy(cerr);
1917
+ continue;
1918
+ }
1919
+ rcvd = rd_kafka_messages_count(batch);
1920
+ if (rcvd == 0) {
1921
+ rd_kafka_messages_destroy(batch);
1922
+ batch = NULL;
1923
+ }
1924
+ }
1925
+ if (batch) {
1926
+ rd_kafka_messages_destroy(batch);
1927
+ batch = NULL;
1928
+ }
1929
+ }
1930
+
1931
+ /* Inject 5s delay on ShareAcknowledge so close() stays in flight. */
1932
+ TEST_SAY("Injecting %dms RTT delay on broker\n", rtt_delay_ms);
1933
+ rd_kafka_mock_broker_set_rtt(mcluster, 1, rtt_delay_ms);
1934
+
1935
+ queue = rd_kafka_queue_new(test_share_consumer_get_rk(consumer));
1936
+
1937
+ TEST_SAY("Calling close_queue() to initiate async close\n");
1938
+ close_error = rd_kafka_share_consumer_close_queue(consumer, queue);
1939
+ TEST_ASSERT(close_error == NULL, "close_queue: %s",
1940
+ close_error ? rd_kafka_error_string(close_error) : "");
1941
+
1942
+ /* close_queue has returned. Consumer must now be in closing state
1943
+ * (not fully closed yet, since ShareAcknowledge response is still
1944
+ * delayed). Every subsequent API call must be rejected. */
1945
+ TEST_ASSERT(!rd_kafka_share_consumer_closed(consumer),
1946
+ "consumer should still be closing, not closed");
1947
+
1948
+ TEST_SAY("Exercising APIs on closing consumer\n");
1949
+ verify_all_apis_return_error(consumer, topic);
1950
+
1951
+ /* Wait for async close to complete before destroying. */
1952
+ TEST_SAY("Waiting for consumer to finish closing\n");
1953
+ while (!rd_kafka_share_consumer_closed(consumer)) {
1954
+ rd_usleep(500 * 1000, NULL); /* 500ms */
1955
+ }
1956
+
1957
+ rd_kafka_queue_destroy(queue);
1958
+ test_share_destroy(consumer);
1959
+ test_mock_cluster_destroy(mcluster);
1960
+
1961
+ SUB_TEST_PASS();
1962
+ }
1963
+
1964
+ /**
1965
+ * @brief Test that the share-ack callback fires for un-committed implicit
1966
+ * acks during close.
1967
+ *
1968
+ * In implicit mode, records consumed in the first poll are auto-acked via
1969
+ * piggyback on the next ShareFetch. After polling twice (first to fetch,
1970
+ * second to drive the piggyback ack) and closing without an explicit
1971
+ * commit, the ack callback must have fired with the consumed offsets.
1972
+ */
1973
+ static void do_test_implicit_ack_callback_fires_on_close(void) {
1974
+ const char *topic_name;
1975
+ const char *group;
1976
+ char group_id[64];
1977
+ rd_kafka_share_t *rkshare;
1978
+ rd_kafka_messages_t *raw_batch = NULL;
1979
+ rd_kafka_topic_partition_list_t *subs;
1980
+ rd_kafka_error_t *error;
1981
+ ack_receipts_t receipts;
1982
+ size_t rcvd, j;
1983
+ int consumed = 0;
1984
+ int attempts = 0;
1985
+
1986
+ SUB_TEST();
1987
+
1988
+ ack_receipts_init(&receipts);
1989
+
1990
+ topic_name = test_mk_topic_name("0178-impl-ack-on-close", 1);
1991
+ test_create_topic_wait_exists(common_admin, topic_name, 1, -1,
1992
+ 60 * 1000);
1993
+ test_produce_msgs_simple(common_producer, topic_name, 0, 1);
1994
+
1995
+ rd_snprintf(group_id, sizeof(group_id),
1996
+ "0178-group-impl-ack-close-%" PRIu64, test_id_generate());
1997
+ group = group_id;
1998
+
1999
+ test_share_set_auto_offset_reset(group, "earliest");
2000
+ rkshare =
2001
+ create_share_consumer_with_receipts(group, "implicit", &receipts);
2002
+
2003
+ subs = rd_kafka_topic_partition_list_new(1);
2004
+ rd_kafka_topic_partition_list_add(subs, topic_name,
2005
+ RD_KAFKA_PARTITION_UA);
2006
+ rd_kafka_share_subscribe(rkshare, subs);
2007
+ rd_kafka_topic_partition_list_destroy(subs);
2008
+
2009
+ /* First poll: fetch the record (auto-ack pending for next poll) */
2010
+ while (consumed < 1 && attempts++ < 30) {
2011
+ error = rd_kafka_share_poll(rkshare, 2000, &raw_batch);
2012
+ if (error) {
2013
+ rd_kafka_error_destroy(error);
2014
+ continue;
2015
+ }
2016
+ rcvd = rd_kafka_messages_count(raw_batch);
2017
+ for (j = 0; j < rcvd; j++) {
2018
+ rd_kafka_message_t *rkm =
2019
+ rd_kafka_messages_get(raw_batch, j);
2020
+ if (rkm && !rkm->err)
2021
+ consumed++;
2022
+ }
2023
+ rd_kafka_messages_destroy(raw_batch);
2024
+ raw_batch = NULL;
2025
+ }
2026
+ TEST_ASSERT(consumed == 1, "Expected 1 record, got %d", consumed);
2027
+
2028
+ /* Second poll: drives the piggybacked implicit ack to the broker */
2029
+ attempts = 5;
2030
+ while (attempts-- > 0) {
2031
+ error = rd_kafka_share_poll(rkshare, 1000, &raw_batch);
2032
+ if (error)
2033
+ rd_kafka_error_destroy(error);
2034
+ rd_kafka_messages_destroy(raw_batch);
2035
+ raw_batch = NULL;
2036
+ }
2037
+
2038
+ /* Close - any pending ack-completion callbacks must be flushed */
2039
+ TEST_SAY("Closing share consumer without explicit commit\n");
2040
+ error = rd_kafka_share_consumer_close(rkshare);
2041
+ TEST_ASSERT(error == NULL, "close returned error: %s",
2042
+ rd_kafka_error_string(error));
2043
+
2044
+ TEST_SAY(
2045
+ "After close: callback_invocations=%d, receipts=%d, "
2046
+ "last_cb_err=%s\n",
2047
+ receipts.callback_invocations, receipts.receipt_cnt,
2048
+ rd_kafka_err2name(receipts.last_cb_err));
2049
+
2050
+ TEST_ASSERT(receipts.callback_invocations >= 1,
2051
+ "Expected ack callback to fire at least once "
2052
+ "by the time close returns, got %d invocations",
2053
+ receipts.callback_invocations);
2054
+ TEST_ASSERT(receipts.receipt_cnt >= 1,
2055
+ "Expected at least 1 acked offset reported via "
2056
+ "the callback by close, got %d",
2057
+ receipts.receipt_cnt);
2058
+ TEST_ASSERT(receipts.last_cb_err == RD_KAFKA_RESP_ERR_NO_ERROR,
2059
+ "Expected NO_ERROR in ack callback, got %s",
2060
+ rd_kafka_err2name(receipts.last_cb_err));
2061
+
2062
+ ack_receipts_destroy(&receipts);
2063
+ test_share_destroy(rkshare);
2064
+
2065
+ SUB_TEST_PASS();
2066
+ }
2067
+
2068
+
2069
+ int main_0178_share_consumer_close(int argc, char **argv) {
2070
+ /* Set overall timeout for all tests */
2071
+ test_timeout_set(600);
2072
+
2073
+ /* Create common handles reused across all real-broker tests */
2074
+ common_producer = test_create_producer();
2075
+ common_admin = test_create_producer();
2076
+
2077
+ /* Real broker tests */
2078
+ do_test_close_with_acknowledge();
2079
+ do_test_close_without_acknowledge();
2080
+ do_test_implicit_ack_callback_fires_on_close();
2081
+
2082
+ /* Cleanup common handles */
2083
+ rd_kafka_destroy(common_admin);
2084
+ rd_kafka_destroy(common_producer);
2085
+
2086
+ return 0;
2087
+ }
2088
+
2089
+ int main_0178_share_consumer_close_local(int argc, char **argv) {
2090
+ /* Mock broker tests only (no real broker needed) */
2091
+ TEST_SKIP_MOCK_CLUSTER(0);
2092
+ test_timeout_set(300);
2093
+
2094
+ do_test_close_with_slow_broker_response();
2095
+ do_test_close_respects_socket_timeout();
2096
+ do_test_close_with_broker_error_response();
2097
+ do_test_close_with_broker_busy();
2098
+ do_test_close_with_broker_down();
2099
+ do_test_api_calls_on_closed_consumer();
2100
+ do_test_api_calls_during_closing();
2101
+ return 0;
2102
+ }