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.
- checksums.yaml +4 -4
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -15
- data/.github/workflows/benchmark.yml +26 -26
- data/.github/workflows/compatibility.yml +63 -63
- data/.github/workflows/fuzz.yml +33 -33
- data/.github/workflows/lint.yml +21 -21
- data/.github/workflows/operations.yml +24 -24
- data/.github/workflows/production-validation.yml +111 -111
- data/.github/workflows/release.yml +77 -77
- data/.github/workflows/security.yml +39 -37
- data/.github/workflows/test.yml +26 -26
- data/.github/workflows/workload.yml +58 -58
- data/.gitignore +16 -5
- data/.rubocop.yml +50 -44
- data/.standard.yml +9 -14
- data/ARCHITECTURE.md +21 -21
- data/CHANGELOG.md +57 -27
- data/CODE_OF_CONDUCT.md +13 -13
- data/CONTRIBUTING.md +29 -29
- data/GOVERNANCE.md +16 -16
- data/Gemfile +18 -17
- data/Gemfile.lock +125 -71
- data/README.md +168 -12
- data/ROADMAP.md +27 -27
- data/Rakefile +76 -71
- data/SECURITY.md +54 -54
- data/SUPPORT.md +14 -14
- data/accelerator/bin/SHA256SUMS +6 -0
- data/accelerator/bin/rubydb-accelerator-darwin-amd64 +0 -0
- data/accelerator/bin/rubydb-accelerator-darwin-arm64 +0 -0
- data/accelerator/bin/rubydb-accelerator-linux-amd64 +0 -0
- data/accelerator/bin/rubydb-accelerator-linux-arm64 +0 -0
- data/accelerator/bin/rubydb-accelerator-windows-amd64.exe +0 -0
- data/accelerator/bin/rubydb-accelerator-windows-arm64.exe +0 -0
- data/accelerator/cmd/rubydb-accelerator/main.go +11 -0
- data/accelerator/go.mod +3 -0
- data/accelerator/internal/execution/aggregate.go +94 -0
- data/accelerator/internal/execution/distinct.go +22 -0
- data/accelerator/internal/execution/filter.go +73 -0
- data/accelerator/internal/execution/join.go +79 -0
- data/accelerator/internal/execution/operators.go +167 -0
- data/accelerator/internal/execution/scan.go +20 -0
- data/accelerator/internal/execution/sort.go +62 -0
- data/accelerator/internal/execution/types.go +136 -0
- data/accelerator/internal/execution/value.go +67 -0
- data/accelerator/internal/memory/arena.go +47 -0
- data/accelerator/internal/memory/reuse.go +22 -0
- data/accelerator/internal/metrics/registry.go +67 -0
- data/accelerator/internal/parallel/bounded_queue.go +56 -0
- data/accelerator/internal/parallel/scheduler.go +47 -0
- data/accelerator/internal/parallel/worker_pool.go +53 -0
- data/accelerator/internal/protocol/cancellation.go +48 -0
- data/accelerator/internal/protocol/columnar.go +263 -0
- data/accelerator/internal/protocol/frame.go +187 -0
- data/accelerator/internal/runtime/worker.go +521 -0
- data/accelerator/internal/storage/page_reader.go +81 -0
- data/accelerator/internal/storage/snapshot_scan.go +539 -0
- data/accelerator/internal/wal/checksum.go +13 -0
- data/accelerator/internal/wal/compression.go +41 -0
- data/accelerator/internal/wal/group_commit.go +24 -0
- data/accelerator/internal/wal/record_encoder.go +40 -0
- data/adapters/activerecord/Gemfile +11 -11
- data/adapters/activerecord/README.md +8 -3
- data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +881 -879
- data/adapters/activerecord/rubydb-activerecord.gemspec +21 -21
- data/adapters/activerecord/spec/rubydb_adapter_integration_spec.rb +143 -143
- data/adapters/ruby/README.md +18 -18
- data/adapters/sequel/README.md +11 -11
- data/config/monitoring/prometheus-alerts.yml +39 -39
- data/config/production.yml +36 -36
- data/docs/README.md +77 -71
- data/docs/architecture/concurrency.md +14 -14
- data/docs/architecture/current-state.md +125 -125
- data/docs/architecture/execution-engine.md +25 -25
- data/docs/architecture/go-accelerator.md +179 -0
- data/docs/architecture/indexes.md +19 -19
- data/docs/architecture/mvcc.md +19 -19
- data/docs/architecture/overview.md +13 -13
- data/docs/architecture/pages.md +11 -11
- data/docs/architecture/production-roadmap.md +82 -82
- data/docs/architecture/query-planner.md +20 -20
- data/docs/architecture/recovery.md +18 -18
- data/docs/architecture/sql-engine.md +12 -12
- data/docs/architecture/storage-engine.md +14 -14
- data/docs/architecture/transactions.md +10 -10
- data/docs/architecture/wal.md +28 -28
- data/docs/cli-cheatsheet.md +98 -98
- data/docs/cli.md +299 -275
- data/docs/contributing/architecture.md +9 -9
- data/docs/contributing/benchmarking.md +30 -14
- data/docs/contributing/development.md +16 -16
- data/docs/contributing/release-process.md +49 -49
- data/docs/contributing/testing.md +16 -16
- data/docs/debugging.md +229 -229
- data/docs/developer/branching.md +10 -10
- data/docs/developer/database-diff.md +10 -10
- data/docs/developer/local-development.md +49 -17
- data/docs/developer/snapshots.md +9 -9
- data/docs/developer/temporal-data.md +10 -10
- data/docs/developer-guide.md +297 -297
- data/docs/getting-started/first-database.md +16 -16
- data/docs/getting-started/first-query.md +13 -13
- data/docs/getting-started/installation.md +19 -19
- data/docs/getting-started/local-to-production.md +300 -300
- data/docs/getting-started/quickstart.md +17 -17
- data/docs/getting-started/rails.md +16 -16
- data/docs/hardening_backlog.md +93 -93
- data/docs/lessons-learned.md +112 -112
- data/docs/operations/backups.md +33 -33
- data/docs/operations/disaster-recovery.md +31 -31
- data/docs/operations/failover.md +30 -30
- data/docs/operations/monitoring.md +25 -25
- data/docs/operations/production-guide.md +295 -295
- data/docs/operations/production-runbook.md +45 -45
- data/docs/operations/replication.md +33 -33
- data/docs/operations/restore.md +6 -6
- data/docs/operations/runbook.md +34 -34
- data/docs/operations/upgrades.md +14 -14
- data/docs/operations/workload-testing.md +17 -17
- data/docs/production-readiness.md +118 -118
- data/docs/production_validation.md +150 -150
- data/docs/rails/active-record.md +11 -11
- data/docs/rails/compatibility-guide.md +90 -90
- data/docs/rails/database-yml.md +92 -92
- data/docs/rails/installation.md +17 -17
- data/docs/rails/migrations.md +17 -17
- data/docs/rails/production.md +82 -82
- data/docs/rails/troubleshooting.md +18 -18
- data/docs/release.md +25 -25
- data/docs/server/architecture.md +10 -10
- data/docs/server/authentication.md +10 -10
- data/docs/server/configuration.md +16 -16
- data/docs/server/connection-pooling.md +10 -10
- data/docs/server/deployment.md +10 -10
- data/docs/server/protocol.md +12 -12
- data/docs/sql/compatibility-guide.md +82 -82
- data/docs/sql/compatibility.md +39 -39
- data/docs/sql/data-types.md +10 -10
- data/docs/sql/functions.md +9 -9
- data/docs/sql/joins.md +9 -9
- data/docs/sql/operators.md +9 -9
- data/docs/sql/sqlite-compatibility.md +21 -21
- data/docs/sql/syntax.md +10 -10
- data/docs/sql/transactions.md +10 -10
- data/docs/troubleshooting.md +244 -244
- data/lessons/01-foundations.md +73 -0
- data/lessons/02-local-development.md +121 -0
- data/lessons/03-embedded-rubydb.md +99 -0
- data/lessons/04-rails-complex-apps.md +138 -0
- data/lessons/05-rubydb-production-server.md +237 -0
- data/lessons/06-postgresql-massive-apps.md +96 -0
- data/lessons/07-hybrid-microservices.md +179 -0
- data/lessons/08-migrations-backups-recovery.md +86 -0
- data/lessons/09-observability-security-scale.md +87 -0
- data/lessons/10-release-readiness.md +192 -0
- data/lessons/11-community-adapter.md +323 -0
- data/lessons/12-rails-ecommerce-pressure.md +263 -0
- data/lib/rubydb/accelerator/client.rb +451 -0
- data/lib/rubydb/accelerator/error.rb +22 -0
- data/lib/rubydb/accelerator/manager.rb +606 -0
- data/lib/rubydb/accelerator.rb +13 -0
- data/lib/rubydb/backup/archive.rb +332 -334
- data/lib/rubydb/backup/backup.rb +400 -401
- data/lib/rubydb/backup/incremental.rb +349 -353
- data/lib/rubydb/backup/restore.rb +289 -290
- data/lib/rubydb/backup/snapshot.rb +265 -267
- data/lib/rubydb/backup/verification.rb +276 -279
- data/lib/rubydb/branching/branch.rb +181 -181
- data/lib/rubydb/branching/branch_manager.rb +307 -311
- data/lib/rubydb/branching/branch_metadata.rb +140 -140
- data/lib/rubydb/branching/checkout.rb +165 -166
- data/lib/rubydb/branching/copy_on_write.rb +272 -272
- data/lib/rubydb/branching/diff.rb +137 -138
- data/lib/rubydb/branching/merge.rb +282 -285
- data/lib/rubydb/build_info.rb +15 -15
- data/lib/rubydb/catalog/catalog.rb +391 -391
- data/lib/rubydb/catalog/column.rb +112 -112
- data/lib/rubydb/catalog/constraint.rb +180 -180
- data/lib/rubydb/catalog/database.rb +184 -184
- data/lib/rubydb/catalog/index.rb +97 -97
- data/lib/rubydb/catalog/schema.rb +103 -103
- data/lib/rubydb/catalog/sequence.rb +90 -90
- data/lib/rubydb/catalog/system_catalog.rb +698 -698
- data/lib/rubydb/catalog/table.rb +178 -178
- data/lib/rubydb/catalog/trigger.rb +102 -102
- data/lib/rubydb/catalog/view.rb +66 -66
- data/lib/rubydb/cli/application.rb +168 -163
- data/lib/rubydb/cli/commands/accelerator.rb +72 -0
- data/lib/rubydb/cli/commands/backup.rb +80 -81
- data/lib/rubydb/cli/commands/branch.rb +72 -72
- data/lib/rubydb/cli/commands/checkout.rb +54 -54
- data/lib/rubydb/cli/commands/create.rb +58 -58
- data/lib/rubydb/cli/commands/diff.rb +76 -77
- data/lib/rubydb/cli/commands/doctor.rb +77 -74
- data/lib/rubydb/cli/commands/drop.rb +57 -57
- data/lib/rubydb/cli/commands/init.rb +101 -102
- data/lib/rubydb/cli/commands/inspect.rb +95 -95
- data/lib/rubydb/cli/commands/merge.rb +63 -63
- data/lib/rubydb/cli/commands/migrate.rb +62 -62
- data/lib/rubydb/cli/commands/restart.rb +42 -39
- data/lib/rubydb/cli/commands/restore.rb +121 -121
- data/lib/rubydb/cli/commands/shell.rb +365 -365
- data/lib/rubydb/cli/commands/snapshot.rb +79 -79
- data/lib/rubydb/cli/commands/start.rb +88 -82
- data/lib/rubydb/cli/commands/status.rb +96 -92
- data/lib/rubydb/cli/commands/stop.rb +47 -47
- data/lib/rubydb/cli/commands/vacuum.rb +58 -58
- data/lib/rubydb/cli/formatter.rb +221 -221
- data/lib/rubydb/cli/output.rb +168 -168
- data/lib/rubydb/client/client.rb +309 -304
- data/lib/rubydb/client/connection.rb +429 -415
- data/lib/rubydb/client/connection_pool.rb +168 -168
- data/lib/rubydb/client/connection_url.rb +96 -96
- data/lib/rubydb/client/prepared_statement.rb +60 -60
- data/lib/rubydb/client/result.rb +127 -123
- data/lib/rubydb/client/statement.rb +52 -52
- data/lib/rubydb/client/transaction.rb +130 -130
- data/lib/rubydb/concurrency/concurrency.rb +19 -19
- data/lib/rubydb/concurrency/deadlock_detector.rb +148 -150
- data/lib/rubydb/concurrency/latch.rb +101 -101
- data/lib/rubydb/concurrency/lock_graph.rb +163 -165
- data/lib/rubydb/concurrency/mutex.rb +181 -183
- data/lib/rubydb/concurrency/rw_lock.rb +180 -180
- data/lib/rubydb/concurrency/scheduler.rb +248 -250
- data/lib/rubydb/concurrency/worker_pool.rb +145 -143
- data/lib/rubydb/configuration/config.rb +170 -170
- data/lib/rubydb/configuration/defaults.rb +191 -179
- data/lib/rubydb/configuration/environment.rb +152 -152
- data/lib/rubydb/configuration/parser.rb +185 -185
- data/lib/rubydb/configuration/validation.rb +228 -221
- data/lib/rubydb/constants.rb +74 -74
- data/lib/rubydb/constraints/check.rb +181 -181
- data/lib/rubydb/constraints/constraint.rb +101 -101
- data/lib/rubydb/constraints/foreign_key.rb +130 -130
- data/lib/rubydb/constraints/not_null.rb +64 -64
- data/lib/rubydb/constraints/primary_key.rb +99 -99
- data/lib/rubydb/constraints/unique.rb +106 -108
- data/lib/rubydb/constraints/validator.rb +349 -350
- data/lib/rubydb/errors/authentication_error.rb +10 -10
- data/lib/rubydb/errors/authorization_error.rb +23 -23
- data/lib/rubydb/errors/client_error.rb +10 -10
- data/lib/rubydb/errors/configuration_error.rb +10 -10
- data/lib/rubydb/errors/connection_error.rb +10 -10
- data/lib/rubydb/errors/constraint_error.rb +23 -23
- data/lib/rubydb/errors/corruption_error.rb +10 -10
- data/lib/rubydb/errors/database_error.rb +10 -10
- data/lib/rubydb/errors/error.rb +20 -20
- data/lib/rubydb/errors/execution_error.rb +10 -10
- data/lib/rubydb/errors/parser_error.rb +10 -10
- data/lib/rubydb/errors/recovery_error.rb +10 -10
- data/lib/rubydb/errors/replication_error.rb +10 -10
- data/lib/rubydb/errors/server_error.rb +6 -6
- data/lib/rubydb/errors/storage_error.rb +10 -10
- data/lib/rubydb/errors/transaction_error.rb +10 -10
- data/lib/rubydb/execution/accelerator_dispatch.rb +30 -0
- data/lib/rubydb/execution/aggregate_executor.rb +134 -138
- data/lib/rubydb/execution/cost_model.rb +72 -0
- data/lib/rubydb/execution/delete_executor.rb +110 -112
- data/lib/rubydb/execution/distinct_executor.rb +131 -135
- data/lib/rubydb/execution/executor.rb +1544 -1188
- data/lib/rubydb/execution/expression.rb +191 -193
- data/lib/rubydb/execution/index_scan.rb +142 -142
- data/lib/rubydb/execution/insert_executor.rb +215 -217
- data/lib/rubydb/execution/join_executor.rb +243 -249
- data/lib/rubydb/execution/limit_executor.rb +83 -85
- data/lib/rubydb/execution/operator_selection.rb +57 -0
- data/lib/rubydb/execution/optimizer.rb +227 -215
- data/lib/rubydb/execution/physical_plan.rb +47 -0
- data/lib/rubydb/execution/plan.rb +355 -353
- data/lib/rubydb/execution/planner.rb +508 -536
- data/lib/rubydb/execution/predicate.rb +235 -235
- data/lib/rubydb/execution/scan.rb +49 -49
- data/lib/rubydb/execution/sequential_scan.rb +63 -63
- data/lib/rubydb/execution/sort_executor.rb +194 -185
- data/lib/rubydb/execution/update_executor.rb +160 -162
- data/lib/rubydb/functions/aggregate.rb +70 -70
- data/lib/rubydb/functions/date_functions.rb +274 -278
- data/lib/rubydb/functions/function.rb +85 -85
- data/lib/rubydb/functions/json_functions.rb +231 -215
- data/lib/rubydb/functions/numeric_functions.rb +346 -346
- data/lib/rubydb/functions/scalar.rb +52 -52
- data/lib/rubydb/functions/string_functions.rb +383 -383
- data/lib/rubydb/functions/system_functions.rb +258 -246
- data/lib/rubydb/history/as_of.rb +238 -238
- data/lib/rubydb/history/change.rb +105 -105
- data/lib/rubydb/history/history.rb +131 -131
- data/lib/rubydb/history/history_manager.rb +228 -229
- data/lib/rubydb/history/temporal_query.rb +202 -202
- data/lib/rubydb/history/timeline.rb +144 -144
- data/lib/rubydb/indexes/btree.rb +215 -186
- data/lib/rubydb/indexes/btree_cursor.rb +258 -258
- data/lib/rubydb/indexes/btree_node.rb +384 -385
- data/lib/rubydb/indexes/hash_index.rb +150 -150
- data/lib/rubydb/indexes/index.rb +71 -71
- data/lib/rubydb/indexes/index_manager.rb +408 -406
- data/lib/rubydb/indexes/index_scan.rb +466 -470
- data/lib/rubydb/migrations/migration.rb +253 -254
- data/lib/rubydb/migrations/migration_lock.rb +146 -146
- data/lib/rubydb/migrations/migration_manager.rb +187 -176
- data/lib/rubydb/migrations/migration_version.rb +71 -71
- data/lib/rubydb/migrations/schema_diff.rb +211 -211
- data/lib/rubydb/migrations/schema_version.rb +64 -64
- data/lib/rubydb/monitoring/events.rb +155 -160
- data/lib/rubydb/monitoring/health.rb +216 -222
- data/lib/rubydb/monitoring/logger.rb +188 -193
- data/lib/rubydb/monitoring/metrics.rb +363 -359
- data/lib/rubydb/monitoring/performance.rb +176 -176
- data/lib/rubydb/monitoring/statistics.rb +168 -170
- data/lib/rubydb/mvcc/garbage_collector.rb +199 -199
- data/lib/rubydb/mvcc/mvcc.rb +16 -16
- data/lib/rubydb/mvcc/snapshot.rb +146 -147
- data/lib/rubydb/mvcc/vacuum.rb +180 -180
- data/lib/rubydb/mvcc/version.rb +106 -106
- data/lib/rubydb/mvcc/version_store.rb +396 -398
- data/lib/rubydb/mvcc/visibility.rb +107 -109
- data/lib/rubydb/protocol/capabilities.rb +125 -125
- data/lib/rubydb/protocol/decoder.rb +142 -145
- data/lib/rubydb/protocol/encoder.rb +131 -136
- data/lib/rubydb/protocol/handshake.rb +306 -305
- data/lib/rubydb/protocol/message.rb +121 -121
- data/lib/rubydb/protocol/parameter_binder.rb +101 -0
- data/lib/rubydb/protocol/protocol.rb +276 -277
- data/lib/rubydb/protocol/version.rb +54 -54
- data/lib/rubydb/rails/adapter.rb +245 -239
- data/lib/rubydb/rails/connection.rb +312 -314
- data/lib/rubydb/rails/database_statements.rb +122 -122
- data/lib/rubydb/rails/migration.rb +131 -131
- data/lib/rubydb/rails/quoting.rb +109 -109
- data/lib/rubydb/rails/result.rb +117 -117
- data/lib/rubydb/rails/schema_statements.rb +339 -339
- data/lib/rubydb/rails/transaction.rb +105 -105
- data/lib/rubydb/rails/type.rb +126 -126
- data/lib/rubydb/recovery/checkpoint.rb +261 -257
- data/lib/rubydb/recovery/consistency.rb +457 -467
- data/lib/rubydb/recovery/corruption_detector.rb +5 -5
- data/lib/rubydb/recovery/crash_recovery.rb +381 -387
- data/lib/rubydb/recovery/recovery_manager.rb +204 -206
- data/lib/rubydb/recovery/redo.rb +235 -237
- data/lib/rubydb/recovery/undo.rb +204 -206
- data/lib/rubydb/replication/failover.rb +5 -5
- data/lib/rubydb/replication/fencing.rb +63 -63
- data/lib/rubydb/replication/primary.rb +461 -450
- data/lib/rubydb/replication/replica.rb +382 -384
- data/lib/rubydb/replication/replication_log.rb +194 -200
- data/lib/rubydb/replication/replication_manager.rb +307 -308
- data/lib/rubydb/replication/replication_slot.rb +293 -295
- data/lib/rubydb/replication/replication_stream.rb +198 -201
- data/lib/rubydb/rubydb.rb +570 -560
- data/lib/rubydb/security/access_control.rb +252 -254
- data/lib/rubydb/security/audit_log.rb +209 -213
- data/lib/rubydb/security/authentication.rb +302 -302
- data/lib/rubydb/security/authorization.rb +282 -282
- data/lib/rubydb/security/credentials.rb +192 -196
- data/lib/rubydb/security/password.rb +205 -215
- data/lib/rubydb/security/permissions.rb +74 -74
- data/lib/rubydb/security/role.rb +99 -101
- data/lib/rubydb/security/user.rb +86 -86
- data/lib/rubydb/server/connection.rb +383 -366
- data/lib/rubydb/server/connection_pool.rb +193 -193
- data/lib/rubydb/server/lifecycle.rb +227 -228
- data/lib/rubydb/server/listener.rb +139 -136
- data/lib/rubydb/server/request_handler.rb +277 -276
- data/lib/rubydb/server/server.rb +363 -364
- data/lib/rubydb/server/session.rb +416 -369
- data/lib/rubydb/server/worker.rb +206 -210
- data/lib/rubydb/server/worker_pool.rb +168 -168
- data/lib/rubydb/sql/ast/alter_table.rb +169 -169
- data/lib/rubydb/sql/ast/begin_transaction.rb +47 -47
- data/lib/rubydb/sql/ast/commit.rb +37 -37
- data/lib/rubydb/sql/ast/constraint.rb +92 -83
- data/lib/rubydb/sql/ast/create_database.rb +41 -41
- data/lib/rubydb/sql/ast/create_index.rb +61 -61
- data/lib/rubydb/sql/ast/create_schema.rb +52 -52
- data/lib/rubydb/sql/ast/create_table.rb +187 -187
- data/lib/rubydb/sql/ast/delete.rb +54 -54
- data/lib/rubydb/sql/ast/drop_database.rb +41 -41
- data/lib/rubydb/sql/ast/drop_index.rb +41 -41
- data/lib/rubydb/sql/ast/drop_schema.rb +49 -49
- data/lib/rubydb/sql/ast/drop_table.rb +49 -49
- data/lib/rubydb/sql/ast/explain.rb +64 -64
- data/lib/rubydb/sql/ast/expression.rb +617 -604
- data/lib/rubydb/sql/ast/insert.rb +66 -66
- data/lib/rubydb/sql/ast/node.rb +42 -42
- data/lib/rubydb/sql/ast/rollback.rb +63 -63
- data/lib/rubydb/sql/ast/savepoint.rb +59 -59
- data/lib/rubydb/sql/ast/select.rb +88 -88
- data/lib/rubydb/sql/ast/set_operation.rb +22 -20
- data/lib/rubydb/sql/ast/trigger.rb +35 -29
- data/lib/rubydb/sql/ast/update.rb +88 -88
- data/lib/rubydb/sql/ast/vacuum.rb +19 -19
- data/lib/rubydb/sql/ast/view.rb +38 -32
- data/lib/rubydb/sql/ast/with.rb +32 -32
- data/lib/rubydb/sql/grammar.rb +86 -86
- data/lib/rubydb/sql/keywords.rb +156 -156
- data/lib/rubydb/sql/lexer.rb +209 -214
- data/lib/rubydb/sql/operators.rb +100 -100
- data/lib/rubydb/sql/parser.rb +1167 -1170
- data/lib/rubydb/sql/planner/analyzer.rb +283 -302
- data/lib/rubydb/sql/planner/binder.rb +537 -550
- data/lib/rubydb/sql/planner/type_checker.rb +427 -431
- data/lib/rubydb/sql/token.rb +210 -210
- data/lib/rubydb/storage/buffer_frame.rb +44 -44
- data/lib/rubydb/storage/buffer_pool.rb +155 -155
- data/lib/rubydb/storage/database_lock.rb +74 -74
- data/lib/rubydb/storage/deserializer.rb +332 -342
- data/lib/rubydb/storage/engine.rb +2409 -2330
- data/lib/rubydb/storage/file_manager.rb +191 -187
- data/lib/rubydb/storage/free_space_map.rb +79 -81
- data/lib/rubydb/storage/page.rb +92 -94
- data/lib/rubydb/storage/page_allocator.rb +852 -855
- data/lib/rubydb/storage/page_header.rb +63 -67
- data/lib/rubydb/storage/page_manager.rb +127 -131
- data/lib/rubydb/storage/record.rb +58 -58
- data/lib/rubydb/storage/row.rb +78 -78
- data/lib/rubydb/storage/serializer.rb +51 -51
- data/lib/rubydb/storage/snapshot_reader.rb +167 -0
- data/lib/rubydb/storage/storage_layout.rb +151 -151
- data/lib/rubydb/storage/storage_manager.rb +114 -114
- data/lib/rubydb/storage/tuple.rb +458 -461
- data/lib/rubydb/storage/visibility_map.rb +964 -973
- data/lib/rubydb/transactions/commit_manager.rb +219 -220
- data/lib/rubydb/transactions/isolation.rb +98 -98
- data/lib/rubydb/transactions/lock.rb +76 -76
- data/lib/rubydb/transactions/lock_manager.rb +359 -362
- data/lib/rubydb/transactions/savepoint.rb +142 -143
- data/lib/rubydb/transactions/transaction.rb +214 -215
- data/lib/rubydb/transactions/transaction_id.rb +84 -84
- data/lib/rubydb/transactions/transaction_log.rb +256 -257
- data/lib/rubydb/transactions/transaction_manager.rb +434 -435
- data/lib/rubydb/types/bigint.rb +36 -36
- data/lib/rubydb/types/blob.rb +37 -37
- data/lib/rubydb/types/boolean.rb +34 -34
- data/lib/rubydb/types/date.rb +39 -39
- data/lib/rubydb/types/decimal.rb +48 -48
- data/lib/rubydb/types/float.rb +34 -34
- data/lib/rubydb/types/integer.rb +36 -36
- data/lib/rubydb/types/json.rb +41 -41
- data/lib/rubydb/types/null.rb +34 -34
- data/lib/rubydb/types/smallint.rb +36 -36
- data/lib/rubydb/types/text.rb +37 -37
- data/lib/rubydb/types/time.rb +46 -46
- data/lib/rubydb/types/timestamp.rb +39 -39
- data/lib/rubydb/types/type.rb +119 -119
- data/lib/rubydb/types/uuid.rb +47 -47
- data/lib/rubydb/types/varchar.rb +37 -37
- data/lib/rubydb/version.rb +32 -32
- data/lib/rubydb/wal/archive.rb +207 -193
- data/lib/rubydb/wal/checkpoint.rb +181 -183
- data/lib/rubydb/wal/lsn.rb +94 -94
- data/lib/rubydb/wal/reader.rb +259 -260
- data/lib/rubydb/wal/record.rb +105 -105
- data/lib/rubydb/wal/segment.rb +193 -193
- data/lib/rubydb/wal/wal.rb +481 -452
- data/lib/rubydb/wal/writer.rb +236 -236
- data/lib/rubydb.rb +7 -7
- data/packaging/docker/docker-compose.failover.yml +43 -43
- data/packaging/homebrew/rubydb.rb +19 -19
- data/rubydb.gemspec +70 -57
- data/scripts/benchmark +7 -7
- data/scripts/build_accelerator +49 -0
- data/scripts/durability_drill +37 -37
- data/scripts/fuzz +63 -63
- data/scripts/release +77 -42
- data/scripts/release_check +43 -43
- data/scripts/replication_failover_drill +268 -250
- data/scripts/replication_network_failover_drill +287 -255
- data/scripts/restore_drill +45 -45
- data/scripts/security +45 -0
- metadata +102 -1
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Lesson 7: a hybrid microservice architecture
|
|
2
|
+
|
|
3
|
+
A practical RubyDB architecture is to keep the large shared business system
|
|
4
|
+
on PostgreSQL and use RubyDB for a small service with a narrow responsibility.
|
|
5
|
+
Examples include a local catalog, a bounded document/index service, an
|
|
6
|
+
internal workflow, or a tenant-isolated tool whose SQL and recovery needs have
|
|
7
|
+
been validated.
|
|
8
|
+
|
|
9
|
+
## Give each service ownership
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
Rails monolith / public API
|
|
13
|
+
|
|
|
14
|
+
+--> PostgreSQL: users, billing, orders, reporting
|
|
15
|
+
|
|
|
16
|
+
+--> RubyDB service API: bounded internal records
|
|
17
|
+
|
|
|
18
|
+
+--> one RubyDB server and persistent data directory
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The services communicate through an API or an event contract. They do not
|
|
22
|
+
share an embedded file and they do not write directly into each other’s tables.
|
|
23
|
+
Each service owns its migrations, credentials, backups, alerts, and recovery
|
|
24
|
+
runbook.
|
|
25
|
+
|
|
26
|
+
## Ruby client for a service
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
# app/services/catalog_store.rb
|
|
30
|
+
require "rubydb"
|
|
31
|
+
|
|
32
|
+
class CatalogStore
|
|
33
|
+
def initialize(url: ENV.fetch("RUBYDB_URL"))
|
|
34
|
+
@client = RubyDB::Client::Client.new(url: url)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def find(code)
|
|
38
|
+
result = @client.query(
|
|
39
|
+
"SELECT code, title FROM catalog_items WHERE code = ?",
|
|
40
|
+
[code]
|
|
41
|
+
)
|
|
42
|
+
result.to_a.first
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def close
|
|
46
|
+
@client.disconnect
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Use the actual client API in the version pinned by the service and add tests
|
|
52
|
+
for connection failures, timeouts, duplicate requests, and empty results. In a
|
|
53
|
+
long-running app, put client lifecycle management in the application’s
|
|
54
|
+
dependency/container layer and close it during shutdown.
|
|
55
|
+
|
|
56
|
+
## Python services with the RubyDB adapter
|
|
57
|
+
|
|
58
|
+
Python applications connect to RubyDB server mode through the published
|
|
59
|
+
`rubydb-python` DB-API 2.0 adapter. The Python process must not open an
|
|
60
|
+
embedded `.rdb` file. Put the server URL in a secret-managed environment
|
|
61
|
+
variable:
|
|
62
|
+
|
|
63
|
+
```powershell
|
|
64
|
+
$env:RUBYDB_URL = "rubydbs://service_user:URL_ENCODED_PASSWORD@rubydb.internal:7432/orders?verify_peer=true&ca_file=%2Fetc%2Frubydb%2Ftls%2Fca.crt"
|
|
65
|
+
python -m pip install rubydb-python
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use parameterized queries and a bounded pool in workers:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import os
|
|
72
|
+
from rubydb import ConnectionPool
|
|
73
|
+
|
|
74
|
+
pool = ConnectionPool(os.environ["RUBYDB_URL"], min_size=1, max_size=8)
|
|
75
|
+
try:
|
|
76
|
+
with pool.connection() as connection:
|
|
77
|
+
with connection.cursor() as cursor:
|
|
78
|
+
cursor.execute(
|
|
79
|
+
"SELECT id, status FROM jobs WHERE account_id = ?",
|
|
80
|
+
[account_id],
|
|
81
|
+
)
|
|
82
|
+
rows = cursor.fetchall()
|
|
83
|
+
finally:
|
|
84
|
+
pool.close()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The adapter is synchronous DB-API code. In an async framework such as Flaxon,
|
|
88
|
+
run database calls in a worker thread so a slow query does not block the event
|
|
89
|
+
loop:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import asyncio
|
|
93
|
+
from rubydb import connect
|
|
94
|
+
|
|
95
|
+
async def load_jobs(url):
|
|
96
|
+
def query():
|
|
97
|
+
with connect(url, timeout=5) as db:
|
|
98
|
+
with db.cursor() as cursor:
|
|
99
|
+
cursor.execute("SELECT id, status FROM jobs ORDER BY id")
|
|
100
|
+
return cursor.fetchall()
|
|
101
|
+
|
|
102
|
+
return await asyncio.to_thread(query)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Run the complete examples in `examples/python_flask` and
|
|
106
|
+
`examples/python_flaxon`. Both examples use real RubyDB TCP traffic and have
|
|
107
|
+
live integration tests; they are intentionally small starting points, not a
|
|
108
|
+
replacement for application-specific authorization, migrations, backups,
|
|
109
|
+
timeouts, monitoring, and load testing.
|
|
110
|
+
|
|
111
|
+
## Node.js and TypeScript services
|
|
112
|
+
|
|
113
|
+
Node services use the `rubydb-node` package over the same RubyDB server
|
|
114
|
+
protocol:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
npm install rubydb-node
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { connect } from "rubydb-node";
|
|
122
|
+
|
|
123
|
+
const db = await connect(process.env.RUBYDB_URL!);
|
|
124
|
+
try {
|
|
125
|
+
const result = await db.query(
|
|
126
|
+
"SELECT id, state FROM jobs WHERE account_id = ?",
|
|
127
|
+
[accountId],
|
|
128
|
+
);
|
|
129
|
+
console.log(result.rows);
|
|
130
|
+
} finally {
|
|
131
|
+
await db.close();
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Use `ConnectionPool` for concurrent workers, keep the pool bounded per process,
|
|
136
|
+
and use `rubydbs://` with peer verification in production. The package is
|
|
137
|
+
TypeScript-first, supports prepared statements, transactions, timeouts with
|
|
138
|
+
wire cancellation, and does not access embedded database files. See
|
|
139
|
+
`adapters/rubydb/README.md` for the full Node release and operations boundary.
|
|
140
|
+
|
|
141
|
+
## A small Rails service
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
# service/config/database.yml
|
|
145
|
+
production:
|
|
146
|
+
adapter: rubydb
|
|
147
|
+
embedded: false
|
|
148
|
+
url: <%= ENV.fetch("RUBYDB_URL") %>
|
|
149
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Keep the API idempotent. A client timeout can happen after the server commits
|
|
153
|
+
a write, so a retry must use an idempotency key or first check the operation’s
|
|
154
|
+
result. For cross-service workflows, record an outbox/event in the owning
|
|
155
|
+
system and design consumers to tolerate duplicate delivery.
|
|
156
|
+
|
|
157
|
+
## What belongs where
|
|
158
|
+
|
|
159
|
+
Keep users, payments, orders, and cross-tenant reporting in PostgreSQL when
|
|
160
|
+
they need shared relational consistency and broad analytical tooling. Keep
|
|
161
|
+
RubyDB data that can be independently backed up, restored, migrated, and
|
|
162
|
+
reconciled. Do not split a single atomic business transaction across the two
|
|
163
|
+
databases unless you have designed and tested a distributed workflow.
|
|
164
|
+
|
|
165
|
+
## Failure and deployment rules
|
|
166
|
+
|
|
167
|
+
* Deploy the RubyDB service with a persistent volume and one server owner.
|
|
168
|
+
* Make the service private; clients use TLS and least-privilege credentials.
|
|
169
|
+
* Set bounded connection and request timeouts and expose a useful health probe.
|
|
170
|
+
* Retry only idempotent operations, with backoff and a maximum attempt count.
|
|
171
|
+
* Maintain a PostgreSQL and RubyDB restore drill independently.
|
|
172
|
+
* Version the API/event contract before changing either database schema.
|
|
173
|
+
|
|
174
|
+
## Checkpoint
|
|
175
|
+
|
|
176
|
+
The checkpoint passes when each data set has one owner, the API can tolerate a
|
|
177
|
+
restarted database service, duplicate requests do not create duplicate business
|
|
178
|
+
records, and the two systems can be restored independently. Continue to [lesson 8](08-migrations-backups-recovery.md)
|
|
179
|
+
for migration and recovery drills.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Lesson 8: migrations, backups, and recovery
|
|
2
|
+
|
|
3
|
+
Production data work is a controlled change to a recoverable system. A green
|
|
4
|
+
migration on an empty database is not enough. Test populated tables, rollback
|
|
5
|
+
boundaries, disk growth, indexes, locks, and application compatibility.
|
|
6
|
+
|
|
7
|
+
## RubyDB migration workflow
|
|
8
|
+
|
|
9
|
+
Preview and apply a migration against a disposable or staging copy:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
rubydb doctor --quick --json
|
|
13
|
+
rubydb backup --database data/app.rdb --dir backups --type full --compress
|
|
14
|
+
rubydb migrate --database data/app.rdb --path db/migrate --dry-run
|
|
15
|
+
rubydb migrate --database data/app.rdb --path db/migrate
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Run the migration once from a release job, not once per web process. Keep the
|
|
19
|
+
pre-migration backup and record the database version, application revision,
|
|
20
|
+
backup checksum, operator, and start/end times.
|
|
21
|
+
|
|
22
|
+
## Verified RubyDB backup and restore
|
|
23
|
+
|
|
24
|
+
Create a full backup with verification enabled, then dry-run the restore:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
rubydb backup --database data/app.rdb --dir backups --type full --compress
|
|
28
|
+
rubydb restore --dir backups --latest --dry-run
|
|
29
|
+
rubydb restore --database restored/app.rdb --dir backups --latest
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If a release depends on incremental or differential backups, retain the entire
|
|
33
|
+
required chain and its manifest. Keep backups away from the database disk and
|
|
34
|
+
test that a new host can read them. Restore into a new inactive path; do not
|
|
35
|
+
overwrite the only source with `--force`.
|
|
36
|
+
|
|
37
|
+
After restoring, verify both structure and meaning:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
rubydb inspect --database restored/app.rdb --stats --wal
|
|
41
|
+
rubydb shell --database restored/app.rdb --json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Run schema checks, row counts, foreign-key checks, representative application
|
|
45
|
+
queries, and business totals. Record the achieved RPO and RTO rather than
|
|
46
|
+
assuming the command’s success proves recoverability.
|
|
47
|
+
|
|
48
|
+
## PostgreSQL backup and restore
|
|
49
|
+
|
|
50
|
+
For PostgreSQL, use the provider’s managed backup/PITR feature where possible
|
|
51
|
+
and rehearse an independent logical backup:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
pg_dump --format=custom --file=tmp/app-staging.dump "$DATABASE_URL"
|
|
55
|
+
createdb app_restore
|
|
56
|
+
pg_restore --clean --if-exists --dbname=app_restore tmp/app-staging.dump
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The restore target must be isolated from production. Validate extensions,
|
|
60
|
+
roles, ownership, sequences, indexes, and application behavior after restore.
|
|
61
|
+
For a managed service, follow its documented restore and point-in-time
|
|
62
|
+
procedure instead of assuming local `createdb` access exists.
|
|
63
|
+
|
|
64
|
+
## Corruption and interrupted writes
|
|
65
|
+
|
|
66
|
+
When storage or recovery is suspect:
|
|
67
|
+
|
|
68
|
+
1. stop application writes and preserve the database, WAL, metadata, config,
|
|
69
|
+
and logs together;
|
|
70
|
+
2. copy the evidence to a separate incident location;
|
|
71
|
+
3. run `doctor --quick` and `inspect --stats --wal` on the copy;
|
|
72
|
+
4. restore the latest verified backup into a new path;
|
|
73
|
+
5. compare schema, row counts, checksums, and business totals; and
|
|
74
|
+
6. document what data was lost, replayed, or manually reconciled.
|
|
75
|
+
|
|
76
|
+
Do not “repair” corruption by deleting files, dropping tables, or running full
|
|
77
|
+
vacuum on the only copy. Use the repository’s durability and recovery drills
|
|
78
|
+
to rehearse interrupted checkpoints, full disks, corrupted files, compaction,
|
|
79
|
+
and restore at scale before an incident.
|
|
80
|
+
|
|
81
|
+
## Checkpoint
|
|
82
|
+
|
|
83
|
+
The checkpoint passes when an operator who did not create the backup can restore
|
|
84
|
+
it on another path, prove the data is usable, and state measured RPO/RTO. Keep
|
|
85
|
+
the report with the release evidence. Continue to [lesson 9](09-observability-security-scale.md)
|
|
86
|
+
for operations and security.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Lesson 9: observability, security, and scale
|
|
2
|
+
|
|
3
|
+
Production readiness is an operating discipline. A database is ready only when
|
|
4
|
+
the team can see failures, limit damage, rotate credentials, and make a safe
|
|
5
|
+
recovery decision under pressure.
|
|
6
|
+
|
|
7
|
+
## Protect the connection
|
|
8
|
+
|
|
9
|
+
For RubyDB server mode, use a private network, password authentication, and
|
|
10
|
+
TLS with peer verification:
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
rubydbs://app_user:URL_ENCODED_PASSWORD@db.internal.example:7432/app?verify_peer=true&ca_file=%2Fetc%2Frubydb%2Fca.crt
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Store `RUBYDB_URL`, `RUBYDB_PASSWORD`, certificates, and private keys in the
|
|
17
|
+
deployment secret manager. Do not commit them, put them in a Docker image, or
|
|
18
|
+
print them in health checks. Give the application only the database privileges
|
|
19
|
+
it needs and use a separate migration/admin identity.
|
|
20
|
+
|
|
21
|
+
For PostgreSQL, use `DATABASE_URL` from the provider’s secret store, TLS
|
|
22
|
+
settings required by that provider, least-privilege roles, and rotated
|
|
23
|
+
credentials. Review role grants after every schema or service change.
|
|
24
|
+
|
|
25
|
+
## Set limits before load
|
|
26
|
+
|
|
27
|
+
Bound every layer that can wait:
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
# config/database.yml
|
|
31
|
+
production:
|
|
32
|
+
adapter: rubydb
|
|
33
|
+
embedded: false
|
|
34
|
+
url: <%= ENV.fetch("RUBYDB_URL") %>
|
|
35
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
|
|
36
|
+
checkout_timeout: <%= ENV.fetch("DB_CHECKOUT_TIMEOUT", "5") %>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Also set application request/job timeouts, connection idle limits, server
|
|
40
|
+
connection limits, maximum request body sizes, and process memory/CPU limits.
|
|
41
|
+
Choose values from measurements. A timeout should fail a request cleanly and
|
|
42
|
+
produce a useful event; it should not cause an unsafe blind retry.
|
|
43
|
+
|
|
44
|
+
## Monitor signals that lead to incidents
|
|
45
|
+
|
|
46
|
+
Collect metrics and structured logs for:
|
|
47
|
+
|
|
48
|
+
* request and query latency by operation, including p95 and p99;
|
|
49
|
+
* error, timeout, cancellation, retry, and deadlock counts;
|
|
50
|
+
* active connections, pool wait time, and transaction age;
|
|
51
|
+
* WAL/checkpoint growth, database size, free disk, and compaction/vacuum time;
|
|
52
|
+
* backup age, backup verification result, restore drill age, and RPO/RTO; and
|
|
53
|
+
* process restarts, readiness failures, replication lag, and failover events.
|
|
54
|
+
|
|
55
|
+
The RubyDB CLI is useful evidence in an operator check:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
rubydb --env production status --json
|
|
59
|
+
rubydb --env production doctor --quick --json
|
|
60
|
+
rubydb inspect --database data/app.rdb --stats --wal
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Turn those outputs into alerts with thresholds and an owner. A green process
|
|
64
|
+
status is not the same as a healthy application: include a real application
|
|
65
|
+
query and a write/read smoke test in staging and deployment verification.
|
|
66
|
+
|
|
67
|
+
## Load and concurrency validation
|
|
68
|
+
|
|
69
|
+
Start with a reproducible workload, then increase concurrency gradually:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
RUBYDB_WORKLOAD_THREADS=16 RUBYDB_WORKLOAD_OPERATIONS=10000 \
|
|
73
|
+
ruby benchmarks/concurrent_workload.rb
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Use the repository’s documented workload script and record its output. Measure
|
|
77
|
+
throughput, latency, failures, cancellations, timeouts, deadlocks, memory, WAL,
|
|
78
|
+
and disk use. Run long enough to expose leaks and queue growth. Test separate
|
|
79
|
+
processes and separate hosts for server mode; an in-process benchmark does not
|
|
80
|
+
prove network behavior.
|
|
81
|
+
|
|
82
|
+
## Checkpoint
|
|
83
|
+
|
|
84
|
+
The checkpoint passes when alerts have thresholds and owners, secrets can be
|
|
85
|
+
rotated without source changes, resource limits are documented, and a sustained
|
|
86
|
+
test produces a baseline with no unexplained errors or unbounded growth.
|
|
87
|
+
Continue to [lesson 10](10-release-readiness.md) for the release gate.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Lesson 10: release readiness
|
|
2
|
+
|
|
3
|
+
The final checkpoint is evidence, not optimism. A production release should
|
|
4
|
+
identify the exact RubyDB and adapter versions, supported Ruby/Rails/OS matrix,
|
|
5
|
+
tested SQL surface, backup artifact, restore result, load baseline, security
|
|
6
|
+
review, and rollback owner.
|
|
7
|
+
|
|
8
|
+
## Run repository checks
|
|
9
|
+
|
|
10
|
+
From the RubyDB repository:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
bundle install
|
|
14
|
+
bundle exec rspec
|
|
15
|
+
bundle exec rake
|
|
16
|
+
git diff --check
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Run the Rails adapter suite from its directory and repeat it for every Rails
|
|
20
|
+
and Ruby version you claim to support:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
cd adapters/activerecord
|
|
24
|
+
bundle install
|
|
25
|
+
bundle exec rspec
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Add your application’s complex query, migration, schema dump/load, concurrency,
|
|
29
|
+
and failure tests to CI. A passing library suite does not certify an arbitrary
|
|
30
|
+
application.
|
|
31
|
+
|
|
32
|
+
## Release a gem safely
|
|
33
|
+
|
|
34
|
+
Review the project’s release instructions and run the preflight with a version
|
|
35
|
+
that has not already been published:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
RUBYDB_RELEASE_VERSION=0.1.6 ruby scripts/release
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
On PowerShell, use:
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
$env:RUBYDB_RELEASE_VERSION = "0.1.6"
|
|
45
|
+
ruby scripts/release
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The release script builds the gem and writes a checksum. Check the artifact
|
|
49
|
+
locally before publishing:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
gem specification pkg/rubydb-0.1.6.gem
|
|
53
|
+
gem install pkg/rubydb-0.1.6.gem --local
|
|
54
|
+
ruby -rrubydb -e 'puts RubyDB::VERSION'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Publishing requires a RubyGems API key or trusted publishing setup configured
|
|
58
|
+
on the release machine. The local script publishes only when both the explicit
|
|
59
|
+
publish flag and secret are present; never commit the secret:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
RUBYDB_RELEASE_VERSION=0.1.6 \
|
|
63
|
+
RUBYDB_PUBLISH=1 \
|
|
64
|
+
GEM_HOST_API_KEY="YOUR_RUBYGEMS_API_KEY" \
|
|
65
|
+
ruby scripts/release
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
On Windows PowerShell:
|
|
69
|
+
|
|
70
|
+
```powershell
|
|
71
|
+
$env:RUBYDB_RELEASE_VERSION = "0.1.6"
|
|
72
|
+
$env:RUBYDB_PUBLISH = "1"
|
|
73
|
+
$env:GEM_HOST_API_KEY = "YOUR_RUBYGEMS_API_KEY"
|
|
74
|
+
ruby scripts/release
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Prefer the repository’s signed GitHub Actions release workflow for a public
|
|
78
|
+
release. Store signing keys and RubyGems secrets only in protected secret
|
|
79
|
+
storage; do not put them in the repository or a checked-in `.env` file.
|
|
80
|
+
|
|
81
|
+
Release the adapter separately when its version changes, update the changelog,
|
|
82
|
+
tag the source commit, and publish the checksums and supported-version notes.
|
|
83
|
+
|
|
84
|
+
## Publish the Python adapter to PyPI
|
|
85
|
+
|
|
86
|
+
The Python adapter is a separate distribution named `rubydb-python`; publishing
|
|
87
|
+
the Ruby gem does not publish this package. Build it from the adapter directory
|
|
88
|
+
and validate both distribution formats before upload:
|
|
89
|
+
|
|
90
|
+
```powershell
|
|
91
|
+
cd adapters/python
|
|
92
|
+
python -m pip install --upgrade build twine
|
|
93
|
+
python -m build
|
|
94
|
+
python -m twine check dist/*
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Prefer PyPI Trusted Publishing from CI. For a local upload, use a short-lived,
|
|
98
|
+
scope-limited PyPI token through the environment or Twine's prompt. Never
|
|
99
|
+
commit a token:
|
|
100
|
+
|
|
101
|
+
```powershell
|
|
102
|
+
$env:TWINE_USERNAME = "__token__"
|
|
103
|
+
$env:TWINE_PASSWORD = (Get-Clipboard).Trim()
|
|
104
|
+
python -m twine upload dist/*
|
|
105
|
+
Remove-Item Env:TWINE_PASSWORD
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
After upload, verify the package from a clean environment and run the live
|
|
109
|
+
adapter tests against a RubyDB server:
|
|
110
|
+
|
|
111
|
+
```powershell
|
|
112
|
+
python -m venv .venv-clean
|
|
113
|
+
.venv-clean\Scripts\Activate.ps1
|
|
114
|
+
python -m pip install rubydb-python
|
|
115
|
+
$env:RUBYDB_URL = "rubydbs://service_user:password@127.0.0.1:7432/rubydb"
|
|
116
|
+
python -m unittest discover -s adapters/python/tests -v
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The package provides DB-API 2.0 access to RubyDB server mode. It is not a
|
|
120
|
+
PostgreSQL driver and does not make PostgreSQL SQL portable to RubyDB. Pin the
|
|
121
|
+
adapter and server versions together, use TLS in production, and keep the
|
|
122
|
+
application's migration and rollback procedure under version control.
|
|
123
|
+
|
|
124
|
+
## Build and publish the Node adapter
|
|
125
|
+
|
|
126
|
+
The Node adapter is a separate public npm package named `rubydb-node`. The
|
|
127
|
+
literal `node/rubydb` is not a valid npm name because npm reserves `/` for
|
|
128
|
+
scoped packages such as `@scope/package`.
|
|
129
|
+
|
|
130
|
+
```powershell
|
|
131
|
+
cd adapters/rubydb
|
|
132
|
+
npm ci
|
|
133
|
+
npm test
|
|
134
|
+
npm run publish:check
|
|
135
|
+
npm publish --access public
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Use npm Trusted Publishing from CI or a protected npm token. Never commit an
|
|
139
|
+
`.npmrc` containing credentials. The package's live test runs against a real
|
|
140
|
+
RubyDB server when `RUBYDB_URL` is set:
|
|
141
|
+
|
|
142
|
+
```powershell
|
|
143
|
+
$env:RUBYDB_URL = "rubydb://rubydb@127.0.0.1:7432/rubydb"
|
|
144
|
+
npm test
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The package is a Node.js/TypeScript RubyDB client, not a PostgreSQL driver. Pin
|
|
148
|
+
the npm client and RubyDB server versions together and validate the target
|
|
149
|
+
application's SQL, retry, TLS, migration, backup, and failover behavior.
|
|
150
|
+
|
|
151
|
+
## Deployment gate
|
|
152
|
+
|
|
153
|
+
For a direct RubyDB production deployment, follow [lesson 5](05-rubydb-production-server.md)
|
|
154
|
+
from top to bottom before this gate. For a massive Rails application, follow
|
|
155
|
+
[lesson 6](06-postgresql-massive-apps.md) and keep RubyDB at a separate service
|
|
156
|
+
boundary.
|
|
157
|
+
|
|
158
|
+
Do not promote until all of these have an owner and a recorded result:
|
|
159
|
+
|
|
160
|
+
* application tests pass against the production database topology;
|
|
161
|
+
* migrations pass on empty and populated staging data;
|
|
162
|
+
* a verified backup restores on another path or host;
|
|
163
|
+
* load tests cover concurrency, timeouts, cancellation, and resource limits;
|
|
164
|
+
* multi-process client/server tests cover restart and network failure;
|
|
165
|
+
* failover and fencing behavior is validated if high availability is claimed;
|
|
166
|
+
* TLS, secrets, least privilege, certificate rotation, and audit logging are
|
|
167
|
+
reviewed;
|
|
168
|
+
* dashboards and alerts page an on-call person;
|
|
169
|
+
* rollback, upgrade, and data-reconciliation procedures are rehearsed; and
|
|
170
|
+
* the README and compatibility guide state what is supported and what is not.
|
|
171
|
+
|
|
172
|
+
## A sensible first production architecture
|
|
173
|
+
|
|
174
|
+
For a large Rails product, use managed PostgreSQL for the main application and
|
|
175
|
+
deploy RubyDB only for an independently owned microservice whose workload has
|
|
176
|
+
passed the lessons above. For a small internal or single-owner service, RubyDB
|
|
177
|
+
server mode can be reasonable when its SQL, concurrency, recovery, and
|
|
178
|
+
operational limits are accepted. Do not call either architecture universally
|
|
179
|
+
compatible without workload evidence.
|
|
180
|
+
|
|
181
|
+
## Final checkpoint
|
|
182
|
+
|
|
183
|
+
The journey is complete when a new operator can deploy the exact release,
|
|
184
|
+
verify a real query, observe health and capacity, restore data, and explain the
|
|
185
|
+
rollback path without relying on the author’s laptop. Keep the evidence with
|
|
186
|
+
the release and repeat the drills after major RubyDB, Rails, schema, or hosting
|
|
187
|
+
changes.
|
|
188
|
+
|
|
189
|
+
Continue using the repository’s [CLI guide](../docs/cli.md), [production
|
|
190
|
+
operations guide](../docs/operations/production-guide.md), [Rails compatibility
|
|
191
|
+
guide](../docs/rails/compatibility-guide.md), and [SQL compatibility
|
|
192
|
+
guide](../docs/sql/compatibility-guide.md) as the detailed references.
|