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,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module RubyDB
6
+ module Server
7
+ # Lifecycle - Manages server lifecycle events
8
+ class Lifecycle
9
+ attr_reader :state, :stats
10
+
11
+ # Lifecycle states
12
+ STATE_INIT = :init
13
+ STATE_STARTING = :starting
14
+ STATE_RUNNING = :running
15
+ STATE_STOPPING = :stopping
16
+ STATE_STOPPED = :stopped
17
+ STATE_FAILED = :failed
18
+ STATE_RESTARTING = :restarting
19
+
20
+ def initialize(server, config = {})
21
+ @server = server
22
+ @config = config
23
+ @state = STATE_INIT
24
+ @health_check_interval = config[:health_check_interval] || 30
25
+ @graceful_timeout = config[:graceful_timeout] || 30
26
+ @max_restart_attempts = config[:max_restart_attempts] || 3
27
+ @restart_attempts = 0
28
+ @stats = {
29
+ state_changes: 0,
30
+ start_count: 0,
31
+ stop_count: 0,
32
+ restart_count: 0,
33
+ failure_count: 0,
34
+ uptime_seconds: 0,
35
+ last_state_change: nil,
36
+ health_checks: 0,
37
+ health_check_failures: 0
38
+ }
39
+ @lock = Mutex.new
40
+ @health_thread = nil
41
+ @running = false
42
+ @shutdown = false
43
+ @started_at = nil
44
+
45
+ # Register signal handlers
46
+ register_signal_handlers
47
+ end
48
+
49
+ def start
50
+ @lock.synchronize do
51
+ return if @state == STATE_RUNNING
52
+
53
+ change_state(STATE_STARTING)
54
+ @started_at = Time.now
55
+
56
+ begin
57
+ # Start the server
58
+ @server.start
59
+ change_state(STATE_RUNNING)
60
+ @stats[:start_count] += 1
61
+ @restart_attempts = 0
62
+
63
+ # Start health check
64
+ start_health_check
65
+
66
+ true
67
+ rescue => e
68
+ change_state(STATE_FAILED)
69
+ @stats[:failure_count] += 1
70
+ raise
71
+ end
72
+ end
73
+ end
74
+
75
+ def stop(graceful = true)
76
+ @lock.synchronize do
77
+ return if @state == STATE_STOPPED
78
+
79
+ change_state(STATE_STOPPING)
80
+
81
+ begin
82
+ if graceful
83
+ # Graceful shutdown
84
+ @server.stop
85
+ else
86
+ # Force shutdown
87
+ @server.stop
88
+ end
89
+
90
+ change_state(STATE_STOPPED)
91
+ @stats[:stop_count] += 1
92
+ @running = false
93
+
94
+ true
95
+ rescue => e
96
+ change_state(STATE_FAILED)
97
+ @stats[:failure_count] += 1
98
+ raise
99
+ end
100
+ end
101
+ end
102
+
103
+ def restart(graceful = true)
104
+ @lock.synchronize do
105
+ change_state(STATE_RESTARTING)
106
+ @stats[:restart_count] += 1
107
+
108
+ begin
109
+ stop(graceful)
110
+ start
111
+ true
112
+ rescue => e
113
+ change_state(STATE_FAILED)
114
+ @stats[:failure_count] += 1
115
+ false
116
+ end
117
+ end
118
+ end
119
+
120
+ def health_check
121
+ @lock.synchronize do
122
+ @stats[:health_checks] += 1
123
+
124
+ begin
125
+ # Check if server is running
126
+ unless @server.running?
127
+ @stats[:health_check_failures] += 1
128
+ return false
129
+ end
130
+
131
+ # Check connection pool
132
+ pool = @server.connection_pool
133
+ if pool && pool.total_connections > 0
134
+ if pool.respond_to?(:healthy?) && !pool.healthy?
135
+ @stats[:health_check_failures] += 1
136
+ return false
137
+ end
138
+ end
139
+
140
+ true
141
+ rescue => e
142
+ @stats[:health_check_failures] += 1
143
+ false
144
+ end
145
+ end
146
+ end
147
+
148
+ def running?
149
+ @state == STATE_RUNNING
150
+ end
151
+
152
+ def stats
153
+ @lock.synchronize do
154
+ elapsed = @started_at ? (Time.now - @started_at).to_i : 0
155
+ @stats[:uptime_seconds] = elapsed
156
+
157
+ @stats.merge({
158
+ state: @state,
159
+ running: running?,
160
+ started_at: @started_at&.iso8601,
161
+ restart_attempts: @restart_attempts,
162
+ max_restart_attempts: @max_restart_attempts,
163
+ graceful_timeout: @graceful_timeout
164
+ })
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ def change_state(new_state)
171
+ @state = new_state
172
+ @stats[:state_changes] += 1
173
+ @stats[:last_state_change] = Time.now
174
+ end
175
+
176
+ def start_health_check
177
+ return if @health_thread
178
+
179
+ @running = true
180
+ @health_thread = Thread.new do
181
+ while @running && !@shutdown
182
+ sleep(@health_check_interval)
183
+
184
+ unless health_check
185
+ # Health check failed
186
+ @stats[:health_check_failures] += 1
187
+
188
+ if @restart_attempts < @max_restart_attempts
189
+ @restart_attempts += 1
190
+ restart
191
+ else
192
+ change_state(STATE_FAILED)
193
+ @running = false
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def register_signal_handlers
201
+ # SIGINT - Graceful shutdown
202
+ trap("INT") do
203
+ puts "\nReceived SIGINT, shutting down gracefully..."
204
+ @shutdown = true
205
+ stop(true)
206
+ end
207
+
208
+ # SIGTERM - Graceful shutdown
209
+ trap("TERM") do
210
+ puts "Received SIGTERM, shutting down gracefully..."
211
+ @shutdown = true
212
+ stop(true)
213
+ end
214
+
215
+ # SIGHUP - Restart
216
+ begin
217
+ trap("HUP") do
218
+ puts "Received SIGHUP, restarting..."
219
+ restart
220
+ end
221
+ rescue ArgumentError
222
+ # Windows does not expose SIGHUP; restart remains available via
223
+ # the explicit server API.
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "openssl"
5
+
6
+ module RubyDB
7
+ module Server
8
+ # Listener - Accepts incoming connections
9
+ class Listener
10
+ attr_reader :stats
11
+
12
+ def initialize(config, connection_pool, worker_pool)
13
+ @config = config
14
+ @connection_pool = connection_pool
15
+ @worker_pool = worker_pool
16
+ @socket = nil
17
+ @running = false
18
+ @accept_thread = nil
19
+ @ssl_enabled = false
20
+ @stats = {
21
+ connections_accepted: 0,
22
+ connections_rejected: 0,
23
+ accept_errors: 0,
24
+ total_accept_time_ms: 0,
25
+ avg_accept_time_ms: 0
26
+ }
27
+ @lock = Mutex.new
28
+ end
29
+
30
+ def start
31
+ @lock.synchronize do
32
+ return if @running
33
+
34
+ begin
35
+ tcp_socket = TCPServer.new(@config[:host], @config[:port])
36
+ tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
37
+
38
+ ssl = @config[:ssl] || {}
39
+ if ssl == true || ssl[:enabled] || ssl["enabled"]
40
+ ssl = {} if ssl == true
41
+ context = OpenSSL::SSL::SSLContext.new
42
+ context.cert = OpenSSL::X509::Certificate.new(File.binread(ssl[:cert_file] || ssl["cert_file"]))
43
+ context.key = OpenSSL::PKey.read(File.binread(ssl[:key_file] || ssl["key_file"]))
44
+ context.ca_file = ssl[:ca_file] || ssl["ca_file"] if ssl[:ca_file] || ssl["ca_file"]
45
+ context.verify_mode = (ssl[:verify_peer] || ssl["verify_peer"]) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
46
+ context.min_version = (ssl[:min_version] || ssl["min_version"] || :TLS1_2)
47
+ context.max_version = (ssl[:max_version] || ssl["max_version"]) if ssl[:max_version] || ssl["max_version"]
48
+ @socket = OpenSSL::SSL::SSLServer.new(tcp_socket, context)
49
+ @ssl_enabled = true
50
+ else
51
+ @socket = tcp_socket
52
+ end
53
+
54
+ @running = true
55
+ @accept_thread = Thread.new { accept_loop }
56
+
57
+ true
58
+ rescue => e
59
+ raise ServerError, "Failed to start listener: #{e.message}"
60
+ end
61
+ end
62
+ end
63
+
64
+ def stop
65
+ @lock.synchronize do
66
+ return unless @running
67
+
68
+ @running = false
69
+
70
+ if @accept_thread
71
+ @accept_thread.kill
72
+ @accept_thread = nil
73
+ end
74
+
75
+ if @socket
76
+ @socket.close rescue nil
77
+ @socket = nil
78
+ end
79
+ @ssl_enabled = false
80
+
81
+ true
82
+ end
83
+ end
84
+
85
+ def running?
86
+ @running
87
+ end
88
+
89
+ private
90
+
91
+ def accept_loop
92
+ while @running
93
+ begin
94
+ start_time = Time.now
95
+
96
+ # SSLServer performs the TLS handshake while accepting. Keep that
97
+ # operation blocking so a non-blocking handshake is not discarded
98
+ # and left waiting forever on the client.
99
+ client = if @ssl_enabled
100
+ @socket.accept
101
+ else
102
+ candidate = @socket.accept_nonblock(exception: false)
103
+ candidate.is_a?(IO) ? candidate : nil
104
+ end
105
+
106
+ if client
107
+ # Check if we have capacity
108
+ if @connection_pool.can_accept?
109
+ @connection_pool.add_connection(client)
110
+ @stats[:connections_accepted] += 1
111
+ else
112
+ client.close
113
+ @stats[:connections_rejected] += 1
114
+ end
115
+
116
+ elapsed_ms = (Time.now - start_time) * 1000
117
+ @stats[:total_accept_time_ms] += elapsed_ms
118
+ @stats[:avg_accept_time_ms] = @stats[:total_accept_time_ms] / @stats[:connections_accepted] if @stats[:connections_accepted] > 0
119
+ end
120
+
121
+ # Small sleep to prevent CPU spinning
122
+ sleep(0.01)
123
+
124
+ rescue IO::WaitReadable, Errno::EAGAIN, Errno::EWOULDBLOCK
125
+ next if @ssl_enabled
126
+ # No connection ready, continue
127
+ sleep(0.01)
128
+ rescue => e
129
+ @stats[:accept_errors] += 1
130
+ sleep(0.1) # Back off on error
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
4
+
5
+ module RubyDB
6
+ module Server
7
+ # RequestHandler - Handles and routes requests
8
+ class RequestHandler
9
+ attr_reader :stats
10
+
11
+ def initialize(engine, transaction_manager, config = {})
12
+ @engine = engine
13
+ @transaction_manager = transaction_manager
14
+ @config = config
15
+ @stats = {
16
+ requests_processed: 0,
17
+ requests_failed: 0,
18
+ query_requests: 0,
19
+ prepare_requests: 0,
20
+ execute_requests: 0,
21
+ transaction_requests: 0,
22
+ other_requests: 0,
23
+ total_processing_time_ms: 0,
24
+ avg_processing_time_ms: 0
25
+ }
26
+ @lock = Monitor.new
27
+ @handlers = {}
28
+ @middleware = []
29
+ @query_handlers = {}
30
+ @prepared_statements = {}
31
+ @statement_counter = 0
32
+ @health = config[:health] || RubyDB::Monitoring::Health.new(engine, auto_check: false)
33
+ @metrics = config[:metrics] || RubyDB::Monitoring::Metrics.new(auto_flush: false)
34
+
35
+ # Register default handlers
36
+ register_default_handlers
37
+ end
38
+
39
+ def handle(request)
40
+ @lock.synchronize do
41
+ start_time = Time.now
42
+
43
+ begin
44
+ # Apply middleware
45
+ request = apply_middleware(request)
46
+
47
+ # Route request
48
+ result = route_request(request)
49
+
50
+ @stats[:requests_processed] += 1
51
+
52
+ elapsed_ms = (Time.now - start_time) * 1000
53
+ @stats[:total_processing_time_ms] += elapsed_ms
54
+ @stats[:avg_processing_time_ms] = @stats[:total_processing_time_ms] / @stats[:requests_processed] if @stats[:requests_processed] > 0
55
+
56
+ result
57
+
58
+ rescue => e
59
+ @stats[:requests_failed] += 1
60
+ error_response(e.message)
61
+ end
62
+ end
63
+ end
64
+
65
+ def register_handler(type, handler = nil, &block)
66
+ @handlers[type] = handler || block
67
+ end
68
+
69
+ def register_middleware(middleware)
70
+ @middleware << middleware
71
+ end
72
+
73
+ def register_query_handler(pattern, handler)
74
+ @query_handlers[pattern] = handler
75
+ end
76
+
77
+ def prepare_statement(sql)
78
+ @lock.synchronize do
79
+ stmt_id = "stmt_#{Time.now.to_i}_#{@statement_counter}"
80
+ @statement_counter += 1
81
+
82
+ @prepared_statements[stmt_id] = {
83
+ sql: sql,
84
+ created_at: Time.now,
85
+ params: extract_params(sql)
86
+ }
87
+
88
+ stmt_id
89
+ end
90
+ end
91
+
92
+ def execute_statement(stmt_id, params = [])
93
+ @lock.synchronize do
94
+ stmt = @prepared_statements[stmt_id]
95
+ return error_response("Statement not found") unless stmt
96
+
97
+ # Execute the prepared statement
98
+ execute_sql(stmt[:sql], params)
99
+ end
100
+ end
101
+
102
+ def close_statement(stmt_id)
103
+ @lock.synchronize do
104
+ @prepared_statements.delete(stmt_id)
105
+ success_response({ closed: true })
106
+ end
107
+ end
108
+
109
+ def stats
110
+ @lock.synchronize do
111
+ @stats.merge({
112
+ prepared_statements: @prepared_statements.size,
113
+ middleware_count: @middleware.size,
114
+ handler_count: @handlers.size
115
+ })
116
+ end
117
+ end
118
+
119
+ private
120
+
121
+ def register_default_handlers
122
+ # Query handler
123
+ register_handler(:query) do |request|
124
+ handle_query(request)
125
+ end
126
+
127
+ # Prepare handler
128
+ register_handler(:prepare) do |request|
129
+ handle_prepare(request)
130
+ end
131
+
132
+ # Execute handler
133
+ register_handler(:execute) do |request|
134
+ handle_execute(request)
135
+ end
136
+
137
+ # Transaction handlers
138
+ register_handler(:begin) do |request|
139
+ handle_begin(request)
140
+ end
141
+
142
+ register_handler(:commit) do |request|
143
+ handle_commit(request)
144
+ end
145
+
146
+ register_handler(:rollback) do |request|
147
+ handle_rollback(request)
148
+ end
149
+
150
+ # Ping handler
151
+ register_handler(:ping) do |request|
152
+ handle_ping(request)
153
+ end
154
+
155
+ register_handler(:liveness) { |_request| success_response(@health.liveness) }
156
+ register_handler(:readiness) { |_request| success_response(@health.readiness) }
157
+ register_handler(:health) { |_request| success_response(@health.check) }
158
+ register_handler(:metrics) { |_request| success_response({ format: "prometheus", body: @metrics.to_prometheus }) }
159
+ end
160
+
161
+ def route_request(request)
162
+ type = request[:type]
163
+
164
+ handler = @handlers[type]
165
+ if handler
166
+ handler.call(request)
167
+ else
168
+ error_response("No handler for request type: #{type}")
169
+ end
170
+ end
171
+
172
+ def apply_middleware(request)
173
+ @middleware.each do |mw|
174
+ request = mw.call(request)
175
+ break if request.nil?
176
+ end
177
+ request
178
+ end
179
+
180
+ def handle_query(request)
181
+ @stats[:query_requests] += 1
182
+ sql = request[:sql]
183
+ params = request[:params] || []
184
+
185
+ # Check for prepared statement pattern
186
+ @query_handlers.each do |pattern, handler|
187
+ if sql.match?(pattern)
188
+ return handler.call(sql, params)
189
+ end
190
+ end
191
+
192
+ # Execute SQL
193
+ execute_sql(sql, params, deadline_at: request[:deadline_at])
194
+ end
195
+
196
+ def handle_prepare(request)
197
+ @stats[:prepare_requests] += 1
198
+ sql = request[:sql]
199
+ stmt_id = prepare_statement(sql)
200
+
201
+ success_response({
202
+ statement_id: stmt_id,
203
+ sql: sql,
204
+ params: @prepared_statements[stmt_id][:params]
205
+ })
206
+ end
207
+
208
+ def handle_execute(request)
209
+ @stats[:execute_requests] += 1
210
+ stmt_id = request[:statement_id]
211
+ params = request[:params] || []
212
+
213
+ execute_statement(stmt_id, params)
214
+ end
215
+
216
+ def handle_begin(request)
217
+ @stats[:transaction_requests] += 1
218
+ @transaction_manager.begin_transaction
219
+
220
+ success_response({
221
+ transaction_id: @transaction_manager.current_transaction&.id
222
+ })
223
+ end
224
+
225
+ def handle_commit(request)
226
+ @stats[:transaction_requests] += 1
227
+ @transaction_manager.commit_transaction
228
+
229
+ success_response({ committed: true })
230
+ end
231
+
232
+ def handle_rollback(request)
233
+ @stats[:transaction_requests] += 1
234
+ @transaction_manager.rollback_transaction
235
+
236
+ success_response({ rolled_back: true })
237
+ end
238
+
239
+ def handle_ping(request)
240
+ @stats[:other_requests] += 1
241
+ success_response({ pong: true, timestamp: Time.now.iso8601 })
242
+ end
243
+
244
+ def execute_sql(sql, params, deadline_at: nil)
245
+ tokens = RubyDB::SQL::Lexer.new(sql).tokenize
246
+ statements = RubyDB::SQL::Parser.new(tokens).parse
247
+ results = statements.map do |statement|
248
+ plan = RubyDB::Execution::Planner.new(@engine).plan(statement)
249
+ RubyDB::Execution::Executor.new(@engine, deadline_at: deadline_at).execute(plan)
250
+ end
251
+ results.size == 1 ? results.first : results
252
+ end
253
+
254
+ def extract_params(sql)
255
+ # Extract parameter placeholders from SQL
256
+ sql.scan(/\$(\d+)/).flatten.map(&:to_i).uniq.sort
257
+ end
258
+
259
+ def success_response(data)
260
+ {
261
+ success: true,
262
+ data: data,
263
+ timestamp: Time.now.iso8601
264
+ }
265
+ end
266
+
267
+ def error_response(message)
268
+ {
269
+ success: false,
270
+ error: message,
271
+ timestamp: Time.now.iso8601
272
+ }
273
+ end
274
+ end
275
+ end
276
+ end