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,185 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+
4
+ const vsr = @import("../vsr.zig");
5
+ const constants = vsr.constants;
6
+ const MultiBatchDecoder = vsr.multi_batch.MultiBatchDecoder;
7
+ const MultiBatchEncoder = vsr.multi_batch.MultiBatchEncoder;
8
+ const stdx = @import("stdx");
9
+ const MiB = stdx.MiB;
10
+ const fuzz = @import("../testing/fuzz.zig");
11
+
12
+ pub fn main(gpa: std.mem.Allocator, args: fuzz.FuzzArgs) !void {
13
+ var prng = stdx.PRNG.from_seed(args.seed);
14
+ const message_body_size_min = constants.sector_size - @sizeOf(vsr.Header);
15
+ const message_body_size_max = (1 * MiB) - @sizeOf(vsr.Header);
16
+ const buffer_expected = try gpa.alignedAlloc(
17
+ u8,
18
+ @alignOf(vsr.Header),
19
+ message_body_size_max,
20
+ );
21
+ defer gpa.free(buffer_expected);
22
+
23
+ const buffer_actual = try gpa.alignedAlloc(
24
+ u8,
25
+ @alignOf(vsr.Header),
26
+ message_body_size_max,
27
+ );
28
+ defer gpa.free(buffer_actual);
29
+
30
+ const events_max = args.events_max orelse 1024;
31
+ for (0..events_max) |_| {
32
+ const buffer_size: usize = prng.range_inclusive(
33
+ usize,
34
+ message_body_size_min,
35
+ message_body_size_max,
36
+ );
37
+ try run_fuzz(.{
38
+ .prng = &prng,
39
+ .buffer_expected = buffer_expected[0..buffer_size],
40
+ .buffer_actual = buffer_actual[0..buffer_size],
41
+ });
42
+ }
43
+ }
44
+
45
+ fn run_fuzz(options: struct {
46
+ prng: *stdx.PRNG,
47
+ buffer_expected: []u8,
48
+ buffer_actual: []u8,
49
+ }) !void {
50
+ assert(options.buffer_expected.len == options.buffer_actual.len);
51
+ // The end of the buffer must be aligned with the postamble.
52
+ const postamble_alignment = options.buffer_expected.len % @sizeOf(u16);
53
+ assert(postamble_alignment < @sizeOf(u16));
54
+ const buffer_expected: []u8 =
55
+ options.buffer_expected[0 .. options.buffer_expected.len - postamble_alignment];
56
+ const buffer_actual: []u8 =
57
+ options.buffer_actual[0 .. options.buffer_actual.len - postamble_alignment];
58
+
59
+ // Generate the batch plan with element sizes from 2^0 to 2^8.
60
+ const batch_element_size: u32 = std.math.pow(u32, 2, options.prng.int_inclusive(u32, 8));
61
+ var batches = stdx.BoundedArrayType(u32, 8190){};
62
+ const batch_count_max = options.prng.range_inclusive(
63
+ usize,
64
+ 1,
65
+ batches.capacity(),
66
+ );
67
+
68
+ // Encoder will ignore and overwrite any existing content in the target buffer,
69
+ // at the end, both buffers must be equal.
70
+ options.prng.fill(buffer_expected);
71
+ options.prng.fill(buffer_actual);
72
+
73
+ // Encode.
74
+ var encoder = MultiBatchEncoder.init(buffer_actual, .{
75
+ .element_size = batch_element_size,
76
+ });
77
+ var expect_payload_size: u32 = 0;
78
+ var expect_trailer_size: u32 = 0;
79
+ for (0..batch_count_max) |_| {
80
+ assert(expect_payload_size + expect_trailer_size <= buffer_expected.len);
81
+
82
+ const trailer_size_next = vsr.multi_batch.trailer_total_size(.{
83
+ .batch_count = @intCast(batches.count() + 1),
84
+ .element_size = batch_element_size,
85
+ });
86
+ const expect_padding: u32 = expect_payload_size % @sizeOf(u16);
87
+ assert(expect_padding < @sizeOf(u16));
88
+ if (buffer_expected.len < expect_payload_size + expect_padding + trailer_size_next) {
89
+ assert(batches.count() > 0);
90
+ assert(encoder.writable() == null);
91
+ break;
92
+ }
93
+
94
+ const batch_element_count_max: u32 = @intCast(@divFloor(
95
+ buffer_expected.len -
96
+ (expect_payload_size + expect_padding + trailer_size_next),
97
+ batch_element_size,
98
+ ));
99
+ const batch_element_count: u32 = if (batch_element_count_max > 0)
100
+ switch (options.prng.enum_uniform(enum { zero, one, random })) {
101
+ .zero => 0,
102
+ .one => 1,
103
+ .random => options.prng.range_inclusive(u32, 1, @min(
104
+ std.math.maxInt(u16), // Cannot encode more than `u16` elements.
105
+ batch_element_count_max,
106
+ )),
107
+ }
108
+ else
109
+ 0;
110
+ const batch_size: u32 = batch_element_count * batch_element_size;
111
+ const writable: []u8 = encoder.writable().?;
112
+ assert(batch_size <= writable.len);
113
+
114
+ const source: []u8 = buffer_expected[expect_payload_size..][0..batch_size];
115
+ const target: []u8 = writable[0..batch_size];
116
+ stdx.copy_disjoint(.exact, u8, target, source);
117
+
118
+ encoder.add(batch_size);
119
+ expect_payload_size += batch_size;
120
+ batches.push(batch_element_count);
121
+ expect_trailer_size = trailer_size_next;
122
+ }
123
+ assert(batches.count() > 0);
124
+ assert(batches.count() == encoder.batch_count);
125
+ assert(expect_payload_size + expect_trailer_size <= buffer_expected.len);
126
+ assert(expect_payload_size == encoder.buffer_index);
127
+
128
+ const expect_padding: u32 = expect_payload_size % @sizeOf(u16);
129
+ assert(expect_padding < @sizeOf(u16));
130
+
131
+ const encoder_bytes_written = encoder.finish();
132
+ assert(encoder_bytes_written > 0);
133
+ assert(encoder_bytes_written ==
134
+ expect_payload_size + expect_padding + expect_trailer_size);
135
+
136
+ {
137
+ // Decode.
138
+ var decoder = try MultiBatchDecoder.init(
139
+ buffer_actual[0..encoder_bytes_written],
140
+ .{ .element_size = batch_element_size },
141
+ );
142
+ assert(expect_payload_size == decoder.payload.len);
143
+
144
+ var payloads_decoded: u32 = 0;
145
+ for (batches.const_slice()) |batch_element_count| {
146
+ const batch_size: u32 = batch_element_count * batch_element_size;
147
+ const expect_batch: []u8 = buffer_expected[payloads_decoded..][0..batch_size];
148
+ payloads_decoded += batch_size;
149
+
150
+ const decoded_batch = decoder.pop().?;
151
+ assert(batch_size == decoded_batch.len);
152
+ assert(std.mem.eql(u8, expect_batch, decoded_batch));
153
+ }
154
+ assert(payloads_decoded == decoder.payload.len);
155
+ assert(decoder.pop() == null);
156
+ }
157
+
158
+ // Verify that any flipped bit mutates the results or causes a decoding error
159
+ // (but never a panic).
160
+ for (0..32) |_| {
161
+ const byte_index = options.prng.int_inclusive(usize, encoder_bytes_written - 1);
162
+ for (0..@bitSizeOf(u8)) |bit_index| {
163
+ buffer_actual[byte_index] ^= @as(u8, 1) << @as(u3, @intCast(bit_index));
164
+ defer buffer_actual[byte_index] ^= @as(u8, 1) << @as(u3, @intCast(bit_index));
165
+
166
+ var decoder = MultiBatchDecoder.init(
167
+ buffer_actual[0..encoder_bytes_written],
168
+ .{ .element_size = batch_element_size },
169
+ ) catch continue;
170
+
171
+ var same: bool = true;
172
+ var payloads_decoded: u32 = 0;
173
+ for (batches.const_slice()) |batch_element_count| {
174
+ const batch_size: u32 = batch_element_count * batch_element_size;
175
+ const expect_batch: []u8 = buffer_expected[payloads_decoded..][0..batch_size];
176
+ payloads_decoded += batch_size;
177
+
178
+ const decoded_batch = decoder.pop().?;
179
+ same = same and std.mem.eql(u8, expect_batch, decoded_batch);
180
+ }
181
+ assert(decoder.pop() == null);
182
+ assert(!same);
183
+ }
184
+ }
185
+ }
@@ -0,0 +1,333 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+
4
+ const stdx = @import("stdx");
5
+ const ratio = stdx.PRNG.ratio;
6
+ const Ratio = stdx.PRNG.Ratio;
7
+
8
+ pub const RepairBudgetJournal = struct {
9
+ capacity: u32,
10
+ available: u32,
11
+
12
+ replica_index: u8,
13
+
14
+ // Tracks the prepare ops requested from each remote replica.
15
+ replicas_requested_prepares: []RequestedPrepares,
16
+
17
+ // Exponential weighted moving average of the repair latency for each remote replica.
18
+ //
19
+ // Repair latency is calculated as the duration elapsed between when a prepare is requested from
20
+ // a remote replica, and when it is either received from the remote replica (see `decrement`),
21
+ // or expired (see `maybe_expire_requested_prepares`).
22
+ replicas_repair_latency: []stdx.Duration,
23
+
24
+ // Probability of choosing a random replica with available budget, as opposed to one with the
25
+ // best repair latency with available budget.
26
+ //
27
+ // Experiments ensure that we try alternative repair routes, and avoids potential resonance
28
+ // wherein we keep requesting from a permanently crashed replica with the best repair latency.
29
+ // This is because we don't penalize the repair latency once it exceeds `duration_expiry_max`,
30
+ // so if a crashed replica has the best latency, it may remain that way forever.
31
+ experiment_chance: Ratio = ratio(1, 10),
32
+
33
+ // Multiple of repair latency used to determine expiry duration, which is the time we wait
34
+ // before reclaiming the budget for an inflight repair request.
35
+ repair_latency_multiple_expiry: u8 = 2,
36
+
37
+ // The maximum amount of time we wait before reclaiming the budget for an inflight repair
38
+ // request.
39
+ //
40
+ // Capped at 500ms to avoid an unbounded increase in the tracked repair latency for remote
41
+ // replicas. Specifically, helps avoid the case where a partitioned replica with missing
42
+ // prepares gets into a cycle of requesting prepares, waiting for them to expire, and then
43
+ // increasing the repair latency on expiry.
44
+ duration_expiry_max: stdx.Duration = .ms(500),
45
+
46
+ // Maximum inflight `request_prepare` messages per remote replica, at any point of time.
47
+ //
48
+ // This is kept small to ensure that even if the budget to a remote replica is saturated
49
+ // by multiple replicas, overflowing the egress `send_queue` (which leads to dropped messages)
50
+ // on the remote replica is unlikely. For example, since the `send_queue` is currently sized
51
+ // to 4 messages, if we were to set this limit to 4 as well, multiple repairing replicas are
52
+ // more likely to overflow the remote replica's send queue.
53
+ const repair_messages_inflight_count_max = 2;
54
+
55
+ const RequestedPrepares = std.AutoArrayHashMapUnmanaged(u64, stdx.Instant);
56
+
57
+ pub fn init(gpa: std.mem.Allocator, options: struct {
58
+ replica_index: u8,
59
+ replica_count: u8,
60
+ }) !RepairBudgetJournal {
61
+ const remote_replica_count = if (options.replica_index < options.replica_count)
62
+ // Replicas can repair from all replicas but themselves.
63
+ options.replica_count - 1
64
+ else
65
+ // Standbys can repair from all replicas.
66
+ options.replica_count;
67
+
68
+ var replicas_requested_prepares = try gpa.alloc(RequestedPrepares, options.replica_count);
69
+ errdefer gpa.free(replicas_requested_prepares);
70
+
71
+ for (replicas_requested_prepares, 0..) |*requested_prepares, replica| {
72
+ errdefer for (replicas_requested_prepares[0..replica]) |*m| m.deinit(gpa);
73
+ requested_prepares.* = .{};
74
+
75
+ try requested_prepares.ensureTotalCapacity(gpa, repair_messages_inflight_count_max);
76
+ errdefer requested_prepares.deinit(gpa);
77
+ }
78
+
79
+ errdefer for (replicas_requested_prepares) |*m| m.deinit(gpa);
80
+
81
+ const replicas_repair_latency = try gpa.alloc(stdx.Duration, options.replica_count);
82
+ errdefer gpa.free(replicas_repair_latency);
83
+
84
+ // Initialize repair latency to 1 ms for all replicas, this gets refined as we start
85
+ // repairing from these replicas. We choose a value lower than the the typical latency
86
+ // between two replicas, so as to not bias replica selection when we have few measurements.
87
+ @memset(replicas_repair_latency, .ms(1));
88
+
89
+ return RepairBudgetJournal{
90
+ .capacity = repair_messages_inflight_count_max * remote_replica_count,
91
+ .available = repair_messages_inflight_count_max * remote_replica_count,
92
+ .replica_index = options.replica_index,
93
+ .replicas_requested_prepares = replicas_requested_prepares,
94
+ .replicas_repair_latency = replicas_repair_latency,
95
+ };
96
+ }
97
+
98
+ pub fn deinit(budget: *RepairBudgetJournal, gpa: std.mem.Allocator) void {
99
+ for (budget.replicas_requested_prepares) |*requested_prepares| {
100
+ requested_prepares.deinit(gpa);
101
+ }
102
+ gpa.free(budget.replicas_requested_prepares);
103
+ gpa.free(budget.replicas_repair_latency);
104
+ }
105
+
106
+ /// Returns the index of the replica with the lowest repair latency, and budget availability, if
107
+ /// one exists. Otherwise, returns null. For a fraction of ops (guided by `experiment_chance`),
108
+ /// diverges from this heuristic and returns the index of a random replica with budget
109
+ /// availability, using reservoir sampling.
110
+ pub fn decrement(budget: *RepairBudgetJournal, options: struct {
111
+ op: u64,
112
+ now: stdx.Instant,
113
+ prng: *stdx.PRNG,
114
+ }) ?u8 {
115
+ assert(budget.capacity > 0);
116
+ assert(budget.available > 0);
117
+
118
+ budget.assert_invariants();
119
+ defer budget.assert_invariants();
120
+
121
+ const experiment = options.prng.chance(budget.experiment_chance);
122
+ var experiment_replica_index: ?u8 = null;
123
+ var reservoir = stdx.PRNG.Reservoir.init();
124
+
125
+ var repair_latency_min: ?stdx.Duration = null;
126
+ var repair_latency_min_replica_index: ?u8 = null;
127
+
128
+ for (budget.replicas_requested_prepares, 0..) |*requested_prepares, replica_index| {
129
+ // Disallow requesting from a replica from which this op already been requested.
130
+ if (requested_prepares.get(options.op) != null) continue;
131
+ // Disallow requests to self.
132
+ if (replica_index == budget.replica_index) continue;
133
+ // Enforce per-replica budget.
134
+ if (requested_prepares.count() == repair_messages_inflight_count_max) continue;
135
+
136
+ const replica_repair_latency = budget.replicas_repair_latency[replica_index];
137
+
138
+ if (repair_latency_min == null or replica_repair_latency.ns < repair_latency_min.?.ns) {
139
+ repair_latency_min = replica_repair_latency;
140
+ repair_latency_min_replica_index = @intCast(replica_index);
141
+ }
142
+
143
+ // Reservoir sampling with an arbitrarily chosen weight of 1 for each item suffices
144
+ // our use case, as the goal is to get some degree of randomness during experiments.
145
+ if (reservoir.replace(options.prng, 1)) {
146
+ experiment_replica_index = @intCast(replica_index);
147
+ }
148
+ }
149
+ assert((repair_latency_min == null) == (repair_latency_min_replica_index == null));
150
+ assert((repair_latency_min_replica_index == null) == (experiment_replica_index == null));
151
+
152
+ const replica_index_maybe = if (experiment)
153
+ experiment_replica_index
154
+ else
155
+ repair_latency_min_replica_index;
156
+
157
+ if (replica_index_maybe) |replica_index| {
158
+ assert(replica_index != budget.replica_index);
159
+ budget.replicas_requested_prepares[replica_index].putAssumeCapacityNoClobber(
160
+ options.op,
161
+ options.now,
162
+ );
163
+
164
+ budget.available -= 1;
165
+ }
166
+
167
+ return replica_index_maybe;
168
+ }
169
+
170
+ /// Increments the budget by 1 for each replica that this prepare op has been requested from.
171
+ /// Also refines the repair latency for each of these replicas.
172
+ pub fn increment(budget: *RepairBudgetJournal, options: struct {
173
+ op: u64,
174
+ now: stdx.Instant,
175
+ }) void {
176
+ budget.assert_invariants();
177
+ defer budget.assert_invariants();
178
+
179
+ for (budget.replicas_requested_prepares, 0..) |*requested_prepares, replica_index| {
180
+ if (requested_prepares.fetchSwapRemove(options.op)) |requested_prepare| {
181
+ budget.available += 1;
182
+
183
+ // We have no information about the replica that sent this prepare, as the message
184
+ // header stores the index of the primary processed that prepare. Consequently, we
185
+ // refine repair latency for all replicas that this prepare op was requested from.
186
+ // This would lead to some inaccuracy in the latency measurement, but is acceptable
187
+ // since the scenario where a prepare has been requested from multiple replicas is
188
+ // rare in practice. The more common scenario is that we have a large number of
189
+ // prepares missing (for e.g. after state sync, or if a lagging replica transitions
190
+ // to a new checkpoint), in which case we request a unique op from each replica.
191
+ budget.replicas_repair_latency[replica_index] = ewma_add_duration(
192
+ budget.replicas_repair_latency[replica_index],
193
+ options.now.duration_since(requested_prepare.value),
194
+ );
195
+ }
196
+ }
197
+ }
198
+
199
+ pub fn refill(budget: *RepairBudgetJournal) void {
200
+ budget.assert_invariants();
201
+ defer budget.assert_invariants();
202
+
203
+ for (budget.replicas_requested_prepares) |*requested_prepares| {
204
+ requested_prepares.clearRetainingCapacity();
205
+ }
206
+ budget.available = budget.capacity;
207
+ }
208
+
209
+ /// Iterates through the inflight requests across all remote replicas, and reclaims the budget
210
+ /// for expired requests. Penalizes the replicas for which some expired requests were found,
211
+ /// duration spent waiting for the expired requests to their repair latency.
212
+ ///
213
+ /// Expiry provides resilience to network faults, by ensuring that a dropped packet or the
214
+ /// remote replica crashing doesn't cause an op to get stuck in the queue for a remote replica.
215
+ /// We avoid spurious expiry due to transient network hiccups like increased latency by waiting
216
+ /// for twice the measured repair latency.
217
+ pub fn maybe_expire_requested_prepares(budget: *RepairBudgetJournal, now: stdx.Instant) void {
218
+ budget.assert_invariants();
219
+ defer budget.assert_invariants();
220
+
221
+ for (budget.replicas_requested_prepares, 0..) |*requested_prepares, replica_index| {
222
+ var requested_prepares_index: u32 = 0;
223
+
224
+ while (requested_prepares_index < requested_prepares.entries.len) {
225
+ const requested_at = requested_prepares.values()[requested_prepares_index];
226
+ const duration_since_requested_at = now.duration_since(requested_at);
227
+ const duration_expiry_ns = @min(
228
+ budget.repair_latency_multiple_expiry *
229
+ budget.replicas_repair_latency[replica_index].ns,
230
+ budget.duration_expiry_max.ns,
231
+ );
232
+ if (duration_since_requested_at.ns > duration_expiry_ns) {
233
+ requested_prepares.swapRemoveAt(requested_prepares_index);
234
+ budget.replicas_repair_latency[replica_index] = ewma_add_duration(
235
+ budget.replicas_repair_latency[replica_index],
236
+ duration_since_requested_at,
237
+ );
238
+ budget.available += 1;
239
+ } else {
240
+ requested_prepares_index += 1;
241
+ }
242
+ }
243
+ }
244
+ }
245
+
246
+ fn assert_invariants(budget: *const RepairBudgetJournal) void {
247
+ assert(budget.available <= budget.capacity);
248
+ if (budget.replica_index < budget.replicas_requested_prepares.len) {
249
+ assert(budget.replicas_requested_prepares[budget.replica_index].count() == 0);
250
+ }
251
+
252
+ var requested_prepares_count: u32 = 0;
253
+ for (budget.replicas_requested_prepares) |*requested_prepares| {
254
+ requested_prepares_count += @intCast(requested_prepares.count());
255
+ }
256
+ assert(budget.capacity - budget.available == requested_prepares_count);
257
+ }
258
+
259
+ fn ewma_add_duration(old: stdx.Duration, new: stdx.Duration) stdx.Duration {
260
+ return .{
261
+ .ns = @divFloor((old.ns * 4) + new.ns, 5),
262
+ };
263
+ }
264
+ };
265
+
266
+ pub const RepairBudgetGrid = struct {
267
+ capacity: u32,
268
+ available: u32,
269
+ refill_max: u32,
270
+ requested: std.AutoArrayHashMapUnmanaged(BlockIdentifier, void),
271
+
272
+ const BlockIdentifier = struct { address: u64, checksum: u128 };
273
+
274
+ pub fn init(gpa: std.mem.Allocator, options: struct {
275
+ capacity: u32,
276
+ refill_max: u32,
277
+ }) !RepairBudgetGrid {
278
+ assert(options.refill_max <= options.capacity);
279
+
280
+ var requested: std.AutoArrayHashMapUnmanaged(BlockIdentifier, void) = .{};
281
+ try requested.ensureTotalCapacity(gpa, options.capacity);
282
+ errdefer requested.deinit(gpa);
283
+
284
+ return RepairBudgetGrid{
285
+ .capacity = options.capacity,
286
+ .available = options.capacity,
287
+ .refill_max = options.refill_max,
288
+ .requested = requested,
289
+ };
290
+ }
291
+
292
+ pub fn deinit(budget: *RepairBudgetGrid, gpa: std.mem.Allocator) void {
293
+ budget.requested.deinit(gpa);
294
+ }
295
+
296
+ fn assert_invariants(budget: *RepairBudgetGrid) void {
297
+ assert(budget.available <= budget.capacity);
298
+ assert(budget.available + budget.requested.count() <= budget.capacity);
299
+ }
300
+
301
+ pub fn decrement(budget: *RepairBudgetGrid, block_identifier: BlockIdentifier) bool {
302
+ budget.assert_invariants();
303
+ defer budget.assert_invariants();
304
+
305
+ assert(budget.available > 0);
306
+ assert(block_identifier.address > 0);
307
+
308
+ const gop = budget.requested.getOrPutAssumeCapacity(block_identifier);
309
+ if (gop.found_existing) {
310
+ return false;
311
+ } else {
312
+ budget.available -= 1;
313
+ return true;
314
+ }
315
+ }
316
+
317
+ pub fn increment(budget: *RepairBudgetGrid, block_identifier: BlockIdentifier) void {
318
+ budget.assert_invariants();
319
+ defer budget.assert_invariants();
320
+
321
+ if (budget.requested.swapRemove(block_identifier)) {
322
+ budget.available = @min((budget.available + 1), budget.capacity);
323
+ }
324
+ }
325
+
326
+ pub fn refill(budget: *RepairBudgetGrid) void {
327
+ budget.assert_invariants();
328
+ defer budget.assert_invariants();
329
+
330
+ budget.available = @min((budget.available + budget.refill_max), budget.capacity);
331
+ budget.requested.clearRetainingCapacity();
332
+ }
333
+ };