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
data/rubydb.gemspec
CHANGED
|
@@ -1,57 +1,70 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name =
|
|
7
|
-
spec.version = RubyDB::VERSION
|
|
8
|
-
spec.authors = [
|
|
9
|
-
spec.email = [
|
|
10
|
-
|
|
11
|
-
spec.summary =
|
|
12
|
-
spec.description = <<~DESC
|
|
13
|
-
RubyDB is a developer-first relational database foundation written in Ruby.
|
|
14
|
-
It combines SQLite-like simplicity with a growing SQL, WAL, MVCC, server,
|
|
15
|
-
replication, and Rails integration surface. Production deployment is
|
|
16
|
-
limited to the capabilities and validation documented by the project.
|
|
17
|
-
DESC
|
|
18
|
-
spec.homepage =
|
|
19
|
-
spec.license =
|
|
20
|
-
# The implementation is currently compatible with the workspace Ruby runtime used for
|
|
21
|
-
# local development and CI. This is a practical compatibility target until the
|
|
22
|
-
# project adds a narrower support matrix and explicit Ruby-version policy.
|
|
23
|
-
spec.required_ruby_version =
|
|
24
|
-
|
|
25
|
-
spec.metadata[
|
|
26
|
-
spec.metadata[
|
|
27
|
-
spec.metadata[
|
|
28
|
-
|
|
29
|
-
# RubyGems signing is opt-in for release builders. The private key and
|
|
30
|
-
# certificate are supplied through protected CI paths and never committed.
|
|
31
|
-
signing_key = ENV[
|
|
32
|
-
certificate = ENV[
|
|
33
|
-
if signing_key || certificate
|
|
34
|
-
raise
|
|
35
|
-
raise "RubyGems signing key not found: #{signing_key}" unless File.file?(signing_key)
|
|
36
|
-
raise "RubyGems certificate not found: #{certificate}" unless File.file?(certificate)
|
|
37
|
-
|
|
38
|
-
spec.signing_key = signing_key
|
|
39
|
-
spec.cert_chain = [File.read(certificate)]
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Specify which files should be added to the gem when it is released.
|
|
43
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
44
|
-
spec.files = Dir.chdir(__dir__) do
|
|
45
|
-
`git ls-files -z`.split("\x0")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/rubydb/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "rubydb"
|
|
7
|
+
spec.version = RubyDB::VERSION
|
|
8
|
+
spec.authors = ["Aldane Hutchinson"]
|
|
9
|
+
spec.email = ["aldanehutchinson5@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "A developer-first relational database for Ruby"
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
RubyDB is a developer-first relational database foundation written in Ruby.
|
|
14
|
+
It combines SQLite-like simplicity with a growing SQL, WAL, MVCC, server,
|
|
15
|
+
replication, and Rails integration surface. Production deployment is
|
|
16
|
+
limited to the capabilities and validation documented by the project.
|
|
17
|
+
DESC
|
|
18
|
+
spec.homepage = "https://github.com/aldanedev-create/rubydb"
|
|
19
|
+
spec.license = "MIT"
|
|
20
|
+
# The implementation is currently compatible with the workspace Ruby runtime used for
|
|
21
|
+
# local development and CI. This is a practical compatibility target until the
|
|
22
|
+
# project adds a narrower support matrix and explicit Ruby-version policy.
|
|
23
|
+
spec.required_ruby_version = ">= 3.3.0"
|
|
24
|
+
|
|
25
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
26
|
+
spec.metadata["source_code_uri"] = "https://github.com/aldanedev-create/rubydb/tree/main"
|
|
27
|
+
spec.metadata["changelog_uri"] = "https://github.com/aldanedev-create/rubydb/blob/main/CHANGELOG.md"
|
|
28
|
+
|
|
29
|
+
# RubyGems signing is opt-in for release builders. The private key and
|
|
30
|
+
# certificate are supplied through protected CI paths and never committed.
|
|
31
|
+
signing_key = ENV["RUBYDB_GEM_SIGNING_KEY"]
|
|
32
|
+
certificate = ENV["RUBYDB_GEM_CERT"]
|
|
33
|
+
if signing_key || certificate
|
|
34
|
+
raise "RUBYDB_GEM_SIGNING_KEY and RUBYDB_GEM_CERT must be provided together" unless signing_key && certificate
|
|
35
|
+
raise "RubyGems signing key not found: #{signing_key}" unless File.file?(signing_key)
|
|
36
|
+
raise "RubyGems certificate not found: #{certificate}" unless File.file?(certificate)
|
|
37
|
+
|
|
38
|
+
spec.signing_key = signing_key
|
|
39
|
+
spec.cert_chain = [File.read(certificate)]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Specify which files should be added to the gem when it is released.
|
|
43
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
44
|
+
spec.files = Dir.chdir(__dir__) do
|
|
45
|
+
tracked_files = `git ls-files -z`.split("\x0")
|
|
46
|
+
accelerator_files = Dir[
|
|
47
|
+
"accelerator/bin/rubydb-accelerator-*",
|
|
48
|
+
"accelerator/bin/SHA256SUMS",
|
|
49
|
+
"accelerator/cmd/**/*.go",
|
|
50
|
+
"accelerator/internal/**/*.go",
|
|
51
|
+
"accelerator/go.mod"
|
|
52
|
+
]
|
|
53
|
+
runtime_files = Dir["lib/rubydb/accelerator.rb", "lib/rubydb/accelerator/**/*.rb", "lib/rubydb/cli/commands/accelerator.rb", "lib/rubydb/storage/snapshot_reader.rb"]
|
|
54
|
+
physical_execution_files = Dir["lib/rubydb/execution/physical_plan.rb", "lib/rubydb/execution/cost_model.rb", "lib/rubydb/execution/operator_selection.rb", "lib/rubydb/execution/accelerator_dispatch.rb"]
|
|
55
|
+
(tracked_files + accelerator_files + runtime_files + physical_execution_files).uniq.select { |f| File.file?(f) }.reject do |f|
|
|
56
|
+
f.match?(%r{^(test|spec|features|benchmarks|fuzz|chaos|examples|adapters/python)/}) || f.end_with?("_test.go")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
spec.bindir = "exe"
|
|
60
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
61
|
+
spec.require_paths = ["lib"]
|
|
62
|
+
|
|
63
|
+
spec.add_dependency "digest", "~> 3.1"
|
|
64
|
+
spec.add_dependency "json", "~> 2.7"
|
|
65
|
+
spec.add_dependency "date", "~> 3.3"
|
|
66
|
+
spec.add_dependency "time", "~> 0.4"
|
|
67
|
+
spec.add_dependency "bigdecimal", ">= 3.1"
|
|
68
|
+
spec.add_dependency "concurrent-ruby", ">= 1.2"
|
|
69
|
+
spec.add_dependency "base64", ">= 0.2"
|
|
70
|
+
end
|
data/scripts/benchmark
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "rbconfig"
|
|
5
|
-
|
|
6
|
-
root = File.expand_path("..", __dir__)
|
|
7
|
-
exec RbConfig.ruby, File.join(root, "benchmarks", "concurrent_workload.rb"), *ARGV
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
|
|
6
|
+
root = File.expand_path("..", __dir__)
|
|
7
|
+
exec RbConfig.ruby, File.join(root, "benchmarks", "concurrent_workload.rb"), *ARGV
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "digest"
|
|
6
|
+
require "rbconfig"
|
|
7
|
+
|
|
8
|
+
root = File.expand_path("..", __dir__)
|
|
9
|
+
accelerator = File.join(root, "accelerator")
|
|
10
|
+
output_dir = File.join(accelerator, "bin")
|
|
11
|
+
FileUtils.mkdir_p(output_dir)
|
|
12
|
+
|
|
13
|
+
go = ENV["GO"]
|
|
14
|
+
if go.nil? || go.empty?
|
|
15
|
+
candidates = [
|
|
16
|
+
File.join(ENV.fetch("ProgramFiles", "C:/Program Files"), "Go", "bin", "go.exe"),
|
|
17
|
+
File.join(ENV.fetch("ProgramW6432", "C:/Program Files"), "Go", "bin", "go.exe"),
|
|
18
|
+
"go"
|
|
19
|
+
]
|
|
20
|
+
go = candidates.find { |candidate| candidate == "go" || File.file?(candidate) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
abort "Go compiler not found. Set GO to the go executable or install Go." unless go
|
|
24
|
+
|
|
25
|
+
targets = ENV.fetch("RUBYDB_ACCELERATOR_TARGETS", "windows-amd64,windows-arm64,linux-amd64,linux-arm64,darwin-amd64,darwin-arm64").split(",").map(&:strip).reject(&:empty?)
|
|
26
|
+
targets = ["#{ENV.fetch("GOOS", RbConfig::CONFIG["host_os"].to_s.match?(/mswin|mingw|cygwin/i) ? "windows" : "linux")}-#{ENV.fetch("GOARCH", "amd64")}"] if ENV["RUBYDB_ACCELERATOR_TARGETS"] == "current"
|
|
27
|
+
|
|
28
|
+
targets.each do |target|
|
|
29
|
+
os, arch = target.split("-", 2)
|
|
30
|
+
abort "Invalid accelerator target #{target.inspect}; use os-arch" unless os && arch
|
|
31
|
+
|
|
32
|
+
extension = (os == "windows") ? ".exe" : ""
|
|
33
|
+
output = File.join(output_dir, "rubydb-accelerator-#{os}-#{arch}#{extension}")
|
|
34
|
+
env = {"GOOS" => os, "GOARCH" => arch, "CGO_ENABLED" => "0"}
|
|
35
|
+
command = [go, "build", "-trimpath", "-ldflags=-s -w", "-o", output, "./cmd/rubydb-accelerator"]
|
|
36
|
+
puts "Building #{os}/#{arch} -> #{output}"
|
|
37
|
+
Dir.chdir(accelerator) do
|
|
38
|
+
abort "Go build failed for #{target}" unless system(env, *command)
|
|
39
|
+
end
|
|
40
|
+
FileUtils.chmod(0o755, output) unless os == "windows"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
manifest = Dir[File.join(output_dir, "rubydb-accelerator-*")].sort.filter_map do |path|
|
|
44
|
+
next unless File.file?(path)
|
|
45
|
+
|
|
46
|
+
"#{Digest::SHA256.file(path).hexdigest} #{File.basename(path)}"
|
|
47
|
+
end
|
|
48
|
+
File.write(File.join(output_dir, "SHA256SUMS"), "#{manifest.join("\n")}\n")
|
|
49
|
+
puts "RubyDB accelerator binaries are ready in #{output_dir}"
|
data/scripts/durability_drill
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Runs the real crash, filesystem-fault, corruption, compaction, and restore
|
|
5
|
-
# paths as one release gate. Physical quota filling is opt-in because filling a
|
|
6
|
-
# developer's filesystem without an explicit safety flag would be dangerous.
|
|
7
|
-
|
|
8
|
-
require "json"
|
|
9
|
-
require "rbconfig"
|
|
10
|
-
|
|
11
|
-
root = File.expand_path("..", __dir__)
|
|
12
|
-
Dir.chdir(root)
|
|
13
|
-
specs = %w[
|
|
14
|
-
spec/filesystem_fault_injection_spec.rb
|
|
15
|
-
spec/transaction_crash_recovery_spec.rb
|
|
16
|
-
spec/crash_recovery_spec.rb
|
|
17
|
-
spec/crash_recovery_wal_failure_spec.rb
|
|
18
|
-
spec/corruption_repair_spec.rb
|
|
19
|
-
spec/compaction_persistence_spec.rb
|
|
20
|
-
spec/backup_restore_spec.rb
|
|
21
|
-
spec/incremental_backup_spec.rb
|
|
22
|
-
spec/delta_restore_spec.rb
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
command = [RbConfig.ruby, "-Ilib", "-S", "rspec", *specs]
|
|
26
|
-
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
27
|
-
success = system(*command)
|
|
28
|
-
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
29
|
-
|
|
30
|
-
result = {
|
|
31
|
-
success: success,
|
|
32
|
-
checks: %w[filesystem_faults crash_recovery corruption_detection compaction backup_restore],
|
|
33
|
-
physical_quota: ENV["RUBYDB_DURABILITY_QUOTA_DIR"] ? "operator_configured" : "not_run",
|
|
34
|
-
elapsed_seconds: elapsed.round(3)
|
|
35
|
-
}
|
|
36
|
-
puts JSON.generate(result)
|
|
37
|
-
exit(success ? 0 : 1)
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Runs the real crash, filesystem-fault, corruption, compaction, and restore
|
|
5
|
+
# paths as one release gate. Physical quota filling is opt-in because filling a
|
|
6
|
+
# developer's filesystem without an explicit safety flag would be dangerous.
|
|
7
|
+
|
|
8
|
+
require "json"
|
|
9
|
+
require "rbconfig"
|
|
10
|
+
|
|
11
|
+
root = File.expand_path("..", __dir__)
|
|
12
|
+
Dir.chdir(root)
|
|
13
|
+
specs = %w[
|
|
14
|
+
spec/filesystem_fault_injection_spec.rb
|
|
15
|
+
spec/transaction_crash_recovery_spec.rb
|
|
16
|
+
spec/crash_recovery_spec.rb
|
|
17
|
+
spec/crash_recovery_wal_failure_spec.rb
|
|
18
|
+
spec/corruption_repair_spec.rb
|
|
19
|
+
spec/compaction_persistence_spec.rb
|
|
20
|
+
spec/backup_restore_spec.rb
|
|
21
|
+
spec/incremental_backup_spec.rb
|
|
22
|
+
spec/delta_restore_spec.rb
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
command = [RbConfig.ruby, "-Ilib", "-S", "rspec", *specs]
|
|
26
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
27
|
+
success = system(*command)
|
|
28
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
29
|
+
|
|
30
|
+
result = {
|
|
31
|
+
success: success,
|
|
32
|
+
checks: %w[filesystem_faults crash_recovery corruption_detection compaction backup_restore],
|
|
33
|
+
physical_quota: ENV["RUBYDB_DURABILITY_QUOTA_DIR"] ? "operator_configured" : "not_run",
|
|
34
|
+
elapsed_seconds: elapsed.round(3)
|
|
35
|
+
}
|
|
36
|
+
puts JSON.generate(result)
|
|
37
|
+
exit(success ? 0 : 1)
|
data/scripts/fuzz
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Run the repository's fuzzers as a bounded CI or local safety gate.
|
|
5
|
-
# Invalid SQL and intentionally injected WAL corruption are expected inputs;
|
|
6
|
-
# uncaught errors and reported crashes are not.
|
|
7
|
-
|
|
8
|
-
require "tmpdir"
|
|
9
|
-
|
|
10
|
-
root = File.expand_path("..", __dir__)
|
|
11
|
-
$LOAD_PATH.unshift(File.join(root, "lib"))
|
|
12
|
-
require "rubydb"
|
|
13
|
-
require_relative "../fuzz/sql_parser"
|
|
14
|
-
require_relative "../fuzz/wal"
|
|
15
|
-
require_relative "../fuzz/storage"
|
|
16
|
-
require_relative "../fuzz/transactions"
|
|
17
|
-
require_relative "../fuzz/query_engine"
|
|
18
|
-
|
|
19
|
-
iterations = Integer(ENV.fetch("RUBYDB_FUZZ_ITERATIONS", "100"), 10)
|
|
20
|
-
abort "RUBYDB_FUZZ_ITERATIONS must be positive" unless iterations.positive?
|
|
21
|
-
seed = Integer(ENV.fetch("RUBYDB_FUZZ_SEED", "20260909"), 10)
|
|
22
|
-
|
|
23
|
-
def run_fuzzer(name)
|
|
24
|
-
stats = yield
|
|
25
|
-
crashes = stats[:crashes].to_i
|
|
26
|
-
errors = stats[:errors].to_i
|
|
27
|
-
abort "#{name} found #{crashes} crash(es) and #{errors} error(s)" if crashes.positive? || errors.positive?
|
|
28
|
-
puts "#{name}: #{stats.inspect}"
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
Dir.mktmpdir("rubydb-fuzz-") do |directory|
|
|
32
|
-
run_fuzzer("SQL parser") do
|
|
33
|
-
RubyDB::Fuzz::SQLParserFuzzer.new(max_iterations: iterations, seed: seed).fuzz
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
run_fuzzer("WAL") do
|
|
37
|
-
RubyDB::Fuzz::WALFuzzer.new(
|
|
38
|
-
max_iterations: iterations,
|
|
39
|
-
seed: seed,
|
|
40
|
-
test_dir: File.join(directory, "wal")
|
|
41
|
-
).fuzz
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
run_fuzzer("storage") do
|
|
45
|
-
RubyDB::Fuzz::StorageFuzzer.new(
|
|
46
|
-
max_iterations: iterations,
|
|
47
|
-
seed: seed,
|
|
48
|
-
test_dir: File.join(directory, "storage")
|
|
49
|
-
).fuzz
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
engine = RubyDB::Storage::Engine.new(File.join(directory, "engine.rdb"))
|
|
53
|
-
begin
|
|
54
|
-
run_fuzzer("transactions") do
|
|
55
|
-
RubyDB::Fuzz::TransactionsFuzzer.new(engine, max_iterations: iterations, seed: seed).fuzz
|
|
56
|
-
end
|
|
57
|
-
run_fuzzer("query engine") do
|
|
58
|
-
RubyDB::Fuzz::QueryEngineFuzzer.new(engine, max_iterations: iterations, seed: seed).fuzz
|
|
59
|
-
end
|
|
60
|
-
ensure
|
|
61
|
-
engine.close
|
|
62
|
-
end
|
|
63
|
-
end
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Run the repository's fuzzers as a bounded CI or local safety gate.
|
|
5
|
+
# Invalid SQL and intentionally injected WAL corruption are expected inputs;
|
|
6
|
+
# uncaught errors and reported crashes are not.
|
|
7
|
+
|
|
8
|
+
require "tmpdir"
|
|
9
|
+
|
|
10
|
+
root = File.expand_path("..", __dir__)
|
|
11
|
+
$LOAD_PATH.unshift(File.join(root, "lib"))
|
|
12
|
+
require "rubydb"
|
|
13
|
+
require_relative "../fuzz/sql_parser"
|
|
14
|
+
require_relative "../fuzz/wal"
|
|
15
|
+
require_relative "../fuzz/storage"
|
|
16
|
+
require_relative "../fuzz/transactions"
|
|
17
|
+
require_relative "../fuzz/query_engine"
|
|
18
|
+
|
|
19
|
+
iterations = Integer(ENV.fetch("RUBYDB_FUZZ_ITERATIONS", "100"), 10)
|
|
20
|
+
abort "RUBYDB_FUZZ_ITERATIONS must be positive" unless iterations.positive?
|
|
21
|
+
seed = Integer(ENV.fetch("RUBYDB_FUZZ_SEED", "20260909"), 10)
|
|
22
|
+
|
|
23
|
+
def run_fuzzer(name)
|
|
24
|
+
stats = yield
|
|
25
|
+
crashes = stats[:crashes].to_i
|
|
26
|
+
errors = stats[:errors].to_i
|
|
27
|
+
abort "#{name} found #{crashes} crash(es) and #{errors} error(s)" if crashes.positive? || errors.positive?
|
|
28
|
+
puts "#{name}: #{stats.inspect}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Dir.mktmpdir("rubydb-fuzz-") do |directory|
|
|
32
|
+
run_fuzzer("SQL parser") do
|
|
33
|
+
RubyDB::Fuzz::SQLParserFuzzer.new(max_iterations: iterations, seed: seed).fuzz
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
run_fuzzer("WAL") do
|
|
37
|
+
RubyDB::Fuzz::WALFuzzer.new(
|
|
38
|
+
max_iterations: iterations,
|
|
39
|
+
seed: seed,
|
|
40
|
+
test_dir: File.join(directory, "wal")
|
|
41
|
+
).fuzz
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
run_fuzzer("storage") do
|
|
45
|
+
RubyDB::Fuzz::StorageFuzzer.new(
|
|
46
|
+
max_iterations: iterations,
|
|
47
|
+
seed: seed,
|
|
48
|
+
test_dir: File.join(directory, "storage")
|
|
49
|
+
).fuzz
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
engine = RubyDB::Storage::Engine.new(File.join(directory, "engine.rdb"))
|
|
53
|
+
begin
|
|
54
|
+
run_fuzzer("transactions") do
|
|
55
|
+
RubyDB::Fuzz::TransactionsFuzzer.new(engine, max_iterations: iterations, seed: seed).fuzz
|
|
56
|
+
end
|
|
57
|
+
run_fuzzer("query engine") do
|
|
58
|
+
RubyDB::Fuzz::QueryEngineFuzzer.new(engine, max_iterations: iterations, seed: seed).fuzz
|
|
59
|
+
end
|
|
60
|
+
ensure
|
|
61
|
+
engine.close
|
|
62
|
+
end
|
|
63
|
+
end
|
data/scripts/release
CHANGED
|
@@ -1,50 +1,85 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
4
|
require "digest"
|
|
5
|
+
require "fileutils"
|
|
5
6
|
require "rbconfig"
|
|
6
7
|
require "rubygems"
|
|
7
|
-
|
|
8
|
-
root = File.expand_path("..", __dir__)
|
|
9
|
-
Dir.chdir(root)
|
|
10
|
-
|
|
11
|
-
abort "release metadata preflight failed" unless system(RbConfig.ruby, File.join(root, "scripts", "release_check"))
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
abort "
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
abort "
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
8
|
+
|
|
9
|
+
root = File.expand_path("..", __dir__)
|
|
10
|
+
Dir.chdir(root)
|
|
11
|
+
|
|
12
|
+
abort "release metadata preflight failed" unless system(RbConfig.ruby, File.join(root, "scripts", "release_check"))
|
|
13
|
+
abort "Go accelerator build failed" unless system(RbConfig.ruby, File.join(root, "scripts", "build_accelerator"))
|
|
14
|
+
|
|
15
|
+
release_descriptors = [
|
|
16
|
+
"rubydb.gemspec",
|
|
17
|
+
"adapters/activerecord/rubydb-activerecord.gemspec",
|
|
18
|
+
"packaging/homebrew/rubydb.rb"
|
|
19
|
+
]
|
|
20
|
+
untracked_runtime_files = `git ls-files --others --exclude-standard -- lib exe accelerator rubydb.gemspec`.lines.map(&:chomp)
|
|
21
|
+
unless untracked_runtime_files.empty?
|
|
22
|
+
abort "Runtime files are untracked and would be omitted from the gem: #{untracked_runtime_files.join(", ")}. Commit them before publishing."
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
placeholder_pattern = /yourusername|your name|you@example\.com|your\.email@example\.com/i
|
|
26
|
+
placeholder_files = release_descriptors.select do |path|
|
|
27
|
+
File.exist?(path) && File.read(path).match?(placeholder_pattern)
|
|
28
|
+
end
|
|
29
|
+
unless placeholder_files.empty?
|
|
30
|
+
abort "Release metadata contains placeholders in: #{placeholder_files.join(", ")}. Set the real owner, contact, homepage, source, and changelog URLs before publishing."
|
|
31
|
+
end
|
|
32
|
+
abort "rake build:checksum failed" unless system("rake", "build:checksum")
|
|
33
|
+
|
|
34
|
+
if ENV["RUBYDB_GEM_SIGNING_KEY"] || ENV["RUBYDB_GEM_CERT"]
|
|
35
|
+
abort "Both RUBYDB_GEM_SIGNING_KEY and RUBYDB_GEM_CERT are required for gem signing" unless ENV["RUBYDB_GEM_SIGNING_KEY"] && ENV["RUBYDB_GEM_CERT"]
|
|
36
|
+
abort "RubyGems signing key does not exist" unless File.file?(ENV["RUBYDB_GEM_SIGNING_KEY"])
|
|
37
|
+
abort "RubyGems signing certificate does not exist" unless File.file?(ENV["RUBYDB_GEM_CERT"])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
gem_path = Dir["pkg/rubydb-*.gem"].max_by { |path| File.mtime(path) }
|
|
41
|
+
abort "No built gem found" unless gem_path
|
|
42
|
+
checksum_path = File.join("checksums", "#{File.basename(gem_path)}.sha512")
|
|
43
|
+
expected = File.read(checksum_path).split.first
|
|
44
|
+
actual = Digest::SHA512.file(gem_path).hexdigest
|
|
45
|
+
abort "Checksum verification failed" unless expected == actual
|
|
46
|
+
|
|
47
|
+
# Build the ActiveRecord adapter alongside the core release. Its version is
|
|
48
|
+
# intentionally independent so Rails applications can pin the adapter while
|
|
49
|
+
# remaining on the RubyDB 0.1.x compatibility line.
|
|
50
|
+
adapter_dir = File.join(root, "adapters", "activerecord")
|
|
51
|
+
adapter_spec = Dir.chdir(adapter_dir) { Gem::Specification.load("rubydb-activerecord.gemspec") }
|
|
52
|
+
abort "Unable to load ActiveRecord adapter gemspec" unless adapter_spec
|
|
53
|
+
adapter_pkg_dir = File.join(adapter_dir, "pkg")
|
|
54
|
+
FileUtils.mkdir_p(adapter_pkg_dir)
|
|
55
|
+
adapter_gem_path = File.join(adapter_pkg_dir, "rubydb-activerecord-#{adapter_spec.version}.gem")
|
|
56
|
+
abort "ActiveRecord adapter gem build failed" unless system(
|
|
57
|
+
"gem", "build", "-V", "rubydb-activerecord.gemspec", "--output", adapter_gem_path,
|
|
58
|
+
chdir: adapter_dir
|
|
59
|
+
)
|
|
60
|
+
adapter_checksum_dir = File.join(root, "checksums")
|
|
61
|
+
FileUtils.mkdir_p(adapter_checksum_dir)
|
|
62
|
+
adapter_checksum_path = File.join(adapter_checksum_dir, "#{File.basename(adapter_gem_path)}.sha512")
|
|
63
|
+
adapter_expected = Digest::SHA512.file(adapter_gem_path).hexdigest
|
|
64
|
+
File.write(adapter_checksum_path, "#{adapter_expected} #{File.basename(adapter_gem_path)}\n")
|
|
65
|
+
abort "ActiveRecord adapter checksum verification failed" unless
|
|
66
|
+
Digest::SHA512.file(adapter_gem_path).hexdigest == File.read(adapter_checksum_path).split.first
|
|
67
|
+
puts "Adapter #{adapter_spec.name} #{adapter_spec.version} built to #{adapter_gem_path}."
|
|
68
|
+
puts "Adapter checksum written to #{adapter_checksum_path}"
|
|
69
|
+
|
|
40
70
|
if ENV["RUBYDB_PUBLISH"] == "1"
|
|
71
|
+
puts "RubyGems 2FA/OTP settings: https://rubygems.org/profile/edit"
|
|
72
|
+
puts "If prompted for an OTP, enter the current code from your authenticator."
|
|
41
73
|
api_key = ENV.fetch("GEM_HOST_API_KEY", "")
|
|
42
74
|
credentials_path = Gem.configuration.credentials_path
|
|
43
75
|
credentials_available = File.file?(credentials_path) && !File.zero?(credentials_path)
|
|
44
76
|
abort "Set GEM_HOST_API_KEY or configure RubyGems credentials at #{credentials_path}" if api_key.empty? && !credentials_available
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
77
|
+
[gem_path, adapter_gem_path].each do |artifact|
|
|
78
|
+
abort "gem push failed for #{artifact}" unless system("gem", "push", artifact)
|
|
79
|
+
puts "Published #{artifact}"
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
puts "Release artifact verified: #{gem_path}"
|
|
83
|
+
puts "Release artifact verified: #{adapter_gem_path}"
|
|
84
|
+
puts "To publish both: RUBYDB_PUBLISH=1 GEM_HOST_API_KEY=<RubyGems API key> ruby scripts/release"
|
|
85
|
+
end
|
data/scripts/release_check
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Fail-closed release metadata validation. This stays separate from gem
|
|
5
|
-
# building so CI can prove that a tag has a reviewed changelog entry before
|
|
6
|
-
# any artifact is published.
|
|
7
|
-
|
|
8
|
-
require "pathname"
|
|
9
|
-
|
|
10
|
-
root = Pathname.new(__dir__).parent
|
|
11
|
-
$LOAD_PATH.unshift(root.join("lib").to_s)
|
|
12
|
-
require "rubydb/version"
|
|
13
|
-
|
|
14
|
-
version = RubyDB::VERSION
|
|
15
|
-
github_ref = ENV.fetch("GITHUB_REF_NAME", "").strip
|
|
16
|
-
requested_version = ENV.fetch("RUBYDB_RELEASE_VERSION", "").strip
|
|
17
|
-
|
|
18
|
-
tag_version = if github_ref.start_with?("v")
|
|
19
|
-
github_ref.delete_prefix("v")
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
if !requested_version.empty? && tag_version && requested_version != tag_version
|
|
23
|
-
abort "RUBYDB_RELEASE_VERSION #{requested_version} does not match GITHUB_REF_NAME #{github_ref}"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
release_version = requested_version.empty? ? tag_version : requested_version
|
|
27
|
-
if release_version.nil? || release_version.empty?
|
|
28
|
-
if ENV["RUBYDB_PUBLISH"] == "1"
|
|
29
|
-
abort "Publishing requires GITHUB_REF_NAME=v#{version} or RUBYDB_RELEASE_VERSION=#{version}"
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
puts "Release preflight skipped: no release tag or explicit release version"
|
|
33
|
-
exit 0
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
abort "Release version #{release_version} does not match RubyDB::VERSION #{version}" unless release_version == version
|
|
37
|
-
|
|
38
|
-
changelog_path = root.join("CHANGELOG.md")
|
|
39
|
-
changelog = changelog_path.read
|
|
40
|
-
header = /^##\s+#{Regexp.escape(release_version)}(?:\s|$)/
|
|
41
|
-
abort "CHANGELOG.md has no entry for version #{release_version}" unless changelog.match?(header)
|
|
42
|
-
|
|
43
|
-
puts "Release preflight passed for RubyDB #{release_version}"
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Fail-closed release metadata validation. This stays separate from gem
|
|
5
|
+
# building so CI can prove that a tag has a reviewed changelog entry before
|
|
6
|
+
# any artifact is published.
|
|
7
|
+
|
|
8
|
+
require "pathname"
|
|
9
|
+
|
|
10
|
+
root = Pathname.new(__dir__).parent
|
|
11
|
+
$LOAD_PATH.unshift(root.join("lib").to_s)
|
|
12
|
+
require "rubydb/version"
|
|
13
|
+
|
|
14
|
+
version = RubyDB::VERSION
|
|
15
|
+
github_ref = ENV.fetch("GITHUB_REF_NAME", "").strip
|
|
16
|
+
requested_version = ENV.fetch("RUBYDB_RELEASE_VERSION", "").strip
|
|
17
|
+
|
|
18
|
+
tag_version = if github_ref.start_with?("v")
|
|
19
|
+
github_ref.delete_prefix("v")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if !requested_version.empty? && tag_version && requested_version != tag_version
|
|
23
|
+
abort "RUBYDB_RELEASE_VERSION #{requested_version} does not match GITHUB_REF_NAME #{github_ref}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
release_version = requested_version.empty? ? tag_version : requested_version
|
|
27
|
+
if release_version.nil? || release_version.empty?
|
|
28
|
+
if ENV["RUBYDB_PUBLISH"] == "1"
|
|
29
|
+
abort "Publishing requires GITHUB_REF_NAME=v#{version} or RUBYDB_RELEASE_VERSION=#{version}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
puts "Release preflight skipped: no release tag or explicit release version"
|
|
33
|
+
exit 0
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
abort "Release version #{release_version} does not match RubyDB::VERSION #{version}" unless release_version == version
|
|
37
|
+
|
|
38
|
+
changelog_path = root.join("CHANGELOG.md")
|
|
39
|
+
changelog = changelog_path.read
|
|
40
|
+
header = /^##\s+#{Regexp.escape(release_version)}(?:\s|$)/
|
|
41
|
+
abort "CHANGELOG.md has no entry for version #{release_version}" unless changelog.match?(header)
|
|
42
|
+
|
|
43
|
+
puts "Release preflight passed for RubyDB #{release_version}"
|