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,1219 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
- const mem = std.mem;
4
-
5
- const constants = @import("constants.zig");
6
- const log = std.log.scoped(.message_bus);
7
-
8
- const vsr = @import("vsr.zig");
9
-
10
- const stdx = @import("stdx");
11
- const maybe = stdx.maybe;
12
- const RingBufferType = stdx.RingBufferType;
13
- const MessagePool = @import("message_pool.zig").MessagePool;
14
- const Message = MessagePool.Message;
15
- const MessageBuffer = @import("./message_buffer.zig").MessageBuffer;
16
- const QueueType = @import("./queue.zig").QueueType;
17
- const Tracer = vsr.trace.Tracer;
18
-
19
- pub fn MessageBusType(comptime IO: type) type {
20
- // Slice points to a subslice of send_queue_buffer.
21
- const SendQueue = RingBufferType(*Message, .slice);
22
-
23
- const ProcessID = union(vsr.ProcessType) {
24
- replica: u8,
25
- client: u128,
26
- };
27
-
28
- return struct {
29
- pool: *MessagePool,
30
- io: *IO,
31
-
32
- process: ProcessID,
33
- /// Prefix for log messages.
34
- id: u128,
35
-
36
- /// The file descriptor for the process on which to accept connections.
37
- accept_fd: ?IO.socket_t = null,
38
- /// Address the accept_fd is bound to, as reported by `getsockname`.
39
- ///
40
- /// This allows passing port 0 as an address for the OS to pick an open port for us
41
- /// in a TOCTOU immune way and logging the resulting port number.
42
- accept_address: ?Address = null,
43
- accept_completion: IO.Completion = undefined,
44
- /// The connection reserved for the currently in progress accept operation.
45
- /// This is non-null exactly when an accept operation is submitted.
46
- accept_connection: ?*Connection = null,
47
-
48
- /// The callback to be called when a message is received.
49
- on_messages_callback: *const fn (message_bus: *MessageBus, buffer: *MessageBuffer) void,
50
-
51
- /// SendQueue storage shared by all connections.
52
- send_queue_buffer: []*Message,
53
- /// This slice is allocated with a fixed size in the init function and never reallocated.
54
- connections: []Connection,
55
- /// Number of connections currently in use (i.e. connection.state != .free).
56
- connections_used: u32 = 0,
57
- connections_suspended: QueueType(Connection) = QueueType(Connection).init(.{
58
- .name = null,
59
- }),
60
- resume_receive_completion: IO.Completion = undefined,
61
- resume_receive_submitted: bool = false,
62
-
63
- /// Map from replica index to the currently active connection for that replica, if any.
64
- /// The connection for the process replica if any will always be null.
65
- replicas: []?*Connection,
66
- replicas_addresses: []Address,
67
- /// The number of outgoing `connect()` attempts for a given replica:
68
- /// Reset to zero after a successful `on_connect()`.
69
- replicas_connect_attempts: []u64,
70
-
71
- /// Map from client id to the currently active connection for that client.
72
- /// This is used to make lookup of client connections when sending messages
73
- /// efficient and to ensure old client connections are dropped if a new one
74
- /// is established.
75
- clients: std.AutoHashMapUnmanaged(u128, *Connection) = .{},
76
-
77
- /// Used to apply jitter when calculating exponential backoff:
78
- /// Seeded with the process' replica index or client ID.
79
- prng: stdx.PRNG,
80
-
81
- trace: ?*Tracer,
82
-
83
- comptime {
84
- // Assert it is correct to use u32 to track sizes.
85
- assert(constants.message_size_max < std.math.maxInt(u32));
86
- }
87
-
88
- pub const Options = struct {
89
- configuration: []const Address,
90
- io: *IO,
91
- trace: ?*Tracer,
92
- clients_limit: ?u32 = null,
93
- };
94
- const Address = std.net.Address;
95
- const MessageBus = @This();
96
-
97
- /// Initialize the MessageBus for the given configuration and replica/client process.
98
- pub fn init(
99
- allocator: mem.Allocator,
100
- process_id: ProcessID,
101
- message_pool: *MessagePool,
102
- on_messages_callback: *const fn (message_bus: *MessageBus, buffer: *MessageBuffer) void,
103
- options: Options,
104
- ) !MessageBus {
105
- switch (process_id) {
106
- .replica => assert(options.clients_limit.? > 0),
107
- .client => assert(options.clients_limit == null),
108
- }
109
-
110
- const connections_max: u32 = switch (process_id) {
111
- // The maximum number of connections that can be held open by the server at any
112
- // time. -1 since we don't need a connection to ourself.
113
- .replica => @intCast(options.configuration.len - 1 + options.clients_limit.?),
114
- .client => @intCast(options.configuration.len),
115
- };
116
-
117
- const send_queue_max = switch (process_id) {
118
- .replica => constants.connection_send_queue_max_replica,
119
- .client => constants.connection_send_queue_max_client,
120
- };
121
-
122
- const send_queue_buffer = try allocator.alloc(
123
- *Message,
124
- connections_max * send_queue_max,
125
- );
126
- @memset(send_queue_buffer, undefined);
127
- errdefer allocator.free(send_queue_buffer);
128
-
129
- const connections = try allocator.alloc(Connection, connections_max);
130
- errdefer allocator.free(connections);
131
- for (connections, 0..) |*connection, index| {
132
- connection.* = .{
133
- .send_queue = .{
134
- .buffer = send_queue_buffer[index * send_queue_max ..][0..send_queue_max],
135
- },
136
- };
137
- }
138
-
139
- const replicas = try allocator.alloc(?*Connection, options.configuration.len);
140
- errdefer allocator.free(replicas);
141
- @memset(replicas, null);
142
-
143
- const replicas_addresses = try allocator.alloc(Address, options.configuration.len);
144
- errdefer allocator.free(replicas_addresses);
145
- stdx.copy_disjoint(.exact, Address, replicas_addresses, options.configuration);
146
-
147
- const replicas_connect_attempts = try allocator.alloc(u64, options.configuration.len);
148
- errdefer allocator.free(replicas_connect_attempts);
149
- @memset(replicas_connect_attempts, 0);
150
-
151
- const prng_seed = switch (process_id) {
152
- .replica => |replica| replica,
153
- .client => |client| @as(u64, @truncate(client)),
154
- };
155
-
156
- var bus: MessageBus = .{
157
- .pool = message_pool,
158
- .io = options.io,
159
- .process = process_id,
160
- .id = switch (process_id) {
161
- .replica => |index| @as(u128, index),
162
- .client => |id| id,
163
- },
164
- .on_messages_callback = on_messages_callback,
165
- .send_queue_buffer = send_queue_buffer,
166
- .connections = connections,
167
- .replicas = replicas,
168
- .replicas_addresses = replicas_addresses,
169
- .replicas_connect_attempts = replicas_connect_attempts,
170
- .prng = stdx.PRNG.from_seed(prng_seed),
171
- .trace = options.trace,
172
- };
173
-
174
- switch (process_id) {
175
- .replica => {
176
- // Pre-allocate enough memory to hold all possible connections
177
- // in the client map.
178
- try bus.clients.ensureTotalCapacity(allocator, connections_max);
179
- errdefer bus.clients.deinit(allocator);
180
-
181
- return bus;
182
- },
183
- .client => return bus,
184
- }
185
- }
186
-
187
- pub fn deinit(bus: *MessageBus, allocator: std.mem.Allocator) void {
188
- bus.clients.deinit(allocator);
189
-
190
- if (bus.accept_fd) |fd| {
191
- assert(bus.process == .replica);
192
- assert(bus.accept_address != null);
193
- bus.io.close_socket(fd);
194
- }
195
-
196
- const send_queue_max = switch (bus.process) {
197
- .replica => constants.connection_send_queue_max_replica,
198
- .client => constants.connection_send_queue_max_client,
199
- };
200
- var send_queue_buffer_previous: ?[]*Message = null;
201
- for (bus.connections) |*connection| {
202
- if (connection.fd) |fd| {
203
- bus.io.close_socket(fd);
204
- }
205
-
206
- if (connection.recv_buffer) |*buffer| buffer.deinit(bus.pool);
207
- connection.recv_buffer = null;
208
- while (connection.send_queue.pop()) |message| bus.unref(message);
209
-
210
- assert(connection.send_queue.buffer.len == send_queue_max);
211
- if (send_queue_buffer_previous) |previous| {
212
- assert(connection.send_queue.buffer.ptr == previous.ptr + previous.len);
213
- } else {
214
- assert(connection.send_queue.buffer.ptr == bus.send_queue_buffer.ptr);
215
- }
216
- send_queue_buffer_previous = connection.send_queue.buffer;
217
- }
218
- assert(bus.send_queue_buffer.ptr + bus.send_queue_buffer.len ==
219
- send_queue_buffer_previous.?.ptr + send_queue_buffer_previous.?.len);
220
-
221
- allocator.free(bus.replicas_connect_attempts);
222
- allocator.free(bus.replicas_addresses);
223
- allocator.free(bus.replicas);
224
- allocator.free(bus.connections);
225
- allocator.free(bus.send_queue_buffer);
226
- bus.* = undefined;
227
- }
228
-
229
- fn init_tcp(io: *IO, process: vsr.ProcessType, family: u32) !IO.socket_t {
230
- return try io.open_socket_tcp(family, .{
231
- .rcvbuf = constants.tcp_rcvbuf,
232
- .sndbuf = switch (process) {
233
- .replica => constants.tcp_sndbuf_replica,
234
- .client => constants.tcp_sndbuf_client,
235
- },
236
- .keepalive = if (constants.tcp_keepalive) .{
237
- .keepidle = constants.tcp_keepidle,
238
- .keepintvl = constants.tcp_keepintvl,
239
- .keepcnt = constants.tcp_keepcnt,
240
- } else null,
241
- .user_timeout_ms = constants.tcp_user_timeout_ms,
242
- .nodelay = constants.tcp_nodelay,
243
- });
244
- }
245
-
246
- pub fn listen(bus: *MessageBus) !void {
247
- assert(bus.process == .replica);
248
- assert(bus.accept_fd == null);
249
- assert(bus.accept_address == null);
250
-
251
- const address = bus.replicas_addresses[bus.process.replica];
252
- const fd = try init_tcp(bus.io, .replica, address.any.family);
253
- errdefer bus.io.close_socket(fd);
254
-
255
- const accept_address = try bus.io.listen(fd, address, .{
256
- .backlog = constants.tcp_backlog,
257
- });
258
-
259
- bus.accept_fd = fd;
260
- bus.accept_address = accept_address;
261
- }
262
-
263
- pub fn tick(bus: *MessageBus) void {
264
- assert(bus.process == .replica);
265
- bus.tick_connect();
266
- bus.tick_accept(); // Only replicas accept connections from other replicas and clients.
267
- }
268
-
269
- pub fn trace_gauge(bus: *MessageBus) void {
270
- if (bus.trace) |trace| {
271
- var counts = std.enums.EnumArray(std.meta.Tag(vsr.Peer), u32).initFill(0);
272
- for (bus.connections) |*connection| {
273
- if (connection.state == .connected) {
274
- counts.getPtr(connection.peer).* += 1;
275
- }
276
- }
277
-
278
- var counts_iterator = counts.iterator();
279
- while (counts_iterator.next()) |entry| {
280
- trace.gauge(
281
- .{ .message_bus_connections = .{ .peer = entry.key } },
282
- entry.value.*,
283
- );
284
- }
285
-
286
- trace.gauge(.message_bus_connections_max, bus.connections.len);
287
- }
288
- }
289
-
290
- // The same as tick, but asserts a client and avoids accept, allowing Zig's lazy semantics
291
- // to not add dead accept code to client libraries.
292
- pub fn tick_client(bus: *MessageBus) void {
293
- assert(bus.process == .client);
294
- bus.tick_connect();
295
- }
296
-
297
- fn tick_connect(bus: *MessageBus) void {
298
- const replica_next = switch (bus.process) {
299
- // Each replica is responsible for connecting to replicas that come
300
- // after it in the configuration. This ensures that replicas never try
301
- // to connect to each other at the same time.
302
- .replica => |replica| replica + 1,
303
- // The client connects to all replicas.
304
- .client => 0,
305
- };
306
- for (bus.replicas[replica_next..], replica_next..) |*connection, replica| {
307
- if (connection.* == null) bus.connect(@intCast(replica));
308
- }
309
- assert(bus.connections_used >= bus.replicas.len - replica_next);
310
- }
311
-
312
- fn tick_accept(bus: *MessageBus) void {
313
- assert(bus.process == .replica);
314
- assert(bus.accept_fd != null); // Must listen before tick.
315
- bus.accept();
316
- }
317
-
318
- fn accept(bus: *MessageBus) void {
319
- assert(bus.process == .replica);
320
- assert(bus.accept_fd != null);
321
-
322
- if (bus.accept_connection != null) return;
323
- // All connections are currently in use, do nothing.
324
- if (bus.connections_used == bus.connections.len) return;
325
- assert(bus.connections_used < bus.connections.len);
326
- bus.accept_connection = for (bus.connections) |*connection| {
327
- if (connection.state == .free) {
328
- connection.state = .accepting;
329
- break connection;
330
- }
331
- } else unreachable;
332
- bus.io.accept(
333
- *MessageBus,
334
- bus,
335
- accept_callback,
336
- &bus.accept_completion,
337
- bus.accept_fd.?,
338
- );
339
- }
340
-
341
- fn accept_callback(
342
- bus: *MessageBus,
343
- _: *IO.Completion,
344
- result: IO.AcceptError!IO.socket_t,
345
- ) void {
346
- assert(bus.process == .replica);
347
-
348
- assert(bus.accept_connection != null);
349
- const connection: *Connection = bus.accept_connection.?;
350
- bus.accept_connection = null;
351
-
352
- assert(connection.peer == .unknown);
353
- assert(connection.fd == null);
354
- assert(connection.state == .accepting);
355
- defer assert(connection.state == .connected or connection.state == .free);
356
-
357
- if (result) |fd| {
358
- connection.state = .connected;
359
- connection.fd = fd;
360
- bus.connections_used += 1;
361
-
362
- bus.assert_connection_initial_state(connection);
363
- assert(connection.recv_buffer == null);
364
- connection.recv_buffer = MessageBuffer.init(bus.pool);
365
- bus.recv(connection);
366
- // Don't start send loop yet --- on accept, we don't know which peer this is.
367
- assert(connection.send_queue.empty());
368
- assert(connection.state == .connected);
369
- } else |err| {
370
- connection.state = .free;
371
- // TODO: some errors should probably be fatal
372
- log.warn("{}: on_accept: {}", .{ bus.id, err });
373
- }
374
- }
375
-
376
- fn connect(bus: *MessageBus, replica: u8) void {
377
- assert(bus.replicas[replica] == null);
378
-
379
- // Obtain a connection struct for our new replica connection.
380
- // If there is a free connection, use that. Otherwise drop
381
- // a client or unknown connection to make space. Prefer dropping
382
- // a client connection to an unknown one as the unknown peer may
383
- // be a replica. Since shutting a connection down does not happen
384
- // instantly, simply return after starting the shutdown and try again
385
- // on the next tick().
386
- const connection_free: *Connection = for (bus.connections) |*connection| {
387
- if (connection.state == .free) break connection;
388
- } else {
389
- bus.connect_reclaim_connection();
390
- return;
391
- };
392
-
393
- assert(connection_free.state == .free);
394
- // This will immediately add the connection to bus.replicas,
395
- // or else will return early if a socket file descriptor cannot be obtained:
396
- bus.connect_connection(connection_free, replica);
397
- switch (connection_free.state) {
398
- .connecting => assert(bus.replicas[replica] != null),
399
- .free => assert(bus.replicas[replica] == null),
400
- else => unreachable,
401
- }
402
- }
403
-
404
- fn connect_reclaim_connection(bus: *MessageBus) void {
405
- for (bus.connections) |*connection| assert(connection.state != .free);
406
-
407
- // If there is already a connection being shut down, no need to kill another.
408
- for (bus.connections) |*connection| {
409
- if (connection.state == .terminating) return;
410
- }
411
-
412
- log.info("{}: connect_to_replica: no free connection, disconnecting a client", .{
413
- bus.id,
414
- });
415
- for (bus.connections) |*connection| {
416
- if (connection.peer == .client) {
417
- bus.terminate(connection, .shutdown);
418
- return;
419
- }
420
- }
421
-
422
- log.info("{}: connect_to_replica: no free connection, disconnecting unknown peer", .{
423
- bus.id,
424
- });
425
- for (bus.connections) |*connection| {
426
- if (connection.peer == .unknown) {
427
- bus.terminate(connection, .shutdown);
428
- return;
429
- }
430
- }
431
-
432
- // We assert that the max number of connections is greater
433
- // than the number of replicas in init().
434
- unreachable;
435
- }
436
-
437
- /// Attempt to connect to a replica.
438
- /// The slot in the Message.replicas slices is immediately reserved.
439
- /// Failure is silent and returns the connection to an unused state.
440
- fn connect_connection(bus: *MessageBus, connection: *Connection, replica: u8) void {
441
- if (bus.process == .replica) assert(replica > bus.process.replica);
442
-
443
- assert(connection.state == .free);
444
- assert(connection.fd == null);
445
-
446
- const family = bus.replicas_addresses[replica].any.family;
447
- connection.fd = init_tcp(bus.io, bus.process, family) catch |err| {
448
- log.err("{}: connect_to_replica: init_tcp error={s}", .{
449
- bus.id,
450
- @errorName(err),
451
- });
452
- return;
453
- };
454
- connection.peer = .{ .replica = replica };
455
- connection.state = .connecting;
456
- bus.connections_used += 1;
457
-
458
- assert(bus.replicas[replica] == null);
459
- bus.replicas[replica] = connection;
460
-
461
- const attempts = &bus.replicas_connect_attempts[replica];
462
- const ms = vsr.exponential_backoff_with_jitter(
463
- &bus.prng,
464
- constants.connection_delay_min_ms,
465
- constants.connection_delay_max_ms,
466
- attempts.*,
467
- );
468
- attempts.* += 1;
469
-
470
- log.debug("{}: connect_to_replica: connecting to={} after={}ms", .{
471
- bus.id,
472
- connection.peer.replica,
473
- ms,
474
- });
475
-
476
- assert(!connection.recv_submitted);
477
- connection.recv_submitted = true;
478
-
479
- bus.io.timeout(
480
- *MessageBus,
481
- bus,
482
- connect_timeout_callback,
483
- // We use `recv_completion` for the connection `timeout()` and `connect()` calls
484
- &connection.recv_completion,
485
- @as(u63, @intCast(ms * std.time.ns_per_ms)),
486
- );
487
- }
488
-
489
- fn connect_timeout_callback(
490
- bus: *MessageBus,
491
- completion: *IO.Completion,
492
- result: IO.TimeoutError!void,
493
- ) void {
494
- const connection: *Connection = @alignCast(
495
- @fieldParentPtr("recv_completion", completion),
496
- );
497
- assert(connection.recv_submitted);
498
- connection.recv_submitted = false;
499
- if (connection.state == .terminating) {
500
- bus.terminate_join(connection);
501
- return;
502
- }
503
- assert(connection.state == .connecting);
504
- result catch unreachable;
505
-
506
- log.debug("{}: on_connect_with_exponential_backoff: to={}", .{
507
- bus.id,
508
- connection.peer.replica,
509
- });
510
-
511
- assert(!connection.recv_submitted);
512
- connection.recv_submitted = true;
513
-
514
- bus.io.connect(
515
- *MessageBus,
516
- bus,
517
- connect_callback,
518
- // We use `recv_completion` for the connection `timeout()` and `connect()` calls
519
- &connection.recv_completion,
520
- connection.fd.?,
521
- bus.replicas_addresses[connection.peer.replica],
522
- );
523
- }
524
-
525
- fn connect_callback(
526
- bus: *MessageBus,
527
- completion: *IO.Completion,
528
- result: IO.ConnectError!void,
529
- ) void {
530
- const connection: *Connection = @alignCast(
531
- @fieldParentPtr("recv_completion", completion),
532
- );
533
- assert(connection.recv_submitted);
534
- connection.recv_submitted = false;
535
-
536
- if (connection.state == .terminating) {
537
- bus.terminate_join(connection);
538
- return;
539
- }
540
- assert(connection.state == .connecting);
541
- connection.state = .connected;
542
-
543
- result catch |err| {
544
- log.warn("{}: on_connect: error to={} {}", .{
545
- bus.id,
546
- connection.peer.replica,
547
- err,
548
- });
549
- bus.terminate(connection, .no_shutdown);
550
- return;
551
- };
552
-
553
- log.info("{}: on_connect: connected to={}", .{ bus.id, connection.peer.replica });
554
- bus.replicas_connect_attempts[connection.peer.replica] = 0;
555
-
556
- bus.assert_connection_initial_state(connection);
557
- assert(connection.recv_buffer == null);
558
- connection.recv_buffer = MessageBuffer.init(bus.pool);
559
- bus.recv(connection);
560
- bus.send(connection);
561
- assert(connection.state == .connected);
562
- }
563
-
564
- fn assert_connection_initial_state(bus: *MessageBus, connection: *Connection) void {
565
- assert(bus.connections_used > 0);
566
-
567
- assert(connection.peer == .unknown or connection.peer == .replica);
568
- assert(connection.state == .connected);
569
- assert(connection.fd != null);
570
-
571
- assert(connection.recv_submitted == false);
572
- assert(connection.recv_buffer == null);
573
-
574
- assert(connection.send_submitted == false);
575
- assert(connection.send_progress == 0);
576
- }
577
-
578
- /// The recv loop.
579
- ///
580
- /// Kickstarted by `accept` and `connect`, and loops onto itself via `recv_buffer_drain`.
581
- fn recv(bus: *MessageBus, connection: *Connection) void {
582
- assert(connection.state == .connected);
583
- assert(connection.fd != null);
584
- assert(connection.recv_buffer != null);
585
-
586
- assert(!connection.recv_submitted);
587
- connection.recv_submitted = true;
588
-
589
- bus.io.recv(
590
- *MessageBus,
591
- bus,
592
- recv_callback,
593
- &connection.recv_completion,
594
- connection.fd.?,
595
- connection.recv_buffer.?.recv_slice(),
596
- );
597
- }
598
-
599
- fn recv_callback(
600
- bus: *MessageBus,
601
- completion: *IO.Completion,
602
- result: IO.RecvError!usize,
603
- ) void {
604
- const connection: *Connection = @alignCast(
605
- @fieldParentPtr("recv_completion", completion),
606
- );
607
- assert(connection.recv_submitted);
608
- connection.recv_submitted = false;
609
- if (connection.state == .terminating) {
610
- bus.terminate_join(connection);
611
- return;
612
- }
613
- assert(connection.state == .connected);
614
- const bytes_received = result catch |err| {
615
- // TODO: maybe don't need to close on *every* error
616
- log.warn("{}: on_recv: from={} {}", .{ bus.id, connection.peer, err });
617
- bus.terminate(connection, .shutdown);
618
- return;
619
- };
620
- // No bytes received means that the peer closed its side of the connection.
621
- if (bytes_received == 0) {
622
- log.info("{}: on_recv: from={} orderly shutdown", .{ bus.id, connection.peer });
623
- bus.terminate(connection, .no_shutdown);
624
- return;
625
- }
626
- assert(bytes_received <= constants.message_size_max);
627
- assert(connection.recv_buffer != null);
628
- connection.recv_buffer.?.recv_advance(@intCast(bytes_received));
629
-
630
- switch (bus.process) {
631
- // Replicas may forward messages from clients or from other replicas so we
632
- // may receive messages from a peer before we know who they are:
633
- // This has the same effect as an asymmetric network where, for a short time
634
- // bounded by the time it takes to ping, we can hear from a peer before we
635
- // can send back to them.
636
- .replica => {
637
- while (connection.recv_buffer.?.next_header()) |header| {
638
- if (bus.recv_update_peer(connection, header.peer_type())) {
639
- connection.recv_buffer.?.suspend_message(&header);
640
- } else {
641
- log.warn("{}: on_recv: invalid peer transition {any} -> {any}", .{
642
- bus.id,
643
- connection.peer,
644
- header.peer_type(),
645
- });
646
- connection.recv_buffer.?.invalidate(.misdirected);
647
- }
648
- }
649
- },
650
- // The client connects only to replicas and should set peer when connecting:
651
- .client => assert(connection.peer == .replica),
652
- }
653
- bus.recv_buffer_drain(connection);
654
- }
655
-
656
- fn recv_update_peer(bus: *MessageBus, connection: *Connection, peer: vsr.Peer) bool {
657
- assert(bus.process == .replica);
658
- assert(bus.clients.capacity() > 0);
659
-
660
- assert(bus.connections_used > 0);
661
-
662
- assert(connection.state == .connected);
663
- assert(connection.fd != null);
664
- assert(connection.recv_buffer != null);
665
-
666
- switch (vsr.Peer.transition(connection.peer, peer)) {
667
- .retain => return true,
668
- .reject => return false,
669
- .update => {},
670
- }
671
-
672
- switch (peer) {
673
- .replica => |replica_index| {
674
- if (replica_index >= bus.replicas_addresses.len) return false;
675
-
676
- // Allowed transitions:
677
- // * unknown → replica
678
- // * client_likely → replica
679
- assert(connection.peer == .unknown or connection.peer == .client_likely);
680
-
681
- // If there is a connection to this replica, terminate and replace it.
682
- if (bus.replicas[replica_index]) |old| {
683
- assert(old != connection);
684
- assert(old.peer == .replica);
685
- assert(old.peer.replica == replica_index);
686
- assert(old.state != .free);
687
- if (old.state != .terminating) bus.terminate(old, .shutdown);
688
- }
689
-
690
- switch (connection.peer) {
691
- .unknown => {},
692
- // If this connection was misclassified to a client due to a forwarded
693
- // request message (see `peer_type` in message_header.zig), it may
694
- // reside in the clients map. If so, it must be popped and mapped to the
695
- // correct replica.
696
- .client_likely => |existing| {
697
- if (bus.clients.get(existing)) |existing_connection| {
698
- if (existing_connection == connection) {
699
- assert(bus.clients.remove(existing));
700
- }
701
- }
702
- },
703
- .replica, .client => unreachable,
704
- }
705
-
706
- bus.replicas[replica_index] = connection;
707
- log.info("{}: set_and_verify_peer: connection from replica={}", .{
708
- bus.id,
709
- replica_index,
710
- });
711
- },
712
- .client => |client_id| {
713
- assert(client_id != 0);
714
-
715
- // Allowed transitions:
716
- // * unknown → client
717
- // * client_likely → client
718
- assert(connection.peer == .unknown or connection.peer == .client_likely);
719
-
720
- if (connection.peer == .client_likely) {
721
- assert(connection.peer.client_likely == client_id);
722
- }
723
-
724
- // If there is a connection to this client, terminate and replace it.
725
- const result = bus.clients.getOrPutAssumeCapacity(client_id);
726
- if (result.found_existing) {
727
- const old = result.value_ptr.*;
728
- assert(old.state == .connected or old.state == .terminating);
729
- if (connection.peer == .unknown) assert(old != connection);
730
-
731
- switch (old.peer) {
732
- .client, .client_likely => |client| {
733
- assert(client == client_id);
734
- },
735
- .unknown, .replica => unreachable,
736
- }
737
-
738
- if (old != connection and old.state != .terminating) {
739
- bus.terminate(old, .shutdown);
740
- }
741
- }
742
-
743
- result.value_ptr.* = connection;
744
- log.info("{}: set_and_verify_peer: connection from client={}", .{
745
- bus.id,
746
- client_id,
747
- });
748
- },
749
-
750
- .client_likely => |client_id| {
751
- assert(client_id != 0);
752
- switch (connection.peer) {
753
- .unknown => {
754
- // If the peer transitions from unknown -> client_likely, either
755
- // a replica or a client may be sending a request message. Instead
756
- // of terminating an existing connection and replacing it, if one
757
- // exists in the client map, we wait for it to get resolved to
758
- // either a replica or a client.
759
- const result =
760
- bus.clients.getOrPutAssumeCapacity(client_id);
761
- if (!result.found_existing) {
762
- result.value_ptr.* = connection;
763
- log.info("{}: set_and_verify_peer: connection from " ++
764
- "client_likely={}", .{ bus.id, client_id });
765
- }
766
- },
767
- .replica, .client, .client_likely => unreachable,
768
- }
769
- },
770
- .unknown => {},
771
- }
772
-
773
- connection.peer = peer;
774
-
775
- return true;
776
- }
777
-
778
- /// Attempt moving messages from recv buffer into replica for processing. Called when recv
779
- /// syscall completes, or when a replica signals readiness to consume previously suspended
780
- /// messages.
781
- fn recv_buffer_drain(bus: *MessageBus, connection: *Connection) void {
782
- assert(connection.recv_buffer != null);
783
-
784
- if (connection.recv_buffer.?.has_message()) {
785
- bus.on_messages_callback(bus, &connection.recv_buffer.?);
786
- }
787
-
788
- if (connection.recv_buffer.?.invalid) |reason| {
789
- log.warn("{}: on_recv: from={} terminating connection: invalid {s}", .{
790
- bus.id,
791
- connection.peer,
792
- @tagName(reason),
793
- });
794
- bus.terminate(connection, .no_shutdown);
795
- return;
796
- }
797
-
798
- if (connection.recv_buffer.?.has_message()) {
799
- maybe(connection.state == .terminating);
800
- bus.connections_suspended.push(connection);
801
- } else {
802
- if (connection.state == .terminating) {
803
- bus.terminate_join(connection);
804
- } else {
805
- bus.recv(connection);
806
- }
807
- }
808
- }
809
-
810
- pub fn send_message_to_replica(bus: *MessageBus, replica: u8, message: *Message) void {
811
- // Messages sent by a replica to itself should never be passed to the message bus.
812
- if (bus.process == .replica) assert(replica != bus.process.replica);
813
-
814
- if (bus.replicas[replica]) |connection| {
815
- bus.send_message(connection, message);
816
- } else {
817
- log.debug("{}: send_message_to_replica: no connection to={} header={}", .{
818
- bus.id,
819
- replica,
820
- message.header,
821
- });
822
- }
823
- }
824
-
825
- /// Try to send the message to the client with the given id.
826
- /// If the client is not currently connected, the message is silently dropped.
827
- pub fn send_message_to_client(bus: *MessageBus, client_id: u128, message: *Message) void {
828
- assert(bus.process == .replica);
829
- assert(bus.clients.capacity() > 0);
830
-
831
- if (bus.clients.get(client_id)) |connection| {
832
- bus.send_message(connection, message);
833
- } else {
834
- log.debug(
835
- "{}: send_message_to_client: no connection to={}",
836
- .{ bus.id, client_id },
837
- );
838
- }
839
- }
840
-
841
- /// Add a message to the connection's send queue, starting a send operation
842
- /// if the queue was previously empty.
843
- fn send_message(bus: *MessageBus, connection: *Connection, message: *Message) void {
844
- assert(connection.peer != .unknown);
845
-
846
- switch (connection.state) {
847
- .connected, .connecting => {},
848
- .terminating => return,
849
- .free, .accepting => unreachable,
850
- }
851
- if (connection.send_queue.full()) {
852
- log.info("{}: send_message: to={} queue full, dropping command={s}", .{
853
- bus.id,
854
- connection.peer,
855
- @tagName(message.header.command),
856
- });
857
- return;
858
- }
859
- connection.send_queue.push_assume_capacity(message.ref());
860
- // If the connection has not yet been established we can't send yet.
861
- // Instead on_connect() will call send().
862
- if (connection.state == .connecting) {
863
- assert(connection.peer == .replica);
864
- return;
865
- }
866
- // If there is no send operation currently in progress, start one.
867
- if (!connection.send_submitted) bus.send(connection);
868
- }
869
-
870
- /// Send loop.
871
- ///
872
- /// Kickstarted by `connect` and loops onto itself until all enqueue messages are sent.
873
- /// `accept` doesn't start the send loop because it doesn't know the identity of the peer.
874
- fn send(bus: *MessageBus, connection: *Connection) void {
875
- assert(connection.peer != .unknown);
876
- assert(connection.state == .connected);
877
- assert(connection.fd != null);
878
- assert(!connection.send_submitted);
879
-
880
- bus.send_now(connection);
881
-
882
- const message = connection.send_queue.head() orelse
883
- return; // Nothing more to send, break out of the send loop.
884
- connection.send_submitted = true;
885
- bus.io.send(
886
- *MessageBus,
887
- bus,
888
- send_callback,
889
- &connection.send_completion,
890
- connection.fd.?,
891
- message.buffer[connection.send_progress..message.header.size],
892
- );
893
- }
894
-
895
- // Optimization/fast path: try to immediately copy the send queue over to the in-kernel
896
- // send buffer, falling back to asynchronous send if that's not possible.
897
- fn send_now(bus: *MessageBus, connection: *Connection) void {
898
- assert(connection.state == .connected);
899
- assert(connection.fd != null);
900
- assert(!connection.send_submitted);
901
-
902
- for (0..connection.send_queue.count) |_| {
903
- const message = connection.send_queue.head().?;
904
- assert(connection.send_progress < message.header.size);
905
- const write_size = bus.io.send_now(
906
- connection.fd.?,
907
- message.buffer[connection.send_progress..message.header.size],
908
- ) orelse return;
909
- assert(write_size <= constants.message_size_max);
910
- connection.send_progress += @intCast(write_size);
911
- assert(connection.send_progress <= message.header.size);
912
- if (connection.send_progress == message.header.size) {
913
- _ = connection.send_queue.pop();
914
- bus.unref(message);
915
- connection.send_progress = 0;
916
- } else {
917
- assert(connection.send_progress < message.header.size);
918
- return;
919
- }
920
- }
921
- }
922
-
923
- fn send_callback(
924
- bus: *MessageBus,
925
- completion: *IO.Completion,
926
- result: IO.SendError!usize,
927
- ) void {
928
- const connection: *Connection = @alignCast(
929
- @fieldParentPtr("send_completion", completion),
930
- );
931
- assert(connection.send_submitted);
932
- connection.send_submitted = false;
933
- assert(connection.peer != .unknown);
934
- if (connection.state == .terminating) {
935
- bus.terminate_join(connection);
936
- return;
937
- }
938
- assert(connection.state == .connected);
939
- const write_size = result catch |err| {
940
- // TODO: maybe don't need to close on *every* error
941
- log.warn("{}: on_send: to={} {}", .{
942
- bus.id,
943
- connection.peer,
944
- err,
945
- });
946
- bus.terminate(connection, .shutdown);
947
- return;
948
- };
949
- assert(write_size <= constants.message_size_max);
950
- connection.send_progress += @intCast(write_size);
951
- assert(connection.send_progress <= connection.send_queue.head().?.header.size);
952
- // If the message has been fully sent, move on to the next one.
953
- if (connection.send_progress == connection.send_queue.head().?.header.size) {
954
- connection.send_progress = 0;
955
- const message = connection.send_queue.pop().?;
956
- bus.unref(message);
957
- }
958
- bus.send(connection);
959
- }
960
-
961
- /// Clean up an active connection and reset it to its initial, unused, state.
962
- /// This reset does not happen instantly as currently in progress operations
963
- /// must first be stopped. The `how` arg allows the caller to specify if a
964
- /// shutdown syscall should be made or not before proceeding to wait for
965
- /// currently in progress operations to complete and close the socket.
966
- /// I'll be back! (when the Connection is reused after being fully closed)
967
- fn terminate(
968
- bus: *MessageBus,
969
- connection: *Connection,
970
- how: enum { shutdown, no_shutdown },
971
- ) void {
972
- assert(connection.state != .free);
973
- assert(connection.fd != null);
974
- switch (how) {
975
- .shutdown => {
976
- // The shutdown syscall will cause currently in progress send/recv
977
- // operations to be gracefully closed while keeping the fd open.
978
- //
979
- // TODO: Investigate differences between shutdown() on Linux vs Darwin.
980
- // Especially how this interacts with our assumptions around pending I/O.
981
- bus.io.shutdown(connection.fd.?, .both) catch |err| switch (err) {
982
- error.SocketNotConnected => {
983
- // This should only happen if we for some reason decide to terminate
984
- // a connection while a connect operation is in progress.
985
- // This is fine though, we simply continue with the logic below and
986
- // wait for the connect operation to finish.
987
-
988
- // TODO: This currently happens in other cases if the
989
- // connection was closed due to an error. We need to intelligently
990
- // decide whether to shutdown or close directly based on the error
991
- // before these assertions may be re-enabled.
992
-
993
- //assert(connection.state == .connecting);
994
- //assert(connection.recv_submitted);
995
- //assert(!connection.send_submitted);
996
- },
997
- // Ignore all the remaining errors for now
998
- error.ConnectionAborted,
999
- error.ConnectionResetByPeer,
1000
- error.BlockingOperationInProgress,
1001
- error.NetworkSubsystemFailed,
1002
- error.SystemResources,
1003
- error.Unexpected,
1004
- => {},
1005
- };
1006
- },
1007
- .no_shutdown => {},
1008
- }
1009
- assert(connection.state != .terminating);
1010
- connection.state = .terminating;
1011
- bus.terminate_join(connection);
1012
- }
1013
-
1014
- fn terminate_join(bus: *MessageBus, connection: *Connection) void {
1015
- assert(connection.state == .terminating);
1016
- // If a recv or send operation is currently submitted to the kernel,
1017
- // submitting a close would cause a race. Therefore we must wait for
1018
- // any currently submitted operation to complete.
1019
- if (connection.recv_submitted or connection.send_submitted) return;
1020
- // Even if there's no active physical IO in progress, we want to wait until all
1021
- // messages already received are consumed, to prevent graceful termination of
1022
- // connection from dropping messages.
1023
- if (connection.recv_buffer) |*receive_buffer| {
1024
- if (receive_buffer.has_message()) return;
1025
- }
1026
-
1027
- bus.terminate_close(connection);
1028
- }
1029
-
1030
- fn terminate_close(bus: *MessageBus, connection: *Connection) void {
1031
- assert(connection.state == .terminating);
1032
- assert(!connection.recv_submitted);
1033
- assert(!connection.send_submitted);
1034
- if (connection.recv_buffer) |receive_buffer| assert(!receive_buffer.has_message());
1035
- assert(connection.fd != null);
1036
-
1037
- connection.send_submitted = true;
1038
- connection.recv_submitted = true;
1039
- // We can free resources now that there is no longer any I/O in progress.
1040
- while (connection.send_queue.pop()) |message| {
1041
- bus.unref(message);
1042
- }
1043
- if (connection.recv_buffer) |*buffer| buffer.deinit(bus.pool);
1044
- connection.recv_buffer = null;
1045
- const fd = connection.fd.?;
1046
- connection.fd = null;
1047
- // It's OK to use the send completion here as we know that no send
1048
- // operation is currently in progress.
1049
- bus.io.close(
1050
- *MessageBus,
1051
- bus,
1052
- terminate_close_callback,
1053
- &connection.send_completion,
1054
- fd,
1055
- );
1056
- }
1057
-
1058
- fn terminate_close_callback(
1059
- bus: *MessageBus,
1060
- completion: *IO.Completion,
1061
- result: IO.CloseError!void,
1062
- ) void {
1063
- const connection: *Connection = @alignCast(
1064
- @fieldParentPtr("send_completion", completion),
1065
- );
1066
- assert(connection.state == .terminating);
1067
- assert(connection.recv_submitted);
1068
- assert(connection.send_submitted);
1069
- assert(connection.recv_buffer == null);
1070
- assert(connection.send_queue.empty());
1071
- assert(connection.fd == null);
1072
- assert(!bus.connections_suspended.contains(connection));
1073
-
1074
- result catch |err| {
1075
- log.warn("{}: on_close: to={} {}", .{ bus.id, connection.peer, err });
1076
- };
1077
-
1078
- // Reset the connection to its initial state.
1079
- switch (connection.peer) {
1080
- .unknown => {},
1081
- .client, .client_likely => |client_id| {
1082
- assert(bus.process == .replica);
1083
- // A newer client connection may have replaced this one:
1084
- if (bus.clients.get(client_id)) |existing_connection| {
1085
- if (existing_connection == connection) {
1086
- assert(bus.clients.remove(client_id));
1087
- }
1088
- } else {
1089
- // A newer client connection may even leapfrog this connection
1090
- // and then be terminated and set to null before we can get
1091
- // here.
1092
- }
1093
- },
1094
- .replica => |replica| {
1095
- // A newer replica connection may have replaced this one:
1096
- if (bus.replicas[replica] == connection) {
1097
- bus.replicas[replica] = null;
1098
- } else {
1099
- // A newer replica connection may even leapfrog this connection and
1100
- // then be terminated and set to null before we can get here:
1101
- stdx.maybe(bus.replicas[replica] == null);
1102
- }
1103
- },
1104
- }
1105
- bus.connections_used -= 1;
1106
- connection.* = .{
1107
- .send_queue = .{
1108
- .buffer = connection.send_queue.buffer,
1109
- },
1110
- };
1111
- }
1112
-
1113
- pub fn get_message(
1114
- bus: *MessageBus,
1115
- comptime command: ?vsr.Command,
1116
- ) MessagePool.GetMessageType(command) {
1117
- return bus.pool.get_message(command);
1118
- }
1119
-
1120
- /// `@TypeOf(message)` is one of:
1121
- /// - `*Message`
1122
- /// - `MessageType(command)` for any `command`.
1123
- pub fn unref(bus: *MessageBus, message: anytype) void {
1124
- bus.pool.unref(message);
1125
- }
1126
-
1127
- pub fn resume_needed(bus: *MessageBus) bool {
1128
- if (bus.connections_suspended.empty()) return false;
1129
- if (bus.resume_receive_submitted) return false;
1130
- return true;
1131
- }
1132
-
1133
- pub fn resume_receive(bus: *MessageBus) void {
1134
- if (!bus.resume_needed()) return;
1135
-
1136
- bus.resume_receive_submitted = true;
1137
- bus.io.timeout(
1138
- *MessageBus,
1139
- bus,
1140
- ready_to_receive_callback,
1141
- &bus.resume_receive_completion,
1142
- 0, // Zero timeout means next tick.
1143
- );
1144
- }
1145
-
1146
- fn ready_to_receive_callback(
1147
- bus: *MessageBus,
1148
- completion: *IO.Completion,
1149
- result: IO.TimeoutError!void,
1150
- ) void {
1151
- assert(completion == &bus.resume_receive_completion);
1152
- _ = result catch |e| switch (e) {
1153
- error.Canceled => unreachable,
1154
- error.Unexpected => unreachable,
1155
- };
1156
- assert(bus.resume_receive_submitted);
1157
- bus.resume_receive_submitted = false;
1158
- maybe(bus.connections_suspended.empty());
1159
-
1160
- // Steal the queue to avoid an infinite loop.
1161
- var connections_suspended = bus.connections_suspended;
1162
- bus.connections_suspended.reset();
1163
-
1164
- while (connections_suspended.pop()) |connection| {
1165
- assert(connection.recv_buffer != null);
1166
- assert(connection.recv_buffer.?.advance_size >= @sizeOf(vsr.Header));
1167
- assert(connection.recv_buffer.?.has_message());
1168
- bus.recv_buffer_drain(connection);
1169
- }
1170
- }
1171
-
1172
- /// Used to send/receive messages to/from a client or fellow replica.
1173
- const Connection = struct {
1174
- /// The peer is determined by inspecting all message headers received on this
1175
- /// connection. If the peer changes unexpectedly (for example, due to a misdirected
1176
- /// message), we terminate the connection.
1177
- peer: vsr.Peer = .unknown,
1178
-
1179
- state: enum {
1180
- /// The connection is not in use, with peer set to `.unknown`.
1181
- free,
1182
- /// The connection has been reserved for an in progress accept operation,
1183
- /// with peer set to `.unknown`.
1184
- accepting,
1185
- /// The peer is a replica and a connect operation has been started
1186
- /// but not yet completed.
1187
- connecting,
1188
- /// The peer is fully connected and may be a client, replica, or unknown.
1189
- connected,
1190
- /// The connection is being terminated but cleanup has not yet finished.
1191
- terminating,
1192
- } = .free,
1193
- /// This is guaranteed to be valid only while state is connected.
1194
- /// It will be reset to null during the shutdown process and is always null if the
1195
- /// connection is unused (i.e. peer == .unknown).
1196
- fd: ?IO.socket_t = null,
1197
-
1198
- /// This completion is used for all recv operations.
1199
- /// It is also used for the initial connect when establishing a replica connection.
1200
- recv_completion: IO.Completion = undefined,
1201
- /// True exactly when the recv_completion has been submitted to the IO abstraction
1202
- /// but the callback has not yet been run.
1203
- recv_submitted: bool = false,
1204
- recv_buffer: ?MessageBuffer = null,
1205
-
1206
- /// This completion is used for all send operations.
1207
- send_completion: IO.Completion = undefined,
1208
- /// True exactly when the send_completion has been submitted to the IO abstraction
1209
- /// but the callback has not yet been run.
1210
- send_submitted: bool = false,
1211
- /// Number of bytes of the current message that have already been sent.
1212
- send_progress: u32 = 0,
1213
- /// The queue of messages to send to the client or replica peer.
1214
- send_queue: SendQueue,
1215
- /// For connections_suspended.
1216
- link: QueueType(Connection).Link = .{},
1217
- };
1218
- };
1219
- }