tigerbeetle 0.0.36 → 0.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/ext/tb_client/extconf.rb +13 -13
  4. data/ext/tb_client/tigerbeetle/LICENSE +177 -0
  5. data/ext/tb_client/tigerbeetle/build.zig +2327 -0
  6. data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
  7. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
  8. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
  9. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
  10. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
  11. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
  12. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
  13. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
  14. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1092 -0
  15. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
  16. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
  17. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
  18. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
  19. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
  20. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
  21. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
  22. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
  23. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
  24. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
  25. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
  26. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
  27. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
  28. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
  29. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
  30. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
  31. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
  32. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
  33. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
  34. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
  35. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
  36. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
  37. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
  38. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
  39. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
  40. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
  41. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
  42. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
  43. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
  44. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
  45. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
  46. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
  47. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
  48. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
  49. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
  50. data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
  51. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
  52. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
  53. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
  54. data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
  55. data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
  56. data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
  57. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
  58. data/ext/tb_client/tigerbeetle/src/direction.zig +120 -0
  59. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
  60. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
  61. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
  62. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
  63. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
  64. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
  65. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
  66. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
  67. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
  68. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
  69. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
  70. data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
  71. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
  72. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
  73. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
  74. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
  75. data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
  76. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
  77. data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
  78. data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
  79. data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
  80. data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
  81. data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
  82. data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
  83. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
  84. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
  85. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
  86. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
  87. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
  88. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
  89. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
  90. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
  91. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
  92. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
  93. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
  94. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
  95. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
  96. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
  97. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
  98. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
  99. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
  100. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
  101. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
  102. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
  103. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
  104. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
  105. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +359 -0
  106. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
  107. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
  108. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +962 -0
  109. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
  110. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
  111. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
  112. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
  113. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
  114. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
  115. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
  116. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
  117. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +90 -0
  118. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
  119. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
  120. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
  121. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +534 -0
  122. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
  123. data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
  124. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
  125. data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
  126. data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
  127. data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
  128. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
  129. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
  130. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
  131. data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
  132. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
  133. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
  134. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
  135. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
  136. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
  137. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
  138. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
  139. data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
  140. data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
  141. data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
  142. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
  143. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
  144. data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
  145. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
  146. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
  147. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
  148. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
  149. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
  150. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
  151. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
  152. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
  153. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
  154. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
  155. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
  156. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
  157. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
  158. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
  159. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
  160. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
  161. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
  162. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
  163. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
  164. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
  165. data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
  166. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
  167. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
  168. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
  169. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
  170. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
  171. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
  172. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
  173. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
  174. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
  175. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
  176. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
  177. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
  178. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
  179. data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
  180. data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
  181. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
  182. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
  183. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
  184. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
  185. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
  186. data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
  187. data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
  188. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
  189. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
  190. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
  191. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
  192. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
  193. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
  194. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
  195. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
  196. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
  197. data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
  198. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
  199. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
  200. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
  201. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
  202. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
  203. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
  204. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
  205. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
  206. data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
  207. data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
  208. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
  209. data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
  210. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
  211. data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
  212. data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
  213. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
  214. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
  215. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
  216. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
  217. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
  218. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
  219. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
  220. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
  221. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
  222. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
  223. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
  224. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
  225. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
  226. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
  227. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
  228. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
  229. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
  230. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
  231. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
  232. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
  233. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
  234. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
  235. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2928 -0
  236. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
  237. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
  238. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
  239. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
  240. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
  241. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
  242. data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
  243. data/lib/tb_client/shared_lib.rb +12 -5
  244. data/lib/tigerbeetle/platforms.rb +9 -0
  245. data/lib/tigerbeetle/version.rb +2 -2
  246. data/tigerbeetle.gemspec +22 -5
  247. metadata +242 -3
  248. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,543 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+
4
+ const Allocator = std.mem.Allocator;
5
+
6
+ const stdx = @import("stdx");
7
+ const constants = @import("../constants.zig");
8
+ const is_composite_key = @import("composite_key.zig").is_composite_key;
9
+
10
+ const ScanTreeType = @import("scan_tree.zig").ScanTreeType;
11
+ const ScanMergeUnionType = @import("scan_merge.zig").ScanMergeUnionType;
12
+ const ScanMergeIntersectionType = @import("scan_merge.zig").ScanMergeIntersectionType;
13
+ const ScanMergeDifferenceType = @import("scan_merge.zig").ScanMergeDifferenceType;
14
+ const ScanBuffer = @import("scan_buffer.zig").ScanBuffer;
15
+ const ScanState = @import("scan_state.zig").ScanState;
16
+
17
+ const Direction = @import("../direction.zig").Direction;
18
+ const TimestampRange = @import("timestamp_range.zig").TimestampRange;
19
+
20
+ const Error = @import("scan_buffer.zig").Error;
21
+
22
+ /// Scans work with asynchronous iterators --- streams.
23
+ ///
24
+ /// Iterator has `fn next() ?Item`,
25
+ /// while a stream has `fn next() Pending!?Item`
26
+ ///
27
+ /// When a stream returns `error.Pending`, this means that it is unknown
28
+ /// whether a stream has more items or not, and further IO is required.
29
+ const Pending = error{Pending};
30
+
31
+ /// ScanBuilder is a helper to create and combine scans using
32
+ /// any of the Groove's indexes.
33
+ pub fn ScanBuilderType(
34
+ // TODO: Instead of a single Groove per ScanType, introduce the concept of Orthogonal Grooves.
35
+ // For example, indexes from the Grooves `Transfers` and `PendingTransfers` can be
36
+ // used together in the same query, and the timestamp they produce can be used for
37
+ // lookups in either Grooves:
38
+ // ```
39
+ // SELECT Transfers WHERE Transfers.code=1 AND Transfers.pending_status=posted.
40
+ // ```
41
+ //
42
+ // Although the relation between orthogonal grooves is always 1:1 by the timestamp,
43
+ // when looking up an object in the Groove `A` by a timestamp found in the Groove `B`, it will
44
+ // require additional information to correctly assert if `B` "must have" or "may have" a
45
+ // corresponding match in `A`.
46
+ // E.g.: Every AccountBalance **must have** a corresponding Account, however the opposite
47
+ // isn't true.
48
+ // ```
49
+ // SELECT AccountBalances WHERE Accounts.user_data_32=100
50
+ // ```
51
+ comptime Groove: type,
52
+ comptime Storage: type,
53
+ ) type {
54
+ return struct {
55
+ const ScanBuilder = @This();
56
+
57
+ pub const Scan = ScanType(Groove, Storage);
58
+
59
+ /// Each `ScanTree` consumes memory and I/O, so they are limited by `lsm_scans_max`.
60
+ scans: *[constants.lsm_scans_max]Scan,
61
+ scan_count: u32 = 0,
62
+
63
+ /// Merging `ScanTree`s does not require additional resources, so `ScanMerge`s are stored
64
+ /// in a separate buffer limited to `lsm_scans_max - 1`.
65
+ /// If `lsm_scans_max = 4`, we can have at most 4 scans and 3 merge operations:
66
+ /// M₁(M₂(S₁, S₂), M₃(S₃, S₄)).
67
+ merges: *[constants.lsm_scans_max - 1]Scan,
68
+ merge_count: u32 = 0,
69
+
70
+ pub fn init(self: *ScanBuilder, allocator: Allocator) !void {
71
+ self.* = .{
72
+ .scans = undefined,
73
+ .merges = undefined,
74
+ };
75
+
76
+ self.scans = try allocator.create([constants.lsm_scans_max]Scan);
77
+ errdefer allocator.destroy(self.scans);
78
+
79
+ self.merges = try allocator.create([constants.lsm_scans_max - 1]Scan);
80
+ errdefer allocator.destroy(self.merges);
81
+ }
82
+
83
+ pub fn deinit(self: *ScanBuilder, allocator: Allocator) void {
84
+ allocator.destroy(self.scans);
85
+ allocator.destroy(self.merges);
86
+
87
+ self.* = undefined;
88
+ }
89
+
90
+ pub fn reset(self: *ScanBuilder) void {
91
+ self.* = .{
92
+ .scans = self.scans,
93
+ .merges = self.merges,
94
+ };
95
+ }
96
+
97
+ /// Initializes a Scan over the secondary index specified by `index`,
98
+ /// searching for an exact match in the `CompositeKey`'s prefix.
99
+ /// Produces the criteria equivalent to `WHERE field = $value`.
100
+ /// Results are ordered by `timestamp`.
101
+ pub fn scan_prefix(
102
+ self: *ScanBuilder,
103
+ comptime index: std.meta.FieldEnum(Groove.IndexTrees),
104
+ buffer: *const ScanBuffer,
105
+ snapshot: u64,
106
+ prefix: CompositeKeyPrefixType(index),
107
+ timestamp_range: TimestampRange,
108
+ direction: Direction,
109
+ ) *Scan {
110
+ const field = comptime std.enums.nameCast(std.meta.FieldEnum(Scan.Dispatcher), index);
111
+ const ScanImpl = ScanImplType(field);
112
+ return self.scan_add(
113
+ field,
114
+ ScanImpl.init(
115
+ &@field(self.groove().indexes, @tagName(index)),
116
+ buffer,
117
+ snapshot,
118
+ key_from_value(index, prefix, timestamp_range.min),
119
+ key_from_value(index, prefix, timestamp_range.max),
120
+ direction,
121
+ ),
122
+ ) catch unreachable; //TODO: define error handling for the query API.
123
+ }
124
+
125
+ /// Initializes a Scan over the `id` searching for an exact match.
126
+ /// Produces the criteria equivalent to `WHERE id = $value`.
127
+ /// Results are always unique.
128
+ pub fn scan_id(
129
+ self: *ScanBuilder,
130
+ buffer: *const ScanBuffer,
131
+ snapshot: u64,
132
+ id: u128,
133
+ direction: Direction,
134
+ ) *Scan {
135
+ comptime assert(Groove.IdTree != void);
136
+
137
+ const ScanImpl = ScanImplType(.id);
138
+ return self.scan_add(
139
+ .id,
140
+ ScanImpl.init(
141
+ &self.groove().ids,
142
+ buffer,
143
+ snapshot,
144
+ id,
145
+ id,
146
+ direction,
147
+ ),
148
+ ) catch unreachable; //TODO: define error handling for the query API.
149
+ }
150
+
151
+ /// Initializes a Scan over a timestamp range.
152
+ /// Produces the criteria equivalent to `WHERE timestamp BETWEEN $min AND $max`.
153
+ /// Results are ordered by `timestamp`.
154
+ pub fn scan_timestamp(
155
+ self: *ScanBuilder,
156
+ buffer: *const ScanBuffer,
157
+ snapshot: u64,
158
+ timestamp_range: TimestampRange,
159
+ direction: Direction,
160
+ ) *Scan {
161
+ const ScanImpl = ScanImplType(.timestamp);
162
+ return self.scan_add(
163
+ .timestamp,
164
+ ScanImpl.init(
165
+ &self.groove().objects,
166
+ buffer,
167
+ snapshot,
168
+ timestamp_range.min,
169
+ timestamp_range.max,
170
+ direction,
171
+ ),
172
+ ) catch unreachable; //TODO: define error handling for the query API.
173
+ }
174
+
175
+ /// Initializes a Scan performing the union operation over multiple scans.
176
+ /// E.g. S₁ ∪ S₂ ∪ Sₙ.
177
+ /// Produces the criteria equivalent to
178
+ /// `WHERE <condition_1> OR <condition_2> OR <condition_N>`.
179
+ ///
180
+ /// All scans must yield results in the same direction.
181
+ pub fn merge_union(
182
+ self: *ScanBuilder,
183
+ scans: []const *Scan,
184
+ ) *Scan {
185
+ const Impl = ScanImplType(.merge_union);
186
+ return self.merge_add(
187
+ .merge_union,
188
+ Impl.init(scans),
189
+ ) catch unreachable; //TODO: define error handling for the query API.;
190
+ }
191
+
192
+ /// Initializes a Scan performing the intersection operation over multiple scans.
193
+ /// E.g. S₁ ∩ S₂ ∩ Sₙ.
194
+ /// Produces the criteria equivalent to
195
+ /// WHERE <condition_1> AND <condition_2> AND <condition_N>`.
196
+ ///
197
+ /// All scans must yield results in the same direction.
198
+ pub fn merge_intersection(
199
+ self: *ScanBuilder,
200
+ scans: []const *Scan,
201
+ ) *Scan {
202
+ const Impl = ScanImplType(.merge_intersection);
203
+ return self.merge_add(
204
+ .merge_intersection,
205
+ Impl.init(scans),
206
+ ) catch unreachable; //TODO: define error handling for the query API.;
207
+ }
208
+
209
+ /// Initializes a Scan performing the difference (minus) of two scans.
210
+ /// E.g. S₁ - S₂.
211
+ /// Produces the criteria equivalent to
212
+ /// `WHERE <condition_1> AND NOT <condition_2>`.
213
+ ///
214
+ /// Both scans must yield results in the same direction.
215
+ pub fn merge_difference(
216
+ self: *ScanBuilder,
217
+ scan_a: *Scan,
218
+ scan_b: *Scan,
219
+ ) *Scan {
220
+ _ = scan_b;
221
+ _ = scan_a;
222
+ _ = self;
223
+ stdx.unimplemented("merge_difference not implemented");
224
+ }
225
+
226
+ fn scan_add(
227
+ self: *ScanBuilder,
228
+ comptime field: std.meta.FieldEnum(Scan.Dispatcher),
229
+ init_expression: ScanImplType(field),
230
+ ) Error!*Scan {
231
+ if (self.scan_count == self.scans.len) {
232
+ return Error.ScansMaxExceeded;
233
+ }
234
+
235
+ const scan = &self.scans[self.scan_count];
236
+ self.scan_count += 1;
237
+ assert(self.scan_count <= self.scans.len);
238
+
239
+ scan.* = .{
240
+ .dispatcher = @unionInit(
241
+ Scan.Dispatcher,
242
+ @tagName(field),
243
+ init_expression,
244
+ ),
245
+ .assigned = false,
246
+ };
247
+
248
+ return scan;
249
+ }
250
+
251
+ fn merge_add(
252
+ self: *ScanBuilder,
253
+ comptime field: std.meta.FieldEnum(Scan.Dispatcher),
254
+ init_expression: ScanImplType(field),
255
+ ) Error!*Scan {
256
+ if (self.merge_count == self.merges.len) {
257
+ return Error.ScansMaxExceeded;
258
+ }
259
+
260
+ const scan = &self.merges[self.merge_count];
261
+ self.merge_count += 1;
262
+ assert(self.merge_count <= self.merges.len);
263
+
264
+ scan.* = .{
265
+ .dispatcher = @unionInit(
266
+ Scan.Dispatcher,
267
+ @tagName(field),
268
+ init_expression,
269
+ ),
270
+ .assigned = false,
271
+ };
272
+
273
+ return scan;
274
+ }
275
+
276
+ fn CompositeKeyType(comptime index: std.meta.FieldEnum(Groove.IndexTrees)) type {
277
+ const IndexTree = @FieldType(Groove.IndexTrees, @tagName(index));
278
+ return IndexTree.Table.Value;
279
+ }
280
+
281
+ fn CompositeKeyPrefixType(comptime index: std.meta.FieldEnum(Groove.IndexTrees)) type {
282
+ const CompositeKey = CompositeKeyType(index);
283
+ return @FieldType(CompositeKey, "field");
284
+ }
285
+
286
+ fn ScanImplType(comptime field: std.meta.FieldEnum(Scan.Dispatcher)) type {
287
+ return @FieldType(Scan.Dispatcher, @tagName(field));
288
+ }
289
+
290
+ fn key_from_value(
291
+ comptime field: std.meta.FieldEnum(Groove.IndexTrees),
292
+ prefix: CompositeKeyPrefixType(field),
293
+ timestamp: u64,
294
+ ) CompositeKeyType(field).Key {
295
+ return CompositeKeyType(field).key_from_value(&.{
296
+ .field = prefix,
297
+ .timestamp = timestamp,
298
+ });
299
+ }
300
+
301
+ inline fn groove(self: *ScanBuilder) *Groove {
302
+ return @alignCast(@fieldParentPtr("scan_builder", self));
303
+ }
304
+ };
305
+ }
306
+
307
+ /// Common `Scan` interface.
308
+ ///
309
+ /// Allows combining different underlying scans into a single output,
310
+ /// for example `(A₁ ∪ A₂) ∩ B₁` produces the criteria equivalent to
311
+ /// `WHERE (<condition_a1> OR <condition_a2>) AND <condition_b>`.
312
+ pub fn ScanType(
313
+ comptime Groove: type,
314
+ comptime Storage: type,
315
+ ) type {
316
+ return struct {
317
+ const Scan = @This();
318
+
319
+ /// This pattern of callback `fn(*Context, *Scan)` with the `Context` holding the function
320
+ /// pointer is well suited for this use case as it allows composing multiple scans from
321
+ /// a pool without requiring the caller to keep track of the reference of the topmost scan.
322
+ /// Example:
323
+ /// ```
324
+ /// var scan1: *Scan = ... // Add some condition
325
+ /// var scan2: *Scan = ... // Add another condition
326
+ /// var scan3: *Scan = merge_union(scan1, scan2); // Merge both scans.
327
+ /// scan3.read(&context); // scan3 will be returned during the callback.
328
+ /// ```
329
+ pub const Callback = *const fn (*Context, *Scan) void;
330
+ pub const Context = struct {
331
+ callback: Callback,
332
+ };
333
+
334
+ /// Comptime dispatcher for all scan implementations that share the same interface.
335
+ /// Generates a tagged union with an specialized `ScanTreeType` for each queryable field in
336
+ /// the `Groove` (e.g. `timestamp`, `id` if present, and secondary indexes), plus a
337
+ /// `ScanMergeType` for each merge operation (e.g. union, intersection, and difference).
338
+ ///
339
+ /// Example:
340
+ /// ```
341
+ /// const Dispatcher = union(enum) {
342
+ /// .timestamp: ScanTree(...),
343
+ /// .id: ScanTree(...),
344
+ /// .code: ScanTree(...),
345
+ /// .ledger: ScanTree(...),
346
+ /// // ... All other indexes ...
347
+ /// .merge_union: ...
348
+ /// .merge_intersection: ...
349
+ /// .merge_difference: ...
350
+ /// };
351
+ /// ```
352
+ pub const Dispatcher = T: {
353
+ var type_info = @typeInfo(union(enum) {
354
+ timestamp: ScanTreeType(*Context, Groove.ObjectTree, Storage),
355
+
356
+ merge_union: ScanMergeUnionType(Groove, Storage),
357
+ merge_intersection: ScanMergeIntersectionType(Groove, Storage),
358
+ merge_difference: ScanMergeDifferenceType(Groove, Storage),
359
+ });
360
+
361
+ // Union field for the id tree:
362
+ if (Groove.IdTree != void) {
363
+ const ScanTree = ScanTreeType(*Context, Groove.IdTree, Storage);
364
+ type_info.@"union".fields = type_info.@"union".fields ++
365
+ [_]std.builtin.Type.UnionField{.{
366
+ .name = "id",
367
+ .type = ScanTree,
368
+ .alignment = @alignOf(ScanTree),
369
+ }};
370
+ }
371
+
372
+ // Union fields for each index tree:
373
+ for (std.meta.fields(Groove.IndexTrees)) |field| {
374
+ const IndexTree = field.type;
375
+ const ScanTree = ScanTreeType(*Context, IndexTree, Storage);
376
+ type_info.@"union".fields = type_info.@"union".fields ++
377
+ [_]std.builtin.Type.UnionField{.{
378
+ .name = field.name,
379
+ .type = ScanTree,
380
+ .alignment = @alignOf(ScanTree),
381
+ }};
382
+ }
383
+
384
+ // We need a tagged union for dynamic dispatching.
385
+ type_info.@"union".tag_type = blk: {
386
+ const union_fields = type_info.@"union".fields;
387
+ var tag_fields: [union_fields.len]std.builtin.Type.EnumField =
388
+ undefined;
389
+ for (&tag_fields, union_fields, 0..) |*tag_field, union_field, i| {
390
+ tag_field.* = .{
391
+ .name = union_field.name,
392
+ .value = i,
393
+ };
394
+ }
395
+
396
+ break :blk @Type(.{ .@"enum" = .{
397
+ .tag_type = std.math.IntFittingRange(0, tag_fields.len - 1),
398
+ .fields = &tag_fields,
399
+ .decls = &.{},
400
+ .is_exhaustive = true,
401
+ } });
402
+ };
403
+
404
+ break :T @Type(type_info);
405
+ };
406
+
407
+ dispatcher: Dispatcher,
408
+ assigned: bool,
409
+
410
+ pub fn read(scan: *Scan, context: *Context) void {
411
+ @setEvalBranchQuota(4_000);
412
+ switch (scan.dispatcher) {
413
+ inline else => |*scan_impl, tag| {
414
+ const Impl = @TypeOf(scan_impl.*);
415
+ const on_read_callback = struct {
416
+ fn callback(ctx: *Context, ptr: *Impl) void {
417
+ ctx.callback(ctx, parent(tag, ptr));
418
+ }
419
+ }.callback;
420
+ scan_impl.read(context, on_read_callback);
421
+ },
422
+ }
423
+ }
424
+
425
+ pub fn next(scan: *Scan) Pending!?u64 {
426
+ switch (scan.dispatcher) {
427
+ inline .merge_union,
428
+ .merge_intersection,
429
+ .merge_difference,
430
+ => |*scan_merge| return try scan_merge.next(),
431
+ inline else => |*scan_tree, index| {
432
+ while (try scan_tree.next()) |value| {
433
+ const ScanTree = @TypeOf(scan_tree.*);
434
+ if (ScanTree.Tree.Table.tombstone(&value)) {
435
+ // When iterating over `ScanTreeType`, it can return a tombstone, which
436
+ // indicates the value was deleted, and must be ignored in the results.
437
+ continue;
438
+ }
439
+
440
+ if (Groove.IdTree != void) {
441
+ if (index == .id) {
442
+ // When iterating over `IdTree` it can return a timestamp zero,
443
+ // which indicates an orphaned id.
444
+ if (value.timestamp == 0) {
445
+ assert(Groove.config.orphaned_ids);
446
+ continue;
447
+ }
448
+ }
449
+ }
450
+
451
+ assert(value.timestamp >= TimestampRange.timestamp_min);
452
+ assert(value.timestamp <= TimestampRange.timestamp_max);
453
+ return value.timestamp;
454
+ }
455
+ return null;
456
+ },
457
+ }
458
+ }
459
+
460
+ pub fn state(scan: *const Scan) ScanState {
461
+ switch (scan.dispatcher) {
462
+ inline else => |*scan_impl| return scan_impl.state,
463
+ }
464
+ }
465
+
466
+ pub fn snapshot(scan: *const Scan) u64 {
467
+ return switch (scan.dispatcher) {
468
+ inline else => |*scan_impl| scan_impl.snapshot,
469
+ };
470
+ }
471
+
472
+ pub fn probe(scan: *Scan, timestamp: u64) void {
473
+ switch (scan.dispatcher) {
474
+ inline .timestamp,
475
+ .merge_union,
476
+ .merge_intersection,
477
+ .merge_difference,
478
+ => |*scan_impl| scan_impl.probe(timestamp),
479
+ inline else => |*scan_impl, tag| {
480
+ const ScanTree = @TypeOf(scan_impl.*);
481
+ const Value = ScanTree.Tree.Table.Value;
482
+
483
+ if (comptime is_composite_key(Value)) {
484
+ const prefix = prefix: {
485
+ const prefix_lower = Value.key_prefix(scan_impl.key_lower);
486
+ const prefix_upper = Value.key_prefix(scan_impl.key_upper);
487
+ assert(prefix_lower == prefix_upper);
488
+ break :prefix prefix_lower;
489
+ };
490
+ scan_impl.probe(Value.key_from_value(&.{
491
+ .field = prefix,
492
+ .timestamp = timestamp,
493
+ }));
494
+ } else {
495
+ comptime assert(tag == .id);
496
+ comptime assert(Groove.IdTree != void);
497
+
498
+ // Scans over the IdTree cannot probe for a next timestamp.
499
+ assert(scan_impl.key_lower == scan_impl.key_upper);
500
+ }
501
+ },
502
+ }
503
+ }
504
+
505
+ /// Returns the direction of the output timestamp values.
506
+ pub fn direction(scan: *const Scan) Direction {
507
+ switch (scan.dispatcher) {
508
+ inline .timestamp,
509
+ .merge_union,
510
+ .merge_intersection,
511
+ .merge_difference,
512
+ => |*scan_impl| return scan_impl.direction,
513
+ inline else => |*scan_impl, tag| {
514
+ const ScanTree = @TypeOf(scan_impl.*);
515
+ const Value = ScanTree.Tree.Table.Value;
516
+ if (comptime is_composite_key(Value)) {
517
+ // Secondary indexes can only produce results sorted by timestamp if
518
+ // scanning over the same key prefix.
519
+ assert(Value.key_prefix(scan_impl.key_lower) ==
520
+ Value.key_prefix(scan_impl.key_upper));
521
+ } else {
522
+ comptime assert(tag == .id);
523
+ comptime assert(Groove.IdTree != void);
524
+ assert(scan_impl.key_lower == scan_impl.key_upper);
525
+ }
526
+
527
+ return scan_impl.direction;
528
+ },
529
+ }
530
+ }
531
+
532
+ inline fn parent(
533
+ comptime field: std.meta.FieldEnum(Dispatcher),
534
+ impl: *@FieldType(Dispatcher, @tagName(field)),
535
+ ) *Scan {
536
+ const dispatcher: *Dispatcher = @alignCast(@fieldParentPtr(
537
+ @tagName(field),
538
+ impl,
539
+ ));
540
+ return @fieldParentPtr("dispatcher", dispatcher);
541
+ }
542
+ };
543
+ }