tigerbeetle 0.0.36 → 0.0.38

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 (248) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -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 +1092 -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 +120 -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 +359 -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 +962 -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 +90 -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 +534 -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 +2928 -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/platforms.rb +9 -0
  245. data/lib/tigerbeetle/version.rb +2 -2
  246. data/tigerbeetle.gemspec +22 -5
  247. metadata +242 -3
  248. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,331 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+ const mem = std.mem;
4
+
5
+ const constants = @import("../../constants.zig");
6
+ const vsr = @import("../../vsr.zig");
7
+ const stdx = @import("stdx");
8
+ const maybe = stdx.maybe;
9
+
10
+ const message_pool = @import("../../message_pool.zig");
11
+ const MessagePool = message_pool.MessagePool;
12
+ const Message = MessagePool.Message;
13
+
14
+ const ReplicaSet = stdx.BitSetType(constants.members_max);
15
+ const Commits = std.ArrayList(struct {
16
+ header: vsr.Header.Prepare,
17
+ // null for operation=root and operation=upgrade
18
+ release: ?vsr.Release,
19
+ replicas: ReplicaSet = .{},
20
+ });
21
+
22
+ const ReplicaHead = struct {
23
+ view: u32,
24
+ op: u64,
25
+ };
26
+
27
+ pub fn StateCheckerType(comptime Client: type, comptime Replica: type) type {
28
+ return struct {
29
+ const StateChecker = @This();
30
+
31
+ node_count: u8,
32
+ replica_count: u8,
33
+
34
+ commits: Commits,
35
+ commit_mins: [constants.members_max]u64 = @splat(0),
36
+
37
+ replicas: []const Replica,
38
+ clients: []const ?Client,
39
+ /// Tracks the latest reply for every non-evicted client.
40
+ client_replies: std.AutoArrayHashMapUnmanaged(u128, vsr.Header.Reply),
41
+ clients_exhaustive: bool = true,
42
+ clients_register_op_latest: u64 = 0,
43
+
44
+ /// The number of times the canonical state has been advanced.
45
+ requests_committed: u64 = 0,
46
+
47
+ /// Tracks the latest op acked by a replica across restarts.
48
+ replica_head_max: []ReplicaHead,
49
+
50
+ pub fn init(allocator: mem.Allocator, options: struct {
51
+ cluster_id: u128,
52
+ replica_count: u8,
53
+ replicas: []const Replica,
54
+ clients: []const ?Client,
55
+ }) !StateChecker {
56
+ const root_prepare = vsr.Header.Prepare.root(options.cluster_id);
57
+
58
+ var commits = Commits.init(allocator);
59
+ errdefer commits.deinit();
60
+
61
+ var commit_replicas: ReplicaSet = .{};
62
+ for (options.replicas, 0..) |_, i| commit_replicas.set(i);
63
+ try commits.append(.{
64
+ .header = root_prepare,
65
+ .release = null,
66
+ .replicas = commit_replicas,
67
+ });
68
+
69
+ var client_replies: std.AutoArrayHashMapUnmanaged(u128, vsr.Header.Reply) = .{};
70
+ try client_replies.ensureTotalCapacity(allocator, constants.clients_max);
71
+ errdefer client_replies.deinit(allocator);
72
+
73
+ const replica_head_max = try allocator.alloc(ReplicaHead, options.replicas.len);
74
+ errdefer allocator.free(replica_head_max);
75
+ for (replica_head_max) |*head| head.* = .{ .view = 0, .op = 0 };
76
+
77
+ return StateChecker{
78
+ .node_count = @intCast(options.replicas.len),
79
+ .replica_count = options.replica_count,
80
+ .commits = commits,
81
+ .replicas = options.replicas,
82
+ .clients = options.clients,
83
+ .client_replies = client_replies,
84
+ .replica_head_max = replica_head_max,
85
+ };
86
+ }
87
+
88
+ pub fn deinit(state_checker: *StateChecker) void {
89
+ const allocator = state_checker.commits.allocator;
90
+
91
+ allocator.free(state_checker.replica_head_max);
92
+ state_checker.client_replies.deinit(allocator);
93
+ state_checker.commits.deinit();
94
+ }
95
+
96
+ pub fn on_client_eviction(state_checker: *StateChecker, client_id: u128) void {
97
+ const removed = state_checker.client_replies.swapRemove(client_id);
98
+ maybe(removed);
99
+ // Disable checking of `Client.request_inflight`, to guard against the following panic:
100
+ // 1. Client `A` sends an `operation=register` to a fresh cluster. (`A₁`)
101
+ // 2. Cluster prepares + commits `A₁`, and sends the reply to `A`.
102
+ // 4. `A` receives the reply to `A₁`, and issues a second request (`A₂`).
103
+ // 5. `clients_max` other clients register, evicting `A`'s session.
104
+ // 6. An old retry (or replay) of `A₁` arrives at the cluster.
105
+ // 7. `A₁` is committed (for a second time, as a different op).
106
+ // If `StateChecker` were to check `Client.request_inflight`, it would see that `A₁`
107
+ // is not actually in-flight, despite being committed for the "first time" by a
108
+ // replica.
109
+ state_checker.clients_exhaustive = false;
110
+ }
111
+
112
+ pub fn on_message(state_checker: *StateChecker, message: *const Message) void {
113
+ switch (message.header.into_any()) {
114
+ .prepare_ok => |header| {
115
+ const head = &state_checker.replica_head_max[header.replica];
116
+ if (header.view > head.view or
117
+ (header.view == head.view and header.op > head.op))
118
+ {
119
+ head.view = header.view;
120
+ head.op = header.op;
121
+ }
122
+ },
123
+ .reply => |header| {
124
+ if (header.operation == .register and
125
+ header.op > state_checker.clients_register_op_latest)
126
+ {
127
+ state_checker.client_replies
128
+ .putAssumeCapacityNoClobber(header.client, header.*);
129
+ state_checker.clients_register_op_latest = header.op;
130
+ } else {
131
+ if (state_checker.client_replies.getEntry(header.client)) |entry| {
132
+ if (entry.value_ptr.op < header.op) {
133
+ entry.value_ptr.* = header.*;
134
+ } else {
135
+ // An old message is replayed.
136
+ }
137
+ } else {
138
+ // Client was evicted, an old message is replayed.
139
+ }
140
+ }
141
+ },
142
+ else => {},
143
+ }
144
+ }
145
+
146
+ /// Verify that the cluster has advanced since the replica was lost.
147
+ /// Then forget about the given replica's progress, since its data file has been "lost".
148
+ pub fn reformat(state_checker: *StateChecker, replica_index: u8) void {
149
+ const reformat_state = state_checker.replica_head_max[replica_index];
150
+ var commit_advanced: bool = false;
151
+ for (
152
+ state_checker.commit_mins[0..state_checker.replica_head_max.len],
153
+ 0..,
154
+ ) |commit_min, i| {
155
+ if (i != replica_index) {
156
+ commit_advanced = commit_advanced or reformat_state.op < commit_min;
157
+ }
158
+ }
159
+ assert(commit_advanced);
160
+
161
+ state_checker.replica_head_max[replica_index] = .{ .view = 0, .op = 0 };
162
+ state_checker.commit_mins[replica_index] = 0;
163
+ }
164
+
165
+ /// Returns whether the replica's state changed since the last check_state().
166
+ pub fn check_state(state_checker: *StateChecker, replica_index: u8) !void {
167
+ const replica = &state_checker.replicas[replica_index];
168
+ if (replica.syncing == .updating_checkpoint) {
169
+ // Allow a syncing replica to fast-forward its commit.
170
+ //
171
+ // But "fast-forwarding" may actually move commit_min slightly backwards:
172
+ // 1. Suppose op X is a checkpoint trigger.
173
+ // 2. We are committing op X-1 but are stuck due to a block that does not exist in
174
+ // the cluster anymore.
175
+ // 3. When we sync, `commit_min` "backtracks", to `X - lsm_compaction_ops`.
176
+ const commit_min_source = state_checker.commit_mins[replica_index];
177
+ const commit_min_target =
178
+ replica.syncing.updating_checkpoint.header.op;
179
+ assert(commit_min_source <= commit_min_target + constants.lsm_compaction_ops);
180
+ state_checker.commit_mins[replica_index] = commit_min_target;
181
+ return;
182
+ }
183
+
184
+ assert(replica.view >= state_checker.replica_head_max[replica_index].view);
185
+
186
+ const commit_root_op = replica.superblock.working.vsr_state.checkpoint.header.op;
187
+ const commit_root = replica.superblock.working.vsr_state.checkpoint.header.checksum;
188
+
189
+ const commit_a = state_checker.commit_mins[replica_index];
190
+ const commit_b = replica.commit_min;
191
+
192
+ const header_b = replica.journal.header_with_op(replica.commit_min);
193
+
194
+ if (header_b == null and replica.commit_min != replica.op_checkpoint()) {
195
+ // The slot with commit_min may have been overwritten by an op from the next wrap.
196
+ // Further, the op may then also be truncated as part of a view change.
197
+ if (replica.journal.header_for_op(replica.commit_min)) |header| {
198
+ assert(header.op == replica.commit_min + constants.journal_slot_count);
199
+ }
200
+ return;
201
+ }
202
+
203
+ if (header_b != null) assert(header_b.?.op == commit_b);
204
+
205
+ const checksum_a = state_checker.commits.items[commit_a].header.checksum;
206
+ // Even if we have header_b, if its op is commit_root_op, we can't trust it.
207
+ // If we just finished state sync, the header in our log might not have been
208
+ // committed (it might be left over from before sync).
209
+ const checksum_b = if (commit_b == commit_root_op) commit_root else header_b.?.checksum;
210
+
211
+ assert(checksum_b != commit_root or
212
+ replica.commit_min == replica.superblock.working.vsr_state.checkpoint.header.op);
213
+ assert((commit_a == commit_b) == (checksum_a == checksum_b));
214
+
215
+ if (checksum_a == checksum_b) return;
216
+
217
+ assert(commit_b < commit_a or commit_a + 1 == commit_b);
218
+ state_checker.commit_mins[replica_index] = commit_b;
219
+
220
+ // If some other replica has already reached this state, then it will be in the commit
221
+ // history:
222
+ if (replica.commit_min < state_checker.commits.items.len) {
223
+ const commit = &state_checker.commits.items[commit_b];
224
+ if (replica.op_checkpoint() < replica.commit_min) {
225
+ if (commit.release) |release| assert(release.value == replica.release.value);
226
+ } else {
227
+ // When op_checkpoint==commit_min, we recovered from checkpoint, so it is ok if
228
+ // the release doesn't match. (commit_min is not actually being executed.)
229
+ assert(replica.op_checkpoint() == replica.commit_min);
230
+ }
231
+
232
+ assert(checksum_b == commit.header.checksum);
233
+ commit.replicas.set(replica_index);
234
+
235
+ assert(replica.commit_min < state_checker.commits.items.len);
236
+ // A replica may transition more than once to the same state, for example, when
237
+ // restarting after a crash and replaying the log. The more important invariant is
238
+ // that the cluster as a whole may not transition to the same state more than once,
239
+ // and once transitioned may not regress.
240
+ return;
241
+ }
242
+
243
+ if (header_b == null) return;
244
+ assert(header_b.?.checksum == checksum_b);
245
+ assert(header_b.?.parent == checksum_a);
246
+ assert(header_b.?.op > 0);
247
+ assert(header_b.?.command == .prepare);
248
+ assert(header_b.?.operation != .reserved);
249
+
250
+ if (header_b.?.client == 0) {
251
+ assert(header_b.?.operation == .upgrade or
252
+ header_b.?.operation == .pulse);
253
+ } else {
254
+ if (state_checker.clients_exhaustive) {
255
+ // The replica has transitioned to state `b` that is not yet in the commit
256
+ // history. Check if this is a valid new state based on the originating client's
257
+ // inflight request.
258
+ const client: *const Client = for (state_checker.clients) |*client| {
259
+ if (client.*.?.id == header_b.?.client) break &client.*.?;
260
+ } else unreachable;
261
+
262
+ if (client.request_inflight == null) {
263
+ return error.ReplicaTransitionedToInvalidState;
264
+ }
265
+
266
+ const request = client.request_inflight.?.message;
267
+ assert(request.header.client == header_b.?.client);
268
+ assert(request.header.checksum == header_b.?.request_checksum);
269
+ assert(request.header.request == header_b.?.request);
270
+ assert(request.header.command == .request);
271
+ assert(request.header.operation == header_b.?.operation);
272
+ assert(request.header.size == header_b.?.size);
273
+ // `checksum_body` will not match; the leader's StateMachine updated the
274
+ // timestamps in the prepare body's accounts/transfers.
275
+ } else {
276
+ // Either:
277
+ // - The cluster is running with one or more raw MessageBus "clients", so there
278
+ // may be requests not found in `Cluster.clients`.
279
+ // - The test includes one or more client evictions.
280
+ }
281
+ }
282
+
283
+ state_checker.requests_committed += 1;
284
+ assert(state_checker.requests_committed == header_b.?.op);
285
+
286
+ const release = release: {
287
+ if (header_b.?.operation == .root or
288
+ header_b.?.operation == .upgrade)
289
+ {
290
+ break :release null;
291
+ } else {
292
+ break :release replica.release;
293
+ }
294
+ };
295
+
296
+ assert(state_checker.commits.items.len == header_b.?.op);
297
+ state_checker.commits.append(.{
298
+ .header = header_b.?.*,
299
+ .release = release,
300
+ }) catch unreachable;
301
+ state_checker.commits.items[header_b.?.op].replicas.set(replica_index);
302
+ }
303
+
304
+ pub fn replica_convergence(state_checker: *StateChecker, replica_index: u8) bool {
305
+ const a = state_checker.commits.items.len - 1;
306
+ const b = state_checker.commit_mins[replica_index];
307
+ return a == b;
308
+ }
309
+
310
+ pub fn assert_cluster_convergence(state_checker: *StateChecker) void {
311
+ for (state_checker.commits.items, 0..) |commit, i| {
312
+ assert(commit.replicas.count() > 0);
313
+ assert(commit.header.command == .prepare);
314
+ assert(commit.header.op == i);
315
+ if (i > 0) {
316
+ const previous = state_checker.commits.items[i - 1].header;
317
+ assert(commit.header.parent == previous.checksum);
318
+ assert(commit.header.view >= previous.view);
319
+ }
320
+ }
321
+ }
322
+
323
+ pub fn header_with_op(state_checker: *StateChecker, op: u64) vsr.Header.Prepare {
324
+ assert(op < state_checker.commits.items.len);
325
+ const commit = &state_checker.commits.items[op];
326
+ assert(commit.header.op == op);
327
+ assert(commit.replicas.count() > 0);
328
+ return commit.header;
329
+ }
330
+ };
331
+ }