rubydb 0.1.0
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 +7 -0
- data/.gitattributes +20 -0
- data/.github/ISSUE_TEMPLATE/bug.yml +0 -0
- data/.github/ISSUE_TEMPLATE/feature.yml +0 -0
- data/.github/ISSUE_TEMPLATE/performance.yml +0 -0
- data/.github/ISSUE_TEMPLATE/security.yml +0 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- data/.github/dependabot.yml +0 -0
- data/.github/workflows/benchmark.yml +26 -0
- data/.github/workflows/compatibility.yml +63 -0
- data/.github/workflows/fuzz.yml +33 -0
- data/.github/workflows/lint.yml +21 -0
- data/.github/workflows/operations.yml +24 -0
- data/.github/workflows/production-validation.yml +111 -0
- data/.github/workflows/release.yml +77 -0
- data/.github/workflows/security.yml +37 -0
- data/.github/workflows/test.yml +26 -0
- data/.github/workflows/workload.yml +58 -0
- data/.gitignore +43 -0
- data/.rspec +4 -0
- data/.rubocop.yml +44 -0
- data/.ruby-version +1 -0
- data/.standard.yml +14 -0
- data/ARCHITECTURE.md +21 -0
- data/CHANGELOG.md +31 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/CONTRIBUTING.md +29 -0
- data/GOVERNANCE.md +16 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +71 -0
- data/LICENSE +60 -0
- data/README.md +162 -0
- data/ROADMAP.md +27 -0
- data/Rakefile +71 -0
- data/SECURITY.md +54 -0
- data/SUPPORT.md +14 -0
- data/adapters/activerecord/Gemfile +11 -0
- data/adapters/activerecord/README.md +9 -0
- data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +887 -0
- data/adapters/activerecord/rubydb-activerecord.gemspec +27 -0
- data/adapters/activerecord/spec/rubydb_adapter_integration_spec.rb +151 -0
- data/adapters/ruby/README.md +18 -0
- data/adapters/sequel/README.md +11 -0
- data/bin/rubydb +19 -0
- data/bin/rubydb-server +126 -0
- data/config/development.yml +0 -0
- data/config/monitoring/prometheus-alerts.yml +39 -0
- data/config/production.yml +36 -0
- data/config/test.yml +0 -0
- data/docs/README.md +71 -0
- data/docs/architecture/concurrency.md +14 -0
- data/docs/architecture/current-state.md +125 -0
- data/docs/architecture/execution-engine.md +25 -0
- data/docs/architecture/indexes.md +19 -0
- data/docs/architecture/mvcc.md +19 -0
- data/docs/architecture/overview.md +13 -0
- data/docs/architecture/pages.md +11 -0
- data/docs/architecture/production-roadmap.md +82 -0
- data/docs/architecture/query-planner.md +20 -0
- data/docs/architecture/recovery.md +18 -0
- data/docs/architecture/sql-engine.md +12 -0
- data/docs/architecture/storage-engine.md +14 -0
- data/docs/architecture/transactions.md +10 -0
- data/docs/architecture/wal.md +28 -0
- data/docs/cli-cheatsheet.md +98 -0
- data/docs/cli.md +275 -0
- data/docs/contributing/architecture.md +9 -0
- data/docs/contributing/benchmarking.md +14 -0
- data/docs/contributing/development.md +16 -0
- data/docs/contributing/release-process.md +49 -0
- data/docs/contributing/testing.md +16 -0
- data/docs/debugging.md +229 -0
- data/docs/developer/branching.md +10 -0
- data/docs/developer/database-diff.md +10 -0
- data/docs/developer/local-development.md +17 -0
- data/docs/developer/snapshots.md +9 -0
- data/docs/developer/temporal-data.md +10 -0
- data/docs/developer-guide.md +297 -0
- data/docs/getting-started/first-database.md +16 -0
- data/docs/getting-started/first-query.md +13 -0
- data/docs/getting-started/installation.md +19 -0
- data/docs/getting-started/local-to-production.md +300 -0
- data/docs/getting-started/quickstart.md +17 -0
- data/docs/getting-started/rails.md +16 -0
- data/docs/hardening_backlog.md +93 -0
- data/docs/lessons-learned.md +112 -0
- data/docs/operations/backups.md +33 -0
- data/docs/operations/disaster-recovery.md +31 -0
- data/docs/operations/failover.md +30 -0
- data/docs/operations/monitoring.md +25 -0
- data/docs/operations/production-guide.md +295 -0
- data/docs/operations/production-runbook.md +45 -0
- data/docs/operations/replication.md +33 -0
- data/docs/operations/restore.md +6 -0
- data/docs/operations/runbook.md +34 -0
- data/docs/operations/upgrades.md +14 -0
- data/docs/operations/workload-testing.md +17 -0
- data/docs/production-readiness.md +118 -0
- data/docs/production_validation.md +150 -0
- data/docs/rails/active-record.md +11 -0
- data/docs/rails/compatibility-guide.md +90 -0
- data/docs/rails/database-yml.md +92 -0
- data/docs/rails/installation.md +17 -0
- data/docs/rails/migrations.md +17 -0
- data/docs/rails/production.md +82 -0
- data/docs/rails/troubleshooting.md +18 -0
- data/docs/release.md +59 -0
- data/docs/server/architecture.md +10 -0
- data/docs/server/authentication.md +10 -0
- data/docs/server/configuration.md +16 -0
- data/docs/server/connection-pooling.md +10 -0
- data/docs/server/deployment.md +10 -0
- data/docs/server/protocol.md +12 -0
- data/docs/sql/compatibility-guide.md +82 -0
- data/docs/sql/compatibility.md +39 -0
- data/docs/sql/data-types.md +10 -0
- data/docs/sql/functions.md +9 -0
- data/docs/sql/joins.md +9 -0
- data/docs/sql/operators.md +9 -0
- data/docs/sql/sqlite-compatibility.md +21 -0
- data/docs/sql/syntax.md +10 -0
- data/docs/sql/transactions.md +10 -0
- data/docs/troubleshooting.md +244 -0
- data/exe/rubydb +23 -0
- data/lib/rubydb/backup/archive.rb +334 -0
- data/lib/rubydb/backup/backup.rb +401 -0
- data/lib/rubydb/backup/incremental.rb +353 -0
- data/lib/rubydb/backup/restore.rb +290 -0
- data/lib/rubydb/backup/snapshot.rb +267 -0
- data/lib/rubydb/backup/verification.rb +279 -0
- data/lib/rubydb/branching/branch.rb +181 -0
- data/lib/rubydb/branching/branch_manager.rb +311 -0
- data/lib/rubydb/branching/branch_metadata.rb +140 -0
- data/lib/rubydb/branching/checkout.rb +166 -0
- data/lib/rubydb/branching/copy_on_write.rb +272 -0
- data/lib/rubydb/branching/diff.rb +138 -0
- data/lib/rubydb/branching/merge.rb +285 -0
- data/lib/rubydb/build_info.rb +15 -0
- data/lib/rubydb/catalog/catalog.rb +391 -0
- data/lib/rubydb/catalog/column.rb +112 -0
- data/lib/rubydb/catalog/constraint.rb +180 -0
- data/lib/rubydb/catalog/database.rb +184 -0
- data/lib/rubydb/catalog/index.rb +97 -0
- data/lib/rubydb/catalog/schema.rb +103 -0
- data/lib/rubydb/catalog/sequence.rb +90 -0
- data/lib/rubydb/catalog/system_catalog.rb +698 -0
- data/lib/rubydb/catalog/table.rb +178 -0
- data/lib/rubydb/catalog/trigger.rb +102 -0
- data/lib/rubydb/catalog/view.rb +66 -0
- data/lib/rubydb/cli/application.rb +163 -0
- data/lib/rubydb/cli/commands/backup.rb +81 -0
- data/lib/rubydb/cli/commands/branch.rb +72 -0
- data/lib/rubydb/cli/commands/checkout.rb +54 -0
- data/lib/rubydb/cli/commands/create.rb +58 -0
- data/lib/rubydb/cli/commands/diff.rb +77 -0
- data/lib/rubydb/cli/commands/doctor.rb +74 -0
- data/lib/rubydb/cli/commands/drop.rb +57 -0
- data/lib/rubydb/cli/commands/init.rb +102 -0
- data/lib/rubydb/cli/commands/inspect.rb +95 -0
- data/lib/rubydb/cli/commands/merge.rb +63 -0
- data/lib/rubydb/cli/commands/migrate.rb +62 -0
- data/lib/rubydb/cli/commands/restart.rb +39 -0
- data/lib/rubydb/cli/commands/restore.rb +121 -0
- data/lib/rubydb/cli/commands/shell.rb +365 -0
- data/lib/rubydb/cli/commands/snapshot.rb +79 -0
- data/lib/rubydb/cli/commands/start.rb +82 -0
- data/lib/rubydb/cli/commands/status.rb +92 -0
- data/lib/rubydb/cli/commands/stop.rb +47 -0
- data/lib/rubydb/cli/commands/vacuum.rb +58 -0
- data/lib/rubydb/cli/formatter.rb +221 -0
- data/lib/rubydb/cli/output.rb +168 -0
- data/lib/rubydb/client/client.rb +304 -0
- data/lib/rubydb/client/connection.rb +415 -0
- data/lib/rubydb/client/connection_pool.rb +168 -0
- data/lib/rubydb/client/connection_url.rb +96 -0
- data/lib/rubydb/client/prepared_statement.rb +60 -0
- data/lib/rubydb/client/result.rb +123 -0
- data/lib/rubydb/client/statement.rb +52 -0
- data/lib/rubydb/client/transaction.rb +130 -0
- data/lib/rubydb/concurrency/concurrency.rb +19 -0
- data/lib/rubydb/concurrency/deadlock_detector.rb +150 -0
- data/lib/rubydb/concurrency/latch.rb +101 -0
- data/lib/rubydb/concurrency/lock_graph.rb +165 -0
- data/lib/rubydb/concurrency/mutex.rb +183 -0
- data/lib/rubydb/concurrency/rw_lock.rb +180 -0
- data/lib/rubydb/concurrency/scheduler.rb +250 -0
- data/lib/rubydb/concurrency/worker_pool.rb +143 -0
- data/lib/rubydb/configuration/config.rb +170 -0
- data/lib/rubydb/configuration/defaults.rb +179 -0
- data/lib/rubydb/configuration/environment.rb +152 -0
- data/lib/rubydb/configuration/parser.rb +185 -0
- data/lib/rubydb/configuration/validation.rb +221 -0
- data/lib/rubydb/constants.rb +74 -0
- data/lib/rubydb/constraints/check.rb +181 -0
- data/lib/rubydb/constraints/constraint.rb +101 -0
- data/lib/rubydb/constraints/foreign_key.rb +130 -0
- data/lib/rubydb/constraints/not_null.rb +64 -0
- data/lib/rubydb/constraints/primary_key.rb +99 -0
- data/lib/rubydb/constraints/unique.rb +108 -0
- data/lib/rubydb/constraints/validator.rb +350 -0
- data/lib/rubydb/errors/authentication_error.rb +10 -0
- data/lib/rubydb/errors/authorization_error.rb +23 -0
- data/lib/rubydb/errors/client_error.rb +10 -0
- data/lib/rubydb/errors/configuration_error.rb +10 -0
- data/lib/rubydb/errors/connection_error.rb +10 -0
- data/lib/rubydb/errors/constraint_error.rb +23 -0
- data/lib/rubydb/errors/corruption_error.rb +10 -0
- data/lib/rubydb/errors/database_error.rb +10 -0
- data/lib/rubydb/errors/error.rb +20 -0
- data/lib/rubydb/errors/execution_error.rb +10 -0
- data/lib/rubydb/errors/parser_error.rb +10 -0
- data/lib/rubydb/errors/recovery_error.rb +10 -0
- data/lib/rubydb/errors/replication_error.rb +10 -0
- data/lib/rubydb/errors/server_error.rb +6 -0
- data/lib/rubydb/errors/storage_error.rb +10 -0
- data/lib/rubydb/errors/transaction_error.rb +10 -0
- data/lib/rubydb/execution/aggregate_executor.rb +138 -0
- data/lib/rubydb/execution/delete_executor.rb +112 -0
- data/lib/rubydb/execution/distinct_executor.rb +135 -0
- data/lib/rubydb/execution/executor.rb +1192 -0
- data/lib/rubydb/execution/expression.rb +193 -0
- data/lib/rubydb/execution/index_scan.rb +142 -0
- data/lib/rubydb/execution/insert_executor.rb +217 -0
- data/lib/rubydb/execution/join_executor.rb +249 -0
- data/lib/rubydb/execution/limit_executor.rb +85 -0
- data/lib/rubydb/execution/optimizer.rb +215 -0
- data/lib/rubydb/execution/plan.rb +361 -0
- data/lib/rubydb/execution/planner.rb +546 -0
- data/lib/rubydb/execution/predicate.rb +235 -0
- data/lib/rubydb/execution/scan.rb +49 -0
- data/lib/rubydb/execution/sequential_scan.rb +63 -0
- data/lib/rubydb/execution/sort_executor.rb +185 -0
- data/lib/rubydb/execution/update_executor.rb +162 -0
- data/lib/rubydb/functions/aggregate.rb +70 -0
- data/lib/rubydb/functions/date_functions.rb +278 -0
- data/lib/rubydb/functions/function.rb +85 -0
- data/lib/rubydb/functions/json_functions.rb +215 -0
- data/lib/rubydb/functions/numeric_functions.rb +346 -0
- data/lib/rubydb/functions/scalar.rb +52 -0
- data/lib/rubydb/functions/string_functions.rb +383 -0
- data/lib/rubydb/functions/system_functions.rb +246 -0
- data/lib/rubydb/history/as_of.rb +238 -0
- data/lib/rubydb/history/change.rb +105 -0
- data/lib/rubydb/history/history.rb +131 -0
- data/lib/rubydb/history/history_manager.rb +229 -0
- data/lib/rubydb/history/temporal_query.rb +202 -0
- data/lib/rubydb/history/timeline.rb +144 -0
- data/lib/rubydb/indexes/btree.rb +186 -0
- data/lib/rubydb/indexes/btree_cursor.rb +258 -0
- data/lib/rubydb/indexes/btree_node.rb +385 -0
- data/lib/rubydb/indexes/hash_index.rb +150 -0
- data/lib/rubydb/indexes/index.rb +71 -0
- data/lib/rubydb/indexes/index_manager.rb +406 -0
- data/lib/rubydb/indexes/index_scan.rb +470 -0
- data/lib/rubydb/migrations/migration.rb +254 -0
- data/lib/rubydb/migrations/migration_lock.rb +146 -0
- data/lib/rubydb/migrations/migration_manager.rb +176 -0
- data/lib/rubydb/migrations/migration_version.rb +71 -0
- data/lib/rubydb/migrations/schema_diff.rb +211 -0
- data/lib/rubydb/migrations/schema_version.rb +64 -0
- data/lib/rubydb/monitoring/events.rb +160 -0
- data/lib/rubydb/monitoring/health.rb +222 -0
- data/lib/rubydb/monitoring/logger.rb +193 -0
- data/lib/rubydb/monitoring/metrics.rb +359 -0
- data/lib/rubydb/monitoring/performance.rb +176 -0
- data/lib/rubydb/monitoring/statistics.rb +170 -0
- data/lib/rubydb/mvcc/garbage_collector.rb +199 -0
- data/lib/rubydb/mvcc/mvcc.rb +16 -0
- data/lib/rubydb/mvcc/snapshot.rb +147 -0
- data/lib/rubydb/mvcc/vacuum.rb +180 -0
- data/lib/rubydb/mvcc/version.rb +106 -0
- data/lib/rubydb/mvcc/version_store.rb +398 -0
- data/lib/rubydb/mvcc/visibility.rb +109 -0
- data/lib/rubydb/protocol/capabilities.rb +125 -0
- data/lib/rubydb/protocol/decoder.rb +145 -0
- data/lib/rubydb/protocol/encoder.rb +136 -0
- data/lib/rubydb/protocol/handshake.rb +305 -0
- data/lib/rubydb/protocol/message.rb +121 -0
- data/lib/rubydb/protocol/protocol.rb +277 -0
- data/lib/rubydb/protocol/version.rb +54 -0
- data/lib/rubydb/rails/adapter.rb +239 -0
- data/lib/rubydb/rails/connection.rb +314 -0
- data/lib/rubydb/rails/database_statements.rb +122 -0
- data/lib/rubydb/rails/migration.rb +131 -0
- data/lib/rubydb/rails/quoting.rb +109 -0
- data/lib/rubydb/rails/result.rb +120 -0
- data/lib/rubydb/rails/schema_statements.rb +339 -0
- data/lib/rubydb/rails/transaction.rb +105 -0
- data/lib/rubydb/rails/type.rb +126 -0
- data/lib/rubydb/recovery/checkpoint.rb +257 -0
- data/lib/rubydb/recovery/consistency.rb +467 -0
- data/lib/rubydb/recovery/corruption_detector.rb +5 -0
- data/lib/rubydb/recovery/crash_recovery.rb +387 -0
- data/lib/rubydb/recovery/recovery_manager.rb +206 -0
- data/lib/rubydb/recovery/redo.rb +237 -0
- data/lib/rubydb/recovery/undo.rb +206 -0
- data/lib/rubydb/replication/failover.rb +5 -0
- data/lib/rubydb/replication/fencing.rb +63 -0
- data/lib/rubydb/replication/primary.rb +450 -0
- data/lib/rubydb/replication/replica.rb +384 -0
- data/lib/rubydb/replication/replication_log.rb +200 -0
- data/lib/rubydb/replication/replication_manager.rb +308 -0
- data/lib/rubydb/replication/replication_slot.rb +295 -0
- data/lib/rubydb/replication/replication_stream.rb +201 -0
- data/lib/rubydb/rubydb.rb +560 -0
- data/lib/rubydb/security/access_control.rb +254 -0
- data/lib/rubydb/security/audit_log.rb +213 -0
- data/lib/rubydb/security/authentication.rb +302 -0
- data/lib/rubydb/security/authorization.rb +282 -0
- data/lib/rubydb/security/credentials.rb +196 -0
- data/lib/rubydb/security/password.rb +215 -0
- data/lib/rubydb/security/permissions.rb +74 -0
- data/lib/rubydb/security/role.rb +101 -0
- data/lib/rubydb/security/user.rb +86 -0
- data/lib/rubydb/server/connection.rb +366 -0
- data/lib/rubydb/server/connection_pool.rb +193 -0
- data/lib/rubydb/server/lifecycle.rb +228 -0
- data/lib/rubydb/server/listener.rb +136 -0
- data/lib/rubydb/server/request_handler.rb +276 -0
- data/lib/rubydb/server/server.rb +364 -0
- data/lib/rubydb/server/session.rb +369 -0
- data/lib/rubydb/server/worker.rb +210 -0
- data/lib/rubydb/server/worker_pool.rb +168 -0
- data/lib/rubydb/sql/ast/alter_table.rb +169 -0
- data/lib/rubydb/sql/ast/begin_transaction.rb +47 -0
- data/lib/rubydb/sql/ast/commit.rb +37 -0
- data/lib/rubydb/sql/ast/constraint.rb +83 -0
- data/lib/rubydb/sql/ast/create_database.rb +41 -0
- data/lib/rubydb/sql/ast/create_index.rb +61 -0
- data/lib/rubydb/sql/ast/create_schema.rb +52 -0
- data/lib/rubydb/sql/ast/create_table.rb +187 -0
- data/lib/rubydb/sql/ast/delete.rb +54 -0
- data/lib/rubydb/sql/ast/drop_database.rb +41 -0
- data/lib/rubydb/sql/ast/drop_index.rb +41 -0
- data/lib/rubydb/sql/ast/drop_schema.rb +49 -0
- data/lib/rubydb/sql/ast/drop_table.rb +49 -0
- data/lib/rubydb/sql/ast/explain.rb +64 -0
- data/lib/rubydb/sql/ast/expression.rb +604 -0
- data/lib/rubydb/sql/ast/insert.rb +82 -0
- data/lib/rubydb/sql/ast/node.rb +42 -0
- data/lib/rubydb/sql/ast/rollback.rb +63 -0
- data/lib/rubydb/sql/ast/savepoint.rb +59 -0
- data/lib/rubydb/sql/ast/select.rb +88 -0
- data/lib/rubydb/sql/ast/set_operation.rb +20 -0
- data/lib/rubydb/sql/ast/trigger.rb +29 -0
- data/lib/rubydb/sql/ast/update.rb +88 -0
- data/lib/rubydb/sql/ast/vacuum.rb +19 -0
- data/lib/rubydb/sql/ast/view.rb +32 -0
- data/lib/rubydb/sql/ast/with.rb +32 -0
- data/lib/rubydb/sql/grammar.rb +86 -0
- data/lib/rubydb/sql/keywords.rb +156 -0
- data/lib/rubydb/sql/lexer.rb +214 -0
- data/lib/rubydb/sql/operators.rb +100 -0
- data/lib/rubydb/sql/parser.rb +1185 -0
- data/lib/rubydb/sql/planner/analyzer.rb +302 -0
- data/lib/rubydb/sql/planner/binder.rb +550 -0
- data/lib/rubydb/sql/planner/type_checker.rb +431 -0
- data/lib/rubydb/sql/token.rb +210 -0
- data/lib/rubydb/storage/buffer_frame.rb +44 -0
- data/lib/rubydb/storage/buffer_pool.rb +155 -0
- data/lib/rubydb/storage/database_lock.rb +74 -0
- data/lib/rubydb/storage/deserializer.rb +342 -0
- data/lib/rubydb/storage/engine.rb +2338 -0
- data/lib/rubydb/storage/file_manager.rb +187 -0
- data/lib/rubydb/storage/free_space_map.rb +81 -0
- data/lib/rubydb/storage/page.rb +94 -0
- data/lib/rubydb/storage/page_allocator.rb +855 -0
- data/lib/rubydb/storage/page_header.rb +67 -0
- data/lib/rubydb/storage/page_manager.rb +131 -0
- data/lib/rubydb/storage/record.rb +58 -0
- data/lib/rubydb/storage/row.rb +78 -0
- data/lib/rubydb/storage/serializer.rb +51 -0
- data/lib/rubydb/storage/storage_layout.rb +151 -0
- data/lib/rubydb/storage/storage_manager.rb +114 -0
- data/lib/rubydb/storage/tuple.rb +461 -0
- data/lib/rubydb/storage/visibility_map.rb +984 -0
- data/lib/rubydb/transactions/commit_manager.rb +220 -0
- data/lib/rubydb/transactions/isolation.rb +98 -0
- data/lib/rubydb/transactions/lock.rb +76 -0
- data/lib/rubydb/transactions/lock_manager.rb +362 -0
- data/lib/rubydb/transactions/savepoint.rb +143 -0
- data/lib/rubydb/transactions/transaction.rb +215 -0
- data/lib/rubydb/transactions/transaction_id.rb +84 -0
- data/lib/rubydb/transactions/transaction_log.rb +257 -0
- data/lib/rubydb/transactions/transaction_manager.rb +435 -0
- data/lib/rubydb/types/bigint.rb +36 -0
- data/lib/rubydb/types/blob.rb +37 -0
- data/lib/rubydb/types/boolean.rb +34 -0
- data/lib/rubydb/types/date.rb +39 -0
- data/lib/rubydb/types/decimal.rb +48 -0
- data/lib/rubydb/types/float.rb +34 -0
- data/lib/rubydb/types/integer.rb +36 -0
- data/lib/rubydb/types/json.rb +41 -0
- data/lib/rubydb/types/null.rb +34 -0
- data/lib/rubydb/types/smallint.rb +36 -0
- data/lib/rubydb/types/text.rb +37 -0
- data/lib/rubydb/types/time.rb +46 -0
- data/lib/rubydb/types/timestamp.rb +39 -0
- data/lib/rubydb/types/type.rb +119 -0
- data/lib/rubydb/types/uuid.rb +47 -0
- data/lib/rubydb/types/varchar.rb +37 -0
- data/lib/rubydb/version.rb +33 -0
- data/lib/rubydb/wal/archive.rb +193 -0
- data/lib/rubydb/wal/checkpoint.rb +183 -0
- data/lib/rubydb/wal/lsn.rb +94 -0
- data/lib/rubydb/wal/reader.rb +260 -0
- data/lib/rubydb/wal/record.rb +105 -0
- data/lib/rubydb/wal/segment.rb +193 -0
- data/lib/rubydb/wal/wal.rb +452 -0
- data/lib/rubydb/wal/writer.rb +236 -0
- data/lib/rubydb.rb +7 -0
- data/packaging/docker/Dockerfile +36 -0
- data/packaging/docker/docker-compose.failover.yml +43 -0
- data/packaging/docker/entrypoint.sh +15 -0
- data/packaging/homebrew/rubydb.rb +19 -0
- data/packaging/systemd/rubydb.service +22 -0
- data/rubydb.gemspec +57 -0
- data/scripts/benchmark +7 -0
- data/scripts/durability_drill +37 -0
- data/scripts/format +0 -0
- data/scripts/fuzz +63 -0
- data/scripts/lint +0 -0
- data/scripts/release +50 -0
- data/scripts/release_check +43 -0
- data/scripts/replication_failover_drill +250 -0
- data/scripts/replication_network_failover_drill +255 -0
- data/scripts/restore_drill +45 -0
- data/scripts/security +0 -0
- data/scripts/setup +0 -0
- data/scripts/test +0 -0
- data/scripts/test-chaos +0 -0
- data/scripts/test-crash +0 -0
- data/scripts/test-integration +0 -0
- data/scripts/test-unit +0 -0
- data/visibility_map.json +1 -0
- metadata +538 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyDB
|
|
4
|
+
module Client
|
|
5
|
+
# ConnectionPool - Manages client connections
|
|
6
|
+
class ConnectionPool
|
|
7
|
+
attr_reader :config, :stats
|
|
8
|
+
|
|
9
|
+
def initialize(config = {})
|
|
10
|
+
@config = config
|
|
11
|
+
@pool_size = config[:pool_size] || 5
|
|
12
|
+
@connections = []
|
|
13
|
+
@available = []
|
|
14
|
+
@stats = {
|
|
15
|
+
total_connections: 0,
|
|
16
|
+
active_connections: 0,
|
|
17
|
+
idle_connections: 0,
|
|
18
|
+
borrowed_count: 0,
|
|
19
|
+
returned_count: 0,
|
|
20
|
+
created_count: 0,
|
|
21
|
+
destroyed_count: 0,
|
|
22
|
+
wait_count: 0,
|
|
23
|
+
timeout_count: 0
|
|
24
|
+
}
|
|
25
|
+
@lock = Mutex.new
|
|
26
|
+
@condition = ConditionVariable.new
|
|
27
|
+
@initialized = false
|
|
28
|
+
@shutdown = false
|
|
29
|
+
|
|
30
|
+
# Pre-create only when auto-connect is requested. A disabled
|
|
31
|
+
# auto-connect pool remains lazy and creates connections on borrow.
|
|
32
|
+
initialize_pool if config.fetch(:auto_connect, true)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def get_connection(timeout = @config[:timeout] || 30)
|
|
36
|
+
@lock.synchronize do
|
|
37
|
+
@stats[:borrowed_count] += 1
|
|
38
|
+
|
|
39
|
+
start_time = Time.now
|
|
40
|
+
|
|
41
|
+
while true
|
|
42
|
+
# Check if there are available connections
|
|
43
|
+
unless @available.empty?
|
|
44
|
+
conn = @available.pop
|
|
45
|
+
if conn && conn.connected?
|
|
46
|
+
@stats[:active_connections] += 1
|
|
47
|
+
return conn
|
|
48
|
+
else
|
|
49
|
+
# Remove dead connection
|
|
50
|
+
remove_connection(conn) if conn
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Check if we can create a new connection
|
|
55
|
+
if @connections.size < @pool_size
|
|
56
|
+
conn = create_connection
|
|
57
|
+
@stats[:active_connections] += 1
|
|
58
|
+
return conn
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Wait for a connection to become available
|
|
62
|
+
if Time.now - start_time > timeout
|
|
63
|
+
@stats[:timeout_count] += 1
|
|
64
|
+
raise ClientError, "Connection pool timeout: no connections available"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
@stats[:wait_count] += 1
|
|
68
|
+
@condition.wait(@lock, 0.1)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def release_connection(connection)
|
|
74
|
+
@lock.synchronize do
|
|
75
|
+
return unless connection
|
|
76
|
+
|
|
77
|
+
@stats[:returned_count] += 1
|
|
78
|
+
@stats[:active_connections] -= 1
|
|
79
|
+
|
|
80
|
+
if connection.connected?
|
|
81
|
+
@available << connection
|
|
82
|
+
@condition.signal
|
|
83
|
+
else
|
|
84
|
+
remove_connection(connection)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def close_all
|
|
90
|
+
@lock.synchronize do
|
|
91
|
+
@shutdown = true
|
|
92
|
+
|
|
93
|
+
@connections.each do |conn|
|
|
94
|
+
conn.disconnect if conn.connected?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
@connections.clear
|
|
98
|
+
@available.clear
|
|
99
|
+
|
|
100
|
+
@stats[:active_connections] = 0
|
|
101
|
+
@stats[:idle_connections] = 0
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def active_count
|
|
106
|
+
@stats[:active_connections]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def idle_count
|
|
110
|
+
@available.size
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def total_count
|
|
114
|
+
@connections.size
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def stats
|
|
118
|
+
@lock.synchronize do
|
|
119
|
+
@stats.merge({
|
|
120
|
+
pool_size: @pool_size,
|
|
121
|
+
total: total_count,
|
|
122
|
+
active: active_count,
|
|
123
|
+
idle: idle_count,
|
|
124
|
+
available: @available.size,
|
|
125
|
+
shutdown: @shutdown
|
|
126
|
+
})
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def initialize_pool
|
|
133
|
+
@lock.synchronize do
|
|
134
|
+
return if @initialized
|
|
135
|
+
|
|
136
|
+
# Create initial connections
|
|
137
|
+
@pool_size.times do
|
|
138
|
+
conn = create_connection
|
|
139
|
+
@available << conn
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
@initialized = true
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def create_connection
|
|
147
|
+
conn = Connection.new(@config)
|
|
148
|
+
conn.connect
|
|
149
|
+
@connections << conn
|
|
150
|
+
@stats[:total_connections] += 1
|
|
151
|
+
@stats[:created_count] += 1
|
|
152
|
+
conn
|
|
153
|
+
rescue => e
|
|
154
|
+
raise ClientError, "Failed to create connection: #{e.message}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def remove_connection(connection)
|
|
158
|
+
return unless connection
|
|
159
|
+
|
|
160
|
+
connection.disconnect if connection.connected?
|
|
161
|
+
@connections.delete(connection)
|
|
162
|
+
@available.delete(connection)
|
|
163
|
+
@stats[:total_connections] -= 1
|
|
164
|
+
@stats[:destroyed_count] += 1
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module RubyDB
|
|
6
|
+
module Client
|
|
7
|
+
# Parses RubyDB's own connection URL format. This is deliberately not a
|
|
8
|
+
# PostgreSQL URL parser: RubyDB speaks its framed RubyDB protocol.
|
|
9
|
+
module ConnectionURL
|
|
10
|
+
SUPPORTED_SCHEMES = %w[rubydb rubydbs].freeze
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def parse(value)
|
|
15
|
+
raise ArgumentError, "RubyDB connection URL is required" if value.nil? || value.to_s.empty?
|
|
16
|
+
|
|
17
|
+
uri = URI.parse(value.to_s)
|
|
18
|
+
scheme = uri.scheme.to_s.downcase
|
|
19
|
+
raise ArgumentError, "Unsupported RubyDB connection URL scheme" unless SUPPORTED_SCHEMES.include?(scheme)
|
|
20
|
+
raise ArgumentError, "RubyDB connection URL requires a host" if uri.host.nil? || uri.host.empty?
|
|
21
|
+
|
|
22
|
+
database = uri.path.to_s.sub(%r{\A/}, "")
|
|
23
|
+
raise ArgumentError, "RubyDB connection URL requires a database name" if database.empty?
|
|
24
|
+
|
|
25
|
+
query = URI.decode_www_form(uri.query.to_s).to_h
|
|
26
|
+
ssl_mode = query["sslmode"]&.downcase
|
|
27
|
+
ssl_enabled = scheme == "rubydbs" || boolean(query["ssl"], false) || %w[require verify-ca verify-full].include?(ssl_mode)
|
|
28
|
+
verify_peer = if query.key?("verify_peer")
|
|
29
|
+
boolean(query["verify_peer"], true)
|
|
30
|
+
else
|
|
31
|
+
%w[verify-ca verify-full].include?(ssl_mode) || scheme == "rubydbs"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ssl = {enabled: true, verify_peer: verify_peer} if ssl_enabled
|
|
35
|
+
if ssl
|
|
36
|
+
ssl[:ca_file] = query["ca_file"] if query["ca_file"]
|
|
37
|
+
ssl[:cert_file] = query["cert_file"] if query["cert_file"]
|
|
38
|
+
ssl[:key_file] = query["key_file"] if query["key_file"]
|
|
39
|
+
ssl[:min_version] = query["min_version"] if query["min_version"]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
result = {
|
|
43
|
+
host: uri.host,
|
|
44
|
+
port: uri.port || 7432,
|
|
45
|
+
username: decode(uri.user),
|
|
46
|
+
password: decode(uri.password),
|
|
47
|
+
database: URI.decode_www_form_component(database),
|
|
48
|
+
ssl: ssl || false
|
|
49
|
+
}
|
|
50
|
+
result[:timeout] = positive_number(query["timeout"], "timeout") if query["timeout"]
|
|
51
|
+
result[:pool_size] = positive_integer(query["pool_size"], "pool_size") if query["pool_size"]
|
|
52
|
+
result[:compress] = boolean(query["compress"], false) if query.key?("compress")
|
|
53
|
+
result[:format] = query["format"].to_sym if query["format"]
|
|
54
|
+
result.compact
|
|
55
|
+
rescue URI::InvalidURIError
|
|
56
|
+
raise ArgumentError, "Invalid RubyDB connection URL"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def decode(value)
|
|
60
|
+
value && URI.decode_www_form_component(value)
|
|
61
|
+
end
|
|
62
|
+
private_class_method :decode
|
|
63
|
+
|
|
64
|
+
def boolean(value, default)
|
|
65
|
+
return default if value.nil?
|
|
66
|
+
|
|
67
|
+
case value.to_s.downcase
|
|
68
|
+
when "1", "true", "yes", "on" then true
|
|
69
|
+
when "0", "false", "no", "off" then false
|
|
70
|
+
else raise ArgumentError, "Invalid boolean connection URL option"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
private_class_method :boolean
|
|
74
|
+
|
|
75
|
+
def positive_integer(value, name)
|
|
76
|
+
number = Integer(value, 10)
|
|
77
|
+
raise ArgumentError, "#{name} must be positive" unless number.positive?
|
|
78
|
+
|
|
79
|
+
number
|
|
80
|
+
rescue ArgumentError
|
|
81
|
+
raise ArgumentError, "Invalid #{name} connection URL option"
|
|
82
|
+
end
|
|
83
|
+
private_class_method :positive_integer
|
|
84
|
+
|
|
85
|
+
def positive_number(value, name)
|
|
86
|
+
number = Float(value)
|
|
87
|
+
raise ArgumentError, "#{name} must be positive" unless number.positive?
|
|
88
|
+
|
|
89
|
+
number
|
|
90
|
+
rescue ArgumentError
|
|
91
|
+
raise ArgumentError, "Invalid #{name} connection URL option"
|
|
92
|
+
end
|
|
93
|
+
private_class_method :positive_number
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyDB
|
|
4
|
+
module Client
|
|
5
|
+
# PreparedStatement - Prepared SQL statement
|
|
6
|
+
class PreparedStatement
|
|
7
|
+
attr_reader :id, :sql, :client, :params
|
|
8
|
+
|
|
9
|
+
def initialize(id, sql, client)
|
|
10
|
+
@id = id
|
|
11
|
+
@sql = sql
|
|
12
|
+
@client = client
|
|
13
|
+
@params = []
|
|
14
|
+
@param_count = sql.scan(/\$/).size
|
|
15
|
+
@created_at = Time.now
|
|
16
|
+
@closed = false
|
|
17
|
+
@lock = Mutex.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def execute(params = [])
|
|
21
|
+
@lock.synchronize do
|
|
22
|
+
raise ClientError, "Statement is closed" if @closed
|
|
23
|
+
@params = params
|
|
24
|
+
@client.execute(@id, params)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def execute_batch(params_list)
|
|
29
|
+
@lock.synchronize do
|
|
30
|
+
raise ClientError, "Statement is closed" if @closed
|
|
31
|
+
results = []
|
|
32
|
+
params_list.each do |params|
|
|
33
|
+
results << @client.execute(@id, params)
|
|
34
|
+
end
|
|
35
|
+
results
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def close
|
|
40
|
+
@lock.synchronize do
|
|
41
|
+
return if @closed
|
|
42
|
+
@client.close_statement(@id)
|
|
43
|
+
@closed = true
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def closed?
|
|
48
|
+
@closed
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_s
|
|
52
|
+
"#<PreparedStatement id=#{@id} sql=\"#{@sql[0..50]}#{'...' if @sql.length > 50}\">"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def inspect
|
|
56
|
+
to_s
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyDB
|
|
4
|
+
module Client
|
|
5
|
+
# Result - Query result set
|
|
6
|
+
class Result
|
|
7
|
+
attr_reader :columns, :rows, :row_count, :affected_rows, :sql
|
|
8
|
+
attr_reader :command_tag, :statement_id, :transaction_id
|
|
9
|
+
attr_reader :error
|
|
10
|
+
|
|
11
|
+
def initialize(data = {})
|
|
12
|
+
@columns = data[:columns] || []
|
|
13
|
+
@rows = data[:rows] || []
|
|
14
|
+
@row_count = data[:row_count] || @rows.size
|
|
15
|
+
@affected_rows = data[:affected_rows] || 0
|
|
16
|
+
@sql = data[:sql]
|
|
17
|
+
@command_tag = data[:command_tag] || "SELECT"
|
|
18
|
+
@statement_id = data[:statement_id]
|
|
19
|
+
@transaction_id = data[:transaction_id]
|
|
20
|
+
@success = data[:success] != false
|
|
21
|
+
@error = data[:error]
|
|
22
|
+
@warnings = data[:warnings] || []
|
|
23
|
+
@notice = data[:notice]
|
|
24
|
+
@execution_time_ms = data[:execution_time_ms] || 0
|
|
25
|
+
@created_at = Time.now
|
|
26
|
+
@position = 0
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def success?
|
|
30
|
+
@success
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def error?
|
|
34
|
+
!@success && !@error.nil?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def empty?
|
|
38
|
+
@rows.empty?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def first
|
|
42
|
+
@rows.first
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def last
|
|
46
|
+
@rows.last
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def [](index)
|
|
50
|
+
@rows[index]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def each
|
|
54
|
+
@rows.each { |row| yield row }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def each_with_index
|
|
58
|
+
@rows.each_with_index { |row, idx| yield row, idx }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def each_row
|
|
62
|
+
@rows.each { |row| yield row }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def each_column
|
|
66
|
+
@columns.each { |col| yield col }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def column_names
|
|
70
|
+
@columns.map { |c| c[:name] || c }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def column_types
|
|
74
|
+
@columns.map { |c| c[:type] || "text" }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def to_a
|
|
78
|
+
@rows
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def to_hash
|
|
82
|
+
{
|
|
83
|
+
columns: @columns,
|
|
84
|
+
rows: @rows,
|
|
85
|
+
row_count: @row_count,
|
|
86
|
+
affected_rows: @affected_rows,
|
|
87
|
+
command_tag: @command_tag,
|
|
88
|
+
success: @success,
|
|
89
|
+
error: @error,
|
|
90
|
+
execution_time_ms: @execution_time_ms
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def to_json
|
|
95
|
+
JSON.generate(to_hash)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def inspect
|
|
99
|
+
rows_preview = @rows.first(3).map(&:inspect).join(", ")
|
|
100
|
+
more = @rows.size > 3 ? "..." : ""
|
|
101
|
+
"#<Result rows=#{@row_count} columns=#{@columns.size} data=[#{rows_preview}#{more}]>"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def to_s
|
|
105
|
+
if @rows.empty?
|
|
106
|
+
"Result: #{@command_tag} (0 rows)"
|
|
107
|
+
else
|
|
108
|
+
"Result: #{@command_tag} (#{@row_count} rows)"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Enumerable support
|
|
113
|
+
include Enumerable
|
|
114
|
+
|
|
115
|
+
def size
|
|
116
|
+
@row_count
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
alias length size
|
|
120
|
+
alias count size
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyDB
|
|
4
|
+
module Client
|
|
5
|
+
# Statement - SQL statement
|
|
6
|
+
class Statement
|
|
7
|
+
attr_reader :sql, :params, :client, :result
|
|
8
|
+
|
|
9
|
+
def initialize(sql, client)
|
|
10
|
+
@sql = sql
|
|
11
|
+
@client = client
|
|
12
|
+
@result = nil
|
|
13
|
+
@executed = false
|
|
14
|
+
@params = []
|
|
15
|
+
@param_count = sql.scan(/\$/).size
|
|
16
|
+
@created_at = Time.now
|
|
17
|
+
@lock = Mutex.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def execute(params = [])
|
|
21
|
+
@lock.synchronize do
|
|
22
|
+
@params = params
|
|
23
|
+
@result = @client.query(@sql, params)
|
|
24
|
+
@executed = true
|
|
25
|
+
@result
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def execute_batch(params_list)
|
|
30
|
+
@lock.synchronize do
|
|
31
|
+
results = []
|
|
32
|
+
params_list.each do |params|
|
|
33
|
+
results << execute(params)
|
|
34
|
+
end
|
|
35
|
+
results
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def executed?
|
|
40
|
+
@executed
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_s
|
|
44
|
+
@sql
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def inspect
|
|
48
|
+
"#<Statement sql=\"#{@sql[0..50]}#{'...' if @sql.length > 50}\">"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyDB
|
|
4
|
+
module Client
|
|
5
|
+
# Transaction - Database transaction
|
|
6
|
+
class Transaction
|
|
7
|
+
attr_reader :id, :client, :started_at, :options
|
|
8
|
+
|
|
9
|
+
def initialize(client, options = {})
|
|
10
|
+
@client = client
|
|
11
|
+
@options = options
|
|
12
|
+
@id = "txn_#{Time.now.to_i}_#{rand(1000)}"
|
|
13
|
+
@started_at = Time.now
|
|
14
|
+
@active = true
|
|
15
|
+
@committed = false
|
|
16
|
+
@rolled_back = false
|
|
17
|
+
@savepoints = []
|
|
18
|
+
@lock = Mutex.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def query(sql, params = [])
|
|
22
|
+
@lock.synchronize do
|
|
23
|
+
raise ClientError, "Transaction is not active" unless @active
|
|
24
|
+
@client.query(sql, params)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def prepare(sql)
|
|
29
|
+
@lock.synchronize do
|
|
30
|
+
raise ClientError, "Transaction is not active" unless @active
|
|
31
|
+
@client.prepare(sql)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def execute(statement_id, params = [])
|
|
36
|
+
@lock.synchronize do
|
|
37
|
+
raise ClientError, "Transaction is not active" unless @active
|
|
38
|
+
@client.execute(statement_id, params)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def savepoint(name)
|
|
43
|
+
@lock.synchronize do
|
|
44
|
+
raise ClientError, "Transaction is not active" unless @active
|
|
45
|
+
@savepoints << name
|
|
46
|
+
@client.query("SAVEPOINT #{name}")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def rollback_to_savepoint(name)
|
|
51
|
+
@lock.synchronize do
|
|
52
|
+
raise ClientError, "Transaction is not active" unless @active
|
|
53
|
+
unless @savepoints.include?(name)
|
|
54
|
+
raise ClientError, "Savepoint '#{name}' not found"
|
|
55
|
+
end
|
|
56
|
+
@client.query("ROLLBACK TO SAVEPOINT #{name}")
|
|
57
|
+
@savepoints.delete(name)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def release_savepoint(name)
|
|
62
|
+
@lock.synchronize do
|
|
63
|
+
raise ClientError, "Transaction is not active" unless @active
|
|
64
|
+
unless @savepoints.include?(name)
|
|
65
|
+
raise ClientError, "Savepoint '#{name}' not found"
|
|
66
|
+
end
|
|
67
|
+
@client.query("RELEASE SAVEPOINT #{name}")
|
|
68
|
+
@savepoints.delete(name)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def commit
|
|
73
|
+
@lock.synchronize do
|
|
74
|
+
raise ClientError, "Transaction is not active" unless @active
|
|
75
|
+
end
|
|
76
|
+
@client.commit
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def rollback
|
|
80
|
+
@lock.synchronize do
|
|
81
|
+
raise ClientError, "Transaction is not active" unless @active
|
|
82
|
+
end
|
|
83
|
+
@client.rollback
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def active?
|
|
87
|
+
@active
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def committed?
|
|
91
|
+
@committed
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def rolled_back?
|
|
95
|
+
@rolled_back
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Used by Client#commit/#rollback after the wire request has completed.
|
|
99
|
+
def mark_committed
|
|
100
|
+
@lock.synchronize do
|
|
101
|
+
@active = false
|
|
102
|
+
@committed = true
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def mark_rolled_back
|
|
107
|
+
@lock.synchronize do
|
|
108
|
+
@active = false
|
|
109
|
+
@rolled_back = true
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def to_hash
|
|
114
|
+
{
|
|
115
|
+
id: @id,
|
|
116
|
+
started_at: @started_at.iso8601,
|
|
117
|
+
active: @active,
|
|
118
|
+
committed: @committed,
|
|
119
|
+
rolled_back: @rolled_back,
|
|
120
|
+
savepoints: @savepoints,
|
|
121
|
+
options: @options
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def inspect
|
|
126
|
+
"#<Transaction id=#{@id} active=#{@active}>"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# lib/rubydb/concurrency.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "etc"
|
|
5
|
+
|
|
6
|
+
# Main entry point for Concurrency module
|
|
7
|
+
require_relative "concurrency/scheduler"
|
|
8
|
+
require_relative "concurrency/latch"
|
|
9
|
+
require_relative "concurrency/mutex"
|
|
10
|
+
require_relative "concurrency/rw_lock"
|
|
11
|
+
require_relative "concurrency/deadlock_detector"
|
|
12
|
+
require_relative "concurrency/lock_graph"
|
|
13
|
+
require_relative "concurrency/worker_pool"
|
|
14
|
+
|
|
15
|
+
module RubyDB
|
|
16
|
+
module Concurrency
|
|
17
|
+
# All classes are now loaded
|
|
18
|
+
end
|
|
19
|
+
end
|