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,279 @@
1
+ //! FaultDetector estimates the probability that the primary crashed.
2
+ //! It is implemented as a pure algorithm in a "sans-io" style.
3
+ //!
4
+ //! Intuition: imagine looking from a window at the street below and trying to guess whether the
5
+ //! nearest traffic light (not directly visible) is red or green. If you see the cars going, it
6
+ //! definitely was green recently. If there are no cars, it could be that the light is red, or that
7
+ //! you are out of peak hour and there are no cars at all. So, a reasonable algorithm is to note
8
+ //! the average interarrival time over a sliding window, and signal "red" when there's a suspicious
9
+ //! absence of cars, relative to recent history. Note latency-throughput interaction: if the cars
10
+ //! are frequent, but there's a large distance between your window and the traffic light, you notice
11
+ //! red very quickly, but only after "wave edge" reaches you.
12
+ //!
13
+ //! Applying to TigerBeetle, let's consider the case where the cluster is under stable load and
14
+ //! prepares are flowing regularly. In this case, a backup uses a sliding window to compute current
15
+ //! rate of prepares, and sounds an alarm (StartViewChange) if the flow stops.
16
+ //!
17
+ //! To solve the case of an idle cluster, primary broadcasts Commit messages periodically, which are
18
+ //! interchangeable with Prepare messages as a signal that the primary is alive.
19
+ //!
20
+ //! Another complication is that prepare load can be bursty. If the load ramps up quickly, the
21
+ //! primary is seen as extremely alive, which is OK. However, if the load is cut off exogenously
22
+ //! (because the client finished a batch job, not because the primary crashed), this will look like
23
+ //! a dead primary. To solve this, primary uses central-bank style algorithm, where it injects extra
24
+ //! Commit messages to keep prepare rate relatively stable in the short run.
25
+
26
+ const std = @import("std");
27
+ const stdx = @import("stdx");
28
+ const assert = std.debug.assert;
29
+ const Instant = stdx.Instant;
30
+ const Duration = stdx.Duration;
31
+
32
+ interval_min: Duration,
33
+ interval_max: Duration,
34
+
35
+ signal_last: Instant,
36
+ interval_ewma: Duration,
37
+
38
+ const FaultDetector = @This();
39
+
40
+ pub fn init(options: struct {
41
+ now: Instant,
42
+ interval_min: Duration,
43
+ interval_max: Duration,
44
+ }) FaultDetector {
45
+ assert(options.interval_min.ns < options.interval_max.ns);
46
+ // Sanity check and overflow protection for ewma.
47
+ assert(options.interval_max.ns <= 10 * std.time.ns_per_hour);
48
+ return .{
49
+ .interval_min = options.interval_min,
50
+ .interval_max = options.interval_max,
51
+
52
+ .signal_last = options.now,
53
+ .interval_ewma = options.interval_max,
54
+ };
55
+ }
56
+
57
+ pub fn signal(detector: *FaultDetector, now: Instant) void {
58
+ const past = detector.signal_last;
59
+ assert(past.ns <= now.ns);
60
+ const interval = now.duration_since(past)
61
+ // Clamp first, then ewma_add, to avoid overflows.
62
+ .clamp(detector.interval_min, detector.interval_max);
63
+
64
+ detector.interval_ewma = ewma_add_duration(detector.interval_ewma, interval);
65
+ detector.signal_last = now;
66
+ }
67
+
68
+ /// Is the signal overdue?
69
+ /// * green --- signal is on time
70
+ /// * yellow --- signal seems delayed/lost
71
+ /// * red --- signaler is likely dead
72
+ ///
73
+ /// On yellow, the primary injects a Commit.
74
+ /// On red, a backup sends SV.
75
+ ///
76
+ /// Rough model:
77
+ /// - Random delays, but 2X delay is suspicious.
78
+ /// - An individual signal can get lost.
79
+ /// Either of the above suggests 2X+some as a cutoff point for a fault.
80
+ /// We round up to 3X cutoff for red, and 1.5X cutoff for yellow.
81
+ ///
82
+ /// An alternative approach would be to build a probabilistic model of the prepare/commit arrival
83
+ /// process, and then compute the actual probability of primary being dead using Bayes' rule. We
84
+ /// don't do that, because we don't know the actual underlying model. A simple rule like the above
85
+ /// will not give us the optimal answer, but it should work in variety of different contexts!
86
+ pub fn tardy(detector: *FaultDetector, now: Instant) enum { green, yellow, red } {
87
+ const past = detector.signal_last;
88
+ assert(past.ns <= now.ns);
89
+ const interval = now.duration_since(past);
90
+
91
+ if (interval.ns *| 2 <= detector.interval_ewma.ns * 3) { // interval <= 1.5 * interval_ewma
92
+ return .green;
93
+ }
94
+ assert(interval.ns >= detector.interval_ewma.ns);
95
+ if (interval.ns <= detector.interval_ewma.ns * 3) {
96
+ return .yellow;
97
+ }
98
+ assert(interval.ns > detector.interval_ewma.ns);
99
+ return .red;
100
+ }
101
+
102
+ pub fn reset(detector: *FaultDetector, now: Instant) void {
103
+ const past = detector.signal_last;
104
+ assert(past.ns <= now.ns);
105
+ detector.* = FaultDetector.init(.{
106
+ .now = now,
107
+ .interval_min = detector.interval_min,
108
+ .interval_max = detector.interval_max,
109
+ });
110
+ }
111
+
112
+ fn ewma_add_duration(old: Duration, new: Duration) Duration {
113
+ return .{
114
+ .ns = @divFloor((old.ns * 4) + new.ns, 5),
115
+ };
116
+ }
117
+
118
+ test "FaultDetector: smoke" {
119
+ // Test that computed ewma interval tracks actual interval,
120
+ // clamped to limits.
121
+ var now: Instant = .{ .ns = 1_000 };
122
+ var detector = FaultDetector.init(.{
123
+ .now = now,
124
+ .interval_min = .ms(100),
125
+ .interval_max = .ms(2_000),
126
+ });
127
+ assert(detector.tardy(now) == .green);
128
+ assert(detector.interval_ewma.to_ms() == 2_000);
129
+
130
+ for (0..100) |_| {
131
+ now = now.add(.ms(200));
132
+ detector.signal(now);
133
+ }
134
+ now = now.add(.ms(200));
135
+ assert(detector.tardy(now) == .green);
136
+ assert(detector.interval_ewma.to_ms() == 200);
137
+
138
+ now = now.add(.ms(200));
139
+ assert(detector.tardy(now) == .yellow);
140
+
141
+ now = now.add(.ms(250));
142
+ assert(detector.tardy(now) == .red);
143
+
144
+ for (0..100) |_| {
145
+ now = now.add(.ms(1_000));
146
+ detector.signal(now);
147
+ }
148
+ now = now.add(.ms(1_000));
149
+ assert(detector.tardy(now) == .green);
150
+ assert(detector.interval_ewma.to_ms() == 999);
151
+
152
+ for (0..100) |_| {
153
+ now = now.add(.ms(10));
154
+ detector.signal(now);
155
+ }
156
+ now = now.add(.ms(10));
157
+ assert(detector.tardy(now) == .green);
158
+ assert(detector.interval_ewma.to_ms() == 100);
159
+
160
+ for (0..100) |_| {
161
+ now = now.add(.ms(10_000));
162
+ detector.signal(now);
163
+ }
164
+ now = now.add(.ms(10_000));
165
+ assert(detector.tardy(now) == .red);
166
+ assert(detector.interval_ewma.to_ms() == 1_999);
167
+ }
168
+
169
+ test "FaultDetector: smoothing" {
170
+ // Check that, after a burst of prepares is abruptly ended,
171
+ // the primary can gradually reduce the arrival interval,
172
+ // without triggering a view change.
173
+ var now: Instant = .{ .ns = 1_000 };
174
+ var primary = FaultDetector.init(.{
175
+ .now = now,
176
+ .interval_min = .ms(50),
177
+ .interval_max = .ms(2_000),
178
+ });
179
+
180
+ const backup_delay: Duration = .ms(100);
181
+ var backup = FaultDetector.init(.{
182
+ .now = now,
183
+ .interval_min = .ms(50),
184
+ .interval_max = .ms(2_000),
185
+ });
186
+
187
+ const commit_interval: Duration = .ms(500);
188
+ const request_interval: Duration = .ms(100);
189
+
190
+ var commit_last = now;
191
+ var request_last = now;
192
+
193
+ for (0..1_000) |_| {
194
+ now = now.add(.ms(10)); // Advance by one tick.
195
+
196
+ // Primary broadcasts commit message every 500ms.
197
+ if (now.duration_since(commit_last).ns > commit_interval.ns) {
198
+ commit_last = now;
199
+ primary.signal(now);
200
+ backup.signal(now.add(backup_delay));
201
+ }
202
+ assert(primary.tardy(now) == .green);
203
+
204
+ // Primary converts a request into prepare every 100ms.
205
+ if (now.duration_since(request_last).ns > request_interval.ns) {
206
+ request_last = now;
207
+ primary.signal(now);
208
+ backup.signal(now.add(backup_delay));
209
+ }
210
+ assert(backup.tardy(now.add(backup_delay)) == .green);
211
+ }
212
+ assert(primary.interval_ewma.to_ms() == 95);
213
+ assert(backup.interval_ewma.to_ms() == 95);
214
+
215
+ for (0..1_000) |_| {
216
+ now = now.add(.ms(10)); // Advance by one tick.
217
+
218
+ if (now.duration_since(commit_last).ns > commit_interval.ns) {
219
+ commit_last = now;
220
+ primary.signal(now);
221
+ backup.signal(now.add(backup_delay));
222
+ }
223
+ switch (primary.tardy(now)) {
224
+ .green => {},
225
+ .yellow => {
226
+ // Stay awhile and listen, wanderer, the story of the next line.
227
+ //
228
+ // The original implementation in replica.zig didn't have the equivalent. In other
229
+ // words, the primary was always sending a commit message every 500 ms, and then
230
+ // additionally injecting a "bonus" commit whenever fault detector flashed yellow.
231
+ // That is, when the delay since last commit/prepare exceeded 1.5X of the current
232
+ // interval. Because 1.5 > 1, it should be the case that the ewma of the interval
233
+ // gradually converges to 500 ms, right?
234
+ //
235
+ // Wrong! Implementing this test to double-check "obviously correct" logic showed
236
+ // that the interval expands from 100ms to 250ms, but then gets stuck!
237
+ // Here's the picture. Originally we start with an idle cluster where only commits
238
+ // are pulsed periodically:
239
+ //
240
+ // C C C C C
241
+ //
242
+ // Then the load starts, and we get a lot of prepares in between:
243
+ //
244
+ // C P P P C P P P C P P P C P P P C
245
+ //
246
+ // Then, the load cuts off, but the primary starts Injecting extra commits to
247
+ // keep the interval:
248
+ //
249
+ // C I I I C I I C I C ....
250
+ //
251
+ // The frequency of injected commits goes down to just one, but then gets stuck:
252
+ //
253
+ // C I C I C I C I C
254
+ //
255
+ // Both the first and the last lines are fixed points. For the first line, the ewma
256
+ // is 500ms and no commits are injected. For the last line, the ewma is 250ms, and
257
+ // at 250*1.5=350ms after the last C, an I gets injected.
258
+ //
259
+ // More generally, if we are currently injecting one extra message, we need to
260
+ // tolerate at least 2X injection delay to get to zero extra messages, _if_ we keep
261
+ // a steady pace of heartbeat commits. This means that delay to detect a crashed
262
+ // primary needs to grow proportionally more, and that feels like too much of a lag.
263
+ // Instead, we let go of the constraint of keeping the heartbeat steady, and skip
264
+ // the next normal beat whenever we inject one.
265
+ commit_last = now;
266
+
267
+ primary.signal(now);
268
+ backup.signal(now.add(backup_delay));
269
+ },
270
+ .red => unreachable,
271
+ }
272
+ switch (backup.tardy(now.add(backup_delay))) {
273
+ .green, .yellow => {},
274
+ .red => unreachable,
275
+ }
276
+ }
277
+ assert(primary.interval_ewma.to_ms() == 499);
278
+ assert(backup.interval_ewma.to_ms() == 499);
279
+ }