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,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module RubyDB
6
+ module Concurrency
7
+ # DeadlockDetector - Detects and resolves deadlocks
8
+ class DeadlockDetector
9
+ attr_reader :stats
10
+
11
+ def initialize(config = {})
12
+ @lock_graph = LockGraph.new
13
+ @deadlock_resolution = config[:resolution] || :abort_victim
14
+ @timeout = config[:timeout] || 30
15
+ @max_attempts = config[:max_attempts] || 3
16
+ @transaction_manager = config[:transaction_manager]
17
+ @stats = {
18
+ deadlocks_detected: 0,
19
+ deadlocks_resolved: 0,
20
+ victims_aborted: 0,
21
+ detection_runs: 0,
22
+ avg_detection_time_ms: 0,
23
+ total_detection_time_ms: 0
24
+ }
25
+ @lock = Mutex.new
26
+ @detection_thread = nil
27
+ @running = false
28
+ end
29
+
30
+ def start
31
+ @lock.synchronize do
32
+ return if @running
33
+
34
+ @running = true
35
+ @detection_thread = Thread.new do
36
+ detection_loop
37
+ end
38
+ end
39
+ end
40
+
41
+ def stop
42
+ @lock.synchronize do
43
+ @running = false
44
+ @detection_thread&.kill
45
+ @detection_thread = nil
46
+ end
47
+ end
48
+
49
+ def detect_deadlocks
50
+ @lock.synchronize do
51
+ @stats[:detection_runs] += 1
52
+ start_time = Time.now
53
+
54
+ # Build wait-for graph
55
+ @lock_graph.build
56
+
57
+ # Detect cycles
58
+ cycles = @lock_graph.detect_cycles
59
+
60
+ if cycles.any?
61
+ @stats[:deadlocks_detected] += cycles.size
62
+ cycles.each do |cycle|
63
+ resolve_deadlock(cycle)
64
+ end
65
+ end
66
+
67
+ detection_time = (Time.now - start_time) * 1000
68
+ @stats[:total_detection_time_ms] += detection_time
69
+ @stats[:avg_detection_time_ms] = @stats[:total_detection_time_ms] / @stats[:detection_runs]
70
+
71
+ cycles
72
+ end
73
+ end
74
+
75
+ def resolve_deadlock(cycle)
76
+ # Choose victim based on policy
77
+ victim = choose_victim(cycle)
78
+
79
+ if victim
80
+ abort_victim(victim)
81
+ @stats[:deadlocks_resolved] += 1
82
+ @stats[:victims_aborted] += 1
83
+ end
84
+
85
+ victim
86
+ end
87
+
88
+ def choose_victim(cycle)
89
+ # Choose the transaction with lowest priority or oldest timestamp
90
+ # For simplicity, choose the one with the most locks
91
+ cycle.max_by { |txn| txn[:locks] || 0 }
92
+ end
93
+
94
+ def abort_victim(victim)
95
+ victim[:aborted] = true
96
+ victim[:abort_time] = Time.now
97
+
98
+ if @transaction_manager.respond_to?(:get_transaction) &&
99
+ @transaction_manager.respond_to?(:rollback_transaction)
100
+ transaction = @transaction_manager.get_transaction(victim[:id])
101
+ @transaction_manager.rollback_transaction(transaction) if transaction
102
+ end
103
+
104
+ # Keep the graph in sync even when no transaction manager is attached.
105
+ @lock_graph.release_locks(victim[:id])
106
+ end
107
+
108
+ def add_lock_holder(resource, holder)
109
+ @lock.synchronize do
110
+ @lock_graph.add_holder(resource, holder)
111
+ end
112
+ end
113
+
114
+ def add_lock_waiter(resource, waiter)
115
+ @lock.synchronize do
116
+ @lock_graph.add_waiter(resource, waiter)
117
+ end
118
+ end
119
+
120
+ def remove_transaction(transaction_id)
121
+ @lock.synchronize do
122
+ @lock_graph.remove_transaction(transaction_id)
123
+ end
124
+ end
125
+
126
+ def stats
127
+ @lock.synchronize do
128
+ @stats.merge({
129
+ running: @running,
130
+ graph_size: @lock_graph.size,
131
+ graph_edges: @lock_graph.edge_count
132
+ })
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def detection_loop
139
+ while @running
140
+ sleep(@timeout)
141
+ begin
142
+ detect_deadlocks
143
+ rescue => e
144
+ # Log error but continue
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Concurrency
5
+ # Latch - A synchronization primitive that allows threads to wait until a count reaches zero
6
+ class Latch
7
+ attr_reader :count, :stats
8
+
9
+ def initialize(count = 1)
10
+ @count = count
11
+ @mutex = Mutex.new
12
+ @cond = ConditionVariable.new
13
+ @stats = {
14
+ wait_count: 0,
15
+ signal_count: 0,
16
+ timed_out: 0
17
+ }
18
+ end
19
+
20
+ def await(timeout = nil)
21
+ @mutex.synchronize do
22
+ @stats[:wait_count] += 1
23
+
24
+ if timeout
25
+ deadline = Time.now + timeout
26
+ while @count > 0 && Time.now < deadline
27
+ @cond.wait(@mutex, [deadline - Time.now, 0.1].max)
28
+ end
29
+
30
+ if @count > 0
31
+ @stats[:timed_out] += 1
32
+ return false
33
+ end
34
+ else
35
+ while @count > 0
36
+ @cond.wait(@mutex)
37
+ end
38
+ end
39
+
40
+ true
41
+ end
42
+ end
43
+
44
+ def count_down(count = 1)
45
+ @mutex.synchronize do
46
+ @count -= count
47
+ @count = 0 if @count < 0
48
+ @stats[:signal_count] += count
49
+ @cond.broadcast if @count == 0
50
+ end
51
+ end
52
+
53
+ def count_up(count = 1)
54
+ @mutex.synchronize do
55
+ @count += count
56
+ end
57
+ end
58
+
59
+ def reset(count = nil)
60
+ @mutex.synchronize do
61
+ @count = count || @count
62
+ @cond.broadcast if @count == 0
63
+ end
64
+ end
65
+
66
+ def ready?
67
+ @mutex.synchronize { @count == 0 }
68
+ end
69
+
70
+ def wait(timeout = nil)
71
+ await(timeout)
72
+ end
73
+
74
+ def to_s
75
+ "Latch(count=#{@count})"
76
+ end
77
+
78
+ def inspect
79
+ to_s
80
+ end
81
+ end
82
+
83
+ # CountDownLatch - A latch that counts down from a specific number
84
+ class CountDownLatch < Latch
85
+ attr_reader :initial_count
86
+
87
+ def initialize(count)
88
+ super(count)
89
+ @initial_count = count
90
+ end
91
+
92
+ def reset
93
+ super(@initial_count)
94
+ end
95
+
96
+ def to_s
97
+ "CountDownLatch(count=#{@count}/#{@initial_count})"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module RubyDB
6
+ module Concurrency
7
+ # LockGraph - Represents the wait-for graph for deadlock detection
8
+ class LockGraph
9
+ attr_reader :nodes, :edges
10
+
11
+ def initialize
12
+ @nodes = {} # transaction_id => {locks: [], waiting_for: []}
13
+ @edges = {} # source => {target: edge_info}
14
+ @reverse_edges = {} # target => [sources]
15
+ @lock = Mutex.new
16
+ end
17
+
18
+ def add_holder(resource, holder)
19
+ @lock.synchronize do
20
+ @nodes[holder[:id]] ||= {
21
+ id: holder[:id],
22
+ locks: [],
23
+ waiting_for: [],
24
+ info: holder
25
+ }
26
+ @nodes[holder[:id]][:locks] << resource
27
+ end
28
+ end
29
+
30
+ def add_waiter(resource, waiter)
31
+ @lock.synchronize do
32
+ @nodes[waiter[:id]] ||= {
33
+ id: waiter[:id],
34
+ locks: [],
35
+ waiting_for: [],
36
+ info: waiter
37
+ }
38
+ @nodes[waiter[:id]][:waiting_for] << resource
39
+
40
+ # Find who holds the resource
41
+ holders = find_holders(resource)
42
+ holders.each do |holder|
43
+ add_edge(waiter[:id], holder[:id], resource)
44
+ end
45
+ end
46
+ end
47
+
48
+ def remove_transaction(transaction_id)
49
+ @lock.synchronize do
50
+ # Remove all edges involving this transaction
51
+ @edges.delete(transaction_id)
52
+ @reverse_edges.delete(transaction_id)
53
+
54
+ @edges.each do |source, targets|
55
+ targets.delete(transaction_id)
56
+ end
57
+
58
+ @reverse_edges.each do |target, sources|
59
+ sources.delete(transaction_id)
60
+ end
61
+
62
+ @nodes.delete(transaction_id)
63
+ end
64
+ end
65
+
66
+ def release_locks(transaction_id)
67
+ @lock.synchronize do
68
+ node = @nodes[transaction_id]
69
+ return unless node
70
+
71
+ # Remove all resources locked by this transaction
72
+ node[:locks].clear
73
+ node[:waiting_for].clear
74
+
75
+ # Clean up edges
76
+ remove_transaction(transaction_id)
77
+ end
78
+ end
79
+
80
+ def build
81
+ @lock.synchronize do
82
+ # Rebuild graph from current state
83
+ # This would be called periodically
84
+ true
85
+ end
86
+ end
87
+
88
+ def detect_cycles
89
+ @lock.synchronize do
90
+ cycles = []
91
+ visited = Set.new
92
+ recursion_stack = Set.new
93
+
94
+ @nodes.each_key do |node|
95
+ if !visited.include?(node)
96
+ path = []
97
+ if detect_cycle_dfs(node, visited, recursion_stack, path)
98
+ cycles << path
99
+ end
100
+ end
101
+ end
102
+
103
+ cycles
104
+ end
105
+ end
106
+
107
+ def size
108
+ @nodes.size
109
+ end
110
+
111
+ def edge_count
112
+ @edges.values.sum(&:size)
113
+ end
114
+
115
+ def to_s
116
+ "LockGraph(nodes=#{@nodes.size}, edges=#{edge_count})"
117
+ end
118
+
119
+ private
120
+
121
+ def find_holders(resource)
122
+ holders = []
123
+ @nodes.each do |id, node|
124
+ if node[:locks].include?(resource)
125
+ holders << { id: id, info: node[:info] }
126
+ end
127
+ end
128
+ holders
129
+ end
130
+
131
+ def add_edge(source, target, resource)
132
+ @edges[source] ||= {}
133
+ @edges[source][target] = { resource: resource, timestamp: Time.now }
134
+
135
+ @reverse_edges[target] ||= Set.new
136
+ @reverse_edges[target] << source
137
+ end
138
+
139
+ def detect_cycle_dfs(node, visited, recursion_stack, path)
140
+ return false if visited.include?(node)
141
+
142
+ visited.add(node)
143
+ recursion_stack.add(node)
144
+ path << node
145
+
146
+ @edges[node]&.keys&.each do |neighbor|
147
+ if recursion_stack.include?(neighbor)
148
+ # Found a cycle
149
+ cycle_start = path.index(neighbor)
150
+ cycle = path[cycle_start..-1] + [neighbor]
151
+ return true
152
+ end
153
+
154
+ if detect_cycle_dfs(neighbor, visited, recursion_stack, path)
155
+ return true
156
+ end
157
+ end
158
+
159
+ recursion_stack.delete(node)
160
+ path.pop
161
+ false
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Concurrency
5
+ # Mutex - A mutual exclusion lock with additional features
6
+ class Mutex
7
+ attr_reader :name, :stats, :owner
8
+
9
+ def initialize(name = nil)
10
+ @mutex = ::Mutex.new
11
+ @name = name
12
+ @owner = nil
13
+ @lock_count = 0
14
+ @waiters = []
15
+ @stats = {
16
+ lock_attempts: 0,
17
+ lock_successes: 0,
18
+ lock_failures: 0,
19
+ unlock_count: 0,
20
+ contention_count: 0,
21
+ avg_wait_time_ms: 0,
22
+ total_wait_time_ms: 0
23
+ }
24
+ @lock = ::Mutex.new
25
+ end
26
+
27
+ def synchronize
28
+ lock
29
+ begin
30
+ yield
31
+ ensure
32
+ unlock
33
+ end
34
+ end
35
+
36
+ def lock(timeout = nil)
37
+ @lock.synchronize do
38
+ @stats[:lock_attempts] += 1
39
+
40
+ start_time = Time.now
41
+ current_thread = Thread.current
42
+
43
+ # If already locked by this thread, increment lock count
44
+ if @owner == current_thread
45
+ @lock_count += 1
46
+ @stats[:lock_successes] += 1
47
+ return true
48
+ end
49
+
50
+ # Try to acquire lock
51
+ success = if timeout
52
+ acquired = false
53
+ deadline = Time.now + timeout
54
+ while !acquired && Time.now < deadline
55
+ acquired = @mutex.try_lock
56
+ sleep(0.001) if !acquired
57
+ end
58
+ acquired
59
+ else
60
+ @mutex.lock
61
+ true
62
+ end
63
+
64
+ if success
65
+ @owner = current_thread
66
+ @lock_count = 1
67
+ @stats[:lock_successes] += 1
68
+
69
+ wait_time = (Time.now - start_time) * 1000
70
+ @stats[:total_wait_time_ms] += wait_time
71
+ @stats[:avg_wait_time_ms] = @stats[:total_wait_time_ms] / @stats[:lock_successes]
72
+ else
73
+ @stats[:lock_failures] += 1
74
+ false
75
+ end
76
+ end
77
+ end
78
+
79
+ def unlock
80
+ @lock.synchronize do
81
+ if @owner != Thread.current
82
+ raise ThreadError, "Mutex not owned by current thread"
83
+ end
84
+
85
+ @lock_count -= 1
86
+
87
+ if @lock_count == 0
88
+ @owner = nil
89
+ @mutex.unlock
90
+ @stats[:unlock_count] += 1
91
+ end
92
+
93
+ true
94
+ end
95
+ end
96
+
97
+ def locked?
98
+ @mutex.locked?
99
+ end
100
+
101
+ def try_lock
102
+ @lock.synchronize do
103
+ @stats[:lock_attempts] += 1
104
+
105
+ if @mutex.try_lock
106
+ @owner = Thread.current
107
+ @lock_count = 1
108
+ @stats[:lock_successes] += 1
109
+ true
110
+ else
111
+ @stats[:lock_failures] += 1
112
+ @stats[:contention_count] += 1
113
+ false
114
+ end
115
+ end
116
+ end
117
+
118
+ def owned?
119
+ @owner == Thread.current
120
+ end
121
+
122
+ def to_s
123
+ status = locked? ? "locked" : "unlocked"
124
+ "#{@name || 'Mutex'} (#{status}, owner=#{@owner&.object_id})"
125
+ end
126
+
127
+ def inspect
128
+ to_s
129
+ end
130
+ end
131
+
132
+ # ReentrantMutex - A mutex that can be locked multiple times by the same thread
133
+ class ReentrantMutex < Mutex
134
+ def initialize(name = nil)
135
+ super(name)
136
+ @recursion_depth = 0
137
+ end
138
+
139
+ def lock(timeout = nil)
140
+ @lock.synchronize do
141
+ @stats[:lock_attempts] += 1
142
+
143
+ if @owner == Thread.current
144
+ @lock_count += 1
145
+ @recursion_depth += 1
146
+ @stats[:lock_successes] += 1
147
+ return true
148
+ end
149
+
150
+ super
151
+ end
152
+ end
153
+
154
+ def unlock
155
+ @lock.synchronize do
156
+ if @owner != Thread.current
157
+ raise ThreadError, "Mutex not owned by current thread"
158
+ end
159
+
160
+ @lock_count -= 1
161
+ @recursion_depth -= 1
162
+
163
+ if @lock_count == 0
164
+ @owner = nil
165
+ @mutex.unlock
166
+ @stats[:unlock_count] += 1
167
+ end
168
+
169
+ true
170
+ end
171
+ end
172
+
173
+ def recursion_depth
174
+ @recursion_depth
175
+ end
176
+
177
+ def to_s
178
+ status = locked? ? "locked" : "unlocked"
179
+ "#{@name || 'ReentrantMutex'} (#{status}, recursion=#{@recursion_depth})"
180
+ end
181
+ end
182
+ end
183
+ end