tigerbeetle 0.0.34 → 0.0.37

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/ext/tb_client/extconf.rb +13 -13
  4. data/ext/tb_client/tigerbeetle/LICENSE +177 -0
  5. data/ext/tb_client/tigerbeetle/build.zig +2327 -0
  6. data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
  7. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
  8. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
  9. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
  10. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
  11. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
  12. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
  13. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
  14. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
  15. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
  16. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
  17. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
  18. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
  19. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
  20. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
  21. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
  22. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
  23. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
  24. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
  25. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
  26. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
  27. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
  28. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
  29. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
  30. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
  31. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
  32. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
  33. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
  34. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
  35. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
  36. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
  37. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
  38. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
  39. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
  40. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
  41. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
  42. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
  43. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
  44. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
  45. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
  46. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
  47. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
  48. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
  49. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
  50. data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
  51. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
  52. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
  53. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
  54. data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
  55. data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
  56. data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
  57. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
  58. data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
  59. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
  60. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
  61. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
  62. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
  63. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
  64. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
  65. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
  66. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
  67. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
  68. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
  69. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
  70. data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
  71. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
  72. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
  73. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
  74. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
  75. data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
  76. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
  77. data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
  78. data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
  79. data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
  80. data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
  81. data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
  82. data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
  83. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
  84. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
  85. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
  86. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
  87. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
  88. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
  89. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
  90. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
  91. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
  92. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
  93. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
  94. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
  95. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
  96. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
  97. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
  98. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
  99. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
  100. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
  101. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
  102. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
  103. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
  104. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
  105. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
  106. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
  107. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
  108. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
  109. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
  110. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
  111. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
  112. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
  113. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
  114. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
  115. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
  116. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
  117. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
  118. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
  119. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
  120. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
  121. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
  122. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
  123. data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
  124. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
  125. data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
  126. data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
  127. data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
  128. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
  129. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
  130. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
  131. data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
  132. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
  133. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
  134. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
  135. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
  136. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
  137. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
  138. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
  139. data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
  140. data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
  141. data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
  142. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
  143. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
  144. data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
  145. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
  146. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
  147. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
  148. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
  149. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
  150. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
  151. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
  152. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
  153. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
  154. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
  155. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
  156. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
  157. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
  158. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
  159. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
  160. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
  161. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
  162. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
  163. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
  164. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
  165. data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
  166. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
  167. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
  168. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
  169. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
  170. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
  171. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
  172. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
  173. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
  174. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
  175. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
  176. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
  177. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
  178. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
  179. data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
  180. data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
  181. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
  182. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
  183. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
  184. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
  185. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
  186. data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
  187. data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
  188. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
  189. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
  190. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
  191. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
  192. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
  193. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
  194. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
  195. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
  196. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
  197. data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
  198. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
  199. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
  200. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
  201. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
  202. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
  203. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
  204. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
  205. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
  206. data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
  207. data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
  208. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
  209. data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
  210. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
  211. data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
  212. data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
  213. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
  214. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
  215. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
  216. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
  217. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
  218. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
  219. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
  220. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
  221. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
  222. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
  223. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
  224. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
  225. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
  226. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
  227. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
  228. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
  229. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
  230. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
  231. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
  232. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
  233. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
  234. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
  235. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
  236. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
  237. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
  238. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
  239. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
  240. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
  241. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
  242. data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
  243. data/lib/tb_client/shared_lib.rb +12 -5
  244. data/lib/tigerbeetle/client.rb +1 -1
  245. data/lib/tigerbeetle/platforms.rb +9 -0
  246. data/lib/tigerbeetle/version.rb +2 -2
  247. data/tigerbeetle.gemspec +22 -5
  248. metadata +242 -3
  249. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,469 @@
1
+ const stdx = @import("stdx");
2
+ const std = @import("std");
3
+ const assert = std.debug.assert;
4
+ const maybe = stdx.maybe;
5
+
6
+ const vsr = @import("./vsr.zig");
7
+ const MessagePool = @import("message_pool.zig").MessagePool;
8
+ const Message = MessagePool.Message;
9
+ const Header = vsr.Header;
10
+ const constants = vsr.constants;
11
+
12
+ /// MessageBuffer is the interface between a MessageBus and a Replica for passing batches of
13
+ /// messages while minimizing copies. It handles message framing, but doesn't do IO directly.
14
+ ///
15
+ /// It is a producer-consumer ring buffer of bytes, with two twists:
16
+ /// - consumer can skip over or "suspend" certain slices, to return to them later.
17
+ /// - producer validates bytes against a checksum, and this validation is sticky: a message is
18
+ /// validated once, even if it is skipped many times.
19
+ ///
20
+ /// Invariant: suspend_size ≤ process_size ≤ advance_size ≤ receive_size
21
+ pub const MessageBuffer = struct {
22
+ /// The buffer passed to the kernel for reading into. This is Message rather than []u8 to
23
+ /// enable zero-copy fast path. If a recv syscall reads exactly one message, no copying occurs.
24
+ message: *Message,
25
+
26
+ /// Suspended bytes, always a number of full messages.
27
+ suspend_size: u32 = 0,
28
+ /// Processed (consumed or suspended) bytes, always a number of full messages.
29
+ process_size: u32 = 0,
30
+ /// Bytes covered by a valid checksum, a number of full messages and maybe a header.
31
+ advance_size: u32 = 0,
32
+ /// The amount of bytes received from the kernel.
33
+ receive_size: u32 = 0,
34
+
35
+ // An error occurred, and the MessageBus should terminate connection.
36
+ // Can be set by replica to indicate semantic errors, such as wrong cluster.
37
+ invalid: ?InvalidReason = null,
38
+
39
+ iterator_state: enum {
40
+ idle,
41
+ after_peek,
42
+ after_consume_suspend,
43
+ } = .idle,
44
+
45
+ const InvalidReason = enum {
46
+ header_checksum,
47
+ header_size,
48
+ header_cluster,
49
+ body_checksum,
50
+ misdirected,
51
+ };
52
+
53
+ fn invariants(buffer: *MessageBuffer) void {
54
+ assert(buffer.suspend_size <= buffer.process_size);
55
+ assert(buffer.process_size <= buffer.advance_size);
56
+ assert(buffer.advance_size <= buffer.receive_size);
57
+ if (buffer.invalid != null) {
58
+ assert(buffer.suspend_size == 0);
59
+ assert(buffer.process_size == 0);
60
+ assert(buffer.advance_size == 0);
61
+ assert(buffer.receive_size == 0);
62
+ assert(buffer.iterator_state == .idle);
63
+ }
64
+ }
65
+
66
+ pub fn init(pool: *MessagePool) MessageBuffer {
67
+ return .{ .message = pool.get_message(null) };
68
+ }
69
+
70
+ pub fn deinit(buffer: *MessageBuffer, pool: *MessagePool) void {
71
+ pool.unref(buffer.message);
72
+ buffer.* = undefined;
73
+ }
74
+
75
+ /// Pass this to the kernel to read into.
76
+ pub fn recv_slice(buffer: *MessageBuffer) []u8 {
77
+ assert(buffer.receive_size < constants.message_size_max);
78
+ assert(buffer.iterator_state == .idle);
79
+ assert(buffer.invalid == null);
80
+ return buffer.message.buffer[buffer.receive_size..];
81
+ }
82
+
83
+ /// When the kernel returns, informs the buffer about the read size.
84
+ pub fn recv_advance(buffer: *MessageBuffer, size: u32) void {
85
+ assert(buffer.iterator_state == .idle);
86
+ assert(buffer.process_size == 0);
87
+ assert(size > 0);
88
+ assert(size <= constants.message_size_max);
89
+
90
+ buffer.receive_size += size;
91
+ assert(buffer.receive_size <= constants.message_size_max);
92
+ buffer.advance();
93
+ }
94
+
95
+ pub fn invalidate(buffer: *MessageBuffer, reason: InvalidReason) void {
96
+ assert(buffer.invalid == null);
97
+ buffer.suspend_size = 0;
98
+ buffer.process_size = 0;
99
+ buffer.advance_size = 0;
100
+ buffer.receive_size = 0;
101
+ buffer.iterator_state = .idle;
102
+ buffer.invalid = reason;
103
+ buffer.invariants();
104
+ }
105
+
106
+ /// Advances the parsing state machine.
107
+ /// Idempotent, but eagerly called whenever receive_size or process_size change.
108
+ fn advance(buffer: *MessageBuffer) void {
109
+ if (buffer.invalid == null) buffer.advance_header();
110
+ if (buffer.invalid == null) buffer.advance_body();
111
+ buffer.invariants();
112
+ }
113
+
114
+ fn advance_header(buffer: *MessageBuffer) void {
115
+ assert(buffer.invalid == null);
116
+ assert(buffer.advance_size <= buffer.receive_size);
117
+ if (buffer.advance_size >= buffer.process_size + @sizeOf(Header)) {
118
+ return; // Header is already known to be valid.
119
+ }
120
+ assert(buffer.advance_size == buffer.process_size);
121
+ if (buffer.receive_size - buffer.process_size < @sizeOf(Header)) {
122
+ return; // Header not received yet.
123
+ }
124
+
125
+ const header_bytes =
126
+ buffer.message.buffer[buffer.process_size..][0..@sizeOf(Header)];
127
+
128
+ var header: Header = undefined;
129
+ stdx.copy_disjoint(.exact, u8, std.mem.asBytes(&header), header_bytes);
130
+
131
+ if (!header.valid_checksum()) {
132
+ buffer.invalidate(.header_checksum);
133
+ return;
134
+ }
135
+
136
+ // Check that command is valid without materializing invalid Zig enum value.
137
+ comptime assert(@sizeOf(vsr.Command) == @sizeOf(u8) and
138
+ @FieldType(Header, "command") == vsr.Command);
139
+ const command_raw: u8 = header_bytes[@offsetOf(Header, "command")];
140
+ _ = std.meta.intToEnum(vsr.Command, command_raw) catch {
141
+ vsr.fatal(
142
+ .unknown_vsr_command,
143
+ "unknown VSR command, crashing for safety " ++
144
+ "(command={d} protocol={d} replica={d} release={})",
145
+ .{
146
+ command_raw,
147
+ header.protocol,
148
+ header.replica,
149
+ header.release,
150
+ },
151
+ );
152
+ };
153
+
154
+ if (header.size < @sizeOf(Header) or header.size > constants.message_size_max) {
155
+ buffer.invalidate(.header_size);
156
+ return;
157
+ }
158
+ assert(@sizeOf(Header) <= header.size and header.size <= constants.message_size_max);
159
+
160
+ buffer.advance_size += @sizeOf(Header);
161
+ }
162
+
163
+ fn advance_body(buffer: *MessageBuffer) void {
164
+ assert(buffer.invalid == null);
165
+ if (buffer.advance_size < buffer.process_size + @sizeOf(Header)) {
166
+ return; // Header not received yet.
167
+ }
168
+
169
+ const header = buffer.copy_header();
170
+
171
+ if (buffer.receive_size - buffer.process_size < header.size) {
172
+ return; // Body not received yet.
173
+ }
174
+
175
+ if (buffer.advance_size >= buffer.process_size + header.size) {
176
+ return; // Body is already known to be valid.
177
+ }
178
+
179
+ assert(buffer.advance_size - buffer.process_size == @sizeOf(Header));
180
+ const body = buffer.message.buffer[buffer.process_size..][@sizeOf(Header)..header.size];
181
+ if (!header.valid_checksum_body(body)) {
182
+ buffer.invalidate(.body_checksum);
183
+ return;
184
+ }
185
+ buffer.advance_size += header.size - @sizeOf(Header);
186
+ }
187
+
188
+ /// Peek at the header for the incoming message. Necessitates a copy to guarantee alignment.
189
+ fn copy_header(buffer: *const MessageBuffer) Header {
190
+ assert(buffer.receive_size - buffer.process_size >= @sizeOf(Header));
191
+ var header: Header = undefined;
192
+ stdx.copy_disjoint(
193
+ .exact,
194
+ u8,
195
+ std.mem.asBytes(&header),
196
+ buffer.message.buffer[buffer.process_size..][0..@sizeOf(Header)],
197
+ );
198
+ return header;
199
+ }
200
+
201
+ pub fn has_message(buffer: *const MessageBuffer) bool {
202
+ const valid_unprocessed = buffer.advance_size - buffer.process_size;
203
+ if (valid_unprocessed >= @sizeOf(Header)) {
204
+ const header = buffer.copy_header();
205
+ if (valid_unprocessed >= header.size) {
206
+ return true;
207
+ }
208
+ }
209
+ return false;
210
+ }
211
+
212
+ /// MessageBuffer is also an iterator which must be driven to completion.
213
+ /// A call to next_header must be immediately followed by a call to consume_message
214
+ /// or suspend_message.
215
+ pub fn next_header(buffer: *MessageBuffer) ?Header {
216
+ maybe(buffer.invalid != null);
217
+
218
+ switch (buffer.iterator_state) {
219
+ .idle, .after_consume_suspend => {},
220
+ else => unreachable,
221
+ }
222
+
223
+ const valid_unprocessed = buffer.advance_size - buffer.process_size;
224
+ if (valid_unprocessed >= @sizeOf(Header)) {
225
+ assert(buffer.invalid == null);
226
+ const header = buffer.copy_header();
227
+ if (valid_unprocessed >= header.size) {
228
+ buffer.iterator_state = .after_peek;
229
+ return header;
230
+ }
231
+ }
232
+
233
+ // Move from this:
234
+ // | bytes | hole | bytes | hole |
235
+ // ^suspend_size ^process_size ^receive_size
236
+ //
237
+ // To this:
238
+ // | bytes | hole |
239
+ // ^ suspend_size,process_size ^ receive_size
240
+ assert(buffer.suspend_size <= buffer.process_size);
241
+ assert(buffer.process_size <= buffer.receive_size);
242
+
243
+ if (buffer.suspend_size < buffer.process_size) {
244
+ stdx.copy_left(
245
+ .inexact,
246
+ u8,
247
+ buffer.message.buffer[buffer.suspend_size..],
248
+ buffer.message.buffer[buffer.process_size..buffer.receive_size],
249
+ );
250
+ }
251
+ buffer.receive_size -= (buffer.process_size - buffer.suspend_size);
252
+ buffer.advance_size -= (buffer.process_size - buffer.suspend_size);
253
+ buffer.suspend_size = 0;
254
+ buffer.process_size = 0;
255
+ buffer.iterator_state = .idle;
256
+
257
+ // The purpose of tracking advance_size across iterations is to "cache" checksum validation.
258
+ // As a sanity check, assert that advance-after-back-shift is indeed a no-op.
259
+ const advance_size_idempotent = buffer.advance_size;
260
+ buffer.advance();
261
+ assert(buffer.advance_size == advance_size_idempotent);
262
+
263
+ return null;
264
+ }
265
+
266
+ pub fn consume_message(
267
+ buffer: *MessageBuffer,
268
+ pool: *MessagePool,
269
+ header: *const Header,
270
+ ) *Message {
271
+ assert(buffer.iterator_state == .after_peek);
272
+ assert(buffer.advance_size - buffer.process_size >= header.size);
273
+ assert(buffer.invalid == null);
274
+ defer buffer.iterator_state = .after_consume_suspend;
275
+
276
+ if (buffer.process_size == 0 and buffer.receive_size == header.size) {
277
+ assert(buffer.message.header.checksum == header.checksum);
278
+
279
+ assert(buffer.suspend_size == 0);
280
+ buffer.process_size = 0;
281
+ buffer.receive_size = 0;
282
+ buffer.advance_size = 0;
283
+ buffer.advance();
284
+ assert(buffer.advance_size == 0);
285
+
286
+ defer buffer.message = pool.get_message(null);
287
+
288
+ return buffer.message;
289
+ }
290
+
291
+ const message = pool.get_message(null);
292
+ defer pool.unref(message);
293
+
294
+ stdx.copy_disjoint(
295
+ .inexact,
296
+ u8,
297
+ message.buffer,
298
+ buffer.message.buffer[buffer.process_size..][0..header.size],
299
+ );
300
+ buffer.process_size += header.size;
301
+ assert(buffer.process_size <= buffer.receive_size);
302
+ buffer.advance();
303
+
304
+ assert(message.header.checksum == header.checksum);
305
+ return message.ref();
306
+ }
307
+
308
+ pub fn suspend_message(buffer: *MessageBuffer, header: *const Header) void {
309
+ assert(buffer.iterator_state == .after_peek);
310
+ assert(buffer.advance_size - buffer.process_size >= header.size);
311
+ assert(buffer.invalid == null);
312
+ assert(header.size <= constants.message_size_max);
313
+ assert(std.mem.eql(
314
+ u8,
315
+ std.mem.asBytes(header),
316
+ buffer.message.buffer[buffer.process_size..][0..@sizeOf(Header)],
317
+ ));
318
+ assert(buffer.suspend_size <= buffer.process_size);
319
+
320
+ defer buffer.iterator_state = .after_consume_suspend;
321
+
322
+ if (buffer.suspend_size < buffer.process_size) {
323
+ // Move from this:
324
+ // | bytes | hole | message | bytes |
325
+ // ^suspend_size ^process_size ^receive_size
326
+ //
327
+ // To this:
328
+ // | bytes | message | hole | bytes |
329
+ // ^suspend_size ^process_size ^receive_size
330
+ stdx.copy_left(
331
+ .inexact,
332
+ u8,
333
+ buffer.message.buffer[buffer.suspend_size..],
334
+ buffer.message.buffer[buffer.process_size..][0..header.size],
335
+ );
336
+ }
337
+
338
+ buffer.suspend_size += header.size;
339
+ buffer.process_size += header.size;
340
+ buffer.advance();
341
+ }
342
+ };
343
+
344
+ test "MessageBuffer fuzz" {
345
+ // Generate a byte buffer with a bunch of prepares side-by-side.
346
+ // Optionally corrupt a single bit in the buffer.
347
+ // Feed the buffer in chunks of varying length to the MessageBuffer, verify that all messages
348
+ // are received unless a fault is detected.
349
+ const messages_max = 100;
350
+
351
+ var prng = stdx.PRNG.from_seed_testing();
352
+ const gpa = std.testing.allocator;
353
+
354
+ var buffer: []u8 = try gpa.alloc(u8, 5 * constants.message_size_max);
355
+ defer gpa.free(buffer);
356
+
357
+ for (0..100) |_| {
358
+ const fault = prng.boolean();
359
+ var total_size: u32 = 0;
360
+ var headers: stdx.BoundedArrayType(Header, messages_max) = .{};
361
+ for (0..messages_max) |_| {
362
+ const message_size: u32 = switch (prng.chances(.{
363
+ .min = 10,
364
+ .max = 10,
365
+ .random = 80,
366
+ })) {
367
+ .min => @sizeOf(Header),
368
+ .max => constants.message_size_max,
369
+ .random => prng.range_inclusive(u32, @sizeOf(Header), constants.message_size_max),
370
+ };
371
+
372
+ if (total_size + message_size > buffer.len) {
373
+ break;
374
+ }
375
+
376
+ var header: vsr.Header.Prepare = .{
377
+ .cluster = 1,
378
+ .view = 1,
379
+ .command = .prepare,
380
+ .parent = prng.int(u128),
381
+ .request_checksum = prng.int(u128),
382
+ .checkpoint_id = prng.int(u128),
383
+ .client = 1,
384
+ .commit = 10,
385
+ .timestamp = 999,
386
+ .request = 1,
387
+ .operation = .register,
388
+ .release = vsr.Release.minimum,
389
+ .op = 1,
390
+ .size = message_size,
391
+ };
392
+ const body = buffer[total_size..][@sizeOf(Header)..header.size];
393
+ prng.fill(body);
394
+ header.set_checksum_body(body);
395
+ header.set_checksum();
396
+ stdx.copy_disjoint(
397
+ .exact,
398
+ u8,
399
+ buffer[total_size..][0..@sizeOf(Header)],
400
+ std.mem.asBytes(&header),
401
+ );
402
+ total_size += header.size;
403
+ headers.push(header.frame_const().*);
404
+ }
405
+
406
+ if (fault) {
407
+ const byte_index = prng.index(buffer[0..total_size]);
408
+ const bit_index = prng.int_inclusive(u3, 7);
409
+ buffer[byte_index] ^= @as(u8, 1) << bit_index;
410
+ }
411
+
412
+ var pool = try MessagePool.init(gpa, .{ .replica = .{
413
+ .members_count = 6,
414
+ .pipeline_requests_limit = 1,
415
+ .message_bus = .testing,
416
+ } });
417
+ defer pool.deinit(gpa);
418
+
419
+ var message_buffer = MessageBuffer.init(&pool);
420
+ defer message_buffer.deinit(&pool);
421
+
422
+ var recv_size: u32 = 0;
423
+ while (headers.count() > 0) {
424
+ if (message_buffer.receive_size < constants.message_size_max and
425
+ recv_size < total_size)
426
+ {
427
+ const recv_slice = message_buffer.recv_slice();
428
+ const chunk_size = @min(
429
+ prng.range_inclusive(u32, 1, @intCast(recv_slice.len)),
430
+ total_size - recv_size,
431
+ );
432
+ stdx.copy_disjoint(
433
+ .exact,
434
+ u8,
435
+ recv_slice[0..chunk_size],
436
+ buffer[recv_size..][0..chunk_size],
437
+ );
438
+ message_buffer.recv_advance(chunk_size);
439
+ recv_size += chunk_size;
440
+ }
441
+
442
+ var header_index: u32 = 0;
443
+ while (message_buffer.next_header()) |header| {
444
+ message_buffer.invariants();
445
+ if (prng.boolean()) {
446
+ const message = message_buffer.consume_message(&pool, &header);
447
+ defer pool.unref(message);
448
+
449
+ assert(stdx.equal_bytes(Header, message.header, &headers.get(header_index)));
450
+ _ = headers.ordered_remove(header_index);
451
+ } else {
452
+ message_buffer.suspend_message(&header);
453
+ header_index += 1;
454
+ }
455
+ }
456
+ assert(message_buffer.iterator_state == .idle);
457
+ if (message_buffer.invalid) |reason| {
458
+ if (!fault) std.debug.panic("invalid without faults: {s}", .{@tagName(reason)});
459
+ break;
460
+ }
461
+ }
462
+ if (fault) {
463
+ assert(message_buffer.invalid != null);
464
+ } else {
465
+ assert(message_buffer.invalid == null);
466
+ assert(headers.count() == 0);
467
+ }
468
+ }
469
+ }