rubydb 0.1.4 → 0.1.6

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 (469) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +15 -15
  3. data/.github/workflows/benchmark.yml +26 -26
  4. data/.github/workflows/compatibility.yml +63 -63
  5. data/.github/workflows/fuzz.yml +33 -33
  6. data/.github/workflows/lint.yml +21 -21
  7. data/.github/workflows/operations.yml +24 -24
  8. data/.github/workflows/production-validation.yml +111 -111
  9. data/.github/workflows/release.yml +77 -77
  10. data/.github/workflows/security.yml +39 -37
  11. data/.github/workflows/test.yml +26 -26
  12. data/.github/workflows/workload.yml +58 -58
  13. data/.gitignore +16 -5
  14. data/.rubocop.yml +50 -44
  15. data/.standard.yml +9 -14
  16. data/ARCHITECTURE.md +21 -21
  17. data/CHANGELOG.md +57 -27
  18. data/CODE_OF_CONDUCT.md +13 -13
  19. data/CONTRIBUTING.md +29 -29
  20. data/GOVERNANCE.md +16 -16
  21. data/Gemfile +18 -17
  22. data/Gemfile.lock +125 -71
  23. data/README.md +168 -12
  24. data/ROADMAP.md +27 -27
  25. data/Rakefile +76 -71
  26. data/SECURITY.md +54 -54
  27. data/SUPPORT.md +14 -14
  28. data/accelerator/bin/SHA256SUMS +6 -0
  29. data/accelerator/bin/rubydb-accelerator-darwin-amd64 +0 -0
  30. data/accelerator/bin/rubydb-accelerator-darwin-arm64 +0 -0
  31. data/accelerator/bin/rubydb-accelerator-linux-amd64 +0 -0
  32. data/accelerator/bin/rubydb-accelerator-linux-arm64 +0 -0
  33. data/accelerator/bin/rubydb-accelerator-windows-amd64.exe +0 -0
  34. data/accelerator/bin/rubydb-accelerator-windows-arm64.exe +0 -0
  35. data/accelerator/cmd/rubydb-accelerator/main.go +11 -0
  36. data/accelerator/go.mod +3 -0
  37. data/accelerator/internal/execution/aggregate.go +94 -0
  38. data/accelerator/internal/execution/distinct.go +22 -0
  39. data/accelerator/internal/execution/filter.go +73 -0
  40. data/accelerator/internal/execution/join.go +79 -0
  41. data/accelerator/internal/execution/operators.go +167 -0
  42. data/accelerator/internal/execution/scan.go +20 -0
  43. data/accelerator/internal/execution/sort.go +62 -0
  44. data/accelerator/internal/execution/types.go +136 -0
  45. data/accelerator/internal/execution/value.go +67 -0
  46. data/accelerator/internal/memory/arena.go +47 -0
  47. data/accelerator/internal/memory/reuse.go +22 -0
  48. data/accelerator/internal/metrics/registry.go +67 -0
  49. data/accelerator/internal/parallel/bounded_queue.go +56 -0
  50. data/accelerator/internal/parallel/scheduler.go +47 -0
  51. data/accelerator/internal/parallel/worker_pool.go +53 -0
  52. data/accelerator/internal/protocol/cancellation.go +48 -0
  53. data/accelerator/internal/protocol/columnar.go +263 -0
  54. data/accelerator/internal/protocol/frame.go +187 -0
  55. data/accelerator/internal/runtime/worker.go +521 -0
  56. data/accelerator/internal/storage/page_reader.go +81 -0
  57. data/accelerator/internal/storage/snapshot_scan.go +539 -0
  58. data/accelerator/internal/wal/checksum.go +13 -0
  59. data/accelerator/internal/wal/compression.go +41 -0
  60. data/accelerator/internal/wal/group_commit.go +24 -0
  61. data/accelerator/internal/wal/record_encoder.go +40 -0
  62. data/adapters/activerecord/Gemfile +11 -11
  63. data/adapters/activerecord/README.md +8 -3
  64. data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +881 -879
  65. data/adapters/activerecord/rubydb-activerecord.gemspec +21 -21
  66. data/adapters/activerecord/spec/rubydb_adapter_integration_spec.rb +143 -143
  67. data/adapters/ruby/README.md +18 -18
  68. data/adapters/sequel/README.md +11 -11
  69. data/config/monitoring/prometheus-alerts.yml +39 -39
  70. data/config/production.yml +36 -36
  71. data/docs/README.md +77 -71
  72. data/docs/architecture/concurrency.md +14 -14
  73. data/docs/architecture/current-state.md +125 -125
  74. data/docs/architecture/execution-engine.md +25 -25
  75. data/docs/architecture/go-accelerator.md +179 -0
  76. data/docs/architecture/indexes.md +19 -19
  77. data/docs/architecture/mvcc.md +19 -19
  78. data/docs/architecture/overview.md +13 -13
  79. data/docs/architecture/pages.md +11 -11
  80. data/docs/architecture/production-roadmap.md +82 -82
  81. data/docs/architecture/query-planner.md +20 -20
  82. data/docs/architecture/recovery.md +18 -18
  83. data/docs/architecture/sql-engine.md +12 -12
  84. data/docs/architecture/storage-engine.md +14 -14
  85. data/docs/architecture/transactions.md +10 -10
  86. data/docs/architecture/wal.md +28 -28
  87. data/docs/cli-cheatsheet.md +98 -98
  88. data/docs/cli.md +299 -275
  89. data/docs/contributing/architecture.md +9 -9
  90. data/docs/contributing/benchmarking.md +30 -14
  91. data/docs/contributing/development.md +16 -16
  92. data/docs/contributing/release-process.md +49 -49
  93. data/docs/contributing/testing.md +16 -16
  94. data/docs/debugging.md +229 -229
  95. data/docs/developer/branching.md +10 -10
  96. data/docs/developer/database-diff.md +10 -10
  97. data/docs/developer/local-development.md +49 -17
  98. data/docs/developer/snapshots.md +9 -9
  99. data/docs/developer/temporal-data.md +10 -10
  100. data/docs/developer-guide.md +297 -297
  101. data/docs/getting-started/first-database.md +16 -16
  102. data/docs/getting-started/first-query.md +13 -13
  103. data/docs/getting-started/installation.md +19 -19
  104. data/docs/getting-started/local-to-production.md +300 -300
  105. data/docs/getting-started/quickstart.md +17 -17
  106. data/docs/getting-started/rails.md +16 -16
  107. data/docs/hardening_backlog.md +93 -93
  108. data/docs/lessons-learned.md +112 -112
  109. data/docs/operations/backups.md +33 -33
  110. data/docs/operations/disaster-recovery.md +31 -31
  111. data/docs/operations/failover.md +30 -30
  112. data/docs/operations/monitoring.md +25 -25
  113. data/docs/operations/production-guide.md +295 -295
  114. data/docs/operations/production-runbook.md +45 -45
  115. data/docs/operations/replication.md +33 -33
  116. data/docs/operations/restore.md +6 -6
  117. data/docs/operations/runbook.md +34 -34
  118. data/docs/operations/upgrades.md +14 -14
  119. data/docs/operations/workload-testing.md +17 -17
  120. data/docs/production-readiness.md +118 -118
  121. data/docs/production_validation.md +150 -150
  122. data/docs/rails/active-record.md +11 -11
  123. data/docs/rails/compatibility-guide.md +90 -90
  124. data/docs/rails/database-yml.md +92 -92
  125. data/docs/rails/installation.md +17 -17
  126. data/docs/rails/migrations.md +17 -17
  127. data/docs/rails/production.md +82 -82
  128. data/docs/rails/troubleshooting.md +18 -18
  129. data/docs/release.md +25 -25
  130. data/docs/server/architecture.md +10 -10
  131. data/docs/server/authentication.md +10 -10
  132. data/docs/server/configuration.md +16 -16
  133. data/docs/server/connection-pooling.md +10 -10
  134. data/docs/server/deployment.md +10 -10
  135. data/docs/server/protocol.md +12 -12
  136. data/docs/sql/compatibility-guide.md +82 -82
  137. data/docs/sql/compatibility.md +39 -39
  138. data/docs/sql/data-types.md +10 -10
  139. data/docs/sql/functions.md +9 -9
  140. data/docs/sql/joins.md +9 -9
  141. data/docs/sql/operators.md +9 -9
  142. data/docs/sql/sqlite-compatibility.md +21 -21
  143. data/docs/sql/syntax.md +10 -10
  144. data/docs/sql/transactions.md +10 -10
  145. data/docs/troubleshooting.md +244 -244
  146. data/lessons/01-foundations.md +73 -0
  147. data/lessons/02-local-development.md +121 -0
  148. data/lessons/03-embedded-rubydb.md +99 -0
  149. data/lessons/04-rails-complex-apps.md +138 -0
  150. data/lessons/05-rubydb-production-server.md +237 -0
  151. data/lessons/06-postgresql-massive-apps.md +96 -0
  152. data/lessons/07-hybrid-microservices.md +179 -0
  153. data/lessons/08-migrations-backups-recovery.md +86 -0
  154. data/lessons/09-observability-security-scale.md +87 -0
  155. data/lessons/10-release-readiness.md +192 -0
  156. data/lessons/11-community-adapter.md +323 -0
  157. data/lessons/12-rails-ecommerce-pressure.md +263 -0
  158. data/lib/rubydb/accelerator/client.rb +451 -0
  159. data/lib/rubydb/accelerator/error.rb +22 -0
  160. data/lib/rubydb/accelerator/manager.rb +606 -0
  161. data/lib/rubydb/accelerator.rb +13 -0
  162. data/lib/rubydb/backup/archive.rb +332 -334
  163. data/lib/rubydb/backup/backup.rb +400 -401
  164. data/lib/rubydb/backup/incremental.rb +349 -353
  165. data/lib/rubydb/backup/restore.rb +289 -290
  166. data/lib/rubydb/backup/snapshot.rb +265 -267
  167. data/lib/rubydb/backup/verification.rb +276 -279
  168. data/lib/rubydb/branching/branch.rb +181 -181
  169. data/lib/rubydb/branching/branch_manager.rb +307 -311
  170. data/lib/rubydb/branching/branch_metadata.rb +140 -140
  171. data/lib/rubydb/branching/checkout.rb +165 -166
  172. data/lib/rubydb/branching/copy_on_write.rb +272 -272
  173. data/lib/rubydb/branching/diff.rb +137 -138
  174. data/lib/rubydb/branching/merge.rb +282 -285
  175. data/lib/rubydb/build_info.rb +15 -15
  176. data/lib/rubydb/catalog/catalog.rb +391 -391
  177. data/lib/rubydb/catalog/column.rb +112 -112
  178. data/lib/rubydb/catalog/constraint.rb +180 -180
  179. data/lib/rubydb/catalog/database.rb +184 -184
  180. data/lib/rubydb/catalog/index.rb +97 -97
  181. data/lib/rubydb/catalog/schema.rb +103 -103
  182. data/lib/rubydb/catalog/sequence.rb +90 -90
  183. data/lib/rubydb/catalog/system_catalog.rb +698 -698
  184. data/lib/rubydb/catalog/table.rb +178 -178
  185. data/lib/rubydb/catalog/trigger.rb +102 -102
  186. data/lib/rubydb/catalog/view.rb +66 -66
  187. data/lib/rubydb/cli/application.rb +168 -163
  188. data/lib/rubydb/cli/commands/accelerator.rb +72 -0
  189. data/lib/rubydb/cli/commands/backup.rb +80 -81
  190. data/lib/rubydb/cli/commands/branch.rb +72 -72
  191. data/lib/rubydb/cli/commands/checkout.rb +54 -54
  192. data/lib/rubydb/cli/commands/create.rb +58 -58
  193. data/lib/rubydb/cli/commands/diff.rb +76 -77
  194. data/lib/rubydb/cli/commands/doctor.rb +77 -74
  195. data/lib/rubydb/cli/commands/drop.rb +57 -57
  196. data/lib/rubydb/cli/commands/init.rb +101 -102
  197. data/lib/rubydb/cli/commands/inspect.rb +95 -95
  198. data/lib/rubydb/cli/commands/merge.rb +63 -63
  199. data/lib/rubydb/cli/commands/migrate.rb +62 -62
  200. data/lib/rubydb/cli/commands/restart.rb +42 -39
  201. data/lib/rubydb/cli/commands/restore.rb +121 -121
  202. data/lib/rubydb/cli/commands/shell.rb +365 -365
  203. data/lib/rubydb/cli/commands/snapshot.rb +79 -79
  204. data/lib/rubydb/cli/commands/start.rb +88 -82
  205. data/lib/rubydb/cli/commands/status.rb +96 -92
  206. data/lib/rubydb/cli/commands/stop.rb +47 -47
  207. data/lib/rubydb/cli/commands/vacuum.rb +58 -58
  208. data/lib/rubydb/cli/formatter.rb +221 -221
  209. data/lib/rubydb/cli/output.rb +168 -168
  210. data/lib/rubydb/client/client.rb +309 -304
  211. data/lib/rubydb/client/connection.rb +429 -415
  212. data/lib/rubydb/client/connection_pool.rb +168 -168
  213. data/lib/rubydb/client/connection_url.rb +96 -96
  214. data/lib/rubydb/client/prepared_statement.rb +60 -60
  215. data/lib/rubydb/client/result.rb +127 -123
  216. data/lib/rubydb/client/statement.rb +52 -52
  217. data/lib/rubydb/client/transaction.rb +130 -130
  218. data/lib/rubydb/concurrency/concurrency.rb +19 -19
  219. data/lib/rubydb/concurrency/deadlock_detector.rb +148 -150
  220. data/lib/rubydb/concurrency/latch.rb +101 -101
  221. data/lib/rubydb/concurrency/lock_graph.rb +163 -165
  222. data/lib/rubydb/concurrency/mutex.rb +181 -183
  223. data/lib/rubydb/concurrency/rw_lock.rb +180 -180
  224. data/lib/rubydb/concurrency/scheduler.rb +248 -250
  225. data/lib/rubydb/concurrency/worker_pool.rb +145 -143
  226. data/lib/rubydb/configuration/config.rb +170 -170
  227. data/lib/rubydb/configuration/defaults.rb +191 -179
  228. data/lib/rubydb/configuration/environment.rb +152 -152
  229. data/lib/rubydb/configuration/parser.rb +185 -185
  230. data/lib/rubydb/configuration/validation.rb +228 -221
  231. data/lib/rubydb/constants.rb +74 -74
  232. data/lib/rubydb/constraints/check.rb +181 -181
  233. data/lib/rubydb/constraints/constraint.rb +101 -101
  234. data/lib/rubydb/constraints/foreign_key.rb +130 -130
  235. data/lib/rubydb/constraints/not_null.rb +64 -64
  236. data/lib/rubydb/constraints/primary_key.rb +99 -99
  237. data/lib/rubydb/constraints/unique.rb +106 -108
  238. data/lib/rubydb/constraints/validator.rb +349 -350
  239. data/lib/rubydb/errors/authentication_error.rb +10 -10
  240. data/lib/rubydb/errors/authorization_error.rb +23 -23
  241. data/lib/rubydb/errors/client_error.rb +10 -10
  242. data/lib/rubydb/errors/configuration_error.rb +10 -10
  243. data/lib/rubydb/errors/connection_error.rb +10 -10
  244. data/lib/rubydb/errors/constraint_error.rb +23 -23
  245. data/lib/rubydb/errors/corruption_error.rb +10 -10
  246. data/lib/rubydb/errors/database_error.rb +10 -10
  247. data/lib/rubydb/errors/error.rb +20 -20
  248. data/lib/rubydb/errors/execution_error.rb +10 -10
  249. data/lib/rubydb/errors/parser_error.rb +10 -10
  250. data/lib/rubydb/errors/recovery_error.rb +10 -10
  251. data/lib/rubydb/errors/replication_error.rb +10 -10
  252. data/lib/rubydb/errors/server_error.rb +6 -6
  253. data/lib/rubydb/errors/storage_error.rb +10 -10
  254. data/lib/rubydb/errors/transaction_error.rb +10 -10
  255. data/lib/rubydb/execution/accelerator_dispatch.rb +30 -0
  256. data/lib/rubydb/execution/aggregate_executor.rb +134 -138
  257. data/lib/rubydb/execution/cost_model.rb +72 -0
  258. data/lib/rubydb/execution/delete_executor.rb +110 -112
  259. data/lib/rubydb/execution/distinct_executor.rb +131 -135
  260. data/lib/rubydb/execution/executor.rb +1544 -1188
  261. data/lib/rubydb/execution/expression.rb +191 -193
  262. data/lib/rubydb/execution/index_scan.rb +142 -142
  263. data/lib/rubydb/execution/insert_executor.rb +215 -217
  264. data/lib/rubydb/execution/join_executor.rb +243 -249
  265. data/lib/rubydb/execution/limit_executor.rb +83 -85
  266. data/lib/rubydb/execution/operator_selection.rb +57 -0
  267. data/lib/rubydb/execution/optimizer.rb +227 -215
  268. data/lib/rubydb/execution/physical_plan.rb +47 -0
  269. data/lib/rubydb/execution/plan.rb +355 -353
  270. data/lib/rubydb/execution/planner.rb +508 -536
  271. data/lib/rubydb/execution/predicate.rb +235 -235
  272. data/lib/rubydb/execution/scan.rb +49 -49
  273. data/lib/rubydb/execution/sequential_scan.rb +63 -63
  274. data/lib/rubydb/execution/sort_executor.rb +194 -185
  275. data/lib/rubydb/execution/update_executor.rb +160 -162
  276. data/lib/rubydb/functions/aggregate.rb +70 -70
  277. data/lib/rubydb/functions/date_functions.rb +274 -278
  278. data/lib/rubydb/functions/function.rb +85 -85
  279. data/lib/rubydb/functions/json_functions.rb +231 -215
  280. data/lib/rubydb/functions/numeric_functions.rb +346 -346
  281. data/lib/rubydb/functions/scalar.rb +52 -52
  282. data/lib/rubydb/functions/string_functions.rb +383 -383
  283. data/lib/rubydb/functions/system_functions.rb +258 -246
  284. data/lib/rubydb/history/as_of.rb +238 -238
  285. data/lib/rubydb/history/change.rb +105 -105
  286. data/lib/rubydb/history/history.rb +131 -131
  287. data/lib/rubydb/history/history_manager.rb +228 -229
  288. data/lib/rubydb/history/temporal_query.rb +202 -202
  289. data/lib/rubydb/history/timeline.rb +144 -144
  290. data/lib/rubydb/indexes/btree.rb +215 -186
  291. data/lib/rubydb/indexes/btree_cursor.rb +258 -258
  292. data/lib/rubydb/indexes/btree_node.rb +384 -385
  293. data/lib/rubydb/indexes/hash_index.rb +150 -150
  294. data/lib/rubydb/indexes/index.rb +71 -71
  295. data/lib/rubydb/indexes/index_manager.rb +408 -406
  296. data/lib/rubydb/indexes/index_scan.rb +466 -470
  297. data/lib/rubydb/migrations/migration.rb +253 -254
  298. data/lib/rubydb/migrations/migration_lock.rb +146 -146
  299. data/lib/rubydb/migrations/migration_manager.rb +187 -176
  300. data/lib/rubydb/migrations/migration_version.rb +71 -71
  301. data/lib/rubydb/migrations/schema_diff.rb +211 -211
  302. data/lib/rubydb/migrations/schema_version.rb +64 -64
  303. data/lib/rubydb/monitoring/events.rb +155 -160
  304. data/lib/rubydb/monitoring/health.rb +216 -222
  305. data/lib/rubydb/monitoring/logger.rb +188 -193
  306. data/lib/rubydb/monitoring/metrics.rb +363 -359
  307. data/lib/rubydb/monitoring/performance.rb +176 -176
  308. data/lib/rubydb/monitoring/statistics.rb +168 -170
  309. data/lib/rubydb/mvcc/garbage_collector.rb +199 -199
  310. data/lib/rubydb/mvcc/mvcc.rb +16 -16
  311. data/lib/rubydb/mvcc/snapshot.rb +146 -147
  312. data/lib/rubydb/mvcc/vacuum.rb +180 -180
  313. data/lib/rubydb/mvcc/version.rb +106 -106
  314. data/lib/rubydb/mvcc/version_store.rb +396 -398
  315. data/lib/rubydb/mvcc/visibility.rb +107 -109
  316. data/lib/rubydb/protocol/capabilities.rb +125 -125
  317. data/lib/rubydb/protocol/decoder.rb +142 -145
  318. data/lib/rubydb/protocol/encoder.rb +131 -136
  319. data/lib/rubydb/protocol/handshake.rb +306 -305
  320. data/lib/rubydb/protocol/message.rb +121 -121
  321. data/lib/rubydb/protocol/parameter_binder.rb +101 -0
  322. data/lib/rubydb/protocol/protocol.rb +276 -277
  323. data/lib/rubydb/protocol/version.rb +54 -54
  324. data/lib/rubydb/rails/adapter.rb +245 -239
  325. data/lib/rubydb/rails/connection.rb +312 -314
  326. data/lib/rubydb/rails/database_statements.rb +122 -122
  327. data/lib/rubydb/rails/migration.rb +131 -131
  328. data/lib/rubydb/rails/quoting.rb +109 -109
  329. data/lib/rubydb/rails/result.rb +117 -117
  330. data/lib/rubydb/rails/schema_statements.rb +339 -339
  331. data/lib/rubydb/rails/transaction.rb +105 -105
  332. data/lib/rubydb/rails/type.rb +126 -126
  333. data/lib/rubydb/recovery/checkpoint.rb +261 -257
  334. data/lib/rubydb/recovery/consistency.rb +457 -467
  335. data/lib/rubydb/recovery/corruption_detector.rb +5 -5
  336. data/lib/rubydb/recovery/crash_recovery.rb +381 -387
  337. data/lib/rubydb/recovery/recovery_manager.rb +204 -206
  338. data/lib/rubydb/recovery/redo.rb +235 -237
  339. data/lib/rubydb/recovery/undo.rb +204 -206
  340. data/lib/rubydb/replication/failover.rb +5 -5
  341. data/lib/rubydb/replication/fencing.rb +63 -63
  342. data/lib/rubydb/replication/primary.rb +461 -450
  343. data/lib/rubydb/replication/replica.rb +382 -384
  344. data/lib/rubydb/replication/replication_log.rb +194 -200
  345. data/lib/rubydb/replication/replication_manager.rb +307 -308
  346. data/lib/rubydb/replication/replication_slot.rb +293 -295
  347. data/lib/rubydb/replication/replication_stream.rb +198 -201
  348. data/lib/rubydb/rubydb.rb +570 -560
  349. data/lib/rubydb/security/access_control.rb +252 -254
  350. data/lib/rubydb/security/audit_log.rb +209 -213
  351. data/lib/rubydb/security/authentication.rb +302 -302
  352. data/lib/rubydb/security/authorization.rb +282 -282
  353. data/lib/rubydb/security/credentials.rb +192 -196
  354. data/lib/rubydb/security/password.rb +205 -215
  355. data/lib/rubydb/security/permissions.rb +74 -74
  356. data/lib/rubydb/security/role.rb +99 -101
  357. data/lib/rubydb/security/user.rb +86 -86
  358. data/lib/rubydb/server/connection.rb +383 -366
  359. data/lib/rubydb/server/connection_pool.rb +193 -193
  360. data/lib/rubydb/server/lifecycle.rb +227 -228
  361. data/lib/rubydb/server/listener.rb +139 -136
  362. data/lib/rubydb/server/request_handler.rb +277 -276
  363. data/lib/rubydb/server/server.rb +363 -364
  364. data/lib/rubydb/server/session.rb +416 -369
  365. data/lib/rubydb/server/worker.rb +206 -210
  366. data/lib/rubydb/server/worker_pool.rb +168 -168
  367. data/lib/rubydb/sql/ast/alter_table.rb +169 -169
  368. data/lib/rubydb/sql/ast/begin_transaction.rb +47 -47
  369. data/lib/rubydb/sql/ast/commit.rb +37 -37
  370. data/lib/rubydb/sql/ast/constraint.rb +92 -83
  371. data/lib/rubydb/sql/ast/create_database.rb +41 -41
  372. data/lib/rubydb/sql/ast/create_index.rb +61 -61
  373. data/lib/rubydb/sql/ast/create_schema.rb +52 -52
  374. data/lib/rubydb/sql/ast/create_table.rb +187 -187
  375. data/lib/rubydb/sql/ast/delete.rb +54 -54
  376. data/lib/rubydb/sql/ast/drop_database.rb +41 -41
  377. data/lib/rubydb/sql/ast/drop_index.rb +41 -41
  378. data/lib/rubydb/sql/ast/drop_schema.rb +49 -49
  379. data/lib/rubydb/sql/ast/drop_table.rb +49 -49
  380. data/lib/rubydb/sql/ast/explain.rb +64 -64
  381. data/lib/rubydb/sql/ast/expression.rb +617 -604
  382. data/lib/rubydb/sql/ast/insert.rb +66 -66
  383. data/lib/rubydb/sql/ast/node.rb +42 -42
  384. data/lib/rubydb/sql/ast/rollback.rb +63 -63
  385. data/lib/rubydb/sql/ast/savepoint.rb +59 -59
  386. data/lib/rubydb/sql/ast/select.rb +88 -88
  387. data/lib/rubydb/sql/ast/set_operation.rb +22 -20
  388. data/lib/rubydb/sql/ast/trigger.rb +35 -29
  389. data/lib/rubydb/sql/ast/update.rb +88 -88
  390. data/lib/rubydb/sql/ast/vacuum.rb +19 -19
  391. data/lib/rubydb/sql/ast/view.rb +38 -32
  392. data/lib/rubydb/sql/ast/with.rb +32 -32
  393. data/lib/rubydb/sql/grammar.rb +86 -86
  394. data/lib/rubydb/sql/keywords.rb +156 -156
  395. data/lib/rubydb/sql/lexer.rb +209 -214
  396. data/lib/rubydb/sql/operators.rb +100 -100
  397. data/lib/rubydb/sql/parser.rb +1167 -1170
  398. data/lib/rubydb/sql/planner/analyzer.rb +283 -302
  399. data/lib/rubydb/sql/planner/binder.rb +537 -550
  400. data/lib/rubydb/sql/planner/type_checker.rb +427 -431
  401. data/lib/rubydb/sql/token.rb +210 -210
  402. data/lib/rubydb/storage/buffer_frame.rb +44 -44
  403. data/lib/rubydb/storage/buffer_pool.rb +155 -155
  404. data/lib/rubydb/storage/database_lock.rb +74 -74
  405. data/lib/rubydb/storage/deserializer.rb +332 -342
  406. data/lib/rubydb/storage/engine.rb +2409 -2330
  407. data/lib/rubydb/storage/file_manager.rb +191 -187
  408. data/lib/rubydb/storage/free_space_map.rb +79 -81
  409. data/lib/rubydb/storage/page.rb +92 -94
  410. data/lib/rubydb/storage/page_allocator.rb +852 -855
  411. data/lib/rubydb/storage/page_header.rb +63 -67
  412. data/lib/rubydb/storage/page_manager.rb +127 -131
  413. data/lib/rubydb/storage/record.rb +58 -58
  414. data/lib/rubydb/storage/row.rb +78 -78
  415. data/lib/rubydb/storage/serializer.rb +51 -51
  416. data/lib/rubydb/storage/snapshot_reader.rb +167 -0
  417. data/lib/rubydb/storage/storage_layout.rb +151 -151
  418. data/lib/rubydb/storage/storage_manager.rb +114 -114
  419. data/lib/rubydb/storage/tuple.rb +458 -461
  420. data/lib/rubydb/storage/visibility_map.rb +964 -973
  421. data/lib/rubydb/transactions/commit_manager.rb +219 -220
  422. data/lib/rubydb/transactions/isolation.rb +98 -98
  423. data/lib/rubydb/transactions/lock.rb +76 -76
  424. data/lib/rubydb/transactions/lock_manager.rb +359 -362
  425. data/lib/rubydb/transactions/savepoint.rb +142 -143
  426. data/lib/rubydb/transactions/transaction.rb +214 -215
  427. data/lib/rubydb/transactions/transaction_id.rb +84 -84
  428. data/lib/rubydb/transactions/transaction_log.rb +256 -257
  429. data/lib/rubydb/transactions/transaction_manager.rb +434 -435
  430. data/lib/rubydb/types/bigint.rb +36 -36
  431. data/lib/rubydb/types/blob.rb +37 -37
  432. data/lib/rubydb/types/boolean.rb +34 -34
  433. data/lib/rubydb/types/date.rb +39 -39
  434. data/lib/rubydb/types/decimal.rb +48 -48
  435. data/lib/rubydb/types/float.rb +34 -34
  436. data/lib/rubydb/types/integer.rb +36 -36
  437. data/lib/rubydb/types/json.rb +41 -41
  438. data/lib/rubydb/types/null.rb +34 -34
  439. data/lib/rubydb/types/smallint.rb +36 -36
  440. data/lib/rubydb/types/text.rb +37 -37
  441. data/lib/rubydb/types/time.rb +46 -46
  442. data/lib/rubydb/types/timestamp.rb +39 -39
  443. data/lib/rubydb/types/type.rb +119 -119
  444. data/lib/rubydb/types/uuid.rb +47 -47
  445. data/lib/rubydb/types/varchar.rb +37 -37
  446. data/lib/rubydb/version.rb +32 -32
  447. data/lib/rubydb/wal/archive.rb +207 -193
  448. data/lib/rubydb/wal/checkpoint.rb +181 -183
  449. data/lib/rubydb/wal/lsn.rb +94 -94
  450. data/lib/rubydb/wal/reader.rb +259 -260
  451. data/lib/rubydb/wal/record.rb +105 -105
  452. data/lib/rubydb/wal/segment.rb +193 -193
  453. data/lib/rubydb/wal/wal.rb +481 -452
  454. data/lib/rubydb/wal/writer.rb +236 -236
  455. data/lib/rubydb.rb +7 -7
  456. data/packaging/docker/docker-compose.failover.yml +43 -43
  457. data/packaging/homebrew/rubydb.rb +19 -19
  458. data/rubydb.gemspec +70 -57
  459. data/scripts/benchmark +7 -7
  460. data/scripts/build_accelerator +49 -0
  461. data/scripts/durability_drill +37 -37
  462. data/scripts/fuzz +63 -63
  463. data/scripts/release +77 -42
  464. data/scripts/release_check +43 -43
  465. data/scripts/replication_failover_drill +268 -250
  466. data/scripts/replication_network_failover_drill +287 -255
  467. data/scripts/restore_drill +45 -45
  468. data/scripts/security +45 -0
  469. metadata +102 -1
@@ -0,0 +1,121 @@
1
+ # Lesson 2: repeatable local development
2
+
3
+ The fastest safe workflow is to make local setup disposable and repeatable.
4
+ Keep database files under `tmp/` or another ignored directory, use separate
5
+ development and test paths, and run migrations from source control.
6
+
7
+ ## Rails application setup
8
+
9
+ Add the RubyDB gems to a Rails application. Pin versions in a real application
10
+ after testing them; the versions below match the published example used by the
11
+ RubyDB project at the time this lesson was written.
12
+
13
+ ```ruby
14
+ # Gemfile
15
+ gem "rubydb", "0.1.6"
16
+ gem "rubydb-activerecord", "0.1.3"
17
+ gem "pg" # Keep this when production may use PostgreSQL.
18
+ ```
19
+
20
+ Install dependencies:
21
+
22
+ ```sh
23
+ bundle install
24
+ ```
25
+
26
+ Configure development and test with separate embedded files:
27
+
28
+ ```yaml
29
+ # config/database.yml
30
+ default: &default
31
+ adapter: rubydb
32
+ embedded: true
33
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
34
+
35
+ development:
36
+ <<: *default
37
+ database: <%= Rails.root.join("tmp/rubydb_development.rdb") %>
38
+
39
+ test:
40
+ <<: *default
41
+ database: <%= Rails.root.join("tmp/rubydb_test.rdb") %>
42
+ ```
43
+
44
+ Run the schema and tests:
45
+
46
+ ```sh
47
+ bin/rails db:create
48
+ bin/rails db:migrate
49
+ bin/rails test
50
+ bin/rails server
51
+ ```
52
+
53
+ `db:create` is harmless only when it targets a disposable local path. Never
54
+ point it at a production database and never commit `.rdb` files.
55
+
56
+ ## A regular Ruby smoke program
57
+
58
+ This is a complete single-process example using the public storage API:
59
+
60
+ ```ruby
61
+ # script/rubydb_smoke.rb
62
+ require "rubydb"
63
+
64
+ path = "tmp/rubydb_smoke.rdb"
65
+ engine = RubyDB::Storage::Engine.new(path)
66
+ begin
67
+ engine.execute("CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
68
+ engine.execute("INSERT INTO events (name) VALUES ('boot')")
69
+ rows = engine.execute("SELECT id, name FROM events ORDER BY id")
70
+ puts rows.inspect
71
+ ensure
72
+ engine.close
73
+ end
74
+ ```
75
+
76
+ Run it with:
77
+
78
+ ```sh
79
+ bundle exec ruby script/rubydb_smoke.rb
80
+ ```
81
+
82
+ For a multi-process application, replace the embedded engine with a RubyDB
83
+ server and `RubyDB::Client::Client`. Do not let a web process and a worker
84
+ open the same embedded path independently.
85
+
86
+ ## Local PostgreSQL when it is the production target
87
+
88
+ Use the same application code against a local PostgreSQL instance when you
89
+ intend to deploy PostgreSQL. This catches dialect, type, index, and migration
90
+ differences early:
91
+
92
+ ```sh
93
+ docker run --name rubydb-lesson-postgres \
94
+ --env POSTGRES_PASSWORD=devpassword \
95
+ --env POSTGRES_DB=lesson_app \
96
+ --publish 5432:5432 \
97
+ --detach postgres:16
98
+ ```
99
+
100
+ Set the local connection string in your shell or an ignored `.env` file:
101
+
102
+ ```sh
103
+ DATABASE_URL=postgresql://postgres:devpassword@127.0.0.1:5432/lesson_app
104
+ bin/rails db:migrate
105
+ bin/rails test
106
+ ```
107
+
108
+ Do not put that password in Git. Stop and remove this disposable container
109
+ only after confirming it is the lesson container:
110
+
111
+ ```sh
112
+ docker stop rubydb-lesson-postgres
113
+ docker rm rubydb-lesson-postgres
114
+ ```
115
+
116
+ ## Checkpoint
117
+
118
+ The checkpoint passes when a new developer can clone the project, run
119
+ `bundle install`, migrate an empty database, run the tests, and reproduce the
120
+ smoke query without manually editing a database file. Continue with [lesson 3](03-embedded-rubydb.md)
121
+ to understand the limits of embedded mode.
@@ -0,0 +1,99 @@
1
+ # Lesson 3: use RubyDB embedded deliberately
2
+
3
+ Embedded mode puts the database engine in the application process. It is
4
+ simple, fast to start, and useful for local development, command-line tools,
5
+ small internal utilities, and a service with one database-owning process.
6
+
7
+ It is not a shared file protocol. Two independent processes must not open the
8
+ same `.rdb` path concurrently. A Rails web server with several worker
9
+ processes, or a web server plus a job worker, should use RubyDB server/client
10
+ mode instead.
11
+
12
+ ## A safe embedded boundary
13
+
14
+ ```text
15
+ one Ruby process
16
+ |
17
+ +-- RubyDB::Storage::Engine
18
+ |
19
+ +-- tmp/service.rdb or /var/lib/service/service.rdb
20
+ ```
21
+
22
+ Keep the path outside the source tree and make its owner explicit:
23
+
24
+ ```ruby
25
+ require "rubydb"
26
+
27
+ database_path = ENV.fetch("RUBYDB_DATABASE", "tmp/service.rdb")
28
+ engine = RubyDB::Storage::Engine.new(database_path)
29
+
30
+ begin
31
+ engine.execute(<<~SQL)
32
+ CREATE TABLE IF NOT EXISTS jobs (
33
+ id INTEGER PRIMARY KEY,
34
+ state TEXT NOT NULL,
35
+ created_at TIMESTAMP
36
+ )
37
+ SQL
38
+
39
+ engine.execute("INSERT INTO jobs (state, created_at) VALUES ('queued', CURRENT_TIMESTAMP)")
40
+ p engine.execute("SELECT id, state FROM jobs ORDER BY id")
41
+ ensure
42
+ engine.close
43
+ end
44
+ ```
45
+
46
+ Use bound values for data. Do not build SQL by interpolating request
47
+ parameters, usernames, or search terms.
48
+
49
+ ## Rails embedded configuration
50
+
51
+ ```yaml
52
+ development:
53
+ adapter: rubydb
54
+ embedded: true
55
+ database: <%= ENV.fetch("RUBYDB_DATABASE", Rails.root.join("tmp/development.rdb")) %>
56
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
57
+ ```
58
+
59
+ For tests, use a different path. For a one-process production service, put the
60
+ database on persistent storage, set restrictive filesystem permissions, and
61
+ ensure the service manager starts only one owner. If the deployment scales the
62
+ service horizontally, embedded mode is the wrong topology.
63
+
64
+ ## Inspect and back up the file
65
+
66
+ The repository CLI has read-only inspection, verified backup, restore, and
67
+ doctor commands:
68
+
69
+ ```sh
70
+ rubydb doctor --quick --json
71
+ rubydb inspect --database tmp/service.rdb --stats --wal
72
+ rubydb backup --database tmp/service.rdb --dir tmp/backups --type full --compress
73
+ ```
74
+
75
+ Check the installed command’s help before automating a version-specific option:
76
+
77
+ ```sh
78
+ rubydb doctor --help
79
+ rubydb backup --help
80
+ rubydb restore --help
81
+ ```
82
+
83
+ Keep the backup directory on a different disk or host in production. A second
84
+ copy on the same failed disk is not a recovery plan.
85
+
86
+ ## What embedded mode does not solve
87
+
88
+ Embedded mode does not provide a network endpoint, cross-host failover,
89
+ automatic leader election, a connection pool shared by processes, or a managed
90
+ cloud backup. Those are deployment responsibilities. If the workload needs
91
+ them, move to server/client mode or choose a managed PostgreSQL service.
92
+
93
+ ## Checkpoint
94
+
95
+ Prove that the owner rule is enforceable: document the process that opens the
96
+ file, the persistent path, the backup destination, and the restore operator.
97
+ Then deliberately run the application with two processes in a disposable test
98
+ environment and confirm your deployment prevents shared-file access. Continue
99
+ to [lesson 4](04-rails-complex-apps.md) for Rails query and migration validation.
@@ -0,0 +1,138 @@
1
+ # Lesson 4: Rails and complex application code
2
+
3
+ RubyDB can be useful for a Rails application when the application stays within
4
+ the adapter’s tested surface. Complex Rails code is still possible, but every
5
+ important query and migration must be exercised against the exact database
6
+ topology you will deploy.
7
+
8
+ ## Pin the adapter and configure the boundary
9
+
10
+ ```ruby
11
+ # Gemfile
12
+ gem "rubydb", "0.1.6"
13
+ gem "rubydb-activerecord", "0.1.3"
14
+ ```
15
+
16
+ Embedded development configuration:
17
+
18
+ ```yaml
19
+ # config/database.yml
20
+ development:
21
+ adapter: rubydb
22
+ embedded: true
23
+ database: <%= Rails.root.join("tmp/development.rdb") %>
24
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
25
+
26
+ test:
27
+ adapter: rubydb
28
+ embedded: true
29
+ database: <%= Rails.root.join("tmp/test.rdb") %>
30
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
31
+ ```
32
+
33
+ For server mode, use one connection URL supplied by the deployment:
34
+
35
+ ```yaml
36
+ production:
37
+ adapter: rubydb
38
+ embedded: false
39
+ url: <%= ENV.fetch("RUBYDB_URL") %>
40
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
41
+ ```
42
+
43
+ The RubyDB adapter URL is `rubydb://` or TLS-enabled `rubydbs://`; it is not a
44
+ PostgreSQL `DATABASE_URL`.
45
+
46
+ ## Associations, joins, and eager loading
47
+
48
+ Start with ordinary Rails models:
49
+
50
+ ```ruby
51
+ class Account < ApplicationRecord
52
+ has_many :projects, dependent: :destroy
53
+ end
54
+
55
+ class Project < ApplicationRecord
56
+ belongs_to :account
57
+ has_many :tasks, dependent: :destroy
58
+
59
+ scope :active, -> { where(status: "active") }
60
+ end
61
+
62
+ class Task < ApplicationRecord
63
+ belongs_to :project
64
+ end
65
+ ```
66
+
67
+ Exercise both the SQL shape and the object-loading behavior:
68
+
69
+ ```ruby
70
+ accounts = Account
71
+ .joins(:projects)
72
+ .merge(Project.active)
73
+ .where(projects: { archived: false })
74
+ .includes(projects: :tasks)
75
+ .distinct
76
+ .order(:name)
77
+
78
+ accounts.each do |account|
79
+ account.projects.each do |project|
80
+ puts [account.name, project.name, project.tasks.size].join(" | ")
81
+ end
82
+ end
83
+ ```
84
+
85
+ This example is a validation target, not a promise that every Arel variation
86
+ or database-specific query will work. Add request or model tests that assert
87
+ the result set, duplicate behavior, `NULL` behavior, ordering, and query count.
88
+
89
+ ## Transactions and migrations
90
+
91
+ Keep a transaction around a business operation and make retry behavior
92
+ explicit:
93
+
94
+ ```ruby
95
+ ApplicationRecord.transaction do
96
+ project = Project.create!(account: account, name: "Billing", status: "active")
97
+ project.tasks.create!(title: "Verify invoice", state: "open")
98
+ end
99
+ ```
100
+
101
+ A migration must be safe on an empty and populated database:
102
+
103
+ ```ruby
104
+ class AddStateToTasks < ActiveRecord::Migration[7.2]
105
+ def change
106
+ add_column :tasks, :state, :string, null: false, default: "open"
107
+ add_index :tasks, [:project_id, :state]
108
+ end
109
+ end
110
+ ```
111
+
112
+ For a large table, test the migration duration, lock behavior, disk usage, and
113
+ rollback boundary on a restored copy. Do not assume a migration that succeeds
114
+ on an empty local file is safe during live traffic.
115
+
116
+ ## The compatibility test matrix
117
+
118
+ Run the application suite for every supported combination, including the
119
+ database actually used in production:
120
+
121
+ ```sh
122
+ bundle exec rails db:drop db:create db:migrate
123
+ bundle exec rails test
124
+ bundle exec rails db:schema:dump
125
+ ```
126
+
127
+ Repeat that sequence with RubyDB embedded, RubyDB server/client, and
128
+ PostgreSQL when all three are supported. Compare schema dumps and test
129
+ business behavior, not merely process exit codes. Rails versions and adapter
130
+ versions should be pinned in CI; the project’s compatibility guide is the
131
+ source of truth for the currently tested matrix.
132
+
133
+ ## Checkpoint
134
+
135
+ The checkpoint passes when your suite covers joins, eager loading, nested
136
+ associations, transactions, constraints, indexes, a populated-table
137
+ migration, schema dump/load, and error handling. Continue to [lesson 5](05-rubydb-production-server.md)
138
+ to run the same application through a private RubyDB server.
@@ -0,0 +1,237 @@
1
+ # Lesson 5: RubyDB server production setup
2
+
3
+ Server mode puts one RubyDB process in charge of the data directory and lets
4
+ Ruby or Rails clients connect over the RubyDB protocol. This is the correct
5
+ RubyDB topology when several application processes need one database. The
6
+ application must never open the server’s `.rdb` files directly.
7
+
8
+ ## Provision the service
9
+
10
+ Use a dedicated service account and persistent storage. The commands below
11
+ assume a Unix-like host; adapt ownership commands to your platform:
12
+
13
+ ```sh
14
+ gem install rubydb -v 0.1.6
15
+ install -d -o rubydb -g rubydb -m 0700 /var/lib/rubydb/data
16
+ install -d -o rubydb -g rubydb -m 0750 /var/log/rubydb
17
+ install -d -o rubydb -g rubydb -m 0700 /etc/rubydb
18
+ ```
19
+
20
+ Keep `/var/lib/rubydb` on persistent storage and place backups in a separate
21
+ failure domain. Restrict the database port to the application network.
22
+
23
+ ## Configuration and startup
24
+
25
+ Create `/etc/rubydb/production.yml` from the repository’s production template.
26
+ Supply credentials and TLS paths through a secret manager or protected
27
+ deployment environment. A production configuration needs WAL, durable storage,
28
+ authentication, TLS, and bounded resources.
29
+
30
+ Start the supervised foreground process:
31
+
32
+ ```sh
33
+ rubydb --config /etc/rubydb/production.yml --env production start
34
+ ```
35
+
36
+ For a development smoke server, the CLI also accepts explicit settings:
37
+
38
+ ```sh
39
+ rubydb --env production start \
40
+ --host 127.0.0.1 \
41
+ --port 7432 \
42
+ --data-dir /var/lib/rubydb/data \
43
+ --log-dir /var/log/rubydb \
44
+ --pid-file /run/rubydb.pid
45
+ ```
46
+
47
+ Use systemd, a container supervisor, or the platform’s process manager to
48
+ restart the process and preserve logs. Do not let an orchestrator launch two
49
+ writers against one embedded path.
50
+
51
+ ## Straight-to-production deployment path
52
+
53
+ Use this path when RubyDB is the chosen production database for a bounded
54
+ service. It works for a Rails app or a regular Ruby app. For a massive shared
55
+ application, use the PostgreSQL path in [lesson 6](06-postgresql-massive-apps.md)
56
+ instead.
57
+
58
+ ### 1. Prepare the database host
59
+
60
+ The database host needs a persistent local volume, a dedicated service account,
61
+ and a private network route from the application. Run these commands as an
62
+ administrator and replace paths only after verifying the target host:
63
+
64
+ ```sh
65
+ gem install rubydb -v 0.1.6
66
+ useradd --system --home-dir /var/lib/rubydb --shell /usr/sbin/nologin rubydb
67
+ install -d -o rubydb -g rubydb -m 0700 /var/lib/rubydb/data
68
+ install -d -o rubydb -g rubydb -m 0750 /var/log/rubydb
69
+ install -d -o root -g rubydb -m 0750 /etc/rubydb
70
+ ```
71
+
72
+ Copy the reviewed `config/production.yml` from the RubyDB repository to
73
+ `/etc/rubydb/production.yml`. Keep the database directory and WAL on approved
74
+ persistent storage. Keep backups on another host or failure domain.
75
+
76
+ ### 2. Install secrets and TLS material
77
+
78
+ Use a CA-issued certificate for a real deployment. Do not use a self-signed
79
+ development certificate for public traffic. Inject these values through a
80
+ secret manager, protected service environment, or equivalent mechanism:
81
+
82
+ ```text
83
+ RUBYDB_USERNAME=app_rw
84
+ RUBYDB_PASSWORD=generated-secret
85
+ RUBYDB_SSL_ENABLED=true
86
+ RUBYDB_SSL_CERT_FILE=/etc/rubydb/tls/server.crt
87
+ RUBYDB_SSL_KEY_FILE=/etc/rubydb/tls/server.key
88
+ RUBYDB_SSL_CA_FILE=/etc/rubydb/tls/ca.crt
89
+ RUBYDB_SSL_VERIFY_PEER=true
90
+ ```
91
+
92
+ Restrict the private key and environment file to the service. Do not put
93
+ secrets in Git, Docker images, process arguments, shell history, or logs.
94
+ Allow port `7432` only from the application and administration networks.
95
+
96
+ ### 3. Start and verify RubyDB
97
+
98
+ Start the foreground process under a service manager such as systemd:
99
+
100
+ ```sh
101
+ sudo -u rubydb env RUBYDB_USERNAME=app_rw RUBYDB_PASSWORD='from-secret-store' \
102
+ rubydb --config /etc/rubydb/production.yml --env production start
103
+ ```
104
+
105
+ Verify the server from the application network:
106
+
107
+ ```sh
108
+ rubydb --config /etc/rubydb/production.yml --env production status --json
109
+ rubydb --config /etc/rubydb/production.yml --env production doctor --json
110
+ ```
111
+
112
+ Run an authenticated TLS query using the same URL that the app will use. The
113
+ password below is intentionally a secret-manager value, not a real credential:
114
+
115
+ ```sh
116
+ RUBYDB_URL='rubydbs://app_rw:URL_ENCODED_PASSWORD@db.internal:7432/app?verify_peer=true&ca_file=%2Fetc%2Frubydb%2Ftls%2Fca.crt' \
117
+ ruby -rrubydb -e 'c=RubyDB::Client::Client.new(url: ENV.fetch("RUBYDB_URL")); p c.query("SELECT 1").to_hash; c.disconnect'
118
+ ```
119
+
120
+ ### 4. Deploy Rails or Ruby
121
+
122
+ For Rails, use server mode and inject the URL:
123
+
124
+ ```yaml
125
+ # config/database.yml
126
+ production:
127
+ adapter: rubydb
128
+ embedded: false
129
+ url: <%= ENV.fetch("RUBYDB_URL") %>
130
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
131
+ ```
132
+
133
+ For a regular Ruby service:
134
+
135
+ ```ruby
136
+ require "rubydb"
137
+
138
+ client = RubyDB::Client::Client.new(url: ENV.fetch("RUBYDB_URL"))
139
+ begin
140
+ result = client.query("SELECT 1")
141
+ puts result.to_hash
142
+ ensure
143
+ client.disconnect
144
+ end
145
+ ```
146
+
147
+ Deploy the application artifact with the pinned RubyDB gems, then run schema
148
+ migrations once from a controlled release job:
149
+
150
+ ```sh
151
+ RAILS_ENV=production bundle exec rails db:migrate
152
+ RAILS_ENV=production bundle exec rails db:migrate:status
153
+ RAILS_ENV=production bundle exec rails runner 'puts ApplicationRecord.connection.select_value("SELECT 1")'
154
+ ```
155
+
156
+ The web and worker processes connect to `RUBYDB_URL`; they never mount or open
157
+ the server’s database directory.
158
+
159
+ ### 5. Back up before accepting traffic
160
+
161
+ Create and verify a full backup, then perform a restore into an inactive path:
162
+
163
+ ```sh
164
+ rubydb backup --database /var/lib/rubydb/data/app.rdb \
165
+ --dir /var/backups/rubydb --type full --compress
166
+ rubydb restore --database /var/lib/rubydb/restore-check.rdb \
167
+ --dir /var/backups/rubydb --latest --dry-run
168
+ rubydb restore --database /var/lib/rubydb/restore-check.rdb \
169
+ --dir /var/backups/rubydb --latest --force
170
+ ```
171
+
172
+ Complete an actual isolated restore and run representative reads before the
173
+ first public request. Record the backup checksum, restore duration, RPO, RTO,
174
+ and the operator responsible.
175
+
176
+ ### 6. Release traffic gradually
177
+
178
+ Start with a canary or a small percentage of traffic. Verify one read, one
179
+ write transaction, one background job, and one application error path. Watch
180
+ latency, errors, active connections, WAL/checkpoint growth, memory, and free
181
+ disk. Keep the previous app artifact and verified backup until the rollback
182
+ window closes.
183
+
184
+ This path makes RubyDB usable in production for a validated bounded workload;
185
+ it does not provide automatic multi-host high availability by itself. If the
186
+ service needs automatic election, cross-host failover, or PostgreSQL-specific
187
+ SQL, stop and complete the corresponding validation or choose PostgreSQL.
188
+
189
+ ## Rails connection
190
+
191
+ ```yaml
192
+ # config/database.yml
193
+ production:
194
+ adapter: rubydb
195
+ embedded: false
196
+ url: <%= ENV.fetch("RUBYDB_URL") %>
197
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
198
+ ```
199
+
200
+ Use TLS verification in the URL:
201
+
202
+ ```text
203
+ rubydbs://app_user:URL_ENCODED_PASSWORD@db.internal.example:7432/app?verify_peer=true&ca_file=%2Fetc%2Frubydb%2Fca.crt
204
+ ```
205
+
206
+ Inject `RUBYDB_URL` from a secret manager. Percent-encode special characters
207
+ in credentials, and never print the complete URL. The server must separately
208
+ define its authentication credentials, certificate, private key, and CA.
209
+
210
+ ## Smoke test before traffic
211
+
212
+ ```sh
213
+ rubydb --config /etc/rubydb/production.yml --env production status --json
214
+ rubydb --config /etc/rubydb/production.yml --env production doctor --quick --json
215
+ RAILS_ENV=production bundle exec rails db:migrate:status
216
+ RAILS_ENV=production bundle exec rails runner 'puts ApplicationRecord.connection.select_value("SELECT 1")'
217
+ ```
218
+
219
+ Then exercise one authenticated read, one transaction that creates and updates
220
+ data, one background job, and one verified backup/restore drill. Capture the
221
+ RubyDB version, application release, configuration checksum, and test output.
222
+
223
+ ## Cloud deployment shape
224
+
225
+ On a platform such as Render, use a private database service with a persistent
226
+ disk and a separate web service. Put the private hostname in `RUBYDB_URL` and
227
+ keep both services in the same region. A web-service scale-out is not database
228
+ high availability; maintain external backups and validate a failover design
229
+ before relying on it.
230
+
231
+ ## Checkpoint
232
+
233
+ The checkpoint passes when multiple app processes connect over TLS, writes are
234
+ visible to another client, migrations run once from a release job, readiness
235
+ checks are monitored, and a restore has been tested into a new directory.
236
+ Continue to [lesson 6](06-postgresql-massive-apps.md) to decide when PostgreSQL
237
+ is the better production choice.
@@ -0,0 +1,96 @@
1
+ # Lesson 6: PostgreSQL for large applications
2
+
3
+ For a massive or business-critical application, PostgreSQL is usually the
4
+ safer system of record. It has a mature ecosystem of managed services,
5
+ replication, backup tooling, connection poolers, extensions, observability,
6
+ and Rails deployment experience. Choose it when many app instances, large
7
+ tables, broad SQL compatibility, or a large operations team are requirements.
8
+
9
+ RubyDB can still be valuable during development or as a bounded service. The
10
+ choice is architectural: do not make one database responsible for a workload
11
+ whose concurrency, failover, or SQL requirements it has not passed.
12
+
13
+ ## Rails configuration
14
+
15
+ Add the PostgreSQL adapter:
16
+
17
+ ```ruby
18
+ # Gemfile
19
+ gem "pg"
20
+ ```
21
+
22
+ Configure production with the provider’s secret connection URL:
23
+
24
+ ```yaml
25
+ # config/database.yml
26
+ production:
27
+ url: <%= ENV.fetch("DATABASE_URL") %>
28
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
29
+ checkout_timeout: <%= ENV.fetch("DB_CHECKOUT_TIMEOUT", "5") %>
30
+ ```
31
+
32
+ The URL is normally shaped like this; use a generated password rather than
33
+ the sample credentials:
34
+
35
+ ```text
36
+ postgresql://app_user:URL_ENCODED_PASSWORD@postgres.internal:5432/app_production
37
+ ```
38
+
39
+ Run migrations from one controlled release job:
40
+
41
+ ```sh
42
+ RAILS_ENV=production bundle exec rails db:migrate
43
+ RAILS_ENV=production bundle exec rails db:migrate:status
44
+ ```
45
+
46
+ Do not run `db:migrate` concurrently from every web instance.
47
+
48
+ ## Local parity
49
+
50
+ Use a pinned PostgreSQL major version locally and in CI:
51
+
52
+ ```sh
53
+ docker run --name lesson-postgres \
54
+ --env POSTGRES_PASSWORD=devpassword \
55
+ --env POSTGRES_DB=app_development \
56
+ --publish 5432:5432 \
57
+ --detach postgres:16
58
+
59
+ DATABASE_URL=postgresql://postgres:devpassword@127.0.0.1:5432/app_development \
60
+ bundle exec rails db:migrate
61
+ ```
62
+
63
+ The password is for a disposable local container only. Use secret storage in
64
+ CI and production.
65
+
66
+ ## Connection pool sizing
67
+
68
+ Each Rails process can open up to its configured pool size. A first estimate is
69
+ `web_processes * pool_size + worker_pools + admin_headroom`, but the database
70
+ provider’s connection limit and actual workload decide the safe value. Measure
71
+ queue time and query latency. A pool setting that is larger than the database
72
+ limit causes timeouts rather than more throughput.
73
+
74
+ For larger deployments, evaluate a PostgreSQL-aware connection pooler and your
75
+ provider’s read-replica strategy. Test transactions, prepared statements,
76
+ failover behavior, and session settings with the pooler before production.
77
+
78
+ ## SQL and data features
79
+
80
+ PostgreSQL is appropriate when the application relies on PostgreSQL-specific
81
+ types, functions, extensions, advanced indexing, row-level locking, or complex
82
+ query plans. Keep those choices explicit in migrations and tests. RubyDB’s
83
+ adapter is not a promise that PostgreSQL SQL can run unchanged on RubyDB.
84
+
85
+ If the application starts on RubyDB and moves to PostgreSQL, run reviewed
86
+ migrations on a new PostgreSQL database, export and transform data explicitly,
87
+ compare row counts and business totals, and test the cutover. An environment
88
+ variable changes where new connections go; it does not convert an `.rdb` file.
89
+
90
+ ## Checkpoint
91
+
92
+ The checkpoint passes when the production Rails build can create a PostgreSQL
93
+ database, apply migrations once, run the full application test suite, take a
94
+ provider-supported backup, restore a staging copy, and demonstrate the
95
+ connection pool remains below the provider limit. Continue to [lesson 7](07-hybrid-microservices.md)
96
+ for a hybrid design using both databases responsibly.