rubydb 0.1.5 → 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/.gitignore +4 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +1 -1
- data/README.md +296 -227
- data/Rakefile +6 -1
- 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/README.md +8 -3
- data/adapters/activerecord/lib/active_record/connection_adapters/rubydb_adapter.rb +50 -42
- data/adapters/activerecord/rubydb-activerecord.gemspec +1 -1
- data/docs/README.md +3 -1
- data/docs/architecture/go-accelerator.md +179 -0
- data/docs/cli.md +24 -0
- data/docs/contributing/benchmarking.md +16 -0
- data/docs/developer/local-development.md +32 -0
- data/docs/release.md +2 -2
- data/lessons/02-local-development.md +2 -2
- data/lessons/04-rails-complex-apps.md +2 -2
- data/lessons/05-rubydb-production-server.md +2 -2
- data/lessons/07-hybrid-microservices.md +175 -90
- data/lessons/10-release-readiness.md +184 -117
- 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/cli/application.rb +6 -1
- data/lib/rubydb/cli/commands/accelerator.rb +72 -0
- data/lib/rubydb/cli/commands/doctor.rb +3 -0
- data/lib/rubydb/client/client.rb +7 -0
- data/lib/rubydb/client/connection.rb +15 -0
- data/lib/rubydb/client/result.rb +5 -1
- data/lib/rubydb/configuration/defaults.rb +12 -0
- data/lib/rubydb/configuration/validation.rb +8 -1
- data/lib/rubydb/execution/accelerator_dispatch.rb +30 -0
- data/lib/rubydb/execution/cost_model.rb +72 -0
- data/lib/rubydb/execution/executor.rb +373 -11
- data/lib/rubydb/execution/operator_selection.rb +57 -0
- data/lib/rubydb/execution/physical_plan.rb +47 -0
- data/lib/rubydb/execution/planner.rb +12 -46
- data/lib/rubydb/execution/sort_executor.rb +22 -8
- data/lib/rubydb/indexes/btree.rb +31 -2
- data/lib/rubydb/rubydb.rb +7 -1
- data/lib/rubydb/server/session.rb +45 -0
- data/lib/rubydb/storage/engine.rb +74 -12
- data/lib/rubydb/storage/snapshot_reader.rb +167 -0
- data/lib/rubydb/version.rb +1 -1
- data/lib/rubydb/wal/archive.rb +17 -0
- data/lib/rubydb/wal/wal.rb +1 -0
- data/rubydb.gemspec +12 -2
- data/scripts/build_accelerator +49 -0
- data/scripts/release +34 -4
- data/scripts/replication_failover_drill +2 -2
- metadata +49 -1
|
@@ -1,94 +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
|
|
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
|
|
49
118
|
```
|
|
50
119
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
pool: <%= ENV.fetch("RAILS_MAX_THREADS", "5") %>
|
|
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
|
+
}
|
|
65
133
|
```
|
|
66
134
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
##
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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.
|
|
@@ -1,125 +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.
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
On PowerShell, use:
|
|
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`.
|
|
42
129
|
|
|
43
130
|
```powershell
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
locally before publishing:
|
|
50
|
-
|
|
51
|
-
```sh
|
|
52
|
-
gem specification pkg/rubydb-0.1.5.gem
|
|
53
|
-
gem install pkg/rubydb-0.1.5.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.5 \
|
|
63
|
-
RUBYDB_PUBLISH=1 \
|
|
64
|
-
GEM_HOST_API_KEY="YOUR_RUBYGEMS_API_KEY" \
|
|
65
|
-
ruby scripts/release
|
|
131
|
+
cd adapters/rubydb
|
|
132
|
+
npm ci
|
|
133
|
+
npm test
|
|
134
|
+
npm run publish:check
|
|
135
|
+
npm publish --access public
|
|
66
136
|
```
|
|
67
137
|
|
|
68
|
-
|
|
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:
|
|
69
141
|
|
|
70
142
|
```powershell
|
|
71
|
-
$env:
|
|
72
|
-
|
|
73
|
-
$env:GEM_HOST_API_KEY = "YOUR_RUBYGEMS_API_KEY"
|
|
74
|
-
ruby scripts/release
|
|
143
|
+
$env:RUBYDB_URL = "rubydb://rubydb@127.0.0.1:7432/rubydb"
|
|
144
|
+
npm test
|
|
75
145
|
```
|
|
76
146
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
operations guide](../docs/operations/production-guide.md), [Rails compatibility
|
|
124
|
-
guide](../docs/rails/compatibility-guide.md), and [SQL compatibility
|
|
125
|
-
guide](../docs/sql/compatibility-guide.md) as the detailed references.
|
|
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.
|