tigerbeetle 0.0.34 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -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 +1084 -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 +11 -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 +362 -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 +1036 -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 +105 -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 +557 -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 +2910 -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/client.rb +1 -1
  245. data/lib/tigerbeetle/platforms.rb +9 -0
  246. data/lib/tigerbeetle/version.rb +2 -2
  247. data/tigerbeetle.gemspec +22 -5
  248. metadata +242 -3
  249. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,179 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+ const math = std.math;
4
+
5
+ const stdx = @import("stdx");
6
+ const KiB = stdx.KiB;
7
+ const MiB = stdx.MiB;
8
+ const GiB = stdx.GiB;
9
+
10
+ const Bench = @import("../testing/bench.zig");
11
+
12
+ const binary_search_values_upsert_index =
13
+ @import("./binary_search.zig").binary_search_values_upsert_index;
14
+
15
+ const kv_types = .{
16
+ .{ .key_size = @sizeOf(u64), .value_size = 128 },
17
+ .{ .key_size = @sizeOf(u256), .value_size = 32 },
18
+ };
19
+
20
+ const Scenario = struct {
21
+ name: []const u8,
22
+ values_per_page: u32,
23
+ page_buffer_size: usize,
24
+ };
25
+
26
+ const scenarios = [_]Scenario{
27
+ .{
28
+ .name = "in-cache",
29
+ .values_per_page = 64,
30
+ .page_buffer_size = 256 * KiB,
31
+ },
32
+ .{
33
+ .name = "out-of-cache",
34
+ .values_per_page = 4_096,
35
+ .page_buffer_size = 1 * GiB,
36
+ },
37
+ };
38
+
39
+ const body_fmt = "{s} K={:_>2}B V={:_>3}B N={:_>5}: WT={}";
40
+ const repetitions: usize = 32;
41
+
42
+ test "benchmark: binary search" {
43
+ var bench: Bench = .init();
44
+ defer bench.deinit();
45
+
46
+ bench.report("WT: Wall time/search", .{});
47
+
48
+ const blob_size = bench.parameter("blob_size", MiB, GiB);
49
+ const searches = bench.parameter("searches", 500, 20_000);
50
+
51
+ var prng = stdx.PRNG.from_seed(bench.seed);
52
+ var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
53
+ defer arena.deinit();
54
+
55
+ const blob = try arena.allocator().alignedAlloc(u8, 64, blob_size);
56
+ var checksum: u64 = 0;
57
+
58
+ inline for (kv_types) |kv| {
59
+ inline for (scenarios) |scenario| {
60
+ // Clamp for `smoke` mode.
61
+ const page_buffer_size = @min(blob_size, scenario.page_buffer_size);
62
+ checksum +%= try run_benchmark(
63
+ &bench,
64
+ scenario.name,
65
+ .{
66
+ .key_size = kv.key_size,
67
+ .value_size = kv.value_size,
68
+ .values_count = scenario.values_per_page,
69
+ },
70
+ searches,
71
+ blob[0..page_buffer_size],
72
+ arena.allocator(),
73
+ &prng,
74
+ );
75
+ }
76
+ }
77
+ bench.report("checksum {}", .{checksum});
78
+ }
79
+
80
+ fn run_benchmark(
81
+ bench: *Bench,
82
+ scenario_name: []const u8,
83
+ comptime layout: Layout,
84
+ search_count: u64,
85
+ page_buffer: []u8,
86
+ arena: std.mem.Allocator,
87
+ prng: *stdx.PRNG,
88
+ ) !u64 {
89
+ const V = ValueType(layout);
90
+ const K = V.Key;
91
+ const Page = struct {
92
+ values: [layout.values_count]V,
93
+ };
94
+
95
+ const page_count = @divFloor(page_buffer.len, @sizeOf(Page));
96
+ assert(page_count > 0);
97
+ if (page_count > 1024 * 1024) @panic("page_count too large");
98
+
99
+ const page_picker = try arena.alloc(usize, page_count);
100
+ shuffled_index(prng, page_picker);
101
+
102
+ const value_picker = try arena.alloc(usize, layout.values_count);
103
+ shuffled_index(prng, value_picker);
104
+
105
+ var page_alloc = std.heap.FixedBufferAllocator.init(page_buffer);
106
+ const pages = try page_alloc.allocator().alloc(Page, page_count);
107
+ prng.fill(std.mem.sliceAsBytes(pages));
108
+ for (pages) |*page| {
109
+ for (&page.values, 0..) |*value, i| value.key = i;
110
+ }
111
+
112
+ var duration_samples: [repetitions]stdx.Duration = undefined;
113
+ var checksum: u64 = 0;
114
+ for (&duration_samples) |*duration| {
115
+ bench.start();
116
+ for (0..search_count) |i| {
117
+ const target = value_picker[i % value_picker.len];
118
+ const page = &pages[page_picker[i % page_picker.len]];
119
+ const hit = page.values[
120
+ binary_search_values_upsert_index(
121
+ K,
122
+ V,
123
+ V.key_from_value,
124
+ page.values[0..],
125
+ target,
126
+ .{},
127
+ )
128
+ ];
129
+
130
+ assert(hit.key == target);
131
+ checksum +%= @truncate(hit.key);
132
+ }
133
+ duration.* = bench.stop();
134
+ duration.ns /= search_count;
135
+ }
136
+
137
+ const result = bench.estimate(&duration_samples);
138
+
139
+ bench.report(body_fmt, .{
140
+ scenario_name,
141
+ layout.key_size,
142
+ layout.value_size,
143
+ layout.values_count,
144
+ result,
145
+ });
146
+
147
+ return checksum;
148
+ }
149
+
150
+ const Layout = struct {
151
+ key_size: usize, // bytes per key
152
+ value_size: usize, // bytes per value
153
+ values_count: usize, // values per page
154
+ };
155
+
156
+ fn ValueType(comptime layout: Layout) type {
157
+ return struct {
158
+ pub const max_key = (1 << (8 * layout.key_size)) - 1;
159
+ pub const Key = math.IntFittingRange(0, max_key);
160
+ const Value = @This();
161
+ key: Key,
162
+ body: [layout.value_size - layout.key_size]u8,
163
+
164
+ comptime {
165
+ assert(@sizeOf(Key) == layout.key_size);
166
+ assert(@sizeOf(Value) == layout.value_size);
167
+ }
168
+
169
+ inline fn key_from_value(self: *const Value) Key {
170
+ return self.key;
171
+ }
172
+ };
173
+ }
174
+
175
+ // shuffle([0,1,…,n-1])
176
+ fn shuffled_index(prng: *stdx.PRNG, indices: []usize) void {
177
+ for (indices, 0..) |*i, j| i.* = j;
178
+ prng.shuffle(usize, indices);
179
+ }
@@ -0,0 +1,424 @@
1
+ const std = @import("std");
2
+ const constants = @import("../constants.zig");
3
+
4
+ const stdx = @import("stdx");
5
+ const assert = std.debug.assert;
6
+ const maybe = stdx.maybe;
7
+
8
+ const SetAssociativeCacheType = @import("set_associative_cache.zig").SetAssociativeCacheType;
9
+ const ScopeCloseMode = @import("tree.zig").ScopeCloseMode;
10
+
11
+ /// A CacheMap is a hybrid between our SetAssociativeCache and a HashMap (stash). The
12
+ /// SetAssociativeCache sits on top and absorbs the majority of get / put requests. Below that,
13
+ /// lives a HashMap. Should an insert() cause an eviction (which can happen either because the Key
14
+ /// is the same, or because our Way is full), the evicted value is caught and put in the stash.
15
+ ///
16
+ /// This allows for a potentially huge cache, with all the advantages of CLOCK Nth-Chance, while
17
+ /// still being able to give hard guarantees that values will be present. The stash will often be
18
+ /// significantly smaller, as the amount of values we're required to guarantee is less than what
19
+ /// we'd like to optimistically keep in memory.
20
+ ///
21
+ /// Within our LSM, the CacheMap is the backing for the combined Groove prefetch + cache. The cache
22
+ /// part fills the use case of an object cache, while the stash ensures that prefetched values
23
+ /// are available in memory during their respective commit.
24
+ ///
25
+ /// Cache invalidation for the stash is handled by `compact`.
26
+ pub fn CacheMapType(
27
+ comptime Key: type,
28
+ comptime Value: type,
29
+ comptime key_from_value: fn (*const Value) callconv(.@"inline") Key,
30
+ comptime hash_from_key: fn (Key) callconv(.@"inline") u64,
31
+ comptime tombstone_from_key: fn (Key) callconv(.@"inline") Value,
32
+ comptime tombstone: fn (*const Value) callconv(.@"inline") bool,
33
+ ) type {
34
+ return struct {
35
+ const CacheMap = @This();
36
+
37
+ const map_load_percentage_max = 50;
38
+
39
+ pub const Cache = SetAssociativeCacheType(
40
+ Key,
41
+ Value,
42
+ key_from_value,
43
+ hash_from_key,
44
+ .{},
45
+ );
46
+
47
+ pub const Map = std.HashMapUnmanaged(
48
+ Value,
49
+ void,
50
+ struct {
51
+ pub inline fn eql(_: @This(), a: Value, b: Value) bool {
52
+ return key_from_value(&a) == key_from_value(&b);
53
+ }
54
+
55
+ pub inline fn hash(_: @This(), value: Value) u64 {
56
+ return stdx.hash_inline(key_from_value(&value));
57
+ }
58
+ },
59
+ map_load_percentage_max,
60
+ );
61
+
62
+ pub const Options = struct {
63
+ cache_value_count_max: u32,
64
+ stash_value_count_max: u32,
65
+ scope_value_count_max: u32,
66
+ name: []const u8,
67
+ };
68
+
69
+ // The hierarchy for lookups is cache (if present) -> stash -> immutable table -> lsm.
70
+ // Lower levels _may_ have stale values, provided the correct value exists
71
+ // in one of the levels above.
72
+ // Evictions from the cache first flow into stash, with `.compact()` clearing it.
73
+ // When cache is null, the stash mirrors the mutable table.
74
+ cache: ?Cache,
75
+ stash: Map,
76
+
77
+ // Scopes allow you to perform operations on the CacheMap before either persisting or
78
+ // discarding them.
79
+ scope_is_active: bool = false,
80
+ scope_rollback_log: std.ArrayListUnmanaged(Value),
81
+
82
+ options: Options,
83
+
84
+ pub fn init(allocator: std.mem.Allocator, options: Options) !CacheMap {
85
+ assert(options.stash_value_count_max > 0);
86
+ maybe(options.cache_value_count_max == 0);
87
+ maybe(options.scope_value_count_max == 0);
88
+
89
+ var cache: ?Cache = if (options.cache_value_count_max == 0) null else try Cache.init(
90
+ allocator,
91
+ options.cache_value_count_max,
92
+ .{ .name = options.name },
93
+ );
94
+ errdefer if (cache) |*cache_unwrapped| cache_unwrapped.deinit(allocator);
95
+
96
+ var stash: Map = .{};
97
+ try stash.ensureTotalCapacity(allocator, options.stash_value_count_max);
98
+ errdefer stash.deinit(allocator);
99
+
100
+ var scope_rollback_log = try std.ArrayListUnmanaged(Value).initCapacity(
101
+ allocator,
102
+ options.scope_value_count_max,
103
+ );
104
+ errdefer scope_rollback_log.deinit(allocator);
105
+
106
+ return CacheMap{
107
+ .cache = cache,
108
+ .stash = stash,
109
+ .scope_rollback_log = scope_rollback_log,
110
+ .options = options,
111
+ };
112
+ }
113
+
114
+ pub fn deinit(self: *CacheMap, allocator: std.mem.Allocator) void {
115
+ assert(!self.scope_is_active);
116
+ assert(self.scope_rollback_log.items.len == 0);
117
+ assert(self.stash.count() <= self.options.stash_value_count_max);
118
+
119
+ self.scope_rollback_log.deinit(allocator);
120
+ self.stash.deinit(allocator);
121
+ if (self.cache) |*cache| cache.deinit(allocator);
122
+ }
123
+
124
+ pub fn reset(self: *CacheMap) void {
125
+ assert(!self.scope_is_active);
126
+ assert(self.scope_rollback_log.items.len == 0);
127
+ assert(self.stash.count() <= self.options.stash_value_count_max);
128
+
129
+ if (self.cache) |*cache| cache.reset();
130
+ self.stash.clearRetainingCapacity();
131
+
132
+ self.* = .{
133
+ .cache = self.cache,
134
+ .stash = self.stash,
135
+ .scope_rollback_log = self.scope_rollback_log,
136
+ .options = self.options,
137
+ };
138
+ }
139
+
140
+ pub fn has(self: *const CacheMap, key: Key) bool {
141
+ return self.get(key) != null;
142
+ }
143
+
144
+ pub fn get(self: *const CacheMap, key: Key) ?*Value {
145
+ return (if (self.cache) |*cache| cache.get(key) else null) orelse
146
+ self.stash.getKeyPtr(tombstone_from_key(key));
147
+ }
148
+
149
+ pub fn upsert(self: *CacheMap, value: *const Value) void {
150
+ const old_value_maybe = self.fetch_upsert(value);
151
+
152
+ // When upserting into a scope:
153
+ if (self.scope_is_active) {
154
+ if (old_value_maybe) |old_value| {
155
+ // If it was updated, append the old value to the scope rollback log.
156
+ self.scope_rollback_log.appendAssumeCapacity(old_value);
157
+ } else {
158
+ // If it was an insert, append a tombstone to the scope rollback log.
159
+ const key = key_from_value(value);
160
+ const key_tombstone = tombstone_from_key(key);
161
+ self.scope_rollback_log.appendAssumeCapacity(key_tombstone);
162
+ }
163
+ }
164
+ }
165
+
166
+ // Upserts the cache and stash and returns the old value in case of
167
+ // an update.
168
+ fn fetch_upsert(self: *CacheMap, value: *const Value) ?Value {
169
+ if (self.cache) |*cache| {
170
+ const key = key_from_value(value);
171
+ const result = cache.upsert(value);
172
+
173
+ if (result.evicted) |*evicted| {
174
+ switch (result.updated) {
175
+ .update => {
176
+ assert(key_from_value(evicted) == key);
177
+ if (constants.verify) assert(!self.stash.contains(value.*));
178
+
179
+ // There was an eviction because an item was updated,
180
+ // the evicted item is always its previous version.
181
+ return evicted.*;
182
+ },
183
+ .insert => {
184
+ assert(key_from_value(evicted) != key);
185
+
186
+ // There was an eviction because a new item was inserted,
187
+ // the evicted item will be added to the stash.
188
+ const stash_updated = self.stash_upsert(evicted);
189
+
190
+ // We don't expect stale values on the stash.
191
+ assert(stash_updated == null);
192
+ },
193
+ }
194
+ } else {
195
+ // It must be an insert without eviction,
196
+ // since updates always evict the old version.
197
+ assert(result.updated == .insert);
198
+ }
199
+
200
+ // The stash may have the old value if nothing was evicted.
201
+ return self.stash_remove(key);
202
+ } else {
203
+ // No cache.
204
+ // Upserting the stash directly.
205
+ return self.stash_upsert(value);
206
+ }
207
+ }
208
+
209
+ fn stash_upsert(self: *CacheMap, value: *const Value) ?Value {
210
+ defer assert(self.stash.count() <= self.options.stash_value_count_max);
211
+ // Using `getOrPutAssumeCapacity` instead of `putAssumeCapacity` is
212
+ // critical, since we use HashMaps with no Value, `putAssumeCapacity`
213
+ // _will not_ clobber the existing value.
214
+ const gop = self.stash.getOrPutAssumeCapacity(value.*);
215
+ defer gop.key_ptr.* = value.*;
216
+
217
+ return if (gop.found_existing)
218
+ gop.key_ptr.*
219
+ else
220
+ null;
221
+ }
222
+
223
+ pub fn remove(self: *CacheMap, key: Key) void {
224
+ // The only thing that tests this in any depth is the cache_map fuzz itself.
225
+ // Make sure we aren't being called in regular code without another once over.
226
+ comptime assert(constants.verify);
227
+
228
+ const cache_removed: ?Value = if (self.cache) |*cache|
229
+ cache.remove(key)
230
+ else
231
+ null;
232
+
233
+ // We don't allow stale values, so we need to remove from the stash as well,
234
+ // since both can have different versions with the same key.
235
+ const stash_removed: ?Value = self.stash_remove(key);
236
+
237
+ if (self.scope_is_active) {
238
+ // TODO: Actually, does the fuzz catch this...
239
+ self.scope_rollback_log.appendAssumeCapacity(
240
+ cache_removed orelse
241
+ stash_removed orelse return,
242
+ );
243
+ }
244
+ }
245
+
246
+ fn stash_remove(self: *CacheMap, key: Key) ?Value {
247
+ assert(self.stash.count() <= self.options.stash_value_count_max);
248
+ return if (self.stash.fetchRemove(tombstone_from_key(key))) |kv|
249
+ kv.key
250
+ else
251
+ null;
252
+ }
253
+
254
+ /// Start a new scope. Within a scope, changes can be persisted
255
+ /// or discarded. At most one scope can be active at a time.
256
+ pub fn scope_open(self: *CacheMap) void {
257
+ assert(!self.scope_is_active);
258
+ assert(self.scope_rollback_log.items.len == 0);
259
+ self.scope_is_active = true;
260
+ }
261
+
262
+ pub fn scope_close(self: *CacheMap, mode: ScopeCloseMode) void {
263
+ assert(self.scope_is_active);
264
+ self.scope_is_active = false;
265
+
266
+ // We don't need to do anything to persist a scope.
267
+ if (mode == .persist) {
268
+ self.scope_rollback_log.clearRetainingCapacity();
269
+ return;
270
+ }
271
+
272
+ // The scope_rollback_log stores the operations we need to reverse the changes a scope
273
+ // made. They get replayed in reverse order.
274
+ var i: usize = self.scope_rollback_log.items.len;
275
+ while (i > 0) {
276
+ i -= 1;
277
+
278
+ const rollback_value = &self.scope_rollback_log.items[i];
279
+ if (tombstone(rollback_value)) {
280
+ // Reverting an insert consists of a .remove call.
281
+ // The value in here will be a tombstone indicating the original value didn't
282
+ // exist.
283
+ const key = key_from_value(rollback_value);
284
+
285
+ // A tombstone in the rollback log can only occur when the value doesn't exist
286
+ // in _both_ the cache and stash on insert.
287
+ const cache_removed =
288
+ if (self.cache) |*cache| cache.remove(key) != null else false;
289
+
290
+ // The key should be in the stash iff it wasn't in the cache.
291
+ const stash_removed = self.stash_remove(key) != null;
292
+ assert(stash_removed != cache_removed);
293
+ } else {
294
+ // Reverting an update or delete consists of an insert of the original value.
295
+ self.upsert(rollback_value);
296
+ }
297
+ }
298
+
299
+ self.scope_rollback_log.clearRetainingCapacity();
300
+ }
301
+
302
+ pub fn compact(self: *CacheMap) void {
303
+ assert(!self.scope_is_active);
304
+ assert(self.scope_rollback_log.items.len == 0);
305
+ assert(self.stash.count() <= self.options.stash_value_count_max);
306
+
307
+ self.stash.clearRetainingCapacity();
308
+ }
309
+ };
310
+ }
311
+
312
+ pub const TestTable = struct {
313
+ pub const Key = u32;
314
+ pub const Value = struct {
315
+ key: Key,
316
+ value: u32,
317
+ tombstone: bool = false,
318
+ padding: [7]u8 = undefined,
319
+ };
320
+
321
+ pub inline fn key_from_value(v: *const Value) u32 {
322
+ return v.key;
323
+ }
324
+
325
+ pub inline fn compare_keys(a: Key, b: Key) std.math.Order {
326
+ return std.math.order(a, b);
327
+ }
328
+
329
+ pub inline fn tombstone_from_key(a: Key) Value {
330
+ return Value{ .key = a, .value = 0, .tombstone = true };
331
+ }
332
+
333
+ pub inline fn tombstone(a: *const TestTable.Value) bool {
334
+ return a.tombstone;
335
+ }
336
+
337
+ pub inline fn hash(key: TestTable.Key) u64 {
338
+ return stdx.hash_inline(key);
339
+ }
340
+ };
341
+
342
+ pub const TestCacheMap = CacheMapType(
343
+ TestTable.Key,
344
+ TestTable.Value,
345
+ TestTable.key_from_value,
346
+ TestTable.hash,
347
+ TestTable.tombstone_from_key,
348
+ TestTable.tombstone,
349
+ );
350
+
351
+ test "cache_map: unit" {
352
+ const testing = std.testing;
353
+
354
+ const allocator = testing.allocator;
355
+
356
+ var cache_map = try TestCacheMap.init(allocator, .{
357
+ .cache_value_count_max = TestCacheMap.Cache.value_count_max_multiple,
358
+ .scope_value_count_max = 32,
359
+ .stash_value_count_max = 32,
360
+ .name = "test map",
361
+ });
362
+ defer cache_map.deinit(allocator);
363
+
364
+ cache_map.upsert(&.{ .key = 1, .value = 1, .tombstone = false });
365
+ try testing.expectEqual(
366
+ TestTable.Value{ .key = 1, .value = 1, .tombstone = false },
367
+ cache_map.get(1).?.*,
368
+ );
369
+
370
+ // Test scope persisting
371
+ cache_map.scope_open();
372
+ cache_map.upsert(&.{ .key = 2, .value = 2, .tombstone = false });
373
+ try testing.expectEqual(
374
+ TestTable.Value{ .key = 2, .value = 2, .tombstone = false },
375
+ cache_map.get(2).?.*,
376
+ );
377
+ cache_map.scope_close(.persist);
378
+ try testing.expectEqual(
379
+ TestTable.Value{ .key = 2, .value = 2, .tombstone = false },
380
+ cache_map.get(2).?.*,
381
+ );
382
+
383
+ // Test scope discard on updates
384
+ cache_map.scope_open();
385
+ cache_map.upsert(&.{ .key = 2, .value = 22, .tombstone = false });
386
+ cache_map.upsert(&.{ .key = 2, .value = 222, .tombstone = false });
387
+ cache_map.upsert(&.{ .key = 2, .value = 2222, .tombstone = false });
388
+ try testing.expectEqual(
389
+ TestTable.Value{ .key = 2, .value = 2222, .tombstone = false },
390
+ cache_map.get(2).?.*,
391
+ );
392
+ cache_map.scope_close(.discard);
393
+ try testing.expectEqual(
394
+ TestTable.Value{ .key = 2, .value = 2, .tombstone = false },
395
+ cache_map.get(2).?.*,
396
+ );
397
+
398
+ // Test scope discard on inserts
399
+ cache_map.scope_open();
400
+ cache_map.upsert(&.{ .key = 3, .value = 3, .tombstone = false });
401
+ try testing.expectEqual(
402
+ TestTable.Value{ .key = 3, .value = 3, .tombstone = false },
403
+ cache_map.get(3).?.*,
404
+ );
405
+ cache_map.upsert(&.{ .key = 3, .value = 33, .tombstone = false });
406
+ try testing.expectEqual(
407
+ TestTable.Value{ .key = 3, .value = 33, .tombstone = false },
408
+ cache_map.get(3).?.*,
409
+ );
410
+ cache_map.scope_close(.discard);
411
+ assert(!cache_map.has(3));
412
+ assert(cache_map.get(3) == null);
413
+
414
+ // Test scope discard on removes
415
+ cache_map.scope_open();
416
+ cache_map.remove(2);
417
+ assert(!cache_map.has(2));
418
+ assert(cache_map.get(2) == null);
419
+ cache_map.scope_close(.discard);
420
+ try testing.expectEqual(
421
+ TestTable.Value{ .key = 2, .value = 2, .tombstone = false },
422
+ cache_map.get(2).?.*,
423
+ );
424
+ }