tigerbeetle 0.0.40 → 0.17.8

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 (293) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +0 -25
  3. data/README.md +670 -80
  4. data/docs/migration.md +201 -0
  5. data/sig/tigerbeetle.rbs +271 -0
  6. data/src/ext/tigerbeetle/extconf.rb +47 -0
  7. data/src/ext/tigerbeetle/lib/aarch64-linux-gnu.2.27/libtb_client.so +0 -0
  8. data/src/ext/tigerbeetle/lib/aarch64-linux-musl/libtb_client.so +0 -0
  9. data/src/ext/tigerbeetle/lib/aarch64-macos/libtb_client.dylib +0 -0
  10. data/src/ext/tigerbeetle/lib/x86_64-linux-gnu.2.27/libtb_client.so +0 -0
  11. data/src/ext/tigerbeetle/lib/x86_64-linux-musl/libtb_client.so +0 -0
  12. data/src/ext/tigerbeetle/lib/x86_64-macos/libtb_client.dylib +0 -0
  13. data/src/ext/tigerbeetle/lib/x86_64-windows/tb_client.dll +0 -0
  14. data/src/ext/tigerbeetle/rb_tb_gen.h +458 -0
  15. data/{ext/tb_client/tigerbeetle/src/clients/rust/assets → src/ext/tigerbeetle}/tb_client.h +18 -16
  16. data/src/ext/tigerbeetle/tigerbeetle.c +310 -0
  17. data/src/tigerbeetle/bindings.rb +347 -0
  18. data/src/tigerbeetle/client.rb +129 -0
  19. data/src/tigerbeetle/completion_dispatcher.rb +108 -0
  20. data/src/tigerbeetle/id.rb +40 -0
  21. data/src/tigerbeetle/tb.rb +3 -0
  22. data/src/tigerbeetle/version.rb +3 -0
  23. data/src/tigerbeetle.rb +39 -0
  24. metadata +33 -350
  25. data/CHANGELOG.md +0 -162
  26. data/ext/tb_client/extconf.rb +0 -41
  27. data/ext/tb_client/tigerbeetle/LICENSE +0 -177
  28. data/ext/tb_client/tigerbeetle/build.zig +0 -2296
  29. data/ext/tb_client/tigerbeetle/src/aof.zig +0 -1000
  30. data/ext/tb_client/tigerbeetle/src/build/fetch.zig +0 -112
  31. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +0 -808
  32. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +0 -1283
  33. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +0 -1704
  34. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +0 -341
  35. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +0 -1450
  36. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +0 -1659
  37. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +0 -406
  38. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +0 -1092
  39. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +0 -286
  40. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +0 -158
  41. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +0 -229
  42. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +0 -110
  43. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +0 -386
  44. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +0 -34
  45. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +0 -281
  46. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +0 -312
  47. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +0 -138
  48. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +0 -466
  49. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +0 -157
  50. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +0 -90
  51. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +0 -203
  52. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +0 -79
  53. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +0 -542
  54. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +0 -109
  55. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +0 -86
  56. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +0 -370
  57. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +0 -386
  58. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +0 -167
  59. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +0 -126
  60. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +0 -996
  61. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +0 -748
  62. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +0 -3238
  63. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +0 -1718
  64. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +0 -190
  65. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +0 -104
  66. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +0 -75
  67. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +0 -522
  68. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +0 -267
  69. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +0 -3
  70. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +0 -379
  71. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +0 -131
  72. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +0 -63
  73. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +0 -588
  74. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +0 -73
  75. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +0 -106
  76. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +0 -305
  77. data/ext/tb_client/tigerbeetle/src/config.zig +0 -296
  78. data/ext/tb_client/tigerbeetle/src/constants.zig +0 -790
  79. data/ext/tb_client/tigerbeetle/src/copyhound.zig +0 -202
  80. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +0 -72
  81. data/ext/tb_client/tigerbeetle/src/direction.zig +0 -120
  82. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +0 -158
  83. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +0 -156
  84. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +0 -252
  85. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +0 -313
  86. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +0 -87
  87. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +0 -63
  88. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +0 -47
  89. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +0 -28
  90. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +0 -61
  91. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +0 -169
  92. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +0 -46
  93. data/ext/tb_client/tigerbeetle/src/ewah.zig +0 -445
  94. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +0 -128
  95. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +0 -171
  96. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +0 -179
  97. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +0 -662
  98. data/ext/tb_client/tigerbeetle/src/io/common.zig +0 -155
  99. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +0 -1093
  100. data/ext/tb_client/tigerbeetle/src/io/linux.zig +0 -1880
  101. data/ext/tb_client/tigerbeetle/src/io/test.zig +0 -1005
  102. data/ext/tb_client/tigerbeetle/src/io/windows.zig +0 -1598
  103. data/ext/tb_client/tigerbeetle/src/io.zig +0 -34
  104. data/ext/tb_client/tigerbeetle/src/iops.zig +0 -134
  105. data/ext/tb_client/tigerbeetle/src/list.zig +0 -236
  106. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +0 -848
  107. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +0 -179
  108. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +0 -424
  109. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +0 -420
  110. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +0 -2114
  111. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +0 -185
  112. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +0 -1146
  113. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +0 -1102
  114. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +0 -200
  115. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +0 -1495
  116. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +0 -739
  117. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +0 -166
  118. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +0 -754
  119. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +0 -1294
  120. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +0 -510
  121. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +0 -1241
  122. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -628
  123. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +0 -247
  124. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +0 -116
  125. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +0 -543
  126. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +0 -938
  127. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +0 -293
  128. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +0 -359
  129. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +0 -99
  130. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +0 -17
  131. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +0 -962
  132. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +0 -617
  133. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +0 -84
  134. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +0 -1500
  135. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -149
  136. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -7
  137. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +0 -865
  138. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +0 -607
  139. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +0 -843
  140. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +0 -90
  141. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +0 -40
  142. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +0 -629
  143. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +0 -933
  144. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +0 -534
  145. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +0 -469
  146. data/ext/tb_client/tigerbeetle/src/message_bus.zig +0 -1219
  147. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +0 -936
  148. data/ext/tb_client/tigerbeetle/src/message_pool.zig +0 -343
  149. data/ext/tb_client/tigerbeetle/src/multiversion.zig +0 -2195
  150. data/ext/tb_client/tigerbeetle/src/queue.zig +0 -390
  151. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +0 -201
  152. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +0 -1356
  153. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +0 -496
  154. data/ext/tb_client/tigerbeetle/src/repl.zig +0 -1034
  155. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +0 -973
  156. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +0 -1866
  157. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +0 -304
  158. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +0 -227
  159. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +0 -658
  160. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +0 -466
  161. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +0 -1058
  162. data/ext/tb_client/tigerbeetle/src/scripts.zig +0 -105
  163. data/ext/tb_client/tigerbeetle/src/shell.zig +0 -1195
  164. data/ext/tb_client/tigerbeetle/src/stack.zig +0 -260
  165. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +0 -911
  166. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +0 -2079
  167. data/ext/tb_client/tigerbeetle/src/state_machine.zig +0 -4872
  168. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +0 -288
  169. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +0 -3128
  170. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +0 -82
  171. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +0 -157
  172. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +0 -292
  173. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +0 -65
  174. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +0 -1414
  175. data/ext/tb_client/tigerbeetle/src/stdx/huge_page_allocator.zig +0 -115
  176. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +0 -92
  177. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +0 -677
  178. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +0 -336
  179. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +0 -511
  180. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +0 -112
  181. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +0 -1163
  182. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +0 -142
  183. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +0 -361
  184. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +0 -275
  185. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +0 -295
  186. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +0 -436
  187. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +0 -48
  188. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +0 -402
  189. data/ext/tb_client/tigerbeetle/src/storage.zig +0 -489
  190. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +0 -180
  191. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +0 -146
  192. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +0 -53
  193. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +0 -61
  194. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +0 -76
  195. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +0 -110
  196. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +0 -412
  197. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +0 -331
  198. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -458
  199. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +0 -1198
  200. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +0 -128
  201. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +0 -181
  202. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +0 -144
  203. data/ext/tb_client/tigerbeetle/src/testing/id.zig +0 -97
  204. data/ext/tb_client/tigerbeetle/src/testing/io.zig +0 -317
  205. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +0 -126
  206. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +0 -533
  207. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +0 -154
  208. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +0 -389
  209. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +0 -1247
  210. data/ext/tb_client/tigerbeetle/src/testing/table.zig +0 -249
  211. data/ext/tb_client/tigerbeetle/src/testing/time.zig +0 -98
  212. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +0 -212
  213. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +0 -26
  214. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +0 -579
  215. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +0 -39
  216. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +0 -214
  217. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +0 -34
  218. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +0 -785
  219. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +0 -543
  220. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +0 -181
  221. data/ext/tb_client/tigerbeetle/src/tidy.zig +0 -1449
  222. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +0 -227
  223. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +0 -1069
  224. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +0 -1422
  225. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +0 -1658
  226. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +0 -518
  227. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +0 -36
  228. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +0 -646
  229. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +0 -958
  230. data/ext/tb_client/tigerbeetle/src/time.zig +0 -236
  231. data/ext/tb_client/tigerbeetle/src/trace/event.zig +0 -745
  232. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +0 -462
  233. data/ext/tb_client/tigerbeetle/src/trace.zig +0 -556
  234. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +0 -321
  235. data/ext/tb_client/tigerbeetle/src/vopr.zig +0 -1785
  236. data/ext/tb_client/tigerbeetle/src/vortex.zig +0 -101
  237. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +0 -473
  238. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +0 -208
  239. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +0 -43
  240. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +0 -768
  241. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +0 -532
  242. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +0 -338
  243. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +0 -1019
  244. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +0 -279
  245. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +0 -1381
  246. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +0 -315
  247. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +0 -1460
  248. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +0 -757
  249. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +0 -797
  250. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +0 -2586
  251. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +0 -308
  252. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +0 -1777
  253. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +0 -715
  254. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +0 -185
  255. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +0 -333
  256. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +0 -12356
  257. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +0 -416
  258. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +0 -165
  259. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +0 -2928
  260. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +0 -1075
  261. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +0 -1603
  262. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -484
  263. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +0 -405
  264. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -355
  265. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +0 -29
  266. data/ext/tb_client/tigerbeetle/src/vsr.zig +0 -1727
  267. data/lib/tb_client/shared_lib.rb +0 -66
  268. data/lib/tb_client.rb +0 -282
  269. data/lib/tigerbeetle/account.rb +0 -38
  270. data/lib/tigerbeetle/account_balance.rb +0 -23
  271. data/lib/tigerbeetle/account_filter.rb +0 -31
  272. data/lib/tigerbeetle/atomic_counter.rb +0 -14
  273. data/lib/tigerbeetle/client.rb +0 -214
  274. data/lib/tigerbeetle/converters/account.rb +0 -63
  275. data/lib/tigerbeetle/converters/account_balance.rb +0 -31
  276. data/lib/tigerbeetle/converters/account_filter.rb +0 -32
  277. data/lib/tigerbeetle/converters/base.rb +0 -35
  278. data/lib/tigerbeetle/converters/create_accounts_result.rb +0 -21
  279. data/lib/tigerbeetle/converters/create_transfers_result.rb +0 -21
  280. data/lib/tigerbeetle/converters/query_filter.rb +0 -33
  281. data/lib/tigerbeetle/converters/time.rb +0 -23
  282. data/lib/tigerbeetle/converters/transfer.rb +0 -64
  283. data/lib/tigerbeetle/converters/uint_128.rb +0 -24
  284. data/lib/tigerbeetle/converters.rb +0 -12
  285. data/lib/tigerbeetle/error.rb +0 -4
  286. data/lib/tigerbeetle/id.rb +0 -30
  287. data/lib/tigerbeetle/platforms.rb +0 -9
  288. data/lib/tigerbeetle/query_filter.rb +0 -31
  289. data/lib/tigerbeetle/request.rb +0 -7
  290. data/lib/tigerbeetle/transfer.rb +0 -40
  291. data/lib/tigerbeetle/version.rb +0 -4
  292. data/lib/tigerbeetle.rb +0 -13
  293. data/tigerbeetle.gemspec +0 -60
@@ -1,1146 +0,0 @@
1
- const std = @import("std");
2
- const builtin = @import("builtin");
3
- const assert = std.debug.assert;
4
- const maybe = stdx.maybe;
5
- const mem = std.mem;
6
- const log = std.log.scoped(.forest);
7
-
8
- const stdx = @import("stdx");
9
- const constants = @import("../constants.zig");
10
-
11
- const schema = @import("schema.zig");
12
- const GridType = @import("../vsr/grid.zig").GridType;
13
- const NodePool = @import("node_pool.zig").NodePoolType(constants.lsm_manifest_node_size, 16);
14
- const ManifestLogType = @import("manifest_log.zig").ManifestLogType;
15
- const ManifestLogPace = @import("manifest_log.zig").Pace;
16
-
17
- const ScratchMemory = @import("scratch_memory.zig").ScratchMemory;
18
- const ScanBufferPool = @import("scan_buffer.zig").ScanBufferPool;
19
- const ResourcePoolType = @import("compaction.zig").ResourcePoolType;
20
- const snapshot_min_for_table_output = @import("compaction.zig").snapshot_min_for_table_output;
21
- const compaction_op_min = @import("compaction.zig").compaction_op_min;
22
- const compaction_block_count_beat_min = @import("compaction.zig").compaction_block_count_beat_min;
23
- const compaction_input_tables_max = @import("compaction.zig").compaction_tables_input_max;
24
-
25
- /// The maximum number of tables for the forest as a whole. This is set a bit backwards due to how
26
- /// the code is structured: a single tree should be able to use all the tables in the forest, so the
27
- /// table_count_max of the forest is equal to the table_count_max of a single tree.
28
- /// In future, Forest.table_count_max could exceed Tree.table_count_max.
29
- pub const table_count_max = @import("tree.zig").table_count_max;
30
-
31
- pub fn ForestType(comptime _Storage: type, comptime groove_cfg: anytype) type {
32
- const groove_count = std.meta.fields(@TypeOf(groove_cfg)).len;
33
- var groove_fields: [groove_count]std.builtin.Type.StructField = undefined;
34
- var groove_options_fields: [groove_count]std.builtin.Type.StructField = undefined;
35
-
36
- for (std.meta.fields(@TypeOf(groove_cfg)), 0..) |field, i| {
37
- const Groove = @field(groove_cfg, field.name);
38
- groove_fields[i] = .{
39
- .name = field.name,
40
- .type = Groove,
41
- .default_value_ptr = null,
42
- .is_comptime = false,
43
- .alignment = @alignOf(Groove),
44
- };
45
-
46
- groove_options_fields[i] = .{
47
- .name = field.name,
48
- .type = Groove.Options,
49
- .default_value_ptr = null,
50
- .is_comptime = false,
51
- .alignment = @alignOf(Groove),
52
- };
53
- }
54
-
55
- const _Grooves = @Type(.{
56
- .@"struct" = .{
57
- .layout = .auto,
58
- .fields = &groove_fields,
59
- .decls = &.{},
60
- .is_tuple = false,
61
- },
62
- });
63
-
64
- const _GroovesOptions = @Type(.{
65
- .@"struct" = .{
66
- .layout = .auto,
67
- .fields = &groove_options_fields,
68
- .decls = &.{},
69
- .is_tuple = false,
70
- },
71
- });
72
-
73
- {
74
- // Verify that every tree id is unique.
75
- comptime var ids: []const u16 = &.{};
76
-
77
- inline for (std.meta.fields(_Grooves)) |groove_field| {
78
- const Groove = groove_field.type;
79
-
80
- for (std.meta.fields(@TypeOf(Groove.config.ids))) |field| {
81
- const id = @field(Groove.config.ids, field.name);
82
- assert(id > 0);
83
- assert(std.mem.indexOfScalar(u16, ids, id) == null);
84
-
85
- ids = ids ++ [_]u16{id};
86
- }
87
- }
88
- }
89
-
90
- const TreeInfo = struct {
91
- Tree: type,
92
- tree_name: []const u8,
93
- tree_id: u16,
94
- groove_name: []const u8,
95
- groove_tree: union(enum) { objects, ids, indexes: []const u8 },
96
- };
97
-
98
- // Invariants:
99
- // - tree_infos[tree_id - tree_id_range.min].tree_id == tree_id
100
- // - tree_infos.len == tree_id_range.max - tree_id_range.min
101
- const _tree_infos = tree_infos: {
102
- @setEvalBranchQuota(32_000);
103
-
104
- var tree_infos: []const TreeInfo = &[_]TreeInfo{};
105
- for (std.meta.fields(_Grooves)) |groove_field| {
106
- const Groove = groove_field.type;
107
-
108
- tree_infos = tree_infos ++ &[_]TreeInfo{.{
109
- .Tree = Groove.ObjectTree,
110
- .tree_name = groove_field.name,
111
- .tree_id = @field(Groove.config.ids, "timestamp"),
112
- .groove_name = groove_field.name,
113
- .groove_tree = .objects,
114
- }};
115
-
116
- if (Groove.IdTree != void) {
117
- tree_infos = tree_infos ++ &[_]TreeInfo{.{
118
- .Tree = Groove.IdTree,
119
- .tree_name = groove_field.name ++ ".id",
120
- .tree_id = @field(Groove.config.ids, "id"),
121
- .groove_name = groove_field.name,
122
- .groove_tree = .ids,
123
- }};
124
- }
125
-
126
- for (std.meta.fields(Groove.IndexTrees)) |tree_field| {
127
- tree_infos = tree_infos ++ &[_]TreeInfo{.{
128
- .Tree = tree_field.type,
129
- .tree_name = groove_field.name ++ "." ++ tree_field.name,
130
- .tree_id = @field(Groove.config.ids, tree_field.name),
131
- .groove_name = groove_field.name,
132
- .groove_tree = .{ .indexes = tree_field.name },
133
- }};
134
- }
135
- }
136
-
137
- var tree_id_min = std.math.maxInt(u16);
138
- for (tree_infos) |tree_info| tree_id_min = @min(tree_id_min, tree_info.tree_id);
139
-
140
- var tree_infos_sorted: [tree_infos.len]TreeInfo = undefined;
141
- var tree_infos_set: stdx.BitSetType(tree_infos.len) = .{};
142
- for (tree_infos) |tree_info| {
143
- const tree_index = tree_info.tree_id - tree_id_min;
144
- assert(!tree_infos_set.is_set(tree_index));
145
-
146
- tree_infos_sorted[tree_index] = tree_info;
147
- tree_infos_set.set(tree_index);
148
- }
149
-
150
- // There are no gaps in the tree ids.
151
- assert(tree_infos_set.full());
152
-
153
- break :tree_infos tree_infos_sorted;
154
- };
155
-
156
- const _TreeID = comptime tree_id: {
157
- var fields: [_tree_infos.len]std.builtin.Type.EnumField = undefined;
158
- for (_tree_infos, 0..) |tree_info, i| {
159
- fields[i] = .{
160
- .name = @ptrCast(tree_info.tree_name),
161
- .value = tree_info.tree_id,
162
- };
163
- }
164
- break :tree_id @Type(.{ .@"enum" = .{
165
- .tag_type = u16,
166
- .fields = &fields,
167
- .decls = &.{},
168
- .is_exhaustive = true,
169
- } });
170
- };
171
-
172
- comptime {
173
- assert(std.enums.values(_TreeID).len == _tree_infos.len);
174
- for (std.enums.values(_TreeID)) |tree_id| {
175
- const tree_info = _tree_infos[@intFromEnum(tree_id) - _tree_infos[0].tree_id];
176
- assert(tree_id == @as(_TreeID, @enumFromInt(tree_info.tree_id)));
177
- }
178
- }
179
-
180
- const Grid = GridType(_Storage);
181
-
182
- return struct {
183
- const Forest = @This();
184
-
185
- pub const ManifestLog = ManifestLogType(Storage);
186
- const CompactionSchedule = CompactionScheduleType(Forest, Grid);
187
-
188
- const Callback = *const fn (*Forest) void;
189
-
190
- pub const Storage = _Storage;
191
- pub const groove_config = groove_cfg;
192
- pub const Grooves = _Grooves;
193
- pub const GroovesOptions = _GroovesOptions;
194
- // TreeID is an enum with a value for each tree type.
195
- // Individual trees use `u16` to store their own id, to avoid dependency on the entire
196
- // forest.
197
- // Use `tree_id_cast` function to convert this type-erased u16 to a TreeID.
198
- pub const TreeID = _TreeID;
199
- pub const tree_infos = _tree_infos;
200
- pub const tree_id_range = .{
201
- .min = tree_infos[0].tree_id,
202
- .max = tree_infos[tree_infos.len - 1].tree_id,
203
- };
204
-
205
- const manifest_log_compaction_pace = ManifestLogPace.init(.{
206
- .tree_count = tree_infos.len,
207
- // TODO Make this a runtime argument (from the CLI, derived from storage-size-max if
208
- // possible).
209
- .tables_max = table_count_max,
210
- .compact_extra_blocks = constants.lsm_manifest_compact_extra_blocks,
211
- });
212
- pub const manifest_log_blocks_released_half_bar_max =
213
- manifest_log_compaction_pace.half_bar_compact_blocks_max;
214
-
215
- pub const Options = struct {
216
- node_count: u32,
217
- /// The amount of blocks allocated for compactions. Compactions will be deterministic
218
- /// regardless of how much blocks you give them, but will run in fewer steps with more
219
- /// memory.
220
- compaction_block_count: u32,
221
-
222
- pub const compaction_block_count_min: u32 = compaction_block_count_beat_min;
223
- };
224
-
225
- progress: ?union(enum) {
226
- open: struct { callback: Callback },
227
- checkpoint: struct { callback: Callback },
228
- compact: struct {
229
- op: u64,
230
- callback: Callback,
231
- },
232
- } = null,
233
-
234
- compaction_progress: ?struct {
235
- trees_done: bool,
236
- manifest_log_done: bool,
237
-
238
- fn all_done(compaction_progress: @This()) bool {
239
- return compaction_progress.trees_done and compaction_progress.manifest_log_done;
240
- }
241
- } = null,
242
-
243
- grid: *Grid,
244
- next_tick: Grid.NextTick = undefined,
245
-
246
- grooves: Grooves,
247
- node_pool: NodePool,
248
- manifest_log: ManifestLog,
249
-
250
- compaction_schedule: CompactionSchedule,
251
-
252
- scan_buffer_pool: ScanBufferPool,
253
-
254
- radix_buffer: ScratchMemory,
255
-
256
- pub fn init(
257
- forest: *Forest,
258
- allocator: mem.Allocator,
259
- grid: *Grid,
260
- options: Options,
261
- // (e.g.) .{ .transfers = .{ .cache_entries_max = 128, … }, .accounts = … }
262
- grooves_options: GroovesOptions,
263
- ) !void {
264
- assert(options.compaction_block_count >= Options.compaction_block_count_min);
265
- forest.* = .{
266
- .grid = grid,
267
- .grooves = undefined,
268
- .node_pool = undefined,
269
- .manifest_log = undefined,
270
- .compaction_schedule = undefined,
271
- .scan_buffer_pool = undefined,
272
- .radix_buffer = undefined,
273
- };
274
-
275
- // TODO: look into using lsm_table_size_max for the node_count.
276
- try forest.node_pool.init(allocator, options.node_count);
277
- errdefer forest.node_pool.deinit(allocator);
278
-
279
- try forest.manifest_log.init(
280
- allocator,
281
- grid,
282
- &manifest_log_compaction_pace,
283
- );
284
- errdefer forest.manifest_log.deinit(allocator);
285
-
286
- var grooves_initialized: usize = 0;
287
- errdefer inline for (std.meta.fields(Grooves), 0..) |field, field_index| {
288
- if (grooves_initialized >= field_index + 1) {
289
- const Groove = field.type;
290
- const groove: *Groove = &@field(forest.grooves, field.name);
291
- groove.deinit(allocator);
292
- }
293
- };
294
-
295
- const radix_buffer_size: usize = comptime blk: {
296
- var size_max: usize = 0;
297
- for (std.enums.values(_TreeID)) |tree_id| {
298
- const tree = _tree_infos[@intFromEnum(tree_id) - _tree_infos[0].tree_id];
299
- const size = tree.Tree.Table.value_count_max * @sizeOf(tree.Tree.Value);
300
- assert(size > 0);
301
- size_max = @max(size_max, size);
302
- }
303
- break :blk size_max;
304
- };
305
-
306
- forest.radix_buffer = try .init(allocator, radix_buffer_size);
307
- errdefer forest.radix_buffer.deinit(allocator);
308
-
309
- inline for (std.meta.fields(Grooves)) |field| {
310
- const Groove = field.type;
311
- const groove: *Groove = &@field(forest.grooves, field.name);
312
- const groove_options: Groove.Options = @field(grooves_options, field.name);
313
-
314
- try groove.init(
315
- allocator,
316
- &forest.node_pool,
317
- grid,
318
- &forest.radix_buffer,
319
- groove_options,
320
- );
321
- grooves_initialized += 1;
322
- }
323
-
324
- try forest.compaction_schedule.init(
325
- allocator,
326
- grid,
327
- forest,
328
- options.compaction_block_count,
329
- );
330
- errdefer forest.compaction_schedule.deinit(allocator);
331
-
332
- try forest.scan_buffer_pool.init(allocator);
333
- errdefer forest.scan_buffer_pool.deinit(allocator);
334
- }
335
-
336
- pub fn deinit(forest: *Forest, allocator: mem.Allocator) void {
337
- inline for (std.meta.fields(Grooves)) |field| {
338
- const Groove = field.type;
339
- const groove: *Groove = &@field(forest.grooves, field.name);
340
- groove.deinit(allocator);
341
- }
342
-
343
- forest.manifest_log.deinit(allocator);
344
- forest.node_pool.deinit(allocator);
345
-
346
- forest.radix_buffer.deinit(allocator);
347
-
348
- forest.compaction_schedule.deinit(allocator);
349
- forest.scan_buffer_pool.deinit(allocator);
350
- }
351
-
352
- pub fn reset(forest: *Forest) void {
353
- // Components using the node_pool must release all nodes they acquired upon reset.
354
- defer assert(forest.node_pool.free.count() == forest.node_pool.free.bit_length);
355
-
356
- inline for (std.meta.fields(Grooves)) |field| {
357
- @field(forest.grooves, field.name).reset();
358
- }
359
-
360
- forest.grid.trace.cancel(.lookup);
361
- forest.grid.trace.cancel(.lookup_worker);
362
- forest.grid.trace.cancel(.scan_tree);
363
- forest.grid.trace.cancel(.scan_tree_level);
364
-
365
- forest.manifest_log.reset();
366
- forest.scan_buffer_pool.reset();
367
- forest.compaction_schedule.reset();
368
-
369
- forest.* = .{
370
- // Don't reset the grid – replica is responsible for grid cancellation.
371
- .grid = forest.grid,
372
- .grooves = forest.grooves,
373
- .node_pool = forest.node_pool,
374
- .manifest_log = forest.manifest_log,
375
-
376
- .compaction_schedule = forest.compaction_schedule,
377
-
378
- .scan_buffer_pool = forest.scan_buffer_pool,
379
- .radix_buffer = forest.radix_buffer,
380
- };
381
- }
382
-
383
- pub fn open(forest: *Forest, callback: Callback) void {
384
- assert(forest.progress == null);
385
- assert(forest.compaction_progress == null);
386
-
387
- forest.progress = .{ .open = .{ .callback = callback } };
388
-
389
- inline for (std.meta.fields(Grooves)) |field| {
390
- @field(forest.grooves, field.name).open_commence(&forest.manifest_log);
391
- }
392
-
393
- forest.manifest_log.open(manifest_log_open_event, manifest_log_open_callback);
394
- }
395
-
396
- fn manifest_log_open_event(
397
- manifest_log: *ManifestLog,
398
- table: *const schema.ManifestNode.TableInfo,
399
- ) void {
400
- const forest: *Forest = @fieldParentPtr("manifest_log", manifest_log);
401
- assert(forest.progress.? == .open);
402
- assert(forest.compaction_progress == null);
403
- assert(table.label.level < constants.lsm_levels);
404
- assert(table.label.event != .remove);
405
-
406
- if (table.tree_id < tree_id_range.min or table.tree_id > tree_id_range.max) {
407
- log.err("manifest_log_open_event: unknown table in manifest: {}", .{table});
408
- @panic("Forest.manifest_log_open_event: unknown table in manifest");
409
- }
410
- switch (tree_id_cast(table.tree_id)) {
411
- inline else => |tree_id| {
412
- var tree: *TreeForIdType(tree_id) = forest.tree_for_id(tree_id);
413
- tree.open_table(table);
414
- },
415
- }
416
- }
417
-
418
- fn manifest_log_open_callback(manifest_log: *ManifestLog) void {
419
- const forest: *Forest = @fieldParentPtr("manifest_log", manifest_log);
420
- assert(forest.progress.? == .open);
421
- assert(forest.compaction_progress == null);
422
- forest.verify_tables_recovered();
423
-
424
- inline for (std.meta.fields(Grooves)) |field| {
425
- @field(forest.grooves, field.name).open_complete();
426
- }
427
- forest.verify_table_extents();
428
-
429
- const callback = forest.progress.?.open.callback;
430
- forest.progress = null;
431
- callback(forest);
432
- }
433
-
434
- pub fn compact(forest: *Forest, callback: Callback, op: u64) void {
435
- const compaction_beat = op % constants.lsm_compaction_ops;
436
-
437
- const first_beat = compaction_beat == 0;
438
- const last_half_beat = compaction_beat ==
439
- @divExact(constants.lsm_compaction_ops, 2) - 1;
440
- const half_beat = compaction_beat == @divExact(constants.lsm_compaction_ops, 2);
441
- const last_beat = compaction_beat == constants.lsm_compaction_ops - 1;
442
- assert(@as(usize, @intFromBool(first_beat)) + @intFromBool(last_half_beat) +
443
- @intFromBool(half_beat) + @intFromBool(last_beat) <= 1);
444
-
445
- log.debug("entering forest.compact() op={} constants.lsm_compaction_ops={} " ++
446
- "first_beat={} last_half_beat={} half_beat={} last_beat={}", .{
447
- op,
448
- constants.lsm_compaction_ops,
449
- first_beat,
450
- last_half_beat,
451
- half_beat,
452
- last_beat,
453
- });
454
-
455
- assert(forest.progress == null);
456
- forest.progress = .{ .compact = .{
457
- .op = op,
458
- .callback = callback,
459
- } };
460
-
461
- // Run trees and manifest log compaction in parallel, join in compact_finish.
462
- assert(forest.compaction_progress == null);
463
- forest.compaction_progress = .{
464
- .trees_done = false,
465
- .manifest_log_done = false,
466
- };
467
-
468
- // No compactions are run during the absolute first bar, or during
469
- // the first bar of the checkpoint that we are currently recovering
470
- // from (see `op_compacted` for the rationale behind this).
471
- if (op < constants.lsm_compaction_ops or
472
- forest.grid.superblock.working.vsr_state.op_compacted(op))
473
- {
474
- forest.compaction_progress.?.manifest_log_done = true;
475
- forest.compaction_progress.?.trees_done = true;
476
- forest.grid.on_next_tick(compact_finish_next_tick, &forest.next_tick);
477
- return;
478
- }
479
-
480
- // Manifest log compaction. Run on the last beat of each half-bar.
481
- // Start before forest compaction for lesser fragmentation, as
482
- // manifest log grid reservations are much smaller than compaction's.
483
- // TODO: Figure out a plan wrt the pacing here. Putting it on the
484
- // last beat kinda-sorta balances out, because we expect to naturally
485
- // do less other compaction work on the last beat.
486
- if (last_beat or last_half_beat) {
487
- forest.manifest_log.compact(compact_manifest_log_callback, op);
488
- } else {
489
- forest.compaction_progress.?.manifest_log_done = true;
490
- }
491
-
492
- forest.compaction_schedule.beat_start(compact_trees_callback, op);
493
- }
494
-
495
- fn compact_finish_next_tick(next_tick: *Grid.NextTick) void {
496
- const forest: *Forest = @alignCast(
497
- @fieldParentPtr("next_tick", next_tick),
498
- );
499
- forest.compact_finish();
500
- }
501
-
502
- fn compact_trees_callback(forest: *Forest) void {
503
- assert(forest.progress.? == .compact);
504
- assert(forest.compaction_progress != null);
505
- assert(!forest.compaction_progress.?.trees_done);
506
- forest.compaction_progress.?.trees_done = true;
507
-
508
- if (forest.compaction_progress.?.all_done()) {
509
- forest.compact_finish();
510
- }
511
- }
512
-
513
- fn compact_manifest_log_callback(manifest_log: *ManifestLog) void {
514
- const forest: *Forest = @fieldParentPtr("manifest_log", manifest_log);
515
-
516
- assert(forest.progress.? == .compact);
517
- assert(forest.compaction_progress != null);
518
- assert(!forest.compaction_progress.?.manifest_log_done);
519
- forest.compaction_progress.?.manifest_log_done = true;
520
-
521
- if (forest.compaction_progress.?.all_done()) {
522
- forest.compact_finish();
523
- }
524
- }
525
-
526
- fn compact_finish(forest: *Forest) void {
527
- assert(forest.progress.? == .compact);
528
- assert(forest.compaction_progress != null);
529
- assert(forest.compaction_progress.?.trees_done);
530
- assert(forest.compaction_progress.?.manifest_log_done);
531
- assert(forest.compaction_schedule.pool.idle());
532
- assert(forest.compaction_schedule.pool.blocks_acquired() <=
533
- compaction_block_count_beat_min);
534
-
535
- forest.verify_table_extents();
536
-
537
- assert(forest.progress.? == .compact);
538
- const op = forest.progress.?.compact.op;
539
-
540
- const compaction_beat = op % constants.lsm_compaction_ops;
541
- const last_half_beat = compaction_beat ==
542
- @divExact(constants.lsm_compaction_ops, 2) - 1;
543
- const last_beat = compaction_beat == constants.lsm_compaction_ops - 1;
544
-
545
- if (op < constants.lsm_compaction_ops or
546
- forest.grid.superblock.working.vsr_state.op_compacted(op))
547
- {
548
- // No compaction was run.
549
- } else {
550
- for (0..constants.lsm_levels) |level_b| {
551
- if (level_active(.{ .level_b = level_b, .op = op })) {
552
- inline for (comptime std.enums.values(Forest.TreeID)) |tree_id| {
553
- const compaction =
554
- forest.compaction_schedule.compaction_at(level_b, tree_id);
555
-
556
- // Apply the changes to the manifest. This will run at the target
557
- // compaction beat that is requested.
558
- if (last_beat or last_half_beat) compaction.bar_complete();
559
- }
560
- }
561
- }
562
- }
563
-
564
- // Groove sync compaction - must be done after all async work for the beat completes.
565
- inline for (std.meta.fields(Grooves)) |field| {
566
- @field(forest.grooves, field.name).compact(op);
567
- }
568
-
569
- if (last_beat or last_half_beat) {
570
- if (forest.compaction_schedule.bar_input_size) |bar_input_size| {
571
- assert(bar_input_size == 0);
572
- forest.compaction_schedule.bar_input_size = null;
573
- }
574
-
575
- // On the last beat of the bar, make sure that manifest log compaction is finished.
576
- forest.manifest_log.compact_end();
577
-
578
- // Swap the mutable and immutable tables; this must happen on the last beat,
579
- // regardless of pacing.
580
- if (last_beat) {
581
- inline for (comptime std.enums.values(TreeID)) |tree_id| {
582
- const tree = tree_for_id(forest, tree_id);
583
-
584
- log.debug("swap_mutable_and_immutable({s})", .{tree.config.name});
585
- tree.swap_mutable_and_immutable(
586
- snapshot_min_for_table_output(compaction_op_min(op)),
587
- );
588
-
589
- // Ensure tables haven't overflowed.
590
- tree.manifest.assert_level_table_counts();
591
- }
592
- }
593
- }
594
-
595
- const callback = forest.progress.?.compact.callback;
596
- forest.progress = null;
597
- forest.compaction_progress = null;
598
-
599
- callback(forest);
600
- }
601
-
602
- pub fn checkpoint(forest: *Forest, callback: Callback) void {
603
- assert(forest.progress == null);
604
- assert(forest.compaction_progress == null);
605
- forest.grid.assert_only_repairing();
606
- forest.verify_table_extents();
607
-
608
- forest.progress = .{ .checkpoint = .{ .callback = callback } };
609
-
610
- inline for (std.meta.fields(Grooves)) |field| {
611
- @field(forest.grooves, field.name).assert_between_bars();
612
- }
613
-
614
- inline for (comptime std.enums.values(TreeID)) |tree_id| {
615
- const tree = tree_for_id(forest, tree_id);
616
-
617
- // The last immutable table constructed before the checkpoint must not absorb any
618
- // mutable table, because otherwise recovering from checkpoint would construct a
619
- // different immutable table.
620
- assert(!tree.table_immutable.mutability.immutable.absorbed);
621
- maybe(tree.table_immutable.count() > 0);
622
- assert(tree.table_mutable.count() == 0);
623
- }
624
-
625
- forest.manifest_log.checkpoint(checkpoint_manifest_log_callback);
626
- }
627
-
628
- fn checkpoint_manifest_log_callback(manifest_log: *ManifestLog) void {
629
- const forest: *Forest = @fieldParentPtr("manifest_log", manifest_log);
630
- assert(forest.progress.? == .checkpoint);
631
- assert(forest.compaction_progress == null);
632
- forest.verify_table_extents();
633
- forest.verify_tables_recovered();
634
-
635
- const callback = forest.progress.?.checkpoint.callback;
636
- forest.progress = null;
637
- callback(forest);
638
- }
639
-
640
- pub fn tree_id_cast(tree_id: u16) TreeID {
641
- return @enumFromInt(tree_id);
642
- }
643
-
644
- fn TreeForIdType(comptime tree_id: TreeID) type {
645
- const tree_info = tree_infos[@intFromEnum(tree_id) - tree_id_range.min];
646
- assert(tree_info.tree_id == @intFromEnum(tree_id));
647
-
648
- return tree_info.Tree;
649
- }
650
-
651
- pub fn tree_info_for_id(comptime tree_id: TreeID) TreeInfo {
652
- const tree_info = tree_infos[@intFromEnum(tree_id) - tree_id_range.min];
653
- assert(tree_info.tree_id == @intFromEnum(tree_id));
654
-
655
- return tree_info;
656
- }
657
-
658
- pub fn tree_for_id(forest: *Forest, comptime tree_id: TreeID) *TreeForIdType(tree_id) {
659
- const tree_info = tree_infos[@intFromEnum(tree_id) - tree_id_range.min];
660
- assert(tree_info.tree_id == @intFromEnum(tree_id));
661
-
662
- var groove = &@field(forest.grooves, tree_info.groove_name);
663
-
664
- switch (tree_info.groove_tree) {
665
- .objects => return &groove.objects,
666
- .ids => return &groove.ids,
667
- .indexes => |index_name| return &@field(groove.indexes, index_name),
668
- }
669
- }
670
-
671
- pub fn tree_for_id_const(
672
- forest: *const Forest,
673
- comptime tree_id: TreeID,
674
- ) *const TreeForIdType(tree_id) {
675
- const tree_info = tree_infos[@intFromEnum(tree_id) - tree_id_range.min];
676
- assert(tree_info.tree_id == @intFromEnum(tree_id));
677
-
678
- const groove = &@field(forest.grooves, tree_info.groove_name);
679
-
680
- switch (tree_info.groove_tree) {
681
- .objects => return &groove.objects,
682
- .ids => return &groove.ids,
683
- .indexes => |index_name| return &@field(groove.indexes, index_name),
684
- }
685
- }
686
-
687
- /// Returns whether the forest contains this table (ignoring differences in snapshot_max) at
688
- /// any level.
689
- pub fn contains_table(
690
- forest: *const Forest,
691
- table: *const schema.ManifestNode.TableInfo,
692
- ) bool {
693
- switch (tree_id_cast(table.tree_id)) {
694
- inline else => |tree_id| {
695
- const tree = forest.tree_for_id_const(tree_id);
696
- const Tree = Forest.TreeForIdType(tree_id);
697
- const tree_table = Tree.Manifest.TreeTableInfo.decode(table);
698
- for (&tree.manifest.levels) |manifest_level| {
699
- if (manifest_level.find(&tree_table)) |level_table| {
700
- assert(tree_table.checksum == level_table.table_info.checksum);
701
- assert(tree_table.address == level_table.table_info.address);
702
- assert(tree_table.key_min == level_table.table_info.key_min);
703
- assert(tree_table.key_max == level_table.table_info.key_max);
704
- assert(tree_table.snapshot_min == level_table.table_info.snapshot_min);
705
-
706
- assert(tree_table.snapshot_max <= level_table.table_info.snapshot_max);
707
- return true;
708
- }
709
- }
710
- return false;
711
- },
712
- }
713
- }
714
-
715
- /// Verify that `ManifestLog.table_extents` has an extent for every active table.
716
- ///
717
- /// (Invoked between beats.)
718
- fn verify_table_extents(forest: *const Forest) void {
719
- var tables_count: usize = 0;
720
- inline for (comptime std.enums.values(TreeID)) |tree_id| {
721
- for (0..constants.lsm_levels) |level| {
722
- const tree_level = forest.tree_for_id_const(tree_id).manifest.levels[level];
723
- tables_count += tree_level.tables.len();
724
-
725
- if (constants.verify) {
726
- var tables_iterator = tree_level.tables.iterator_from_index(0, .ascending);
727
- while (tables_iterator.next()) |table| {
728
- assert(forest.manifest_log.table_extents.get(table.address) != null);
729
- }
730
- }
731
- }
732
- }
733
- assert(tables_count == forest.manifest_log.table_extents.count());
734
- }
735
-
736
- /// Verify the tables recovered into the ManifestLevels after opening the manifest log.
737
- ///
738
- /// There are two strategies to reconstruct the LSM's manifest levels (i.e. the list of
739
- /// tables) from a superblock manifest:
740
- ///
741
- /// 1. Iterate the manifest events in chronological order, replaying each
742
- /// insert/update/remove in sequence.
743
- /// 2. Iterate the manifest events in reverse-chronological order, ignoring events for
744
- /// tables that have already been encountered.
745
- ///
746
- /// The manifest levels constructed by each strategy are identical.
747
- ///
748
- /// 1. This function implements strategy 1, to validate `ManifestLog.open()`.
749
- /// 2. `ManifestLog.open()` implements strategy 2.
750
- ///
751
- /// (Strategy 2 minimizes the number of ManifestLevel mutations.)
752
- ///
753
- /// (Invoked immediately after open() or checkpoint()).
754
- fn verify_tables_recovered(forest: *const Forest) void {
755
- const ForestTableIteratorType =
756
- @import("./forest_table_iterator.zig").ForestTableIteratorType;
757
- const ForestTableIterator = ForestTableIteratorType(Forest);
758
-
759
- assert(forest.grid.superblock.opened);
760
- assert(forest.manifest_log.opened);
761
-
762
- if (Forest.Storage != @import("../testing/storage.zig").Storage) return;
763
-
764
- // The manifest log is opened, which means we have all of the manifest blocks.
765
- // But if the replica is syncing, those blocks might still be writing (and thus not in
766
- // the TestStorage when we go to retrieve them).
767
- if (forest.grid.superblock.working.vsr_state.sync_op_max > 0) return;
768
-
769
- // The latest version of each table, keyed by table checksum.
770
- // Null when the table has been deleted.
771
- var tables_latest = std.AutoHashMap(u128, struct {
772
- table: schema.ManifestNode.TableInfo,
773
- manifest_block: u64,
774
- manifest_entry: u32,
775
- }).init(forest.grid.superblock.storage.allocator);
776
- defer tables_latest.deinit();
777
-
778
- // Replay manifest events in chronological order.
779
- // Accumulate all tables that belong in the recovered forest's ManifestLevels.
780
- for (0..forest.manifest_log.log_block_checksums.count) |i| {
781
- const block_checksum = forest.manifest_log.log_block_checksums.get(i).?;
782
- const block_address = forest.manifest_log.log_block_addresses.get(i).?;
783
- assert(block_address > 0);
784
-
785
- const block = forest.grid.superblock.storage.grid_block(block_address).?;
786
- const block_header = schema.header_from_block(block);
787
- assert(block_header.address == block_address);
788
- assert(block_header.checksum == block_checksum);
789
- assert(block_header.block_type == .manifest);
790
-
791
- const block_schema = schema.ManifestNode.from(block);
792
- assert(block_schema.entry_count > 0);
793
- assert(block_schema.entry_count <= schema.ManifestNode.entry_count_max);
794
-
795
- for (block_schema.tables_const(block), 0..) |*table, entry| {
796
- if (table.label.event == .remove) {
797
- maybe(tables_latest.remove(table.checksum));
798
- } else {
799
- tables_latest.put(table.checksum, .{
800
- .table = table.*,
801
- .manifest_block = block_address,
802
- .manifest_entry = @intCast(entry),
803
- }) catch @panic("oom");
804
- }
805
- }
806
-
807
- if (i > 0) {
808
- // Verify the linked-list.
809
- const block_previous = schema.ManifestNode.previous(block).?;
810
- assert(block_previous.checksum ==
811
- forest.manifest_log.log_block_checksums.get(i - 1).?);
812
- assert(block_previous.address ==
813
- forest.manifest_log.log_block_addresses.get(i - 1).?);
814
- }
815
- }
816
-
817
- // Verify that the SuperBlock Manifest's table extents are correct.
818
- var tables_latest_iterator = tables_latest.valueIterator();
819
- var table_extent_counts: usize = 0;
820
- while (tables_latest_iterator.next()) |table| {
821
- const table_extent = forest.manifest_log.table_extents.get(table.table.address).?;
822
- assert(table.manifest_block == table_extent.block);
823
- assert(table.manifest_entry == table_extent.entry);
824
-
825
- table_extent_counts += 1;
826
- }
827
- assert(table_extent_counts == forest.manifest_log.table_extents.count());
828
-
829
- // Verify the tables in `tables` are exactly the tables recovered by the Forest.
830
- var forest_tables_iterator = ForestTableIterator{};
831
- while (forest_tables_iterator.next(forest)) |forest_table_item| {
832
- const table_latest = tables_latest.get(forest_table_item.checksum).?;
833
- assert(table_latest.table.label.level == forest_table_item.label.level);
834
- assert(std.meta.eql(table_latest.table.key_min, forest_table_item.key_min));
835
- assert(std.meta.eql(table_latest.table.key_max, forest_table_item.key_max));
836
- assert(table_latest.table.checksum == forest_table_item.checksum);
837
- assert(table_latest.table.address == forest_table_item.address);
838
- assert(table_latest.table.snapshot_min == forest_table_item.snapshot_min);
839
- assert(table_latest.table.snapshot_max == forest_table_item.snapshot_max);
840
- assert(table_latest.table.tree_id == forest_table_item.tree_id);
841
-
842
- const table_removed = tables_latest.remove(forest_table_item.checksum);
843
- assert(table_removed);
844
- }
845
- assert(tables_latest.count() == 0);
846
- }
847
-
848
- /// Calculates the maximum number of blocks that could be released by Tree and ManifestLog
849
- /// compactions before a checkpoint becomes durable on a commit quorum of replicas.
850
- ///
851
- /// A checkpoint is guaranteed to be durable when a replica commits the (pipeline + 1)th
852
- /// prepare after checkpoint trigger (see `op_repair_min` in replica.zig for more details).
853
- /// Therefore, the maximum number of blocks released prior checkpoint durability is
854
- /// equivalent to the maximum number of blocks released by the first pipeline of prepares
855
- /// after checkpoint trigger.
856
- pub fn compaction_blocks_released_per_pipeline_max() usize {
857
- const half_bar_ops = @divExact(constants.lsm_compaction_ops, 2);
858
- const pipeline_half_bars =
859
- stdx.div_ceil(constants.pipeline_prepare_queue_max, half_bar_ops);
860
-
861
- // Maximum number of blocks released within a single half-bar by compaction.
862
- const compaction_blocks_released_half_bar_max = blocks: {
863
- var blocks: usize = 0;
864
- inline for (Forest.tree_infos) |tree_info| {
865
- blocks +=
866
- stdx.div_ceil(constants.lsm_levels, 2) *
867
- (compaction_input_tables_max *
868
- (1 + tree_info.Tree.Table.layout.value_block_count_max));
869
- }
870
- break :blocks blocks;
871
- };
872
-
873
- const compaction_blocks_released_pipeline_max =
874
- (pipeline_half_bars * compaction_blocks_released_half_bar_max) +
875
- // Compaction is paced across all beats, so if a pipeline is less than half a bar,
876
- // for simplicity, use the upper bound for a half a bar (treating pacing as
877
- // imperfect).
878
- @intFromBool(pipeline_half_bars == 0) * compaction_blocks_released_half_bar_max;
879
-
880
- // Maximum number of blocks released within a pipeline by ManifestLog compactions.
881
- const manifest_log_blocks_released_pipeline_max =
882
- pipeline_half_bars * Forest.manifest_log_blocks_released_half_bar_max;
883
-
884
- return compaction_blocks_released_pipeline_max +
885
- manifest_log_blocks_released_pipeline_max;
886
- }
887
- };
888
- }
889
-
890
- /// Plans a bar's worth of compaction work across all the trees in the Forest, and schedules it
891
- /// one beat at a time. Each bar is divided into two half bars with `lsm_compaction_ops/2` beats
892
- /// each. Even levels (0 → 1, 2 → 4, etc.) are active during the first half bar and odd levels
893
- /// (immutable → 0, 1 → 3, etc.) are active during the second half bar.
894
- ///
895
- /// We now describe the scheduling algorithm. In the description, we refer to each (tree, level)
896
- /// combination as a `Compaction`, for example the compaction from level 0 → 1 in the Accounts tree.
897
- ///
898
- /// At the first beat of each half bar:
899
- /// 1. Calculate the half-bar quota for each Compaction, which is the total number of bytes that
900
- /// Compaction needs to chew through. We use this as an estimate of time the compaction will take
901
- /// and then slice it into small chunks, to spread it evenly across the beats of the half bar.
902
- /// 2. Calculate the half-bar quota for the entire Forest by summing up the aforementioned quotas.
903
- ///
904
- /// At each beat:
905
- /// 1. Calculate the Forest's beat quota by equally dividing the half-bar quota across each beat.
906
- /// 2. Resume a suspended Compaction, or start a new one.
907
- /// 3. Run active Compaction till either the Forest's beat quota is met, or its half-bar quota is
908
- /// met. If its the latter, go to step 2. If its the former...
909
- /// 4. Suspend active Compaction and finish the beat.
910
- fn CompactionScheduleType(comptime Forest: type, comptime Grid: type) type {
911
- return struct {
912
- grid: *Grid,
913
- forest: *Forest,
914
- pool: ResourcePool,
915
- next_tick: Grid.NextTick = undefined,
916
- callback: ?*const fn (*Forest) void = null,
917
- bar_input_size: ?u64 = null,
918
- beat_input_size: ?u64 = null,
919
-
920
- const CompactionSchedule = @This();
921
- const ResourcePool = ResourcePoolType(Grid);
922
-
923
- pub fn init(
924
- self: *CompactionSchedule,
925
- allocator: mem.Allocator,
926
- grid: *Grid,
927
- forest: *Forest,
928
- block_count: u32,
929
- ) !void {
930
- assert(block_count >= compaction_block_count_beat_min);
931
-
932
- self.* = .{ .grid = grid, .forest = forest, .pool = undefined };
933
- self.pool = try ResourcePool.init(allocator, block_count);
934
- errdefer self.pool.deinit(allocator);
935
- }
936
-
937
- pub fn deinit(self: *CompactionSchedule, allocator: mem.Allocator) void {
938
- self.pool.deinit(allocator);
939
- }
940
-
941
- pub fn reset(self: *CompactionSchedule) void {
942
- self.pool.reset();
943
-
944
- self.* = .{ .grid = self.grid, .forest = self.forest, .pool = self.pool };
945
- }
946
-
947
- pub fn beat_start(self: *CompactionSchedule, callback: Forest.Callback, op: u64) void {
948
- assert(self.pool.idle());
949
- assert(self.pool.grid_reservation == null);
950
-
951
- assert(self.callback == null);
952
- assert(op >= constants.lsm_compaction_ops);
953
- assert(!self.grid.superblock.working.vsr_state.op_compacted(op));
954
- assert(self.beat_input_size == null);
955
-
956
- self.beat_input_size = 0;
957
- self.callback = callback;
958
-
959
- const half_bar = @divExact(constants.lsm_compaction_ops, 2);
960
- const compaction_beat = op % constants.lsm_compaction_ops;
961
-
962
- const first_beat = compaction_beat == 0;
963
- const half_beat = compaction_beat == half_bar;
964
-
965
- if (first_beat or half_beat) {
966
- assert(self.pool.blocks_acquired() == 0);
967
- assert(self.bar_input_size == null);
968
-
969
- var bar_input_size: u64 = 0;
970
- for (0..constants.lsm_levels) |level_b| {
971
- if (level_active(.{ .level_b = level_b, .op = op })) {
972
- inline for (comptime std.enums.values(Forest.TreeID)) |tree_id| {
973
- const tree = Forest.tree_info_for_id(tree_id);
974
- const Value = tree.Tree.Value;
975
- const compaction = self.compaction_at(level_b, tree_id);
976
-
977
- assert(
978
- self.pool.blocks_free() >=
979
- // Input index & value blocks may be carried to the next beat.
980
- compaction.level_a_index_block.buffer.len +
981
- compaction.level_a_value_block.buffer.len +
982
- compaction.level_b_index_block.buffer.len +
983
- compaction.level_b_value_block.buffer.len +
984
- // At least one output index & value block.
985
- (1 + 1),
986
- );
987
- const bar_input_values = compaction.bar_commence(op);
988
-
989
- bar_input_size += (bar_input_values * @sizeOf(Value));
990
- }
991
- }
992
- }
993
- self.bar_input_size = bar_input_size;
994
- }
995
-
996
- const beats_total = half_bar;
997
- const beats_done = compaction_beat % half_bar;
998
- const beats_remaining = beats_total - beats_done;
999
-
1000
- self.beat_input_size = stdx.div_ceil(self.bar_input_size.?, beats_remaining);
1001
-
1002
- // This is akin to a dry run for the actual compaction work that is going to happen
1003
- // during this beat, wherein we:
1004
- // * Invoke beat_commence on the active compactions to set beat quotas
1005
- // * Reserve blocks in the grid for the output of these compactions
1006
- {
1007
- // 1 since we may have partially finished index/value blocks from the previous beat.
1008
- var beat_index_blocks_max: u64 = 1;
1009
- var beat_value_blocks_max: u64 = 1;
1010
-
1011
- var beat_input_size = self.beat_input_size.?;
1012
- for (0..constants.lsm_levels) |level_b| {
1013
- if (level_active(.{ .level_b = level_b, .op = op })) {
1014
- inline for (comptime std.enums.values(Forest.TreeID)) |tree_id| {
1015
- const tree = Forest.tree_info_for_id(tree_id);
1016
- const compaction = self.compaction_at(level_b, tree_id);
1017
-
1018
- const Value = tree.Tree.Value;
1019
- const Table = tree.Tree.Table;
1020
-
1021
- compaction.beat_commence(
1022
- stdx.div_ceil(beat_input_size, @sizeOf(Value)),
1023
- );
1024
-
1025
- // The +1 is for imperfections in pacing our immutable table, which
1026
- // might cause us to overshoot by a single block (limited to 1 due
1027
- // to how the immutable table values are consumed.)
1028
- const beat_value_blocks = stdx.div_ceil(
1029
- compaction.quotas.beat,
1030
- Table.layout.block_value_count_max,
1031
- ) + 1;
1032
- const beat_index_blocks = stdx.div_ceil(
1033
- beat_value_blocks,
1034
- Table.value_block_count_max,
1035
- );
1036
- beat_value_blocks_max += beat_value_blocks;
1037
- beat_index_blocks_max += beat_index_blocks;
1038
-
1039
- beat_input_size -|= (compaction.quotas.beat * @sizeOf(Value));
1040
- }
1041
- }
1042
- }
1043
- assert(beat_input_size == 0);
1044
- self.pool.grid_reservation = self.grid.reserve(
1045
- beat_value_blocks_max + beat_index_blocks_max,
1046
- );
1047
- }
1048
-
1049
- self.beat_resume();
1050
- }
1051
-
1052
- fn beat_resume(self: *CompactionSchedule) void {
1053
- assert(self.callback != null);
1054
- assert(self.pool.grid_reservation != null);
1055
-
1056
- if (self.beat_input_size == 0) {
1057
- self.beat_finish();
1058
- return;
1059
- }
1060
-
1061
- const op = self.forest.progress.?.compact.op;
1062
-
1063
- for (0..constants.lsm_levels) |level_b| {
1064
- if (level_active(.{ .level_b = level_b, .op = op })) {
1065
- inline for (comptime std.enums.values(Forest.TreeID)) |tree_id| {
1066
- const compaction = self.compaction_at(level_b, tree_id);
1067
-
1068
- const resumed = compaction.compaction_dispatch_enter(.{
1069
- .pool = &self.pool,
1070
- .callback = beat_resume_callback,
1071
- });
1072
-
1073
- switch (resumed) {
1074
- .pending => return,
1075
- .ready => {},
1076
- }
1077
- }
1078
- }
1079
- }
1080
- }
1081
-
1082
- fn beat_resume_callback(pool: *ResourcePool, tree_id: u16, values_consumed: u64) void {
1083
- const self: *CompactionSchedule = @fieldParentPtr("pool", pool);
1084
- assert(self.callback != null);
1085
-
1086
- switch (Forest.tree_id_cast(tree_id)) {
1087
- inline else => |id| {
1088
- const Value = Forest.tree_info_for_id(id).Tree.Value;
1089
- const input_bytes_consumed = values_consumed * @sizeOf(Value);
1090
- self.bar_input_size.? -= input_bytes_consumed;
1091
- self.beat_input_size.? -|= input_bytes_consumed;
1092
- },
1093
- }
1094
-
1095
- self.beat_resume();
1096
- }
1097
-
1098
- fn beat_finish(self: *CompactionSchedule) void {
1099
- assert(self.callback != null);
1100
-
1101
- assert(self.bar_input_size.? >= 0);
1102
- assert(self.beat_input_size == 0);
1103
- self.beat_input_size = null;
1104
-
1105
- if (self.pool.grid_reservation) |reservation| {
1106
- self.grid.forfeit(reservation);
1107
- self.pool.grid_reservation = null;
1108
- }
1109
-
1110
- self.grid.on_next_tick(beat_finish_next_tick, &self.next_tick);
1111
- }
1112
-
1113
- fn beat_finish_next_tick(next_tick: *Grid.NextTick) void {
1114
- const self: *CompactionSchedule = @alignCast(
1115
- @fieldParentPtr("next_tick", next_tick),
1116
- );
1117
- const callback = self.callback.?;
1118
- self.callback = null;
1119
- callback(self.forest);
1120
- }
1121
-
1122
- fn compaction_at(
1123
- self: *CompactionSchedule,
1124
- level_b: usize,
1125
- comptime tree_id: Forest.TreeID,
1126
- ) *Forest.TreeForIdType(tree_id).Compaction {
1127
- return &self.forest.tree_for_id(tree_id).compactions[level_b];
1128
- }
1129
- };
1130
- }
1131
-
1132
- fn level_active(options: struct { level_b: usize, op: u64 }) bool {
1133
- const half_bar_beat_count = @divExact(constants.lsm_compaction_ops, 2);
1134
- const compaction_beat = options.op % constants.lsm_compaction_ops;
1135
- return (compaction_beat < half_bar_beat_count) == (options.level_b % 2 == 1);
1136
- }
1137
-
1138
- test level_active {
1139
- assert(!level_active(.{ .level_b = 0, .op = constants.lsm_compaction_ops }));
1140
- assert(level_active(.{ .level_b = 1, .op = constants.lsm_compaction_ops }));
1141
- assert(!level_active(.{ .level_b = 2, .op = constants.lsm_compaction_ops }));
1142
-
1143
- assert(level_active(.{ .level_b = 0, .op = @divExact(constants.lsm_compaction_ops, 2) }));
1144
- assert(!level_active(.{ .level_b = 1, .op = @divExact(constants.lsm_compaction_ops, 2) }));
1145
- assert(level_active(.{ .level_b = 2, .op = @divExact(constants.lsm_compaction_ops, 2) }));
1146
- }