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,628 @@
1
+ //! Fuzz ManifestLog open()/insert()/update()/remove()/compact()/checkpoint().
2
+ //!
3
+ //! Invariants checked:
4
+ //!
5
+ //! - Checkpoint flushes all buffered log blocks (including partial blocks).
6
+ //! - The state of the ManifestLog immediately after recovery matches
7
+ //! the state of the ManifestLog immediately after the latest checkpoint.
8
+ //! - ManifestLog.open() only returns the latest version of each table.
9
+ //! - The ManifestLog performs enough compaction to not "fall behind" (i.e. run out of blocks).
10
+ //!
11
+ const std = @import("std");
12
+ const assert = std.debug.assert;
13
+ const log = std.log.scoped(.fuzz_lsm_manifest_log);
14
+
15
+ const stdx = @import("stdx");
16
+ const vsr = @import("../vsr.zig");
17
+ const fixtures = @import("../testing/fixtures.zig");
18
+ const constants = @import("../constants.zig");
19
+ const SuperBlock = @import("../vsr/superblock.zig").SuperBlockType(Storage);
20
+ const TimeSim = @import("../testing/time.zig").TimeSim;
21
+ const Storage = @import("../testing/storage.zig").Storage;
22
+ const Grid = @import("../vsr/grid.zig").GridType(Storage);
23
+ const ManifestLog = @import("manifest_log.zig").ManifestLogType(Storage);
24
+ const ManifestLogPace = @import("manifest_log.zig").Pace;
25
+ const fuzz = @import("../testing/fuzz.zig");
26
+ const schema = @import("./schema.zig");
27
+ const compaction_tables_input_max = @import("./compaction.zig").compaction_tables_input_max;
28
+ const TableInfo = schema.ManifestNode.TableInfo;
29
+ const ratio = stdx.PRNG.ratio;
30
+
31
+ const tree_count = 20;
32
+ const manifest_log_compaction_pace = ManifestLogPace.init(.{
33
+ // Use many trees so that we fill manifest blocks quickly.
34
+ // (This makes it easier to hit "worst case" scenarios in manifest compaction pacing.)
35
+ .tree_count = tree_count,
36
+ // Use a artificially low table-count-max so that we can easily fill the manifest log and verify
37
+ // that pacing is correct.
38
+ .tables_max = schema.ManifestNode.entry_count_max * 100,
39
+ .compact_extra_blocks = constants.lsm_manifest_compact_extra_blocks,
40
+ });
41
+
42
+ pub fn main(gpa: std.mem.Allocator, args: fuzz.FuzzArgs) !void {
43
+ var prng = stdx.PRNG.from_seed(args.seed);
44
+
45
+ const events_count = @min(
46
+ args.events_max orelse @as(usize, 1e7),
47
+ fuzz.random_int_exponential(&prng, usize, 1e6),
48
+ );
49
+
50
+ const events = try generate_events(gpa, &prng, events_count);
51
+ defer gpa.free(events);
52
+
53
+ try run_fuzz(gpa, &prng, events);
54
+ log.info("Passed!", .{});
55
+ }
56
+
57
+ fn run_fuzz(
58
+ gpa: std.mem.Allocator,
59
+ prng: *stdx.PRNG,
60
+ events: []const ManifestEvent,
61
+ ) !void {
62
+ const storage_options: Storage.Options = .{
63
+ .seed = prng.int(u64),
64
+ .size = constants.storage_size_limit_default,
65
+ .read_latency_min = .ms(10),
66
+ .read_latency_mean = fuzz.range_inclusive_ms(prng, 10, 400),
67
+ .write_latency_min = .ms(10),
68
+ .write_latency_mean = fuzz.range_inclusive_ms(prng, 10, 400),
69
+ };
70
+
71
+ var env: Environment = undefined;
72
+ try env.init(gpa, storage_options);
73
+ defer env.deinit();
74
+
75
+ {
76
+ fixtures.open_superblock(&env.superblock);
77
+ fixtures.open_grid(&env.grid);
78
+
79
+ // The first checkpoint is trivially durable.
80
+ env.grid.free_set.mark_checkpoint_durable();
81
+
82
+ env.open();
83
+ env.wait(&env.manifest_log);
84
+ }
85
+
86
+ // The manifest doesn't compact during the first bar.
87
+ for (0..2) |_| {
88
+ try env.half_bar_commence();
89
+ try env.half_bar_complete();
90
+ }
91
+
92
+ try env.half_bar_commence();
93
+
94
+ for (events) |event| {
95
+ log.debug("event={}", .{event});
96
+ switch (event) {
97
+ .append => |table_info| try env.append(&table_info),
98
+ .compact => {
99
+ try env.half_bar_complete();
100
+ try env.half_bar_commence();
101
+ },
102
+ .checkpoint => {
103
+ // Checkpoint always immediately follows compaction.
104
+ try env.half_bar_complete();
105
+ try env.checkpoint();
106
+ try env.half_bar_commence();
107
+ },
108
+ .noop => {},
109
+ }
110
+ }
111
+
112
+ try env.half_bar_complete();
113
+ }
114
+
115
+ const ManifestEvent = union(enum) {
116
+ append: TableInfo,
117
+ compact,
118
+ checkpoint,
119
+ /// The random EventType could not be generated — this simplifies event generation.
120
+ noop,
121
+ };
122
+
123
+ fn generate_events(
124
+ gpa: std.mem.Allocator,
125
+ prng: *stdx.PRNG,
126
+ events_count: usize,
127
+ ) ![]const ManifestEvent {
128
+ var events = std.ArrayList(ManifestEvent).init(gpa);
129
+ errdefer events.deinit();
130
+
131
+ var tables = std.ArrayList(TableInfo).init(gpa);
132
+ defer tables.deinit();
133
+
134
+ // The maximum number of (live) tables that the manifest has at any point in time.
135
+ var tables_max: usize = 0;
136
+
137
+ // Dummy table address for Table Infos.
138
+ var table_address: u64 = 1;
139
+
140
+ const compacts_per_checkpoint = fuzz.random_int_exponential(prng, usize, 16);
141
+ log.info("compacts_per_checkpoint = {d}", .{compacts_per_checkpoint});
142
+
143
+ // When true, create as many entries as possible.
144
+ // This tries to test the manifest upper-bound calculation.
145
+ const fill_always = prng.chance(ratio(1, 4));
146
+
147
+ // The maximum number of snapshot-max updates per half-bar.
148
+ // For now, half of the total compactions.
149
+ const updates_max = stdx.div_ceil(constants.lsm_levels, 2) * compaction_tables_input_max;
150
+
151
+ while (events.items.len < events_count) {
152
+ const fill = fill_always or prng.boolean();
153
+ // All of the trees we are inserting/modifying have the same id (for simplicity), but we
154
+ // want to perform more updates if there are more trees, to better simulate a real state
155
+ // machine.
156
+ for (0..tree_count) |_| {
157
+ const operations: struct {
158
+ update_levels: usize,
159
+ update_snapshots: usize,
160
+ inserts: usize,
161
+ } = operations: {
162
+ const move = !fill and prng.chance(ratio(1, 10));
163
+ if (move) {
164
+ break :operations .{
165
+ .update_levels = 1,
166
+ .update_snapshots = 0,
167
+ .inserts = 0,
168
+ };
169
+ } else {
170
+ const updates =
171
+ if (fill) updates_max else prng.int_inclusive(usize, updates_max);
172
+ break :operations .{
173
+ .update_levels = 0,
174
+ .update_snapshots = updates,
175
+ .inserts = updates,
176
+ };
177
+ }
178
+ };
179
+
180
+ for (0..operations.inserts) |_| {
181
+ if (tables.items.len == manifest_log_compaction_pace.tables_max) break;
182
+
183
+ const table = TableInfo{
184
+ .checksum = 0,
185
+ .address = table_address,
186
+ .snapshot_min = 1,
187
+ .snapshot_max = std.math.maxInt(u64),
188
+ .key_min = @splat(0),
189
+ .key_max = @splat(0),
190
+ .value_count = 1,
191
+ .tree_id = 1,
192
+ .label = .{
193
+ .event = .insert,
194
+ .level = prng.int_inclusive(u6, constants.lsm_levels - 1),
195
+ },
196
+ };
197
+
198
+ table_address += 1;
199
+ try tables.append(table);
200
+ try events.append(.{ .append = table });
201
+ }
202
+ tables_max = @max(tables_max, tables.items.len);
203
+
204
+ for (0..operations.update_levels) |_| {
205
+ if (tables.items.len == 0) break;
206
+
207
+ var table = tables.items[prng.index(tables.items)];
208
+ if (table.label.level == constants.lsm_levels - 1) continue;
209
+ table.label.event = .update;
210
+ table.label.level += 1;
211
+ try events.append(.{ .append = table });
212
+ }
213
+
214
+ for (0..operations.update_snapshots) |_| {
215
+ if (tables.items.len == 0) break;
216
+
217
+ var table = tables.items[prng.index(tables.items)];
218
+ // Only update a table snapshot_max once (like real compaction).
219
+ if (table.snapshot_max == 2) continue;
220
+ table.label.event = .update;
221
+ table.snapshot_max = 2;
222
+ try events.append(.{ .append = table });
223
+ }
224
+ }
225
+
226
+ // We apply removes only after all inserts/updates (rather than mixing them together) to
227
+ // mimic how compaction is followed by remove_invisible_tables().
228
+ var i: usize = 0;
229
+ while (i < tables.items.len) {
230
+ if (tables.items[i].snapshot_max == 2) {
231
+ var table = tables.swapRemove(i);
232
+ table.label.event = .remove;
233
+ try events.append(.{ .append = table });
234
+ } else {
235
+ i += 1;
236
+ }
237
+ }
238
+
239
+ if (prng.int_inclusive(usize, compacts_per_checkpoint) == 0) {
240
+ try events.append(.checkpoint);
241
+ } else {
242
+ try events.append(.compact);
243
+ }
244
+ }
245
+ log.info("event_count = {d}", .{events.items.len});
246
+ log.info("tables_max = {d}/{d}", .{ tables_max, manifest_log_compaction_pace.tables_max });
247
+
248
+ return events.toOwnedSlice();
249
+ }
250
+
251
+ const Environment = struct {
252
+ gpa: std.mem.Allocator,
253
+ storage: Storage,
254
+ storage_verify: Storage,
255
+ time_sim: TimeSim,
256
+ trace: Storage.Tracer,
257
+ trace_verify: Storage.Tracer,
258
+ superblock: SuperBlock,
259
+ superblock_verify: SuperBlock,
260
+ superblock_context: SuperBlock.Context,
261
+
262
+ grid: Grid,
263
+ grid_verify: Grid,
264
+
265
+ manifest_log: ManifestLog,
266
+ manifest_log_verify: ManifestLog,
267
+ manifest_log_model: ManifestLogModel,
268
+ manifest_log_opening: ?ManifestLogModel.TableMap,
269
+ pending: u32,
270
+
271
+ fn init(
272
+ env: *Environment, // In-place construction for stable addresses.
273
+ gpa: std.mem.Allocator,
274
+ storage_options: Storage.Options,
275
+ ) !void {
276
+ comptime var fields_initialized = 0;
277
+
278
+ fields_initialized += 1;
279
+ env.gpa = gpa;
280
+
281
+ fields_initialized += 1;
282
+ env.storage = try fixtures.init_storage(gpa, storage_options);
283
+ errdefer env.storage.deinit(gpa);
284
+
285
+ try fixtures.storage_format(gpa, &env.storage, .{});
286
+
287
+ fields_initialized += 1;
288
+ env.storage_verify = try fixtures.init_storage(gpa, storage_options);
289
+ errdefer env.storage_verify.deinit(gpa);
290
+
291
+ fields_initialized += 1;
292
+ env.time_sim = fixtures.init_time(.{});
293
+
294
+ fields_initialized += 1;
295
+ env.trace = try fixtures.init_tracer(gpa, env.time_sim.time(), .{});
296
+ errdefer env.trace.deinit(gpa);
297
+
298
+ fields_initialized += 1;
299
+ env.trace_verify = try fixtures.init_tracer(gpa, env.time_sim.time(), .{});
300
+ errdefer env.trace_verify.deinit(gpa);
301
+
302
+ fields_initialized += 1;
303
+ env.superblock = try fixtures.init_superblock(gpa, &env.storage, .{});
304
+ errdefer env.superblock.deinit(gpa);
305
+
306
+ fields_initialized += 1;
307
+ env.superblock_verify = try fixtures.init_superblock(gpa, &env.storage_verify, .{});
308
+ errdefer env.superblock_verify.deinit(gpa);
309
+
310
+ fields_initialized += 1;
311
+ env.superblock_context = undefined;
312
+
313
+ fields_initialized += 1;
314
+ env.grid = try fixtures.init_grid(gpa, &env.trace, &env.superblock, .{
315
+ // Grid.mark_checkpoint_not_durable releases the FreeSet checkpoints blocks into
316
+ // FreeSet.blocks_released_prior_checkpoint_durability.
317
+ .blocks_released_prior_checkpoint_durability_max = 0,
318
+ });
319
+ errdefer env.grid.deinit(gpa);
320
+
321
+ fields_initialized += 1;
322
+ env.grid_verify =
323
+ try fixtures.init_grid(gpa, &env.trace_verify, &env.superblock_verify, .{});
324
+ errdefer env.grid_verify.deinit(gpa);
325
+
326
+ fields_initialized += 1;
327
+ try env.manifest_log.init(gpa, &env.grid, &manifest_log_compaction_pace);
328
+ errdefer env.manifest_log.deinit(gpa);
329
+
330
+ fields_initialized += 1;
331
+ try env.manifest_log_verify.init(
332
+ gpa,
333
+ &env.grid_verify,
334
+ &manifest_log_compaction_pace,
335
+ );
336
+ errdefer env.manifest_log_verify.deinit(gpa);
337
+
338
+ fields_initialized += 1;
339
+ env.manifest_log_model = try ManifestLogModel.init(gpa);
340
+ errdefer env.manifest_log_model.deinit();
341
+
342
+ fields_initialized += 1;
343
+ env.manifest_log_opening = null;
344
+ fields_initialized += 1;
345
+ env.pending = 0;
346
+
347
+ comptime assert(fields_initialized == std.meta.fields(@This()).len);
348
+ }
349
+
350
+ fn deinit(env: *Environment) void {
351
+ assert(env.manifest_log_opening == null);
352
+ env.manifest_log_model.deinit();
353
+ env.manifest_log_verify.deinit(env.gpa);
354
+ env.manifest_log.deinit(env.gpa);
355
+ env.grid_verify.deinit(env.gpa);
356
+ env.grid.deinit(env.gpa);
357
+ env.superblock_verify.deinit(env.gpa);
358
+ env.superblock.deinit(env.gpa);
359
+ env.trace_verify.deinit(env.gpa);
360
+ env.trace.deinit(env.gpa);
361
+ env.storage_verify.deinit(env.gpa);
362
+ env.storage.deinit(env.gpa);
363
+ env.* = undefined;
364
+ }
365
+
366
+ fn wait(env: *Environment, manifest_log: *ManifestLog) void {
367
+ while (env.pending > 0) {
368
+ manifest_log.superblock.storage.run();
369
+ }
370
+ }
371
+
372
+ fn open(env: *Environment) void {
373
+ assert(env.pending == 0);
374
+
375
+ env.pending += 1;
376
+ env.manifest_log.open(open_event, open_callback);
377
+ }
378
+
379
+ fn open_event(manifest_log: *ManifestLog, table: *const TableInfo) void {
380
+ _ = manifest_log;
381
+ _ = table;
382
+
383
+ // This ManifestLog is only opened during setup, when it has no blocks.
384
+ unreachable;
385
+ }
386
+
387
+ fn open_callback(manifest_log: *ManifestLog) void {
388
+ const env: *Environment = @fieldParentPtr("manifest_log", manifest_log);
389
+ env.pending -= 1;
390
+ }
391
+
392
+ fn append(env: *Environment, table: *const TableInfo) !void {
393
+ try env.manifest_log_model.append(table);
394
+ env.manifest_log.append(table);
395
+ }
396
+
397
+ fn half_bar_commence(env: *Environment) !void {
398
+ env.pending += 1;
399
+ const op = vsr.Checkpoint.checkpoint_after(
400
+ env.manifest_log.superblock.working.vsr_state.checkpoint.header.op,
401
+ );
402
+ env.manifest_log.compact(
403
+ manifest_log_compact_callback,
404
+ op,
405
+ );
406
+ env.wait(&env.manifest_log);
407
+ }
408
+
409
+ fn manifest_log_compact_callback(manifest_log: *ManifestLog) void {
410
+ const env: *Environment = @fieldParentPtr("manifest_log", manifest_log);
411
+ env.pending -= 1;
412
+ }
413
+
414
+ fn half_bar_complete(env: *Environment) !void {
415
+ env.manifest_log.compact_end();
416
+ }
417
+
418
+ fn checkpoint(env: *Environment) !void {
419
+ assert(env.manifest_log.grid_reservation == null);
420
+
421
+ try env.manifest_log_model.checkpoint();
422
+
423
+ env.pending += 1;
424
+ env.manifest_log.checkpoint(checkpoint_manifest_log_callback);
425
+ env.wait(&env.manifest_log);
426
+
427
+ env.pending += 1;
428
+ env.grid.checkpoint(checkpoint_grid_callback);
429
+ env.wait(&env.manifest_log);
430
+
431
+ const vsr_state = &env.manifest_log.superblock.working.vsr_state;
432
+
433
+ env.pending += 1;
434
+ env.manifest_log.superblock.checkpoint(
435
+ checkpoint_superblock_callback,
436
+ &env.superblock_context,
437
+ .{
438
+ .header = header: {
439
+ var header = vsr.Header.Prepare.root(fixtures.cluster);
440
+ header.op = vsr.Checkpoint.checkpoint_after(vsr_state.checkpoint.header.op);
441
+ header.set_checksum();
442
+ break :header header;
443
+ },
444
+ .view_attributes = null,
445
+ .manifest_references = env.manifest_log.checkpoint_references(),
446
+ .free_set_references = .{
447
+ .blocks_acquired = env.grid
448
+ .free_set_checkpoint_blocks_acquired.checkpoint_reference(),
449
+ .blocks_released = env.grid
450
+ .free_set_checkpoint_blocks_released.checkpoint_reference(),
451
+ },
452
+ .client_sessions_reference = .{
453
+ .last_block_checksum = 0,
454
+ .last_block_address = 0,
455
+ .trailer_size = 0,
456
+ .checksum = vsr.checksum(&.{}),
457
+ },
458
+ .commit_max = vsr.Checkpoint.checkpoint_after(vsr_state.commit_max),
459
+ .sync_op_min = 0,
460
+ .sync_op_max = 0,
461
+ .storage_size = vsr.superblock.data_file_size_min +
462
+ (env.grid.free_set.highest_address_acquired() orelse 0) * constants.block_size,
463
+ .release = vsr.Release.minimum,
464
+ },
465
+ );
466
+ env.wait(&env.manifest_log);
467
+
468
+ // The fuzzer runs in a single process, all checkpoints are trivially durable. Use
469
+ // free_set.mark_checkpoint_durable() instead of grid.mark_checkpoint_durable(); the
470
+ // latter requires passing a callback, which is called synchronously in fuzzers anyway.
471
+ env.grid.mark_checkpoint_not_durable();
472
+ env.grid.free_set.mark_checkpoint_durable();
473
+
474
+ try env.verify();
475
+ }
476
+
477
+ fn checkpoint_manifest_log_callback(manifest_log: *ManifestLog) void {
478
+ const env: *Environment = @fieldParentPtr("manifest_log", manifest_log);
479
+ env.pending -= 1;
480
+ }
481
+
482
+ fn checkpoint_grid_callback(grid: *Grid) void {
483
+ const env: *Environment = @fieldParentPtr("grid", grid);
484
+ env.pending -= 1;
485
+ }
486
+
487
+ fn checkpoint_superblock_callback(context: *SuperBlock.Context) void {
488
+ const env: *Environment = @fieldParentPtr("superblock_context", context);
489
+ env.pending -= 1;
490
+ }
491
+
492
+ /// Verify that the state of a ManifestLog restored from checkpoint matches the state
493
+ /// immediately after the checkpoint was created.
494
+ fn verify(env: *Environment) !void {
495
+ {
496
+ env.storage_verify.copy(env.manifest_log.superblock.storage);
497
+ env.storage_verify.reset();
498
+
499
+ env.trace_verify.deinit(env.gpa);
500
+ env.trace_verify = try fixtures.init_tracer(env.gpa, env.time_sim.time(), .{});
501
+
502
+ // Reset the state so that the manifest log (and dependencies) can be reused.
503
+ // Do not "defer deinit()" because these are cleaned up by Env.deinit().
504
+ env.superblock_verify.deinit(env.gpa);
505
+ env.superblock_verify = try fixtures.init_superblock(env.gpa, &env.storage_verify, .{});
506
+
507
+ env.grid_verify.deinit(env.gpa);
508
+ env.grid_verify = try Grid.init(env.gpa, .{
509
+ .superblock = &env.superblock_verify,
510
+ .trace = &env.trace_verify,
511
+ .missing_blocks_max = 0,
512
+ .missing_tables_max = 0,
513
+ .blocks_released_prior_checkpoint_durability_max = 0,
514
+ });
515
+
516
+ env.manifest_log_verify.deinit(env.gpa);
517
+ try env.manifest_log_verify.init(
518
+ env.gpa,
519
+ &env.grid_verify,
520
+ &manifest_log_compaction_pace,
521
+ );
522
+ }
523
+
524
+ fixtures.open_superblock(&env.superblock_verify);
525
+
526
+ assert(env.manifest_log_opening == null);
527
+ env.manifest_log_opening = try env.manifest_log_model.tables.clone();
528
+ defer {
529
+ assert(env.manifest_log_opening.?.count() == 0);
530
+ env.manifest_log_opening.?.deinit();
531
+ env.manifest_log_opening = null;
532
+ }
533
+
534
+ env.pending += 1;
535
+ env.manifest_log_verify.open(verify_manifest_open_event, verify_manifest_open_callback);
536
+ env.wait(&env.manifest_log_verify);
537
+
538
+ try std.testing.expect(hash_map_equals(
539
+ u64,
540
+ ManifestLog.TableExtent,
541
+ &env.manifest_log.table_extents,
542
+ &env.manifest_log_verify.table_extents,
543
+ ));
544
+ }
545
+
546
+ fn verify_manifest_open_event(
547
+ manifest_log_verify: *ManifestLog,
548
+ table: *const TableInfo,
549
+ ) void {
550
+ const env: *Environment = @fieldParentPtr("manifest_log_verify", manifest_log_verify);
551
+ assert(env.pending > 0);
552
+
553
+ const expect = env.manifest_log_opening.?.fetchRemove(table.address).?;
554
+ assert(std.meta.eql(expect.value, table.*));
555
+ }
556
+
557
+ fn verify_manifest_open_callback(manifest_log_verify: *ManifestLog) void {
558
+ const env: *Environment = @fieldParentPtr("manifest_log_verify", manifest_log_verify);
559
+ env.pending -= 1;
560
+ }
561
+ };
562
+
563
+ const ManifestLogModel = struct {
564
+ /// Stores the latest checkpointed version of every table.
565
+ /// Indexed by table address.
566
+ const TableMap = std.AutoHashMap(u64, TableInfo);
567
+
568
+ /// Stores table updates that are not yet checkpointed.
569
+ const AppendList = std.ArrayList(TableInfo);
570
+
571
+ tables: TableMap,
572
+ appends: AppendList,
573
+
574
+ fn init(gpa: std.mem.Allocator) !ManifestLogModel {
575
+ const tables = TableMap.init(gpa);
576
+ errdefer tables.deinit(gpa);
577
+
578
+ const appends = AppendList.init(gpa);
579
+ errdefer appends.deinit(gpa);
580
+
581
+ return ManifestLogModel{
582
+ .tables = tables,
583
+ .appends = appends,
584
+ };
585
+ }
586
+
587
+ fn deinit(model: *ManifestLogModel) void {
588
+ model.tables.deinit();
589
+ model.appends.deinit();
590
+ }
591
+
592
+ fn append(model: *ManifestLogModel, table: *const TableInfo) !void {
593
+ try model.appends.append(table.*);
594
+ }
595
+
596
+ fn checkpoint(model: *ManifestLogModel) !void {
597
+ for (model.appends.items) |table_info| {
598
+ switch (table_info.label.event) {
599
+ .insert,
600
+ .update,
601
+ => try model.tables.put(table_info.address, table_info),
602
+ .remove => {
603
+ const removed = model.tables.fetchRemove(table_info.address).?;
604
+ assert(std.meta.eql(removed.value, table_info));
605
+ },
606
+ .reserved => unreachable,
607
+ }
608
+ }
609
+ model.appends.clearRetainingCapacity();
610
+ }
611
+ };
612
+
613
+ fn hash_map_equals(
614
+ comptime K: type,
615
+ comptime V: type,
616
+ a: *const std.AutoHashMapUnmanaged(K, V),
617
+ b: *const std.AutoHashMapUnmanaged(K, V),
618
+ ) bool {
619
+ if (a.count() != b.count()) return false;
620
+
621
+ var a_iterator = a.iterator();
622
+ while (a_iterator.next()) |a_entry| {
623
+ const a_value = a_entry.value_ptr.*;
624
+ const b_value = b.get(a_entry.key_ptr.*) orelse return false;
625
+ if (!std.meta.eql(a_value, b_value)) return false;
626
+ }
627
+ return true;
628
+ }