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
@@ -1,295 +1,293 @@
1
- # frozen_string_literal: true
2
-
3
- require "time"
4
- require "set"
5
-
6
- module RubyDB
7
- module Replication
8
- # Durable slot state is represented by the primary while the replication
9
- # catalog is still file-backed. A slot retains the last acknowledged LSN
10
- # and prevents accidental reuse of a replica identity.
11
- class ReplicationSlot
12
- attr_reader :name, :primary
13
- attr_accessor :confirmed_lsn
14
-
15
- def initialize(name, primary)
16
- @name = name.to_s
17
- @primary = primary
18
- @confirmed_lsn = 0
19
- end
20
-
21
- def advance(lsn)
22
- @confirmed_lsn = [@confirmed_lsn, lsn.to_i].max
23
- end
24
- end
25
-
26
- # Failover - Handles automatic failover
27
- class Failover
28
- attr_reader :stats
29
-
30
- # Failover states
31
- STATE_NORMAL = :normal
32
- STATE_HEALTH_CHECK = :health_check
33
- STATE_DETECTED = :detected
34
- STATE_FAILING_OVER = :failing_over
35
- STATE_PROMOTING = :promoting
36
- STATE_VERIFYING = :verifying
37
- STATE_COMPLETE = :complete
38
- STATE_FAILED = :failed
39
-
40
- def initialize(replication_manager, config = {})
41
- @replication_manager = replication_manager
42
- @config = config
43
- @state = STATE_NORMAL
44
- @failover_triggered = false
45
- @failover_start_time = nil
46
- @candidate_replicas = []
47
- @chosen_candidate = nil
48
- @health_check_history = []
49
- @stats = {
50
- failovers: 0,
51
- successful_failovers: 0,
52
- failed_failovers: 0,
53
- total_failover_time_ms: 0,
54
- avg_failover_time_ms: 0,
55
- last_failover_time: nil,
56
- last_failover_result: nil,
57
- health_check_failures: 0,
58
- consecutive_failures: 0
59
- }
60
- @lock = Mutex.new
61
- @running = false
62
- @failover_thread = nil
63
- @failover_timeout = config[:timeout] || 30
64
- @check_interval = config[:check_interval] || 5
65
- @max_consecutive_failures = config[:max_consecutive_failures] || 3
66
- @failover_trigger = config[:trigger] || :auto
67
- end
68
-
69
- def start
70
- @lock.synchronize do
71
- return if @running
72
-
73
- @running = true
74
- @state = STATE_NORMAL
75
- @failover_thread = Thread.new { failover_loop }
76
-
77
- puts "Failover handler started"
78
- true
79
- end
80
- end
81
-
82
- def stop
83
- @lock.synchronize do
84
- return unless @running
85
-
86
- @running = false
87
-
88
- @failover_thread&.kill
89
- @failover_thread = nil
90
-
91
- puts "Failover handler stopped"
92
- true
93
- end
94
- end
95
-
96
- def trigger_failover(reason = "manual trigger")
97
- @lock.synchronize do
98
- return false if @failover_triggered
99
-
100
- @failover_triggered = true
101
- @failover_start_time = Time.now
102
- @stats[:failovers] += 1
103
-
104
- Thread.new do
105
- execute_failover(reason)
106
- end
107
-
108
- true
109
- end
110
- end
111
-
112
- def health_check
113
- trigger_reason = nil
114
- result = @lock.synchronize do
115
- result = @replication_manager.health_check
116
-
117
- @health_check_history << {
118
- timestamp: Time.now,
119
- result: result
120
- }
121
-
122
- if @health_check_history.size > 100
123
- @health_check_history.shift
124
- end
125
-
126
- if result[:healthy] == false
127
- @stats[:health_check_failures] += 1
128
- @stats[:consecutive_failures] += 1
129
-
130
- if @stats[:consecutive_failures] >= @max_consecutive_failures &&
131
- @failover_trigger == :auto &&
132
- !@failover_triggered
133
- trigger_reason = "health check failures"
134
- end
135
- else
136
- @stats[:consecutive_failures] = 0
137
- end
138
-
139
- result
140
- end
141
- trigger_failover(trigger_reason) if trigger_reason
142
- result
143
- end
144
-
145
- def stats
146
- @lock.synchronize do
147
- @stats.merge({
148
- state: @state,
149
- running: @running,
150
- failover_triggered: @failover_triggered,
151
- consecutive_failures: @stats[:consecutive_failures],
152
- health_check_history_size: @health_check_history.size
153
- })
154
- end
155
- end
156
-
157
- private
158
-
159
- def failover_loop
160
- while @running
161
- sleep(@check_interval)
162
- begin
163
- health_check
164
- rescue => e
165
- # Log error but continue
166
- end
167
- end
168
- end
169
-
170
- def execute_failover(reason)
171
- @lock.synchronize do
172
- @state = STATE_DETECTED
173
- start_time = Time.now
174
-
175
- begin
176
- # Step 1: Find candidate replicas
177
- @state = STATE_HEALTH_CHECK
178
- candidates = find_candidate_replicas
179
-
180
- if candidates.empty?
181
- @state = STATE_FAILED
182
- @stats[:failed_failovers] += 1
183
- return failover_result(false, "No candidate replicas found")
184
- end
185
-
186
- # Step 2: Choose best candidate
187
- @state = STATE_PROMOTING
188
- @chosen_candidate = choose_best_candidate(candidates)
189
-
190
- unless @chosen_candidate
191
- @state = STATE_FAILED
192
- @stats[:failed_failovers] += 1
193
- return failover_result(false, "No suitable candidate found")
194
- end
195
-
196
- # Step 3: Promote candidate
197
- @state = STATE_FAILING_OVER
198
- promote_result = promote_candidate(@chosen_candidate)
199
-
200
- unless promote_result[:success]
201
- @state = STATE_FAILED
202
- @stats[:failed_failovers] += 1
203
- return failover_result(false, "Promotion failed: #{promote_result[:error]}")
204
- end
205
-
206
- # Step 4: Verify promotion
207
- @state = STATE_VERIFYING
208
- verify_result = verify_promotion(@chosen_candidate)
209
-
210
- unless verify_result[:success]
211
- @state = STATE_FAILED
212
- @stats[:failed_failovers] += 1
213
- return failover_result(false, "Verification failed: #{verify_result[:error]}")
214
- end
215
-
216
- # Step 5: Complete failover
217
- @state = STATE_COMPLETE
218
- @stats[:successful_failovers] += 1
219
-
220
- elapsed_ms = (Time.now - start_time) * 1000
221
- @stats[:total_failover_time_ms] += elapsed_ms
222
- @stats[:avg_failover_time_ms] = @stats[:total_failover_time_ms] / @stats[:successful_failovers]
223
- @stats[:last_failover_time] = Time.now
224
-
225
- failover_result(true, "Failover completed successfully", {
226
- candidate: @chosen_candidate[:node_id],
227
- elapsed_ms: elapsed_ms,
228
- reason: reason
229
- })
230
-
231
- rescue => e
232
- @state = STATE_FAILED
233
- @stats[:failed_failovers] += 1
234
- failover_result(false, "Failover failed: #{e.message}")
235
- ensure
236
- @failover_triggered = false
237
- @chosen_candidate = nil
238
- end
239
- end
240
- end
241
-
242
- def find_candidate_replicas
243
- candidates = []
244
-
245
- replica = @replication_manager.replica
246
- status = replica&.replication_status
247
- if replica&.running? && status && status[:state] == Replica::STATE_SYNCED
248
- candidates << {
249
- node_id: @replication_manager.replica.config[:node_id] || "replica_1",
250
- host: replica.primary_host,
251
- port: replica.primary_port,
252
- status: status[:state].to_s,
253
- lag_ms: status[:lag_ms].to_i,
254
- replayed_lsn: status[:last_replayed_lsn].to_i
255
- }
256
- end
257
-
258
- candidates
259
- end
260
-
261
- def choose_best_candidate(candidates)
262
- candidates.min_by { |c| [c[:lag_ms] || 0, -(c[:replayed_lsn] || 0)] }
263
- end
264
-
265
- def promote_candidate(candidate)
266
- result = @replication_manager.promote_to_primary
267
- return { success: false, error: "Promotion manager returned no result" } unless result.is_a?(Hash)
268
-
269
- result[:success] ? result : { success: false, error: result[:error] || "Promotion failed" }
270
- end
271
-
272
- def verify_promotion(candidate)
273
- mode = @replication_manager.respond_to?(:mode) ? @replication_manager.mode : nil
274
- return { success: false, error: "Promotion did not enter primary mode" } unless mode == ReplicationManager::MODE_PRIMARY
275
-
276
- health = @replication_manager.health_check
277
- health[:healthy] ? { success: true, health: health } : { success: false, error: "Promoted node is unhealthy" }
278
- end
279
-
280
- def failover_result(success, message, extra = {})
281
- started_at = @failover_start_time || Time.now
282
- result = {
283
- success: success,
284
- message: message,
285
- timestamp: Time.now.iso8601,
286
- elapsed_ms: (Time.now - started_at) * 1000,
287
- state: @state
288
- }.merge(extra)
289
-
290
- @stats[:last_failover_result] = result
291
- result
292
- end
293
- end
294
- end
295
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module RubyDB
6
+ module Replication
7
+ # Durable slot state is represented by the primary while the replication
8
+ # catalog is still file-backed. A slot retains the last acknowledged LSN
9
+ # and prevents accidental reuse of a replica identity.
10
+ class ReplicationSlot
11
+ attr_reader :name, :primary
12
+ attr_accessor :confirmed_lsn
13
+
14
+ def initialize(name, primary)
15
+ @name = name.to_s
16
+ @primary = primary
17
+ @confirmed_lsn = 0
18
+ end
19
+
20
+ def advance(lsn)
21
+ @confirmed_lsn = [@confirmed_lsn, lsn.to_i].max
22
+ end
23
+ end
24
+
25
+ # Failover - Handles automatic failover
26
+ class Failover
27
+ attr_reader :stats
28
+
29
+ # Failover states
30
+ STATE_NORMAL = :normal
31
+ STATE_HEALTH_CHECK = :health_check
32
+ STATE_DETECTED = :detected
33
+ STATE_FAILING_OVER = :failing_over
34
+ STATE_PROMOTING = :promoting
35
+ STATE_VERIFYING = :verifying
36
+ STATE_COMPLETE = :complete
37
+ STATE_FAILED = :failed
38
+
39
+ def initialize(replication_manager, config = {})
40
+ @replication_manager = replication_manager
41
+ @config = config
42
+ @state = STATE_NORMAL
43
+ @failover_triggered = false
44
+ @failover_start_time = nil
45
+ @candidate_replicas = []
46
+ @chosen_candidate = nil
47
+ @health_check_history = []
48
+ @stats = {
49
+ failovers: 0,
50
+ successful_failovers: 0,
51
+ failed_failovers: 0,
52
+ total_failover_time_ms: 0,
53
+ avg_failover_time_ms: 0,
54
+ last_failover_time: nil,
55
+ last_failover_result: nil,
56
+ health_check_failures: 0,
57
+ consecutive_failures: 0
58
+ }
59
+ @lock = Mutex.new
60
+ @running = false
61
+ @failover_thread = nil
62
+ @failover_timeout = config[:timeout] || 30
63
+ @check_interval = config[:check_interval] || 5
64
+ @max_consecutive_failures = config[:max_consecutive_failures] || 3
65
+ @failover_trigger = config[:trigger] || :auto
66
+ end
67
+
68
+ def start
69
+ @lock.synchronize do
70
+ return if @running
71
+
72
+ @running = true
73
+ @state = STATE_NORMAL
74
+ @failover_thread = Thread.new { failover_loop }
75
+
76
+ puts "Failover handler started"
77
+ true
78
+ end
79
+ end
80
+
81
+ def stop
82
+ @lock.synchronize do
83
+ return unless @running
84
+
85
+ @running = false
86
+
87
+ @failover_thread&.kill
88
+ @failover_thread = nil
89
+
90
+ puts "Failover handler stopped"
91
+ true
92
+ end
93
+ end
94
+
95
+ def trigger_failover(reason = "manual trigger")
96
+ @lock.synchronize do
97
+ return false if @failover_triggered
98
+
99
+ @failover_triggered = true
100
+ @failover_start_time = Time.now
101
+ @stats[:failovers] += 1
102
+
103
+ Thread.new do
104
+ execute_failover(reason)
105
+ end
106
+
107
+ true
108
+ end
109
+ end
110
+
111
+ def health_check
112
+ trigger_reason = nil
113
+ result = @lock.synchronize do
114
+ result = @replication_manager.health_check
115
+
116
+ @health_check_history << {
117
+ timestamp: Time.now,
118
+ result: result
119
+ }
120
+
121
+ if @health_check_history.size > 100
122
+ @health_check_history.shift
123
+ end
124
+
125
+ if result[:healthy] == false
126
+ @stats[:health_check_failures] += 1
127
+ @stats[:consecutive_failures] += 1
128
+
129
+ if @stats[:consecutive_failures] >= @max_consecutive_failures &&
130
+ @failover_trigger == :auto &&
131
+ !@failover_triggered
132
+ trigger_reason = "health check failures"
133
+ end
134
+ else
135
+ @stats[:consecutive_failures] = 0
136
+ end
137
+
138
+ result
139
+ end
140
+ trigger_failover(trigger_reason) if trigger_reason
141
+ result
142
+ end
143
+
144
+ def stats
145
+ @lock.synchronize do
146
+ @stats.merge({
147
+ state: @state,
148
+ running: @running,
149
+ failover_triggered: @failover_triggered,
150
+ consecutive_failures: @stats[:consecutive_failures],
151
+ health_check_history_size: @health_check_history.size
152
+ })
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def failover_loop
159
+ while @running
160
+ sleep(@check_interval)
161
+ begin
162
+ health_check
163
+ rescue
164
+ # Log error but continue
165
+ end
166
+ end
167
+ end
168
+
169
+ def execute_failover(reason)
170
+ @lock.synchronize do
171
+ @state = STATE_DETECTED
172
+ start_time = Time.now
173
+
174
+ begin
175
+ # Step 1: Find candidate replicas
176
+ @state = STATE_HEALTH_CHECK
177
+ candidates = find_candidate_replicas
178
+
179
+ if candidates.empty?
180
+ @state = STATE_FAILED
181
+ @stats[:failed_failovers] += 1
182
+ return failover_result(false, "No candidate replicas found")
183
+ end
184
+
185
+ # Step 2: Choose best candidate
186
+ @state = STATE_PROMOTING
187
+ @chosen_candidate = choose_best_candidate(candidates)
188
+
189
+ unless @chosen_candidate
190
+ @state = STATE_FAILED
191
+ @stats[:failed_failovers] += 1
192
+ return failover_result(false, "No suitable candidate found")
193
+ end
194
+
195
+ # Step 3: Promote candidate
196
+ @state = STATE_FAILING_OVER
197
+ promote_result = promote_candidate(@chosen_candidate)
198
+
199
+ unless promote_result[:success]
200
+ @state = STATE_FAILED
201
+ @stats[:failed_failovers] += 1
202
+ return failover_result(false, "Promotion failed: #{promote_result[:error]}")
203
+ end
204
+
205
+ # Step 4: Verify promotion
206
+ @state = STATE_VERIFYING
207
+ verify_result = verify_promotion(@chosen_candidate)
208
+
209
+ unless verify_result[:success]
210
+ @state = STATE_FAILED
211
+ @stats[:failed_failovers] += 1
212
+ return failover_result(false, "Verification failed: #{verify_result[:error]}")
213
+ end
214
+
215
+ # Step 5: Complete failover
216
+ @state = STATE_COMPLETE
217
+ @stats[:successful_failovers] += 1
218
+
219
+ elapsed_ms = (Time.now - start_time) * 1000
220
+ @stats[:total_failover_time_ms] += elapsed_ms
221
+ @stats[:avg_failover_time_ms] = @stats[:total_failover_time_ms] / @stats[:successful_failovers]
222
+ @stats[:last_failover_time] = Time.now
223
+
224
+ failover_result(true, "Failover completed successfully", {
225
+ candidate: @chosen_candidate[:node_id],
226
+ elapsed_ms: elapsed_ms,
227
+ reason: reason
228
+ })
229
+ rescue => e
230
+ @state = STATE_FAILED
231
+ @stats[:failed_failovers] += 1
232
+ failover_result(false, "Failover failed: #{e.message}")
233
+ ensure
234
+ @failover_triggered = false
235
+ @chosen_candidate = nil
236
+ end
237
+ end
238
+ end
239
+
240
+ def find_candidate_replicas
241
+ candidates = []
242
+
243
+ replica = @replication_manager.replica
244
+ status = replica&.replication_status
245
+ if replica&.running? && status && status[:state] == Replica::STATE_SYNCED
246
+ candidates << {
247
+ node_id: @replication_manager.replica.config[:node_id] || "replica_1",
248
+ host: replica.primary_host,
249
+ port: replica.primary_port,
250
+ status: status[:state].to_s,
251
+ lag_ms: status[:lag_ms].to_i,
252
+ replayed_lsn: status[:last_replayed_lsn].to_i
253
+ }
254
+ end
255
+
256
+ candidates
257
+ end
258
+
259
+ def choose_best_candidate(candidates)
260
+ candidates.min_by { |c| [c[:lag_ms] || 0, -(c[:replayed_lsn] || 0)] }
261
+ end
262
+
263
+ def promote_candidate(candidate)
264
+ result = @replication_manager.promote_to_primary
265
+ return {success: false, error: "Promotion manager returned no result"} unless result.is_a?(Hash)
266
+
267
+ result[:success] ? result : {success: false, error: result[:error] || "Promotion failed"}
268
+ end
269
+
270
+ def verify_promotion(candidate)
271
+ mode = @replication_manager.respond_to?(:mode) ? @replication_manager.mode : nil
272
+ return {success: false, error: "Promotion did not enter primary mode"} unless mode == ReplicationManager::MODE_PRIMARY
273
+
274
+ health = @replication_manager.health_check
275
+ health[:healthy] ? {success: true, health: health} : {success: false, error: "Promoted node is unhealthy"}
276
+ end
277
+
278
+ def failover_result(success, message, extra = {})
279
+ started_at = @failover_start_time || Time.now
280
+ result = {
281
+ success: success,
282
+ message: message,
283
+ timestamp: Time.now.iso8601,
284
+ elapsed_ms: (Time.now - started_at) * 1000,
285
+ state: @state
286
+ }.merge(extra)
287
+
288
+ @stats[:last_failover_result] = result
289
+ result
290
+ end
291
+ end
292
+ end
293
+ end