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,13 @@
1
+ # First query
2
+
3
+ RubyDB executes the documented SQL subset through its lexer, parser, planner,
4
+ and executor:
5
+
6
+ ```ruby
7
+ rows = engine.execute("SELECT id, email FROM accounts WHERE id = 1 ORDER BY id LIMIT 10")
8
+ puts rows.inspect
9
+ ```
10
+
11
+ Use the Rails connection or client binding APIs for external values. Do not
12
+ interpolate untrusted input into SQL. See [SQL compatibility](../sql/compatibility.md)
13
+ for supported statements and explicit boundaries.
@@ -0,0 +1,19 @@
1
+ # Installation
2
+
3
+ RubyDB requires Ruby 3.3 or newer in the current support policy.
4
+
5
+ ```sh
6
+ gem install rubydb --pre
7
+ ```
8
+
9
+ From a checkout:
10
+
11
+ ```sh
12
+ bundle install
13
+ bundle exec rspec
14
+ ```
15
+
16
+ For Rails, install the adapter from `adapters/activerecord` and use the
17
+ configuration documented in `docs/rails/database-yml.md`. Pin RubyDB and Ruby
18
+ versions in production and test the exact gem on a restored staging backup
19
+ before deployment.
@@ -0,0 +1,300 @@
1
+ # From local development to production
2
+
3
+ This guide is for a beginner starting with a Ruby or Rails application. It
4
+ shows two valid deployment choices:
5
+
6
+ 1. develop with RubyDB locally and deploy with PostgreSQL; or
7
+ 2. develop with RubyDB locally and keep RubyDB in production.
8
+
9
+ The application code can be similar in both cases, but the database is not
10
+ interchangeable by copying files. Choose the production database before
11
+ launch, run the application’s migrations and query tests against it, and make
12
+ backups before importing important data.
13
+
14
+ ## Which path should I choose?
15
+
16
+ | Goal | Local database | Production database | Recommended when |
17
+ | --- | --- | --- | --- |
18
+ | Learn/build quickly | RubyDB embedded | PostgreSQL | You want the broadest hosting and Rails ecosystem |
19
+ | Keep one Ruby-native database | RubyDB embedded | RubyDB server | Your SQL/workload fits RubyDB and you can operate the server |
20
+ | Prototype only | RubyDB embedded | RubyDB embedded | One process owns the path and data is non-critical |
21
+
22
+ For a public or business-critical application, PostgreSQL is the safer default
23
+ until RubyDB has been validated against your complete workload, backup/restore
24
+ process, concurrency, and failover requirements. RubyDB is not a complete
25
+ PostgreSQL or SQLite replacement.
26
+
27
+ ## Part 1: local Rails development with RubyDB
28
+
29
+ ### 1. Add the gems
30
+
31
+ In the Rails application `Gemfile`:
32
+
33
+ ```ruby
34
+ gem "rubydb"
35
+ gem "rubydb-activerecord"
36
+ gem "pg" # Keep this if PostgreSQL is a possible production target.
37
+ ```
38
+
39
+ Run:
40
+
41
+ ```sh
42
+ bundle install
43
+ ```
44
+
45
+ ### 2. Configure development and test
46
+
47
+ Use an embedded path for local development. Only the Rails process should own
48
+ each path:
49
+
50
+ ```yaml
51
+ default: &default
52
+ adapter: rubydb
53
+ embedded: true
54
+ database: <%= Rails.root.join("tmp/rubydb_development.rdb") %>
55
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
56
+
57
+ development:
58
+ <<: *default
59
+
60
+ test:
61
+ <<: *default
62
+ database: <%= Rails.root.join("tmp/rubydb_test.rdb") %>
63
+ ```
64
+
65
+ Use separate development and test paths. Do not put the database under a
66
+ source-controlled directory. Do not let a web server, job worker, and console
67
+ open the same embedded path at the same time.
68
+
69
+ ### 3. Create the schema and run the app
70
+
71
+ ```sh
72
+ bin/rails db:create
73
+ bin/rails db:migrate
74
+ bin/rails db:seed # only if your seed data is safe to recreate
75
+ bin/rails test
76
+ bin/rails server
77
+ ```
78
+
79
+ Exercise real application flows: sign-up, login, CRUD, joins, eager loading,
80
+ background jobs, file metadata, transactions, and error handling. Do not test
81
+ only that the server boots.
82
+
83
+ ### 4. Use RubyDB in a regular Ruby app
84
+
85
+ Local single-process code can use the embedded engine:
86
+
87
+ ```ruby
88
+ require "rubydb"
89
+
90
+ engine = RubyDB::Storage::Engine.new("tmp/app.rdb")
91
+ begin
92
+ engine.execute("CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY, name TEXT)")
93
+ engine.execute("INSERT INTO events (name) VALUES (?)", ["boot"])
94
+ p engine.execute("SELECT * FROM events")
95
+ ensure
96
+ engine.close
97
+ end
98
+ ```
99
+
100
+ For multiple processes, use the client/server connection described below.
101
+
102
+ ## Part 2A: deploy the same app on PostgreSQL
103
+
104
+ This path keeps RubyDB for local development and uses PostgreSQL in production.
105
+ It is a database migration, not a file copy.
106
+
107
+ ### 1. Configure the production Rails block
108
+
109
+ ```yaml
110
+ production:
111
+ adapter: postgresql
112
+ url: <%= ENV.fetch("DATABASE_URL") %>
113
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
114
+ ```
115
+
116
+ The hosting provider supplies a PostgreSQL URL such as:
117
+
118
+ ```text
119
+ postgresql://app_user:password@postgres.example.com:5432/my_app
120
+ ```
121
+
122
+ Keep `DATABASE_URL` in the provider’s secret settings. Never commit it.
123
+
124
+ ### 2. Review the schema and SQL
125
+
126
+ Before switching, review RubyDB-specific behavior:
127
+
128
+ * integer primary-key/autoincrement behavior;
129
+ * `BOOLEAN`, `JSON`, `UUID`, dates, timestamps, and decimals;
130
+ * indexes, foreign keys, check constraints, and defaults;
131
+ * `NULL`, ordering, grouping, joins, upserts, and functions;
132
+ * migrations that alter populated tables; and
133
+ * raw SQL, extensions, pragmas, and database-specific functions.
134
+
135
+ Run `db:schema:dump` and inspect the generated schema. Do not assume every
136
+ RubyDB schema statement is valid PostgreSQL syntax. Fix migrations or use
137
+ adapter-specific migrations deliberately and document the difference.
138
+
139
+ ### 3. Transfer data safely
140
+
141
+ RubyDB `.rdb` files cannot be opened by PostgreSQL. A safe small-dataset flow is:
142
+
143
+ 1. stop writes to the RubyDB application;
144
+ 2. create a verified RubyDB backup;
145
+ 3. create the PostgreSQL database and run reviewed migrations;
146
+ 4. export each table in a deterministic order, including parent tables first;
147
+ 5. transform types and IDs explicitly;
148
+ 6. import into PostgreSQL using bound/escaped data or PostgreSQL `COPY`;
149
+ 7. compare row counts, checksums, foreign keys, indexes, and business totals;
150
+ 8. run the application test and smoke suite against PostgreSQL; and
151
+ 9. switch traffic only after the verification report is approved.
152
+
153
+ For large or live data, use a purpose-built migration process with checkpoints,
154
+ retries, idempotency, and a cutover plan. Do not write a one-off script that
155
+ silently skips a row or converts an unknown type to text.
156
+
157
+ ### 4. Test production configuration before cutover
158
+
159
+ ```sh
160
+ RAILS_ENV=production DATABASE_URL='postgresql://...' bin/rails db:migrate
161
+ RAILS_ENV=production DATABASE_URL='postgresql://...' bin/rails db:seed
162
+ RAILS_ENV=production DATABASE_URL='postgresql://...' bin/rails test
163
+ ```
164
+
165
+ Run this against a restored staging database, not the only production copy.
166
+ Keep the RubyDB backup until PostgreSQL row counts and application behavior
167
+ have been accepted.
168
+
169
+ ## Part 2B: deploy RubyDB in production
170
+
171
+ This path runs one managed RubyDB server and connects Rails/Ruby processes to it
172
+ over the RubyDB protocol. The application never opens the server’s data file.
173
+
174
+ ### 1. Provision a database host
175
+
176
+ Install a pinned RubyDB release, create a dedicated service account, and attach
177
+ persistent storage. The data directory must survive process restarts and
178
+ deploys. Keep backups on a separate system or failure domain.
179
+
180
+ ```sh
181
+ gem install rubydb -v 0.1.0
182
+ install -d -o rubydb -g rubydb -m 0700 /var/lib/rubydb/data
183
+ install -d -o rubydb -g rubydb -m 0750 /var/log/rubydb
184
+ ```
185
+
186
+ Use the complete setup in the [production operations guide](../operations/production-guide.md)
187
+ for service supervision, TLS, authentication, resource limits, backups, and
188
+ monitoring.
189
+
190
+ ### 2. Start the RubyDB server
191
+
192
+ Use a reviewed production configuration with WAL, authentication, TLS, and
193
+ bounded resources:
194
+
195
+ ```sh
196
+ rubydb --config /etc/rubydb/production.yml --env production start
197
+ ```
198
+
199
+ Keep port `7432` on a private network. Verify the server before connecting the
200
+ application:
201
+
202
+ ```sh
203
+ rubydb --config /etc/rubydb/production.yml --env production status --json
204
+ rubydb --config /etc/rubydb/production.yml --env production doctor --json
205
+ ```
206
+
207
+ ### 3. Put one RubyDB URL in the application environment
208
+
209
+ RubyDB supports its own URL format. `rubydbs` enables TLS:
210
+
211
+ ```text
212
+ RUBYDB_URL=rubydbs://app_user:URL_ENCODED_PASSWORD@db.internal.example:7432/my_app?verify_peer=true&ca_file=%2Fetc%2Frubydb%2Fca.crt
213
+ ```
214
+
215
+ Special characters in usernames and passwords must be percent-encoded. Store
216
+ the complete value in a secret manager. Do not print it during deploys.
217
+
218
+ ### 4. Configure Rails
219
+
220
+ ```yaml
221
+ production:
222
+ adapter: rubydb
223
+ embedded: false
224
+ url: <%= ENV.fetch("RUBYDB_URL") %>
225
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
226
+ ```
227
+
228
+ Run the migration once from a controlled release job:
229
+
230
+ ```sh
231
+ RAILS_ENV=production bundle exec rails db:migrate
232
+ RAILS_ENV=production bundle exec rails runner 'puts User.count'
233
+ ```
234
+
235
+ Do not run migrations simultaneously from every web process. Confirm that the
236
+ Rails pool fits below the RubyDB server connection limit with room for workers,
237
+ monitoring, and administration.
238
+
239
+ ### 5. Configure regular Ruby
240
+
241
+ ```ruby
242
+ require "rubydb"
243
+
244
+ client = RubyDB::Client::Client.new(url: ENV.fetch("RUBYDB_URL"))
245
+ begin
246
+ result = client.query("SELECT 1")
247
+ puts result.to_hash
248
+ ensure
249
+ client.disconnect
250
+ end
251
+ ```
252
+
253
+ Use idempotency keys for retried writes. A timeout or broken connection does
254
+ not prove that a write was rolled back; check the operation outcome before
255
+ retrying a non-idempotent request.
256
+
257
+ ### 6. Perform a production smoke test
258
+
259
+ Before routing users:
260
+
261
+ ```sh
262
+ RAILS_ENV=production bundle exec rails db:migrate:status
263
+ RAILS_ENV=production bundle exec rails runner 'puts User.limit(1).to_a.inspect'
264
+ rubydb --config /etc/rubydb/production.yml --env production doctor --json
265
+ ```
266
+
267
+ Then test one authenticated read, one create/update/delete transaction, one
268
+ background job, one backup, and one restore into a separate directory. Confirm
269
+ logs, metrics, disk alerts, WAL/checkpoint state, and rollback ownership.
270
+
271
+ ## Render-style cloud deployment
272
+
273
+ On a platform such as Render, use a private service for the RubyDB server and a
274
+ web service for Rails. Attach a persistent disk to the RubyDB service and set
275
+ `RUBYDB_URL` on the Rails service. Keep both services in the same region and
276
+ use the private hostname. A platform’s default filesystem is often ephemeral;
277
+ verify the provider’s storage behavior before using it for database files.
278
+
279
+ A single persistent disk normally means one RubyDB primary instance. Do not
280
+ assume that increasing the web-service instance count creates database
281
+ high availability. Maintain external backups and validate failover separately.
282
+
283
+ ## Final beginner checklist
284
+
285
+ Before calling the app production-ready, confirm:
286
+
287
+ * the production database choice is written down;
288
+ * all application SQL and migrations pass against that database;
289
+ * credentials are environment/secret-manager values, not source files;
290
+ * RubyDB uses server mode when more than one process connects;
291
+ * TLS, authentication, private networking, and least privilege are enabled;
292
+ * a verified backup can be restored on another directory or host;
293
+ * monitoring and alerts are visible to an on-call owner;
294
+ * connection, lock, request, and shutdown timeouts are bounded; and
295
+ * rollback, data migration, and incident procedures have been rehearsed.
296
+
297
+ For deeper details, continue with [Rails compatibility](../rails/compatibility-guide.md),
298
+ [production operations](../operations/production-guide.md),
299
+ [troubleshooting](../troubleshooting.md), and [debugging](../debugging.md).
300
+
@@ -0,0 +1,17 @@
1
+ # Quick start
2
+
3
+ Create a local database in one owning Ruby process:
4
+
5
+ ```ruby
6
+ require "rubydb"
7
+
8
+ engine = RubyDB::Storage::Engine.new("tmp/quickstart.rdb")
9
+ engine.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
10
+ engine.execute("INSERT INTO users (id, name) VALUES (1, 'Aldane')")
11
+ p engine.execute("SELECT * FROM users")
12
+ engine.close
13
+ ```
14
+
15
+ For a full walkthrough see [first database](first-database.md) and
16
+ [first query](first-query.md). For multiple processes, use the server/client
17
+ topology and do not share an embedded path.
@@ -0,0 +1,16 @@
1
+ # Rails quick start
2
+
3
+ The runnable application under `examples/rails_app` demonstrates a real Rails
4
+ migration, model, query, and server:
5
+
6
+ ```sh
7
+ cd examples/rails_app
8
+ bundle install
9
+ bundle exec ruby bin/rails db:migrate
10
+ bundle exec ruby bin/rails server -b 127.0.0.1 -p 3001
11
+ ```
12
+
13
+ Use the ActiveRecord adapter only within its tested feature surface. For a
14
+ production Rails deployment, use server mode for multiple processes, protect
15
+ TLS/authentication secrets, back up before migrations, and run the exact Rails
16
+ matrix and populated-schema checks described in `docs/rails/production.md`.
@@ -0,0 +1,93 @@
1
+ # Remaining production work
2
+
3
+ Completed checkpoints: exclusive embedded engine ownership, duplicate-open
4
+ rejection, release after process exit, initialization failure cleanup, fail-closed
5
+ metadata and recovery startup, atomic metadata replacement, and deterministic
6
+ background-maintenance shutdown.
7
+
8
+ The following work remains open; passing the regression suite does not certify
9
+ these capabilities.
10
+
11
+ 1. Server concurrency: cancellation and sustained load with latency percentiles.
12
+ Client deadlines are checked before execution and propagated into the query
13
+ executor, which checks during long read phases. Engine transaction state is
14
+ now scoped per client connection thread, and concurrent commit/rollback
15
+ behavior is covered. Multi-process durability is covered; wire-level
16
+ cancellation is now request-scoped and cooperative: a client can send
17
+ a cancel frame while the connection reader remains active, and the server
18
+ acknowledges it only for the active request. Mixed transaction reference-log
19
+ and restart validation remain open.
20
+ 2. Persistence: fault-test disk-full and interrupted checkpoints/schema changes;
21
+ index metadata load and write errors now fail visibly, and failed schema
22
+ publications roll back in-memory state. Metadata and index catalogs are
23
+ published through unique temporary files with flush/fsync/atomic rename.
24
+ Commit acknowledgements now expose durable versus uncertain WAL state and
25
+ recovery-required post-WAL flush failures. Storage accepts an explicit
26
+ `io_fault_injector` hook for page writes, file extension/truncation, and
27
+ sync operations; those paths now verify typed failures and descriptor
28
+ cleanup. Compaction now reads the engine's actual record-header layout,
29
+ observes dirty buffer-pool pages, and is covered through reopen validation.
30
+ Real filesystem quota and power-loss tests remain environment work.
31
+ 3. SQL correctness: ambiguous identifiers and broader dialect-specific edge
32
+ cases remain open. Boolean false values, `IS NULL`, NULL comparison behavior, and
33
+ outer-join NULL extension now have regression coverage. Non-recursive CTEs,
34
+ subqueries, set operations, targeted `ON CONFLICT DO UPDATE`, and
35
+ ranking/partition window functions, explicit `ROWS` window frames, bounded
36
+ recursive CTEs, dependency-aware inner-join reordering, and targetless
37
+ conflict updates using primary/unique definitions are implemented; multi-row
38
+ `VALUES` sources are now atomic when executed outside a caller transaction.
39
+ Broader dialect upsert forms and statistics-driven plan costing remain open.
40
+ 4. Replication: synchronized acknowledgements and promotion with an explicit
41
+ recovery point. Engine commits now package all committed row changes into a
42
+ single replication envelope after the local WAL commit point. Primary
43
+ connections bootstrap the catalog before row replay, including empty replicas. TCP input now
44
+ uses bounded newline framing and replay positions are fsynced before ack;
45
+ replica engine mutation entry points are read-only except for internal replay.
46
+ Promotion now rejects any received/replayed LSN gap and can require a
47
+ caller-supplied recovery point. Active primary engine mutations validate the
48
+ fencing lease before writing. Replication shutdown now closes established
49
+ replica sockets, and a reconnect/catch-up partition test verifies that the
50
+ replica resumes from the durable log. A process-level failover drill now
51
+ kills and replaces an independent primary, advances the fencing epoch from
52
+ another process, rejects the stale writer, and verifies replica catch-up.
53
+ A reproducible TCP fault-proxy drill now exercises live partition, healing,
54
+ catch-up, fencing, and promotion while the primary remains alive. True
55
+ multi-host partitions, independent fencing infrastructure, and split-brain
56
+ recovery still require deployment validation before adding automatic election.
57
+ 5. Rails: populated migration round trips, eager loading, nested associations,
58
+ and live adapter coverage are present. CI now exercises Rails 7.1, 7.2, and
59
+ 8.0 against the adapter; connection-pool behavior and each version's
60
+ compatibility result still require hosted-run evidence. Migration
61
+ tracking now uses stable content checksums and fails closed for changed or
62
+ missing applied migrations. Native and ActiveRecord schema dumps now
63
+ round-trip automatic/custom primary-key modes, defaults, and indexes through
64
+ live engines.
65
+ 6. Operations: backup manifest writes now use durable atomic publication, live
66
+ engines flush WAL/storage before physical backup, and a scheduled restore
67
+ drill reopens restored files. Upgrade tests, measured resource limits,
68
+ alerting, and full security review remain open. Replication peers now
69
+ support constant-time shared-token authentication when configured; TLS and
70
+ credential rotation procedures still require deployment validation.
71
+
72
+ 7. Release engineering: cross-platform Ruby 3.3/3.4 CI, Rails 7.1/7.2/8.0
73
+ adapter jobs, and a deterministic bounded fuzz safety workflow are wired
74
+ into GitHub Actions. CI enforces 25% line and 20% branch coverage (the
75
+ current audit measured 62.0% line and 32.84% branch). Release provenance
76
+ signing is enabled, RubyGems gem-level signing requires protected
77
+ key/certificate paths, tag/changelog preflight fails closed, dependency
78
+ audit runs weekly, and tag releases can publish generated GitHub release
79
+ notes. A maintainer must still provision the RubyGems signing secrets and
80
+ review generated notes before publication.
81
+
82
+ Deployment tests must record the commit, platform, workload and measured
83
+ results. Keep untested features marked as unvalidated.
84
+
85
+ ## Latest documentation checkpoint
86
+
87
+ The current repository also ships a tested common SQLite-style compatibility
88
+ profile, a durability release gate, Prometheus alert rules, a multi-node Docker
89
+ topology manifest, a production runbook, and a release checklist. The lessons
90
+ from these hardening changes are collected in
91
+ [`docs/lessons-learned.md`](lessons-learned.md). These artifacts improve
92
+ repeatability but do not replace hosted multi-host, physical-filesystem, or
93
+ independent security validation.
@@ -0,0 +1,112 @@
1
+ # RubyDB lessons learned
2
+
3
+ This page records the engineering lessons from hardening RubyDB. Each lesson is
4
+ paired with the rule it creates and the validation that protects it. New
5
+ features should update this page when they change an operational guarantee.
6
+
7
+ ## Correctness before breadth
8
+
9
+ An implemented parser branch, adapter method, or replication class is not a
10
+ production feature until it is exercised end to end. A narrow, explicit
11
+ compatibility contract is safer than accepting syntax with incomplete
12
+ semantics.
13
+
14
+ Rule: document supported behavior and fail closed for unsupported behavior.
15
+ Validation: integration specs and the SQL compatibility documents must agree.
16
+
17
+ ## Durability is a sequence, not a boolean
18
+
19
+ “Write succeeded” and “data is durable” are different outcomes. WAL ordering,
20
+ flush errors, atomic metadata publication, checksums, recovery, and restore must
21
+ be tested together. A successful in-memory mutation cannot hide a failed
22
+ publication or sync.
23
+
24
+ Rule: never report a durable acknowledgement before the required WAL and file
25
+ operations succeed. Preserve uncertain state for recovery instead of guessing.
26
+ Validation: filesystem fault injection, crash recovery, compaction/reopen, and
27
+ backup/restore drills.
28
+
29
+ ## Ownership must be explicit
30
+
31
+ An embedded file cannot safely be opened by multiple independent application
32
+ processes merely because each process has a mutex. The operating-system lock and
33
+ the server boundary are part of the data-safety model.
34
+
35
+ Rule: one embedded owner; multiple processes use the server/client topology.
36
+ Validation: duplicate-open rejection, process workloads, and connection-limit
37
+ tests.
38
+
39
+ ## Replication needs fencing before election
40
+
41
+ Replica catch-up alone does not prevent two writers. Promotion requires a
42
+ known recovery point, durable fencing, stale-writer rejection, and operator
43
+ evidence. Automatic election without an independent fencing authority can turn
44
+ a network partition into split-brain writes.
45
+
46
+ Rule: keep automatic election disabled until multi-host fencing is independently
47
+ validated in the deployment environment.
48
+ Validation: process replacement, TCP partition/healing, stale-primary rejection,
49
+ and a real multi-host drill.
50
+
51
+ ## Cancellation must reach the wire and the executor
52
+
53
+ A client-side timeout that only stops waiting does not stop database work. The
54
+ server must keep reading control frames, mark the active request cancelled, and
55
+ the executor must check cooperatively during long operations.
56
+
57
+ Rule: test cancellation as an in-flight protocol event, not as a mocked timeout.
58
+ Validation: the production soak sends a real cancel frame and checks the server
59
+ request lifecycle.
60
+
61
+ ## Deadlock recovery belongs to transaction ownership
62
+
63
+ Detecting a cycle is insufficient if the chosen victim is not rolled back and
64
+ its locks are not released. The lock manager and transaction manager must share
65
+ the same victim identity and lifecycle.
66
+
67
+ Rule: every resolved deadlock has a victim, rollback, lock release, and a
68
+ retryable error for the victim.
69
+ Validation: the two-transaction cycle specs and the production soak.
70
+
71
+ ## Adapters are compatibility products
72
+
73
+ ActiveRecord compatibility includes schema introspection, quoted identifiers,
74
+ binds, associations, eager loading, migrations, schema dumps, connection pools,
75
+ and error behavior. Passing one CRUD example is not Rails compatibility.
76
+
77
+ Rule: test the adapter against every supported Rails version and a populated
78
+ schema, not only an empty database.
79
+ Validation: the Rails matrix and adapter integration suite for Rails 7.1, 7.2,
80
+ and 8.0.
81
+
82
+ ## Operations are part of the implementation
83
+
84
+ Backups, restore destinations, monitoring thresholds, certificate rotation,
85
+ secret storage, upgrade rollback, incident preservation, and release evidence
86
+ are not afterthoughts. Operators need safe commands and clear stop conditions.
87
+
88
+ Rule: every destructive or irreversible operation must have a dry run, a backup
89
+ boundary, and a documented recovery path.
90
+ Validation: the production runbook, restore drill, alert rules, and release
91
+ preflight.
92
+
93
+ ## Evidence must be reproducible
94
+
95
+ Test counts without the commit, runtime, platform, workload, seed, and result
96
+ artifact are difficult to trust or reproduce.
97
+
98
+ Rule: archive machine-readable workload output and record commit, Ruby/Rails
99
+ versions, OS, resources, seed, latency percentiles, recovery time, and RPO/RTO.
100
+ Validation: CI artifacts, bounded fuzz seeds, benchmark output, and release
101
+ checklists.
102
+
103
+ ## Security claims require a separate review
104
+
105
+ Passing authentication and TLS tests does not review deployment identity,
106
+ filesystem permissions, secret lifecycle, dependency exposure, or incident
107
+ response.
108
+
109
+ Rule: treat the repository security suite as a baseline and require an
110
+ independent review before sensitive or regulated workloads.
111
+ Validation: dependency audit, CodeQL, TLS/auth tests, deployment review, and
112
+ credential/certificate rotation drills.
@@ -0,0 +1,33 @@
1
+ # Backup policy
2
+
3
+ Backups are only useful after they have been verified and restored. Keep the
4
+ database file, WAL chain, backup manifest, and checksum together. Store a copy
5
+ outside the database host and encrypt it with the organization’s approved
6
+ archive tooling.
7
+
8
+ ## Create and verify
9
+
10
+ Stop writes for a maintenance backup when possible, then run:
11
+
12
+ ```sh
13
+ rubydb backup --dir /var/lib/rubydb/backups
14
+ rubydb restore --latest --dry-run --dir /var/lib/rubydb/backups
15
+ ```
16
+
17
+ The backup implementation flushes the WAL and storage before copying files and
18
+ publishes its manifest atomically. A successful command is not a substitute for
19
+ the scheduled restore drill in `scripts/restore_drill`.
20
+
21
+ ## Retention and deletion
22
+
23
+ Use a documented retention schedule such as daily backups for 14 days, weekly
24
+ backups for 12 weeks, and monthly backups for the required compliance period.
25
+ Delete only verified, expired backup sets; never delete a base backup while an
26
+ incremental or differential chain depends on it.
27
+
28
+ ## Recovery point checks
29
+
30
+ Record the backup name, manifest checksum, database version, page size, WAL
31
+ position, and verification result in the deployment record. Before restoring,
32
+ confirm that the selected backup is complete and that its destination is a new,
33
+ inactive directory.
@@ -0,0 +1,31 @@
1
+ # Disaster recovery
2
+
3
+ Define application-specific recovery objectives before deployment:
4
+
5
+ - RPO: the maximum acceptable committed data loss.
6
+ - RTO: the maximum time to restore service.
7
+ - retention: how long backups and incident copies must be kept.
8
+
9
+ RubyDB’s verified recovery path is restore to a new directory followed by an
10
+ engine reopen and row/catalog verification. It does not silently repair
11
+ unknown corruption or perform automatic cross-region failover.
12
+
13
+ ## Recovery procedure
14
+
15
+ 1. Declare the incident and stop application writes to the affected database.
16
+ 2. Preserve the original data directory, WAL, logs, configuration, and process
17
+ information. Work from a copied incident set.
18
+ 3. Select the newest backup whose manifest and checksum verify within the RPO.
19
+ 4. Restore into a new directory and run `rubydb restore --dry-run` before
20
+ opening it.
21
+ 5. Open the restored database, run migrations/status checks, and execute the
22
+ application smoke and integrity queries.
23
+ 6. Switch the application to the restored directory only after validation.
24
+ 7. Preserve the failed directory until the incident review is complete.
25
+
26
+ Run `ruby scripts/restore_drill` in staging on the same filesystem and RubyDB
27
+ release family used in production. Record measured restore time, recovered WAL
28
+ position, row counts, and any gaps against RTO/RPO.
29
+
30
+ Do not remove WAL, run vacuum, force promotion, or overwrite the source during
31
+ an active corruption investigation.
@@ -0,0 +1,30 @@
1
+ # Failover procedure
2
+
3
+ Automatic election is intentionally disabled until fencing and partition
4
+ testing are complete. Failover is an operator-controlled procedure.
5
+
6
+ ## Promote a replica safely
7
+
8
+ 1. Confirm the primary is stopped or fenced and cannot accept writes.
9
+ 2. Confirm the candidate replica is `SYNCED`, or is `DISCONNECTED` only after
10
+ it was previously synchronized, and that received and replayed WAL
11
+ positions are equal.
12
+ 3. Compare the candidate replay position with the incident recovery point.
13
+ 4. Promote only after the candidate passes the configured fencing checks.
14
+ 5. Point clients at the promoted node and run read/write smoke queries.
15
+ 6. Keep the old primary isolated until its data and fencing epoch are reviewed.
16
+
17
+ Promotion must fail closed when a replica is lagging, has never replayed a
18
+ durable position, or is missing a required recovery point. A disconnected but
19
+ caught-up replica is eligible only for explicit operator promotion after the
20
+ old primary is stopped or fenced. Never force promotion to hide a WAL gap.
21
+
22
+ ## Rejoin the old primary
23
+
24
+ Treat the old primary as a stale writer. Preserve its files, compare WAL and
25
+ catalog state, and rebuild or re-bootstrap it as a replica if divergence cannot
26
+ be proven absent. Re-enable it for writes only after a new fencing lease and a
27
+ clean recovery validation.
28
+
29
+ Record the old and new fencing epochs, replica LSNs, promotion time, observed
30
+ lag, and application error rate in the incident record.