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,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Concurrency
5
+ # RWLock - Read-Write lock with fairness
6
+ class RWLock
7
+ attr_reader :stats
8
+
9
+ def initialize(config = {})
10
+ @mutex = Mutex.new
11
+ @readers = 0
12
+ @writers = 0
13
+ @pending_readers = 0
14
+ @pending_writers = 0
15
+ @prefer_writer = config[:prefer_writer] || false
16
+ @fair = config[:fair] || true
17
+ @stats = {
18
+ read_locks: 0,
19
+ write_locks: 0,
20
+ read_unlocks: 0,
21
+ write_unlocks: 0,
22
+ read_waits: 0,
23
+ write_waits: 0,
24
+ contention: 0
25
+ }
26
+ @cond = ConditionVariable.new
27
+ @lock = Mutex.new
28
+ end
29
+
30
+ def read_lock(timeout = nil)
31
+ @lock.synchronize do
32
+ start_time = Time.now
33
+ @stats[:read_locks] += 1
34
+
35
+ # Wait if there are writers or pending writers
36
+ while (@writers > 0 || (@prefer_writer && @pending_writers > 0)) ||
37
+ (@fair && @pending_writers > 0 && @pending_readers > @pending_writers)
38
+ @stats[:read_waits] += 1
39
+ @stats[:contention] += 1
40
+
41
+ if timeout
42
+ deadline = Time.now + timeout
43
+ @cond.wait(@lock, [deadline - Time.now, 0.1].max)
44
+ if Time.now >= deadline && (@writers > 0 || @pending_writers > 0)
45
+ return false
46
+ end
47
+ else
48
+ @cond.wait(@lock)
49
+ end
50
+ end
51
+
52
+ @readers += 1
53
+ @stats[:read_waits] += 1 if Time.now - start_time > 0.01
54
+ true
55
+ end
56
+ end
57
+
58
+ def write_lock(timeout = nil)
59
+ @lock.synchronize do
60
+ start_time = Time.now
61
+ @stats[:write_locks] += 1
62
+ @pending_writers += 1
63
+
64
+ # Wait until no readers or writers
65
+ while @readers > 0 || @writers > 0 || @pending_readers > 0
66
+ @stats[:write_waits] += 1
67
+ @stats[:contention] += 1
68
+
69
+ if timeout
70
+ deadline = Time.now + timeout
71
+ @cond.wait(@lock, [deadline - Time.now, 0.1].max)
72
+ if Time.now >= deadline && (@readers > 0 || @writers > 0)
73
+ @pending_writers -= 1
74
+ return false
75
+ end
76
+ else
77
+ @cond.wait(@lock)
78
+ end
79
+ end
80
+
81
+ @pending_writers -= 1
82
+ @writers += 1
83
+ true
84
+ end
85
+ end
86
+
87
+ def read_unlock
88
+ @lock.synchronize do
89
+ if @readers <= 0
90
+ raise "No readers to unlock"
91
+ end
92
+
93
+ @readers -= 1
94
+ @stats[:read_unlocks] += 1
95
+ @cond.broadcast if @readers == 0
96
+ true
97
+ end
98
+ end
99
+
100
+ def write_unlock
101
+ @lock.synchronize do
102
+ if @writers <= 0
103
+ raise "No writers to unlock"
104
+ end
105
+
106
+ @writers -= 1
107
+ @stats[:write_unlocks] += 1
108
+ @cond.broadcast
109
+ true
110
+ end
111
+ end
112
+
113
+ def with_read_lock
114
+ if read_lock
115
+ begin
116
+ yield
117
+ ensure
118
+ read_unlock
119
+ end
120
+ end
121
+ end
122
+
123
+ def with_write_lock
124
+ if write_lock
125
+ begin
126
+ yield
127
+ ensure
128
+ write_unlock
129
+ end
130
+ end
131
+ end
132
+
133
+ def try_read_lock
134
+ @lock.synchronize do
135
+ if @writers == 0 && !(@prefer_writer && @pending_writers > 0)
136
+ @readers += 1
137
+ @stats[:read_locks] += 1
138
+ true
139
+ else
140
+ @stats[:contention] += 1
141
+ false
142
+ end
143
+ end
144
+ end
145
+
146
+ def try_write_lock
147
+ @lock.synchronize do
148
+ if @readers == 0 && @writers == 0
149
+ @writers += 1
150
+ @stats[:write_locks] += 1
151
+ true
152
+ else
153
+ @stats[:contention] += 1
154
+ false
155
+ end
156
+ end
157
+ end
158
+
159
+ def reader_count
160
+ @readers
161
+ end
162
+
163
+ def writer_count
164
+ @writers
165
+ end
166
+
167
+ def stats
168
+ @lock.synchronize do
169
+ @stats.merge({
170
+ readers: @readers,
171
+ writers: @writers,
172
+ pending_readers: @pending_readers,
173
+ pending_writers: @pending_writers,
174
+ total_locks: @stats[:read_locks] + @stats[:write_locks]
175
+ })
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thread"
4
+ require "set"
5
+ require "time"
6
+
7
+ module RubyDB
8
+ module Concurrency
9
+ # Scheduler - Manages execution of concurrent tasks with priorities
10
+ class Scheduler
11
+ attr_reader :stats, :queue_size
12
+
13
+ # Scheduling policies
14
+ POLICY_FIFO = :fifo
15
+ POLICY_PRIORITY = :priority
16
+ POLICY_ROUND_ROBIN = :round_robin
17
+ POLICY_FAIR = :fair
18
+
19
+ def initialize(config = {})
20
+ @policy = config[:policy] || POLICY_FIFO
21
+ @max_threads = config[:max_threads] || (Etc.nprocessors * 2)
22
+ @min_threads = config[:min_threads] || [2, Etc.nprocessors].min
23
+ @queue_size = 0
24
+ @thread_pool = []
25
+ @task_queue = []
26
+ @priority_queues = {}
27
+ @stats = {
28
+ tasks_submitted: 0,
29
+ tasks_completed: 0,
30
+ tasks_failed: 0,
31
+ tasks_queued: 0,
32
+ avg_wait_time_ms: 0,
33
+ avg_execution_time_ms: 0,
34
+ total_wait_time_ms: 0,
35
+ total_execution_time_ms: 0
36
+ }
37
+ @lock = Mutex.new
38
+ @cond = ConditionVariable.new
39
+ @running = false
40
+ @shutdown = false
41
+ @scheduler_thread = nil
42
+
43
+ # Initialize priority queues
44
+ [1, 2, 3, 4, 5].each do |priority|
45
+ @priority_queues[priority] = []
46
+ end
47
+
48
+ start
49
+ end
50
+
51
+ def start
52
+ @lock.synchronize do
53
+ return if @running
54
+
55
+ @running = true
56
+ @shutdown = false
57
+
58
+ # Start worker threads
59
+ @min_threads.times do
60
+ start_worker
61
+ end
62
+
63
+ # Start scheduler thread
64
+ @scheduler_thread = Thread.new do
65
+ schedule_loop
66
+ end
67
+ end
68
+ end
69
+
70
+ def stop(wait = true)
71
+ @lock.synchronize do
72
+ @shutdown = true
73
+ @cond.broadcast
74
+
75
+ if wait
76
+ @scheduler_thread&.join
77
+ @thread_pool.each(&:join)
78
+ end
79
+ end
80
+ end
81
+
82
+ def submit(task, priority: 3, timeout: nil)
83
+ @lock.synchronize do
84
+ raise "Scheduler is shutting down" if @shutdown
85
+
86
+ entry = {
87
+ task: task,
88
+ priority: priority,
89
+ submitted_at: Time.now,
90
+ timeout: timeout,
91
+ status: :queued
92
+ }
93
+
94
+ if priority.is_a?(Integer) && priority.between?(1, 5)
95
+ @priority_queues[priority] << entry
96
+ else
97
+ @task_queue << entry
98
+ end
99
+
100
+ @queue_size += 1
101
+ @stats[:tasks_submitted] += 1
102
+ @stats[:tasks_queued] += 1
103
+ @cond.signal
104
+
105
+ entry
106
+ end
107
+ end
108
+
109
+ def submit_block(priority: 3, timeout: nil, &block)
110
+ submit(block, priority: priority, timeout: timeout)
111
+ end
112
+
113
+ def wait_for_task(task_entry, timeout: nil)
114
+ start_time = Time.now
115
+ deadline = timeout ? start_time + timeout : nil
116
+
117
+ loop do
118
+ @lock.synchronize do
119
+ break if task_entry[:status] == :completed || task_entry[:status] == :failed
120
+
121
+ if deadline && Time.now > deadline
122
+ task_entry[:status] = :timeout
123
+ break
124
+ end
125
+
126
+ @cond.wait(@lock, 0.1)
127
+ end
128
+ end
129
+
130
+ task_entry[:result]
131
+ end
132
+
133
+ def running?
134
+ @running
135
+ end
136
+
137
+ def stats
138
+ @lock.synchronize do
139
+ @stats.merge({
140
+ queue_size: @queue_size,
141
+ threads: @thread_pool.size,
142
+ running: @running,
143
+ policy: @policy,
144
+ max_threads: @max_threads,
145
+ min_threads: @min_threads
146
+ })
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ def start_worker
153
+ worker = Thread.new do
154
+ worker_loop
155
+ end
156
+ @thread_pool << worker
157
+ worker
158
+ end
159
+
160
+ def worker_loop
161
+ while !@shutdown
162
+ task_entry = nil
163
+
164
+ @lock.synchronize do
165
+ while !@shutdown && !has_task?
166
+ @cond.wait(@lock, 1)
167
+ end
168
+
169
+ task_entry = dequeue_task
170
+ end
171
+
172
+ if task_entry
173
+ execute_task(task_entry)
174
+ end
175
+ end
176
+ end
177
+
178
+ def schedule_loop
179
+ while !@shutdown
180
+ sleep(1) if @thread_pool.size >= @max_threads
181
+
182
+ @lock.synchronize do
183
+ # Adjust thread pool size based on workload
184
+ if @queue_size > @thread_pool.size * 2 && @thread_pool.size < @max_threads
185
+ start_worker
186
+ elsif @queue_size < @thread_pool.size / 2 && @thread_pool.size > @min_threads
187
+ # Could shrink pool
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ def has_task?
194
+ @task_queue.any? || @priority_queues.any? { |_, q| q.any? }
195
+ end
196
+
197
+ def dequeue_task
198
+ # Priority-based dequeuing
199
+ @priority_queues.keys.sort.each do |priority|
200
+ queue = @priority_queues[priority]
201
+ if queue.any?
202
+ entry = queue.shift
203
+ @queue_size -= 1
204
+ return entry
205
+ end
206
+ end
207
+
208
+ if @task_queue.any?
209
+ entry = case @policy
210
+ when POLICY_FIFO
211
+ @task_queue.shift
212
+ when POLICY_ROUND_ROBIN
213
+ @task_queue.rotate!.first
214
+ else
215
+ @task_queue.shift
216
+ end
217
+ @queue_size -= 1
218
+ return entry
219
+ end
220
+
221
+ nil
222
+ end
223
+
224
+ def execute_task(task_entry)
225
+ start_time = Time.now
226
+ task_entry[:status] = :running
227
+
228
+ begin
229
+ result = if task_entry[:task].is_a?(Proc)
230
+ task_entry[:task].call
231
+ else
232
+ task_entry[:task]
233
+ end
234
+
235
+ task_entry[:status] = :completed
236
+ task_entry[:result] = result
237
+ @stats[:tasks_completed] += 1
238
+ rescue => e
239
+ task_entry[:status] = :failed
240
+ task_entry[:error] = e
241
+ @stats[:tasks_failed] += 1
242
+ ensure
243
+ execution_time = (Time.now - start_time) * 1000
244
+ @stats[:total_execution_time_ms] += execution_time
245
+ @stats[:avg_execution_time_ms] = @stats[:total_execution_time_ms] / @stats[:tasks_completed] if @stats[:tasks_completed] > 0
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Concurrency
5
+ # WorkerPool - Manages a pool of worker threads
6
+ class WorkerPool
7
+ attr_reader :stats, :size, :active_count, :idle_count
8
+
9
+ def initialize(config = {})
10
+ @size = config[:size] || Etc.nprocessors
11
+ @max_size = config[:max_size] || @size * 2
12
+ @min_size = config[:min_size] || [1, @size].min
13
+ @workers = []
14
+ @task_queue = Queue.new
15
+ @stats = {
16
+ tasks_processed: 0,
17
+ tasks_failed: 0,
18
+ tasks_queued: 0,
19
+ avg_processing_time_ms: 0,
20
+ total_processing_time_ms: 0,
21
+ worker_creations: 0,
22
+ worker_destructions: 0
23
+ }
24
+ @shutdown = false
25
+ @lock = Mutex.new
26
+ @cond = ConditionVariable.new
27
+ @idle_count = 0
28
+ @active_count = 0
29
+ @worker_id_counter = 0
30
+
31
+ # Pre-create workers
32
+ @size.times { create_worker }
33
+ end
34
+
35
+ def submit(task)
36
+ @task_queue << task
37
+ @stats[:tasks_queued] += 1
38
+ @cond.signal
39
+ task
40
+ end
41
+
42
+ def submit_block(&block)
43
+ submit(block)
44
+ end
45
+
46
+ def shutdown(wait = true)
47
+ @lock.synchronize do
48
+ @shutdown = true
49
+ @cond.broadcast
50
+
51
+ if wait
52
+ @workers.each(&:join)
53
+ end
54
+ end
55
+ end
56
+
57
+ def resize(new_size)
58
+ @lock.synchronize do
59
+ new_size = [new_size, @min_size].max
60
+ new_size = [new_size, @max_size].min
61
+
62
+ if new_size > @workers.size
63
+ (new_size - @workers.size).times { create_worker }
64
+ elsif new_size < @workers.size
65
+ (new_size - @workers.size).times do
66
+ worker = @workers.pop
67
+ worker&.kill
68
+ @stats[:worker_destructions] += 1
69
+ end
70
+ end
71
+
72
+ @size = new_size
73
+ end
74
+ end
75
+
76
+ def stats
77
+ @lock.synchronize do
78
+ @stats.merge({
79
+ size: @size,
80
+ workers: @workers.size,
81
+ active: @active_count,
82
+ idle: @idle_count,
83
+ queue_size: @task_queue.size,
84
+ max_size: @max_size,
85
+ min_size: @min_size,
86
+ shutdown: @shutdown
87
+ })
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def create_worker
94
+ @worker_id_counter += 1
95
+ worker_id = @worker_id_counter
96
+
97
+ worker = Thread.new do
98
+ worker_loop(worker_id)
99
+ end
100
+
101
+ @workers << worker
102
+ @stats[:worker_creations] += 1
103
+ worker
104
+ end
105
+
106
+ def worker_loop(worker_id)
107
+ while !@shutdown
108
+ task = nil
109
+
110
+ begin
111
+ task = @task_queue.pop
112
+ @lock.synchronize { @idle_count -= 1; @active_count += 1 }
113
+
114
+ start_time = Time.now
115
+
116
+ if task.is_a?(Proc)
117
+ task.call
118
+ elsif task.respond_to?(:call)
119
+ task.call
120
+ end
121
+
122
+ processing_time = (Time.now - start_time) * 1000
123
+ @lock.synchronize do
124
+ @stats[:tasks_processed] += 1
125
+ @stats[:total_processing_time_ms] += processing_time
126
+ @stats[:avg_processing_time_ms] = @stats[:total_processing_time_ms] / @stats[:tasks_processed]
127
+ end
128
+
129
+ rescue => e
130
+ @lock.synchronize do
131
+ @stats[:tasks_failed] += 1
132
+ end
133
+ ensure
134
+ @lock.synchronize do
135
+ @active_count -= 1
136
+ @idle_count += 1
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end