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,416 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+
4
+ const constants = @import("../constants.zig");
5
+ const stdx = @import("stdx");
6
+ const vsr = @import("../vsr.zig");
7
+ const Header = vsr.Header;
8
+ const data_file_size_min = @import("./superblock.zig").data_file_size_min;
9
+
10
+ /// Initialize the TigerBeetle replica's data file.
11
+ pub fn format(
12
+ comptime Storage: type,
13
+ gpa: std.mem.Allocator,
14
+ storage: *Storage,
15
+ options: vsr.SuperBlockType(Storage).FormatOptions,
16
+ ) !void {
17
+ const ReplicaFormat = ReplicaFormatType(Storage);
18
+ const SuperBlock = vsr.SuperBlockType(Storage);
19
+
20
+ var superblock = try SuperBlock.init(gpa, storage, .{
21
+ .storage_size_limit = data_file_size_min,
22
+ });
23
+ defer superblock.deinit(gpa);
24
+
25
+ var replica_format = try ReplicaFormat.init(gpa);
26
+ defer replica_format.deinit(gpa);
27
+
28
+ try replica_format.queue_format_wal(options.cluster, storage);
29
+ replica_format.format_and_tick(storage, &superblock, options);
30
+ replica_format.verify_writes();
31
+ }
32
+
33
+ /// When formatting, we write:
34
+ /// * constants.journal_slot_count many prepares,
35
+ /// * 1 write that contains all of the headers.
36
+ pub const writes_max = constants.journal_slot_count + 1;
37
+
38
+ fn ReplicaFormatType(comptime Storage: type) type {
39
+ const SuperBlock = vsr.SuperBlockType(Storage);
40
+ return struct {
41
+ const ReplicaFormat = @This();
42
+
43
+ const Write = struct {
44
+ write: Storage.Write = undefined,
45
+ replica_format: *ReplicaFormat,
46
+
47
+ issued_buffer: []const u8,
48
+ issued_offset: u64,
49
+ };
50
+
51
+ formatting: bool = false,
52
+ formatting_superblock: bool = false,
53
+ superblock_context: SuperBlock.Context = undefined,
54
+
55
+ writes: [writes_max]Write = undefined,
56
+ writes_pending: u64 = 0,
57
+
58
+ sectors_written: std.DynamicBitSetUnmanaged,
59
+ arena: std.heap.ArenaAllocator,
60
+
61
+ fn init(gpa: std.mem.Allocator) !ReplicaFormat {
62
+ var sectors_written = try std.DynamicBitSetUnmanaged.initEmpty(
63
+ gpa,
64
+ @divExact(data_file_size_min, constants.sector_size),
65
+ );
66
+ errdefer sectors_written.deinit(gpa);
67
+
68
+ var arena = std.heap.ArenaAllocator.init(gpa);
69
+ errdefer arena.deinit();
70
+
71
+ return .{
72
+ .sectors_written = sectors_written,
73
+ .arena = arena,
74
+ };
75
+ }
76
+
77
+ fn deinit(self: *ReplicaFormat, gpa: std.mem.Allocator) void {
78
+ self.arena.deinit();
79
+ self.sectors_written.deinit(gpa);
80
+ }
81
+
82
+ fn queue_format_wal(
83
+ self: *ReplicaFormat,
84
+ cluster: u128,
85
+ storage: *Storage,
86
+ ) !void {
87
+ assert(!self.formatting and !self.formatting_superblock);
88
+
89
+ const arena = self.arena.allocator();
90
+
91
+ // The logical offset *within the Zone*.
92
+ // Even though the prepare zone follows the redundant header zone, write the prepares
93
+ // first. This allows the test Storage to check the invariant "never write the redundant
94
+ // header before the prepare".
95
+ for (0..constants.journal_slot_count) |slot| {
96
+ // Direct I/O requires the buffer to be sector-aligned. Allocate a buffer for each
97
+ // sector in the arena, so they can be written concurrently.
98
+ const header_buffer = try arena.alignedAlloc(
99
+ u8,
100
+ constants.sector_size,
101
+ constants.sector_size,
102
+ );
103
+ const header: *Header.Prepare = std.mem.bytesAsValue(
104
+ Header.Prepare,
105
+ header_buffer,
106
+ );
107
+ header.* = slot_header(cluster, slot);
108
+ assert(header.valid_checksum());
109
+
110
+ const prepare_offset = slot * constants.message_size_max;
111
+ assert(prepare_offset <= constants.journal_size_prepares);
112
+ assert(prepare_offset % @sizeOf(Header) == 0);
113
+ assert(prepare_offset % constants.sector_size == 0);
114
+
115
+ // Zero padding to produce identical checksums of an empty datafile, not because
116
+ // it's required for correctness.
117
+ const header_padding = header_buffer[@sizeOf(Header.Prepare)..];
118
+ @memset(header_padding, 0);
119
+ assert(stdx.zeroed(header_padding));
120
+
121
+ if (header.op == 0) {
122
+ assert(header.operation == .root);
123
+ } else {
124
+ assert(header.operation == .reserved);
125
+ }
126
+
127
+ self.writes[self.writes_pending] = .{
128
+ .replica_format = self,
129
+ .issued_buffer = header_buffer,
130
+ .issued_offset = prepare_offset,
131
+ };
132
+
133
+ storage.write_sectors(
134
+ write_sectors_callback,
135
+ &self.writes[self.writes_pending].write,
136
+ header_buffer,
137
+ .wal_prepares,
138
+ prepare_offset,
139
+ );
140
+ self.writes_pending += 1;
141
+ }
142
+
143
+ // Direct I/O requires the buffer to be sector-aligned. Unlike the Prepares above that
144
+ // require a buffer per prepare, since they are spread out with zeros inbetween, the
145
+ // headers zone is contiguous so a single buffer will do.
146
+ //
147
+ // There might be padding, so allocate []u8 instead of []Header.Prepare.
148
+ const headers_buffer = try arena.alignedAlloc(
149
+ u8,
150
+ constants.sector_size,
151
+ vsr.sector_ceil(constants.journal_size_headers),
152
+ );
153
+
154
+ for (0..constants.journal_slot_count) |slot| {
155
+ const header_buffer =
156
+ headers_buffer[slot * @sizeOf(Header.Prepare) ..][0..@sizeOf(Header.Prepare)];
157
+ const header: *Header.Prepare = @alignCast(
158
+ std.mem.bytesAsValue(Header.Prepare, header_buffer),
159
+ );
160
+ header.* = slot_header(cluster, slot);
161
+ assert(header.valid_checksum());
162
+
163
+ if (header.op == 0) {
164
+ assert(header.operation == .root);
165
+ } else {
166
+ assert(header.operation == .reserved);
167
+ }
168
+ }
169
+
170
+ // Zero padding to produce identical checksums of an empty datafile, not because it's
171
+ // required for correctness.
172
+ const headers_padding =
173
+ headers_buffer[constants.journal_slot_count * @sizeOf(Header.Prepare) ..];
174
+ @memset(headers_padding, 0);
175
+ assert(stdx.zeroed(headers_padding));
176
+
177
+ self.writes[self.writes_pending] = .{
178
+ .replica_format = self,
179
+ .issued_buffer = headers_buffer,
180
+ .issued_offset = 0,
181
+ };
182
+ storage.write_sectors(
183
+ write_sectors_callback,
184
+ &self.writes[self.writes_pending].write,
185
+ headers_buffer,
186
+ .wal_headers,
187
+ 0,
188
+ );
189
+ self.writes_pending += 1;
190
+ }
191
+
192
+ fn format_and_tick(
193
+ self: *ReplicaFormat,
194
+ storage: *Storage,
195
+ superblock: *SuperBlock,
196
+ superblock_options: SuperBlock.FormatOptions,
197
+ ) void {
198
+ assert(self.writes_pending == writes_max);
199
+
200
+ self.formatting = true;
201
+ while (self.formatting) storage.run();
202
+
203
+ self.formatting_superblock = true;
204
+ superblock.format(
205
+ format_superblock_callback,
206
+ &self.superblock_context,
207
+ superblock_options,
208
+ );
209
+ while (self.formatting_superblock) storage.run();
210
+ }
211
+
212
+ fn write_sectors_callback(storage_write: *Storage.Write) void {
213
+ const write: *Write = @fieldParentPtr("write", storage_write);
214
+ const self = write.replica_format;
215
+
216
+ assert(self.formatting);
217
+ assert(!self.formatting_superblock);
218
+
219
+ self.writes_pending -= 1;
220
+
221
+ const sector_offset = @divExact(
222
+ storage_write.zone.offset(write.issued_offset),
223
+ constants.sector_size,
224
+ );
225
+ const sector_count = @divExact(write.issued_buffer.len, constants.sector_size);
226
+
227
+ for (sector_offset..sector_offset + sector_count) |sector| {
228
+ self.sectors_written.set(sector);
229
+ }
230
+
231
+ if (self.writes_pending == 0) {
232
+ self.formatting = false;
233
+ }
234
+ }
235
+
236
+ fn format_superblock_callback(superblock_context: *SuperBlock.Context) void {
237
+ const self: *ReplicaFormat =
238
+ @alignCast(@fieldParentPtr("superblock_context", superblock_context));
239
+ assert(!self.formatting);
240
+ assert(self.formatting_superblock);
241
+ self.formatting_superblock = false;
242
+ }
243
+
244
+ fn verify_writes(self: *ReplicaFormat) void {
245
+ assert(!self.formatting and !self.formatting_superblock);
246
+ assert(self.writes_pending == 0);
247
+
248
+ assert(self.sectors_written.count() > 0);
249
+ assert(self.sectors_written.capacity() ==
250
+ @divExact(data_file_size_min, constants.sector_size));
251
+
252
+ // Expect that:
253
+ // * every sector in the wal_headers zone has been written,
254
+ // * the first sector in every constants.message_size_max has been written,
255
+ // * nothing else has been written.
256
+ //
257
+ // This might seem to miss the superblock zone, but that's handled entirely by
258
+ // superblock.zig, which reads back the headers to validate it has been written
259
+ // correctly.
260
+ for (0..self.sectors_written.capacity()) |sector| {
261
+ const sector_start = sector * constants.sector_size;
262
+
263
+ const zone = for (std.enums.values(vsr.Zone)) |zone| {
264
+ if (sector_start >= zone.start() and
265
+ sector_start < zone.start() + zone.size().?) break zone;
266
+ } else unreachable;
267
+
268
+ switch (zone) {
269
+ // Every sector in the wal_headers zone has been written:
270
+ .wal_headers => assert(self.sectors_written.isSet(sector)),
271
+
272
+ // The first sector in every constants.message_size_max has been written:
273
+ .wal_prepares => {
274
+ if ((sector_start - zone.start()) % constants.message_size_max == 0) {
275
+ assert(self.sectors_written.isSet(sector));
276
+ } else {
277
+ assert(!self.sectors_written.isSet(sector));
278
+ }
279
+ },
280
+
281
+ // Nothing else has been written:
282
+ else => assert(!self.sectors_written.isSet(sector)),
283
+ }
284
+ }
285
+ }
286
+ };
287
+ }
288
+
289
+ pub fn slot_header(cluster: u128, slot: u64) Header.Prepare {
290
+ assert(slot < constants.journal_slot_count);
291
+ assert(slot * @sizeOf(Header.Prepare) < constants.journal_size_headers);
292
+ assert(slot * constants.message_size_max < constants.journal_size_prepares);
293
+ assert(@sizeOf(Header.Prepare) < constants.sector_size);
294
+
295
+ return if (slot == 0)
296
+ Header.Prepare.root(cluster)
297
+ else
298
+ Header.Prepare.reserve(cluster, slot);
299
+ }
300
+
301
+ test slot_header {
302
+ const allocator = std.testing.allocator;
303
+
304
+ const header_buffer = try allocator.create(Header.Prepare);
305
+ defer allocator.destroy(header_buffer);
306
+
307
+ for (0..constants.journal_slot_count) |slot| {
308
+ const header = slot_header(0, slot);
309
+
310
+ try std.testing.expect(header.valid_checksum());
311
+ try std.testing.expect(header.valid_checksum_body(&[0]u8{}));
312
+ try std.testing.expectEqual(header.invalid(), null);
313
+ try std.testing.expectEqual(header.cluster, 0);
314
+ try std.testing.expectEqual(header.op, slot);
315
+ try std.testing.expectEqual(header.size, @sizeOf(vsr.Header));
316
+ try std.testing.expectEqual(header.command, .prepare);
317
+ if (slot == 0) {
318
+ try std.testing.expectEqual(header.operation, .root);
319
+ } else {
320
+ try std.testing.expectEqual(header.operation, .reserved);
321
+ }
322
+ }
323
+ }
324
+
325
+ test "format" {
326
+ const Storage = @import("../testing/storage.zig").Storage;
327
+ const fixtures = @import("../testing/fixtures.zig");
328
+ const allocator = std.testing.allocator;
329
+ const cluster = 0;
330
+ const replica = 1;
331
+ const replica_count = 1;
332
+
333
+ var storage = try fixtures.init_storage(allocator, .{
334
+ .size = data_file_size_min,
335
+ .iops_write_max = writes_max,
336
+ });
337
+ defer storage.deinit(allocator);
338
+
339
+ try format(Storage, allocator, &storage, .{
340
+ .cluster = cluster,
341
+ .release = vsr.Release.minimum,
342
+ .replica = replica,
343
+ .replica_count = replica_count,
344
+ .view = null,
345
+ });
346
+
347
+ // Verify the superblock headers.
348
+ var copy: u8 = 0;
349
+ while (copy < constants.superblock_copies) : (copy += 1) {
350
+ const superblock_header = storage.superblock_header(copy);
351
+
352
+ try std.testing.expectEqual(superblock_header.copy, copy);
353
+ try std.testing.expectEqual(superblock_header.cluster, cluster);
354
+ try std.testing.expectEqual(superblock_header.sequence, 1);
355
+ try std.testing.expectEqual(
356
+ superblock_header.vsr_state.checkpoint.storage_size,
357
+ storage.size,
358
+ );
359
+ try std.testing.expectEqual(superblock_header.vsr_state.checkpoint.header.op, 0);
360
+ try std.testing.expectEqual(superblock_header.vsr_state.commit_max, 0);
361
+ try std.testing.expectEqual(superblock_header.vsr_state.view, 0);
362
+ try std.testing.expectEqual(superblock_header.vsr_state.log_view, 0);
363
+ try std.testing.expectEqual(
364
+ superblock_header.vsr_state.replica_id,
365
+ superblock_header.vsr_state.members[replica],
366
+ );
367
+ try std.testing.expectEqual(superblock_header.vsr_state.replica_count, replica_count);
368
+ }
369
+
370
+ // Verify the WAL headers and prepares zones.
371
+ for (storage.wal_headers(), storage.wal_prepares(), 0..) |header, *message, slot| {
372
+ try std.testing.expect(std.meta.eql(header, message.header));
373
+
374
+ try std.testing.expect(header.valid_checksum());
375
+ try std.testing.expect(header.valid_checksum_body(&[0]u8{}));
376
+ try std.testing.expectEqual(header.invalid(), null);
377
+ try std.testing.expectEqual(header.cluster, cluster);
378
+ try std.testing.expectEqual(header.op, slot);
379
+ try std.testing.expectEqual(header.size, @sizeOf(vsr.Header));
380
+ try std.testing.expectEqual(header.command, .prepare);
381
+ if (slot == 0) {
382
+ try std.testing.expectEqual(header.operation, .root);
383
+ } else {
384
+ try std.testing.expectEqual(header.operation, .reserved);
385
+ }
386
+ }
387
+
388
+ // Verify client replies. The contents are not zeroed.
389
+ try std.testing.expectEqual(storage.client_replies().len, constants.clients_max);
390
+
391
+ // Verify grid alignment. The contents of the padding are not zeroed.
392
+ try std.testing.expect(vsr.Zone.grid.start() % constants.sector_size == 0);
393
+
394
+ // Explicitly zero client_replies and the grid padding. This is not required for formatting, but
395
+ // it allows for easy checksums of the entire testing storage.
396
+ @memset(
397
+ storage.memory[vsr.Zone.client_replies.offset(0)..][0..vsr.Zone.client_replies.size().?],
398
+ 0,
399
+ );
400
+ if (vsr.Zone.grid_padding.size().? > 0) {
401
+ @memset(
402
+ storage.memory[vsr.Zone.grid_padding.offset(0)..][0..vsr.Zone.grid_padding.size().?],
403
+ 0,
404
+ );
405
+ }
406
+
407
+ // Lastly, verify the entire storage contents against a known good checksum for the given
408
+ // cluster, replica and replica count.
409
+ //
410
+ // This doesn't match the output from `tigerbeetle format ...` since the testing storage / slot
411
+ // counts are lower.
412
+ try std.testing.expectEqual(
413
+ vsr.checksum(storage.memory),
414
+ 339529914272821912685300045374558551362,
415
+ );
416
+ }
@@ -0,0 +1,165 @@
1
+ //! Replica recovery: Format a data file to replace one which was permanently lost.
2
+ //!
3
+ //! 1. The recovery process send `pipeline_prepare_queue_max` requests (1 register + many noops) to
4
+ //! the cluster.
5
+ //! 2. Once those have committed, it creates the new data file. The data file is identical to
6
+ //! `tigerbeetle format`'s output *except* that `vsr_state.view == client.view + 2` (where
7
+ //! `client.view` is the view number of the client at the end of committing the requests).
8
+ //! 3. The recovery process exits. Now running `tigerbeetle start` as normal will work.
9
+ //!
10
+ //! The `pipeline_prepare_queue_max` committed requests ensure that if the newly recovered replica
11
+ //! nacks uncommitted ops via a DVC message, it is nacking ops which were definitely not received by
12
+ //! the previous version of the replica.
13
+ //!
14
+ //! The +2 is because:
15
+ //! - We don't want to join in the same view, since the replica might have participated in it before
16
+ //! being lost, and we can't remember any promises we made.
17
+ //! - Likewise, we don't want to go to view + 1 -- if we were the first to collect a SVC quorum
18
+ //! before being lost, we might have sent a DVC. Since we don't remember, we must skip past
19
+ //! `view + 11 to ensure that we don't send a different DVC. (We have the invariant that if a
20
+ //! replica sends a DVC for a given view, then all DVC's it sends for that view will be
21
+ //! identical.)
22
+ const std = @import("std");
23
+ const assert = std.debug.assert;
24
+
25
+ const constants = @import("../constants.zig");
26
+ const vsr = @import("../vsr.zig");
27
+ const format = @import("./replica_format.zig").format;
28
+
29
+ const log = std.log.scoped(.reformat);
30
+
31
+ pub fn ReplicaReformatType(
32
+ comptime StateMachine: type,
33
+ comptime MessageBus: type,
34
+ comptime Storage: type,
35
+ ) type {
36
+ const Client = vsr.ClientType(StateMachine.Operation, MessageBus);
37
+ const SuperBlock = vsr.SuperBlockType(Storage);
38
+
39
+ return struct {
40
+ const ReplicaReformat = @This();
41
+
42
+ const Result = union(enum) {
43
+ failed: anyerror,
44
+ ok,
45
+ };
46
+
47
+ allocator: std.mem.Allocator,
48
+ options: SuperBlock.FormatOptions,
49
+ client: *Client,
50
+ storage: *Storage,
51
+
52
+ requests_done: u32 = 0,
53
+ result: ?Result = null,
54
+
55
+ pub fn init(
56
+ allocator: std.mem.Allocator,
57
+ client: *Client,
58
+ storage: *Storage,
59
+ options: SuperBlock.FormatOptions,
60
+ ) !ReplicaReformat {
61
+ assert(options.view == null);
62
+ assert(options.replica_count >= 3);
63
+
64
+ return .{
65
+ .allocator = allocator,
66
+ .options = options,
67
+ .client = client,
68
+ .storage = storage,
69
+ };
70
+ }
71
+
72
+ pub fn deinit(reformat: *ReplicaReformat, allocator: std.mem.Allocator) void {
73
+ _ = reformat;
74
+ _ = allocator;
75
+ }
76
+ pub fn done(reformat: *const ReplicaReformat) ?Result {
77
+ assert(reformat.requests_done <= constants.pipeline_prepare_queue_max);
78
+ return reformat.result;
79
+ }
80
+
81
+ pub fn start(reformat: *ReplicaReformat) void {
82
+ assert(reformat.requests_done == 0);
83
+ const user_data = @intFromPtr(reformat);
84
+ reformat.client.register(client_register_callback, user_data);
85
+ }
86
+
87
+ fn client_register_callback(
88
+ user_data: u128,
89
+ register_result: *const vsr.RegisterResult,
90
+ ) void {
91
+ _ = register_result;
92
+ const reformat: *ReplicaReformat = @ptrFromInt(@as(usize, @intCast(user_data)));
93
+ assert(reformat.requests_done == 0);
94
+
95
+ log.debug("{}: register", .{reformat.options.replica});
96
+
97
+ reformat.requests_done += 1;
98
+ reformat.client_request();
99
+ }
100
+
101
+ fn client_request(reformat: *ReplicaReformat) void {
102
+ assert(reformat.requests_done < constants.pipeline_prepare_queue_max);
103
+
104
+ log.debug("{}: request start={}", .{
105
+ reformat.options.replica,
106
+ reformat.requests_done,
107
+ });
108
+
109
+ const message = reformat.client.get_message().build(.request);
110
+ errdefer reformat.client.release_message(message.base());
111
+
112
+ message.header.* = .{
113
+ .client = reformat.client.id,
114
+ .request = 0, // Set inside `raw_request`.
115
+ .cluster = reformat.client.cluster,
116
+ .command = .request,
117
+ .release = reformat.client.release,
118
+ .operation = .noop,
119
+ .size = @sizeOf(vsr.Header),
120
+ .previous_request_latency = 0,
121
+ };
122
+
123
+ const user_data = @intFromPtr(reformat);
124
+ reformat.client.raw_request(client_request_callback, user_data, message);
125
+ }
126
+
127
+ fn client_request_callback(
128
+ user_data: u128,
129
+ operation: vsr.Operation,
130
+ timestamp: u64,
131
+ results: []u8,
132
+ ) void {
133
+ assert(operation == .noop);
134
+ assert(timestamp > 0);
135
+
136
+ const reformat: *ReplicaReformat = @ptrFromInt(@as(usize, @intCast(user_data)));
137
+ assert(reformat.requests_done > 0);
138
+ assert(reformat.requests_done < constants.pipeline_prepare_queue_max);
139
+ assert(results.len == 0);
140
+
141
+ log.debug("{}: request done={}", .{
142
+ reformat.options.replica,
143
+ reformat.requests_done,
144
+ });
145
+
146
+ reformat.requests_done += 1;
147
+ if (reformat.requests_done == constants.pipeline_prepare_queue_max) {
148
+ // +2 since we might have sent a DVC as part of +1 before we crashed.
149
+ reformat.options.view = reformat.client.view + 2;
150
+ format(
151
+ Storage,
152
+ reformat.allocator,
153
+ reformat.storage,
154
+ reformat.options,
155
+ ) catch |err| {
156
+ reformat.result = .{ .failed = err };
157
+ return;
158
+ };
159
+ reformat.result = .ok;
160
+ } else {
161
+ reformat.client_request();
162
+ }
163
+ }
164
+ };
165
+ }