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,1495 +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 math = std.math;
6
- const mem = std.mem;
7
-
8
- const stdx = @import("stdx");
9
- const constants = @import("../constants.zig");
10
-
11
- const TableType = @import("table.zig").TableType;
12
- const TimestampRange = @import("timestamp_range.zig").TimestampRange;
13
- const TreeType = @import("tree.zig").TreeType;
14
- const GridType = @import("../vsr/grid.zig").GridType;
15
- const CompositeKeyType = @import("composite_key.zig").CompositeKeyType;
16
- const NodePool = @import("node_pool.zig").NodePoolType(constants.lsm_manifest_node_size, 16);
17
- const CacheMapType = @import("cache_map.zig").CacheMapType;
18
- const ScopeCloseMode = @import("tree.zig").ScopeCloseMode;
19
- const ManifestLogType = @import("manifest_log.zig").ManifestLogType;
20
- const ScanBuilderType = @import("scan_builder.zig").ScanBuilderType;
21
-
22
- const ScratchMemory = @import("scratch_memory.zig").ScratchMemory;
23
-
24
- const snapshot_latest = @import("tree.zig").snapshot_latest;
25
-
26
- fn ObjectTreeHelperType(comptime Object: type) type {
27
- assert(@hasField(Object, "timestamp"));
28
- assert(@FieldType(Object, "timestamp") == u64);
29
-
30
- return struct {
31
- inline fn key_from_value(value: *const Object) u64 {
32
- return value.timestamp & ~@as(u64, tombstone_bit);
33
- }
34
-
35
- const sentinel_key = std.math.maxInt(u64);
36
- const tombstone_bit = 1 << (64 - 1);
37
-
38
- inline fn tombstone(value: *const Object) bool {
39
- return (value.timestamp & tombstone_bit) != 0;
40
- }
41
-
42
- inline fn tombstone_from_key(timestamp: u64) Object {
43
- assert(timestamp & tombstone_bit == 0);
44
-
45
- var value = std.mem.zeroes(Object); // Full zero-initialized Value.
46
- value.timestamp = timestamp | tombstone_bit;
47
- return value;
48
- }
49
- };
50
- }
51
-
52
- const IdTreeValue = extern struct {
53
- id: u128,
54
- timestamp: u64,
55
- padding: u64 = 0,
56
-
57
- comptime {
58
- // Assert that there is no implicit padding.
59
- assert(@sizeOf(IdTreeValue) == 32);
60
- assert(stdx.no_padding(IdTreeValue));
61
- }
62
-
63
- inline fn key_from_value(value: *const IdTreeValue) u128 {
64
- return value.id;
65
- }
66
-
67
- const sentinel_key = std.math.maxInt(u128);
68
- const tombstone_bit = 1 << (64 - 1);
69
-
70
- inline fn tombstone(value: *const IdTreeValue) bool {
71
- return (value.timestamp & tombstone_bit) != 0;
72
- }
73
-
74
- inline fn tombstone_from_key(id: u128) IdTreeValue {
75
- return .{
76
- .id = id,
77
- .timestamp = tombstone_bit,
78
- };
79
- }
80
- };
81
-
82
- /// Normalizes index tree field types into either u64 or u128 for CompositeKey
83
- fn IndexCompositeKeyType(comptime Field: type) type {
84
- switch (@typeInfo(Field)) {
85
- .void => return void,
86
- .@"enum" => |e| {
87
- return switch (@bitSizeOf(e.tag_type)) {
88
- 0...@bitSizeOf(u64) => u64,
89
- @bitSizeOf(u65)...@bitSizeOf(u128) => u128,
90
- else => @compileError("Unsupported enum tag for index: " ++ @typeName(e.tag_type)),
91
- };
92
- },
93
- .int => |i| {
94
- if (i.signedness != .unsigned) {
95
- @compileError("Index int type (" ++ @typeName(Field) ++ ") is not unsigned");
96
- }
97
- return switch (@bitSizeOf(Field)) {
98
- 0...@bitSizeOf(u64) => u64,
99
- @bitSizeOf(u65)...@bitSizeOf(u128) => u128,
100
- else => @compileError("Unsupported int type for index: " ++ @typeName(Field)),
101
- };
102
- },
103
- else => @compileError("Index type " ++ @typeName(Field) ++ " is not supported"),
104
- }
105
- }
106
-
107
- comptime {
108
- assert(IndexCompositeKeyType(void) == void);
109
- assert(IndexCompositeKeyType(u0) == u64);
110
- assert(IndexCompositeKeyType(enum(u0) { x }) == u64);
111
-
112
- assert(IndexCompositeKeyType(u1) == u64);
113
- assert(IndexCompositeKeyType(u16) == u64);
114
- assert(IndexCompositeKeyType(enum(u16) { x }) == u64);
115
-
116
- assert(IndexCompositeKeyType(u32) == u64);
117
- assert(IndexCompositeKeyType(u63) == u64);
118
- assert(IndexCompositeKeyType(u64) == u64);
119
-
120
- assert(IndexCompositeKeyType(enum(u65) { x }) == u128);
121
- assert(IndexCompositeKeyType(u65) == u128);
122
- assert(IndexCompositeKeyType(u128) == u128);
123
- }
124
-
125
- fn IndexTreeType(
126
- comptime Storage: type,
127
- comptime Field: type,
128
- comptime table_value_count_max: usize,
129
- ) type {
130
- const CompositeKey = CompositeKeyType(IndexCompositeKeyType(Field));
131
- const Table = TableType(
132
- CompositeKey.Key,
133
- CompositeKey,
134
- CompositeKey.key_from_value,
135
- CompositeKey.sentinel_key,
136
- CompositeKey.tombstone,
137
- CompositeKey.tombstone_from_key,
138
- table_value_count_max,
139
- .secondary_index,
140
- );
141
-
142
- return TreeType(Table, Storage);
143
- }
144
-
145
- /// A Groove is a collection of LSM trees auto generated for fields on a struct type
146
- /// as well as custom derived fields from said struct type.
147
- pub fn GrooveType(
148
- comptime Storage: type,
149
- comptime Object: type,
150
- /// An anonymous struct instance which contains the following:
151
- ///
152
- /// - ids: { .tree = u16 }:
153
- /// An anonymous struct which maps each of the groove's trees to a stable, forest-unique,
154
- /// tree identifier.
155
- ///
156
- /// - batch_value_count_max: { .field = usize }:
157
- /// An anonymous struct which contains, for each field of `Object`,
158
- /// the maximum number of values per table per batch for the corresponding index tree.
159
- ///
160
- /// - ignored: [][]const u8:
161
- /// An array of fields on the Object type that should not be given index trees
162
- ///
163
- /// - optional: [][]const u8:
164
- /// An array of fields that should *not* index zero values.
165
- ///
166
- /// - derived: { .field = *const fn (*const Object) ?DerivedType }:
167
- /// An anonymous struct which contain fields that don't exist on the Object
168
- /// but can be derived from an Object instance using the field's corresponding function.
169
- ///
170
- /// - orphaned_ids: bool:
171
- /// Whether Groove should store objectless `id`s to prevent their reuse.
172
- /// Should be `true` only if the object contains an `id` field.
173
- ///
174
- /// - objects_cache: bool:
175
- /// Whether Groove should have an ObjectCache.
176
- /// Should be `false` only if both `prefetch_entries_for_update_max` and
177
- /// `prefetch_entries_for_read_max` are set to 0.
178
- comptime groove_options: anytype,
179
- ) type {
180
- @setEvalBranchQuota(64_000);
181
-
182
- const has_id = @hasField(Object, "id");
183
- comptime if (has_id) assert(@FieldType(Object, "id") == u128);
184
- comptime if (groove_options.orphaned_ids) assert(has_id);
185
-
186
- assert(@hasField(Object, "timestamp"));
187
- assert(@FieldType(Object, "timestamp") == u64);
188
-
189
- comptime var index_fields: []const std.builtin.Type.StructField = &.{};
190
-
191
- const primary_field = if (has_id) "id" else "timestamp";
192
- const PrimaryKey = if (has_id) u128 else u64;
193
-
194
- // Generate index LSM trees from the struct fields.
195
- for (std.meta.fields(Object)) |field| {
196
- // See if we should ignore this field from the options.
197
- //
198
- // By default, we ignore the "timestamp" field since it's a special identifier.
199
- // Since the "timestamp" is ignored by default, it shouldn't be provided
200
- // in groove_options.ignored.
201
- comptime var ignored =
202
- mem.eql(u8, field.name, "timestamp") or mem.eql(u8, field.name, "id");
203
- for (groove_options.ignored) |ignored_field_name| {
204
- comptime assert(!std.mem.eql(u8, ignored_field_name, "timestamp"));
205
- comptime assert(!std.mem.eql(u8, ignored_field_name, "id"));
206
- ignored = ignored or std.mem.eql(u8, field.name, ignored_field_name);
207
- }
208
-
209
- if (!ignored) {
210
- const table_value_count_max = constants.lsm_compaction_ops *
211
- @field(groove_options.batch_value_count_max, field.name);
212
- const IndexTree = IndexTreeType(Storage, field.type, table_value_count_max);
213
- index_fields = index_fields ++ [_]std.builtin.Type.StructField{
214
- .{
215
- .name = field.name,
216
- .type = IndexTree,
217
- .default_value_ptr = null,
218
- .is_comptime = false,
219
- .alignment = @alignOf(IndexTree),
220
- },
221
- };
222
- }
223
- }
224
-
225
- // Generate IndexTrees for fields derived from the Value in groove_options.
226
- const derived_fields = std.meta.fields(@TypeOf(groove_options.derived));
227
- for (derived_fields) |field| {
228
- // Get the function info for the derived field.
229
- const derive_func = @field(groove_options.derived, field.name);
230
- const derive_func_info = @typeInfo(@TypeOf(derive_func)).@"fn";
231
-
232
- // Make sure it has only one argument.
233
- if (derive_func_info.params.len != 1) {
234
- @compileError("expected derive fn to take in *const " ++ @typeName(Object));
235
- }
236
-
237
- // Make sure the function takes in a reference to the Value:
238
- const derive_arg = derive_func_info.params[0];
239
- if (derive_arg.is_generic) @compileError("expected derive fn arg to not be generic");
240
- if (derive_arg.type != *const Object) {
241
- @compileError("expected derive fn to take in *const " ++ @typeName(Object));
242
- }
243
-
244
- // Get the return value from the derived field as the DerivedType.
245
- if (derive_func_info.return_type == null) {
246
- @compileError("expected derive fn to return valid tree index type");
247
- }
248
-
249
- const derive_return_type = @typeInfo(derive_func_info.return_type.?);
250
- if (derive_return_type != .optional) {
251
- @compileError("expected derive fn to return optional tree index type");
252
- }
253
-
254
- const DerivedType = derive_return_type.optional.child;
255
- const table_value_count_max = constants.lsm_compaction_ops *
256
- @field(groove_options.batch_value_count_max, field.name);
257
- const IndexTree = IndexTreeType(Storage, DerivedType, table_value_count_max);
258
-
259
- index_fields = index_fields ++ [_]std.builtin.Type.StructField{
260
- .{
261
- .name = field.name,
262
- .type = IndexTree,
263
- .default_value_ptr = null,
264
- .is_comptime = false,
265
- .alignment = @alignOf(IndexTree),
266
- },
267
- };
268
- }
269
-
270
- comptime var index_options_fields: [index_fields.len]std.builtin.Type.StructField = undefined;
271
- for (index_fields, 0..) |index_field, i| {
272
- const IndexTree = index_field.type;
273
- index_options_fields[i] = .{
274
- .name = index_field.name,
275
- .type = IndexTree.Options,
276
- .default_value_ptr = null,
277
- .is_comptime = false,
278
- .alignment = @alignOf(IndexTree.Options),
279
- };
280
- }
281
-
282
- // Verify that every tree referenced by "optional" corresponds to an actual field.
283
- for (groove_options.optional) |field_name| {
284
- if (!@hasField(Object, field_name)) {
285
- std.debug.panic("optional: unrecognized field name: {s}", .{field_name});
286
- }
287
- }
288
-
289
- const ObjectTreeHelper = ObjectTreeHelperType(Object);
290
-
291
- const _ObjectTree = blk: {
292
- const table_value_count_max = constants.lsm_compaction_ops *
293
- groove_options.batch_value_count_max.timestamp;
294
- const Table = TableType(
295
- u64, // key = timestamp
296
- Object,
297
- ObjectTreeHelper.key_from_value,
298
- ObjectTreeHelper.sentinel_key,
299
- ObjectTreeHelper.tombstone,
300
- ObjectTreeHelper.tombstone_from_key,
301
- table_value_count_max,
302
- .general,
303
- );
304
- break :blk TreeType(Table, Storage);
305
- };
306
-
307
- const _IdTree = if (!has_id) void else blk: {
308
- const table_value_count_max = constants.lsm_compaction_ops *
309
- groove_options.batch_value_count_max.id;
310
- const Table = TableType(
311
- u128,
312
- IdTreeValue,
313
- IdTreeValue.key_from_value,
314
- IdTreeValue.sentinel_key,
315
- IdTreeValue.tombstone,
316
- IdTreeValue.tombstone_from_key,
317
- table_value_count_max,
318
- .general,
319
- );
320
- break :blk TreeType(Table, Storage);
321
- };
322
-
323
- const _IndexTrees = @Type(.{
324
- .@"struct" = .{
325
- .layout = .auto,
326
- .fields = index_fields,
327
- .decls = &.{},
328
- .is_tuple = false,
329
- },
330
- });
331
- const _IndexTreeOptions = @Type(.{
332
- .@"struct" = .{
333
- .layout = .auto,
334
- .fields = &index_options_fields,
335
- .decls = &.{},
336
- .is_tuple = false,
337
- },
338
- });
339
-
340
- const has_scan = index_fields.len > 0;
341
-
342
- // Verify groove index count:
343
- const indexes_count_actual = std.meta.fields(_IndexTrees).len;
344
- const indexes_count_expect = std.meta.fields(Object).len -
345
- groove_options.ignored.len -
346
- // The id/timestamp fields are implicitly ignored since it's the primary key for ObjectTree:
347
- (@as(usize, 1) + @intFromBool(has_id)) +
348
- std.meta.fields(@TypeOf(groove_options.derived)).len;
349
-
350
- assert(indexes_count_actual == indexes_count_expect);
351
- assert(indexes_count_actual == std.meta.fields(_IndexTreeOptions).len);
352
-
353
- const _IndexTreeFieldHelperType = struct {
354
- fn HelperType(comptime field_name: []const u8) type {
355
- return struct {
356
- pub const Index = type: {
357
- if (is_derived) {
358
- const derived_fn = @typeInfo(@TypeOf(@field(
359
- groove_options.derived,
360
- field_name,
361
- )));
362
- assert(derived_fn == .@"fn");
363
- assert(derived_fn.@"fn".return_type != null);
364
-
365
- const return_type = @typeInfo(derived_fn.@"fn".return_type.?);
366
- assert(return_type == .optional);
367
- break :type return_type.optional.child;
368
- }
369
-
370
- break :type @TypeOf(@field(@as(Object, undefined), field_name));
371
- };
372
- pub const IndexPrefix = switch (@typeInfo(Index)) {
373
- .void => void,
374
- .int => Index,
375
- .@"enum" => |info| info.tag_type,
376
- else => @compileError("Unsupported index type for " ++ field_name),
377
- };
378
-
379
- const is_derived: bool = is_derived: {
380
- for (derived_fields) |derived_field| {
381
- if (std.mem.eql(u8, derived_field.name, field_name)) break :is_derived true;
382
- }
383
- break :is_derived false;
384
- };
385
-
386
- const allow_zero: bool = allow_zero: {
387
- for (groove_options.optional) |optional| {
388
- if (std.mem.eql(u8, field_name, optional)) {
389
- assert(!is_derived);
390
- break :allow_zero false;
391
- }
392
- }
393
- break :allow_zero true;
394
- };
395
-
396
- inline fn as_prefix(index: Index) IndexPrefix {
397
- return switch (@typeInfo(Index)) {
398
- .void => {},
399
- .int => index,
400
- .@"enum" => @intFromEnum(index),
401
- else => unreachable,
402
- };
403
- }
404
-
405
- /// Try to extract an index from the object, deriving it when necessary.
406
- /// Null means the value should not be indexed.
407
- pub fn index_from_object(object: *const Object) ?IndexPrefix {
408
- if (is_derived) {
409
- return if (@field(groove_options.derived, field_name)(object)) |value|
410
- as_prefix(value)
411
- else
412
- null;
413
- } else {
414
- const value = as_prefix(@field(object, field_name));
415
- return if (allow_zero or value != 0)
416
- value
417
- else
418
- null;
419
- }
420
- }
421
- };
422
- }
423
- }.HelperType;
424
-
425
- const ObjectsCacheHelpers = struct {
426
- const tombstone_bit = 1 << (64 - 1);
427
-
428
- inline fn key_from_value(value: *const Object) PrimaryKey {
429
- if (has_id) {
430
- return value.id;
431
- } else {
432
- return value.timestamp & ~@as(u64, tombstone_bit);
433
- }
434
- }
435
-
436
- inline fn hash(key: PrimaryKey) u64 {
437
- return stdx.hash_inline(key);
438
- }
439
-
440
- inline fn tombstone_from_key(a: PrimaryKey) Object {
441
- var obj: Object = undefined;
442
- if (has_id) {
443
- obj.id = a;
444
- obj.timestamp = 0;
445
- } else {
446
- obj.timestamp = a;
447
- }
448
- obj.timestamp |= tombstone_bit;
449
- return obj;
450
- }
451
-
452
- inline fn tombstone(a: *const Object) bool {
453
- return (a.timestamp & tombstone_bit) != 0;
454
- }
455
- };
456
-
457
- const _ObjectsCache = if (groove_options.objects_cache) CacheMapType(
458
- PrimaryKey,
459
- Object,
460
- ObjectsCacheHelpers.key_from_value,
461
- ObjectsCacheHelpers.hash,
462
- ObjectsCacheHelpers.tombstone_from_key,
463
- ObjectsCacheHelpers.tombstone,
464
- ) else void;
465
-
466
- const TimestampSet = struct {
467
- const TimestampSet = @This();
468
- const Found = union(enum) { found: u128, not_found };
469
- const Map = std.AutoHashMapUnmanaged(u64, Found);
470
-
471
- map: Map,
472
-
473
- fn init(self: *TimestampSet, allocator: mem.Allocator, entries_max: u32) !void {
474
- self.* = .{
475
- .map = undefined,
476
- };
477
-
478
- self.map = .{};
479
- try self.map.ensureTotalCapacity(allocator, entries_max);
480
- errdefer self.map.deinit(allocator);
481
- }
482
-
483
- fn deinit(self: *TimestampSet, allocator: mem.Allocator) void {
484
- self.map.deinit(allocator);
485
- self.* = undefined;
486
- }
487
-
488
- fn reset(self: *TimestampSet) void {
489
- self.map.clearRetainingCapacity();
490
- }
491
-
492
- /// Marks the timestamp as "found" or "not found".
493
- /// Can be called only once per timestamp.
494
- fn set(self: *TimestampSet, timestamp: u64, value: Found) void {
495
- self.map.putAssumeCapacityNoClobber(timestamp, value);
496
- }
497
-
498
- /// Whether the previously enqueued timestamp was found or not.
499
- fn get(self: *const TimestampSet, timestamp: u64) Found {
500
- const result = self.map.get(timestamp);
501
- assert(result != null);
502
-
503
- return result.?;
504
- }
505
-
506
- fn has(self: *const TimestampSet, timestamp: u64) bool {
507
- return self.map.contains(timestamp);
508
- }
509
- };
510
-
511
- return struct {
512
- const Groove = @This();
513
-
514
- pub const ObjectTree = _ObjectTree;
515
- pub const IdTree = _IdTree;
516
- pub const IndexTrees = _IndexTrees;
517
- pub const ObjectsCache = _ObjectsCache;
518
- pub const config = groove_options;
519
-
520
- /// Helper function for interacting with an Index field type.
521
- pub const IndexTreeFieldHelperType = _IndexTreeFieldHelperType;
522
-
523
- const Grid = GridType(Storage);
524
- const ManifestLog = ManifestLogType(Storage);
525
-
526
- const LookupBy = enum {
527
- /// Either `id` or `timestamp` for objects without the id field.
528
- /// This is the same key used by the object cache map.
529
- primary_key,
530
-
531
- /// Lookup by `timestamp` in an object where the object cache map is indexed by `id`.
532
- /// In this case, the `timestamp` is also indexed to support indirect lookups such as
533
- /// `exists()` and `get_by_timestamp()`.
534
- /// Invariant: `has_id` is true.
535
- timestamp,
536
- };
537
-
538
- const PrefetchKey = union(enum) {
539
- id: if (has_id) u128 else void,
540
- timestamp: u64,
541
- };
542
-
543
- const PrefetchKeys = std.AutoHashMapUnmanaged(
544
- PrefetchKey,
545
- struct {
546
- level: u8,
547
- lookup_by: LookupBy,
548
- },
549
- );
550
-
551
- const LookupResult = union(enum) {
552
- found_object: Object,
553
- found_orphaned_id,
554
- not_found,
555
- };
556
-
557
- pub const ScanBuilder = if (has_scan) ScanBuilderType(Groove, Storage) else void;
558
-
559
- grid: *Grid,
560
- objects: ObjectTree,
561
- ids: IdTree,
562
- indexes: IndexTrees,
563
-
564
- /// Object IDs and timestamps enqueued to be prefetched.
565
- /// Prefetching ensures that point lookups against the latest snapshot are synchronous.
566
- /// This shields state machine implementations from the challenges of concurrency and I/O,
567
- /// and enables simple state machine function signatures that commit writes atomically.
568
- prefetch_keys: PrefetchKeys,
569
-
570
- /// The snapshot to prefetch from.
571
- prefetch_snapshot: ?u64 = null,
572
-
573
- /// This is used to accelerate point lookups and is not used for range queries.
574
- /// It's also where prefetched data is loaded into, so we don't have a different
575
- /// prefetch cache to our object cache.
576
- ///
577
- /// The values cache is only used for the latest snapshot for simplicity.
578
- /// Earlier snapshots will still be able to utilize the block cache.
579
- ///
580
- /// The values cache is updated on every `insert()`/`upsert()`/`remove()` and stores
581
- /// a duplicate of data that's already in table_mutable. This is done because
582
- /// keeping table_mutable as an array, and simplifying the compaction path
583
- /// is faster than trying to amortize and save memory.
584
- ///
585
- /// Invariant: if there is an objects_cache then if something is in the mutable
586
- /// table, it _must_ exist in our object cache.
587
- /// Otherwise, the ObjectsCache is of type void.
588
- objects_cache: ObjectsCache,
589
-
590
- timestamps: if (has_id) TimestampSet else void,
591
-
592
- scan_builder: ScanBuilder,
593
-
594
- pub const IndexTreeOptions = _IndexTreeOptions;
595
-
596
- pub const Options = struct {
597
- /// The maximum number of objects that might be prefetched and not modified by a batch.
598
- prefetch_entries_for_read_max: u32,
599
- /// The maximum number of objects that might be prefetched and then modified by a batch.
600
- prefetch_entries_for_update_max: u32,
601
- cache_entries_max: u32,
602
-
603
- tree_options_object: ObjectTree.Options,
604
- tree_options_id: if (has_id) IdTree.Options else void,
605
- tree_options_index: IndexTreeOptions,
606
- };
607
-
608
- pub fn init(
609
- groove: *Groove,
610
- allocator: mem.Allocator,
611
- node_pool: *NodePool,
612
- grid: *Grid,
613
- radix_buffer: *ScratchMemory,
614
- options: Options,
615
- ) !void {
616
- assert(options.tree_options_object.batch_value_count_limit *
617
- constants.lsm_compaction_ops <= ObjectTree.Table.value_count_max);
618
- assert(radix_buffer.state == .free);
619
-
620
- groove.* = .{
621
- .grid = grid,
622
-
623
- .objects = undefined,
624
- .ids = undefined,
625
- .indexes = undefined,
626
- .prefetch_keys = undefined,
627
- .objects_cache = if (ObjectsCache != void) undefined else {},
628
- .timestamps = undefined,
629
- .scan_builder = undefined,
630
- };
631
-
632
- groove.objects_cache = if (ObjectsCache != void) try ObjectsCache.init(allocator, .{
633
- .cache_value_count_max = options.cache_entries_max,
634
- // In the worst case, each stash must be able to store
635
- // batch_value_count_limit per beat (to contain either TableMutable or
636
- // TableImmutable) as well as the maximum number of prefetches a bar may
637
- // perform, excluding prefetches already accounted
638
- // for by batch_value_count_limit.
639
- .stash_value_count_max = constants.lsm_compaction_ops *
640
- (options.tree_options_object.batch_value_count_limit +
641
- options.prefetch_entries_for_read_max),
642
-
643
- // Scopes are limited to a single beat, so the maximum number of entries in
644
- // a single scope is batch_value_count_limit (total – not per beat).
645
- .scope_value_count_max = options.tree_options_object.batch_value_count_limit,
646
-
647
- .name = ObjectTree.tree_name(),
648
- }) else {
649
- // If there are no modifications or point lookups on the Groove then
650
- // no `objects_cache` is needed.
651
- assert(options.prefetch_entries_for_read_max == 0);
652
- assert(options.prefetch_entries_for_update_max == 0);
653
- {}
654
- };
655
-
656
- errdefer if (ObjectsCache != void) groove.objects_cache.deinit(allocator);
657
-
658
- // Initialize the object LSM tree.
659
- try groove.objects.init(
660
- allocator,
661
- node_pool,
662
- grid,
663
- radix_buffer,
664
- .{
665
- .id = @field(groove_options.ids, "timestamp"),
666
- .name = ObjectTree.tree_name(),
667
- },
668
- options.tree_options_object,
669
- );
670
- errdefer groove.objects.deinit(allocator);
671
-
672
- if (has_id) try groove.ids.init(
673
- allocator,
674
- node_pool,
675
- grid,
676
- radix_buffer,
677
- .{
678
- .id = @field(groove_options.ids, "id"),
679
- .name = ObjectTree.tree_name() ++ ".id",
680
- },
681
- options.tree_options_id,
682
- );
683
- errdefer if (has_id) groove.ids.deinit(allocator);
684
-
685
- var index_trees_initialized: usize = 0;
686
- // Make sure to deinit initialized index LSM trees on error.
687
- errdefer inline for (std.meta.fields(IndexTrees), 0..) |field, field_index| {
688
- if (index_trees_initialized >= field_index + 1) {
689
- const Tree = field.type;
690
- const tree: *Tree = &@field(groove.indexes, field.name);
691
- tree.deinit(allocator);
692
- }
693
- };
694
-
695
- // Initialize index LSM trees.
696
- inline for (std.meta.fields(IndexTrees)) |field| {
697
- const Tree = field.type;
698
- const tree: *Tree = &@field(groove.indexes, field.name);
699
- try tree.init(
700
- allocator,
701
- node_pool,
702
- grid,
703
- radix_buffer,
704
- .{
705
- .id = @field(groove_options.ids, field.name),
706
- .name = ObjectTree.tree_name() ++ "." ++ field.name,
707
- },
708
- @field(options.tree_options_index, field.name),
709
- );
710
- index_trees_initialized += 1;
711
- }
712
-
713
- groove.prefetch_keys = .{};
714
- try groove.prefetch_keys.ensureTotalCapacity(
715
- allocator,
716
- options.prefetch_entries_for_read_max + options.prefetch_entries_for_update_max,
717
- );
718
- errdefer groove.prefetch_keys.deinit(allocator);
719
-
720
- if (has_id) try groove.timestamps.init(
721
- allocator,
722
- options.prefetch_entries_for_read_max,
723
- );
724
- errdefer if (has_id) groove.timestamps.deinit(allocator);
725
-
726
- if (has_scan) try groove.scan_builder.init(allocator);
727
- errdefer if (has_scan) groove.scan_builder.deinit(allocator);
728
- }
729
-
730
- pub fn deinit(groove: *Groove, allocator: mem.Allocator) void {
731
- inline for (std.meta.fields(IndexTrees)) |field| {
732
- @field(groove.indexes, field.name).deinit(allocator);
733
- }
734
-
735
- groove.objects.deinit(allocator);
736
- if (has_id) groove.ids.deinit(allocator);
737
-
738
- groove.prefetch_keys.deinit(allocator);
739
-
740
- if (ObjectsCache != void) groove.objects_cache.deinit(allocator);
741
- if (has_id) groove.timestamps.deinit(allocator);
742
- if (has_scan) groove.scan_builder.deinit(allocator);
743
-
744
- groove.* = undefined;
745
- }
746
-
747
- pub fn reset(groove: *Groove) void {
748
- inline for (std.meta.fields(IndexTrees)) |field| {
749
- @field(groove.indexes, field.name).reset();
750
- }
751
- groove.objects.reset();
752
- if (has_id) groove.ids.reset();
753
-
754
- groove.prefetch_keys.clearRetainingCapacity();
755
-
756
- if (ObjectsCache != void) groove.objects_cache.reset();
757
-
758
- if (has_id) groove.timestamps.reset();
759
- if (has_scan) groove.scan_builder.reset();
760
-
761
- groove.* = .{
762
- .grid = groove.grid,
763
- .objects = groove.objects,
764
- .ids = groove.ids,
765
- .indexes = groove.indexes,
766
- .prefetch_keys = groove.prefetch_keys,
767
- .prefetch_snapshot = null,
768
- .objects_cache = groove.objects_cache,
769
- .timestamps = groove.timestamps,
770
- .scan_builder = groove.scan_builder,
771
- };
772
- }
773
-
774
- pub fn get(groove: *const Groove, key: PrimaryKey) LookupResult {
775
- if (groove.objects_cache.get(key)) |object| {
776
- if (object.timestamp == 0) {
777
- assert(has_id);
778
- assert(groove_options.orphaned_ids);
779
- return .found_orphaned_id;
780
- }
781
-
782
- return .{ .found_object = object.* };
783
- }
784
-
785
- return .not_found;
786
- }
787
-
788
- /// Looks up an object by `timestamp`.
789
- /// Use `get()` for objects that don't have the `id` field.
790
- /// The timestamp must have been passed to `prefetch_enqueue_by_timestamp`.
791
- pub fn get_by_timestamp(groove: *const Groove, timestamp: u64) LookupResult {
792
- // Only applicable to objects with an `id` field.
793
- // Use `get` if the object is already keyed by timestamp.
794
- comptime assert(has_id);
795
- assert(TimestampRange.valid(timestamp));
796
-
797
- return switch (groove.timestamps.get(timestamp)) {
798
- .found => |id| groove.get(id),
799
- .not_found => .not_found,
800
- };
801
- }
802
-
803
- /// Returns whether an object with this timestamp exists or not.
804
- /// The timestamp to be checked must have been passed to `prefetch_exists_enqueue`.
805
- pub fn exists(groove: *const Groove, timestamp: u64) bool {
806
- // Only applicable to objects with an `id` field.
807
- // Use `get` if the object is already keyed by timestamp.
808
- comptime assert(has_id);
809
- assert(TimestampRange.valid(timestamp));
810
-
811
- return groove.timestamps.get(timestamp) == .found;
812
- }
813
-
814
- /// Must be called directly before the state machine begins queuing ids for prefetch.
815
- pub fn prefetch_setup(groove: *Groove, snapshot_target: u64) void {
816
- assert(snapshot_target < snapshot_latest);
817
-
818
- groove.prefetch_snapshot = snapshot_target;
819
- assert(groove.prefetch_keys.count() == 0);
820
-
821
- if (has_id) groove.timestamps.reset();
822
- }
823
-
824
- /// This must be called by the state machine for every key to be prefetched.
825
- /// We tolerate duplicate IDs enqueued by the state machine.
826
- /// For example, if all unique operations require the same two dependencies.
827
- pub fn prefetch_enqueue(groove: *Groove, key: PrimaryKey) void {
828
- if (groove.objects_cache.has(key)) return;
829
-
830
- if (has_id) {
831
- // No need to check again if the key is already present.
832
- if (groove.prefetch_keys.contains(.{ .id = key })) return;
833
- if (!groove.ids.key_range_contains(groove.prefetch_snapshot.?, key)) return;
834
-
835
- groove.prefetch_from_memory_by_id(key);
836
- } else {
837
- if (groove.prefetch_keys.contains(.{ .timestamp = key })) return;
838
- if (!groove.objects.key_range_contains(groove.prefetch_snapshot.?, key)) return;
839
-
840
- groove.prefetch_from_memory_by_timestamp(key, .primary_key);
841
- }
842
- }
843
-
844
- /// This must be called by the state machine for every timestamp to be checked by `exists`.
845
- /// The first call to this function may trigger the sorting of the mutable table, which is
846
- /// likely a no-op since timestamps are strictly increasing and the table should already
847
- /// be sorted, except for objects that are frequently updated (e.g., accounts).
848
- /// We tolerate duplicate timestamps enqueued by the state machine.
849
- pub fn prefetch_enqueue_by_timestamp(
850
- groove: *Groove,
851
- timestamp: u64,
852
- ) void {
853
- // Only applicable to objects with an `id` field.
854
- // Use `prefetch_enqueue` if the object is already keyed by timestamp.
855
- comptime assert(has_id);
856
-
857
- // Instead of asserting, we allow and ignore invalid timestamps (most likely zero),
858
- // so the prefetch step does not need to verify the data's validity.
859
- if (!TimestampRange.valid(timestamp)) return;
860
-
861
- // No need to check again if the key is already present or enqueued for prefetching.
862
- if (groove.timestamps.has(timestamp) or
863
- groove.prefetch_keys.contains(.{ .timestamp = timestamp })) return;
864
-
865
- // The mutable table needs to be sorted to enable searching by timestamp.
866
- // The immutable table will be searched by `prefetch_from_memory_by_timestamp`.
867
- groove.objects.table_mutable.sort();
868
- if (groove.objects.table_mutable.get(timestamp)) |object| {
869
- assert(object.timestamp == timestamp);
870
- groove.timestamps.set(timestamp, .{ .found = object.id });
871
- return;
872
- }
873
-
874
- groove.prefetch_from_memory_by_timestamp(timestamp, .timestamp);
875
- }
876
-
877
- /// This function attempts to prefetch a value for the given id from the IdTree's
878
- /// table blocks in the grid cache.
879
- /// If found in the IdTree, we attempt to prefetch a value for the timestamp.
880
- fn prefetch_from_memory_by_id(groove: *Groove, id: u128) void {
881
- comptime assert(has_id);
882
- switch (groove.ids.lookup_from_levels_cache(
883
- groove.prefetch_snapshot.?,
884
- id,
885
- )) {
886
- .negative => {},
887
- .positive => |id_tree_value| {
888
- if (IdTreeValue.tombstone(id_tree_value)) return;
889
-
890
- if (id_tree_value.timestamp == 0) {
891
- assert(groove_options.orphaned_ids);
892
-
893
- // Zeroed timestamp indicates the object is not present,
894
- // and this id cannot be used anymore.
895
- groove.objects_cache.upsert(
896
- &std.mem.zeroInit(Object, .{
897
- .id = id_tree_value.id,
898
- }),
899
- );
900
- } else {
901
- if (groove.prefetch_keys.get(.{
902
- .timestamp = id_tree_value.timestamp,
903
- })) |prefetch_entry| {
904
- // We don't want duplicate keys when prefetching the same object
905
- // multiple times, but the `contains(.id)` check performed during
906
- // `prefetch_enqueue()` may return false if:
907
-
908
- // 1. The `IdTree` is already in memory (but not the `ObjectTree`),
909
- // so we inserted the `.timestamp` rather than the `.id`.
910
- maybe(prefetch_entry.lookup_by == .primary_key);
911
-
912
- // 2. The same object was enqueued for prefetch by both `.id`
913
- // and `.timestamp`.
914
- maybe(prefetch_entry.lookup_by == .timestamp);
915
- return;
916
- }
917
- groove.prefetch_from_memory_by_timestamp(
918
- id_tree_value.timestamp,
919
- .primary_key,
920
- );
921
- }
922
- },
923
- .possible => |level| {
924
- groove.prefetch_keys.putAssumeCapacityNoClobber(
925
- .{ .id = id },
926
- .{
927
- .level = level,
928
- .lookup_by = .primary_key,
929
- },
930
- );
931
- },
932
- }
933
- }
934
-
935
- /// This function attempts to prefetch a value for the timestamp from the ObjectTree's
936
- /// table blocks in the grid cache.
937
- fn prefetch_from_memory_by_timestamp(
938
- groove: *Groove,
939
- timestamp: u64,
940
- lookup_by: LookupBy,
941
- ) void {
942
- assert(TimestampRange.valid(timestamp));
943
- assert(lookup_by == .primary_key or has_id);
944
-
945
- switch (groove.objects.lookup_from_levels_cache(
946
- groove.prefetch_snapshot.?,
947
- timestamp,
948
- )) {
949
- .negative => switch (lookup_by) {
950
- .primary_key => {},
951
- .timestamp => if (has_id)
952
- groove.timestamps.set(timestamp, .not_found)
953
- else
954
- unreachable,
955
- },
956
- .positive => |object| {
957
- assert(!ObjectTreeHelper.tombstone(object));
958
- switch (lookup_by) {
959
- .primary_key => groove.objects_cache.upsert(object),
960
- .timestamp => if (has_id) {
961
- groove.objects_cache.upsert(object);
962
- groove.timestamps.set(object.timestamp, .{ .found = object.id });
963
- } else unreachable,
964
- }
965
- },
966
- .possible => |level| {
967
- groove.prefetch_keys.putAssumeCapacityNoClobber(
968
- .{ .timestamp = timestamp },
969
- .{
970
- .level = level,
971
- .lookup_by = lookup_by,
972
- },
973
- );
974
- },
975
- }
976
- }
977
- /// Ensure the objects corresponding to all ids enqueued with prefetch_enqueue() are
978
- /// available in `objects_cache`.
979
- pub fn prefetch(
980
- groove: *Groove,
981
- callback: *const fn (*PrefetchContext) void,
982
- context: *PrefetchContext,
983
- ) void {
984
- context.* = .{
985
- .groove = groove,
986
- .callback = callback,
987
- .snapshot = groove.prefetch_snapshot.?,
988
- .key_iterator = groove.prefetch_keys.iterator(),
989
- };
990
- groove.prefetch_snapshot = null;
991
- context.start_workers();
992
- }
993
-
994
- pub const PrefetchContext = struct {
995
- groove: *Groove,
996
- callback: *const fn (*PrefetchContext) void,
997
- snapshot: u64,
998
-
999
- key_iterator: PrefetchKeys.Iterator,
1000
- /// The goal is to fully utilize the disk I/O to ensure the prefetch completes as
1001
- /// quickly as possible, so we run multiple lookups in parallel based on the max
1002
- /// I/O depth of the Grid.
1003
- workers: [Grid.read_iops_max]PrefetchWorker = undefined,
1004
- /// The number of workers that are currently running in parallel.
1005
- workers_pending: u32 = 0,
1006
-
1007
- next_tick: Grid.NextTick = undefined,
1008
-
1009
- fn start_workers(context: *PrefetchContext) void {
1010
- assert(context.workers_pending == 0);
1011
-
1012
- context.groove.grid.trace.start(.{
1013
- .lookup = .{ .tree = @enumFromInt(context.groove.objects.config.id) },
1014
- });
1015
-
1016
- // Track an extra "worker" that will finish after the loop.
1017
- // This allows the callback to be called asynchronously on `next_tick`
1018
- // if all workers are finished synchronously.
1019
- context.workers_pending += 1;
1020
-
1021
- for (&context.workers, 0..) |*worker, index| {
1022
- assert(context.workers_pending == index + 1);
1023
-
1024
- worker.* = .{
1025
- .index = @intCast(index),
1026
- .context = context,
1027
- };
1028
-
1029
- context.groove.grid.trace.start(
1030
- .{ .lookup_worker = .{
1031
- .index = worker.index,
1032
- .tree = @enumFromInt(context.groove.objects.config.id),
1033
- } },
1034
- );
1035
-
1036
- context.workers_pending += 1;
1037
- worker.lookup_start_next();
1038
-
1039
- // If the worker finished synchronously (e.g `workers_pending` decreased),
1040
- // we don't need to start new ones.
1041
- if (context.workers_pending == index + 1) break;
1042
- }
1043
-
1044
- assert(context.workers_pending > 0);
1045
- context.workers_pending -= 1;
1046
-
1047
- if (context.workers_pending == 0) {
1048
- // All workers finished synchronously,
1049
- // calling the callback on `next_tick`.
1050
- context.groove.grid.on_next_tick(worker_next_tick, &context.next_tick);
1051
- }
1052
- }
1053
-
1054
- fn worker_next_tick(completion: *Grid.NextTick) void {
1055
- const context: *PrefetchContext = @alignCast(
1056
- @fieldParentPtr("next_tick", completion),
1057
- );
1058
- assert(context.workers_pending == 0);
1059
- context.finish();
1060
- }
1061
-
1062
- fn worker_finished(context: *PrefetchContext) void {
1063
- context.workers_pending -= 1;
1064
- if (context.workers_pending == 0) context.finish();
1065
- }
1066
-
1067
- fn finish(context: *PrefetchContext) void {
1068
- assert(context.workers_pending == 0);
1069
-
1070
- assert(context.key_iterator.next() == null);
1071
- context.groove.prefetch_keys.clearRetainingCapacity();
1072
- assert(context.groove.prefetch_keys.count() == 0);
1073
-
1074
- context.groove.grid.trace.stop(.{
1075
- .lookup = .{ .tree = @enumFromInt(context.groove.objects.config.id) },
1076
- });
1077
-
1078
- context.callback(context);
1079
- }
1080
- };
1081
-
1082
- pub const PrefetchWorker = struct {
1083
- // Since lookup contexts are used one at a time, it's safe to access
1084
- // the union's fields and reuse the same memory for all context instances.
1085
- // Can't use extern/packed union as the LookupContexts aren't ABI compliant.
1086
- const LookupContext = union(enum) {
1087
- id: if (has_id) IdTree.LookupContext else void,
1088
- object: ObjectTree.LookupContext,
1089
-
1090
- pub const Field = std.meta.FieldEnum(LookupContext);
1091
- pub fn FieldType(comptime field: Field) type {
1092
- return @FieldType(LookupContext, @tagName(field));
1093
- }
1094
-
1095
- pub inline fn parent(
1096
- comptime field: Field,
1097
- completion: *FieldType(field),
1098
- ) *PrefetchWorker {
1099
- const lookup: *LookupContext = @fieldParentPtr(@tagName(field), completion);
1100
- return @fieldParentPtr("lookup", lookup);
1101
- }
1102
-
1103
- pub inline fn get(self: *LookupContext, comptime field: Field) *FieldType(field) {
1104
- self.* = @unionInit(LookupContext, @tagName(field), undefined);
1105
- return &@field(self, @tagName(field));
1106
- }
1107
- };
1108
-
1109
- index: u8,
1110
- context: *PrefetchContext,
1111
- lookup: LookupContext = undefined,
1112
- current: ?struct {
1113
- key: PrefetchKey,
1114
- lookup_by: LookupBy,
1115
- } = null,
1116
-
1117
- fn lookup_start_next(worker: *PrefetchWorker) void {
1118
- assert(worker.current == null);
1119
- const prefetch_entry = worker.context.key_iterator.next() orelse {
1120
- worker.context.groove.grid.trace.stop(
1121
- .{ .lookup_worker = .{
1122
- .index = worker.index,
1123
- .tree = @enumFromInt(worker.context.groove.objects.config.id),
1124
- } },
1125
- );
1126
-
1127
- worker.context.worker_finished();
1128
- return;
1129
- };
1130
-
1131
- worker.current = .{
1132
- .key = prefetch_entry.key_ptr.*,
1133
- .lookup_by = prefetch_entry.value_ptr.lookup_by,
1134
- };
1135
-
1136
- // prefetch_enqueue() ensures that the tree's cache is checked before queueing the
1137
- // object for prefetching. If not in the LSM tree's cache, the object must be read
1138
- // from disk and added to the auxiliary prefetch_objects hash map.
1139
- switch (prefetch_entry.key_ptr.*) {
1140
- .id => |id| if (has_id) {
1141
- worker.context.groove.ids.lookup_from_levels_storage(.{
1142
- .callback = lookup_id_callback,
1143
- .context = worker.lookup.get(.id),
1144
- .snapshot = worker.context.snapshot,
1145
- .key = id,
1146
- .level_min = prefetch_entry.value_ptr.level,
1147
- });
1148
- } else unreachable,
1149
- .timestamp => |timestamp| {
1150
- worker.context.groove.objects.lookup_from_levels_storage(.{
1151
- .callback = lookup_object_callback,
1152
- .context = worker.lookup.get(.object),
1153
- .snapshot = worker.context.snapshot,
1154
- .key = timestamp,
1155
- .level_min = prefetch_entry.value_ptr.level,
1156
- });
1157
- },
1158
- }
1159
- }
1160
-
1161
- fn lookup_id_callback(
1162
- completion: *IdTree.LookupContext,
1163
- result: ?*const IdTreeValue,
1164
- ) void {
1165
- const worker = LookupContext.parent(.id, completion);
1166
- worker.lookup = undefined;
1167
- assert(worker.current != null);
1168
- assert(worker.current.?.key == .id);
1169
- assert(worker.current.?.lookup_by == .primary_key);
1170
-
1171
- if (result) |id_tree_value| {
1172
- if (groove_options.orphaned_ids and
1173
- id_tree_value.timestamp == 0)
1174
- {
1175
- comptime assert(has_id);
1176
-
1177
- // Zeroed timestamp indicates the object is not present,
1178
- // and this id cannot be used anymore.
1179
- worker.context.groove.objects_cache.upsert(
1180
- &std.mem.zeroInit(Object, .{
1181
- .id = id_tree_value.id,
1182
- }),
1183
- );
1184
- } else if (!id_tree_value.tombstone()) {
1185
- worker.lookup_by_timestamp(id_tree_value.timestamp);
1186
- return;
1187
- }
1188
- }
1189
-
1190
- worker.current = null;
1191
- worker.lookup_start_next();
1192
- }
1193
-
1194
- fn lookup_by_timestamp(worker: *PrefetchWorker, timestamp: u64) void {
1195
- assert(TimestampRange.valid(timestamp));
1196
- assert(worker.current != null);
1197
-
1198
- switch (worker.context.groove.objects.lookup_from_levels_cache(
1199
- worker.context.snapshot,
1200
- timestamp,
1201
- )) {
1202
- .negative => {
1203
- lookup_object_callback(worker.lookup.get(.object), null);
1204
- },
1205
- .positive => |value| {
1206
- lookup_object_callback(worker.lookup.get(.object), value);
1207
- },
1208
- .possible => |level_min| {
1209
- worker.context.groove.objects.lookup_from_levels_storage(.{
1210
- .callback = lookup_object_callback,
1211
- .context = worker.lookup.get(.object),
1212
- .snapshot = worker.context.snapshot,
1213
- .key = timestamp,
1214
- .level_min = level_min,
1215
- });
1216
- },
1217
- }
1218
- }
1219
-
1220
- fn lookup_object_callback(
1221
- completion: *ObjectTree.LookupContext,
1222
- result: ?*const Object,
1223
- ) void {
1224
- const worker = LookupContext.parent(.object, completion);
1225
- worker.lookup = undefined;
1226
-
1227
- assert(worker.current != null);
1228
- const entry = worker.current.?;
1229
- worker.current = null;
1230
-
1231
- if (result) |object| {
1232
- assert(!ObjectTreeHelper.tombstone(object));
1233
- switch (entry.key) {
1234
- .id => |key| if (has_id) {
1235
- assert(object.id == key);
1236
- assert(entry.lookup_by == .primary_key);
1237
- } else unreachable,
1238
- .timestamp => |timestamp| {
1239
- assert(object.timestamp == timestamp);
1240
- assert(entry.lookup_by == .primary_key or
1241
- entry.lookup_by == .timestamp);
1242
- },
1243
- }
1244
-
1245
- switch (entry.lookup_by) {
1246
- .primary_key => worker.context.groove.objects_cache.upsert(object),
1247
- .timestamp => if (has_id) {
1248
- worker.context.groove.objects_cache.upsert(object);
1249
- worker.context.groove.timestamps.set(
1250
- object.timestamp,
1251
- .{ .found = object.id },
1252
- );
1253
- } else unreachable,
1254
- }
1255
- } else switch (entry.lookup_by) {
1256
- // If the object wasn't found, it should've been prefetched by timestamp,
1257
- // or handled by `lookup_id_callback`.
1258
- .primary_key => assert(!has_id),
1259
- .timestamp => if (has_id) worker.context.groove.timestamps.set(
1260
- entry.key.timestamp,
1261
- .not_found,
1262
- ) else unreachable,
1263
- }
1264
-
1265
- worker.lookup_start_next();
1266
- }
1267
- };
1268
-
1269
- /// Insert the value into the objects tree and associated index trees. It's up to the
1270
- /// caller to ensure it doesn't already exist.
1271
- pub fn insert(groove: *Groove, object: *const Object) void {
1272
- assert(TimestampRange.valid(object.timestamp));
1273
-
1274
- if (ObjectsCache != void) {
1275
- assert(!groove.objects_cache.has(@field(object, primary_field)));
1276
- groove.objects_cache.upsert(object);
1277
- }
1278
-
1279
- if (has_id) {
1280
- groove.ids.put(&IdTreeValue{ .id = object.id, .timestamp = object.timestamp });
1281
- groove.ids.key_range_update(object.id);
1282
- }
1283
- groove.objects.put(object);
1284
- groove.objects.key_range_update(object.timestamp);
1285
-
1286
- inline for (std.meta.fields(IndexTrees)) |field| {
1287
- const Helper = IndexTreeFieldHelperType(field.name);
1288
- if (Helper.index_from_object(object)) |value| {
1289
- @field(groove.indexes, field.name).put(&.{
1290
- .timestamp = object.timestamp,
1291
- .field = value,
1292
- });
1293
- }
1294
- }
1295
- }
1296
-
1297
- /// Update the value. Requires the old object to be provided.
1298
- /// Update the object and index trees by diff'ing the old and new values.
1299
- pub fn update(
1300
- groove: *Groove,
1301
- values: struct { old: *const Object, new: *const Object },
1302
- ) void {
1303
- const old = values.old;
1304
- const new = values.new;
1305
-
1306
- if (ObjectsCache != void) {
1307
- const old_from_cache = groove.objects_cache.get(@field(old, primary_field)).?;
1308
- assert(stdx.equal_bytes(Object, old_from_cache, old));
1309
- }
1310
-
1311
- // Sanity check to ensure the caller didn't accidentally pass in an alias.
1312
- assert(new != old);
1313
-
1314
- if (has_id) assert(old.id == new.id);
1315
- assert(old.timestamp == new.timestamp);
1316
- assert(TimestampRange.valid(new.timestamp));
1317
-
1318
- // The ID can't change, so no need to update the ID tree. Update the object tree entry
1319
- // if any of the fields (even ignored) are different. We assume the caller will pass in
1320
- // an object that has changes.
1321
- // Unlike the index trees, the new and old values in the object tree share the same
1322
- // key. Therefore put() is sufficient to overwrite the old value.
1323
- {
1324
- const tombstone = ObjectTreeHelper.tombstone;
1325
- const key_from_value = ObjectTreeHelper.key_from_value;
1326
-
1327
- assert(!stdx.equal_bytes(Object, old, new));
1328
- assert(key_from_value(old) == key_from_value(new));
1329
- assert(!tombstone(old) and !tombstone(new));
1330
- }
1331
-
1332
- inline for (std.meta.fields(IndexTrees)) |field| {
1333
- const Helper = IndexTreeFieldHelperType(field.name);
1334
- const old_index = Helper.index_from_object(old);
1335
- const new_index = Helper.index_from_object(new);
1336
-
1337
- // Only update the indexes that change.
1338
- if (old_index != new_index) {
1339
- if (old_index) |value| {
1340
- @field(groove.indexes, field.name).remove(&.{
1341
- .timestamp = old.timestamp,
1342
- .field = value,
1343
- });
1344
- }
1345
- if (new_index) |value| {
1346
- @field(groove.indexes, field.name).put(&.{
1347
- .timestamp = new.timestamp,
1348
- .field = value,
1349
- });
1350
- }
1351
- }
1352
- }
1353
-
1354
- // Putting the objects_cache upsert after the index tree updates is critical:
1355
- // We diff the old and new objects, but the old object will be a pointer into the
1356
- // objects_cache. If we upsert first, there's a high chance old.* == new.* (always,
1357
- // unless old comes from the stash) and no secondary indexes will be updated!
1358
-
1359
- if (ObjectsCache != void) {
1360
- groove.objects_cache.upsert(new);
1361
- }
1362
- groove.objects.put(new);
1363
- }
1364
-
1365
- /// Asserts that the object with the given PrimaryKey exists.
1366
- pub fn remove(groove: *Groove, key: PrimaryKey) void {
1367
- // TODO: Nothing currently calls or tests this method. The forest fuzzer should be
1368
- // extended to cover it.
1369
- assert(false);
1370
-
1371
- const object = groove.objects_cache.get(key).?;
1372
- assert(TimestampRange.valid(object.timestamp));
1373
-
1374
- // TODO: should update the timestamp and id range, see `key_range_update`.
1375
- groove.objects.remove(object);
1376
- if (has_id) {
1377
- groove.ids.remove(&IdTreeValue{ .id = object.id, .timestamp = object.timestamp });
1378
- }
1379
-
1380
- groove.objects_cache.remove(key);
1381
-
1382
- inline for (std.meta.fields(IndexTrees)) |field| {
1383
- const Helper = IndexTreeFieldHelperType(field.name);
1384
- if (Helper.index_from_object(object)) |value| {
1385
- @field(groove.indexes, field.name).remove(&.{
1386
- .timestamp = object.timestamp,
1387
- .field = value,
1388
- });
1389
- }
1390
- }
1391
- }
1392
-
1393
- /// Insert an id associated with no object.
1394
- /// It's up to the caller to ensure it doesn't already exist.
1395
- pub fn insert_orphaned_id(groove: *Groove, id: u128) void {
1396
- comptime assert(groove_options.orphaned_ids);
1397
- comptime assert(has_id);
1398
-
1399
- assert(id != 0);
1400
- assert(id != std.math.maxInt(u128));
1401
-
1402
- // We should not insert an orphaned `id` inside a scope.
1403
- assert(!groove.objects_cache.scope_is_active);
1404
- assert(groove.ids.active_scope == null);
1405
- assert(!groove.objects_cache.has(id));
1406
-
1407
- groove.objects_cache.upsert(&std.mem.zeroInit(Object, .{ .id = id }));
1408
- groove.ids.put(&.{ .id = id, .timestamp = 0 });
1409
- groove.ids.key_range_update(id);
1410
- }
1411
-
1412
- pub fn remove_orphaned_id(groove: *Groove, id: u128) void {
1413
- comptime assert(groove_options.orphaned_ids);
1414
- comptime assert(has_id);
1415
-
1416
- // TODO: Nothing currently calls or tests this method. The forest fuzzer should be
1417
- // extended to cover it.
1418
- assert(false);
1419
-
1420
- _ = groove;
1421
- _ = id;
1422
- }
1423
-
1424
- pub fn scope_open(groove: *Groove) void {
1425
- if (ObjectsCache != void) groove.objects_cache.scope_open();
1426
-
1427
- if (has_id) {
1428
- groove.ids.scope_open();
1429
- }
1430
- groove.objects.scope_open();
1431
-
1432
- inline for (std.meta.fields(IndexTrees)) |field| {
1433
- @field(groove.indexes, field.name).scope_open();
1434
- }
1435
- }
1436
-
1437
- pub fn scope_close(groove: *Groove, mode: ScopeCloseMode) void {
1438
- if (ObjectsCache != void) groove.objects_cache.scope_close(mode);
1439
-
1440
- if (has_id) {
1441
- groove.ids.scope_close(mode);
1442
- }
1443
- groove.objects.scope_close(mode);
1444
-
1445
- inline for (std.meta.fields(IndexTrees)) |field| {
1446
- @field(groove.indexes, field.name).scope_close(mode);
1447
- }
1448
- }
1449
-
1450
- pub fn compact(groove: *Groove, op: u64) void {
1451
- if (has_id) groove.ids.compact();
1452
- groove.objects.compact();
1453
-
1454
- inline for (std.meta.fields(IndexTrees)) |field| {
1455
- @field(groove.indexes, field.name).compact();
1456
- }
1457
-
1458
- // Compact the objects_cache on the last beat of the bar, just like the trees do to
1459
- // their mutable tables.
1460
- if (ObjectsCache != void) {
1461
- const compaction_beat = op % constants.lsm_compaction_ops;
1462
- if (compaction_beat == constants.lsm_compaction_ops - 1) {
1463
- groove.objects_cache.compact();
1464
- }
1465
- }
1466
- }
1467
-
1468
- pub fn open_commence(groove: *Groove, manifest_log: *ManifestLog) void {
1469
- if (has_id) groove.ids.open_commence(manifest_log);
1470
- groove.objects.open_commence(manifest_log);
1471
-
1472
- inline for (std.meta.fields(IndexTrees)) |field| {
1473
- @field(groove.indexes, field.name).open_commence(manifest_log);
1474
- }
1475
- }
1476
-
1477
- pub fn open_complete(groove: *Groove) void {
1478
- if (has_id) groove.ids.open_complete();
1479
- groove.objects.open_complete();
1480
-
1481
- inline for (std.meta.fields(IndexTrees)) |field| {
1482
- @field(groove.indexes, field.name).open_complete();
1483
- }
1484
- }
1485
-
1486
- pub fn assert_between_bars(groove: *const Groove) void {
1487
- if (has_id) groove.ids.assert_between_bars();
1488
- groove.objects.assert_between_bars();
1489
-
1490
- inline for (std.meta.fields(IndexTrees)) |field| {
1491
- @field(groove.indexes, field.name).assert_between_bars();
1492
- }
1493
- }
1494
- };
1495
- }