rubydb 0.1.4 → 0.1.6

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 (469) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +15 -15
  3. data/.github/workflows/benchmark.yml +26 -26
  4. data/.github/workflows/compatibility.yml +63 -63
  5. data/.github/workflows/fuzz.yml +33 -33
  6. data/.github/workflows/lint.yml +21 -21
  7. data/.github/workflows/operations.yml +24 -24
  8. data/.github/workflows/production-validation.yml +111 -111
  9. data/.github/workflows/release.yml +77 -77
  10. data/.github/workflows/security.yml +39 -37
  11. data/.github/workflows/test.yml +26 -26
  12. data/.github/workflows/workload.yml +58 -58
  13. data/.gitignore +16 -5
  14. data/.rubocop.yml +50 -44
  15. data/.standard.yml +9 -14
  16. data/ARCHITECTURE.md +21 -21
  17. data/CHANGELOG.md +57 -27
  18. data/CODE_OF_CONDUCT.md +13 -13
  19. data/CONTRIBUTING.md +29 -29
  20. data/GOVERNANCE.md +16 -16
  21. data/Gemfile +18 -17
  22. data/Gemfile.lock +125 -71
  23. data/README.md +168 -12
  24. data/ROADMAP.md +27 -27
  25. data/Rakefile +76 -71
  26. data/SECURITY.md +54 -54
  27. data/SUPPORT.md +14 -14
  28. data/accelerator/bin/SHA256SUMS +6 -0
  29. data/accelerator/bin/rubydb-accelerator-darwin-amd64 +0 -0
  30. data/accelerator/bin/rubydb-accelerator-darwin-arm64 +0 -0
  31. data/accelerator/bin/rubydb-accelerator-linux-amd64 +0 -0
  32. data/accelerator/bin/rubydb-accelerator-linux-arm64 +0 -0
  33. data/accelerator/bin/rubydb-accelerator-windows-amd64.exe +0 -0
  34. data/accelerator/bin/rubydb-accelerator-windows-arm64.exe +0 -0
  35. data/accelerator/cmd/rubydb-accelerator/main.go +11 -0
  36. data/accelerator/go.mod +3 -0
  37. data/accelerator/internal/execution/aggregate.go +94 -0
  38. data/accelerator/internal/execution/distinct.go +22 -0
  39. data/accelerator/internal/execution/filter.go +73 -0
  40. data/accelerator/internal/execution/join.go +79 -0
  41. data/accelerator/internal/execution/operators.go +167 -0
  42. data/accelerator/internal/execution/scan.go +20 -0
  43. data/accelerator/internal/execution/sort.go +62 -0
  44. data/accelerator/internal/execution/types.go +136 -0
  45. data/accelerator/internal/execution/value.go +67 -0
  46. data/accelerator/internal/memory/arena.go +47 -0
  47. data/accelerator/internal/memory/reuse.go +22 -0
  48. data/accelerator/internal/metrics/registry.go +67 -0
  49. data/accelerator/internal/parallel/bounded_queue.go +56 -0
  50. data/accelerator/internal/parallel/scheduler.go +47 -0
  51. data/accelerator/internal/parallel/worker_pool.go +53 -0
  52. data/accelerator/internal/protocol/cancellation.go +48 -0
  53. data/accelerator/internal/protocol/columnar.go +263 -0
  54. data/accelerator/internal/protocol/frame.go +187 -0
  55. data/accelerator/internal/runtime/worker.go +521 -0
  56. data/accelerator/internal/storage/page_reader.go +81 -0
  57. data/accelerator/internal/storage/snapshot_scan.go +539 -0
  58. data/accelerator/internal/wal/checksum.go +13 -0
  59. data/accelerator/internal/wal/compression.go +41 -0
  60. data/accelerator/internal/wal/group_commit.go +24 -0
  61. data/accelerator/internal/wal/record_encoder.go +40 -0
  62. data/adapters/activerecord/Gemfile +11 -11
  63. data/adapters/activerecord/README.md +8 -3
  64. data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +881 -879
  65. data/adapters/activerecord/rubydb-activerecord.gemspec +21 -21
  66. data/adapters/activerecord/spec/rubydb_adapter_integration_spec.rb +143 -143
  67. data/adapters/ruby/README.md +18 -18
  68. data/adapters/sequel/README.md +11 -11
  69. data/config/monitoring/prometheus-alerts.yml +39 -39
  70. data/config/production.yml +36 -36
  71. data/docs/README.md +77 -71
  72. data/docs/architecture/concurrency.md +14 -14
  73. data/docs/architecture/current-state.md +125 -125
  74. data/docs/architecture/execution-engine.md +25 -25
  75. data/docs/architecture/go-accelerator.md +179 -0
  76. data/docs/architecture/indexes.md +19 -19
  77. data/docs/architecture/mvcc.md +19 -19
  78. data/docs/architecture/overview.md +13 -13
  79. data/docs/architecture/pages.md +11 -11
  80. data/docs/architecture/production-roadmap.md +82 -82
  81. data/docs/architecture/query-planner.md +20 -20
  82. data/docs/architecture/recovery.md +18 -18
  83. data/docs/architecture/sql-engine.md +12 -12
  84. data/docs/architecture/storage-engine.md +14 -14
  85. data/docs/architecture/transactions.md +10 -10
  86. data/docs/architecture/wal.md +28 -28
  87. data/docs/cli-cheatsheet.md +98 -98
  88. data/docs/cli.md +299 -275
  89. data/docs/contributing/architecture.md +9 -9
  90. data/docs/contributing/benchmarking.md +30 -14
  91. data/docs/contributing/development.md +16 -16
  92. data/docs/contributing/release-process.md +49 -49
  93. data/docs/contributing/testing.md +16 -16
  94. data/docs/debugging.md +229 -229
  95. data/docs/developer/branching.md +10 -10
  96. data/docs/developer/database-diff.md +10 -10
  97. data/docs/developer/local-development.md +49 -17
  98. data/docs/developer/snapshots.md +9 -9
  99. data/docs/developer/temporal-data.md +10 -10
  100. data/docs/developer-guide.md +297 -297
  101. data/docs/getting-started/first-database.md +16 -16
  102. data/docs/getting-started/first-query.md +13 -13
  103. data/docs/getting-started/installation.md +19 -19
  104. data/docs/getting-started/local-to-production.md +300 -300
  105. data/docs/getting-started/quickstart.md +17 -17
  106. data/docs/getting-started/rails.md +16 -16
  107. data/docs/hardening_backlog.md +93 -93
  108. data/docs/lessons-learned.md +112 -112
  109. data/docs/operations/backups.md +33 -33
  110. data/docs/operations/disaster-recovery.md +31 -31
  111. data/docs/operations/failover.md +30 -30
  112. data/docs/operations/monitoring.md +25 -25
  113. data/docs/operations/production-guide.md +295 -295
  114. data/docs/operations/production-runbook.md +45 -45
  115. data/docs/operations/replication.md +33 -33
  116. data/docs/operations/restore.md +6 -6
  117. data/docs/operations/runbook.md +34 -34
  118. data/docs/operations/upgrades.md +14 -14
  119. data/docs/operations/workload-testing.md +17 -17
  120. data/docs/production-readiness.md +118 -118
  121. data/docs/production_validation.md +150 -150
  122. data/docs/rails/active-record.md +11 -11
  123. data/docs/rails/compatibility-guide.md +90 -90
  124. data/docs/rails/database-yml.md +92 -92
  125. data/docs/rails/installation.md +17 -17
  126. data/docs/rails/migrations.md +17 -17
  127. data/docs/rails/production.md +82 -82
  128. data/docs/rails/troubleshooting.md +18 -18
  129. data/docs/release.md +25 -25
  130. data/docs/server/architecture.md +10 -10
  131. data/docs/server/authentication.md +10 -10
  132. data/docs/server/configuration.md +16 -16
  133. data/docs/server/connection-pooling.md +10 -10
  134. data/docs/server/deployment.md +10 -10
  135. data/docs/server/protocol.md +12 -12
  136. data/docs/sql/compatibility-guide.md +82 -82
  137. data/docs/sql/compatibility.md +39 -39
  138. data/docs/sql/data-types.md +10 -10
  139. data/docs/sql/functions.md +9 -9
  140. data/docs/sql/joins.md +9 -9
  141. data/docs/sql/operators.md +9 -9
  142. data/docs/sql/sqlite-compatibility.md +21 -21
  143. data/docs/sql/syntax.md +10 -10
  144. data/docs/sql/transactions.md +10 -10
  145. data/docs/troubleshooting.md +244 -244
  146. data/lessons/01-foundations.md +73 -0
  147. data/lessons/02-local-development.md +121 -0
  148. data/lessons/03-embedded-rubydb.md +99 -0
  149. data/lessons/04-rails-complex-apps.md +138 -0
  150. data/lessons/05-rubydb-production-server.md +237 -0
  151. data/lessons/06-postgresql-massive-apps.md +96 -0
  152. data/lessons/07-hybrid-microservices.md +179 -0
  153. data/lessons/08-migrations-backups-recovery.md +86 -0
  154. data/lessons/09-observability-security-scale.md +87 -0
  155. data/lessons/10-release-readiness.md +192 -0
  156. data/lessons/11-community-adapter.md +323 -0
  157. data/lessons/12-rails-ecommerce-pressure.md +263 -0
  158. data/lib/rubydb/accelerator/client.rb +451 -0
  159. data/lib/rubydb/accelerator/error.rb +22 -0
  160. data/lib/rubydb/accelerator/manager.rb +606 -0
  161. data/lib/rubydb/accelerator.rb +13 -0
  162. data/lib/rubydb/backup/archive.rb +332 -334
  163. data/lib/rubydb/backup/backup.rb +400 -401
  164. data/lib/rubydb/backup/incremental.rb +349 -353
  165. data/lib/rubydb/backup/restore.rb +289 -290
  166. data/lib/rubydb/backup/snapshot.rb +265 -267
  167. data/lib/rubydb/backup/verification.rb +276 -279
  168. data/lib/rubydb/branching/branch.rb +181 -181
  169. data/lib/rubydb/branching/branch_manager.rb +307 -311
  170. data/lib/rubydb/branching/branch_metadata.rb +140 -140
  171. data/lib/rubydb/branching/checkout.rb +165 -166
  172. data/lib/rubydb/branching/copy_on_write.rb +272 -272
  173. data/lib/rubydb/branching/diff.rb +137 -138
  174. data/lib/rubydb/branching/merge.rb +282 -285
  175. data/lib/rubydb/build_info.rb +15 -15
  176. data/lib/rubydb/catalog/catalog.rb +391 -391
  177. data/lib/rubydb/catalog/column.rb +112 -112
  178. data/lib/rubydb/catalog/constraint.rb +180 -180
  179. data/lib/rubydb/catalog/database.rb +184 -184
  180. data/lib/rubydb/catalog/index.rb +97 -97
  181. data/lib/rubydb/catalog/schema.rb +103 -103
  182. data/lib/rubydb/catalog/sequence.rb +90 -90
  183. data/lib/rubydb/catalog/system_catalog.rb +698 -698
  184. data/lib/rubydb/catalog/table.rb +178 -178
  185. data/lib/rubydb/catalog/trigger.rb +102 -102
  186. data/lib/rubydb/catalog/view.rb +66 -66
  187. data/lib/rubydb/cli/application.rb +168 -163
  188. data/lib/rubydb/cli/commands/accelerator.rb +72 -0
  189. data/lib/rubydb/cli/commands/backup.rb +80 -81
  190. data/lib/rubydb/cli/commands/branch.rb +72 -72
  191. data/lib/rubydb/cli/commands/checkout.rb +54 -54
  192. data/lib/rubydb/cli/commands/create.rb +58 -58
  193. data/lib/rubydb/cli/commands/diff.rb +76 -77
  194. data/lib/rubydb/cli/commands/doctor.rb +77 -74
  195. data/lib/rubydb/cli/commands/drop.rb +57 -57
  196. data/lib/rubydb/cli/commands/init.rb +101 -102
  197. data/lib/rubydb/cli/commands/inspect.rb +95 -95
  198. data/lib/rubydb/cli/commands/merge.rb +63 -63
  199. data/lib/rubydb/cli/commands/migrate.rb +62 -62
  200. data/lib/rubydb/cli/commands/restart.rb +42 -39
  201. data/lib/rubydb/cli/commands/restore.rb +121 -121
  202. data/lib/rubydb/cli/commands/shell.rb +365 -365
  203. data/lib/rubydb/cli/commands/snapshot.rb +79 -79
  204. data/lib/rubydb/cli/commands/start.rb +88 -82
  205. data/lib/rubydb/cli/commands/status.rb +96 -92
  206. data/lib/rubydb/cli/commands/stop.rb +47 -47
  207. data/lib/rubydb/cli/commands/vacuum.rb +58 -58
  208. data/lib/rubydb/cli/formatter.rb +221 -221
  209. data/lib/rubydb/cli/output.rb +168 -168
  210. data/lib/rubydb/client/client.rb +309 -304
  211. data/lib/rubydb/client/connection.rb +429 -415
  212. data/lib/rubydb/client/connection_pool.rb +168 -168
  213. data/lib/rubydb/client/connection_url.rb +96 -96
  214. data/lib/rubydb/client/prepared_statement.rb +60 -60
  215. data/lib/rubydb/client/result.rb +127 -123
  216. data/lib/rubydb/client/statement.rb +52 -52
  217. data/lib/rubydb/client/transaction.rb +130 -130
  218. data/lib/rubydb/concurrency/concurrency.rb +19 -19
  219. data/lib/rubydb/concurrency/deadlock_detector.rb +148 -150
  220. data/lib/rubydb/concurrency/latch.rb +101 -101
  221. data/lib/rubydb/concurrency/lock_graph.rb +163 -165
  222. data/lib/rubydb/concurrency/mutex.rb +181 -183
  223. data/lib/rubydb/concurrency/rw_lock.rb +180 -180
  224. data/lib/rubydb/concurrency/scheduler.rb +248 -250
  225. data/lib/rubydb/concurrency/worker_pool.rb +145 -143
  226. data/lib/rubydb/configuration/config.rb +170 -170
  227. data/lib/rubydb/configuration/defaults.rb +191 -179
  228. data/lib/rubydb/configuration/environment.rb +152 -152
  229. data/lib/rubydb/configuration/parser.rb +185 -185
  230. data/lib/rubydb/configuration/validation.rb +228 -221
  231. data/lib/rubydb/constants.rb +74 -74
  232. data/lib/rubydb/constraints/check.rb +181 -181
  233. data/lib/rubydb/constraints/constraint.rb +101 -101
  234. data/lib/rubydb/constraints/foreign_key.rb +130 -130
  235. data/lib/rubydb/constraints/not_null.rb +64 -64
  236. data/lib/rubydb/constraints/primary_key.rb +99 -99
  237. data/lib/rubydb/constraints/unique.rb +106 -108
  238. data/lib/rubydb/constraints/validator.rb +349 -350
  239. data/lib/rubydb/errors/authentication_error.rb +10 -10
  240. data/lib/rubydb/errors/authorization_error.rb +23 -23
  241. data/lib/rubydb/errors/client_error.rb +10 -10
  242. data/lib/rubydb/errors/configuration_error.rb +10 -10
  243. data/lib/rubydb/errors/connection_error.rb +10 -10
  244. data/lib/rubydb/errors/constraint_error.rb +23 -23
  245. data/lib/rubydb/errors/corruption_error.rb +10 -10
  246. data/lib/rubydb/errors/database_error.rb +10 -10
  247. data/lib/rubydb/errors/error.rb +20 -20
  248. data/lib/rubydb/errors/execution_error.rb +10 -10
  249. data/lib/rubydb/errors/parser_error.rb +10 -10
  250. data/lib/rubydb/errors/recovery_error.rb +10 -10
  251. data/lib/rubydb/errors/replication_error.rb +10 -10
  252. data/lib/rubydb/errors/server_error.rb +6 -6
  253. data/lib/rubydb/errors/storage_error.rb +10 -10
  254. data/lib/rubydb/errors/transaction_error.rb +10 -10
  255. data/lib/rubydb/execution/accelerator_dispatch.rb +30 -0
  256. data/lib/rubydb/execution/aggregate_executor.rb +134 -138
  257. data/lib/rubydb/execution/cost_model.rb +72 -0
  258. data/lib/rubydb/execution/delete_executor.rb +110 -112
  259. data/lib/rubydb/execution/distinct_executor.rb +131 -135
  260. data/lib/rubydb/execution/executor.rb +1544 -1188
  261. data/lib/rubydb/execution/expression.rb +191 -193
  262. data/lib/rubydb/execution/index_scan.rb +142 -142
  263. data/lib/rubydb/execution/insert_executor.rb +215 -217
  264. data/lib/rubydb/execution/join_executor.rb +243 -249
  265. data/lib/rubydb/execution/limit_executor.rb +83 -85
  266. data/lib/rubydb/execution/operator_selection.rb +57 -0
  267. data/lib/rubydb/execution/optimizer.rb +227 -215
  268. data/lib/rubydb/execution/physical_plan.rb +47 -0
  269. data/lib/rubydb/execution/plan.rb +355 -353
  270. data/lib/rubydb/execution/planner.rb +508 -536
  271. data/lib/rubydb/execution/predicate.rb +235 -235
  272. data/lib/rubydb/execution/scan.rb +49 -49
  273. data/lib/rubydb/execution/sequential_scan.rb +63 -63
  274. data/lib/rubydb/execution/sort_executor.rb +194 -185
  275. data/lib/rubydb/execution/update_executor.rb +160 -162
  276. data/lib/rubydb/functions/aggregate.rb +70 -70
  277. data/lib/rubydb/functions/date_functions.rb +274 -278
  278. data/lib/rubydb/functions/function.rb +85 -85
  279. data/lib/rubydb/functions/json_functions.rb +231 -215
  280. data/lib/rubydb/functions/numeric_functions.rb +346 -346
  281. data/lib/rubydb/functions/scalar.rb +52 -52
  282. data/lib/rubydb/functions/string_functions.rb +383 -383
  283. data/lib/rubydb/functions/system_functions.rb +258 -246
  284. data/lib/rubydb/history/as_of.rb +238 -238
  285. data/lib/rubydb/history/change.rb +105 -105
  286. data/lib/rubydb/history/history.rb +131 -131
  287. data/lib/rubydb/history/history_manager.rb +228 -229
  288. data/lib/rubydb/history/temporal_query.rb +202 -202
  289. data/lib/rubydb/history/timeline.rb +144 -144
  290. data/lib/rubydb/indexes/btree.rb +215 -186
  291. data/lib/rubydb/indexes/btree_cursor.rb +258 -258
  292. data/lib/rubydb/indexes/btree_node.rb +384 -385
  293. data/lib/rubydb/indexes/hash_index.rb +150 -150
  294. data/lib/rubydb/indexes/index.rb +71 -71
  295. data/lib/rubydb/indexes/index_manager.rb +408 -406
  296. data/lib/rubydb/indexes/index_scan.rb +466 -470
  297. data/lib/rubydb/migrations/migration.rb +253 -254
  298. data/lib/rubydb/migrations/migration_lock.rb +146 -146
  299. data/lib/rubydb/migrations/migration_manager.rb +187 -176
  300. data/lib/rubydb/migrations/migration_version.rb +71 -71
  301. data/lib/rubydb/migrations/schema_diff.rb +211 -211
  302. data/lib/rubydb/migrations/schema_version.rb +64 -64
  303. data/lib/rubydb/monitoring/events.rb +155 -160
  304. data/lib/rubydb/monitoring/health.rb +216 -222
  305. data/lib/rubydb/monitoring/logger.rb +188 -193
  306. data/lib/rubydb/monitoring/metrics.rb +363 -359
  307. data/lib/rubydb/monitoring/performance.rb +176 -176
  308. data/lib/rubydb/monitoring/statistics.rb +168 -170
  309. data/lib/rubydb/mvcc/garbage_collector.rb +199 -199
  310. data/lib/rubydb/mvcc/mvcc.rb +16 -16
  311. data/lib/rubydb/mvcc/snapshot.rb +146 -147
  312. data/lib/rubydb/mvcc/vacuum.rb +180 -180
  313. data/lib/rubydb/mvcc/version.rb +106 -106
  314. data/lib/rubydb/mvcc/version_store.rb +396 -398
  315. data/lib/rubydb/mvcc/visibility.rb +107 -109
  316. data/lib/rubydb/protocol/capabilities.rb +125 -125
  317. data/lib/rubydb/protocol/decoder.rb +142 -145
  318. data/lib/rubydb/protocol/encoder.rb +131 -136
  319. data/lib/rubydb/protocol/handshake.rb +306 -305
  320. data/lib/rubydb/protocol/message.rb +121 -121
  321. data/lib/rubydb/protocol/parameter_binder.rb +101 -0
  322. data/lib/rubydb/protocol/protocol.rb +276 -277
  323. data/lib/rubydb/protocol/version.rb +54 -54
  324. data/lib/rubydb/rails/adapter.rb +245 -239
  325. data/lib/rubydb/rails/connection.rb +312 -314
  326. data/lib/rubydb/rails/database_statements.rb +122 -122
  327. data/lib/rubydb/rails/migration.rb +131 -131
  328. data/lib/rubydb/rails/quoting.rb +109 -109
  329. data/lib/rubydb/rails/result.rb +117 -117
  330. data/lib/rubydb/rails/schema_statements.rb +339 -339
  331. data/lib/rubydb/rails/transaction.rb +105 -105
  332. data/lib/rubydb/rails/type.rb +126 -126
  333. data/lib/rubydb/recovery/checkpoint.rb +261 -257
  334. data/lib/rubydb/recovery/consistency.rb +457 -467
  335. data/lib/rubydb/recovery/corruption_detector.rb +5 -5
  336. data/lib/rubydb/recovery/crash_recovery.rb +381 -387
  337. data/lib/rubydb/recovery/recovery_manager.rb +204 -206
  338. data/lib/rubydb/recovery/redo.rb +235 -237
  339. data/lib/rubydb/recovery/undo.rb +204 -206
  340. data/lib/rubydb/replication/failover.rb +5 -5
  341. data/lib/rubydb/replication/fencing.rb +63 -63
  342. data/lib/rubydb/replication/primary.rb +461 -450
  343. data/lib/rubydb/replication/replica.rb +382 -384
  344. data/lib/rubydb/replication/replication_log.rb +194 -200
  345. data/lib/rubydb/replication/replication_manager.rb +307 -308
  346. data/lib/rubydb/replication/replication_slot.rb +293 -295
  347. data/lib/rubydb/replication/replication_stream.rb +198 -201
  348. data/lib/rubydb/rubydb.rb +570 -560
  349. data/lib/rubydb/security/access_control.rb +252 -254
  350. data/lib/rubydb/security/audit_log.rb +209 -213
  351. data/lib/rubydb/security/authentication.rb +302 -302
  352. data/lib/rubydb/security/authorization.rb +282 -282
  353. data/lib/rubydb/security/credentials.rb +192 -196
  354. data/lib/rubydb/security/password.rb +205 -215
  355. data/lib/rubydb/security/permissions.rb +74 -74
  356. data/lib/rubydb/security/role.rb +99 -101
  357. data/lib/rubydb/security/user.rb +86 -86
  358. data/lib/rubydb/server/connection.rb +383 -366
  359. data/lib/rubydb/server/connection_pool.rb +193 -193
  360. data/lib/rubydb/server/lifecycle.rb +227 -228
  361. data/lib/rubydb/server/listener.rb +139 -136
  362. data/lib/rubydb/server/request_handler.rb +277 -276
  363. data/lib/rubydb/server/server.rb +363 -364
  364. data/lib/rubydb/server/session.rb +416 -369
  365. data/lib/rubydb/server/worker.rb +206 -210
  366. data/lib/rubydb/server/worker_pool.rb +168 -168
  367. data/lib/rubydb/sql/ast/alter_table.rb +169 -169
  368. data/lib/rubydb/sql/ast/begin_transaction.rb +47 -47
  369. data/lib/rubydb/sql/ast/commit.rb +37 -37
  370. data/lib/rubydb/sql/ast/constraint.rb +92 -83
  371. data/lib/rubydb/sql/ast/create_database.rb +41 -41
  372. data/lib/rubydb/sql/ast/create_index.rb +61 -61
  373. data/lib/rubydb/sql/ast/create_schema.rb +52 -52
  374. data/lib/rubydb/sql/ast/create_table.rb +187 -187
  375. data/lib/rubydb/sql/ast/delete.rb +54 -54
  376. data/lib/rubydb/sql/ast/drop_database.rb +41 -41
  377. data/lib/rubydb/sql/ast/drop_index.rb +41 -41
  378. data/lib/rubydb/sql/ast/drop_schema.rb +49 -49
  379. data/lib/rubydb/sql/ast/drop_table.rb +49 -49
  380. data/lib/rubydb/sql/ast/explain.rb +64 -64
  381. data/lib/rubydb/sql/ast/expression.rb +617 -604
  382. data/lib/rubydb/sql/ast/insert.rb +66 -66
  383. data/lib/rubydb/sql/ast/node.rb +42 -42
  384. data/lib/rubydb/sql/ast/rollback.rb +63 -63
  385. data/lib/rubydb/sql/ast/savepoint.rb +59 -59
  386. data/lib/rubydb/sql/ast/select.rb +88 -88
  387. data/lib/rubydb/sql/ast/set_operation.rb +22 -20
  388. data/lib/rubydb/sql/ast/trigger.rb +35 -29
  389. data/lib/rubydb/sql/ast/update.rb +88 -88
  390. data/lib/rubydb/sql/ast/vacuum.rb +19 -19
  391. data/lib/rubydb/sql/ast/view.rb +38 -32
  392. data/lib/rubydb/sql/ast/with.rb +32 -32
  393. data/lib/rubydb/sql/grammar.rb +86 -86
  394. data/lib/rubydb/sql/keywords.rb +156 -156
  395. data/lib/rubydb/sql/lexer.rb +209 -214
  396. data/lib/rubydb/sql/operators.rb +100 -100
  397. data/lib/rubydb/sql/parser.rb +1167 -1170
  398. data/lib/rubydb/sql/planner/analyzer.rb +283 -302
  399. data/lib/rubydb/sql/planner/binder.rb +537 -550
  400. data/lib/rubydb/sql/planner/type_checker.rb +427 -431
  401. data/lib/rubydb/sql/token.rb +210 -210
  402. data/lib/rubydb/storage/buffer_frame.rb +44 -44
  403. data/lib/rubydb/storage/buffer_pool.rb +155 -155
  404. data/lib/rubydb/storage/database_lock.rb +74 -74
  405. data/lib/rubydb/storage/deserializer.rb +332 -342
  406. data/lib/rubydb/storage/engine.rb +2409 -2330
  407. data/lib/rubydb/storage/file_manager.rb +191 -187
  408. data/lib/rubydb/storage/free_space_map.rb +79 -81
  409. data/lib/rubydb/storage/page.rb +92 -94
  410. data/lib/rubydb/storage/page_allocator.rb +852 -855
  411. data/lib/rubydb/storage/page_header.rb +63 -67
  412. data/lib/rubydb/storage/page_manager.rb +127 -131
  413. data/lib/rubydb/storage/record.rb +58 -58
  414. data/lib/rubydb/storage/row.rb +78 -78
  415. data/lib/rubydb/storage/serializer.rb +51 -51
  416. data/lib/rubydb/storage/snapshot_reader.rb +167 -0
  417. data/lib/rubydb/storage/storage_layout.rb +151 -151
  418. data/lib/rubydb/storage/storage_manager.rb +114 -114
  419. data/lib/rubydb/storage/tuple.rb +458 -461
  420. data/lib/rubydb/storage/visibility_map.rb +964 -973
  421. data/lib/rubydb/transactions/commit_manager.rb +219 -220
  422. data/lib/rubydb/transactions/isolation.rb +98 -98
  423. data/lib/rubydb/transactions/lock.rb +76 -76
  424. data/lib/rubydb/transactions/lock_manager.rb +359 -362
  425. data/lib/rubydb/transactions/savepoint.rb +142 -143
  426. data/lib/rubydb/transactions/transaction.rb +214 -215
  427. data/lib/rubydb/transactions/transaction_id.rb +84 -84
  428. data/lib/rubydb/transactions/transaction_log.rb +256 -257
  429. data/lib/rubydb/transactions/transaction_manager.rb +434 -435
  430. data/lib/rubydb/types/bigint.rb +36 -36
  431. data/lib/rubydb/types/blob.rb +37 -37
  432. data/lib/rubydb/types/boolean.rb +34 -34
  433. data/lib/rubydb/types/date.rb +39 -39
  434. data/lib/rubydb/types/decimal.rb +48 -48
  435. data/lib/rubydb/types/float.rb +34 -34
  436. data/lib/rubydb/types/integer.rb +36 -36
  437. data/lib/rubydb/types/json.rb +41 -41
  438. data/lib/rubydb/types/null.rb +34 -34
  439. data/lib/rubydb/types/smallint.rb +36 -36
  440. data/lib/rubydb/types/text.rb +37 -37
  441. data/lib/rubydb/types/time.rb +46 -46
  442. data/lib/rubydb/types/timestamp.rb +39 -39
  443. data/lib/rubydb/types/type.rb +119 -119
  444. data/lib/rubydb/types/uuid.rb +47 -47
  445. data/lib/rubydb/types/varchar.rb +37 -37
  446. data/lib/rubydb/version.rb +32 -32
  447. data/lib/rubydb/wal/archive.rb +207 -193
  448. data/lib/rubydb/wal/checkpoint.rb +181 -183
  449. data/lib/rubydb/wal/lsn.rb +94 -94
  450. data/lib/rubydb/wal/reader.rb +259 -260
  451. data/lib/rubydb/wal/record.rb +105 -105
  452. data/lib/rubydb/wal/segment.rb +193 -193
  453. data/lib/rubydb/wal/wal.rb +481 -452
  454. data/lib/rubydb/wal/writer.rb +236 -236
  455. data/lib/rubydb.rb +7 -7
  456. data/packaging/docker/docker-compose.failover.yml +43 -43
  457. data/packaging/homebrew/rubydb.rb +19 -19
  458. data/rubydb.gemspec +70 -57
  459. data/scripts/benchmark +7 -7
  460. data/scripts/build_accelerator +49 -0
  461. data/scripts/durability_drill +37 -37
  462. data/scripts/fuzz +63 -63
  463. data/scripts/release +77 -42
  464. data/scripts/release_check +43 -43
  465. data/scripts/replication_failover_drill +268 -250
  466. data/scripts/replication_network_failover_drill +287 -255
  467. data/scripts/restore_drill +45 -45
  468. data/scripts/security +45 -0
  469. metadata +102 -1
@@ -1,147 +1,146 @@
1
- # frozen_string_literal: true
2
-
3
- require "set"
4
- require "time"
5
-
6
- module RubyDB
7
- module MVCC
8
- # Snapshot - Represents a consistent view of the database at a point in time
9
- class Snapshot
10
- attr_reader :id, :transaction_id, :created_at
11
- attr_reader :active_transactions, :committed_transactions
12
- attr_reader :min_transaction_id, :max_transaction_id
13
- attr_reader :read_keys, :write_keys, :read_predicates
14
-
15
- def initialize(transaction_id, active_transactions = [], committed_transactions = [])
16
- @id = generate_snapshot_id(transaction_id)
17
- @transaction_id = transaction_id
18
- @created_at = Time.now
19
- @active_transactions = Set.new(active_transactions)
20
- @committed_transactions = Set.new(committed_transactions)
21
- @min_transaction_id = active_transactions.min || 0
22
- @max_transaction_id = active_transactions.max || 0
23
- @version_cache = {}
24
- @read_keys = Set.new
25
- @write_keys = Set.new
26
- @read_predicates = Set.new
27
- @cache_size = 1000
28
- @lock = Mutex.new
29
- end
30
-
31
- def record_read(key)
32
- @lock.synchronize { @read_keys.add(key) }
33
- end
34
-
35
- def record_write(key)
36
- @lock.synchronize { @write_keys.add(key) }
37
- end
38
-
39
- def record_predicate(predicate)
40
- @lock.synchronize { @read_predicates.add(predicate) }
41
- end
42
-
43
- def generate_snapshot_id(transaction_id)
44
- "snapshot_#{transaction_id}_#{Time.now.to_i}_#{rand(10000)}"
45
- end
46
-
47
- def visible?(version, check_transaction_id = nil)
48
- @lock.synchronize do
49
- # Check cache
50
- cache_key = version.version_id
51
- if @version_cache.key?(cache_key)
52
- return @version_cache[cache_key]
53
- end
54
-
55
- result = check_visibility(version, check_transaction_id)
56
-
57
- # Cache result
58
- if @version_cache.size < @cache_size
59
- @version_cache[cache_key] = result
60
- end
61
-
62
- result
63
- end
64
- end
65
-
66
- def check_visibility(version, check_transaction_id = nil)
67
- txn_id = check_transaction_id || @transaction_id
68
-
69
- # Same transaction sees its own versions
70
- return true if version.transaction_id == txn_id
71
-
72
- # Check if version is from an active transaction
73
- if @active_transactions.include?(version.transaction_id)
74
- # Active transactions are not visible (unless it's the current transaction)
75
- return false
76
- end
77
-
78
- # Check if version is from a committed transaction
79
- if version.is_committed && @committed_transactions.include?(version.transaction_id)
80
- # Check if the commit happened after snapshot was taken
81
- if version.committed_at && version.committed_at > @created_at
82
- return false
83
- end
84
- return true
85
- end
86
-
87
- # If transaction is not in our lists, it committed before snapshot
88
- if version.is_committed
89
- # Check if it was committed before snapshot
90
- if version.commit_id && version.commit_id < @min_transaction_id
91
- return true
92
- end
93
- end
94
-
95
- # Default: not visible
96
- false
97
- end
98
-
99
- def add_active_transaction(transaction_id)
100
- @lock.synchronize do
101
- @active_transactions.add(transaction_id)
102
- @min_transaction_id = [@min_transaction_id, transaction_id].min
103
- @max_transaction_id = [@max_transaction_id, transaction_id].max
104
- end
105
- end
106
-
107
- def add_committed_transaction(transaction_id)
108
- @lock.synchronize do
109
- @committed_transactions.add(transaction_id)
110
- @active_transactions.delete(transaction_id)
111
- end
112
- end
113
-
114
- def remove_transaction(transaction_id)
115
- @lock.synchronize do
116
- @active_transactions.delete(transaction_id)
117
- @committed_transactions.delete(transaction_id)
118
- end
119
- end
120
-
121
- def includes_transaction?(transaction_id)
122
- @active_transactions.include?(transaction_id) ||
123
- @committed_transactions.include?(transaction_id)
124
- end
125
-
126
- def to_hash
127
- {
128
- id: @id,
129
- transaction_id: @transaction_id,
130
- created_at: @created_at.iso8601,
131
- active_transactions: @active_transactions.to_a,
132
- committed_transactions: @committed_transactions.to_a,
133
- min_transaction_id: @min_transaction_id,
134
- max_transaction_id: @max_transaction_id
135
- }
136
- end
137
-
138
- def inspect
139
- "#<Snapshot id=#{@id} txn=#{@transaction_id} active=#{@active_transactions.size} committed=#{@committed_transactions.size}>"
140
- end
141
-
142
- def to_s
143
- inspect
144
- end
145
- end
146
- end
147
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module RubyDB
6
+ module MVCC
7
+ # Snapshot - Represents a consistent view of the database at a point in time
8
+ class Snapshot
9
+ attr_reader :id, :transaction_id, :created_at
10
+ attr_reader :active_transactions, :committed_transactions
11
+ attr_reader :min_transaction_id, :max_transaction_id
12
+ attr_reader :read_keys, :write_keys, :read_predicates
13
+
14
+ def initialize(transaction_id, active_transactions = [], committed_transactions = [])
15
+ @id = generate_snapshot_id(transaction_id)
16
+ @transaction_id = transaction_id
17
+ @created_at = Time.now
18
+ @active_transactions = Set.new(active_transactions)
19
+ @committed_transactions = Set.new(committed_transactions)
20
+ @min_transaction_id = active_transactions.min || 0
21
+ @max_transaction_id = active_transactions.max || 0
22
+ @version_cache = {}
23
+ @read_keys = Set.new
24
+ @write_keys = Set.new
25
+ @read_predicates = Set.new
26
+ @cache_size = 1000
27
+ @lock = Mutex.new
28
+ end
29
+
30
+ def record_read(key)
31
+ @lock.synchronize { @read_keys.add(key) }
32
+ end
33
+
34
+ def record_write(key)
35
+ @lock.synchronize { @write_keys.add(key) }
36
+ end
37
+
38
+ def record_predicate(predicate)
39
+ @lock.synchronize { @read_predicates.add(predicate) }
40
+ end
41
+
42
+ def generate_snapshot_id(transaction_id)
43
+ "snapshot_#{transaction_id}_#{Time.now.to_i}_#{rand(10000)}"
44
+ end
45
+
46
+ def visible?(version, check_transaction_id = nil)
47
+ @lock.synchronize do
48
+ # Check cache
49
+ cache_key = version.version_id
50
+ if @version_cache.key?(cache_key)
51
+ return @version_cache[cache_key]
52
+ end
53
+
54
+ result = check_visibility(version, check_transaction_id)
55
+
56
+ # Cache result
57
+ if @version_cache.size < @cache_size
58
+ @version_cache[cache_key] = result
59
+ end
60
+
61
+ result
62
+ end
63
+ end
64
+
65
+ def check_visibility(version, check_transaction_id = nil)
66
+ txn_id = check_transaction_id || @transaction_id
67
+
68
+ # Same transaction sees its own versions
69
+ return true if version.transaction_id == txn_id
70
+
71
+ # Check if version is from an active transaction
72
+ if @active_transactions.include?(version.transaction_id)
73
+ # Active transactions are not visible (unless it's the current transaction)
74
+ return false
75
+ end
76
+
77
+ # Check if version is from a committed transaction
78
+ if version.is_committed && @committed_transactions.include?(version.transaction_id)
79
+ # Check if the commit happened after snapshot was taken
80
+ if version.committed_at && version.committed_at > @created_at
81
+ return false
82
+ end
83
+ return true
84
+ end
85
+
86
+ # If transaction is not in our lists, it committed before snapshot
87
+ if version.is_committed
88
+ # Check if it was committed before snapshot
89
+ if version.commit_id && version.commit_id < @min_transaction_id
90
+ return true
91
+ end
92
+ end
93
+
94
+ # Default: not visible
95
+ false
96
+ end
97
+
98
+ def add_active_transaction(transaction_id)
99
+ @lock.synchronize do
100
+ @active_transactions.add(transaction_id)
101
+ @min_transaction_id = [@min_transaction_id, transaction_id].min
102
+ @max_transaction_id = [@max_transaction_id, transaction_id].max
103
+ end
104
+ end
105
+
106
+ def add_committed_transaction(transaction_id)
107
+ @lock.synchronize do
108
+ @committed_transactions.add(transaction_id)
109
+ @active_transactions.delete(transaction_id)
110
+ end
111
+ end
112
+
113
+ def remove_transaction(transaction_id)
114
+ @lock.synchronize do
115
+ @active_transactions.delete(transaction_id)
116
+ @committed_transactions.delete(transaction_id)
117
+ end
118
+ end
119
+
120
+ def includes_transaction?(transaction_id)
121
+ @active_transactions.include?(transaction_id) ||
122
+ @committed_transactions.include?(transaction_id)
123
+ end
124
+
125
+ def to_hash
126
+ {
127
+ id: @id,
128
+ transaction_id: @transaction_id,
129
+ created_at: @created_at.iso8601,
130
+ active_transactions: @active_transactions.to_a,
131
+ committed_transactions: @committed_transactions.to_a,
132
+ min_transaction_id: @min_transaction_id,
133
+ max_transaction_id: @max_transaction_id
134
+ }
135
+ end
136
+
137
+ def inspect
138
+ "#<Snapshot id=#{@id} txn=#{@transaction_id} active=#{@active_transactions.size} committed=#{@committed_transactions.size}>"
139
+ end
140
+
141
+ def to_s
142
+ inspect
143
+ end
144
+ end
145
+ end
146
+ end
@@ -1,180 +1,180 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyDB
4
- module MVCC
5
- # Vacuum - Manages cleanup of old versions and reclaims space
6
- class Vacuum
7
- attr_reader :stats
8
-
9
- def initialize(version_store, config = {})
10
- @version_store = version_store
11
- @config = config
12
- @vacuum_interval = config[:interval] || 60 # seconds
13
- @max_versions_per_row = config[:max_versions] || 100
14
- @min_age = config[:min_age] || 3600 # 1 hour
15
- @batch_size = config[:batch_size] || 1000
16
- @threshold = config[:threshold] || 0.3 # 30% of max versions
17
- @stats = {
18
- vacuum_runs: 0,
19
- versions_removed: 0,
20
- rows_compacted: 0,
21
- space_reclaimed: 0,
22
- last_vacuum: nil,
23
- average_removed_per_run: 0
24
- }
25
- @lock = Mutex.new
26
- @running = false
27
- @vacuum_thread = nil
28
- end
29
-
30
- def start
31
- @lock.synchronize do
32
- return if @running
33
-
34
- @running = true
35
- @vacuum_thread = Thread.new do
36
- while @running
37
- sleep(@vacuum_interval)
38
- begin
39
- run
40
- rescue => e
41
- # Log error but continue
42
- puts "Vacuum error: #{e.message}"
43
- end
44
- end
45
- end
46
- end
47
- end
48
-
49
- def stop
50
- @lock.synchronize do
51
- @running = false
52
- @vacuum_thread&.kill
53
- @vacuum_thread = nil
54
- end
55
- end
56
-
57
- def run
58
- @lock.synchronize do
59
- @stats[:vacuum_runs] += 1
60
- start_time = Time.now
61
-
62
- # Remove old versions
63
- removed = vacuum_versions
64
-
65
- # Compact version chains
66
- compacted = compact_version_chains
67
-
68
- # Update stats
69
- elapsed_ms = ((Time.now - start_time) * 1000).round(2)
70
- @stats[:last_vacuum] = {
71
- time: Time.now.iso8601,
72
- elapsed_ms: elapsed_ms,
73
- removed: removed,
74
- compacted: compacted
75
- }
76
-
77
- @stats[:versions_removed] += removed
78
- @stats[:rows_compacted] += compacted
79
- @stats[:average_removed_per_run] = @stats[:versions_removed] / @stats[:vacuum_runs].to_f
80
-
81
- { removed: removed, compacted: compacted }
82
- end
83
- end
84
-
85
- def vacuum_versions
86
- removed = 0
87
- batch_removed = 0
88
-
89
- # Get rows with excessive versions
90
- rows = @version_store.instance_variable_get(:@versions)
91
- candidates = rows.select do |_, versions|
92
- versions.size > @max_versions_per_row * @threshold
93
- end
94
-
95
- candidates.each do |row_id, versions|
96
- # Calculate how many to remove
97
- remove_count = versions.size - @max_versions_per_row
98
- next if remove_count <= 0
99
-
100
- # Remove oldest versions
101
- remove_count.times do
102
- version = versions.shift
103
- if version && version.is_committed && !version.is_deleted
104
- # Check age
105
- if Time.now - version.created_at > @min_age
106
- @version_store.instance_variable_get(:@version_map).delete(version.version_id)
107
- removed += 1
108
- batch_removed += 1
109
- end
110
- end
111
- break if batch_removed >= @batch_size
112
- end
113
-
114
- break if batch_removed >= @batch_size
115
- end
116
-
117
- removed
118
- end
119
-
120
- def compact_version_chains
121
- compacted = 0
122
- rows = @version_store.instance_variable_get(:@versions)
123
-
124
- rows.each do |row_id, versions|
125
- next if versions.size <= 1
126
-
127
- # Check if there are gaps or holes in the chain
128
- # Remove versions with no data (deleted, aborted)
129
- original_size = versions.size
130
- versions.delete_if do |version|
131
- version.is_deleted || version.is_aborted
132
- end
133
-
134
- if versions.size < original_size
135
- compacted += 1
136
- end
137
-
138
- # Update next_version_id links
139
- versions.each_with_index do |version, idx|
140
- if idx < versions.size - 1
141
- version.instance_variable_set(:@next_version_id, versions[idx + 1].version_id)
142
- else
143
- version.instance_variable_set(:@next_version_id, nil)
144
- end
145
- end
146
- end
147
-
148
- compacted
149
- end
150
-
151
- def force_run
152
- run
153
- end
154
-
155
- def stats
156
- @lock.synchronize do
157
- @stats.merge({
158
- running: @running,
159
- interval: @vacuum_interval,
160
- threshold: @threshold,
161
- max_versions: @max_versions_per_row,
162
- min_age: @min_age
163
- })
164
- end
165
- end
166
-
167
- def status
168
- @lock.synchronize do
169
- {
170
- running: @running,
171
- last_vacuum: @stats[:last_vacuum],
172
- total_removed: @stats[:versions_removed],
173
- total_compacted: @stats[:rows_compacted],
174
- average_removed: @stats[:average_removed_per_run]
175
- }
176
- end
177
- end
178
- end
179
- end
180
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module MVCC
5
+ # Vacuum - Manages cleanup of old versions and reclaims space
6
+ class Vacuum
7
+ attr_reader :stats
8
+
9
+ def initialize(version_store, config = {})
10
+ @version_store = version_store
11
+ @config = config
12
+ @vacuum_interval = config[:interval] || 60 # seconds
13
+ @max_versions_per_row = config[:max_versions] || 100
14
+ @min_age = config[:min_age] || 3600 # 1 hour
15
+ @batch_size = config[:batch_size] || 1000
16
+ @threshold = config[:threshold] || 0.3 # 30% of max versions
17
+ @stats = {
18
+ vacuum_runs: 0,
19
+ versions_removed: 0,
20
+ rows_compacted: 0,
21
+ space_reclaimed: 0,
22
+ last_vacuum: nil,
23
+ average_removed_per_run: 0
24
+ }
25
+ @lock = Mutex.new
26
+ @running = false
27
+ @vacuum_thread = nil
28
+ end
29
+
30
+ def start
31
+ @lock.synchronize do
32
+ return if @running
33
+
34
+ @running = true
35
+ @vacuum_thread = Thread.new do
36
+ while @running
37
+ sleep(@vacuum_interval)
38
+ begin
39
+ run
40
+ rescue => e
41
+ # Log error but continue
42
+ puts "Vacuum error: #{e.message}"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def stop
50
+ @lock.synchronize do
51
+ @running = false
52
+ @vacuum_thread&.kill
53
+ @vacuum_thread = nil
54
+ end
55
+ end
56
+
57
+ def run
58
+ @lock.synchronize do
59
+ @stats[:vacuum_runs] += 1
60
+ start_time = Time.now
61
+
62
+ # Remove old versions
63
+ removed = vacuum_versions
64
+
65
+ # Compact version chains
66
+ compacted = compact_version_chains
67
+
68
+ # Update stats
69
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
70
+ @stats[:last_vacuum] = {
71
+ time: Time.now.iso8601,
72
+ elapsed_ms: elapsed_ms,
73
+ removed: removed,
74
+ compacted: compacted
75
+ }
76
+
77
+ @stats[:versions_removed] += removed
78
+ @stats[:rows_compacted] += compacted
79
+ @stats[:average_removed_per_run] = @stats[:versions_removed] / @stats[:vacuum_runs].to_f
80
+
81
+ {removed: removed, compacted: compacted}
82
+ end
83
+ end
84
+
85
+ def vacuum_versions
86
+ removed = 0
87
+ batch_removed = 0
88
+
89
+ # Get rows with excessive versions
90
+ rows = @version_store.instance_variable_get(:@versions)
91
+ candidates = rows.select do |_, versions|
92
+ versions.size > @max_versions_per_row * @threshold
93
+ end
94
+
95
+ candidates.each do |row_id, versions|
96
+ # Calculate how many to remove
97
+ remove_count = versions.size - @max_versions_per_row
98
+ next if remove_count <= 0
99
+
100
+ # Remove oldest versions
101
+ remove_count.times do
102
+ version = versions.shift
103
+ if version&.is_committed && !version.is_deleted
104
+ # Check age
105
+ if Time.now - version.created_at > @min_age
106
+ @version_store.instance_variable_get(:@version_map).delete(version.version_id)
107
+ removed += 1
108
+ batch_removed += 1
109
+ end
110
+ end
111
+ break if batch_removed >= @batch_size
112
+ end
113
+
114
+ break if batch_removed >= @batch_size
115
+ end
116
+
117
+ removed
118
+ end
119
+
120
+ def compact_version_chains
121
+ compacted = 0
122
+ rows = @version_store.instance_variable_get(:@versions)
123
+
124
+ rows.each do |row_id, versions|
125
+ next if versions.size <= 1
126
+
127
+ # Check if there are gaps or holes in the chain
128
+ # Remove versions with no data (deleted, aborted)
129
+ original_size = versions.size
130
+ versions.delete_if do |version|
131
+ version.is_deleted || version.is_aborted
132
+ end
133
+
134
+ if versions.size < original_size
135
+ compacted += 1
136
+ end
137
+
138
+ # Update next_version_id links
139
+ versions.each_with_index do |version, idx|
140
+ if idx < versions.size - 1
141
+ version.instance_variable_set(:@next_version_id, versions[idx + 1].version_id)
142
+ else
143
+ version.instance_variable_set(:@next_version_id, nil)
144
+ end
145
+ end
146
+ end
147
+
148
+ compacted
149
+ end
150
+
151
+ def force_run
152
+ run
153
+ end
154
+
155
+ def stats
156
+ @lock.synchronize do
157
+ @stats.merge({
158
+ running: @running,
159
+ interval: @vacuum_interval,
160
+ threshold: @threshold,
161
+ max_versions: @max_versions_per_row,
162
+ min_age: @min_age
163
+ })
164
+ end
165
+ end
166
+
167
+ def status
168
+ @lock.synchronize do
169
+ {
170
+ running: @running,
171
+ last_vacuum: @stats[:last_vacuum],
172
+ total_removed: @stats[:versions_removed],
173
+ total_compacted: @stats[:rows_compacted],
174
+ average_removed: @stats[:average_removed_per_run]
175
+ }
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end