rubydb 0.1.0

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 (436) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +20 -0
  3. data/.github/ISSUE_TEMPLATE/bug.yml +0 -0
  4. data/.github/ISSUE_TEMPLATE/feature.yml +0 -0
  5. data/.github/ISSUE_TEMPLATE/performance.yml +0 -0
  6. data/.github/ISSUE_TEMPLATE/security.yml +0 -0
  7. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  8. data/.github/dependabot.yml +0 -0
  9. data/.github/workflows/benchmark.yml +26 -0
  10. data/.github/workflows/compatibility.yml +63 -0
  11. data/.github/workflows/fuzz.yml +33 -0
  12. data/.github/workflows/lint.yml +21 -0
  13. data/.github/workflows/operations.yml +24 -0
  14. data/.github/workflows/production-validation.yml +111 -0
  15. data/.github/workflows/release.yml +77 -0
  16. data/.github/workflows/security.yml +37 -0
  17. data/.github/workflows/test.yml +26 -0
  18. data/.github/workflows/workload.yml +58 -0
  19. data/.gitignore +43 -0
  20. data/.rspec +4 -0
  21. data/.rubocop.yml +44 -0
  22. data/.ruby-version +1 -0
  23. data/.standard.yml +14 -0
  24. data/ARCHITECTURE.md +21 -0
  25. data/CHANGELOG.md +31 -0
  26. data/CODE_OF_CONDUCT.md +13 -0
  27. data/CONTRIBUTING.md +29 -0
  28. data/GOVERNANCE.md +16 -0
  29. data/Gemfile +17 -0
  30. data/Gemfile.lock +71 -0
  31. data/LICENSE +60 -0
  32. data/README.md +162 -0
  33. data/ROADMAP.md +27 -0
  34. data/Rakefile +71 -0
  35. data/SECURITY.md +54 -0
  36. data/SUPPORT.md +14 -0
  37. data/adapters/activerecord/Gemfile +11 -0
  38. data/adapters/activerecord/README.md +9 -0
  39. data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +887 -0
  40. data/adapters/activerecord/rubydb-activerecord.gemspec +27 -0
  41. data/adapters/activerecord/spec/rubydb_adapter_integration_spec.rb +151 -0
  42. data/adapters/ruby/README.md +18 -0
  43. data/adapters/sequel/README.md +11 -0
  44. data/bin/rubydb +19 -0
  45. data/bin/rubydb-server +126 -0
  46. data/config/development.yml +0 -0
  47. data/config/monitoring/prometheus-alerts.yml +39 -0
  48. data/config/production.yml +36 -0
  49. data/config/test.yml +0 -0
  50. data/docs/README.md +71 -0
  51. data/docs/architecture/concurrency.md +14 -0
  52. data/docs/architecture/current-state.md +125 -0
  53. data/docs/architecture/execution-engine.md +25 -0
  54. data/docs/architecture/indexes.md +19 -0
  55. data/docs/architecture/mvcc.md +19 -0
  56. data/docs/architecture/overview.md +13 -0
  57. data/docs/architecture/pages.md +11 -0
  58. data/docs/architecture/production-roadmap.md +82 -0
  59. data/docs/architecture/query-planner.md +20 -0
  60. data/docs/architecture/recovery.md +18 -0
  61. data/docs/architecture/sql-engine.md +12 -0
  62. data/docs/architecture/storage-engine.md +14 -0
  63. data/docs/architecture/transactions.md +10 -0
  64. data/docs/architecture/wal.md +28 -0
  65. data/docs/cli-cheatsheet.md +98 -0
  66. data/docs/cli.md +275 -0
  67. data/docs/contributing/architecture.md +9 -0
  68. data/docs/contributing/benchmarking.md +14 -0
  69. data/docs/contributing/development.md +16 -0
  70. data/docs/contributing/release-process.md +49 -0
  71. data/docs/contributing/testing.md +16 -0
  72. data/docs/debugging.md +229 -0
  73. data/docs/developer/branching.md +10 -0
  74. data/docs/developer/database-diff.md +10 -0
  75. data/docs/developer/local-development.md +17 -0
  76. data/docs/developer/snapshots.md +9 -0
  77. data/docs/developer/temporal-data.md +10 -0
  78. data/docs/developer-guide.md +297 -0
  79. data/docs/getting-started/first-database.md +16 -0
  80. data/docs/getting-started/first-query.md +13 -0
  81. data/docs/getting-started/installation.md +19 -0
  82. data/docs/getting-started/local-to-production.md +300 -0
  83. data/docs/getting-started/quickstart.md +17 -0
  84. data/docs/getting-started/rails.md +16 -0
  85. data/docs/hardening_backlog.md +93 -0
  86. data/docs/lessons-learned.md +112 -0
  87. data/docs/operations/backups.md +33 -0
  88. data/docs/operations/disaster-recovery.md +31 -0
  89. data/docs/operations/failover.md +30 -0
  90. data/docs/operations/monitoring.md +25 -0
  91. data/docs/operations/production-guide.md +295 -0
  92. data/docs/operations/production-runbook.md +45 -0
  93. data/docs/operations/replication.md +33 -0
  94. data/docs/operations/restore.md +6 -0
  95. data/docs/operations/runbook.md +34 -0
  96. data/docs/operations/upgrades.md +14 -0
  97. data/docs/operations/workload-testing.md +17 -0
  98. data/docs/production-readiness.md +118 -0
  99. data/docs/production_validation.md +150 -0
  100. data/docs/rails/active-record.md +11 -0
  101. data/docs/rails/compatibility-guide.md +90 -0
  102. data/docs/rails/database-yml.md +92 -0
  103. data/docs/rails/installation.md +17 -0
  104. data/docs/rails/migrations.md +17 -0
  105. data/docs/rails/production.md +82 -0
  106. data/docs/rails/troubleshooting.md +18 -0
  107. data/docs/release.md +59 -0
  108. data/docs/server/architecture.md +10 -0
  109. data/docs/server/authentication.md +10 -0
  110. data/docs/server/configuration.md +16 -0
  111. data/docs/server/connection-pooling.md +10 -0
  112. data/docs/server/deployment.md +10 -0
  113. data/docs/server/protocol.md +12 -0
  114. data/docs/sql/compatibility-guide.md +82 -0
  115. data/docs/sql/compatibility.md +39 -0
  116. data/docs/sql/data-types.md +10 -0
  117. data/docs/sql/functions.md +9 -0
  118. data/docs/sql/joins.md +9 -0
  119. data/docs/sql/operators.md +9 -0
  120. data/docs/sql/sqlite-compatibility.md +21 -0
  121. data/docs/sql/syntax.md +10 -0
  122. data/docs/sql/transactions.md +10 -0
  123. data/docs/troubleshooting.md +244 -0
  124. data/exe/rubydb +23 -0
  125. data/lib/rubydb/backup/archive.rb +334 -0
  126. data/lib/rubydb/backup/backup.rb +401 -0
  127. data/lib/rubydb/backup/incremental.rb +353 -0
  128. data/lib/rubydb/backup/restore.rb +290 -0
  129. data/lib/rubydb/backup/snapshot.rb +267 -0
  130. data/lib/rubydb/backup/verification.rb +279 -0
  131. data/lib/rubydb/branching/branch.rb +181 -0
  132. data/lib/rubydb/branching/branch_manager.rb +311 -0
  133. data/lib/rubydb/branching/branch_metadata.rb +140 -0
  134. data/lib/rubydb/branching/checkout.rb +166 -0
  135. data/lib/rubydb/branching/copy_on_write.rb +272 -0
  136. data/lib/rubydb/branching/diff.rb +138 -0
  137. data/lib/rubydb/branching/merge.rb +285 -0
  138. data/lib/rubydb/build_info.rb +15 -0
  139. data/lib/rubydb/catalog/catalog.rb +391 -0
  140. data/lib/rubydb/catalog/column.rb +112 -0
  141. data/lib/rubydb/catalog/constraint.rb +180 -0
  142. data/lib/rubydb/catalog/database.rb +184 -0
  143. data/lib/rubydb/catalog/index.rb +97 -0
  144. data/lib/rubydb/catalog/schema.rb +103 -0
  145. data/lib/rubydb/catalog/sequence.rb +90 -0
  146. data/lib/rubydb/catalog/system_catalog.rb +698 -0
  147. data/lib/rubydb/catalog/table.rb +178 -0
  148. data/lib/rubydb/catalog/trigger.rb +102 -0
  149. data/lib/rubydb/catalog/view.rb +66 -0
  150. data/lib/rubydb/cli/application.rb +163 -0
  151. data/lib/rubydb/cli/commands/backup.rb +81 -0
  152. data/lib/rubydb/cli/commands/branch.rb +72 -0
  153. data/lib/rubydb/cli/commands/checkout.rb +54 -0
  154. data/lib/rubydb/cli/commands/create.rb +58 -0
  155. data/lib/rubydb/cli/commands/diff.rb +77 -0
  156. data/lib/rubydb/cli/commands/doctor.rb +74 -0
  157. data/lib/rubydb/cli/commands/drop.rb +57 -0
  158. data/lib/rubydb/cli/commands/init.rb +102 -0
  159. data/lib/rubydb/cli/commands/inspect.rb +95 -0
  160. data/lib/rubydb/cli/commands/merge.rb +63 -0
  161. data/lib/rubydb/cli/commands/migrate.rb +62 -0
  162. data/lib/rubydb/cli/commands/restart.rb +39 -0
  163. data/lib/rubydb/cli/commands/restore.rb +121 -0
  164. data/lib/rubydb/cli/commands/shell.rb +365 -0
  165. data/lib/rubydb/cli/commands/snapshot.rb +79 -0
  166. data/lib/rubydb/cli/commands/start.rb +82 -0
  167. data/lib/rubydb/cli/commands/status.rb +92 -0
  168. data/lib/rubydb/cli/commands/stop.rb +47 -0
  169. data/lib/rubydb/cli/commands/vacuum.rb +58 -0
  170. data/lib/rubydb/cli/formatter.rb +221 -0
  171. data/lib/rubydb/cli/output.rb +168 -0
  172. data/lib/rubydb/client/client.rb +304 -0
  173. data/lib/rubydb/client/connection.rb +415 -0
  174. data/lib/rubydb/client/connection_pool.rb +168 -0
  175. data/lib/rubydb/client/connection_url.rb +96 -0
  176. data/lib/rubydb/client/prepared_statement.rb +60 -0
  177. data/lib/rubydb/client/result.rb +123 -0
  178. data/lib/rubydb/client/statement.rb +52 -0
  179. data/lib/rubydb/client/transaction.rb +130 -0
  180. data/lib/rubydb/concurrency/concurrency.rb +19 -0
  181. data/lib/rubydb/concurrency/deadlock_detector.rb +150 -0
  182. data/lib/rubydb/concurrency/latch.rb +101 -0
  183. data/lib/rubydb/concurrency/lock_graph.rb +165 -0
  184. data/lib/rubydb/concurrency/mutex.rb +183 -0
  185. data/lib/rubydb/concurrency/rw_lock.rb +180 -0
  186. data/lib/rubydb/concurrency/scheduler.rb +250 -0
  187. data/lib/rubydb/concurrency/worker_pool.rb +143 -0
  188. data/lib/rubydb/configuration/config.rb +170 -0
  189. data/lib/rubydb/configuration/defaults.rb +179 -0
  190. data/lib/rubydb/configuration/environment.rb +152 -0
  191. data/lib/rubydb/configuration/parser.rb +185 -0
  192. data/lib/rubydb/configuration/validation.rb +221 -0
  193. data/lib/rubydb/constants.rb +74 -0
  194. data/lib/rubydb/constraints/check.rb +181 -0
  195. data/lib/rubydb/constraints/constraint.rb +101 -0
  196. data/lib/rubydb/constraints/foreign_key.rb +130 -0
  197. data/lib/rubydb/constraints/not_null.rb +64 -0
  198. data/lib/rubydb/constraints/primary_key.rb +99 -0
  199. data/lib/rubydb/constraints/unique.rb +108 -0
  200. data/lib/rubydb/constraints/validator.rb +350 -0
  201. data/lib/rubydb/errors/authentication_error.rb +10 -0
  202. data/lib/rubydb/errors/authorization_error.rb +23 -0
  203. data/lib/rubydb/errors/client_error.rb +10 -0
  204. data/lib/rubydb/errors/configuration_error.rb +10 -0
  205. data/lib/rubydb/errors/connection_error.rb +10 -0
  206. data/lib/rubydb/errors/constraint_error.rb +23 -0
  207. data/lib/rubydb/errors/corruption_error.rb +10 -0
  208. data/lib/rubydb/errors/database_error.rb +10 -0
  209. data/lib/rubydb/errors/error.rb +20 -0
  210. data/lib/rubydb/errors/execution_error.rb +10 -0
  211. data/lib/rubydb/errors/parser_error.rb +10 -0
  212. data/lib/rubydb/errors/recovery_error.rb +10 -0
  213. data/lib/rubydb/errors/replication_error.rb +10 -0
  214. data/lib/rubydb/errors/server_error.rb +6 -0
  215. data/lib/rubydb/errors/storage_error.rb +10 -0
  216. data/lib/rubydb/errors/transaction_error.rb +10 -0
  217. data/lib/rubydb/execution/aggregate_executor.rb +138 -0
  218. data/lib/rubydb/execution/delete_executor.rb +112 -0
  219. data/lib/rubydb/execution/distinct_executor.rb +135 -0
  220. data/lib/rubydb/execution/executor.rb +1192 -0
  221. data/lib/rubydb/execution/expression.rb +193 -0
  222. data/lib/rubydb/execution/index_scan.rb +142 -0
  223. data/lib/rubydb/execution/insert_executor.rb +217 -0
  224. data/lib/rubydb/execution/join_executor.rb +249 -0
  225. data/lib/rubydb/execution/limit_executor.rb +85 -0
  226. data/lib/rubydb/execution/optimizer.rb +215 -0
  227. data/lib/rubydb/execution/plan.rb +361 -0
  228. data/lib/rubydb/execution/planner.rb +546 -0
  229. data/lib/rubydb/execution/predicate.rb +235 -0
  230. data/lib/rubydb/execution/scan.rb +49 -0
  231. data/lib/rubydb/execution/sequential_scan.rb +63 -0
  232. data/lib/rubydb/execution/sort_executor.rb +185 -0
  233. data/lib/rubydb/execution/update_executor.rb +162 -0
  234. data/lib/rubydb/functions/aggregate.rb +70 -0
  235. data/lib/rubydb/functions/date_functions.rb +278 -0
  236. data/lib/rubydb/functions/function.rb +85 -0
  237. data/lib/rubydb/functions/json_functions.rb +215 -0
  238. data/lib/rubydb/functions/numeric_functions.rb +346 -0
  239. data/lib/rubydb/functions/scalar.rb +52 -0
  240. data/lib/rubydb/functions/string_functions.rb +383 -0
  241. data/lib/rubydb/functions/system_functions.rb +246 -0
  242. data/lib/rubydb/history/as_of.rb +238 -0
  243. data/lib/rubydb/history/change.rb +105 -0
  244. data/lib/rubydb/history/history.rb +131 -0
  245. data/lib/rubydb/history/history_manager.rb +229 -0
  246. data/lib/rubydb/history/temporal_query.rb +202 -0
  247. data/lib/rubydb/history/timeline.rb +144 -0
  248. data/lib/rubydb/indexes/btree.rb +186 -0
  249. data/lib/rubydb/indexes/btree_cursor.rb +258 -0
  250. data/lib/rubydb/indexes/btree_node.rb +385 -0
  251. data/lib/rubydb/indexes/hash_index.rb +150 -0
  252. data/lib/rubydb/indexes/index.rb +71 -0
  253. data/lib/rubydb/indexes/index_manager.rb +406 -0
  254. data/lib/rubydb/indexes/index_scan.rb +470 -0
  255. data/lib/rubydb/migrations/migration.rb +254 -0
  256. data/lib/rubydb/migrations/migration_lock.rb +146 -0
  257. data/lib/rubydb/migrations/migration_manager.rb +176 -0
  258. data/lib/rubydb/migrations/migration_version.rb +71 -0
  259. data/lib/rubydb/migrations/schema_diff.rb +211 -0
  260. data/lib/rubydb/migrations/schema_version.rb +64 -0
  261. data/lib/rubydb/monitoring/events.rb +160 -0
  262. data/lib/rubydb/monitoring/health.rb +222 -0
  263. data/lib/rubydb/monitoring/logger.rb +193 -0
  264. data/lib/rubydb/monitoring/metrics.rb +359 -0
  265. data/lib/rubydb/monitoring/performance.rb +176 -0
  266. data/lib/rubydb/monitoring/statistics.rb +170 -0
  267. data/lib/rubydb/mvcc/garbage_collector.rb +199 -0
  268. data/lib/rubydb/mvcc/mvcc.rb +16 -0
  269. data/lib/rubydb/mvcc/snapshot.rb +147 -0
  270. data/lib/rubydb/mvcc/vacuum.rb +180 -0
  271. data/lib/rubydb/mvcc/version.rb +106 -0
  272. data/lib/rubydb/mvcc/version_store.rb +398 -0
  273. data/lib/rubydb/mvcc/visibility.rb +109 -0
  274. data/lib/rubydb/protocol/capabilities.rb +125 -0
  275. data/lib/rubydb/protocol/decoder.rb +145 -0
  276. data/lib/rubydb/protocol/encoder.rb +136 -0
  277. data/lib/rubydb/protocol/handshake.rb +305 -0
  278. data/lib/rubydb/protocol/message.rb +121 -0
  279. data/lib/rubydb/protocol/protocol.rb +277 -0
  280. data/lib/rubydb/protocol/version.rb +54 -0
  281. data/lib/rubydb/rails/adapter.rb +239 -0
  282. data/lib/rubydb/rails/connection.rb +314 -0
  283. data/lib/rubydb/rails/database_statements.rb +122 -0
  284. data/lib/rubydb/rails/migration.rb +131 -0
  285. data/lib/rubydb/rails/quoting.rb +109 -0
  286. data/lib/rubydb/rails/result.rb +120 -0
  287. data/lib/rubydb/rails/schema_statements.rb +339 -0
  288. data/lib/rubydb/rails/transaction.rb +105 -0
  289. data/lib/rubydb/rails/type.rb +126 -0
  290. data/lib/rubydb/recovery/checkpoint.rb +257 -0
  291. data/lib/rubydb/recovery/consistency.rb +467 -0
  292. data/lib/rubydb/recovery/corruption_detector.rb +5 -0
  293. data/lib/rubydb/recovery/crash_recovery.rb +387 -0
  294. data/lib/rubydb/recovery/recovery_manager.rb +206 -0
  295. data/lib/rubydb/recovery/redo.rb +237 -0
  296. data/lib/rubydb/recovery/undo.rb +206 -0
  297. data/lib/rubydb/replication/failover.rb +5 -0
  298. data/lib/rubydb/replication/fencing.rb +63 -0
  299. data/lib/rubydb/replication/primary.rb +450 -0
  300. data/lib/rubydb/replication/replica.rb +384 -0
  301. data/lib/rubydb/replication/replication_log.rb +200 -0
  302. data/lib/rubydb/replication/replication_manager.rb +308 -0
  303. data/lib/rubydb/replication/replication_slot.rb +295 -0
  304. data/lib/rubydb/replication/replication_stream.rb +201 -0
  305. data/lib/rubydb/rubydb.rb +560 -0
  306. data/lib/rubydb/security/access_control.rb +254 -0
  307. data/lib/rubydb/security/audit_log.rb +213 -0
  308. data/lib/rubydb/security/authentication.rb +302 -0
  309. data/lib/rubydb/security/authorization.rb +282 -0
  310. data/lib/rubydb/security/credentials.rb +196 -0
  311. data/lib/rubydb/security/password.rb +215 -0
  312. data/lib/rubydb/security/permissions.rb +74 -0
  313. data/lib/rubydb/security/role.rb +101 -0
  314. data/lib/rubydb/security/user.rb +86 -0
  315. data/lib/rubydb/server/connection.rb +366 -0
  316. data/lib/rubydb/server/connection_pool.rb +193 -0
  317. data/lib/rubydb/server/lifecycle.rb +228 -0
  318. data/lib/rubydb/server/listener.rb +136 -0
  319. data/lib/rubydb/server/request_handler.rb +276 -0
  320. data/lib/rubydb/server/server.rb +364 -0
  321. data/lib/rubydb/server/session.rb +369 -0
  322. data/lib/rubydb/server/worker.rb +210 -0
  323. data/lib/rubydb/server/worker_pool.rb +168 -0
  324. data/lib/rubydb/sql/ast/alter_table.rb +169 -0
  325. data/lib/rubydb/sql/ast/begin_transaction.rb +47 -0
  326. data/lib/rubydb/sql/ast/commit.rb +37 -0
  327. data/lib/rubydb/sql/ast/constraint.rb +83 -0
  328. data/lib/rubydb/sql/ast/create_database.rb +41 -0
  329. data/lib/rubydb/sql/ast/create_index.rb +61 -0
  330. data/lib/rubydb/sql/ast/create_schema.rb +52 -0
  331. data/lib/rubydb/sql/ast/create_table.rb +187 -0
  332. data/lib/rubydb/sql/ast/delete.rb +54 -0
  333. data/lib/rubydb/sql/ast/drop_database.rb +41 -0
  334. data/lib/rubydb/sql/ast/drop_index.rb +41 -0
  335. data/lib/rubydb/sql/ast/drop_schema.rb +49 -0
  336. data/lib/rubydb/sql/ast/drop_table.rb +49 -0
  337. data/lib/rubydb/sql/ast/explain.rb +64 -0
  338. data/lib/rubydb/sql/ast/expression.rb +604 -0
  339. data/lib/rubydb/sql/ast/insert.rb +82 -0
  340. data/lib/rubydb/sql/ast/node.rb +42 -0
  341. data/lib/rubydb/sql/ast/rollback.rb +63 -0
  342. data/lib/rubydb/sql/ast/savepoint.rb +59 -0
  343. data/lib/rubydb/sql/ast/select.rb +88 -0
  344. data/lib/rubydb/sql/ast/set_operation.rb +20 -0
  345. data/lib/rubydb/sql/ast/trigger.rb +29 -0
  346. data/lib/rubydb/sql/ast/update.rb +88 -0
  347. data/lib/rubydb/sql/ast/vacuum.rb +19 -0
  348. data/lib/rubydb/sql/ast/view.rb +32 -0
  349. data/lib/rubydb/sql/ast/with.rb +32 -0
  350. data/lib/rubydb/sql/grammar.rb +86 -0
  351. data/lib/rubydb/sql/keywords.rb +156 -0
  352. data/lib/rubydb/sql/lexer.rb +214 -0
  353. data/lib/rubydb/sql/operators.rb +100 -0
  354. data/lib/rubydb/sql/parser.rb +1185 -0
  355. data/lib/rubydb/sql/planner/analyzer.rb +302 -0
  356. data/lib/rubydb/sql/planner/binder.rb +550 -0
  357. data/lib/rubydb/sql/planner/type_checker.rb +431 -0
  358. data/lib/rubydb/sql/token.rb +210 -0
  359. data/lib/rubydb/storage/buffer_frame.rb +44 -0
  360. data/lib/rubydb/storage/buffer_pool.rb +155 -0
  361. data/lib/rubydb/storage/database_lock.rb +74 -0
  362. data/lib/rubydb/storage/deserializer.rb +342 -0
  363. data/lib/rubydb/storage/engine.rb +2338 -0
  364. data/lib/rubydb/storage/file_manager.rb +187 -0
  365. data/lib/rubydb/storage/free_space_map.rb +81 -0
  366. data/lib/rubydb/storage/page.rb +94 -0
  367. data/lib/rubydb/storage/page_allocator.rb +855 -0
  368. data/lib/rubydb/storage/page_header.rb +67 -0
  369. data/lib/rubydb/storage/page_manager.rb +131 -0
  370. data/lib/rubydb/storage/record.rb +58 -0
  371. data/lib/rubydb/storage/row.rb +78 -0
  372. data/lib/rubydb/storage/serializer.rb +51 -0
  373. data/lib/rubydb/storage/storage_layout.rb +151 -0
  374. data/lib/rubydb/storage/storage_manager.rb +114 -0
  375. data/lib/rubydb/storage/tuple.rb +461 -0
  376. data/lib/rubydb/storage/visibility_map.rb +984 -0
  377. data/lib/rubydb/transactions/commit_manager.rb +220 -0
  378. data/lib/rubydb/transactions/isolation.rb +98 -0
  379. data/lib/rubydb/transactions/lock.rb +76 -0
  380. data/lib/rubydb/transactions/lock_manager.rb +362 -0
  381. data/lib/rubydb/transactions/savepoint.rb +143 -0
  382. data/lib/rubydb/transactions/transaction.rb +215 -0
  383. data/lib/rubydb/transactions/transaction_id.rb +84 -0
  384. data/lib/rubydb/transactions/transaction_log.rb +257 -0
  385. data/lib/rubydb/transactions/transaction_manager.rb +435 -0
  386. data/lib/rubydb/types/bigint.rb +36 -0
  387. data/lib/rubydb/types/blob.rb +37 -0
  388. data/lib/rubydb/types/boolean.rb +34 -0
  389. data/lib/rubydb/types/date.rb +39 -0
  390. data/lib/rubydb/types/decimal.rb +48 -0
  391. data/lib/rubydb/types/float.rb +34 -0
  392. data/lib/rubydb/types/integer.rb +36 -0
  393. data/lib/rubydb/types/json.rb +41 -0
  394. data/lib/rubydb/types/null.rb +34 -0
  395. data/lib/rubydb/types/smallint.rb +36 -0
  396. data/lib/rubydb/types/text.rb +37 -0
  397. data/lib/rubydb/types/time.rb +46 -0
  398. data/lib/rubydb/types/timestamp.rb +39 -0
  399. data/lib/rubydb/types/type.rb +119 -0
  400. data/lib/rubydb/types/uuid.rb +47 -0
  401. data/lib/rubydb/types/varchar.rb +37 -0
  402. data/lib/rubydb/version.rb +33 -0
  403. data/lib/rubydb/wal/archive.rb +193 -0
  404. data/lib/rubydb/wal/checkpoint.rb +183 -0
  405. data/lib/rubydb/wal/lsn.rb +94 -0
  406. data/lib/rubydb/wal/reader.rb +260 -0
  407. data/lib/rubydb/wal/record.rb +105 -0
  408. data/lib/rubydb/wal/segment.rb +193 -0
  409. data/lib/rubydb/wal/wal.rb +452 -0
  410. data/lib/rubydb/wal/writer.rb +236 -0
  411. data/lib/rubydb.rb +7 -0
  412. data/packaging/docker/Dockerfile +36 -0
  413. data/packaging/docker/docker-compose.failover.yml +43 -0
  414. data/packaging/docker/entrypoint.sh +15 -0
  415. data/packaging/homebrew/rubydb.rb +19 -0
  416. data/packaging/systemd/rubydb.service +22 -0
  417. data/rubydb.gemspec +57 -0
  418. data/scripts/benchmark +7 -0
  419. data/scripts/durability_drill +37 -0
  420. data/scripts/format +0 -0
  421. data/scripts/fuzz +63 -0
  422. data/scripts/lint +0 -0
  423. data/scripts/release +50 -0
  424. data/scripts/release_check +43 -0
  425. data/scripts/replication_failover_drill +250 -0
  426. data/scripts/replication_network_failover_drill +255 -0
  427. data/scripts/restore_drill +45 -0
  428. data/scripts/security +0 -0
  429. data/scripts/setup +0 -0
  430. data/scripts/test +0 -0
  431. data/scripts/test-chaos +0 -0
  432. data/scripts/test-crash +0 -0
  433. data/scripts/test-integration +0 -0
  434. data/scripts/test-unit +0 -0
  435. data/visibility_map.json +1 -0
  436. metadata +538 -0
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module MVCC
5
+ # GarbageCollector - Collects and removes garbage versions
6
+ class GarbageCollector
7
+ attr_reader :stats
8
+
9
+ def initialize(version_store, visibility, config = {})
10
+ @version_store = version_store
11
+ @visibility = visibility
12
+ @config = config
13
+ @collection_interval = config[:interval] || 300 # 5 minutes
14
+ @threshold = config[:threshold] || 0.2 # 20% garbage triggers collection
15
+ @max_collect_per_run = config[:max_collect] || 10000
16
+ @stats = {
17
+ collections: 0,
18
+ garbage_collected: 0,
19
+ space_freed: 0,
20
+ last_collection: nil,
21
+ average_collected: 0
22
+ }
23
+ @lock = Mutex.new
24
+ @running = false
25
+ @gc_thread = nil
26
+ end
27
+
28
+ def start
29
+ @lock.synchronize do
30
+ return if @running
31
+
32
+ @running = true
33
+ @gc_thread = Thread.new do
34
+ while @running
35
+ sleep(@collection_interval)
36
+ begin
37
+ collect
38
+ rescue => e
39
+ # Log error but continue
40
+ puts "GC error: #{e.message}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def stop
48
+ @lock.synchronize do
49
+ @running = false
50
+ @gc_thread&.kill
51
+ @gc_thread = nil
52
+ end
53
+ end
54
+
55
+ def collect
56
+ @lock.synchronize do
57
+ @stats[:collections] += 1
58
+ start_time = Time.now
59
+
60
+ collected = 0
61
+
62
+ # Find garbage versions
63
+ garbage = find_garbage
64
+
65
+ # Remove garbage
66
+ garbage.each do |version|
67
+ # Check if version is still referenced
68
+ if is_referenced?(version)
69
+ next
70
+ end
71
+
72
+ # Remove the version
73
+ @version_store.delete_version(version)
74
+ @stats[:garbage_collected] += 1
75
+ collected += 1
76
+
77
+ break if collected >= @max_collect_per_run
78
+ end
79
+
80
+ # Update stats
81
+ elapsed_ms = ((Time.now - start_time) * 1000).round(2)
82
+ @stats[:last_collection] = {
83
+ time: Time.now.iso8601,
84
+ elapsed_ms: elapsed_ms,
85
+ collected: collected,
86
+ garbage_found: garbage.size
87
+ }
88
+
89
+ @stats[:average_collected] = @stats[:garbage_collected] / @stats[:collections].to_f
90
+
91
+ collected
92
+ end
93
+ end
94
+
95
+ def find_garbage
96
+ garbage = []
97
+ versions_by_row = @version_store.instance_variable_get(:@versions)
98
+
99
+ versions_by_row.each do |row_id, versions|
100
+ # Find versions that are garbage
101
+ versions.each do |version|
102
+ if is_garbage?(version)
103
+ garbage << version
104
+ end
105
+ end
106
+ end
107
+
108
+ garbage
109
+ end
110
+
111
+ def is_garbage?(version)
112
+ # Check if version is garbage based on rules
113
+
114
+ # 1. Aborted versions are garbage
115
+ return true if version.is_aborted
116
+
117
+ # 2. Deleted versions that are old
118
+ if version.is_deleted && version.is_committed
119
+ # Check if there's a newer version
120
+ versions = @version_store.get_all_versions(version.row_id)
121
+ newer_versions = versions.select { |v| v.created_at > version.created_at }
122
+ if newer_versions.any?
123
+ return true
124
+ end
125
+ end
126
+
127
+ # 3. Committed versions that are no longer referenced
128
+ if version.is_committed && !version.is_deleted
129
+ # Check if any active transaction needs this version
130
+ if !has_references?(version)
131
+ return true
132
+ end
133
+ end
134
+
135
+ false
136
+ end
137
+
138
+ def is_referenced?(version)
139
+ # Check if version is referenced by any active transaction
140
+ # or is needed for rollback
141
+ has_references?(version)
142
+ end
143
+
144
+ def has_references?(version)
145
+ # Check if any active transaction might need this version
146
+ # For simplicity, we check if there are any active versions
147
+ # that reference this version as previous version
148
+ versions_by_row = @version_store.instance_variable_get(:@versions)
149
+
150
+ versions_by_row.each do |_, versions|
151
+ versions.each do |v|
152
+ if v.prev_version_id == version.version_id
153
+ return true
154
+ end
155
+ end
156
+ end
157
+
158
+ false
159
+ end
160
+
161
+ def force_collect
162
+ collect
163
+ end
164
+
165
+ def stats
166
+ @lock.synchronize do
167
+ @stats.merge({
168
+ running: @running,
169
+ interval: @collection_interval,
170
+ threshold: @threshold,
171
+ max_per_run: @max_collect_per_run,
172
+ garbage_ratio: garbage_ratio
173
+ })
174
+ end
175
+ end
176
+
177
+ def garbage_ratio
178
+ total = @version_store.version_count
179
+ return 0.0 if total == 0
180
+
181
+ # Estimate garbage count
182
+ garbage = find_garbage
183
+ garbage.size.to_f / total
184
+ end
185
+
186
+ def status
187
+ @lock.synchronize do
188
+ {
189
+ running: @running,
190
+ last_collection: @stats[:last_collection],
191
+ total_collected: @stats[:garbage_collected],
192
+ average_collected: @stats[:average_collected],
193
+ garbage_ratio: garbage_ratio
194
+ }
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,16 @@
1
+ # lib/rubydb/mvcc.rb
2
+ # frozen_string_literal: true
3
+
4
+ # Main entry point for MVCC module
5
+ require_relative "mvcc/version"
6
+ require_relative "mvcc/visibility"
7
+ require_relative "mvcc/snapshot"
8
+ require_relative "mvcc/version_store"
9
+ require_relative "mvcc/vacuum"
10
+ require_relative "mvcc/garbage_collector"
11
+
12
+ module RubyDB
13
+ module MVCC
14
+ # All classes are now loaded
15
+ end
16
+ end
@@ -0,0 +1,147 @@
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
@@ -0,0 +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
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module RubyDB
6
+ module MVCC
7
+ # Version - Represents a version of a row
8
+ class Version
9
+ attr_reader :row_id, :version_id, :created_at, :transaction_id
10
+ attr_reader :data, :prev_version_id, :next_version_id
11
+ attr_accessor :is_committed, :is_aborted, :is_deleted
12
+ attr_reader :commit_id, :committed_at, :aborted_at, :visibility
13
+
14
+ def initialize(row_id, data, transaction_id, prev_version_id = nil)
15
+ @row_id = row_id
16
+ @version_id = generate_version_id
17
+ @data = data
18
+ @transaction_id = transaction_id
19
+ @prev_version_id = prev_version_id
20
+ @next_version_id = nil
21
+ @created_at = Time.now
22
+ @is_committed = false
23
+ @is_aborted = false
24
+ @is_deleted = false
25
+ @commit_id = nil
26
+ @visibility = :active # :active, :visible, :hidden, :deleted
27
+ @lock = Mutex.new
28
+ end
29
+
30
+ def generate_version_id
31
+ "#{row_id}_#{Time.now.to_i}_#{rand(10000)}"
32
+ end
33
+
34
+ def commit(commit_id = nil)
35
+ @lock.synchronize do
36
+ @commit_id = commit_id || transaction_id
37
+ @is_committed = true
38
+ @visibility = :visible
39
+ @committed_at = Time.now
40
+ end
41
+ end
42
+
43
+ def abort
44
+ @lock.synchronize do
45
+ @is_aborted = true
46
+ @visibility = :hidden
47
+ @aborted_at = Time.now
48
+ end
49
+ end
50
+
51
+ def mark_deleted
52
+ @lock.synchronize do
53
+ @is_deleted = true
54
+ @visibility = :deleted
55
+ end
56
+ end
57
+
58
+ def visible_to?(transaction_id, snapshot = nil)
59
+ @lock.synchronize do
60
+ # Check snapshot first
61
+ if snapshot
62
+ return snapshot.visible?(self, transaction_id)
63
+ end
64
+
65
+ # If not committed, only visible to its own transaction
66
+ return false unless @is_committed
67
+
68
+ # Check if deleted
69
+ return false if @is_deleted && @visibility == :deleted
70
+
71
+ # Committed versions are visible to all transactions
72
+ # except those that started before this version was committed
73
+ if @commit_id && transaction_id
74
+ return @commit_id <= transaction_id
75
+ end
76
+
77
+ true
78
+ end
79
+ end
80
+
81
+ def to_hash
82
+ {
83
+ row_id: @row_id,
84
+ version_id: @version_id,
85
+ created_at: @created_at.iso8601,
86
+ transaction_id: @transaction_id,
87
+ prev_version_id: @prev_version_id,
88
+ next_version_id: @next_version_id,
89
+ is_committed: @is_committed,
90
+ is_aborted: @is_aborted,
91
+ is_deleted: @is_deleted,
92
+ visibility: @visibility,
93
+ commit_id: @commit_id
94
+ }
95
+ end
96
+
97
+ def inspect
98
+ "#<Version row_id=#{@row_id} version=#{@version_id} txn=#{@transaction_id} visibility=#{@visibility}>"
99
+ end
100
+
101
+ def to_s
102
+ inspect
103
+ end
104
+ end
105
+ end
106
+ end