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,323 @@
1
+ # Lesson 11 — Build a RubyDB adapter for your community
2
+
3
+ This lesson is for a developer who wants to make RubyDB available to another
4
+ language or framework community. The goal is a real, supportable adapter that
5
+ can be released independently—not a thin helper that concatenates SQL.
6
+
7
+ RubyDB adapters connect to a running RubyDB server. They do not open `.rdb`
8
+ files directly. One Ruby process owns an embedded database path; a Python,
9
+ Node.js, Go, Java, Rust, or framework application should connect to RubyDB in
10
+ server mode.
11
+
12
+ ## 1. Choose the community and the support boundary
13
+
14
+ Write this down before coding:
15
+
16
+ ```text
17
+ Adapter: rubydb-example
18
+ Language: Example 1.0+
19
+ Framework: Example Framework 4.x+
20
+ RubyDB: 0.1.x
21
+ Transport: rubydb:// for development, rubydbs:// for production
22
+ API: query, parameters, transactions, prepared statements, pooling
23
+ ```
24
+
25
+ Start with a narrow, honest support matrix. A language driver, an ORM adapter,
26
+ and a migration tool have different responsibilities:
27
+
28
+ - A language driver owns connections, parameters, results, errors, deadlines,
29
+ cancellation, transactions, TLS, and resource cleanup.
30
+ - An ORM adapter maps the ORM's query, type, transaction, schema, and pooling
31
+ APIs to the driver. It must not claim that RubyDB supports another database's
32
+ dialect just because the ORM has a familiar adapter name.
33
+ - A framework integration owns configuration, lifecycle hooks, health checks,
34
+ logging, and deployment conventions for that framework.
35
+
36
+ Do not promise PostgreSQL, MySQL, or SQLite compatibility unless the exact
37
+ syntax and behavior has been implemented and tested. Link users to RubyDB's
38
+ [SQL compatibility guide](../docs/sql/compatibility-guide.md) and state which
39
+ features your adapter supports.
40
+
41
+ ## 2. Study the reference implementations
42
+
43
+ Use the repository's adapters as working references:
44
+
45
+ - [`adapters/python`](../adapters/python) demonstrates a DB-API 2.0 client.
46
+ - [`adapters/rubydb`](../adapters/rubydb) demonstrates a TypeScript client,
47
+ promises, TLS, pooling, prepared statements, and timeout cancellation.
48
+ - [`adapters/activerecord`](../adapters/activerecord) demonstrates a Ruby ORM
49
+ integration and Rails schema behavior.
50
+
51
+ Read the [wire protocol guide](../docs/server/protocol.md),
52
+ [`spec/wire/protocol.md`](../spec/wire/protocol.md), and
53
+ [`spec/protocol/protocol.md`](../spec/protocol/protocol.md) together. The
54
+ implementation and executable tests are authoritative when a draft document
55
+ does not describe a detail.
56
+
57
+ ## 3. Create a maintainable package
58
+
59
+ Keep the adapter in its own repository or in `adapters/<community>` while it is
60
+ being developed. A useful layout is:
61
+
62
+ ```text
63
+ rubydb-example/
64
+ ├── README.md
65
+ ├── LICENSE
66
+ ├── CHANGELOG.md
67
+ ├── CONTRIBUTING.md
68
+ ├── SECURITY.md
69
+ ├── package-or-project-manifest
70
+ ├── src/
71
+ │ ├── connection
72
+ │ ├── protocol
73
+ │ ├── errors
74
+ │ ├── types
75
+ │ └── pool
76
+ ├── tests/
77
+ │ ├── unit/
78
+ │ ├── protocol/
79
+ │ ├── integration/
80
+ │ └── security/
81
+ └── examples/
82
+ └── basic_app/
83
+ ```
84
+
85
+ Keep public API types separate from socket and JSON code. This makes it
86
+ possible to replace the transport or add a framework integration without
87
+ making application code depend on internal protocol objects.
88
+
89
+ Use a package name that is valid for the target registry and clearly belongs
90
+ to your maintainers. For npm, new package names must be lowercase; a scoped
91
+ package therefore looks like `@your-scope/rubydb`, not `@YourScope/rubydb`.
92
+ See npm's [package naming guidance](https://docs.npmjs.com/creating-a-package-json-file)
93
+ before reserving a name. Never use `rubydb` alone for an unofficial package.
94
+
95
+ ## 4. Implement the protocol boundary
96
+
97
+ RubyDB uses bounded, newline-delimited JSON messages for its client/server
98
+ transport. Each message has an envelope with a `type`, an identifier, a
99
+ creation timestamp, and a payload. Keep the following invariants in the driver:
100
+
101
+ 1. Parse the RubyDB URL and reject unknown schemes. Support `rubydb://` for a
102
+ trusted private network and `rubydbs://` for TLS.
103
+ 2. Open one TCP or TLS connection and enable peer verification by default for
104
+ production TLS connections. Allow CA, client certificate, and key settings
105
+ through configuration or a secret manager, never through committed files.
106
+ 3. Apply a maximum frame size before allocating unbounded memory. Reject an
107
+ oversized request or response and close the connection safely.
108
+ 4. Send a handshake with the protocol version, client name, client version,
109
+ username, and database. Follow it with authentication and synchronization.
110
+ Fail closed when the server rejects any step.
111
+ 5. Give every request a unique ID and correlate responses by ID. Do not assume
112
+ that response order will remain safe if multiplexing or asynchronous
113
+ notifications are added later.
114
+ 6. Implement the supported operations: `query`, `prepare`, `execute`, `close`,
115
+ `begin`, `commit`, `rollback`, `ping`, and `terminate`.
116
+ 7. Keep parameter values separate from SQL. Encode supported scalar, array,
117
+ object, date/time, and binary values according to the adapter's documented
118
+ mapping. Never interpolate user input into a query string.
119
+ 8. Normalize result fields into the community's idioms while preserving column
120
+ metadata, rows, row count, affected rows, and insert identifiers.
121
+ 9. Map server error codes into stable public exception types. Include a safe
122
+ message and code, but do not expose passwords, TLS keys, or raw secrets in
123
+ logs.
124
+
125
+ A driver request should conceptually look like this; use the target language's
126
+ JSON and socket APIs rather than copying this pseudocode literally:
127
+
128
+ ```text
129
+ request_id = new_unique_id()
130
+ send {
131
+ type: "query",
132
+ id: request_id,
133
+ created_at: now_as_iso8601,
134
+ payload: {
135
+ sql: "SELECT id, name FROM users WHERE active = ?",
136
+ params: [true],
137
+ deadline_at: deadline_as_iso8601
138
+ }
139
+ }
140
+ response = read_and_match_id(request_id)
141
+ return normalize_result(response.payload.result || response.payload.data)
142
+ ```
143
+
144
+ The exact wire behavior belongs in protocol tests, not in assumptions hidden in
145
+ the adapter. If you need a protocol capability that RubyDB does not advertise,
146
+ open a design issue before inventing a private message type.
147
+
148
+ ## 5. Make transaction behavior explicit
149
+
150
+ Expose explicit `begin`, `commit`, and `rollback` operations. If your
151
+ community API has implicit transactions, document exactly when they begin and
152
+ how a connection returns to an idle state.
153
+
154
+ ```text
155
+ connection.begin()
156
+ try:
157
+ connection.execute(
158
+ "INSERT INTO events (name) VALUES (?)",
159
+ ["community-adapter.started"]
160
+ )
161
+ connection.commit()
162
+ except:
163
+ connection.rollback()
164
+ raise
165
+ ```
166
+
167
+ A connection must not be returned to a pool while it has an open transaction,
168
+ an active cursor, or an unclosed prepared statement. On network loss, mark the
169
+ connection unusable and roll back locally; do not silently reuse it.
170
+
171
+ ## 6. Implement deadlines and cancellation safely
172
+
173
+ Every potentially long operation needs a bounded timeout. On timeout, send a
174
+ wire `cancel` request containing the timed-out request's ID, then drain or
175
+ close the connection according to the response. Cancellation is cooperative;
176
+ it is not permission to kill a thread while it owns database state.
177
+
178
+ Never automatically retry a write just because the client timed out. The write
179
+ may have committed before the response was lost. Tell users to use an
180
+ idempotency key or application-level deduplication for retryable writes.
181
+
182
+ Test all of these cases:
183
+
184
+ - timeout before the server starts execution;
185
+ - cancellation during a long-running query;
186
+ - a cancellation response for an unknown request;
187
+ - connection loss while cancellation is being sent; and
188
+ - a late response arriving after the caller has timed out.
189
+
190
+ ## 7. Add pooling without hiding failures
191
+
192
+ Provide a bounded pool only if the target community expects one. The pool must
193
+ have a maximum size, acquisition timeout, idle cleanup, connection validation,
194
+ and deterministic shutdown. A checkout/return API should make ownership clear:
195
+
196
+ ```text
197
+ pool = Pool(url, min_size=1, max_size=8)
198
+ try:
199
+ rows = pool.use(lambda db:
200
+ db.query("SELECT id FROM jobs WHERE state = ?", ["ready"]).rows
201
+ )
202
+ finally:
203
+ pool.close()
204
+ ```
205
+
206
+ Do not create one unbounded connection per request. Do not share one connection
207
+ between concurrent operations unless the API and protocol explicitly support
208
+ that behavior. Pool limits should be lower than the server's connection and
209
+ resource limits, with headroom for health checks and migrations.
210
+
211
+ ## 8. Test the adapter against a real server
212
+
213
+ A protocol fixture is useful for fast unit tests, but it cannot prove that the
214
+ adapter works. Add a live integration job that starts a pinned RubyDB server
215
+ and runs the adapter against a temporary database.
216
+
217
+ Minimum test groups:
218
+
219
+ - URL parsing, defaults, TLS options, type conversion, and public errors;
220
+ - fragmented frames, multiple frames, blank lines, malformed JSON, and
221
+ oversized frames;
222
+ - handshake, authentication failure, authorization failure, ping, and close;
223
+ - parameterized CRUD, `NULL`, booleans, numbers, dates, text, arrays, and JSON;
224
+ - prepared statements and statement cleanup;
225
+ - commit, rollback, transaction isolation expectations, and pool reuse;
226
+ - deadline, wire cancellation, late responses, and connection loss;
227
+ - concurrent operations up to the documented pool limit;
228
+ - server restart, backup/restore validation, and version mismatch behavior; and
229
+ - secrets absent from exceptions, logs, test output, and published artifacts.
230
+
231
+ Run the RubyDB repository checks first:
232
+
233
+ ```powershell
234
+ bundle install
235
+ bundle exec rspec
236
+ ```
237
+
238
+ Then run the adapter's fast and live suites. The exact commands depend on the
239
+ language, but the live suite should receive a URL from the environment rather
240
+ than hard-code credentials:
241
+
242
+ ```powershell
243
+ $env:RUBYDB_URL = "rubydb://rubydb@127.0.0.1:7432/rubydb"
244
+ your-package-test-command
245
+ your-package-live-integration-command
246
+ ```
247
+
248
+ Repeat the live suite over `rubydbs://` with a test CA. Add property or fuzz
249
+ tests for the frame decoder and parameter encoder. A driver that passes only a
250
+ mock server test is not ready for a community release.
251
+
252
+ ## 9. Document production usage
253
+
254
+ Your README should include copy-and-paste examples for:
255
+
256
+ 1. local development with an isolated database;
257
+ 2. starting RubyDB server mode;
258
+ 3. setting `RUBYDB_URL` through the platform's secret store;
259
+ 4. TLS with peer verification and certificate rotation;
260
+ 5. pool sizing and request timeouts;
261
+ 6. migrations and backup/restore procedures;
262
+ 7. health checks and metrics; and
263
+ 8. unsupported SQL, RubyDB versions, operating systems, and framework versions.
264
+
265
+ Show users the production shape:
266
+
267
+ ```text
268
+ Application processes ──TLS/private network──> RubyDB server
269
+ secrets from manager durable storage + backups
270
+ ```
271
+
272
+ The adapter is not the database server, a backup system, or a failover
273
+ controller. Link to RubyDB's [production operations guide](../docs/operations/production-guide.md)
274
+ and require users to validate their own workload before making availability or
275
+ durability claims.
276
+
277
+ ## 10. Release and maintain the community package
278
+
279
+ Before the first release:
280
+
281
+ - choose a license and add a changelog;
282
+ - publish a support matrix and compatibility policy;
283
+ - enable CI on every supported language/runtime version;
284
+ - run unit, live, security, fuzz, and package-content checks;
285
+ - verify the package contains no `.env`, credentials, private keys, databases,
286
+ build caches, or test secrets;
287
+ - use trusted publishing or a short-lived registry token in CI;
288
+ - sign releases when the target registry supports signing;
289
+ - tag the source commit and record the RubyDB protocol/server version; and
290
+ - provide a security contact and a responsible disclosure policy.
291
+
292
+ For an npm package, inspect the tarball before publishing:
293
+
294
+ ```sh
295
+ npm test
296
+ npm pack --dry-run
297
+ npm publish --access public
298
+ ```
299
+
300
+ For Python, Ruby, Rust, or another registry, use that ecosystem's equivalent
301
+ build, metadata, signature, and upload checks. Release the adapter separately
302
+ from RubyDB and pin compatible versions in the package metadata. After release,
303
+ install the package in a clean environment and rerun the live smoke test.
304
+
305
+ ## Community contribution checklist
306
+
307
+ Open a pull request or design issue with:
308
+
309
+ ```text
310
+ [ ] Adapter name, owner, license, and supported versions are listed.
311
+ [ ] Server-mode boundary and embedded-mode limitation are documented.
312
+ [ ] Parameter binding is used for every user value.
313
+ [ ] Frame limits, malformed input, TLS verification, and secret handling exist.
314
+ [ ] Transactions, timeouts, cancellation, and connection cleanup are tested.
315
+ [ ] Live tests pass against a pinned RubyDB server.
316
+ [ ] Package contents and release provenance are checked.
317
+ [ ] README includes installation, examples, operations, and limitations.
318
+ ```
319
+
320
+ The adapter becomes part of the wider RubyDB ecosystem when users can install
321
+ it, understand its limits, run a real query, observe failures, and upgrade it
322
+ without guessing. That standard protects both RubyDB users and the community
323
+ maintainer.
@@ -0,0 +1,263 @@
1
+ # Lesson 12: Build and pressure-test a Rails shop with RubyDB
2
+
3
+ This lesson uses the runnable application in
4
+ [`examples/rails_ecommerce`](../examples/rails_ecommerce/). It is intentionally
5
+ small enough to understand, but it exercises the database paths that usually
6
+ matter in a commerce service:
7
+
8
+ - catalog filters, ordering, limits, and compound indexes;
9
+ - grouped order summaries;
10
+ - customer/order/item associations and eager loading;
11
+ - a transaction that creates an order and updates inventory;
12
+ - direct ActiveRecord pressure and HTTP request pressure.
13
+
14
+ The example is a validation tool. A successful local run proves that this
15
+ specific application and workload work together; it is not a capacity promise
16
+ for every Rails application or deployment.
17
+
18
+ ## 1. Copy the example and install it
19
+
20
+ From a checkout of RubyDB:
21
+
22
+ ```sh
23
+ cd examples/rails_ecommerce
24
+ bundle install
25
+ ```
26
+
27
+ The example uses local paths to the RubyDB engine and ActiveRecord adapter, so
28
+ you can test the code currently checked out without publishing a new gem.
29
+ When using released gems in your own application, use pinned versions instead:
30
+
31
+ ```ruby
32
+ gem "rubydb", "0.1.6"
33
+ gem "rubydb-activerecord", "0.1.3"
34
+ ```
35
+
36
+ ## 2. Create and seed the local database
37
+
38
+ Embedded mode is the easiest local development setup. RubyDB creates the file
39
+ under `examples/rails_ecommerce/tmp/` and Rails talks to it through the real
40
+ ActiveRecord adapter. The example pins embedded Puma and the ActiveRecord pool
41
+ to one thread/connection because one embedded RubyDB path has one process-local
42
+ owner:
43
+
44
+ ```sh
45
+ bundle exec rails db:prepare
46
+ RUBYDB_PRODUCTS=500 RUBYDB_CUSTOMERS=100 RUBYDB_ORDERS=1000 bundle exec rails db:seed
47
+ bundle exec ruby script/smoke.rb
48
+ ```
49
+
50
+ PowerShell:
51
+
52
+ ```powershell
53
+ $env:RUBYDB_PRODUCTS = "500"
54
+ $env:RUBYDB_CUSTOMERS = "100"
55
+ $env:RUBYDB_ORDERS = "1000"
56
+ bundle exec rails db:prepare
57
+ bundle exec rails db:seed
58
+ bundle exec ruby script/smoke.rb
59
+ ```
60
+
61
+ The seed is deterministic. Change the three environment variables to create a
62
+ larger fixture without changing the application:
63
+
64
+ ```sh
65
+ RUBYDB_PRODUCTS=10000 RUBYDB_CUSTOMERS=2000 RUBYDB_ORDERS=25000 bundle exec rails db:seed
66
+ ```
67
+
68
+ ## 3. Understand the Rails queries
69
+
70
+ The catalog action uses a filtered and ordered relation:
71
+
72
+ ```ruby
73
+ Product.active
74
+ .in_category(params[:category])
75
+ .order(price_cents: :asc)
76
+ .limit(50)
77
+ ```
78
+
79
+ It also executes a grouped aggregate for the category navigation:
80
+
81
+ ```ruby
82
+ Product.active.group(:category).count
83
+ ```
84
+
85
+ The smoke test exercises eager loading and a grouped order query:
86
+
87
+ ```ruby
88
+ Order.completed.group(:status).count
89
+ Order.includes(:customer, :order_items).order(id: :desc).first
90
+ ```
91
+
92
+ The order action keeps the business write atomic:
93
+
94
+ ```ruby
95
+ Order.transaction do
96
+ order = customer.orders.create!(status: "paid", total_cents: total)
97
+ order.order_items.create!(product: product, quantity: quantity, unit_price_cents: price)
98
+ product.update!(stock: product.stock - quantity)
99
+ end
100
+ ```
101
+
102
+ In a real store, add an explicit inventory reservation strategy, idempotency
103
+ keys, payment authorization boundaries, audit records, and a concurrency test
104
+ for overselling. The small example keeps the business flow visible for
105
+ learning.
106
+
107
+ ## 4. Run direct database pressure
108
+
109
+ Run the database workload without HTTP overhead:
110
+
111
+ ```sh
112
+ RUBYDB_PRESSURE_OPERATIONS=1000 bundle exec ruby script/pressure.rb
113
+ ```
114
+
115
+ The output is JSON containing completed operations, errors, throughput, and
116
+ p50/p95/p99 latency. The workload randomly exercises catalog reads, a `LIKE`
117
+ filter, grouped order counts, and eager-loaded order history.
118
+
119
+ To add transaction writes:
120
+
121
+ ```sh
122
+ RUBYDB_PRESSURE_MODE=mixed \
123
+ RUBYDB_PRESSURE_WRITE_RATIO=0.10 \
124
+ RUBYDB_PRESSURE_OPERATIONS=1000 \
125
+ bundle exec ruby script/pressure.rb
126
+ ```
127
+
128
+ The script exits non-zero when any operation fails. Treat the first error as a
129
+ correctness issue to investigate, not as an acceptable benchmark result.
130
+
131
+ ## 5. Run the Rails app and HTTP pressure
132
+
133
+ Start the app:
134
+
135
+ ```sh
136
+ bundle exec rails server -b 127.0.0.1 -p 3002
137
+ ```
138
+
139
+ In a second terminal, send concurrent requests to the JSON catalog endpoint:
140
+
141
+ ```sh
142
+ RUBYDB_HTTP_THREADS=8 \
143
+ RUBYDB_HTTP_REQUESTS=500 \
144
+ bundle exec ruby script/http_pressure.rb
145
+ ```
146
+
147
+ PowerShell:
148
+
149
+ ```powershell
150
+ $env:RUBYDB_HTTP_THREADS = "8"
151
+ $env:RUBYDB_HTTP_REQUESTS = "500"
152
+ bundle exec ruby script/http_pressure.rb
153
+ ```
154
+
155
+ This measures Rails routing, controller work, serialization, and database
156
+ reads together. It does not replace a load test that runs from another host,
157
+ but it gives a reproducible local baseline.
158
+
159
+ ## 6. Test the multi-process topology
160
+
161
+ Embedded mode is for one process owning one database path. For concurrent web
162
+ traffic, web workers,
163
+ job workers, or more than one host, run a RubyDB server and connect over the
164
+ protocol. From the example directory, start the local server using the
165
+ repository executable:
166
+
167
+ ```sh
168
+ mkdir -p tmp/rubydb-commerce-server
169
+ bundle exec ruby ../../exe/rubydb start \
170
+ --host 127.0.0.1 \
171
+ --port 7432 \
172
+ --data-dir tmp/rubydb-commerce-server \
173
+ --log-dir tmp/rubydb-commerce-server/log
174
+ ```
175
+
176
+ In another terminal, migrate and seed through the server:
177
+
178
+ ```sh
179
+ RUBYDB_EMBEDDED=false \
180
+ RUBYDB_URL='rubydb://rubydb@127.0.0.1:7432/rubydb' \
181
+ bundle exec rails db:prepare
182
+
183
+ RUBYDB_EMBEDDED=false \
184
+ RUBYDB_URL='rubydb://rubydb@127.0.0.1:7432/rubydb' \
185
+ RUBYDB_PRODUCTS=1000 RUBYDB_CUSTOMERS=200 RUBYDB_ORDERS=5000 \
186
+ bundle exec rails db:seed
187
+ ```
188
+
189
+ Then run several Rails database workers against the server:
190
+
191
+ ```sh
192
+ RUBYDB_EMBEDDED=false \
193
+ RUBYDB_URL='rubydb://rubydb@127.0.0.1:7432/rubydb' \
194
+ RAILS_MAX_THREADS=8 \
195
+ RUBYDB_PRESSURE_THREADS=8 \
196
+ RUBYDB_PRESSURE_OPERATIONS=2000 \
197
+ bundle exec ruby script/pressure.rb
198
+ ```
199
+
200
+ For TLS, use a `rubydbs://` URL and configure certificate verification. In a
201
+ real deployment, inject the URL through a secret manager; do not commit a
202
+ password into `database.yml` or a benchmark script.
203
+
204
+ ## 7. Read the results correctly
205
+
206
+ Record the JSON output with the Git commit and fixture size. Compare:
207
+
208
+ 1. p50 for normal user requests;
209
+ 2. p95 and p99 for tail latency under pressure;
210
+ 3. throughput and error count;
211
+ 4. server CPU, memory, WAL growth, disk space, and rejected connections;
212
+ 5. results before and after enabling an accelerator policy.
213
+
214
+ Do not compare one tiny in-memory run with a production claim. Go acceleration
215
+ is adaptive: small queries can stay in Ruby when process/protocol overhead is
216
+ larger than the work, while eligible large immutable-snapshot operations can be
217
+ accelerated. Correct results, durable commits, and bounded resource use come
218
+ before a lower benchmark number.
219
+
220
+ ## 8. Run the accelerator A/B check
221
+
222
+ From the repository root, run the same 10,000-row workload with a Ruby-only
223
+ baseline and the required Go worker:
224
+
225
+ ```powershell
226
+ $env:RUBYDB_ACCELERATOR_ROWS = "10000"
227
+ $env:RUBYDB_ACCELERATOR_ITERATIONS = "5"
228
+ $env:RUBYDB_ACCELERATOR_THREADS = "4"
229
+ $env:RUBYDB_ACCELERATOR_REQUESTS = "5"
230
+ bundle exec ruby benchmarks/go_accelerator.rb
231
+ ```
232
+
233
+ The benchmark verifies the row result before and during measurement, performs
234
+ an explicit worker restart, and reports p50/p95/p99 latency, throughput, CPU,
235
+ RSS, concurrent-request errors, worker starts/restarts/failures, and a speed
236
+ gate. `performance_gate_passed` must be `true` for this exact workload and
237
+ machine before selecting `RUBYDB_ACCELERATOR=required`; otherwise leave the
238
+ policy at `auto` or `off`. A Go worker can be correct but slower when the
239
+ workload is dominated by copying Ruby objects through the process boundary.
240
+
241
+ For larger Rails pressure, use at least 10,000 products and 10,000 orders,
242
+ then run the direct and server-mode pressure commands above. Include the
243
+ result JSON, commit, Ruby/Rails versions, and host metrics in the performance
244
+ record.
245
+
246
+ ## 9. Production checklist for this app
247
+
248
+ Before using the pattern for a real service:
249
+
250
+ - pin Ruby, Rails, RubyDB, and adapter versions;
251
+ - use server mode for multiple processes and hosts;
252
+ - run migrations against a backup or restored staging copy first;
253
+ - configure TLS, authentication, connection limits, timeouts, and monitoring;
254
+ - test idempotent order creation and payment retries;
255
+ - test inventory contention and deadlock/timeout behavior;
256
+ - run backup, restore, restart, and disk-space drills;
257
+ - establish an application-specific p95/p99 SLO with representative data;
258
+ - keep PostgreSQL as the comparison target when the application needs its
259
+ broader SQL dialect, ecosystem, or large-scale operational guarantees.
260
+
261
+ This lesson makes RubyDB easy to try locally while keeping the boundary clear:
262
+ the benchmark measures the features the example actually uses, and production
263
+ readiness still requires validation of the exact application and topology.