@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,1815 @@
1
+ /*
2
+ * librdkafka - Apache Kafka C library
3
+ *
4
+ * Copyright (c) 2025, Confluent Inc.
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ *
10
+ * 1. Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ * this list of conditions and the following disclaimer in the documentation
14
+ * and/or other materials provided with the distribution.
15
+ *
16
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ * POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ #include "test.h"
30
+
31
+ /**
32
+ * @brief Share consumer transaction tests.
33
+ *
34
+ * Tests that share consumers correctly handle transactional messages
35
+ * based on the isolation.level configuration:
36
+ *
37
+ * - read_committed: Only see committed messages, aborted filtered out
38
+ * - read_uncommitted: See ALL messages including aborted ones
39
+ *
40
+ * Both modes are tested to verify correct behavior.
41
+ */
42
+
43
+ #define BATCH_SIZE 1000
44
+
45
+ /* Common handles shared across all tests */
46
+ static rd_kafka_t *common_admin;
47
+ static rd_kafka_t *common_regular_producer;
48
+
49
+
50
+ /**
51
+ * @brief Create a transactional producer.
52
+ */
53
+ static rd_kafka_t *create_txn_producer(const char *txn_id) {
54
+ rd_kafka_t *rk;
55
+ rd_kafka_conf_t *conf;
56
+
57
+ test_conf_init(&conf, NULL, 0);
58
+ test_conf_set(conf, "transactional.id", txn_id);
59
+ rd_kafka_conf_set_dr_msg_cb(conf, test_dr_msg_cb);
60
+
61
+ rk = test_create_handle(RD_KAFKA_PRODUCER, conf);
62
+ TEST_CALL_ERROR__(rd_kafka_init_transactions(rk, -1));
63
+
64
+ return rk;
65
+ }
66
+
67
+
68
+ /**
69
+ * @brief Configure share group settings.
70
+ *
71
+ * Sets share.auto.offset.reset=earliest and share.isolation.level
72
+ * based on the isolation_level parameter.
73
+ *
74
+ * Note: share.isolation.level is a BROKER-SIDE share group configuration.
75
+ * Transaction filtering happens on the broker, not the client.
76
+ */
77
+ static void configure_share_group(const char *group,
78
+ const char *isolation_level) {
79
+ test_share_set_auto_offset_reset(group, "earliest");
80
+ test_share_set_isolation_level(group, isolation_level);
81
+ }
82
+
83
+
84
+ /**
85
+ * @brief Subscribe share consumer to a topic.
86
+ */
87
+ static void subscribe_share_consumer(rd_kafka_share_t *rkshare,
88
+ const char *topic) {
89
+ rd_kafka_topic_partition_list_t *subs;
90
+ rd_kafka_resp_err_t err;
91
+
92
+ subs = rd_kafka_topic_partition_list_new(1);
93
+ rd_kafka_topic_partition_list_add(subs, topic, RD_KAFKA_PARTITION_UA);
94
+
95
+ err = rd_kafka_share_subscribe(rkshare, subs);
96
+ TEST_ASSERT(!err, "Share subscribe failed: %s", rd_kafka_err2str(err));
97
+
98
+ rd_kafka_topic_partition_list_destroy(subs);
99
+ }
100
+
101
+
102
+ /**
103
+ * @brief Consume messages from share consumer.
104
+ *
105
+ * @returns Number of non-error messages consumed.
106
+ */
107
+ static int consume_share_messages(rd_kafka_share_t *rkshare,
108
+ int expected_cnt,
109
+ int max_attempts,
110
+ int poll_timeout_ms) {
111
+ rd_kafka_messages_t *batch = NULL;
112
+ int consumed = 0;
113
+ int attempts = max_attempts;
114
+
115
+ while (consumed < expected_cnt && attempts-- > 0) {
116
+ size_t rcvd = 0;
117
+ size_t i;
118
+ rd_kafka_error_t *err;
119
+
120
+ err = rd_kafka_share_poll(rkshare, poll_timeout_ms, &batch);
121
+ if (err) {
122
+ rd_kafka_error_destroy(err);
123
+ continue;
124
+ }
125
+
126
+ rcvd = rd_kafka_messages_count(batch);
127
+ for (i = 0; i < rcvd; i++) {
128
+ rd_kafka_message_t *msg =
129
+ rd_kafka_messages_get(batch, i);
130
+ if (!msg->err)
131
+ consumed++;
132
+ }
133
+ rd_kafka_messages_destroy(batch);
134
+ batch = NULL;
135
+ }
136
+
137
+ return consumed;
138
+ }
139
+
140
+
141
+ /**
142
+ * @brief Poll share consumer expecting no messages (for abort verification).
143
+ *
144
+ * @returns Number of messages received (should be 0 for aborted txns
145
+ * with read_committed).
146
+ */
147
+ static int consume_share_no_msgs(rd_kafka_share_t *rkshare,
148
+ int poll_attempts,
149
+ int poll_timeout_ms) {
150
+ rd_kafka_messages_t *batch = NULL;
151
+ int consumed = 0;
152
+ int attempts = poll_attempts;
153
+
154
+ while (attempts-- > 0) {
155
+ size_t rcvd = 0;
156
+ size_t i;
157
+ rd_kafka_error_t *err;
158
+
159
+ err = rd_kafka_share_poll(rkshare, poll_timeout_ms, &batch);
160
+ if (err) {
161
+ rd_kafka_error_destroy(err);
162
+ continue;
163
+ }
164
+
165
+ rcvd = rd_kafka_messages_count(batch);
166
+ for (i = 0; i < rcvd; i++) {
167
+ rd_kafka_message_t *msg =
168
+ rd_kafka_messages_get(batch, i);
169
+ if (!msg->err)
170
+ consumed++;
171
+ }
172
+ rd_kafka_messages_destroy(batch);
173
+ batch = NULL;
174
+ }
175
+
176
+ return consumed;
177
+ }
178
+
179
+
180
+ /**
181
+ * @brief Consume messages and verify exact offsets.
182
+ *
183
+ * Consumes messages from the share consumer and verifies that:
184
+ * 1. The number of messages matches expected_msg_cnt
185
+ * 2. The offsets match the expected_offsets array
186
+ * 3. None of the forbidden_offsets appear (e.g., control records)
187
+ *
188
+ * @param rkshare Share consumer instance
189
+ * @param expected_msg_cnt Expected number of messages to consume
190
+ * @param expected_offsets Array of expected offsets in order
191
+ * @param forbidden_offsets Array of offsets that should never appear
192
+ * @param forbidden_cnt Number of forbidden offsets
193
+ * @param isolation_level Isolation level string (for logging)
194
+ */
195
+ static void consume_and_verify_offsets(rd_kafka_share_t *rkshare,
196
+ int expected_msg_cnt,
197
+ const int64_t *expected_offsets,
198
+ const int64_t *forbidden_offsets,
199
+ int forbidden_cnt,
200
+ const char *isolation_level) {
201
+ rd_kafka_messages_t *batch = NULL;
202
+ int attempts = 50;
203
+ int64_t received_offsets[10];
204
+ int consumed = 0;
205
+ int i, j;
206
+
207
+ TEST_SAY("[%s] Consuming messages and verifying offsets:\n",
208
+ isolation_level);
209
+
210
+ /* Consume messages and collect offsets */
211
+ while (consumed < expected_msg_cnt && attempts-- > 0) {
212
+ size_t rcvd = 0;
213
+ rd_kafka_error_t *err;
214
+
215
+ err = rd_kafka_share_poll(rkshare, 3000, &batch);
216
+ if (err) {
217
+ rd_kafka_error_destroy(err);
218
+ continue;
219
+ }
220
+
221
+ rcvd = rd_kafka_messages_count(batch);
222
+ for (i = 0; i < (int)rcvd; i++) {
223
+ rd_kafka_message_t *msg =
224
+ rd_kafka_messages_get(batch, i);
225
+ if (!msg->err && consumed < 10) {
226
+ received_offsets[consumed] = msg->offset;
227
+ TEST_SAY(" Message %d: offset=%" PRId64 "\n",
228
+ consumed, msg->offset);
229
+ consumed++;
230
+ }
231
+ }
232
+ rd_kafka_messages_destroy(batch);
233
+ batch = NULL;
234
+ }
235
+
236
+ /* Verify no forbidden offsets were received */
237
+ for (i = 0; i < consumed; i++) {
238
+ for (j = 0; j < forbidden_cnt; j++) {
239
+ TEST_ASSERT(received_offsets[i] != forbidden_offsets[j],
240
+ "[%s] Received forbidden offset %" PRId64
241
+ " at position %d - should be filtered!",
242
+ isolation_level, forbidden_offsets[j], i);
243
+ }
244
+ }
245
+
246
+ /* Verify message count */
247
+ TEST_ASSERT(consumed == expected_msg_cnt,
248
+ "[%s] Expected %d messages, got %d", isolation_level,
249
+ expected_msg_cnt, consumed);
250
+
251
+ /* Verify exact offsets */
252
+ for (i = 0; i < consumed; i++) {
253
+ TEST_ASSERT(received_offsets[i] == expected_offsets[i],
254
+ "[%s] Message %d: expected offset %" PRId64
255
+ ", got %" PRId64,
256
+ isolation_level, i, expected_offsets[i],
257
+ received_offsets[i]);
258
+ }
259
+
260
+ TEST_SAY(
261
+ "SUCCESS [%s]: All %d offsets verified correctly, "
262
+ "forbidden offsets filtered\n",
263
+ isolation_level, consumed);
264
+ }
265
+
266
+
267
+ /**
268
+ * @brief Test committed transaction visibility.
269
+ *
270
+ * Both read_committed and read_uncommitted should see committed messages.
271
+ */
272
+ static void do_test_committed_transaction(const char *isolation_level) {
273
+ char topic_suffix[64];
274
+ const char *topic;
275
+ char group[128];
276
+ const char *txn_id = "txn-commit-test";
277
+ const int msg_cnt = 100;
278
+ rd_kafka_t *producer;
279
+ rd_kafka_share_t *consumer;
280
+ int consumed;
281
+
282
+ rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-commit-%s",
283
+ isolation_level);
284
+ topic = test_mk_topic_name(topic_suffix, 1);
285
+ rd_snprintf(group, sizeof(group), "share-txn-commit-%s",
286
+ isolation_level);
287
+
288
+ SUB_TEST("isolation.level=%s", isolation_level);
289
+
290
+ /* Create topic */
291
+ test_create_topic(NULL, topic, 1, -1);
292
+
293
+ /* Create transactional producer */
294
+ producer = create_txn_producer(txn_id);
295
+
296
+ /* Create share consumer and set group config */
297
+ consumer = test_create_share_consumer(group, NULL);
298
+ configure_share_group(group, isolation_level);
299
+ subscribe_share_consumer(consumer, topic);
300
+
301
+ /* Begin transaction */
302
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
303
+
304
+ /* Produce messages */
305
+ TEST_SAY("Producing %d messages in transaction\n", msg_cnt);
306
+ test_produce_msgs2(producer, topic, test_id_generate(),
307
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
308
+
309
+ /* Commit transaction */
310
+ TEST_SAY("Committing transaction\n");
311
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
312
+
313
+ /* Consume messages - both modes should receive committed messages */
314
+ TEST_SAY("Consuming messages via share consumer (%s)\n",
315
+ isolation_level);
316
+ consumed = consume_share_messages(consumer, msg_cnt, 50, 3000);
317
+
318
+ TEST_ASSERT(consumed == msg_cnt,
319
+ "[%s] Expected %d messages from committed transaction, "
320
+ "got %d",
321
+ isolation_level, msg_cnt, consumed);
322
+
323
+ TEST_SAY(
324
+ "SUCCESS [%s]: Share consumer received %d committed "
325
+ "messages\n",
326
+ isolation_level, consumed);
327
+
328
+ /* Cleanup */
329
+ test_share_consumer_close(consumer);
330
+ test_share_destroy(consumer);
331
+ rd_kafka_destroy(producer);
332
+
333
+ SUB_TEST_PASS();
334
+ }
335
+
336
+
337
+ /**
338
+ * @brief Test aborted transaction visibility.
339
+ *
340
+ * - read_committed: Should NOT see aborted messages (expect 0)
341
+ * - read_uncommitted: Should see ALL messages (expect msg_cnt)
342
+ */
343
+ static void do_test_aborted_transaction(const char *isolation_level) {
344
+ char topic_suffix[64];
345
+ const char *topic;
346
+ char group[128];
347
+ const char *txn_id = "txn-abort-test";
348
+ const int msg_cnt = 100;
349
+ rd_kafka_t *producer;
350
+ rd_kafka_share_t *consumer;
351
+ int consumed;
352
+ int expected_msgs;
353
+ rd_bool_t is_read_committed =
354
+ !strcmp(isolation_level, "read_committed");
355
+
356
+ rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-abort-%s",
357
+ isolation_level);
358
+ topic = test_mk_topic_name(topic_suffix, 1);
359
+ rd_snprintf(group, sizeof(group), "share-txn-abort-%s",
360
+ isolation_level);
361
+
362
+ SUB_TEST("isolation.level=%s", isolation_level);
363
+
364
+ /* Expected messages depend on isolation level:
365
+ * - read_committed: 0 (aborted messages filtered)
366
+ * - read_uncommitted: msg_cnt (all messages visible) */
367
+ expected_msgs = is_read_committed ? 0 : msg_cnt;
368
+
369
+ /* Create topic */
370
+ test_create_topic(NULL, topic, 1, -1);
371
+
372
+ /* Create transactional producer */
373
+ producer = create_txn_producer(txn_id);
374
+
375
+ /* Create share consumer and set group config */
376
+ consumer = test_create_share_consumer(group, NULL);
377
+ configure_share_group(group, isolation_level);
378
+ subscribe_share_consumer(consumer, topic);
379
+
380
+ /* Begin transaction */
381
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
382
+
383
+ /* Produce messages */
384
+ TEST_SAY("Producing %d messages in transaction\n", msg_cnt);
385
+ test_curr->ignore_dr_err = rd_true; /* Aborted messages may fail */
386
+ test_produce_msgs2(producer, topic, test_id_generate(),
387
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
388
+
389
+ /* Abort transaction */
390
+ TEST_SAY("Aborting transaction\n");
391
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
392
+ test_curr->ignore_dr_err = rd_false;
393
+
394
+ /* Consume messages */
395
+ TEST_SAY("Consuming messages via share consumer (%s), expecting %d\n",
396
+ isolation_level, expected_msgs);
397
+
398
+ if (is_read_committed) {
399
+ consumed = consume_share_no_msgs(consumer, 10, 2000);
400
+ } else {
401
+ consumed =
402
+ consume_share_messages(consumer, expected_msgs, 50, 3000);
403
+ }
404
+
405
+ TEST_ASSERT(consumed == expected_msgs,
406
+ "[%s] Expected %d messages from aborted transaction, "
407
+ "got %d",
408
+ isolation_level, expected_msgs, consumed);
409
+
410
+ TEST_SAY(
411
+ "SUCCESS [%s]: Share consumer received %d messages "
412
+ "(expected %d)\n",
413
+ isolation_level, consumed, expected_msgs);
414
+
415
+ /* Cleanup */
416
+ test_share_consumer_close(consumer);
417
+ test_share_destroy(consumer);
418
+ rd_kafka_destroy(producer);
419
+
420
+ SUB_TEST_PASS();
421
+ }
422
+
423
+
424
+ /**
425
+ * @brief Test mixed transactions (some committed, some aborted).
426
+ *
427
+ * - read_committed: Only see committed messages
428
+ * - read_uncommitted: See ALL messages
429
+ */
430
+ static void do_test_mixed_transactions(const char *isolation_level) {
431
+ char topic_suffix[64];
432
+ const char *topic;
433
+ char group[128];
434
+ const char *txn_id = "txn-mixed-test";
435
+ rd_kafka_t *producer;
436
+ rd_kafka_share_t *consumer;
437
+ int consumed;
438
+ int i;
439
+ int expected_committed = 0;
440
+ int expected_total = 0;
441
+ int expected_msgs;
442
+ rd_bool_t is_read_committed =
443
+ !strcmp(isolation_level, "read_committed");
444
+
445
+ /* Define transaction scenarios */
446
+ struct {
447
+ const char *desc;
448
+ rd_bool_t commit;
449
+ int msg_cnt;
450
+ } txns[] = {
451
+ {"Commit 50 msgs", rd_true, 50}, {"Abort 50 msgs", rd_false, 50},
452
+ {"Commit 30 msgs", rd_true, 30}, {"Abort 20 msgs", rd_false, 20},
453
+ {"Commit 40 msgs", rd_true, 40},
454
+ };
455
+ const int txn_cnt = sizeof(txns) / sizeof(txns[0]);
456
+
457
+ rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-mixed-%s",
458
+ isolation_level);
459
+ topic = test_mk_topic_name(topic_suffix, 1);
460
+ rd_snprintf(group, sizeof(group), "share-txn-mixed-%s",
461
+ isolation_level);
462
+
463
+ SUB_TEST("isolation.level=%s", isolation_level);
464
+
465
+ /* Create topic */
466
+ test_create_topic(NULL, topic, 1, -1);
467
+
468
+ /* Create transactional producer */
469
+ producer = create_txn_producer(txn_id);
470
+
471
+ /* Create share consumer and set group config */
472
+ consumer = test_create_share_consumer(group, NULL);
473
+ configure_share_group(group, isolation_level);
474
+ subscribe_share_consumer(consumer, topic);
475
+
476
+ /* Execute transactions */
477
+ for (i = 0; i < txn_cnt; i++) {
478
+ TEST_SAY("Transaction %d: %s\n", i + 1, txns[i].desc);
479
+
480
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
481
+
482
+ test_curr->ignore_dr_err = !txns[i].commit;
483
+ test_produce_msgs2(producer, topic, test_id_generate(),
484
+ RD_KAFKA_PARTITION_UA, 0, txns[i].msg_cnt,
485
+ NULL, 0);
486
+
487
+ if (txns[i].commit) {
488
+ TEST_CALL_ERROR__(
489
+ rd_kafka_commit_transaction(producer, 30000));
490
+ expected_committed += txns[i].msg_cnt;
491
+ } else {
492
+ TEST_CALL_ERROR__(
493
+ rd_kafka_abort_transaction(producer, 30000));
494
+ }
495
+ expected_total += txns[i].msg_cnt;
496
+ test_curr->ignore_dr_err = rd_false;
497
+ }
498
+
499
+ /* Expected depends on isolation level */
500
+ expected_msgs = is_read_committed ? expected_committed : expected_total;
501
+
502
+ TEST_SAY("Expected messages [%s]: %d (committed=%d, total=%d)\n",
503
+ isolation_level, expected_msgs, expected_committed,
504
+ expected_total);
505
+
506
+ /* Consume messages */
507
+ consumed = consume_share_messages(consumer, expected_msgs, 100, 3000);
508
+
509
+ TEST_ASSERT(consumed == expected_msgs,
510
+ "[%s] Expected %d messages, got %d", isolation_level,
511
+ expected_msgs, consumed);
512
+
513
+ TEST_SAY("SUCCESS [%s]: Share consumer received %d messages\n",
514
+ isolation_level, consumed);
515
+
516
+ /* Cleanup */
517
+ test_share_consumer_close(consumer);
518
+ test_share_destroy(consumer);
519
+ rd_kafka_destroy(producer);
520
+
521
+ SUB_TEST_PASS();
522
+ }
523
+
524
+
525
+ /**
526
+ * @brief Test control records filtering.
527
+ *
528
+ * Control records (abort/commit markers) should never be visible to the
529
+ * application, regardless of isolation level.
530
+ */
531
+ static void do_test_control_records_filtered(const char *isolation_level) {
532
+ char topic_suffix[64];
533
+ const char *topic;
534
+ char group[128];
535
+ const char *txn_id = "txn-ctrl-test";
536
+ rd_kafka_t *producer;
537
+ rd_kafka_share_t *consumer;
538
+ int expected_msgs;
539
+ rd_bool_t is_read_committed =
540
+ !strcmp(isolation_level, "read_committed");
541
+
542
+ rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-ctrl-%s",
543
+ isolation_level);
544
+ topic = test_mk_topic_name(topic_suffix, 1);
545
+ rd_snprintf(group, sizeof(group), "share-txn-ctrl-%s", isolation_level);
546
+
547
+ SUB_TEST("isolation.level=%s", isolation_level);
548
+
549
+ /* Create topic */
550
+ test_create_topic(NULL, topic, 1, -1);
551
+
552
+ /* Create transactional producer */
553
+ producer = create_txn_producer(txn_id);
554
+
555
+ /* Create share consumer and set group config */
556
+ consumer = test_create_share_consumer(group, NULL);
557
+ configure_share_group(group, isolation_level);
558
+ subscribe_share_consumer(consumer, topic);
559
+
560
+ /* Transaction 1: Produce and ABORT (1 msg + 1 abort marker) */
561
+ TEST_SAY("Transaction 1: Produce 1 message, then ABORT\n");
562
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
563
+ test_curr->ignore_dr_err = rd_true;
564
+ test_produce_msgs2(producer, topic, test_id_generate(),
565
+ RD_KAFKA_PARTITION_UA, 0, 1, NULL, 0);
566
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
567
+ test_curr->ignore_dr_err = rd_false;
568
+
569
+ /* Transaction 2: Produce and COMMIT (5 msgs + 1 commit marker) */
570
+ TEST_SAY("Transaction 2: Produce 5 messages, then COMMIT\n");
571
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
572
+ test_produce_msgs2(producer, topic, test_id_generate(),
573
+ RD_KAFKA_PARTITION_UA, 0, 5, NULL, 0);
574
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
575
+
576
+ /*
577
+ * Topic now contains:
578
+ * - Offset 0: aborted message
579
+ * - Offset 1: abort control record
580
+ * - Offset 2-6: committed messages
581
+ * - Offset 7: commit control record
582
+ *
583
+ * read_committed: should see offsets 2,3,4,5,6 (committed only)
584
+ * read_uncommitted: should see offsets 0,2,3,4,5,6 (aborted +
585
+ * committed) Control records (offsets 1 and 7) should NEVER be visible.
586
+ */
587
+ expected_msgs = is_read_committed ? 5 : 6;
588
+
589
+ /* Consume and verify exact offsets */
590
+ int64_t expected_offsets_committed[] = {2, 3, 4, 5, 6};
591
+ int64_t expected_offsets_uncommitted[] = {0, 2, 3, 4, 5, 6};
592
+ int64_t control_record_offsets[] = {1, 7};
593
+ const int64_t *expected_offsets;
594
+
595
+ expected_offsets = is_read_committed ? expected_offsets_committed
596
+ : expected_offsets_uncommitted;
597
+
598
+ consume_and_verify_offsets(consumer, expected_msgs, expected_offsets,
599
+ control_record_offsets, 2, isolation_level);
600
+
601
+ /* Cleanup */
602
+ test_share_consumer_close(consumer);
603
+ test_share_destroy(consumer);
604
+ rd_kafka_destroy(producer);
605
+
606
+ SUB_TEST_PASS();
607
+ }
608
+
609
+
610
+ /**
611
+ * @brief Test multiple partitions with transactions.
612
+ */
613
+ static void do_test_multi_partition_transactions(const char *isolation_level) {
614
+ char topic_suffix[64];
615
+ const char *topic;
616
+ char group[128];
617
+ const char *txn_id = "txn-multipart-test";
618
+ const int partition_cnt = 3;
619
+ const int msgs_per_partition = 30;
620
+ rd_kafka_t *producer;
621
+ rd_kafka_share_t *consumer;
622
+ int consumed;
623
+ int expected_committed;
624
+ int expected_total;
625
+ int expected_msgs;
626
+ rd_bool_t is_read_committed =
627
+ !strcmp(isolation_level, "read_committed");
628
+
629
+ rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-txn-multipart-%s",
630
+ isolation_level);
631
+ topic = test_mk_topic_name(topic_suffix, 1);
632
+ rd_snprintf(group, sizeof(group), "share-txn-multipart-%s",
633
+ isolation_level);
634
+
635
+ SUB_TEST("isolation.level=%s", isolation_level);
636
+
637
+ /* Create topic with multiple partitions */
638
+ test_create_topic(NULL, topic, partition_cnt, -1);
639
+
640
+ /* Create transactional producer */
641
+ producer = create_txn_producer(txn_id);
642
+
643
+ /* Create share consumer and set group config */
644
+ consumer = test_create_share_consumer(group, NULL);
645
+ configure_share_group(group, isolation_level);
646
+ subscribe_share_consumer(consumer, topic);
647
+
648
+ /* Transaction 1: Produce to all partitions and COMMIT */
649
+ TEST_SAY("Transaction 1: Produce to all partitions, COMMIT\n");
650
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
651
+ test_produce_msgs2(producer, topic, test_id_generate(),
652
+ RD_KAFKA_PARTITION_UA, 0,
653
+ msgs_per_partition * partition_cnt, NULL, 0);
654
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
655
+
656
+ /* Transaction 2: Produce to all partitions and ABORT */
657
+ TEST_SAY("Transaction 2: Produce to all partitions, ABORT\n");
658
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
659
+ test_curr->ignore_dr_err = rd_true;
660
+ test_produce_msgs2(producer, topic, test_id_generate(),
661
+ RD_KAFKA_PARTITION_UA, 0,
662
+ msgs_per_partition * partition_cnt, NULL, 0);
663
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
664
+ test_curr->ignore_dr_err = rd_false;
665
+
666
+ /* Transaction 3: Produce to all partitions and COMMIT */
667
+ TEST_SAY("Transaction 3: Produce to all partitions, COMMIT\n");
668
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
669
+ test_produce_msgs2(producer, topic, test_id_generate(),
670
+ RD_KAFKA_PARTITION_UA, 0,
671
+ msgs_per_partition * partition_cnt, NULL, 0);
672
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
673
+
674
+ /* Calculate expected messages */
675
+ expected_committed = 2 * msgs_per_partition * partition_cnt;
676
+ expected_total = 3 * msgs_per_partition * partition_cnt;
677
+ expected_msgs = is_read_committed ? expected_committed : expected_total;
678
+
679
+ TEST_SAY("Expected messages [%s]: %d\n", isolation_level,
680
+ expected_msgs);
681
+
682
+ consumed = consume_share_messages(consumer, expected_msgs, 100, 3000);
683
+
684
+ TEST_ASSERT(consumed == expected_msgs,
685
+ "[%s] Expected %d messages, got %d", isolation_level,
686
+ expected_msgs, consumed);
687
+
688
+ TEST_SAY("SUCCESS [%s]: Share consumer received %d messages\n",
689
+ isolation_level, consumed);
690
+
691
+ /* Cleanup */
692
+ test_share_consumer_close(consumer);
693
+ test_share_destroy(consumer);
694
+ rd_kafka_destroy(producer);
695
+
696
+ SUB_TEST_PASS();
697
+ }
698
+
699
+
700
+ /**
701
+ * @brief Test interleaved transactions from multiple producers.
702
+ */
703
+ static void do_test_interleaved_producers(const char *isolation_level) {
704
+ char topic_suffix[64];
705
+ const char *topic;
706
+ char group[128];
707
+ const int msg_cnt = 50;
708
+ rd_kafka_t *producer1, *producer2;
709
+ rd_kafka_share_t *consumer;
710
+ int consumed;
711
+ int expected_msgs;
712
+ rd_bool_t is_read_committed =
713
+ !strcmp(isolation_level, "read_committed");
714
+
715
+ rd_snprintf(topic_suffix, sizeof(topic_suffix),
716
+ "0177-txn-interleave-%s", isolation_level);
717
+ topic = test_mk_topic_name(topic_suffix, 1);
718
+ rd_snprintf(group, sizeof(group), "share-txn-interleave-%s",
719
+ isolation_level);
720
+
721
+ SUB_TEST("isolation.level=%s", isolation_level);
722
+
723
+ /* Create topic */
724
+ test_create_topic(NULL, topic, 1, -1);
725
+
726
+ /* Create two transactional producers */
727
+ producer1 = create_txn_producer("txn-producer-1");
728
+ producer2 = create_txn_producer("txn-producer-2");
729
+
730
+ /* Create share consumer and set group config */
731
+ consumer = test_create_share_consumer(group, NULL);
732
+ configure_share_group(group, isolation_level);
733
+ subscribe_share_consumer(consumer, topic);
734
+
735
+ /* Producer 1: Begin transaction */
736
+ TEST_SAY("Producer 1: Begin transaction\n");
737
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer1));
738
+
739
+ /* Producer 2: Begin transaction */
740
+ TEST_SAY("Producer 2: Begin transaction\n");
741
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer2));
742
+
743
+ /* Producer 1: Produce messages */
744
+ TEST_SAY("Producer 1: Produce %d messages\n", msg_cnt);
745
+ test_produce_msgs2(producer1, topic, test_id_generate(),
746
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
747
+
748
+ /* Producer 2: Produce messages */
749
+ TEST_SAY("Producer 2: Produce %d messages\n", msg_cnt);
750
+ test_curr->ignore_dr_err = rd_true;
751
+ test_produce_msgs2(producer2, topic, test_id_generate(),
752
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
753
+
754
+ /* Producer 1: COMMIT */
755
+ TEST_SAY("Producer 1: COMMIT\n");
756
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer1, 30000));
757
+
758
+ /* Producer 2: ABORT */
759
+ TEST_SAY("Producer 2: ABORT\n");
760
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer2, 30000));
761
+ test_curr->ignore_dr_err = rd_false;
762
+
763
+ /* Expected: read_committed sees only producer1's msgs,
764
+ * read_uncommitted sees both */
765
+ expected_msgs = is_read_committed ? msg_cnt : (2 * msg_cnt);
766
+
767
+ consumed = consume_share_messages(consumer, expected_msgs, 50, 3000);
768
+
769
+ TEST_ASSERT(consumed == expected_msgs,
770
+ "[%s] Expected %d messages, got %d", isolation_level,
771
+ expected_msgs, consumed);
772
+
773
+ TEST_SAY("SUCCESS [%s]: Share consumer received %d messages\n",
774
+ isolation_level, consumed);
775
+
776
+ /* Cleanup */
777
+ test_share_consumer_close(consumer);
778
+ test_share_destroy(consumer);
779
+ rd_kafka_destroy(producer1);
780
+ rd_kafka_destroy(producer2);
781
+
782
+ SUB_TEST_PASS();
783
+ }
784
+
785
+
786
+ /**
787
+ * @brief Test non-transactional and transactional messages mixed.
788
+ */
789
+ static void do_test_mixed_txn_non_txn(const char *isolation_level) {
790
+ char topic_suffix[64];
791
+ const char *topic;
792
+ char group[128];
793
+ const char *txn_id = "txn-mixed-type-test";
794
+ const int non_txn_msgs = 50;
795
+ const int txn_commit_msgs = 30;
796
+ const int txn_abort_msgs = 20;
797
+ rd_kafka_t *txn_producer;
798
+ rd_kafka_share_t *consumer;
799
+ int consumed;
800
+ int expected_msgs;
801
+ rd_bool_t is_read_committed =
802
+ !strcmp(isolation_level, "read_committed");
803
+
804
+ rd_snprintf(topic_suffix, sizeof(topic_suffix),
805
+ "0177-txn-mixed-type-%s", isolation_level);
806
+ topic = test_mk_topic_name(topic_suffix, 1);
807
+ rd_snprintf(group, sizeof(group), "share-txn-mixed-type-%s",
808
+ isolation_level);
809
+
810
+ SUB_TEST("isolation.level=%s", isolation_level);
811
+
812
+ /* Create topic */
813
+ test_create_topic(NULL, topic, 1, -1);
814
+
815
+ /* Create transactional producer */
816
+ txn_producer = create_txn_producer(txn_id);
817
+
818
+ /* Create share consumer and set group config */
819
+ consumer = test_create_share_consumer(group, NULL);
820
+ configure_share_group(group, isolation_level);
821
+ subscribe_share_consumer(consumer, topic);
822
+
823
+ /* Produce non-transactional messages */
824
+ TEST_SAY("Producing %d non-transactional messages\n", non_txn_msgs);
825
+ test_produce_msgs2(common_regular_producer, topic, test_id_generate(),
826
+ RD_KAFKA_PARTITION_UA, 0, non_txn_msgs, NULL, 0);
827
+
828
+ /* Transactional: Commit */
829
+ TEST_SAY("Producing %d transactional messages (will commit)\n",
830
+ txn_commit_msgs);
831
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(txn_producer));
832
+ test_produce_msgs2(txn_producer, topic, test_id_generate(),
833
+ RD_KAFKA_PARTITION_UA, 0, txn_commit_msgs, NULL, 0);
834
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(txn_producer, 30000));
835
+
836
+ /* Transactional: Abort */
837
+ TEST_SAY("Producing %d transactional messages (will abort)\n",
838
+ txn_abort_msgs);
839
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(txn_producer));
840
+ test_curr->ignore_dr_err = rd_true;
841
+ test_produce_msgs2(txn_producer, topic, test_id_generate(),
842
+ RD_KAFKA_PARTITION_UA, 0, txn_abort_msgs, NULL, 0);
843
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(txn_producer, 30000));
844
+ test_curr->ignore_dr_err = rd_false;
845
+
846
+ /* Expected:
847
+ * read_committed: non-txn + committed = 50 + 30 = 80
848
+ * read_uncommitted: all = 50 + 30 + 20 = 100 */
849
+ expected_msgs = is_read_committed
850
+ ? (non_txn_msgs + txn_commit_msgs)
851
+ : (non_txn_msgs + txn_commit_msgs + txn_abort_msgs);
852
+
853
+ TEST_SAY("Expected messages [%s]: %d\n", isolation_level,
854
+ expected_msgs);
855
+
856
+ consumed = consume_share_messages(consumer, expected_msgs, 100, 3000);
857
+
858
+ TEST_ASSERT(consumed == expected_msgs,
859
+ "[%s] Expected %d messages, got %d", isolation_level,
860
+ expected_msgs, consumed);
861
+
862
+ TEST_SAY("SUCCESS [%s]: Share consumer received %d messages\n",
863
+ isolation_level, consumed);
864
+
865
+ /* Cleanup */
866
+ test_share_consumer_close(consumer);
867
+ test_share_destroy(consumer);
868
+ rd_kafka_destroy(txn_producer);
869
+
870
+ SUB_TEST_PASS();
871
+ }
872
+
873
+
874
+ /**
875
+ * @brief Test dynamic isolation level change (READ_UNCOMMITTED ->
876
+ * READ_COMMITTED).
877
+ *
878
+ * 1. Start with READ_UNCOMMITTED
879
+ * 2. Produce committed txn, consume and acknowledge
880
+ * 3. Produce aborted txn, consume and release
881
+ * 4. Change to READ_COMMITTED
882
+ * 5. Produce more committed/aborted txns
883
+ * 6. Verify only committed txns are now returned
884
+ */
885
+ static void do_test_dynamic_uncommitted_to_committed(void) {
886
+ const char *topic;
887
+ const char *group = "share-dynamic-uc-to-c";
888
+ const char *txn_id = "txn-dynamic-uc-c";
889
+ const int msg_cnt = 10;
890
+ rd_kafka_t *producer;
891
+ rd_kafka_share_t *consumer;
892
+ int consumed;
893
+
894
+ SUB_TEST();
895
+
896
+ topic = test_mk_topic_name("0177-dynamic-uc-to-c", 1);
897
+
898
+ /* Create topic */
899
+ test_create_topic(NULL, topic, 1, -1);
900
+
901
+ /* Create transactional producer */
902
+ producer = create_txn_producer(txn_id);
903
+
904
+ /* Create share consumer with READ_UNCOMMITTED */
905
+ consumer = test_create_share_consumer(group, NULL);
906
+ configure_share_group(group, "read_uncommitted");
907
+ subscribe_share_consumer(consumer, topic);
908
+
909
+ /* Phase 1: Produce and consume committed transaction */
910
+ TEST_SAY("Phase 1: Committed transaction with READ_UNCOMMITTED\n");
911
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
912
+ test_produce_msgs2(producer, topic, test_id_generate(),
913
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
914
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
915
+
916
+ consumed = consume_share_messages(consumer, msg_cnt, 50, 3000);
917
+ TEST_ASSERT(consumed == msg_cnt,
918
+ "Phase 1: Expected %d committed msgs, got %d", msg_cnt,
919
+ consumed);
920
+
921
+ /* Phase 2: Produce aborted transaction - should be visible in
922
+ * READ_UNCOMMITTED */
923
+ TEST_SAY("Phase 2: Aborted transaction with READ_UNCOMMITTED\n");
924
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
925
+ test_curr->ignore_dr_err = rd_true;
926
+ test_produce_msgs2(producer, topic, test_id_generate(),
927
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
928
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
929
+ test_curr->ignore_dr_err = rd_false;
930
+
931
+ consumed = consume_share_messages(consumer, msg_cnt, 50, 3000);
932
+ TEST_ASSERT(consumed == msg_cnt,
933
+ "Phase 2: Expected %d aborted msgs (uncommitted mode), "
934
+ "got %d",
935
+ msg_cnt, consumed);
936
+
937
+ /* Phase 3: Change isolation level to READ_COMMITTED */
938
+ TEST_SAY("Phase 3: Changing isolation level to READ_COMMITTED\n");
939
+ configure_share_group(group, "read_committed");
940
+
941
+ /* Phase 4: Produce aborted transaction - should NOT be visible now */
942
+ TEST_SAY("Phase 4: Aborted transaction with READ_COMMITTED\n");
943
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
944
+ test_curr->ignore_dr_err = rd_true;
945
+ test_produce_msgs2(producer, topic, test_id_generate(),
946
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
947
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
948
+ test_curr->ignore_dr_err = rd_false;
949
+
950
+ consumed = consume_share_no_msgs(consumer, 10, 2000);
951
+ TEST_ASSERT(consumed == 0,
952
+ "Phase 4: Expected 0 aborted msgs (committed mode), got %d",
953
+ consumed);
954
+
955
+ /* Phase 5: Produce committed transaction - should be visible */
956
+ TEST_SAY("Phase 5: Committed transaction with READ_COMMITTED\n");
957
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
958
+ test_produce_msgs2(producer, topic, test_id_generate(),
959
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
960
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
961
+
962
+ consumed = consume_share_messages(consumer, msg_cnt, 50, 3000);
963
+ TEST_ASSERT(consumed == msg_cnt,
964
+ "Phase 5: Expected %d committed msgs, got %d", msg_cnt,
965
+ consumed);
966
+
967
+ TEST_SAY(
968
+ "SUCCESS: Dynamic isolation change READ_UNCOMMITTED -> "
969
+ "READ_COMMITTED works correctly\n");
970
+
971
+ /* Cleanup */
972
+ test_share_consumer_close(consumer);
973
+ test_share_destroy(consumer);
974
+ rd_kafka_destroy(producer);
975
+
976
+ SUB_TEST_PASS();
977
+ }
978
+
979
+
980
+ /**
981
+ * @brief Test interval-based abort pattern.
982
+ *
983
+ * Produce N transactions where every Kth one is aborted.
984
+ * Verify READ_COMMITTED skips exactly the aborted ones.
985
+ * Verify READ_UNCOMMITTED sees all.
986
+ */
987
+ static void do_test_interval_abort_pattern(const char *isolation_level) {
988
+ char topic_suffix[64];
989
+ const char *topic;
990
+ char group[128];
991
+ const char *txn_id = "txn-interval-abort";
992
+ const int total_txns = 20;
993
+ const int abort_interval = 5; /* Every 5th transaction is aborted */
994
+ const int msgs_per_txn = 5;
995
+ rd_kafka_t *producer;
996
+ rd_kafka_share_t *consumer;
997
+ int consumed;
998
+ int i;
999
+ int committed_txns = 0;
1000
+ int aborted_txns = 0;
1001
+ int expected_msgs;
1002
+ rd_bool_t is_read_committed =
1003
+ !strcmp(isolation_level, "read_committed");
1004
+
1005
+ rd_snprintf(topic_suffix, sizeof(topic_suffix), "0177-interval-%s",
1006
+ isolation_level);
1007
+ topic = test_mk_topic_name(topic_suffix, 1);
1008
+ rd_snprintf(group, sizeof(group), "share-interval-%s", isolation_level);
1009
+
1010
+ SUB_TEST("isolation.level=%s", isolation_level);
1011
+
1012
+ /* Create topic */
1013
+ test_create_topic(NULL, topic, 1, -1);
1014
+
1015
+ /* Create transactional producer */
1016
+ producer = create_txn_producer(txn_id);
1017
+
1018
+ /* Create share consumer and set group config */
1019
+ consumer = test_create_share_consumer(group, NULL);
1020
+ configure_share_group(group, isolation_level);
1021
+ subscribe_share_consumer(consumer, topic);
1022
+
1023
+ /* Produce transactions with interval-based abort pattern */
1024
+ TEST_SAY("Producing %d transactions (every %dth is aborted)\n",
1025
+ total_txns, abort_interval);
1026
+
1027
+ for (i = 1; i <= total_txns; i++) {
1028
+ rd_bool_t should_abort = (i % abort_interval == 0);
1029
+
1030
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
1031
+
1032
+ if (should_abort)
1033
+ test_curr->ignore_dr_err = rd_true;
1034
+
1035
+ test_produce_msgs2(producer, topic, test_id_generate(),
1036
+ RD_KAFKA_PARTITION_UA, 0, msgs_per_txn, NULL,
1037
+ 0);
1038
+
1039
+ if (should_abort) {
1040
+ TEST_CALL_ERROR__(
1041
+ rd_kafka_abort_transaction(producer, 30000));
1042
+ test_curr->ignore_dr_err = rd_false;
1043
+ aborted_txns++;
1044
+ TEST_SAY(" Transaction %d: ABORTED\n", i);
1045
+ } else {
1046
+ TEST_CALL_ERROR__(
1047
+ rd_kafka_commit_transaction(producer, 30000));
1048
+ committed_txns++;
1049
+ }
1050
+ }
1051
+
1052
+ TEST_SAY("Produced: %d committed txns, %d aborted txns\n",
1053
+ committed_txns, aborted_txns);
1054
+
1055
+ /* Calculate expected messages */
1056
+ if (is_read_committed) {
1057
+ expected_msgs = committed_txns * msgs_per_txn;
1058
+ } else {
1059
+ expected_msgs = total_txns * msgs_per_txn;
1060
+ }
1061
+
1062
+ TEST_SAY("[%s] Expected %d messages\n", isolation_level, expected_msgs);
1063
+
1064
+ /* Consume and verify */
1065
+ consumed = consume_share_messages(consumer, expected_msgs, 200, 3000);
1066
+
1067
+ TEST_ASSERT(consumed == expected_msgs,
1068
+ "[%s] Expected %d messages, got %d", isolation_level,
1069
+ expected_msgs, consumed);
1070
+
1071
+ /* Verify no extra messages */
1072
+ {
1073
+ int extra = consume_share_no_msgs(consumer, 5, 2000);
1074
+ TEST_ASSERT(extra == 0, "[%s] Unexpected extra messages: %d",
1075
+ isolation_level, extra);
1076
+ }
1077
+
1078
+ TEST_SAY(
1079
+ "SUCCESS [%s]: Interval abort pattern - received exactly %d "
1080
+ "messages (committed=%d, aborted=%d txns)\n",
1081
+ isolation_level, consumed, committed_txns, aborted_txns);
1082
+
1083
+ /* Cleanup */
1084
+ test_share_consumer_close(consumer);
1085
+ test_share_destroy(consumer);
1086
+ rd_kafka_destroy(producer);
1087
+
1088
+ SUB_TEST_PASS();
1089
+ }
1090
+
1091
+
1092
+ /**
1093
+ * @brief Produce exactly one transactional record carrying \p value, then
1094
+ * commit (\p commit=rd_true) or abort the transaction.
1095
+ */
1096
+ static void produce_msg_txn(rd_kafka_t *producer,
1097
+ const char *topic,
1098
+ const char *value,
1099
+ rd_bool_t commit) {
1100
+ rd_kafka_resp_err_t err;
1101
+
1102
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer));
1103
+
1104
+ if (!commit)
1105
+ test_curr->ignore_dr_err = rd_true;
1106
+
1107
+ err = rd_kafka_producev(producer, RD_KAFKA_V_TOPIC(topic),
1108
+ RD_KAFKA_V_VALUE((void *)value, strlen(value)),
1109
+ RD_KAFKA_V_END);
1110
+ TEST_ASSERT(!err, "producev failed: %s", rd_kafka_err2name(err));
1111
+ rd_kafka_flush(producer, 30000);
1112
+
1113
+ if (commit)
1114
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer, 30000));
1115
+ else
1116
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer, 30000));
1117
+
1118
+ test_curr->ignore_dr_err = rd_false;
1119
+ }
1120
+
1121
+
1122
+ /**
1123
+ * @brief Test: dynamic isolation level change READ_COMMITTED ->
1124
+ * READ_UNCOMMITTED with RELEASE acknowledgement on a committed
1125
+ * record.
1126
+ *
1127
+ * Phase 1: produce committed Msg1, consume + ACCEPT.
1128
+ * Phase 2: produce aborted Msg2 (no records visible in read_committed).
1129
+ * Phase 3: produce committed Msg3, consume + RELEASE.
1130
+ * Phase 4: produce aborted Msg4. Released Msg3 may reappear; keep RELEASE-ing
1131
+ * it so the broker can advance past the abort marker.
1132
+ * Phase 5: alter isolation level to read_uncommitted.
1133
+ * Phase 6: produce committed Msg5, aborted Msg6, aborted Msg7, committed Msg8.
1134
+ * Phase 7: consume; the released Msg3 (offset 4) plus the four new messages
1135
+ * (offsets 8, 10, 12, 14) should all be visible -> 5 unique offsets.
1136
+ *
1137
+ * With one record per transaction, the offset layout is:
1138
+ * 0 Msg1 (commit marker 1)
1139
+ * 2 Msg2 (abort marker 3)
1140
+ * 4 Msg3 (commit marker 5)
1141
+ * 6 Msg4 (abort marker 7)
1142
+ * 8 Msg5 (commit marker 9)
1143
+ * 10 Msg6 (abort marker 11)
1144
+ * 12 Msg7 (abort marker 13)
1145
+ * 14 Msg8 (commit marker 15)
1146
+ */
1147
+ static void do_test_dynamic_committed_to_uncommitted_with_release(void) {
1148
+ const char *topic;
1149
+ const char *group = "share-dynamic-c-to-uc-release";
1150
+ const char *txn_id = "txn-dynamic-c-uc-release";
1151
+ rd_kafka_t *producer;
1152
+ rd_kafka_share_t *consumer;
1153
+ rd_kafka_messages_t *batch = NULL;
1154
+ rd_kafka_error_t *error;
1155
+ rd_kafka_resp_err_t ack_err;
1156
+ size_t rcvd, j;
1157
+ int attempts;
1158
+ int consumed;
1159
+ rd_bool_t seen[20] = {0};
1160
+ const int64_t expected_offsets[] = {4, 8, 10, 12, 14};
1161
+ size_t i;
1162
+
1163
+ SUB_TEST();
1164
+
1165
+ topic = test_mk_topic_name("0177-dyn-c-uc-release", 1);
1166
+ test_create_topic(NULL, topic, 1, -1);
1167
+
1168
+ producer = create_txn_producer(txn_id);
1169
+
1170
+ consumer = test_create_share_consumer(group, "explicit");
1171
+ configure_share_group(group, "read_committed");
1172
+ subscribe_share_consumer(consumer, topic);
1173
+
1174
+ /* Phase 1: committed Msg1 -> ACCEPT */
1175
+ TEST_SAY("Phase 1: committed Msg1 with READ_COMMITTED\n");
1176
+ produce_msg_txn(producer, topic, "Message 1", rd_true);
1177
+ consumed = 0;
1178
+ attempts = 0;
1179
+ while (consumed == 0 && attempts++ < 30) {
1180
+ rcvd = 0;
1181
+ error = rd_kafka_share_poll(consumer, 3000, &batch);
1182
+ if (error) {
1183
+ rd_kafka_error_destroy(error);
1184
+ continue;
1185
+ }
1186
+ rcvd = rd_kafka_messages_count(batch);
1187
+ for (j = 0; j < rcvd; j++) {
1188
+ rd_kafka_message_t *msg =
1189
+ rd_kafka_messages_get(batch, j);
1190
+ if (!msg->err) {
1191
+ TEST_ASSERT(msg->offset == 0,
1192
+ "Phase 1: expected offset 0, "
1193
+ "got %" PRId64,
1194
+ msg->offset);
1195
+ ack_err = rd_kafka_share_acknowledge_type(
1196
+ consumer, msg,
1197
+ RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
1198
+ TEST_ASSERT(ack_err ==
1199
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1200
+ "Phase 1 ACCEPT failed: %s",
1201
+ rd_kafka_err2name(ack_err));
1202
+ consumed++;
1203
+ }
1204
+ }
1205
+ rd_kafka_messages_destroy(batch);
1206
+ batch = NULL;
1207
+ }
1208
+ TEST_ASSERT(consumed == 1, "Phase 1: expected 1 record, got %d",
1209
+ consumed);
1210
+ error = rd_kafka_share_commit_async(consumer);
1211
+ TEST_ASSERT(!error, "Phase 1 commit_async failed");
1212
+
1213
+ /* Phase 2: aborted Msg2 - read_committed should hide the record;
1214
+ * the broker eventually advances past the abort marker. */
1215
+ TEST_SAY("Phase 2: aborted Msg2 (read_committed hides it)\n");
1216
+ produce_msg_txn(producer, topic, "Message 2", rd_false);
1217
+ attempts = 10;
1218
+ while (attempts-- > 0) {
1219
+ rcvd = 0;
1220
+ error = rd_kafka_share_poll(consumer, 500, &batch);
1221
+ if (error)
1222
+ rd_kafka_error_destroy(error);
1223
+ rcvd = rd_kafka_messages_count(batch);
1224
+ for (j = 0; j < rcvd; j++) {
1225
+ rd_kafka_message_t *msg =
1226
+ rd_kafka_messages_get(batch, j);
1227
+ TEST_ASSERT(msg->err || msg->offset != 2,
1228
+ "Phase 2: aborted Msg2 leaked at "
1229
+ "offset 2 in read_committed");
1230
+ }
1231
+ rd_kafka_messages_destroy(batch);
1232
+ batch = NULL;
1233
+ }
1234
+
1235
+ /* Phase 3: committed Msg3 -> RELEASE */
1236
+ TEST_SAY("Phase 3: committed Msg3 -> RELEASE\n");
1237
+ produce_msg_txn(producer, topic, "Message 3", rd_true);
1238
+ consumed = 0;
1239
+ attempts = 0;
1240
+ while (consumed == 0 && attempts++ < 30) {
1241
+ rcvd = 0;
1242
+ error = rd_kafka_share_poll(consumer, 3000, &batch);
1243
+ if (error) {
1244
+ rd_kafka_error_destroy(error);
1245
+ continue;
1246
+ }
1247
+ rcvd = rd_kafka_messages_count(batch);
1248
+ for (j = 0; j < rcvd; j++) {
1249
+ rd_kafka_message_t *msg =
1250
+ rd_kafka_messages_get(batch, j);
1251
+ if (!msg->err) {
1252
+ TEST_ASSERT(msg->offset == 4,
1253
+ "Phase 3: expected offset 4 "
1254
+ "(Msg3), got %" PRId64,
1255
+ msg->offset);
1256
+ ack_err = rd_kafka_share_acknowledge_type(
1257
+ consumer, msg,
1258
+ RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_RELEASE);
1259
+ TEST_ASSERT(ack_err ==
1260
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1261
+ "Phase 3 RELEASE failed: %s",
1262
+ rd_kafka_err2name(ack_err));
1263
+ consumed++;
1264
+ }
1265
+ }
1266
+ rd_kafka_messages_destroy(batch);
1267
+ batch = NULL;
1268
+ }
1269
+ TEST_ASSERT(consumed == 1, "Phase 3: expected 1 record, got %d",
1270
+ consumed);
1271
+ error = rd_kafka_share_commit_async(consumer);
1272
+ TEST_ASSERT(!error, "Phase 3 commit_async failed");
1273
+
1274
+ /* Phase 4: aborted Msg4. Released Msg3 may re-appear; RELEASE it
1275
+ * again so the next poll can advance. Keep this loop short: each
1276
+ * RELEASE-and-redeliver bumps Msg3's delivery_count. The default
1277
+ * share.delivery.count.limit is 5; Phase 3 already used one
1278
+ * delivery and Phase 7's final ACCEPT will need one, so cap
1279
+ * Phase 4 at 3 redeliveries to leave headroom and avoid the
1280
+ * record being archived. */
1281
+ TEST_SAY("Phase 4: aborted Msg4; Msg3 may redeliver -> RELEASE\n");
1282
+ produce_msg_txn(producer, topic, "Message 4", rd_false);
1283
+ attempts = 3;
1284
+ while (attempts-- > 0) {
1285
+ rcvd = 0;
1286
+ error = rd_kafka_share_poll(consumer, 1000, &batch);
1287
+ if (error) {
1288
+ rd_kafka_error_destroy(error);
1289
+ continue;
1290
+ }
1291
+ rcvd = rd_kafka_messages_count(batch);
1292
+ for (j = 0; j < rcvd; j++) {
1293
+ rd_kafka_message_t *msg =
1294
+ rd_kafka_messages_get(batch, j);
1295
+ if (!msg->err) {
1296
+ /* Should only be Msg3 (offset 4) re-delivered;
1297
+ * Msg4 at offset 6 is aborted in
1298
+ * read_committed.
1299
+ */
1300
+ TEST_ASSERT(
1301
+ msg->offset == 4,
1302
+ "Phase 4: unexpected offset %" PRId64,
1303
+ msg->offset);
1304
+ ack_err = rd_kafka_share_acknowledge_type(
1305
+ consumer, msg,
1306
+ RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_RELEASE);
1307
+ TEST_ASSERT(ack_err ==
1308
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1309
+ "Phase 4 RELEASE failed: %s",
1310
+ rd_kafka_err2name(ack_err));
1311
+ }
1312
+ }
1313
+ rd_kafka_messages_destroy(batch);
1314
+ batch = NULL;
1315
+ if (rcvd > 0) {
1316
+ error = rd_kafka_share_commit_async(consumer);
1317
+ if (error)
1318
+ rd_kafka_error_destroy(error);
1319
+ }
1320
+ }
1321
+
1322
+ /* Phase 5: alter isolation level to read_uncommitted */
1323
+ TEST_SAY("Phase 5: alter share.isolation.level to read_uncommitted\n");
1324
+ test_share_set_isolation_level(group, "read_uncommitted");
1325
+
1326
+ /* Phase 6: produce 4 more transactions */
1327
+ TEST_SAY("Phase 6: produce Msg5..Msg8\n");
1328
+ produce_msg_txn(producer, topic, "Message 5", rd_true);
1329
+ produce_msg_txn(producer, topic, "Message 6", rd_false);
1330
+ produce_msg_txn(producer, topic, "Message 7", rd_false);
1331
+ produce_msg_txn(producer, topic, "Message 8", rd_true);
1332
+
1333
+ /* Phase 7: expect to see 5 unique offsets: 4 (released Msg3),
1334
+ * 8, 10, 12, 14 (Msg5..Msg8). */
1335
+ TEST_SAY("Phase 7: consume - expect offsets {4,8,10,12,14}\n");
1336
+ attempts = 0;
1337
+ while (attempts++ < 60) {
1338
+ int new_acks = 0;
1339
+ rd_bool_t all_seen;
1340
+ rcvd = 0;
1341
+ error = rd_kafka_share_poll(consumer, 2000, &batch);
1342
+ if (error) {
1343
+ rd_kafka_error_destroy(error);
1344
+ continue;
1345
+ }
1346
+ rcvd = rd_kafka_messages_count(batch);
1347
+ for (j = 0; j < rcvd; j++) {
1348
+ rd_kafka_message_t *msg =
1349
+ rd_kafka_messages_get(batch, j);
1350
+ if (!msg->err) {
1351
+ int64_t off = msg->offset;
1352
+ if (off >= 0 && off < 20)
1353
+ seen[off] = rd_true;
1354
+ ack_err = rd_kafka_share_acknowledge_type(
1355
+ consumer, msg,
1356
+ RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
1357
+ TEST_ASSERT(ack_err ==
1358
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1359
+ "Phase 7 ACCEPT failed: %s",
1360
+ rd_kafka_err2name(ack_err));
1361
+ new_acks++;
1362
+ }
1363
+ }
1364
+ rd_kafka_messages_destroy(batch);
1365
+ batch = NULL;
1366
+ if (new_acks > 0) {
1367
+ error = rd_kafka_share_commit_async(consumer);
1368
+ if (error)
1369
+ rd_kafka_error_destroy(error);
1370
+ }
1371
+
1372
+ all_seen = rd_true;
1373
+ for (i = 0;
1374
+ i < sizeof(expected_offsets) / sizeof(expected_offsets[0]);
1375
+ i++) {
1376
+ if (!seen[expected_offsets[i]]) {
1377
+ all_seen = rd_false;
1378
+ break;
1379
+ }
1380
+ }
1381
+ if (all_seen)
1382
+ break;
1383
+ }
1384
+
1385
+ for (i = 0; i < sizeof(expected_offsets) / sizeof(expected_offsets[0]);
1386
+ i++) {
1387
+ TEST_ASSERT(seen[expected_offsets[i]],
1388
+ "Phase 7: expected offset %" PRId64
1389
+ " not seen after isolation change",
1390
+ expected_offsets[i]);
1391
+ }
1392
+
1393
+ TEST_SAY(
1394
+ "SUCCESS: dynamic READ_COMMITTED -> READ_UNCOMMITTED with "
1395
+ "RELEASE\n");
1396
+
1397
+ test_share_consumer_close(consumer);
1398
+ test_share_destroy(consumer);
1399
+ rd_kafka_destroy(producer);
1400
+
1401
+ SUB_TEST_PASS();
1402
+ }
1403
+
1404
+
1405
+ /**
1406
+ * @brief Test: dynamic isolation level change READ_COMMITTED ->
1407
+ * READ_UNCOMMITTED with REJECT acknowledgement on a committed
1408
+ * record.
1409
+ *
1410
+ * Same setup as the RELEASE variant, except Msg3 is REJECTed instead of
1411
+ * RELEASEd. After the isolation level switch, Msg3 must NOT be redelivered;
1412
+ * only the four new messages are expected -> 4 unique offsets.
1413
+ */
1414
+ static void do_test_dynamic_committed_to_uncommitted_with_reject(void) {
1415
+ const char *topic;
1416
+ const char *group = "share-dynamic-c-to-uc-reject";
1417
+ const char *txn_id = "txn-dynamic-c-uc-reject";
1418
+ rd_kafka_t *producer;
1419
+ rd_kafka_share_t *consumer;
1420
+ rd_kafka_messages_t *batch = NULL;
1421
+ rd_kafka_error_t *error;
1422
+ rd_kafka_resp_err_t ack_err;
1423
+ size_t rcvd, j;
1424
+ int attempts;
1425
+ int consumed;
1426
+ rd_bool_t seen[20] = {0};
1427
+ const int64_t expected_offsets[] = {8, 10, 12, 14};
1428
+ size_t i;
1429
+
1430
+ SUB_TEST();
1431
+
1432
+ topic = test_mk_topic_name("0177-dyn-c-uc-reject", 1);
1433
+ test_create_topic(NULL, topic, 1, -1);
1434
+
1435
+ producer = create_txn_producer(txn_id);
1436
+
1437
+ consumer = test_create_share_consumer(group, "explicit");
1438
+ configure_share_group(group, "read_committed");
1439
+ subscribe_share_consumer(consumer, topic);
1440
+
1441
+ /* Phase 1: committed Msg1 -> ACCEPT */
1442
+ TEST_SAY("Phase 1: committed Msg1 with READ_COMMITTED\n");
1443
+ produce_msg_txn(producer, topic, "Message 1", rd_true);
1444
+ consumed = 0;
1445
+ attempts = 0;
1446
+ while (consumed == 0 && attempts++ < 30) {
1447
+ rcvd = 0;
1448
+ error = rd_kafka_share_poll(consumer, 3000, &batch);
1449
+ if (error) {
1450
+ rd_kafka_error_destroy(error);
1451
+ continue;
1452
+ }
1453
+ rcvd = rd_kafka_messages_count(batch);
1454
+ for (j = 0; j < rcvd; j++) {
1455
+ rd_kafka_message_t *msg =
1456
+ rd_kafka_messages_get(batch, j);
1457
+ if (!msg->err) {
1458
+ TEST_ASSERT(msg->offset == 0,
1459
+ "Phase 1: expected offset 0, "
1460
+ "got %" PRId64,
1461
+ msg->offset);
1462
+ ack_err = rd_kafka_share_acknowledge_type(
1463
+ consumer, msg,
1464
+ RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
1465
+ TEST_ASSERT(ack_err ==
1466
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1467
+ "Phase 1 ACCEPT failed: %s",
1468
+ rd_kafka_err2name(ack_err));
1469
+ consumed++;
1470
+ }
1471
+ }
1472
+ rd_kafka_messages_destroy(batch);
1473
+ batch = NULL;
1474
+ }
1475
+ TEST_ASSERT(consumed == 1, "Phase 1: expected 1 record, got %d",
1476
+ consumed);
1477
+ error = rd_kafka_share_commit_async(consumer);
1478
+ TEST_ASSERT(!error, "Phase 1 commit_async failed");
1479
+
1480
+ /* Phase 2: aborted Msg2 */
1481
+ TEST_SAY("Phase 2: aborted Msg2 (read_committed hides it)\n");
1482
+ produce_msg_txn(producer, topic, "Message 2", rd_false);
1483
+ attempts = 10;
1484
+ while (attempts-- > 0) {
1485
+ rcvd = 0;
1486
+ error = rd_kafka_share_poll(consumer, 500, &batch);
1487
+ if (error)
1488
+ rd_kafka_error_destroy(error);
1489
+ rcvd = rd_kafka_messages_count(batch);
1490
+ for (j = 0; j < rcvd; j++) {
1491
+ rd_kafka_message_t *msg =
1492
+ rd_kafka_messages_get(batch, j);
1493
+ TEST_ASSERT(msg->err || msg->offset != 2,
1494
+ "Phase 2: aborted Msg2 leaked at "
1495
+ "offset 2 in read_committed");
1496
+ }
1497
+ rd_kafka_messages_destroy(batch);
1498
+ batch = NULL;
1499
+ }
1500
+
1501
+ /* Phase 3: committed Msg3 -> REJECT */
1502
+ TEST_SAY("Phase 3: committed Msg3 -> REJECT\n");
1503
+ produce_msg_txn(producer, topic, "Message 3", rd_true);
1504
+ consumed = 0;
1505
+ attempts = 0;
1506
+ while (consumed == 0 && attempts++ < 30) {
1507
+ rcvd = 0;
1508
+ error = rd_kafka_share_poll(consumer, 3000, &batch);
1509
+ if (error) {
1510
+ rd_kafka_error_destroy(error);
1511
+ continue;
1512
+ }
1513
+ rcvd = rd_kafka_messages_count(batch);
1514
+ for (j = 0; j < rcvd; j++) {
1515
+ rd_kafka_message_t *msg =
1516
+ rd_kafka_messages_get(batch, j);
1517
+ if (!msg->err) {
1518
+ TEST_ASSERT(msg->offset == 4,
1519
+ "Phase 3: expected offset 4 "
1520
+ "(Msg3), got %" PRId64,
1521
+ msg->offset);
1522
+ ack_err = rd_kafka_share_acknowledge_type(
1523
+ consumer, msg,
1524
+ RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_REJECT);
1525
+ TEST_ASSERT(ack_err ==
1526
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1527
+ "Phase 3 REJECT failed: %s",
1528
+ rd_kafka_err2name(ack_err));
1529
+ consumed++;
1530
+ }
1531
+ }
1532
+ rd_kafka_messages_destroy(batch);
1533
+ batch = NULL;
1534
+ }
1535
+ TEST_ASSERT(consumed == 1, "Phase 3: expected 1 record, got %d",
1536
+ consumed);
1537
+ error = rd_kafka_share_commit_async(consumer);
1538
+ TEST_ASSERT(!error, "Phase 3 commit_async failed");
1539
+
1540
+ /* Phase 4: aborted Msg4. Msg3 was REJECTed so it should not return.
1541
+ * Just poll for a while to let the broker process the abort marker. */
1542
+ TEST_SAY("Phase 4: aborted Msg4 (Msg3 was REJECTed)\n");
1543
+ produce_msg_txn(producer, topic, "Message 4", rd_false);
1544
+ attempts = 20;
1545
+ while (attempts-- > 0) {
1546
+ rcvd = 0;
1547
+ error = rd_kafka_share_poll(consumer, 500, &batch);
1548
+ if (error)
1549
+ rd_kafka_error_destroy(error);
1550
+ rcvd = rd_kafka_messages_count(batch);
1551
+ for (j = 0; j < rcvd; j++) {
1552
+ rd_kafka_message_t *msg =
1553
+ rd_kafka_messages_get(batch, j);
1554
+ if (!msg->err) {
1555
+ TEST_FAIL(
1556
+ "Phase 4: unexpected record at "
1557
+ "offset %" PRId64
1558
+ " (Msg3 was REJECTed, Msg4 is "
1559
+ "aborted in read_committed)",
1560
+ msg->offset);
1561
+ }
1562
+ }
1563
+ rd_kafka_messages_destroy(batch);
1564
+ batch = NULL;
1565
+ }
1566
+
1567
+ /* Phase 5: alter isolation level to read_uncommitted */
1568
+ TEST_SAY("Phase 5: alter share.isolation.level to read_uncommitted\n");
1569
+ test_share_set_isolation_level(group, "read_uncommitted");
1570
+
1571
+ /* Phase 6: produce 4 more transactions */
1572
+ TEST_SAY("Phase 6: produce Msg5..Msg8\n");
1573
+ produce_msg_txn(producer, topic, "Message 5", rd_true);
1574
+ produce_msg_txn(producer, topic, "Message 6", rd_false);
1575
+ produce_msg_txn(producer, topic, "Message 7", rd_false);
1576
+ produce_msg_txn(producer, topic, "Message 8", rd_true);
1577
+
1578
+ /* Phase 7: expect offsets {8, 10, 12, 14}. Msg3 (offset 4) must NOT
1579
+ * reappear because it was REJECTed. */
1580
+ TEST_SAY("Phase 7: consume - expect offsets {8,10,12,14}, no Msg3\n");
1581
+ attempts = 0;
1582
+ while (attempts++ < 60) {
1583
+ int new_acks = 0;
1584
+ rd_bool_t all_seen;
1585
+ rcvd = 0;
1586
+ error = rd_kafka_share_poll(consumer, 2000, &batch);
1587
+ if (error) {
1588
+ rd_kafka_error_destroy(error);
1589
+ continue;
1590
+ }
1591
+ rcvd = rd_kafka_messages_count(batch);
1592
+ for (j = 0; j < rcvd; j++) {
1593
+ rd_kafka_message_t *msg =
1594
+ rd_kafka_messages_get(batch, j);
1595
+ if (!msg->err) {
1596
+ int64_t off = msg->offset;
1597
+ TEST_ASSERT(off != 4,
1598
+ "Phase 7: Msg3 (offset 4) "
1599
+ "should not be redelivered "
1600
+ "after REJECT");
1601
+ if (off >= 0 && off < 20)
1602
+ seen[off] = rd_true;
1603
+ ack_err = rd_kafka_share_acknowledge_type(
1604
+ consumer, msg,
1605
+ RD_KAFKA_SHARE_ACKNOWLEDGE_TYPE_ACCEPT);
1606
+ TEST_ASSERT(ack_err ==
1607
+ RD_KAFKA_RESP_ERR_NO_ERROR,
1608
+ "Phase 7 ACCEPT failed: %s",
1609
+ rd_kafka_err2name(ack_err));
1610
+ new_acks++;
1611
+ }
1612
+ }
1613
+ rd_kafka_messages_destroy(batch);
1614
+ batch = NULL;
1615
+ if (new_acks > 0) {
1616
+ error = rd_kafka_share_commit_async(consumer);
1617
+ if (error)
1618
+ rd_kafka_error_destroy(error);
1619
+ }
1620
+
1621
+ all_seen = rd_true;
1622
+ for (i = 0;
1623
+ i < sizeof(expected_offsets) / sizeof(expected_offsets[0]);
1624
+ i++) {
1625
+ if (!seen[expected_offsets[i]]) {
1626
+ all_seen = rd_false;
1627
+ break;
1628
+ }
1629
+ }
1630
+ if (all_seen)
1631
+ break;
1632
+ }
1633
+
1634
+ for (i = 0; i < sizeof(expected_offsets) / sizeof(expected_offsets[0]);
1635
+ i++) {
1636
+ TEST_ASSERT(seen[expected_offsets[i]],
1637
+ "Phase 7: expected offset %" PRId64
1638
+ " not seen after isolation change",
1639
+ expected_offsets[i]);
1640
+ }
1641
+ TEST_ASSERT(!seen[4],
1642
+ "Phase 7: Msg3 (offset 4) appeared but it was REJECTed");
1643
+
1644
+ TEST_SAY(
1645
+ "SUCCESS: dynamic READ_COMMITTED -> READ_UNCOMMITTED with "
1646
+ "REJECT\n");
1647
+
1648
+ test_share_consumer_close(consumer);
1649
+ test_share_destroy(consumer);
1650
+ rd_kafka_destroy(producer);
1651
+
1652
+ SUB_TEST_PASS();
1653
+ }
1654
+
1655
+
1656
+ /**
1657
+ * @brief Two share groups, different isolation levels, same topic.
1658
+ *
1659
+ * share.isolation.level is a BROKER-SIDE group config — consumers in
1660
+ * the SAME group cannot have different isolation levels. This test
1661
+ * exercises two distinct groups consuming the same topic concurrently
1662
+ * with different isolation levels.
1663
+ *
1664
+ * Producer mix on the topic:
1665
+ * - txn-A committed: 100 records
1666
+ * - txn-B aborted: 100 records
1667
+ * - txn-C committed: 100 records
1668
+ *
1669
+ * Expected:
1670
+ * - Group G1 (read_committed): sees 200 records (A+C only)
1671
+ * - Group G2 (read_uncommitted): sees 300 records (A+B+C)
1672
+ */
1673
+ static void do_test_two_groups_different_isolation_levels(void) {
1674
+ const char *topic;
1675
+ const char *grp_committed = "share-txn-twogroup-rc";
1676
+ const char *grp_uncommitted = "share-txn-twogroup-ru";
1677
+ rd_kafka_t *producer_a;
1678
+ rd_kafka_t *producer_b;
1679
+ rd_kafka_t *producer_c;
1680
+ rd_kafka_share_t *c_committed;
1681
+ rd_kafka_share_t *c_uncommitted;
1682
+ const int msg_cnt = 100;
1683
+ const int expected_rc = 2 * msg_cnt;
1684
+ const int expected_ru = 3 * msg_cnt;
1685
+ int consumed_rc;
1686
+ int consumed_ru;
1687
+
1688
+ SUB_TEST("two groups, different isolation levels");
1689
+
1690
+ topic = test_mk_topic_name("0177-twogroup-iso", 1);
1691
+ test_create_topic(NULL, topic, 1, -1);
1692
+
1693
+ producer_a = create_txn_producer("twogroup-iso-A");
1694
+ producer_b = create_txn_producer("twogroup-iso-B");
1695
+ producer_c = create_txn_producer("twogroup-iso-C");
1696
+
1697
+ /* Subscribe both consumers BEFORE producing so they're ready to
1698
+ * consume from the earliest offset. */
1699
+ c_committed = test_create_share_consumer(grp_committed, NULL);
1700
+ configure_share_group(grp_committed, "read_committed");
1701
+ subscribe_share_consumer(c_committed, topic);
1702
+
1703
+ c_uncommitted = test_create_share_consumer(grp_uncommitted, NULL);
1704
+ configure_share_group(grp_uncommitted, "read_uncommitted");
1705
+ subscribe_share_consumer(c_uncommitted, topic);
1706
+
1707
+ /* txn-A: committed */
1708
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer_a));
1709
+ test_produce_msgs2(producer_a, topic, test_id_generate(),
1710
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
1711
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer_a, 30000));
1712
+
1713
+ /* txn-B: aborted */
1714
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer_b));
1715
+ test_curr->ignore_dr_err = rd_true;
1716
+ test_produce_msgs2(producer_b, topic, test_id_generate(),
1717
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
1718
+ TEST_CALL_ERROR__(rd_kafka_abort_transaction(producer_b, 30000));
1719
+ test_curr->ignore_dr_err = rd_false;
1720
+
1721
+ /* txn-C: committed */
1722
+ TEST_CALL_ERROR__(rd_kafka_begin_transaction(producer_c));
1723
+ test_produce_msgs2(producer_c, topic, test_id_generate(),
1724
+ RD_KAFKA_PARTITION_UA, 0, msg_cnt, NULL, 0);
1725
+ TEST_CALL_ERROR__(rd_kafka_commit_transaction(producer_c, 30000));
1726
+
1727
+ /* Both groups consume the same topic concurrently. */
1728
+ TEST_SAY("Consuming via read_committed group\n");
1729
+ consumed_rc =
1730
+ consume_share_messages(c_committed, expected_rc, 80, 3000);
1731
+ TEST_SAY("Consuming via read_uncommitted group\n");
1732
+ consumed_ru =
1733
+ consume_share_messages(c_uncommitted, expected_ru, 80, 3000);
1734
+
1735
+ TEST_SAY(
1736
+ "Result: read_committed=%d (expected %d), "
1737
+ "read_uncommitted=%d (expected %d)\n",
1738
+ consumed_rc, expected_rc, consumed_ru, expected_ru);
1739
+
1740
+ TEST_ASSERT(consumed_rc == expected_rc,
1741
+ "read_committed group expected %d records, got %d",
1742
+ expected_rc, consumed_rc);
1743
+ TEST_ASSERT(consumed_ru == expected_ru,
1744
+ "read_uncommitted group expected %d records, got %d",
1745
+ expected_ru, consumed_ru);
1746
+
1747
+ test_share_consumer_close(c_committed);
1748
+ test_share_destroy(c_committed);
1749
+ test_share_consumer_close(c_uncommitted);
1750
+ test_share_destroy(c_uncommitted);
1751
+ rd_kafka_destroy(producer_a);
1752
+ rd_kafka_destroy(producer_b);
1753
+ rd_kafka_destroy(producer_c);
1754
+
1755
+ SUB_TEST_PASS();
1756
+ }
1757
+
1758
+
1759
+ int main_0177_share_consumer_transactions(int argc, char **argv) {
1760
+ /* Test both isolation levels */
1761
+ const char *isolation_levels[] = {"read_committed", "read_uncommitted"};
1762
+ int i;
1763
+
1764
+ test_timeout_set(200);
1765
+
1766
+ /* Create common handles for all tests */
1767
+ common_admin = test_create_producer();
1768
+ common_regular_producer = test_create_producer();
1769
+
1770
+ for (i = 0; i < 2; i++) {
1771
+ const char *level = isolation_levels[i];
1772
+
1773
+ TEST_SAY("\n");
1774
+ TEST_SAY("========================================\n");
1775
+ TEST_SAY("Testing with isolation.level=%s\n", level);
1776
+ TEST_SAY("========================================\n");
1777
+
1778
+ /* Basic transaction tests */
1779
+ do_test_committed_transaction(level);
1780
+ do_test_aborted_transaction(level);
1781
+ do_test_mixed_transactions(level);
1782
+
1783
+ /* Control records filtering */
1784
+ do_test_control_records_filtered(level);
1785
+
1786
+ /* Multi-partition and multi-producer scenarios */
1787
+ do_test_multi_partition_transactions(level);
1788
+ do_test_interleaved_producers(level);
1789
+
1790
+ /* Mixed workloads */
1791
+ do_test_mixed_txn_non_txn(level);
1792
+
1793
+ /* Interval-based abort pattern */
1794
+ do_test_interval_abort_pattern(level);
1795
+ }
1796
+
1797
+ /* Dynamic isolation level change tests (run once, not per-level) */
1798
+ TEST_SAY("\n");
1799
+ TEST_SAY("========================================\n");
1800
+ TEST_SAY("Testing dynamic isolation level changes\n");
1801
+ TEST_SAY("========================================\n");
1802
+
1803
+ do_test_dynamic_uncommitted_to_committed();
1804
+ do_test_dynamic_committed_to_uncommitted_with_release();
1805
+ do_test_dynamic_committed_to_uncommitted_with_reject();
1806
+
1807
+ /* Two-groups, different isolation levels on the same topic */
1808
+ do_test_two_groups_different_isolation_levels();
1809
+
1810
+ /* Cleanup common handles */
1811
+ rd_kafka_destroy(common_admin);
1812
+ rd_kafka_destroy(common_regular_producer);
1813
+
1814
+ return 0;
1815
+ }