trellis-pg 0.1.0.pre.alpha-aarch64-linux → 0.1.0.pre.gamma-aarch64-linux
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/README.md +56 -3
- data/lib/trellis/3.3/trellis_ruby.so +0 -0
- data/lib/trellis/3.4/trellis_ruby.so +0 -0
- data/lib/trellis/4.0/trellis_ruby.so +0 -0
- data/lib/trellis/error.rb +1 -1
- data/lib/trellis/instance.rb +27 -6
- data/lib/trellis/metrics.rb +38 -0
- data/lib/trellis/pg.rb +3 -1
- data/lib/trellis/railtie.rb +1 -1
- data/lib/trellis/schema_dump.rb +2 -2
- data/lib/trellis/values.rb +3 -3
- data/lib/trellis/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 63761a2adb2fa1ca3880becd7c22f5271fd4a4b342a780000848d55f7d9d42d3
|
|
4
|
+
data.tar.gz: ac2f451538877658f9617dce4b13d8c93ad370f4123255e72db2f7954a069241
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d78db6130783dbe45247819ac453fc95bc1fbcc36b6bf2bd790a8dfaea28a6861db69f852fa6b7f611762988444974ee00bffc6eca849c8fa7ca9cfbf7fc1748
|
|
7
|
+
data.tar.gz: 382d87f3aa62444be9143a324820f4a7bca471fe5d98e4970a43d3d8a1984e5ef7a6c9c0cab615b79c9c3c05fc9dd2de44767942a5649c1487167e78088dc533
|
data/README.md
CHANGED
|
@@ -103,6 +103,9 @@ Nothing in the binding uses it.
|
|
|
103
103
|
and `self_check_job` returns `nil` for the replaced job or one whose
|
|
104
104
|
transform was dropped. With no drain worker anywhere in the fleet a job
|
|
105
105
|
stays `:queued`.
|
|
106
|
+
- `Trellis::Metrics.render_prometheus`: the process-wide metrics registry as
|
|
107
|
+
Prometheus text, for a `/metrics` route the app already serves (the
|
|
108
|
+
binding opens no port; see [Metrics](#metrics)). It needs no handle.
|
|
106
109
|
|
|
107
110
|
```ruby
|
|
108
111
|
require "trellis/pg"
|
|
@@ -144,10 +147,59 @@ Trellis.shutdown
|
|
|
144
147
|
| `staging:` | `false` | run the staging worker (installs change capture, starts backfills) here |
|
|
145
148
|
| `drain_threads:` | `0` | threads applying staged changes to the targets |
|
|
146
149
|
| `worker_threads:` | `2` | the threads of each Rust runtime a handle owns (its calls', and its background client's), invisible to Ruby's own sizing |
|
|
150
|
+
| `max_call_pool_size:` | `20` | the most connections the handle's call pool opens; the background pool is sized by `staging:` and `drain_threads:` |
|
|
151
|
+
| `call_pool_wait_timeout_ms:` | `30_000` | how long a call waits for a free call-pool connection before a `ConnectivityError` |
|
|
152
|
+
|
|
153
|
+
The defaults run nothing in the background. Some process in a fleet must set
|
|
154
|
+
`staging: true` and some process must run drain threads, or no definition ever
|
|
155
|
+
reaches `:live`. Any number of processes may set `staging: true`: one takes the
|
|
156
|
+
producer lock and leads, the rest stand by and take over when it goes away, so a
|
|
157
|
+
rolling deploy needs no operator step. Under a preforking Puma with
|
|
158
|
+
`TRELLIS_WORKER=1`, every Puma worker connects with `staging: true`, so all but
|
|
159
|
+
one stand by; that is harmless, and each polls the lock every 5 seconds (see
|
|
160
|
+
[Rolling deploys](../../docs/embedding.md#rolling-deploys)).
|
|
161
|
+
|
|
162
|
+
### Resources
|
|
163
|
+
|
|
164
|
+
At the defaults, a handle holds up to 20 Postgres connections (its call pool,
|
|
165
|
+
opened as calls need them and closed after 60 seconds idle) and 3 threads. A
|
|
166
|
+
worker handle (`staging: true, drain_threads: N`) also owns a background pool
|
|
167
|
+
of up to `2N + 5` connections (`2N + 4` without staging, `1` with staging
|
|
168
|
+
alone), closed after 60 seconds idle, and two connections that stay open: the
|
|
169
|
+
staging session and one `LISTEN` shared by its drain threads. It holds 6
|
|
170
|
+
threads: drain threads are tasks on its runtime, not OS threads. Its memory is
|
|
171
|
+
bounded by row counts, not bytes: about `N` × 100,000 changed rows' images at
|
|
172
|
+
its peak. `max_call_pool_size:` lowers the 20 (the background pool is sized by
|
|
173
|
+
the handle's roles, not by an option), and `worker_threads:` sets the threads.
|
|
174
|
+
Every handle (each `Trellis::Instance` too) costs this again. The full
|
|
175
|
+
accounting is in
|
|
176
|
+
[`docs/embedding.md`](../../docs/embedding.md#what-a-handle-uses).
|
|
177
|
+
|
|
178
|
+
### Metrics
|
|
179
|
+
|
|
180
|
+
The engine's metrics live in one registry per process, holding the series of
|
|
181
|
+
every handle in it; `Trellis::Metrics.render_prometheus` renders them in
|
|
182
|
+
Prometheus's text format. Every series carries a `trellis_instance` label
|
|
183
|
+
(`<database>/<catalog schema>`), so instances in one process stay apart, and
|
|
184
|
+
`trellis_instance_up` is 1 while an instance's handle runs and 0 after it
|
|
185
|
+
shuts down. The metrics themselves are listed in
|
|
186
|
+
[`docs/observability.md`](../../docs/observability.md).
|
|
187
|
+
|
|
188
|
+
The binding opens no port, so serve the text from a route of the app's own:
|
|
147
189
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
190
|
+
```ruby
|
|
191
|
+
# config/routes.rb
|
|
192
|
+
get "/metrics", to: proc {
|
|
193
|
+
[200, { "content-type" => "text/plain; version=0.0.4" }, [Trellis::Metrics.render_prometheus]]
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Under a forking server each worker process has its own registry, holding
|
|
198
|
+
only its own handles' series, so Prometheus scrapes each worker (or the
|
|
199
|
+
worker handle, `staging: true`, runs in a process of its own that it
|
|
200
|
+
scrapes). A process forked while its parent had a handle running can't
|
|
201
|
+
render the registry, as it can't connect: it raises
|
|
202
|
+
`Trellis::ForkedHandleError` (see [Forking servers](#forking-servers)).
|
|
151
203
|
|
|
152
204
|
### Several instances
|
|
153
205
|
|
|
@@ -501,6 +553,7 @@ source rows; a test database has none, so that is immediate. Runtime state
|
|
|
501
553
|
## Layout
|
|
502
554
|
|
|
503
555
|
- `lib/`: the `Trellis` module and `Trellis::Instance`, its error classes and its `Data` values,
|
|
556
|
+
`Trellis::Metrics`,
|
|
504
557
|
the Rails integration (`trellis/railtie.rb`, `trellis/migration.rb`),
|
|
505
558
|
loaded only in an app that has Rails, and the test helpers
|
|
506
559
|
(`trellis/test_support.rb`), loaded only when required.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/trellis/error.rb
CHANGED
|
@@ -82,7 +82,7 @@ module Trellis
|
|
|
82
82
|
#
|
|
83
83
|
# Also Trellis.connect's error in a process forked while its parent had a
|
|
84
84
|
# handle running, which may have inherited a lock that handle's threads
|
|
85
|
-
# held
|
|
85
|
+
# held: shut down before forking (Puma's before_fork).
|
|
86
86
|
class ForkedHandleError < ValidationError
|
|
87
87
|
end
|
|
88
88
|
|
data/lib/trellis/instance.rb
CHANGED
|
@@ -98,7 +98,7 @@ module Trellis
|
|
|
98
98
|
# forked child's calls on an instance it inherited raise
|
|
99
99
|
# Trellis::ForkedHandleError, and its shutdown leaves the parent's handle
|
|
100
100
|
# alone and does nothing. So does Instance.connect in a child forked while
|
|
101
|
-
# any instance of its parent was running
|
|
101
|
+
# any instance of its parent was running.
|
|
102
102
|
class Instance
|
|
103
103
|
extend Checks
|
|
104
104
|
include Checks
|
|
@@ -111,7 +111,7 @@ module Trellis
|
|
|
111
111
|
# Connects a new instance and returns it. Raises ForkedHandleError in
|
|
112
112
|
# a process forked while its parent had an instance running
|
|
113
113
|
# (connected, connecting, or not yet fully shut down): it may have
|
|
114
|
-
# inherited a lock one of that instance's threads held
|
|
114
|
+
# inherited a lock one of that instance's threads held.
|
|
115
115
|
#
|
|
116
116
|
# - url: where the database is, as a libpq connection string
|
|
117
117
|
# ("host=... dbname=...") or URL. Nothing is read from the environment.
|
|
@@ -121,8 +121,10 @@ module Trellis
|
|
|
121
121
|
# - target_schema: the schema a bare target table name is created in.
|
|
122
122
|
# - staging: whether this process runs the instance's staging worker,
|
|
123
123
|
# which installs change capture on the source tables and starts each
|
|
124
|
-
# new transform's backfill.
|
|
125
|
-
#
|
|
124
|
+
# new transform's backfill. Any number of processes may set it: one
|
|
125
|
+
# takes the instance's producer lock and leads, the rest stand by and
|
|
126
|
+
# take over when it goes away, so a rolling deploy needs no
|
|
127
|
+
# operator step.
|
|
126
128
|
# - drain_threads: how many threads apply staged changes to the targets.
|
|
127
129
|
# Some process must run at least one per instance, or no definition
|
|
128
130
|
# reaches :live.
|
|
@@ -130,13 +132,20 @@ module Trellis
|
|
|
130
132
|
# instance owns (its calls', and its background client's), invisible
|
|
131
133
|
# to Ruby's own thread sizing, so kept small and explicit. Each
|
|
132
134
|
# instance has its own; nothing is shared between instances.
|
|
135
|
+
# - max_call_pool_size: the most connections this instance's call pool
|
|
136
|
+
# (its API calls) opens at once. Connections open as calls need them
|
|
137
|
+
# and close after 60 seconds idle. The background client's pool is
|
|
138
|
+
# sized by staging and drain_threads, not by this.
|
|
139
|
+
# - call_pool_wait_timeout_ms: how long a call waits for a free call-pool
|
|
140
|
+
# connection before failing with a ConnectivityError.
|
|
133
141
|
#
|
|
134
142
|
# The defaults (staging: false, drain_threads: 0) run nothing in the
|
|
135
143
|
# background, which is what a migration step or a console wants. The
|
|
136
144
|
# staging worker reads Trellis's tables as it starts, so a staging: true
|
|
137
145
|
# connect to an unmigrated schema fails: migrate first.
|
|
138
146
|
def connect(url:, schema: "trellis", target_schema: "public", staging: false,
|
|
139
|
-
drain_threads: 0, worker_threads: 2
|
|
147
|
+
drain_threads: 0, worker_threads: 2, max_call_pool_size: 20,
|
|
148
|
+
call_pool_wait_timeout_ms: 30_000)
|
|
140
149
|
string_option!(:url, url)
|
|
141
150
|
string_option!(:schema, schema)
|
|
142
151
|
string_option!(:target_schema, target_schema)
|
|
@@ -152,7 +161,19 @@ module Trellis
|
|
|
152
161
|
"worker_threads must be a positive Integer, got: #{worker_threads.inspect}"
|
|
153
162
|
end
|
|
154
163
|
|
|
155
|
-
|
|
164
|
+
unless max_call_pool_size.is_a?(Integer) && max_call_pool_size >= 1
|
|
165
|
+
raise ValidationError,
|
|
166
|
+
"max_call_pool_size must be a positive Integer, got: #{max_call_pool_size.inspect}"
|
|
167
|
+
end
|
|
168
|
+
unless call_pool_wait_timeout_ms.is_a?(Integer) &&
|
|
169
|
+
call_pool_wait_timeout_ms.between?(0, MAX_TIMEOUT_MS)
|
|
170
|
+
raise ValidationError,
|
|
171
|
+
"call_pool_wait_timeout_ms must be a non-negative Integer, got: " \
|
|
172
|
+
"#{call_pool_wait_timeout_ms.inspect}"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
handle = Native.connect(url, schema, target_schema, staging, drain_threads, worker_threads,
|
|
176
|
+
max_call_pool_size, call_pool_wait_timeout_ms)
|
|
156
177
|
instance = new(handle, schema)
|
|
157
178
|
register(instance)
|
|
158
179
|
instance
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Trellis
|
|
4
|
+
# The engine's metrics, in Prometheus text exposition format: the Rust
|
|
5
|
+
# crate's trellis::Metrics#render_prometheus
|
|
6
|
+
# (docs/decisions/0009-observability-decisions.md).
|
|
7
|
+
#
|
|
8
|
+
# The registry is process-wide: one body holds the series of every Trellis
|
|
9
|
+
# handle in this process, so it needs no handle and works before
|
|
10
|
+
# Trellis.connect and after Trellis.shutdown. Every series carries a
|
|
11
|
+
# trellis_instance="<database>/<catalog schema>" label, so instances in one
|
|
12
|
+
# process stay apart. trellis_instance_up is 1 while an instance has a
|
|
13
|
+
# running handle and 0 after its last one shuts down; the other gauges of a
|
|
14
|
+
# stopped instance disappear after 60 seconds. See docs/observability.md.
|
|
15
|
+
#
|
|
16
|
+
# The binding opens no port. Serve the text from a route the app already
|
|
17
|
+
# has, for example in Rails:
|
|
18
|
+
#
|
|
19
|
+
# # config/routes.rb
|
|
20
|
+
# get "/metrics", to: proc {
|
|
21
|
+
# [200, { "content-type" => "text/plain; version=0.0.4" }, [Trellis::Metrics.render_prometheus]]
|
|
22
|
+
# }
|
|
23
|
+
#
|
|
24
|
+
# In a forking server each worker process has its own registry, holding
|
|
25
|
+
# only its own handles' series, so scrape every worker (or run the worker
|
|
26
|
+
# handle, staging: true, in a process of its own and scrape that).
|
|
27
|
+
#
|
|
28
|
+
# A process forked while its parent had an engine running may have
|
|
29
|
+
# inherited the registry's lock held, so rendering there raises
|
|
30
|
+
# Trellis::ForkedHandleError, as Trellis.connect does.
|
|
31
|
+
module Metrics
|
|
32
|
+
# Renders the registry's current contents: # HELP and # TYPE lines, then
|
|
33
|
+
# each series' samples.
|
|
34
|
+
def self.render_prometheus
|
|
35
|
+
Native.render_prometheus
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/trellis/pg.rb
CHANGED
|
@@ -24,6 +24,7 @@ else
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
require_relative "instance"
|
|
27
|
+
require_relative "metrics"
|
|
27
28
|
|
|
28
29
|
# Embedded Trellis for Ruby apps: a native extension over the `trellis`
|
|
29
30
|
# crate's BlockingTrellis, so a Rails app can define and run streaming
|
|
@@ -48,6 +49,7 @@ require_relative "instance"
|
|
|
48
49
|
# - Operations: request_backfill, detect_capture_disruptions, has_live_drain_workers?,
|
|
49
50
|
# has_live_staging_worker?, watermark_token, await_converged, self_check,
|
|
50
51
|
# self_check_job.
|
|
52
|
+
# - Metrics: Trellis::Metrics.render_prometheus, which needs no handle.
|
|
51
53
|
#
|
|
52
54
|
# # A deploy's migration step: the defaults run nothing in the background.
|
|
53
55
|
# Trellis.connect(url: "host=localhost dbname=app")
|
|
@@ -71,7 +73,7 @@ require_relative "instance"
|
|
|
71
73
|
# calls on a handle it inherited raise Trellis::ForkedHandleError, except
|
|
72
74
|
# Trellis.shutdown, which leaves the parent's handle alone and does nothing.
|
|
73
75
|
# So does Trellis.connect in a child forked while its parent's handle was
|
|
74
|
-
# running
|
|
76
|
+
# running.
|
|
75
77
|
module Trellis
|
|
76
78
|
@default = nil
|
|
77
79
|
@lock = Mutex.new
|
data/lib/trellis/railtie.rb
CHANGED
|
@@ -49,7 +49,7 @@ module Trellis
|
|
|
49
49
|
# - config.trellis.connect_on_boot = false, for a server that forks with
|
|
50
50
|
# no hook to shut the handle down first (see below).
|
|
51
51
|
#
|
|
52
|
-
# A handle doesn't survive fork
|
|
52
|
+
# A handle doesn't survive fork. A preforking server that
|
|
53
53
|
# loads the app before forking (Puma's preload_app!, on by default with
|
|
54
54
|
# more than one worker) must shut the boot handle down before it forks,
|
|
55
55
|
# and connect one in each worker:
|
data/lib/trellis/schema_dump.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Trellis
|
|
|
10
10
|
# appends the `INSERT`s that describe how the schema came to be. A database
|
|
11
11
|
# built from the file (`db:schema:load`, `db:prepare`, the test database
|
|
12
12
|
# `maintain_test_schema!` loads) then has every transform defined, with no
|
|
13
|
-
# Trellis step to run: Trellis's worker finds the definitions unbuilt and builds them (
|
|
13
|
+
# Trellis step to run: Trellis's worker finds the definitions unbuilt and builds them (ADR-0010).
|
|
14
14
|
#
|
|
15
15
|
# Needs `config.active_record.schema_format = :sql`: a `schema.rb` can't
|
|
16
16
|
# carry another schema's tables or triggers. With `:ruby`, the dump raises
|
|
@@ -156,7 +156,7 @@ module Trellis
|
|
|
156
156
|
|
|
157
157
|
# The keywords of a libpq connection string or URI. A failure names
|
|
158
158
|
# what was being read and never the string or libpq's message, which
|
|
159
|
-
# can quote part of a password (as Trellis's own parse does
|
|
159
|
+
# can quote part of a password (as Trellis's own parse does).
|
|
160
160
|
def conninfo(url, what)
|
|
161
161
|
PG::Connection.conninfo_parse(url.to_s).to_h { |entry| [entry[:keyword], entry[:val]] }
|
|
162
162
|
rescue PG::Error
|
data/lib/trellis/values.rb
CHANGED
|
@@ -376,11 +376,11 @@ module Trellis
|
|
|
376
376
|
Divergence = Data.define(:kind, :key, :column, :persisted, :recomputed, :table, :detail)
|
|
377
377
|
|
|
378
378
|
# The configuration this process's handle connected with, as Trellis.config
|
|
379
|
-
# returns it: connect's schema and target_schema, and the
|
|
380
|
-
#
|
|
379
|
+
# returns it: connect's schema and target_schema, and the handle's call pool
|
|
380
|
+
# as it was built: max_call_pool_size and call_pool_wait_timeout_ms.
|
|
381
381
|
#
|
|
382
382
|
# Not the url: a connection string can carry a password, and a Config is
|
|
383
383
|
# the kind of value that ends up in a log line whole. The caller already
|
|
384
384
|
# has the url it connected with.
|
|
385
|
-
Config = Data.define(:schema, :target_schema, :
|
|
385
|
+
Config = Data.define(:schema, :target_schema, :max_call_pool_size, :call_pool_wait_timeout_ms)
|
|
386
386
|
end
|
data/lib/trellis/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trellis-pg
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.0.pre.
|
|
4
|
+
version: 0.1.0.pre.gamma
|
|
5
5
|
platform: aarch64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Salesforce
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |
|
|
14
14
|
A native extension over the trellis crate's BlockingTrellis, so a Rails
|
|
@@ -25,6 +25,7 @@ files:
|
|
|
25
25
|
- lib/trellis/4.0/trellis_ruby.so
|
|
26
26
|
- lib/trellis/error.rb
|
|
27
27
|
- lib/trellis/instance.rb
|
|
28
|
+
- lib/trellis/metrics.rb
|
|
28
29
|
- lib/trellis/migration.rb
|
|
29
30
|
- lib/trellis/pg.rb
|
|
30
31
|
- lib/trellis/railtie.rb
|