tigerbeetle 0.0.36 → 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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/ext/tb_client/extconf.rb +13 -13
  3. data/ext/tb_client/tigerbeetle/LICENSE +177 -0
  4. data/ext/tb_client/tigerbeetle/build.zig +2327 -0
  5. data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
  6. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
  7. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
  8. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
  9. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
  10. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
  11. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
  12. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
  13. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
  14. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
  15. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
  16. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
  17. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
  18. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
  19. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
  20. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
  21. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
  22. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
  23. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
  24. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
  25. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
  26. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
  27. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
  28. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
  29. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
  30. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
  31. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
  32. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
  33. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
  34. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
  35. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
  36. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
  37. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
  38. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
  39. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
  40. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
  41. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
  42. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
  43. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
  44. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
  45. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
  46. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
  47. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
  48. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
  49. data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
  50. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
  51. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
  52. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
  53. data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
  54. data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
  55. data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
  56. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
  57. data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
  58. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
  59. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
  60. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
  61. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
  62. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
  63. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
  64. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
  65. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
  66. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
  67. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
  68. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
  69. data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
  70. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
  71. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
  72. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
  73. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
  74. data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
  75. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
  76. data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
  77. data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
  78. data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
  79. data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
  80. data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
  81. data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
  82. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
  83. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
  84. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
  85. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
  86. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
  87. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
  88. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
  89. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
  90. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
  91. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
  92. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
  93. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
  94. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
  95. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
  96. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
  97. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
  98. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
  99. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
  100. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
  101. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
  102. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
  103. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
  104. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
  105. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
  106. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
  107. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
  108. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
  109. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
  110. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
  111. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
  112. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
  113. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
  114. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
  115. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
  116. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
  117. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
  118. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
  119. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
  120. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
  121. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
  122. data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
  123. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
  124. data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
  125. data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
  126. data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
  127. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
  128. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
  129. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
  130. data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
  131. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
  132. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
  133. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
  134. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
  135. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
  136. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
  137. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
  138. data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
  139. data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
  140. data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
  141. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
  142. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
  143. data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
  144. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
  145. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
  146. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
  147. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
  148. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
  149. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
  150. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
  151. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
  152. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
  153. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
  154. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
  155. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
  156. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
  157. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
  158. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
  159. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
  160. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
  161. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
  162. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
  163. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
  164. data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
  165. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
  166. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
  167. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
  168. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
  169. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
  170. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
  171. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
  172. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
  173. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
  174. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
  175. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
  176. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
  177. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
  178. data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
  179. data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
  180. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
  181. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
  182. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
  183. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
  184. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
  185. data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
  186. data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
  187. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
  188. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
  189. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
  190. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
  191. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
  192. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
  193. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
  194. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
  195. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
  196. data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
  197. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
  198. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
  199. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
  200. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
  201. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
  202. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
  203. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
  204. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
  205. data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
  206. data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
  207. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
  208. data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
  209. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
  210. data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
  211. data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
  212. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
  213. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
  214. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
  215. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
  216. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
  217. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
  218. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
  219. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
  220. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
  221. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
  222. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
  223. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
  224. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
  225. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
  226. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
  227. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
  228. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
  229. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
  230. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
  231. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
  232. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
  233. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
  234. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
  235. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
  236. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
  237. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
  238. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
  239. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
  240. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
  241. data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
  242. data/lib/tb_client/shared_lib.rb +12 -5
  243. data/lib/tigerbeetle/platforms.rb +9 -0
  244. data/lib/tigerbeetle/version.rb +1 -1
  245. data/tigerbeetle.gemspec +22 -5
  246. metadata +242 -3
  247. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,1422 @@
1
+ //! Parse and validate command-line arguments for the tigerbeetle binary.
2
+ //!
3
+ //! Everything that can be validated without reading the data file must be validated here.
4
+ //! Caller must additionally assert validity of arguments as a defense in depth.
5
+ //!
6
+ //! Some flags are experimental: intentionally undocumented and are not a part of the official
7
+ //! surface area. Even experimental features must adhere to the same strict standard of safety,
8
+ //! but they come without any performance or usability guarantees.
9
+ //!
10
+ //! Experimental features are not gated by comptime option for safety: it is much easier to review
11
+ //! code for correctness when it is initially added to the main branch, rather when a comptime flag
12
+ //! is lifted.
13
+
14
+ const std = @import("std");
15
+ const assert = std.debug.assert;
16
+ const fmt = std.fmt;
17
+ const net = std.net;
18
+
19
+ const vsr = @import("vsr");
20
+ const stdx = vsr.stdx;
21
+ const constants = vsr.constants;
22
+ const tigerbeetle = vsr.tigerbeetle;
23
+ const data_file_size_min = vsr.superblock.data_file_size_min;
24
+ const StateMachine = @import("./main.zig").StateMachine;
25
+ const Grid = @import("./main.zig").Grid;
26
+ const Ratio = stdx.PRNG.Ratio;
27
+ const ByteSize = stdx.ByteSize;
28
+ const Operation = tigerbeetle.Operation;
29
+ const Duration = stdx.Duration;
30
+
31
+ comptime {
32
+ // Make sure we are running the Accounting StateMachine.
33
+ assert(StateMachine.Operation == tigerbeetle.Operation);
34
+ }
35
+
36
+ const KiB = stdx.KiB;
37
+ const GiB = stdx.GiB;
38
+
39
+ const CLIArgs = union(enum) {
40
+ const Format = struct {
41
+ cluster: ?u128 = null,
42
+ replica: ?u8 = null,
43
+ // Experimental: standbys don't have a concrete practical use-case yet.
44
+ standby: ?u8 = null,
45
+ replica_count: u8,
46
+ development: bool = false,
47
+ log_debug: bool = false,
48
+
49
+ @"--": void,
50
+ path: []const u8,
51
+ };
52
+
53
+ const Recover = struct {
54
+ cluster: u128,
55
+ addresses: []const u8,
56
+ replica: u8,
57
+ replica_count: u8,
58
+ development: bool = false,
59
+ log_debug: bool = false,
60
+
61
+ @"--": void,
62
+ path: []const u8,
63
+ };
64
+
65
+ const Start = struct {
66
+ // Stable CLI arguments.
67
+ addresses: []const u8,
68
+ cache_grid: ?ByteSize = null,
69
+ development: bool = false,
70
+
71
+ // Everything from here until positional arguments is considered experimental, and requires
72
+ // `--experimental` to be set. Experimental flags disable automatic upgrades with
73
+ // multiversion binaries; each replica has to be manually restarted. Experimental flags must
74
+ // default to null, except for bools which must be false.
75
+ experimental: bool = false,
76
+
77
+ limit_storage: ?ByteSize = null,
78
+ limit_pipeline_requests: ?u32 = null,
79
+ limit_request: ?ByteSize = null,
80
+ cache_accounts: ?ByteSize = null,
81
+ cache_transfers: ?ByteSize = null,
82
+ cache_transfers_pending: ?ByteSize = null,
83
+ memory_lsm_manifest: ?ByteSize = null,
84
+ memory_lsm_compaction: ?ByteSize = null,
85
+ trace: ?[]const u8 = null,
86
+ log_debug: bool = false,
87
+ log_trace: bool = false,
88
+ timeout_prepare_ms: ?u64 = null,
89
+ timeout_grid_repair_message_ms: ?u64 = null,
90
+ commit_stall_probability: ?Ratio = null,
91
+
92
+ // Highly experimental options that will be removed in a future release:
93
+ replicate_star: bool = false,
94
+
95
+ statsd: ?[]const u8 = null,
96
+
97
+ /// AOF (Append Only File) logs all transactions synchronously to disk before replying
98
+ /// to the client. The logic behind this code has been kept as simple as possible -
99
+ /// io_uring or kqueue aren't used, there aren't any fancy data structures. Just a simple
100
+ /// log consisting of logged requests. Much like a redis AOF with fsync=on.
101
+ /// Enabling this will have performance implications.
102
+ aof_file: ?[]const u8 = null,
103
+
104
+ /// Legacy AOF option. Mutually exclusive with aof_file, and will have the same effect as
105
+ /// setting aof_file to '<data file path>.aof'.
106
+ aof: bool = false,
107
+
108
+ /// AOF recovery mode: accept timestamps passed by the client.
109
+ /// Only enable this when recovering cluster from AOF.
110
+ aof_recovery: bool = false,
111
+
112
+ @"--": void,
113
+ path: []const u8,
114
+ };
115
+
116
+ const Version = struct {
117
+ verbose: bool = false,
118
+ };
119
+
120
+ const Repl = struct {
121
+ addresses: []const u8,
122
+ cluster: u128,
123
+ verbose: bool = false,
124
+ command: []const u8 = "",
125
+ log_debug: bool = false,
126
+ };
127
+
128
+ // Experimental: the interface is subject to change.
129
+ const Benchmark = struct {
130
+ cache_accounts: ?[]const u8 = null,
131
+ cache_transfers: ?[]const u8 = null,
132
+ cache_transfers_pending: ?[]const u8 = null,
133
+ cache_grid: ?[]const u8 = null,
134
+ account_count: u64 = 10_000,
135
+ account_count_hot: u32 = 0,
136
+ log_debug: bool = false,
137
+ log_debug_replica: bool = false,
138
+ /// The probability distribution used to select accounts when making transfers or queries.
139
+ account_distribution: Command.Benchmark.Distribution = .uniform,
140
+ no_history: bool = false,
141
+ imported: bool = false,
142
+ account_batch_count: u32 = Operation.create_accounts.event_max(
143
+ constants.message_body_size_max,
144
+ ),
145
+ transfer_count: u64 = 10_000_000,
146
+ transfer_hot_percent: u32 = 100,
147
+ transfer_pending: bool = false,
148
+ transfer_batch_count: u32 = Operation.create_transfers.event_max(
149
+ constants.message_body_size_max,
150
+ ),
151
+ transfer_batch_delay: Duration = .ms(0),
152
+ validate: bool = false,
153
+ checksum_performance: bool = false,
154
+ query_count: u32 = 100,
155
+ print_batch_timings: bool = false,
156
+ id_order: Command.Benchmark.IdOrder = .sequential,
157
+ clients: u32 = 1,
158
+ statsd: ?[]const u8 = null,
159
+ trace: ?[]const u8 = null,
160
+ /// When set, don't delete the data file when the benchmark completes.
161
+ file: ?[]const u8 = null,
162
+ addresses: ?[]const u8 = null,
163
+ seed: ?[]const u8 = null,
164
+ };
165
+
166
+ // Experimental: the interface is subject to change.
167
+ const Inspect = union(enum) {
168
+ constants,
169
+ metrics,
170
+ op: struct {
171
+ @"--": void,
172
+ op: u64,
173
+ },
174
+ superblock: struct {
175
+ @"--": void,
176
+ path: []const u8,
177
+ },
178
+ wal: struct {
179
+ slot: ?usize = null,
180
+
181
+ @"--": void,
182
+ path: []const u8,
183
+ },
184
+ replies: struct {
185
+ slot: ?usize = null,
186
+ superblock_copy: ?u8 = null,
187
+
188
+ @"--": void,
189
+ path: []const u8,
190
+ },
191
+ grid: struct {
192
+ block: ?u64 = null,
193
+ superblock_copy: ?u8 = null,
194
+
195
+ @"--": void,
196
+ path: []const u8,
197
+ },
198
+ manifest: struct {
199
+ superblock_copy: ?u8 = null,
200
+
201
+ @"--": void,
202
+ path: []const u8,
203
+ },
204
+ tables: struct {
205
+ superblock_copy: ?u8 = null,
206
+ tree: []const u8,
207
+ level: ?u6 = null,
208
+
209
+ @"--": void,
210
+ path: []const u8,
211
+ },
212
+ integrity: struct {
213
+ log_debug: bool = false,
214
+ seed: ?[]const u8 = null,
215
+ memory_lsm_manifest: ?ByteSize = null,
216
+ skip_wal: bool = false,
217
+ skip_client_replies: bool = false,
218
+ skip_grid: bool = false,
219
+
220
+ @"--": void,
221
+ path: [:0]const u8,
222
+ },
223
+
224
+ pub const help =
225
+ \\Usage:
226
+ \\
227
+ \\ tigerbeetle inspect [-h | --help]
228
+ \\
229
+ \\ tigerbeetle inspect constants
230
+ \\
231
+ \\ tigerbeetle inspect metrics
232
+ \\
233
+ \\ tigerbeetle inspect op <op>
234
+ \\
235
+ \\ tigerbeetle inspect superblock <path>
236
+ \\
237
+ \\ tigerbeetle inspect wal [--slot=<slot>] <path>
238
+ \\
239
+ \\ tigerbeetle inspect replies [--slot=<slot>] <path>
240
+ \\
241
+ \\ tigerbeetle inspect grid [--block=<address>] <path>
242
+ \\
243
+ \\ tigerbeetle inspect manifest <path>
244
+ \\
245
+ \\ tigerbeetle inspect tables --tree=<name|id> [--level=<integer>] <path>
246
+ \\
247
+ \\ tigerbeetle inspect integrity [--log-debug] [--seed=<seed>]
248
+ \\ [--memory-lsm-manifest=<size>]
249
+ \\ [--skip-wal] [--skip-client-replies] [--skip-grid]
250
+ \\ <path>
251
+ \\
252
+ \\Options:
253
+ \\
254
+ \\ When `--superblock-copy` is set, use the trailer referenced by that superblock copy.
255
+ \\ Otherwise, the current quorum will be used by default.
256
+ \\
257
+ \\ -h, --help
258
+ \\ Print this help message and exit.
259
+ \\
260
+ \\ constants
261
+ \\ Print most important compile-time parameters.
262
+ \\
263
+ \\ metrics
264
+ \\ List metrics and their cardinalities.
265
+ \\
266
+ \\ op
267
+ \\ Print op numbers for adjacent checkpoints and triggers.
268
+ \\
269
+ \\ superblock
270
+ \\ Inspect the superblock header copies.
271
+ \\
272
+ \\ wal
273
+ \\ Inspect the WAL headers and prepares.
274
+ \\
275
+ \\ wal --slot=<slot>
276
+ \\ Inspect the WAL header/prepare in the given slot.
277
+ \\
278
+ \\ replies [--superblock-copy=<copy>]
279
+ \\ Inspect the client reply headers and session numbers.
280
+ \\
281
+ \\ replies --slot=<slot> [--superblock-copy=<copy>]
282
+ \\ Inspect a particular client reply.
283
+ \\
284
+ \\ grid [--superblock-copy=<copy>]
285
+ \\ Inspect the free set.
286
+ \\
287
+ \\ grid --block=<address>
288
+ \\ Inspect the block at the given address.
289
+ \\
290
+ \\ manifest [--superblock-copy=<copy>]
291
+ \\ Inspect the LSM manifest.
292
+ \\
293
+ \\ tables --tree=<name|id> [--level=<integer>] [--superblock-copy=<copy>]
294
+ \\ List the tables matching the given tree/level.
295
+ \\ Example tree names: "transfers" (object table), "transfers.amount" (index table).
296
+ \\
297
+ \\ integrity
298
+ \\ Scans the data file and checks all internal checksums to verify internal
299
+ \\ integrity.
300
+ \\
301
+ ;
302
+ };
303
+
304
+ // Internal: used to validate multiversion binaries.
305
+ const Multiversion = struct {
306
+ log_debug: bool = false,
307
+
308
+ @"--": void,
309
+ path: []const u8,
310
+ };
311
+
312
+ // CDC connector for AMQP targets.
313
+ const AMQP = struct {
314
+ addresses: []const u8,
315
+ cluster: u128,
316
+ host: []const u8,
317
+ user: []const u8,
318
+ password: []const u8,
319
+ vhost: []const u8,
320
+ publish_exchange: ?[]const u8 = null,
321
+ publish_routing_key: ?[]const u8 = null,
322
+ event_count_max: ?u32 = null,
323
+ idle_interval_ms: ?u32 = null,
324
+ requests_per_second_limit: ?u32 = null,
325
+ timestamp_last: ?u64 = null,
326
+ verbose: bool = false,
327
+ };
328
+
329
+ format: Format,
330
+ recover: Recover,
331
+ start: Start,
332
+ version: Version,
333
+ repl: Repl,
334
+ benchmark: Benchmark,
335
+ inspect: Inspect,
336
+ multiversion: Multiversion,
337
+ amqp: AMQP,
338
+
339
+ // TODO Document --cache-accounts, --cache-transfers, --cache-transfers-posted, --limit-storage,
340
+ // --limit-pipeline-requests
341
+ pub const help = fmt.comptimePrint(
342
+ \\Usage:
343
+ \\
344
+ \\ tigerbeetle [-h | --help]
345
+ \\
346
+ \\ tigerbeetle format [--cluster=<integer>] --replica=<index> --replica-count=<integer> <path>
347
+ \\
348
+ \\ tigerbeetle start --addresses=<addresses> [--cache-grid=<size><KiB|MiB|GiB>] <path>
349
+ \\
350
+ \\ tigerbeetle recover --cluster=<integer> --addresses=<addresses>
351
+ \\ --replica=<index> --replica-count=<integer> <path>
352
+ \\
353
+ \\ tigerbeetle version [--verbose]
354
+ \\
355
+ \\ tigerbeetle repl --cluster=<integer> --addresses=<addresses>
356
+ \\
357
+ \\Commands:
358
+ \\
359
+ \\ format Create a TigerBeetle replica data file at <path>.
360
+ \\ The --replica and --replica-count arguments are required.
361
+ \\ Each TigerBeetle replica must have its own data file.
362
+ \\
363
+ \\ start Run a TigerBeetle replica from the data file at <path>.
364
+ \\
365
+ \\ recover Create a TigerBeetle replica data file at <path> for recovery.
366
+ \\ Used when a replica's data file is completely lost.
367
+ \\ Replicas with recovered data files must sync with the cluster before
368
+ \\ they can participate in consensus.
369
+ \\
370
+ \\ version Print the TigerBeetle build version and the compile-time config values.
371
+ \\
372
+ \\ repl Enter the TigerBeetle client REPL.
373
+ \\
374
+ \\ amqp CDC connector for AMQP targets.
375
+ \\
376
+ \\Options:
377
+ \\
378
+ \\ -h, --help
379
+ \\ Print this help message and exit.
380
+ \\
381
+ \\ --cluster=<integer>
382
+ \\ Set the cluster ID to the provided 128-bit unsigned decimal integer.
383
+ \\ Defaults to generating a random cluster ID.
384
+ \\
385
+ \\ --replica=<index>
386
+ \\ Set the zero-based index that will be used for the replica process.
387
+ \\ An index greater than or equal to "replica-count" makes the replica a standby.
388
+ \\ The value of this argument will be interpreted as an index into the --addresses array.
389
+ \\
390
+ \\ --replica-count=<integer>
391
+ \\ Set the number of replicas participating in replication.
392
+ \\
393
+ \\ --addresses=<addresses>
394
+ \\ The addresses of all replicas in the cluster.
395
+ \\ Accepts a comma-separated list of IPv4/IPv6 addresses with port numbers.
396
+ \\ The order is significant and must match across all replicas and clients.
397
+ \\ Either the address or port number (but not both) may be omitted,
398
+ \\ in which case a default of {[default_address]s} or {[default_port]d} will be used.
399
+ \\ "addresses[i]" corresponds to replica "i".
400
+ \\
401
+ \\ --cache-grid=<size><KiB|MiB|GiB>
402
+ \\ Set the grid cache size. The grid cache acts like a page cache for TigerBeetle,
403
+ \\ and should be set as large as possible.
404
+ \\ On a machine running only TigerBeetle, this is somewhere around
405
+ \\ (Total RAM) - 3GiB (TigerBeetle) - 1GiB (System), eg 12GiB for a 16GiB machine.
406
+ \\ Defaults to {[default_cache_grid_gb]d}GiB.
407
+ \\
408
+ \\ --verbose
409
+ \\ Print compile-time configuration along with the build version.
410
+ \\
411
+ \\ --development
412
+ \\ Allow the replica to format/start/recover even when Direct IO is unavailable.
413
+ \\ Additionally, use smaller cache sizes and batch size by default.
414
+ \\
415
+ \\ Since this shrinks the batch size, note that:
416
+ \\ * All replicas should use the same batch size. That is, if any replica in the cluster has
417
+ \\ "--development", then all replicas should have "--development".
418
+ \\ * It is always possible to increase the batch size by restarting without "--development".
419
+ \\ * Shrinking the batch size of an existing cluster is possible, but not recommended.
420
+ \\
421
+ \\ For safety, production replicas should always enforce Direct IO -- this flag should only be
422
+ \\ used for testing and development. It should not be used for production or benchmarks.
423
+ \\
424
+ \\Examples:
425
+ \\
426
+ \\ tigerbeetle format --cluster=0 --replica=0 --replica-count=3 0_0.tigerbeetle
427
+ \\ tigerbeetle format --cluster=0 --replica=1 --replica-count=3 0_1.tigerbeetle
428
+ \\ tigerbeetle format --cluster=0 --replica=2 --replica-count=3 0_2.tigerbeetle
429
+ \\
430
+ \\ tigerbeetle start --addresses=127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002 0_0.tigerbeetle
431
+ \\ tigerbeetle start --addresses=3000,3001,3002 0_1.tigerbeetle
432
+ \\ tigerbeetle start --addresses=3000,3001,3002 0_2.tigerbeetle
433
+ \\
434
+ \\ tigerbeetle start --addresses=192.168.0.1,192.168.0.2,192.168.0.3 0_0.tigerbeetle
435
+ \\
436
+ \\ tigerbeetle start --addresses='[::1]:3000,[::1]:3001,[::1]:3002' 0_0.tigerbeetle
437
+ \\
438
+ \\ tigerbeetle recover --cluster=0 --addresses=3003,3001,3002 \
439
+ \\ --replica=1 --replica-count=3 0_1.tigerbeetle
440
+ \\
441
+ \\ tigerbeetle version --verbose
442
+ \\
443
+ \\ tigerbeetle repl --addresses=3000,3001,3002 --cluster=0
444
+ \\
445
+ \\ tigerbeetle amqp --addresses=3000,3001,3002 --cluster=0 \
446
+ \\ --host=127.0.0.1 --vhost=/ --user=guest --password=guest \
447
+ \\ --publish-exchange=my_exhange_name
448
+ \\
449
+ , .{
450
+ .default_address = constants.address,
451
+ .default_port = constants.port,
452
+ .default_cache_grid_gb = @divExact(
453
+ constants.grid_cache_size_default,
454
+ GiB,
455
+ ),
456
+ });
457
+ };
458
+
459
+ const StartDefaults = struct {
460
+ limit_pipeline_requests: u32,
461
+ limit_request: ByteSize,
462
+ cache_accounts: ByteSize,
463
+ cache_transfers: ByteSize,
464
+ cache_transfers_pending: ByteSize,
465
+ cache_grid: ByteSize,
466
+ memory_lsm_compaction: ByteSize,
467
+ };
468
+
469
+ const start_defaults_production = StartDefaults{
470
+ .limit_pipeline_requests = vsr.stdx.div_ceil(constants.clients_max, 2) -
471
+ constants.pipeline_prepare_queue_max,
472
+ .limit_request = .{ .value = constants.message_size_max },
473
+ .cache_accounts = .{ .value = constants.cache_accounts_size_default },
474
+ .cache_transfers = .{ .value = constants.cache_transfers_size_default },
475
+ .cache_transfers_pending = .{ .value = constants.cache_transfers_pending_size_default },
476
+ .cache_grid = .{ .value = constants.grid_cache_size_default },
477
+ .memory_lsm_compaction = .{
478
+ // By default, add a few extra blocks for beat-scoped work.
479
+ .value = (lsm_compaction_block_count_min + 16) * constants.block_size,
480
+ },
481
+ };
482
+
483
+ const start_defaults_development = StartDefaults{
484
+ .limit_pipeline_requests = 0,
485
+ .limit_request = .{ .value = 32 * KiB },
486
+ .cache_accounts = .{ .value = 0 },
487
+ .cache_transfers = .{ .value = 0 },
488
+ .cache_transfers_pending = .{ .value = 0 },
489
+ .cache_grid = .{ .value = constants.block_size * Grid.Cache.value_count_max_multiple },
490
+ .memory_lsm_compaction = .{ .value = lsm_compaction_block_memory_min },
491
+ };
492
+
493
+ const lsm_compaction_block_count_min = StateMachine.Forest.Options.compaction_block_count_min;
494
+ const lsm_compaction_block_memory_min = lsm_compaction_block_count_min * constants.block_size;
495
+
496
+ /// While CLIArgs store raw arguments as passed on the command line, Command ensures that arguments
497
+ /// are properly validated and desugared (e.g, sizes converted to counts where appropriate).
498
+ pub const Command = union(enum) {
499
+ const Addresses = stdx.BoundedArrayType(std.net.Address, constants.members_max);
500
+ const Path = stdx.BoundedArrayType(u8, std.fs.max_path_bytes);
501
+
502
+ pub const Format = struct {
503
+ cluster: u128,
504
+ replica: u8,
505
+ replica_count: u8,
506
+ development: bool,
507
+ path: []const u8,
508
+ log_debug: bool,
509
+ };
510
+
511
+ pub const Recover = struct {
512
+ cluster: u128,
513
+ addresses: Addresses,
514
+ replica: u8,
515
+ replica_count: u8,
516
+ development: bool,
517
+ path: []const u8,
518
+ log_debug: bool,
519
+ };
520
+
521
+ pub const Start = struct {
522
+ addresses: Addresses,
523
+ // true when the value of `--addresses` is exactly `0`. Used to enable "magic zero" mode for
524
+ // testing. We check the raw string rather then the parsed address to prevent triggering
525
+ // this logic by accident.
526
+ addresses_zero: bool,
527
+ cache_accounts: u32,
528
+ cache_transfers: u32,
529
+ cache_transfers_pending: u32,
530
+ storage_size_limit: u64,
531
+ pipeline_requests_limit: u32,
532
+ request_size_limit: u32,
533
+ cache_grid_blocks: u32,
534
+ lsm_forest_compaction_block_count: u32,
535
+ lsm_forest_node_count: u32,
536
+ timeout_prepare_ticks: ?u64,
537
+ timeout_grid_repair_message_ticks: ?u64,
538
+ commit_stall_probability: ?Ratio,
539
+ trace: ?[]const u8,
540
+ development: bool,
541
+ experimental: bool,
542
+ replicate_star: bool,
543
+ aof_file: ?Path,
544
+ aof_recovery: bool,
545
+ path: []const u8,
546
+ log_debug: bool,
547
+ log_trace: bool,
548
+ statsd: ?std.net.Address,
549
+ };
550
+
551
+ pub const Version = struct {
552
+ verbose: bool,
553
+ };
554
+
555
+ pub const Repl = struct {
556
+ addresses: Addresses,
557
+ cluster: u128,
558
+ verbose: bool,
559
+ statements: []const u8,
560
+ log_debug: bool,
561
+ };
562
+
563
+ pub const Benchmark = struct {
564
+ /// The ID order can affect the results of a benchmark significantly. Specifically,
565
+ /// sequential is expected to be the best (since it can take advantage of various
566
+ /// optimizations such as avoiding negative prefetch) while random/reversed can't.
567
+ pub const IdOrder = enum {
568
+ // Use TBIDs (time-based IDs) for transfers and a random start for account IDs.
569
+ // Avoids ID collisions between benchmark runs against the same cluster.
570
+ // Incompatible with --validate (IDs are not deterministically replayable).
571
+ tbid,
572
+ sequential,
573
+ random,
574
+ reversed,
575
+ };
576
+
577
+ pub const Distribution = enum {
578
+ /// Shuffled zipfian numbers where relatively few indexes are selected frequently.
579
+ zipfian,
580
+ /// Also zipfian, but the most recent indexes are selected frequently.
581
+ latest,
582
+ /// Uniform distribution; unrealistic workloads.
583
+ uniform,
584
+ };
585
+
586
+ cache_accounts: ?[]const u8,
587
+ cache_transfers: ?[]const u8,
588
+ cache_transfers_pending: ?[]const u8,
589
+ cache_grid: ?[]const u8,
590
+ log_debug: bool,
591
+ log_debug_replica: bool,
592
+ account_count: u64,
593
+ account_count_hot: u32,
594
+ account_distribution: Distribution,
595
+ no_history: bool,
596
+ imported: bool,
597
+ account_batch_count: u32,
598
+ transfer_count: u64,
599
+ transfer_hot_percent: u32,
600
+ transfer_pending: bool,
601
+ transfer_batch_count: u32,
602
+ transfer_batch_delay: Duration,
603
+ validate: bool,
604
+ checksum_performance: bool,
605
+ query_count: u32,
606
+ print_batch_timings: bool,
607
+ id_order: IdOrder,
608
+ clients: u32,
609
+ statsd: ?[]const u8,
610
+ trace: ?[]const u8,
611
+ file: ?[]const u8,
612
+ addresses: ?Addresses,
613
+ seed: ?[]const u8,
614
+ };
615
+
616
+ pub const Inspect = union(enum) {
617
+ constants,
618
+ metrics,
619
+ op: u64,
620
+ data_file: DataFile,
621
+ integrity: Integrity,
622
+
623
+ pub const DataFile = struct {
624
+ path: []const u8,
625
+ query: union(enum) {
626
+ superblock,
627
+ wal: struct {
628
+ slot: ?usize,
629
+ },
630
+ replies: struct {
631
+ slot: ?usize,
632
+ superblock_copy: ?u8,
633
+ },
634
+ grid: struct {
635
+ block: ?u64,
636
+ superblock_copy: ?u8,
637
+ },
638
+ manifest: struct {
639
+ superblock_copy: ?u8,
640
+ },
641
+ tables: struct {
642
+ superblock_copy: ?u8,
643
+ tree: []const u8,
644
+ level: ?u6,
645
+ },
646
+ },
647
+ };
648
+
649
+ pub const Integrity = struct {
650
+ log_debug: bool,
651
+ seed: ?[]const u8,
652
+ lsm_forest_node_count: u32,
653
+ skip_wal: bool,
654
+ skip_client_replies: bool,
655
+ skip_grid: bool,
656
+ path: [:0]const u8,
657
+ };
658
+ };
659
+
660
+ pub const Multiversion = struct {
661
+ path: []const u8,
662
+ log_debug: bool,
663
+ };
664
+
665
+ pub const AMQP = struct {
666
+ addresses: Addresses,
667
+ cluster: u128,
668
+ host: std.net.Address,
669
+ user: []const u8,
670
+ password: []const u8,
671
+ vhost: []const u8,
672
+ publish_exchange: ?[]const u8,
673
+ publish_routing_key: ?[]const u8,
674
+ event_count_max: ?u32,
675
+ idle_interval_ms: ?u32,
676
+ requests_per_second_limit: ?u32,
677
+ timestamp_last: ?u64,
678
+ log_debug: bool,
679
+ };
680
+
681
+ format: Format,
682
+ recover: Recover,
683
+ start: Start,
684
+ version: Version,
685
+ repl: Repl,
686
+ benchmark: Benchmark,
687
+ inspect: Inspect,
688
+ multiversion: Multiversion,
689
+ amqp: AMQP,
690
+ };
691
+
692
+ /// Parse the command line arguments passed to the `tigerbeetle` binary.
693
+ /// Exits the program with a non-zero exit code if an error is found.
694
+ pub fn parse_args(args_iterator: *std.process.ArgIterator) Command {
695
+ const cli_args = stdx.flags(args_iterator, CLIArgs);
696
+
697
+ return switch (cli_args) {
698
+ .format => |format| .{ .format = parse_args_format(format) },
699
+ .recover => |recover| .{ .recover = parse_args_recover(recover) },
700
+ .start => |start| .{ .start = parse_args_start(start) },
701
+ .version => |version| .{ .version = parse_args_version(version) },
702
+ .repl => |repl| .{ .repl = parse_args_repl(repl) },
703
+ .benchmark => |benchmark| .{ .benchmark = parse_args_benchmark(benchmark) },
704
+ .inspect => |inspect| .{ .inspect = parse_args_inspect(inspect) },
705
+ .multiversion => |multiversion| .{ .multiversion = parse_args_multiversion(multiversion) },
706
+ .amqp => |amqp| .{ .amqp = parse_args_amqp(amqp) },
707
+ };
708
+ }
709
+
710
+ fn parse_args_format(format: CLIArgs.Format) Command.Format {
711
+ if (format.replica_count == 0) {
712
+ vsr.fatal(.cli, "--replica-count: value needs to be greater than zero", .{});
713
+ }
714
+ if (format.replica_count > constants.replicas_max) {
715
+ vsr.fatal(.cli, "--replica-count: value is too large ({}), at most {} is allowed", .{
716
+ format.replica_count,
717
+ constants.replicas_max,
718
+ });
719
+ }
720
+
721
+ if (format.replica == null and format.standby == null) {
722
+ vsr.fatal(.cli, "--replica: argument is required", .{});
723
+ }
724
+
725
+ if (format.replica != null and format.standby != null) {
726
+ vsr.fatal(.cli, "--standby: conflicts with '--replica'", .{});
727
+ }
728
+
729
+ if (format.replica) |replica| {
730
+ if (replica >= format.replica_count) {
731
+ vsr.fatal(.cli, "--replica: value is too large ({}), at most {} is allowed", .{
732
+ replica,
733
+ format.replica_count - 1,
734
+ });
735
+ }
736
+ }
737
+
738
+ if (format.standby) |standby| {
739
+ if (standby < format.replica_count) {
740
+ vsr.fatal(.cli, "--standby: value is too small ({}), at least {} is required", .{
741
+ standby,
742
+ format.replica_count,
743
+ });
744
+ }
745
+ if (standby >= format.replica_count + constants.standbys_max) {
746
+ vsr.fatal(.cli, "--standby: value is too large ({}), at most {} is allowed", .{
747
+ standby,
748
+ format.replica_count + constants.standbys_max - 1,
749
+ });
750
+ }
751
+ }
752
+
753
+ const replica = (format.replica orelse format.standby).?;
754
+ assert(replica < constants.members_max);
755
+ assert(replica < format.replica_count + constants.standbys_max);
756
+
757
+ const cluster_random = std.crypto.random.int(u128);
758
+ assert(cluster_random != 0);
759
+ const cluster = format.cluster orelse cluster_random;
760
+ if (format.cluster == null) {
761
+ std.log.info("generated random cluster id: {}\n", .{cluster});
762
+ } else if (format.cluster.? == 0) {
763
+ std.log.warn("a cluster id of 0 is reserved for testing and benchmarking, " ++
764
+ "do not use in production", .{});
765
+ std.log.warn("omit --cluster=0 to randomly generate a suitable id\n", .{});
766
+ }
767
+
768
+ return .{
769
+ .cluster = cluster, // just an ID, any value is allowed
770
+ .replica = replica,
771
+ .replica_count = format.replica_count,
772
+ .development = format.development,
773
+ .path = format.path,
774
+ .log_debug = format.log_debug,
775
+ };
776
+ }
777
+
778
+ fn parse_args_recover(recover: CLIArgs.Recover) Command.Recover {
779
+ if (recover.replica_count == 0) {
780
+ vsr.fatal(.cli, "--replica-count: value needs to be greater than zero", .{});
781
+ }
782
+ if (recover.replica_count > constants.replicas_max) {
783
+ vsr.fatal(.cli, "--replica-count: value is too large ({}), at most {} is allowed", .{
784
+ recover.replica_count,
785
+ constants.replicas_max,
786
+ });
787
+ }
788
+
789
+ if (recover.replica >= recover.replica_count) {
790
+ vsr.fatal(.cli, "--replica: value is too large ({}), at most {} is allowed", .{
791
+ recover.replica,
792
+ recover.replica_count - 1,
793
+ });
794
+ }
795
+ if (recover.replica_count <= 2) {
796
+ vsr.fatal(.cli, "--replica-count: 1- or 2- replica clusters don't support 'recover'", .{});
797
+ }
798
+
799
+ const replica = recover.replica;
800
+ assert(replica < constants.members_max);
801
+ assert(replica < recover.replica_count);
802
+
803
+ return .{
804
+ .cluster = recover.cluster,
805
+ .addresses = parse_addresses(recover.addresses, "--addresses", Command.Addresses),
806
+ .replica = replica,
807
+ .replica_count = recover.replica_count,
808
+ .development = recover.development,
809
+ .path = recover.path,
810
+ .log_debug = recover.log_debug,
811
+ };
812
+ }
813
+
814
+ fn parse_args_start(start: CLIArgs.Start) Command.Start {
815
+ // Allowlist of stable flags. --development will disable automatic multiversion
816
+ // upgrades too, but the flag itself is stable.
817
+ const stable_args = .{
818
+ "addresses", "cache_grid",
819
+ "development", "experimental",
820
+ };
821
+ inline for (std.meta.fields(@TypeOf(start))) |field| {
822
+ @setEvalBranchQuota(4_000);
823
+ // Positional arguments can't be experimental.
824
+ comptime if (std.mem.eql(u8, field.name, "--")) break;
825
+
826
+ const stable_field = comptime for (stable_args) |stable_arg| {
827
+ assert(std.meta.fieldIndex(@TypeOf(start), stable_arg) != null);
828
+ if (std.mem.eql(u8, field.name, stable_arg)) {
829
+ break true;
830
+ }
831
+ } else false;
832
+ if (stable_field) continue;
833
+
834
+ const flag_name = comptime blk: {
835
+ var result: [2 + field.name.len]u8 = ("--" ++ field.name).*;
836
+ std.mem.replaceScalar(u8, &result, '_', '-');
837
+ break :blk result;
838
+ };
839
+
840
+ // If you've added a flag and get a comptime error here, it's likely because
841
+ // we require experimental flags to default to null.
842
+ const required_default = if (field.type == bool) false else null;
843
+ assert(field.defaultValue().? == required_default);
844
+
845
+ if (@field(start, field.name) != required_default and !start.experimental) {
846
+ vsr.fatal(
847
+ .cli,
848
+ "{s} is marked experimental, add `--experimental` to continue.",
849
+ .{flag_name},
850
+ );
851
+ }
852
+ } else unreachable;
853
+
854
+ const groove_config = StateMachine.Forest.groove_config;
855
+ const AccountsValuesCache = groove_config.accounts.ObjectsCache.Cache;
856
+ const TransfersValuesCache = groove_config.transfers.ObjectsCache.Cache;
857
+ const TransfersPendingValuesCache = groove_config.transfers_pending.ObjectsCache.Cache;
858
+
859
+ const addresses = parse_addresses(start.addresses, "--addresses", Command.Addresses);
860
+ const defaults =
861
+ if (start.development) start_defaults_development else start_defaults_production;
862
+
863
+ const start_limit_storage: ByteSize = start.limit_storage orelse
864
+ .{ .value = constants.storage_size_limit_default };
865
+ const start_memory_lsm_manifest: ByteSize = start.memory_lsm_manifest orelse
866
+ .{ .value = constants.lsm_manifest_memory_size_default };
867
+
868
+ const storage_size_limit = start_limit_storage.bytes();
869
+ const storage_size_limit_min = data_file_size_min;
870
+ const storage_size_limit_max = constants.storage_size_limit_max;
871
+ if (storage_size_limit > storage_size_limit_max) {
872
+ vsr.fatal(.cli, "--limit-storage: size {}{s} exceeds maximum: {}", .{
873
+ start_limit_storage.value,
874
+ start_limit_storage.suffix(),
875
+ vsr.stdx.fmt_int_size_bin_exact(storage_size_limit_max),
876
+ });
877
+ }
878
+ if (storage_size_limit < storage_size_limit_min) {
879
+ vsr.fatal(.cli, "--limit-storage: size {}{s} is below minimum: {}", .{
880
+ start_limit_storage.value,
881
+ start_limit_storage.suffix(),
882
+ vsr.stdx.fmt_int_size_bin_exact(storage_size_limit_min),
883
+ });
884
+ }
885
+ if (storage_size_limit % constants.sector_size != 0) {
886
+ vsr.fatal(
887
+ .cli,
888
+ "--limit-storage: size {}{s} must be a multiple of sector size ({})",
889
+ .{
890
+ start_limit_storage.value,
891
+ start_limit_storage.suffix(),
892
+ vsr.stdx.fmt_int_size_bin_exact(constants.sector_size),
893
+ },
894
+ );
895
+ }
896
+
897
+ const pipeline_limit =
898
+ start.limit_pipeline_requests orelse defaults.limit_pipeline_requests;
899
+ const pipeline_limit_min = 0;
900
+ const pipeline_limit_max = constants.pipeline_request_queue_max;
901
+ if (pipeline_limit > pipeline_limit_max) {
902
+ vsr.fatal(.cli, "--limit-pipeline-requests: count {} exceeds maximum: {}", .{
903
+ pipeline_limit,
904
+ pipeline_limit_max,
905
+ });
906
+ }
907
+ if (pipeline_limit < pipeline_limit_min) {
908
+ vsr.fatal(.cli, "--limit-pipeline-requests: count {} is below minimum: {}", .{
909
+ pipeline_limit,
910
+ pipeline_limit_min,
911
+ });
912
+ }
913
+
914
+ // The minimum is chosen rather arbitrarily as 4096 since it is the sector size.
915
+ const request_size_limit = start.limit_request orelse defaults.limit_request;
916
+ const request_size_limit_min = 4096;
917
+ const request_size_limit_max = constants.message_size_max;
918
+ if (request_size_limit.bytes() > request_size_limit_max) {
919
+ vsr.fatal(.cli, "--limit-request: size {}{s} exceeds maximum: {}", .{
920
+ request_size_limit.value,
921
+ request_size_limit.suffix(),
922
+ vsr.stdx.fmt_int_size_bin_exact(request_size_limit_max),
923
+ });
924
+ }
925
+ if (request_size_limit.bytes() < request_size_limit_min) {
926
+ vsr.fatal(.cli, "--limit-request: size {}{s} is below minimum: {}", .{
927
+ request_size_limit.value,
928
+ request_size_limit.suffix(),
929
+ vsr.stdx.fmt_int_size_bin_exact(request_size_limit_min),
930
+ });
931
+ }
932
+
933
+ const lsm_manifest_memory = start_memory_lsm_manifest.bytes();
934
+ const lsm_manifest_memory_max = constants.lsm_manifest_memory_size_max;
935
+ const lsm_manifest_memory_min = constants.lsm_manifest_memory_size_min;
936
+ const lsm_manifest_memory_multiplier = constants.lsm_manifest_memory_size_multiplier;
937
+ if (lsm_manifest_memory > lsm_manifest_memory_max) {
938
+ vsr.fatal(.cli, "--memory-lsm-manifest: size {}{s} exceeds maximum: {}", .{
939
+ start_memory_lsm_manifest.value,
940
+ start_memory_lsm_manifest.suffix(),
941
+ vsr.stdx.fmt_int_size_bin_exact(lsm_manifest_memory_max),
942
+ });
943
+ }
944
+ if (lsm_manifest_memory < lsm_manifest_memory_min) {
945
+ vsr.fatal(.cli, "--memory-lsm-manifest: size {}{s} is below minimum: {}", .{
946
+ start_memory_lsm_manifest.value,
947
+ start_memory_lsm_manifest.suffix(),
948
+ vsr.stdx.fmt_int_size_bin_exact(lsm_manifest_memory_min),
949
+ });
950
+ }
951
+ if (lsm_manifest_memory % lsm_manifest_memory_multiplier != 0) {
952
+ vsr.fatal(
953
+ .cli,
954
+ "--memory-lsm-manifest: size {}{s} must be a multiple of {}",
955
+ .{
956
+ start_memory_lsm_manifest.value,
957
+ start_memory_lsm_manifest.suffix(),
958
+ vsr.stdx.fmt_int_size_bin_exact(lsm_manifest_memory_multiplier),
959
+ },
960
+ );
961
+ }
962
+
963
+ const lsm_compaction_block_memory =
964
+ start.memory_lsm_compaction orelse defaults.memory_lsm_compaction;
965
+ const lsm_compaction_block_memory_max = constants.compaction_block_memory_size_max;
966
+ if (lsm_compaction_block_memory.bytes() > lsm_compaction_block_memory_max) {
967
+ vsr.fatal(.cli, "--memory-lsm-compaction: size {}{s} exceeds maximum: {}", .{
968
+ lsm_compaction_block_memory.value,
969
+ lsm_compaction_block_memory.suffix(),
970
+ vsr.stdx.fmt_int_size_bin_exact(lsm_compaction_block_memory_max),
971
+ });
972
+ }
973
+ if (lsm_compaction_block_memory.bytes() < lsm_compaction_block_memory_min) {
974
+ vsr.fatal(.cli, "--memory-lsm-compaction: size {}{s} is below minimum: {}", .{
975
+ lsm_compaction_block_memory.value,
976
+ lsm_compaction_block_memory.suffix(),
977
+ vsr.stdx.fmt_int_size_bin_exact(lsm_compaction_block_memory_min),
978
+ });
979
+ }
980
+ if (lsm_compaction_block_memory.bytes() % constants.block_size != 0) {
981
+ vsr.fatal(
982
+ .cli,
983
+ "--memory-lsm-compaction: size {}{s} must be a multiple of {}",
984
+ .{
985
+ lsm_compaction_block_memory.value,
986
+ lsm_compaction_block_memory.suffix(),
987
+ vsr.stdx.fmt_int_size_bin_exact(constants.block_size),
988
+ },
989
+ );
990
+ }
991
+
992
+ const lsm_forest_compaction_block_count: u32 =
993
+ @intCast(@divExact(lsm_compaction_block_memory.bytes(), constants.block_size));
994
+ const lsm_forest_node_count: u32 =
995
+ @intCast(@divExact(lsm_manifest_memory, constants.lsm_manifest_node_size));
996
+
997
+ const aof_file: ?Command.Path = if (start.aof) blk: {
998
+ if (start.aof_file != null) {
999
+ vsr.fatal(.cli, "--aof is mutually exclusive with --aof-file", .{});
1000
+ }
1001
+
1002
+ var aof_file: Command.Path = .{};
1003
+ if (aof_file.capacity() < start.path.len + 4) {
1004
+ vsr.fatal(.cli, "data file path is too long for --aof. use --aof-file", .{});
1005
+ }
1006
+ aof_file.push_slice(start.path);
1007
+ aof_file.push_slice(".aof");
1008
+
1009
+ std.log.warn(
1010
+ "--aof is deprecated. consider switching to '--aof-file={s}'",
1011
+ .{aof_file.const_slice()},
1012
+ );
1013
+
1014
+ break :blk aof_file;
1015
+ } else if (start.aof_file) |start_aof_file| blk: {
1016
+ if (!std.mem.endsWith(u8, start_aof_file, ".aof")) {
1017
+ vsr.fatal(.cli, "--aof-file must end with .aof: '{s}'", .{start_aof_file});
1018
+ }
1019
+
1020
+ var aof_file: Command.Path = .{};
1021
+ if (aof_file.capacity() < start.path.len) {
1022
+ vsr.fatal(.cli, "--aof-file path is too long", .{});
1023
+ }
1024
+ aof_file.push_slice(start_aof_file);
1025
+
1026
+ break :blk aof_file;
1027
+ } else null;
1028
+
1029
+ if (start.log_trace and !start.log_debug) {
1030
+ vsr.fatal(.cli, "--log-debug must be provided when using --log-trace", .{});
1031
+ }
1032
+
1033
+ return .{
1034
+ .addresses = addresses,
1035
+ .addresses_zero = std.mem.eql(u8, start.addresses, "0"),
1036
+ .storage_size_limit = storage_size_limit,
1037
+ .pipeline_requests_limit = pipeline_limit,
1038
+ .request_size_limit = @intCast(request_size_limit.bytes()),
1039
+ .cache_accounts = parse_cache_size_to_count(
1040
+ tigerbeetle.Account,
1041
+ AccountsValuesCache,
1042
+ start.cache_accounts orelse defaults.cache_accounts,
1043
+ "--cache-accounts",
1044
+ ),
1045
+ .cache_transfers = parse_cache_size_to_count(
1046
+ tigerbeetle.Transfer,
1047
+ TransfersValuesCache,
1048
+ start.cache_transfers orelse defaults.cache_transfers,
1049
+ "--cache-transfers",
1050
+ ),
1051
+ .cache_transfers_pending = parse_cache_size_to_count(
1052
+ vsr.state_machine.TransferPending,
1053
+ TransfersPendingValuesCache,
1054
+ start.cache_transfers_pending orelse defaults.cache_transfers_pending,
1055
+ "--cache-transfers-pending",
1056
+ ),
1057
+ .cache_grid_blocks = parse_cache_size_to_count(
1058
+ [constants.block_size]u8,
1059
+ Grid.Cache,
1060
+ start.cache_grid orelse defaults.cache_grid,
1061
+ "--cache-grid",
1062
+ ),
1063
+ .lsm_forest_compaction_block_count = lsm_forest_compaction_block_count,
1064
+ .lsm_forest_node_count = lsm_forest_node_count,
1065
+ .timeout_prepare_ticks = parse_timeout_to_ticks(
1066
+ start.timeout_prepare_ms,
1067
+ "--timeout-prepare-ms",
1068
+ ),
1069
+ .timeout_grid_repair_message_ticks = parse_timeout_to_ticks(
1070
+ start.timeout_grid_repair_message_ms,
1071
+ "--timeout-grid-repair-message-ms",
1072
+ ),
1073
+ .commit_stall_probability = start.commit_stall_probability,
1074
+ .development = start.development,
1075
+ .experimental = start.experimental,
1076
+ .trace = start.trace,
1077
+ .replicate_star = start.replicate_star,
1078
+ .aof_file = aof_file,
1079
+ .aof_recovery = start.aof_recovery,
1080
+ .path = start.path,
1081
+ .log_debug = start.log_debug,
1082
+ .log_trace = start.log_trace,
1083
+ .statsd = if (start.statsd) |statsd_address|
1084
+ parse_address_and_port(statsd_address, "--statsd", 8125)
1085
+ else
1086
+ null,
1087
+ };
1088
+ }
1089
+
1090
+ fn parse_args_version(version: CLIArgs.Version) Command.Version {
1091
+ return .{
1092
+ .verbose = version.verbose,
1093
+ };
1094
+ }
1095
+
1096
+ fn parse_args_repl(repl: CLIArgs.Repl) Command.Repl {
1097
+ const addresses = parse_addresses(repl.addresses, "--addresses", Command.Addresses);
1098
+
1099
+ return .{
1100
+ .addresses = addresses,
1101
+ .cluster = repl.cluster,
1102
+ .verbose = repl.verbose,
1103
+ .statements = repl.command,
1104
+ .log_debug = repl.log_debug,
1105
+ };
1106
+ }
1107
+
1108
+ const account_batch_count_max = @divExact(
1109
+ constants.message_size_max - @sizeOf(vsr.Header),
1110
+ @sizeOf(tigerbeetle.Account),
1111
+ );
1112
+
1113
+ const transfer_batch_count_max = @divExact(
1114
+ constants.message_size_max - @sizeOf(vsr.Header),
1115
+ @sizeOf(tigerbeetle.Transfer),
1116
+ );
1117
+
1118
+ fn parse_args_benchmark(benchmark: CLIArgs.Benchmark) Command.Benchmark {
1119
+ const addresses = if (benchmark.addresses) |addresses|
1120
+ parse_addresses(addresses, "--addresses", Command.Addresses)
1121
+ else
1122
+ null;
1123
+
1124
+ if (benchmark.addresses != null and benchmark.file != null) {
1125
+ vsr.fatal(.cli, "--file: --addresses and --file are mutually exclusive", .{});
1126
+ }
1127
+
1128
+ if (benchmark.account_batch_count == 0) {
1129
+ vsr.fatal(.cli, "--account-batch-count must be greater than 0", .{});
1130
+ }
1131
+
1132
+ if (benchmark.account_batch_count > account_batch_count_max) {
1133
+ vsr.fatal(
1134
+ .cli,
1135
+ "--account-batch-count must be less than or equal to {}",
1136
+ .{account_batch_count_max},
1137
+ );
1138
+ }
1139
+
1140
+ if (benchmark.transfer_batch_count == 0) {
1141
+ vsr.fatal(.cli, "--transfer-batch-count must be greater than 0", .{});
1142
+ }
1143
+
1144
+ if (benchmark.transfer_batch_count > transfer_batch_count_max) {
1145
+ vsr.fatal(
1146
+ .cli,
1147
+ "--transfer-batch-count must be less than or equal to {}",
1148
+ .{transfer_batch_count_max},
1149
+ );
1150
+ }
1151
+
1152
+ return .{
1153
+ .cache_accounts = benchmark.cache_accounts,
1154
+ .cache_transfers = benchmark.cache_transfers,
1155
+ .cache_transfers_pending = benchmark.cache_transfers_pending,
1156
+ .cache_grid = benchmark.cache_grid,
1157
+ .log_debug = benchmark.log_debug,
1158
+ .log_debug_replica = benchmark.log_debug_replica,
1159
+ .account_count = benchmark.account_count,
1160
+ .account_count_hot = benchmark.account_count_hot,
1161
+ .account_distribution = benchmark.account_distribution,
1162
+ .no_history = benchmark.no_history,
1163
+ .imported = benchmark.imported,
1164
+ .account_batch_count = benchmark.account_batch_count,
1165
+ .transfer_count = benchmark.transfer_count,
1166
+ .transfer_hot_percent = benchmark.transfer_hot_percent,
1167
+ .transfer_pending = benchmark.transfer_pending,
1168
+ .transfer_batch_count = benchmark.transfer_batch_count,
1169
+ .transfer_batch_delay = benchmark.transfer_batch_delay,
1170
+ .validate = benchmark.validate,
1171
+ .checksum_performance = benchmark.checksum_performance,
1172
+ .query_count = benchmark.query_count,
1173
+ .print_batch_timings = benchmark.print_batch_timings,
1174
+ .clients = benchmark.clients,
1175
+ .id_order = benchmark.id_order,
1176
+ .statsd = benchmark.statsd,
1177
+ .trace = benchmark.trace,
1178
+ .file = benchmark.file,
1179
+ .addresses = addresses,
1180
+ .seed = benchmark.seed,
1181
+ };
1182
+ }
1183
+
1184
+ fn parse_args_inspect_integrity(args: CLIArgs.Inspect) Command.Inspect.Integrity {
1185
+ const integrity = args.integrity;
1186
+
1187
+ const scrub_memory_lsm_manifest: ByteSize = integrity.memory_lsm_manifest orelse
1188
+ .{ .value = constants.lsm_manifest_memory_size_default };
1189
+
1190
+ const lsm_manifest_memory = scrub_memory_lsm_manifest.bytes();
1191
+ const lsm_manifest_memory_max = constants.lsm_manifest_memory_size_max;
1192
+ const lsm_manifest_memory_min = constants.lsm_manifest_memory_size_min;
1193
+ const lsm_manifest_memory_multiplier = constants.lsm_manifest_memory_size_multiplier;
1194
+ if (lsm_manifest_memory > lsm_manifest_memory_max) {
1195
+ vsr.fatal(.cli, "--memory-lsm-manifest: size {}{s} exceeds maximum: {}", .{
1196
+ scrub_memory_lsm_manifest.value,
1197
+ scrub_memory_lsm_manifest.suffix(),
1198
+ vsr.stdx.fmt_int_size_bin_exact(lsm_manifest_memory_max),
1199
+ });
1200
+ }
1201
+ if (lsm_manifest_memory < lsm_manifest_memory_min) {
1202
+ vsr.fatal(.cli, "--memory-lsm-manifest: size {}{s} is below minimum: {}", .{
1203
+ scrub_memory_lsm_manifest.value,
1204
+ scrub_memory_lsm_manifest.suffix(),
1205
+ vsr.stdx.fmt_int_size_bin_exact(lsm_manifest_memory_min),
1206
+ });
1207
+ }
1208
+ if (lsm_manifest_memory % lsm_manifest_memory_multiplier != 0) {
1209
+ vsr.fatal(
1210
+ .cli,
1211
+ "--memory-lsm-manifest: size {}{s} must be a multiple of {}",
1212
+ .{
1213
+ scrub_memory_lsm_manifest.value,
1214
+ scrub_memory_lsm_manifest.suffix(),
1215
+ vsr.stdx.fmt_int_size_bin_exact(lsm_manifest_memory_multiplier),
1216
+ },
1217
+ );
1218
+ }
1219
+
1220
+ const lsm_forest_node_count: u32 =
1221
+ @intCast(@divExact(lsm_manifest_memory, constants.lsm_manifest_node_size));
1222
+
1223
+ return .{
1224
+ .path = integrity.path,
1225
+ .log_debug = integrity.log_debug,
1226
+ .seed = integrity.seed,
1227
+ .skip_wal = integrity.skip_wal,
1228
+ .skip_client_replies = integrity.skip_client_replies,
1229
+ .skip_grid = integrity.skip_grid,
1230
+ .lsm_forest_node_count = lsm_forest_node_count,
1231
+ };
1232
+ }
1233
+
1234
+ fn parse_args_inspect(inspect: CLIArgs.Inspect) Command.Inspect {
1235
+ const path = switch (inspect) {
1236
+ .constants => return .constants,
1237
+ .metrics => return .metrics,
1238
+ .op => |args| return .{ .op = args.op },
1239
+ .integrity => return .{ .integrity = parse_args_inspect_integrity(inspect) },
1240
+ inline else => |args| args.path,
1241
+ };
1242
+
1243
+ return .{ .data_file = .{
1244
+ .path = path,
1245
+ .query = switch (inspect) {
1246
+ .constants,
1247
+ .metrics,
1248
+ .op,
1249
+ .integrity,
1250
+ => unreachable,
1251
+ .superblock => .superblock,
1252
+ .wal => |args| .{ .wal = .{ .slot = args.slot } },
1253
+ .replies => |args| .{ .replies = .{
1254
+ .slot = args.slot,
1255
+ .superblock_copy = args.superblock_copy,
1256
+ } },
1257
+ .grid => |args| .{ .grid = .{
1258
+ .block = args.block,
1259
+ .superblock_copy = args.superblock_copy,
1260
+ } },
1261
+ .manifest => |args| .{ .manifest = .{
1262
+ .superblock_copy = args.superblock_copy,
1263
+ } },
1264
+ .tables => |args| .{ .tables = .{
1265
+ .superblock_copy = args.superblock_copy,
1266
+ .tree = args.tree,
1267
+ .level = args.level,
1268
+ } },
1269
+ },
1270
+ } };
1271
+ }
1272
+
1273
+ fn parse_args_multiversion(multiversion: CLIArgs.Multiversion) Command.Multiversion {
1274
+ return .{
1275
+ .path = multiversion.path,
1276
+ .log_debug = multiversion.log_debug,
1277
+ };
1278
+ }
1279
+
1280
+ fn parse_args_amqp(amqp: CLIArgs.AMQP) Command.AMQP {
1281
+ const addresses = parse_addresses(amqp.addresses, "--addresses", Command.Addresses);
1282
+ const host = parse_address_and_port(
1283
+ amqp.host,
1284
+ "--host",
1285
+ vsr.cdc.amqp.tcp_port_default,
1286
+ );
1287
+
1288
+ if (amqp.publish_exchange == null and amqp.publish_routing_key == null) {
1289
+ vsr.fatal(
1290
+ .cli,
1291
+ "--publish-exchange and --publish-routing-key cannot both be empty.",
1292
+ .{},
1293
+ );
1294
+ }
1295
+
1296
+ if (amqp.requests_per_second_limit) |requests_per_second_limit| {
1297
+ if (requests_per_second_limit == 0) {
1298
+ vsr.fatal(
1299
+ .cli,
1300
+ "--requests-per-second-limit must not be zero.",
1301
+ .{},
1302
+ );
1303
+ }
1304
+ }
1305
+
1306
+ return .{
1307
+ .addresses = addresses,
1308
+ .cluster = amqp.cluster,
1309
+ .host = host,
1310
+ .user = amqp.user,
1311
+ .password = amqp.password,
1312
+ .vhost = amqp.vhost,
1313
+ .publish_exchange = amqp.publish_exchange,
1314
+ .publish_routing_key = amqp.publish_routing_key,
1315
+ .event_count_max = amqp.event_count_max,
1316
+ .idle_interval_ms = amqp.idle_interval_ms,
1317
+ .requests_per_second_limit = amqp.requests_per_second_limit,
1318
+ .timestamp_last = amqp.timestamp_last,
1319
+ .log_debug = amqp.verbose,
1320
+ };
1321
+ }
1322
+
1323
+ /// Parse and allocate the addresses returning a slice into that array.
1324
+ fn parse_addresses(
1325
+ raw_addresses: []const u8,
1326
+ comptime flag: []const u8,
1327
+ comptime BoundedArray: type,
1328
+ ) BoundedArray {
1329
+ comptime assert(std.mem.startsWith(u8, flag, "--"));
1330
+ var result: BoundedArray = .{};
1331
+
1332
+ const addresses_parsed = vsr.parse_addresses(
1333
+ raw_addresses,
1334
+ result.unused_capacity_slice(),
1335
+ ) catch |err| switch (err) {
1336
+ error.AddressHasTrailingComma => {
1337
+ vsr.fatal(.cli, flag ++ ": invalid trailing comma", .{});
1338
+ },
1339
+ error.AddressLimitExceeded => {
1340
+ vsr.fatal(.cli, flag ++ ": too many addresses, at most {d} are allowed", .{
1341
+ result.capacity(),
1342
+ });
1343
+ },
1344
+ error.AddressHasMoreThanOneColon => {
1345
+ vsr.fatal(.cli, flag ++ ": invalid address with more than one colon", .{});
1346
+ },
1347
+ error.PortOverflow => vsr.fatal(.cli, flag ++ ": port exceeds 65535", .{}),
1348
+ error.PortInvalid => vsr.fatal(.cli, flag ++ ": invalid port", .{}),
1349
+ error.AddressInvalid => vsr.fatal(.cli, flag ++ ": invalid IPv4 or IPv6 address", .{}),
1350
+ };
1351
+ assert(addresses_parsed.len > 0);
1352
+ assert(addresses_parsed.len <= result.capacity());
1353
+ result.resize(addresses_parsed.len) catch unreachable;
1354
+ return result;
1355
+ }
1356
+
1357
+ fn parse_address_and_port(
1358
+ raw_address: []const u8,
1359
+ comptime flag: []const u8,
1360
+ port_default: u16,
1361
+ ) std.net.Address {
1362
+ comptime assert(std.mem.startsWith(u8, flag, "--"));
1363
+
1364
+ const address = vsr.parse_address_and_port(.{
1365
+ .string = raw_address,
1366
+ .port_default = port_default,
1367
+ }) catch |err| switch (err) {
1368
+ error.AddressHasMoreThanOneColon => {
1369
+ vsr.fatal(.cli, flag ++ ": invalid address with more than one colon", .{});
1370
+ },
1371
+ error.PortOverflow => vsr.fatal(.cli, flag ++ ": port exceeds 65535", .{}),
1372
+ error.PortInvalid => vsr.fatal(.cli, flag ++ ": invalid port", .{}),
1373
+ error.AddressInvalid => vsr.fatal(.cli, flag ++ ": invalid IPv4 or IPv6 address", .{}),
1374
+ };
1375
+ return address;
1376
+ }
1377
+
1378
+ /// Given a limit like `10GiB`, a SetAssociativeCache and T return the largest `value_count_max`
1379
+ /// that can fit in the limit.
1380
+ fn parse_cache_size_to_count(
1381
+ comptime T: type,
1382
+ comptime SetAssociativeCache: type,
1383
+ size: ByteSize,
1384
+ cli_flag: []const u8,
1385
+ ) u32 {
1386
+ const value_count_max_multiple = SetAssociativeCache.value_count_max_multiple;
1387
+
1388
+ const count_limit = @divFloor(size.bytes(), @sizeOf(T));
1389
+ const count_rounded = @divFloor(
1390
+ count_limit,
1391
+ value_count_max_multiple,
1392
+ ) * value_count_max_multiple;
1393
+
1394
+ if (count_rounded > std.math.maxInt(u32)) {
1395
+ vsr.fatal(.cli, "{s}: exceeds the limit", .{cli_flag});
1396
+ }
1397
+
1398
+ const result: u32 = @intCast(count_rounded);
1399
+ assert(@as(u64, result) * @sizeOf(T) <= size.bytes());
1400
+
1401
+ return result;
1402
+ }
1403
+
1404
+ fn parse_timeout_to_ticks(timeout_ms: ?u64, cli_flag: []const u8) ?u64 {
1405
+ if (timeout_ms) |ms| {
1406
+ if (ms == 0) {
1407
+ vsr.fatal(.cli, "{s}: timeout {}ms be nonzero", .{ cli_flag, ms });
1408
+ }
1409
+
1410
+ if (ms % constants.tick_ms != 0) {
1411
+ vsr.fatal(
1412
+ .cli,
1413
+ "{s}: timeout {}ms must be a multiple of {}ms",
1414
+ .{ cli_flag, ms, constants.tick_ms },
1415
+ );
1416
+ }
1417
+
1418
+ return @divExact(ms, constants.tick_ms);
1419
+ } else {
1420
+ return null;
1421
+ }
1422
+ }