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,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require "thread"
5
+
6
+ module RubyDB
7
+ module Monitoring
8
+ # Events - Event system
9
+ class Events
10
+ attr_reader :stats
11
+
12
+ # Event types
13
+ EVENT_CONNECT = :connect
14
+ EVENT_DISCONNECT = :disconnect
15
+ EVENT_QUERY = :query
16
+ EVENT_TRANSACTION = :transaction
17
+ EVENT_ERROR = :error
18
+ EVENT_WARNING = :warning
19
+ EVENT_CHECKPOINT = :checkpoint
20
+ EVENT_RECOVERY = :recovery
21
+ EVENT_REPLICATION = :replication
22
+ EVENT_BACKUP = :backup
23
+ EVENT_RESTORE = :restore
24
+ EVENT_MIGRATION = :migration
25
+ EVENT_SCHEMA_CHANGE = :schema_change
26
+ EVENT_SYSTEM = :system
27
+
28
+ def initialize(config = {})
29
+ @config = config
30
+ @listeners = {}
31
+ @event_history = []
32
+ @max_history = config[:max_history] || 10000
33
+ @lock = Mutex.new
34
+ @stats = {
35
+ events_emitted: 0,
36
+ events_processed: 0,
37
+ listeners_registered: 0,
38
+ listeners_removed: 0,
39
+ errors: 0
40
+ }
41
+ @async = config[:async] != false
42
+ @queue = Queue.new if @async
43
+ @processor_thread = nil
44
+
45
+ start_processor if @async
46
+ end
47
+
48
+ def on(event_type, &block)
49
+ @lock.synchronize do
50
+ @listeners[event_type] ||= []
51
+ @listeners[event_type] << block
52
+ @stats[:listeners_registered] += 1
53
+ end
54
+ end
55
+
56
+ def off(event_type, block = nil)
57
+ @lock.synchronize do
58
+ return false unless @listeners[event_type]
59
+
60
+ if block
61
+ @listeners[event_type].delete(block)
62
+ else
63
+ @listeners[event_type].clear
64
+ end
65
+ @stats[:listeners_removed] += 1
66
+ true
67
+ end
68
+ end
69
+
70
+ def emit(event_type, data = {})
71
+ @lock.synchronize do
72
+ @stats[:events_emitted] += 1
73
+
74
+ event = {
75
+ id: generate_event_id,
76
+ type: event_type,
77
+ timestamp: Time.now.iso8601,
78
+ data: data,
79
+ pid: Process.pid,
80
+ thread: Thread.current.object_id
81
+ }
82
+
83
+ @event_history << event
84
+ if @event_history.size > @max_history
85
+ @event_history.shift
86
+ end
87
+
88
+ if @async
89
+ @queue << event
90
+ else
91
+ process_event(event)
92
+ end
93
+
94
+ event
95
+ end
96
+ end
97
+
98
+ def process_event(event)
99
+ @lock.synchronize do
100
+ @stats[:events_processed] += 1
101
+ listeners = @listeners[event[:type]] || []
102
+ listeners.each do |listener|
103
+ begin
104
+ listener.call(event)
105
+ rescue => e
106
+ @stats[:errors] += 1
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ def history(event_type = nil)
113
+ @lock.synchronize do
114
+ if event_type
115
+ @event_history.select { |e| e[:type] == event_type }
116
+ else
117
+ @event_history.dup
118
+ end
119
+ end
120
+ end
121
+
122
+ def clear_history
123
+ @lock.synchronize do
124
+ @event_history.clear
125
+ end
126
+ end
127
+
128
+ def stats
129
+ @lock.synchronize do
130
+ @stats.merge({
131
+ history_size: @event_history.size,
132
+ max_history: @max_history,
133
+ listeners_count: @listeners.size,
134
+ async: @async,
135
+ queue_size: @async ? @queue.size : 0
136
+ })
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ def generate_event_id
143
+ "evt_#{Time.now.to_i}_#{rand(10000)}"
144
+ end
145
+
146
+ def start_processor
147
+ @processor_thread = Thread.new do
148
+ while true
149
+ begin
150
+ event = @queue.pop
151
+ process_event(event)
152
+ rescue => e
153
+ @stats[:errors] += 1
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyDB
4
+ module Monitoring
5
+ # Health - Health checking
6
+ class Health
7
+ attr_reader :stats
8
+
9
+ # Health statuses
10
+ STATUS_HEALTHY = :healthy
11
+ STATUS_DEGRADED = :degraded
12
+ STATUS_UNHEALTHY = :unhealthy
13
+ STATUS_UNKNOWN = :unknown
14
+
15
+ def initialize(engine, config = {})
16
+ @engine = engine
17
+ @config = config
18
+ @checks = {}
19
+ @status = STATUS_UNKNOWN
20
+ @last_check = nil
21
+ @details = {}
22
+ @stats = {
23
+ checks_performed: 0,
24
+ checks_passed: 0,
25
+ checks_failed: 0,
26
+ status_changes: 0
27
+ }
28
+ @lock = Mutex.new
29
+ @check_interval = config[:check_interval] || 30
30
+ @check_thread = nil
31
+ @running = false
32
+
33
+ # Register default checks
34
+ register_default_checks
35
+
36
+ start_check_thread if config[:auto_check] != false
37
+ end
38
+
39
+ def register_check(name, &block)
40
+ @checks[name] = block
41
+ end
42
+
43
+ def check
44
+ @lock.synchronize do
45
+ @stats[:checks_performed] += 1
46
+ @last_check = Time.now
47
+ @details = {}
48
+
49
+ results = {}
50
+
51
+ @checks.each do |name, check|
52
+ begin
53
+ result = check.call(@engine)
54
+ results[name] = result
55
+ @details[name] = result
56
+
57
+ if result[:status] == STATUS_HEALTHY
58
+ @stats[:checks_passed] += 1
59
+ else
60
+ @stats[:checks_failed] += 1
61
+ end
62
+ rescue => e
63
+ results[name] = { status: STATUS_UNHEALTHY, error: e.message }
64
+ @details[name] = { status: STATUS_UNHEALTHY, error: e.message }
65
+ @stats[:checks_failed] += 1
66
+ end
67
+ end
68
+
69
+ # Determine overall status
70
+ old_status = @status
71
+ if results.values.all? { |r| r[:status] == STATUS_HEALTHY }
72
+ @status = STATUS_HEALTHY
73
+ elsif results.values.any? { |r| r[:status] == STATUS_UNHEALTHY }
74
+ @status = STATUS_UNHEALTHY
75
+ else
76
+ @status = STATUS_DEGRADED
77
+ end
78
+
79
+ if @status != old_status
80
+ @stats[:status_changes] += 1
81
+ end
82
+
83
+ {
84
+ status: @status,
85
+ timestamp: @last_check.iso8601,
86
+ checks: results
87
+ }
88
+ end
89
+ end
90
+
91
+ # Liveness answers whether the monitoring process is responsive. It
92
+ # deliberately does not touch storage or replication dependencies.
93
+ def liveness
94
+ {
95
+ status: STATUS_HEALTHY,
96
+ live: true,
97
+ timestamp: Time.now.iso8601
98
+ }
99
+ end
100
+
101
+ # Readiness answers whether the database can safely receive work. It
102
+ # runs the configured dependency checks and never reports ready when a
103
+ # check is unhealthy.
104
+ def readiness
105
+ result = check
106
+ {
107
+ status: result[:status],
108
+ ready: result[:status] == STATUS_HEALTHY,
109
+ timestamp: result[:timestamp],
110
+ checks: result[:checks]
111
+ }
112
+ end
113
+
114
+ def status
115
+ @status
116
+ end
117
+
118
+ def healthy?
119
+ @status == STATUS_HEALTHY
120
+ end
121
+
122
+ def degraded?
123
+ @status == STATUS_DEGRADED
124
+ end
125
+
126
+ def unhealthy?
127
+ @status == STATUS_UNHEALTHY
128
+ end
129
+
130
+ def details
131
+ @details.dup
132
+ end
133
+
134
+ def last_check
135
+ @last_check
136
+ end
137
+
138
+ def stats
139
+ @lock.synchronize do
140
+ @stats.merge({
141
+ status: @status,
142
+ checks_count: @checks.size,
143
+ last_check: @last_check&.iso8601,
144
+ running: @running,
145
+ check_interval: @check_interval
146
+ })
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ def register_default_checks
153
+ register_check("connection") do |engine|
154
+ if engine.connected?
155
+ { status: STATUS_HEALTHY, message: "Connected" }
156
+ else
157
+ { status: STATUS_UNHEALTHY, message: "Disconnected" }
158
+ end
159
+ end
160
+
161
+ register_check("storage") do |engine|
162
+ if engine.storage_available?
163
+ { status: STATUS_HEALTHY, message: "Storage available" }
164
+ else
165
+ { status: STATUS_UNHEALTHY, message: "Storage unavailable" }
166
+ end
167
+ end
168
+
169
+ register_check("memory") do |engine|
170
+ if engine.memory_usage < 0.9
171
+ { status: STATUS_HEALTHY, message: "Memory usage normal" }
172
+ elsif engine.memory_usage < 0.95
173
+ { status: STATUS_DEGRADED, message: "Memory usage high" }
174
+ else
175
+ { status: STATUS_UNHEALTHY, message: "Memory usage critical" }
176
+ end
177
+ end
178
+
179
+ register_check("replication") do |engine|
180
+ if engine.replication_healthy?
181
+ { status: STATUS_HEALTHY, message: "Replication healthy" }
182
+ else
183
+ { status: STATUS_UNHEALTHY, message: "Replication unhealthy" }
184
+ end
185
+ end
186
+
187
+ register_check("connections") do |engine|
188
+ usage = engine.connection_usage
189
+ if usage < 0.8
190
+ { status: STATUS_HEALTHY, message: "Connection usage normal" }
191
+ elsif usage < 0.95
192
+ { status: STATUS_DEGRADED, message: "Connection usage high" }
193
+ else
194
+ { status: STATUS_UNHEALTHY, message: "Connection usage critical" }
195
+ end
196
+ end
197
+
198
+ register_check("wal") do |engine|
199
+ if engine.wal_healthy?
200
+ { status: STATUS_HEALTHY, message: "WAL healthy" }
201
+ else
202
+ { status: STATUS_UNHEALTHY, message: "WAL unhealthy" }
203
+ end
204
+ end
205
+ end
206
+
207
+ def start_check_thread
208
+ @running = true
209
+ @check_thread = Thread.new do
210
+ while @running
211
+ sleep(@check_interval)
212
+ begin
213
+ check
214
+ rescue => e
215
+ # Continue running
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require "json"
5
+ require "fileutils"
6
+
7
+ module RubyDB
8
+ module Monitoring
9
+ # Logger - Structured logging
10
+ class Logger
11
+ attr_reader :stats
12
+
13
+ # Log levels
14
+ LEVEL_DEBUG = 0
15
+ LEVEL_INFO = 1
16
+ LEVEL_WARN = 2
17
+ LEVEL_ERROR = 3
18
+ LEVEL_FATAL = 4
19
+
20
+ LEVEL_NAMES = {
21
+ 0 => "DEBUG",
22
+ 1 => "INFO",
23
+ 2 => "WARN",
24
+ 3 => "ERROR",
25
+ 4 => "FATAL"
26
+ }
27
+
28
+ def initialize(config = {})
29
+ @config = config
30
+ @log_dir = config[:log_dir] || "log"
31
+ @log_file = config[:log_file] || "rubydb.log"
32
+ @level = config[:level] || LEVEL_INFO
33
+ @format = config[:format] || :json
34
+ @max_size = config[:max_size] || 10 * 1024 * 1024
35
+ @max_files = config[:max_files] || 10
36
+ @sync = config[:sync] || false
37
+ @stats = {
38
+ logs_written: 0,
39
+ logs_failed: 0,
40
+ log_rotations: 0,
41
+ buffer_size: 0
42
+ }
43
+ @buffer = []
44
+ @buffer_size = config[:buffer_size] || 100
45
+ @lock = Mutex.new
46
+ @file = nil
47
+
48
+ FileUtils.mkdir_p(@log_dir)
49
+ open_log_file
50
+ start_flush_thread if config[:async] != false
51
+ end
52
+
53
+ def debug(message, data = {})
54
+ log(LEVEL_DEBUG, message, data)
55
+ end
56
+
57
+ def info(message, data = {})
58
+ log(LEVEL_INFO, message, data)
59
+ end
60
+
61
+ def warn(message, data = {})
62
+ log(LEVEL_WARN, message, data)
63
+ end
64
+
65
+ def error(message, data = {})
66
+ log(LEVEL_ERROR, message, data)
67
+ end
68
+
69
+ def fatal(message, data = {})
70
+ log(LEVEL_FATAL, message, data)
71
+ end
72
+
73
+ def log(level, message, data = {})
74
+ return if level < @level
75
+
76
+ @lock.synchronize do
77
+ entry = {
78
+ timestamp: Time.now.iso8601,
79
+ level: LEVEL_NAMES[level],
80
+ message: message,
81
+ data: data,
82
+ pid: Process.pid,
83
+ thread: Thread.current.object_id
84
+ }
85
+
86
+ @buffer << entry
87
+ @stats[:buffer_size] = @buffer.size
88
+
89
+ if @buffer.size >= @buffer_size
90
+ flush
91
+ end
92
+ end
93
+ end
94
+
95
+ def flush
96
+ @lock.synchronize do
97
+ return if @buffer.empty?
98
+
99
+ begin
100
+ rotate_log if File.size(@log_path) > @max_size
101
+
102
+ File.open(@log_path, "a") do |file|
103
+ @buffer.each do |entry|
104
+ line = format_entry(entry)
105
+ file.puts(line)
106
+ end
107
+ file.flush if @sync
108
+ end
109
+
110
+ @stats[:logs_written] += @buffer.size
111
+ @buffer.clear
112
+ @stats[:buffer_size] = 0
113
+
114
+ rescue => e
115
+ @stats[:logs_failed] += 1
116
+ end
117
+ end
118
+ end
119
+
120
+ def close
121
+ flush
122
+ @file.close if @file
123
+ end
124
+
125
+ def level=(level)
126
+ @level = level
127
+ end
128
+
129
+ def level
130
+ @level
131
+ end
132
+
133
+ def stats
134
+ @lock.synchronize do
135
+ @stats.merge({
136
+ level: LEVEL_NAMES[@level],
137
+ format: @format,
138
+ buffer_size: @buffer.size,
139
+ log_path: @log_path,
140
+ sync: @sync
141
+ })
142
+ end
143
+ end
144
+
145
+ private
146
+
147
+ def open_log_file
148
+ @log_path = File.join(@log_dir, @log_file)
149
+ @file = File.open(@log_path, "a")
150
+ @file.sync = @sync
151
+ end
152
+
153
+ def rotate_log
154
+ @file.close if @file
155
+
156
+ timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
157
+ archive_path = "#{@log_path}.#{timestamp}"
158
+ FileUtils.mv(@log_path, archive_path)
159
+
160
+ # Remove old archives
161
+ archives = Dir.glob("#{@log_path}.*").sort
162
+ while archives.size > @max_files - 1
163
+ File.delete(archives.shift)
164
+ end
165
+
166
+ open_log_file
167
+ @stats[:log_rotations] += 1
168
+ end
169
+
170
+ def format_entry(entry)
171
+ case @format
172
+ when :json
173
+ JSON.generate(entry)
174
+ else
175
+ "[#{entry[:timestamp]}] #{entry[:level]} [#{entry[:pid]}]: #{entry[:message]}"
176
+ end
177
+ end
178
+
179
+ def start_flush_thread
180
+ Thread.new do
181
+ loop do
182
+ sleep(1)
183
+ begin
184
+ flush
185
+ rescue => e
186
+ # Continue running
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end