ros-apartment 4.0.0.alpha6 → 4.0.0.alpha7
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 +19 -0
- data/lib/apartment/cli/migrations.rb +16 -1
- data/lib/apartment/instrumentation.rb +2 -2
- data/lib/apartment/migrator.rb +44 -2
- data/lib/apartment/tasks/v4.rake +7 -0
- data/lib/apartment/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '093f09466c1e39d4344764096cec982394980fb08c8b72712c96bd8ad30fdcc7'
|
|
4
|
+
data.tar.gz: 402f7ef009203bb95296a90d7c6ff3dd34eeebca2d70c7da86cdb398ecc6fa60
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 99343f0efe17d444cc23b244581c56135a293371c97ee3fc5d2d2931e51c688c50d9d3d93d8ae55489ec36d3a6a43a910cacdc85d22c43a6c7e80a1bad189faa
|
|
7
|
+
data.tar.gz: 2ccd4c456cf1a5ce190d4b50dedfc131231cd1f02518c0444313987ef0a5fda8acc00c1376bfc5daac791768f1b13685d0bb47a08ae5ba755943a2cd6eb5d04b
|
data/README.md
CHANGED
|
@@ -40,6 +40,25 @@ This gem is a maintained fork of the original [Apartment gem](https://github.com
|
|
|
40
40
|
- Rails 7.2+
|
|
41
41
|
- PostgreSQL 14+, MySQL 8.4+, or SQLite3
|
|
42
42
|
|
|
43
|
+
### Ruby version manager
|
|
44
|
+
|
|
45
|
+
`.ruby-version` is the single source of truth for the local Ruby version (the
|
|
46
|
+
same file `ruby/setup-ruby` reads in CI). Both [mise](https://mise.jdx.dev) and
|
|
47
|
+
[rbenv](https://github.com/rbenv/rbenv) honor it — use whichever you prefer:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# mise (recommended)
|
|
51
|
+
brew install mise # then add `eval "$(mise activate zsh)"` to ~/.zshrc
|
|
52
|
+
mise trust && mise install # one-time trust per clone, then install the pinned Ruby
|
|
53
|
+
|
|
54
|
+
# rbenv (also works, unchanged)
|
|
55
|
+
rbenv install "$(cat .ruby-version)"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The committed `mise.toml` carries settings only (never a version): it tells mise
|
|
59
|
+
to honor `.ruby-version` without any per-developer global config. `mise trust`
|
|
60
|
+
is required once per clone; `bin/dev/setup-worktree` handles it for new worktrees.
|
|
61
|
+
|
|
43
62
|
### Setup
|
|
44
63
|
|
|
45
64
|
```ruby
|
|
@@ -5,6 +5,13 @@ require 'thor'
|
|
|
5
5
|
module Apartment
|
|
6
6
|
class CLI < Thor
|
|
7
7
|
class Migrations < Thor
|
|
8
|
+
# The raised Thor::Error message is the low-context surface a monitor or
|
|
9
|
+
# CI-log tail captures even when the stdout summary is dropped/unindexed.
|
|
10
|
+
# Name a few failed tenants there (capped, so a 600-tenant run does not
|
|
11
|
+
# produce a wall of text); the full per-tenant detail is in the summary
|
|
12
|
+
# and in the migrate_tenant_failed.apartment events.
|
|
13
|
+
FAILED_TENANTS_PREVIEW = 5
|
|
14
|
+
|
|
8
15
|
def self.exit_on_failure? = true
|
|
9
16
|
|
|
10
17
|
desc 'migrate [TENANT]', 'Run migrations for tenants'
|
|
@@ -62,7 +69,15 @@ module Apartment
|
|
|
62
69
|
say(result.summary)
|
|
63
70
|
|
|
64
71
|
trigger_schema_dump if result.success?
|
|
65
|
-
raise(Thor::Error,
|
|
72
|
+
raise(Thor::Error, migration_failure_message(result.failed)) unless result.success?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def migration_failure_message(failures)
|
|
76
|
+
names = failures.map(&:tenant)
|
|
77
|
+
shown = names.first(FAILED_TENANTS_PREVIEW)
|
|
78
|
+
more = names.size - shown.size
|
|
79
|
+
suffix = more.positive? ? ", and #{more} more" : ''
|
|
80
|
+
"Migration failed for #{names.size} tenant(s): #{shown.join(', ')}#{suffix} (see summary above)"
|
|
66
81
|
end
|
|
67
82
|
|
|
68
83
|
# Rollback bypasses the Migrator's parallelism and Result tracking but
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
require 'active_support/notifications'
|
|
4
4
|
|
|
5
5
|
module Apartment
|
|
6
|
-
# Thin wrapper around ActiveSupport::Notifications.
|
|
7
|
-
#
|
|
6
|
+
# Thin wrapper around ActiveSupport::Notifications. All events are namespaced
|
|
7
|
+
# *.apartment; see docs/observability.md for the authoritative event catalog.
|
|
8
8
|
module Instrumentation
|
|
9
9
|
def self.instrument(event, payload = {}, &block)
|
|
10
10
|
event_name = "#{event}.apartment"
|
data/lib/apartment/migrator.rb
CHANGED
|
@@ -121,9 +121,15 @@ module Apartment
|
|
|
121
121
|
duration: monotonic_now - start, error: nil, versions_run: versions
|
|
122
122
|
)
|
|
123
123
|
rescue StandardError => e
|
|
124
|
+
duration = monotonic_now - start
|
|
125
|
+
# Symmetric with the :migrate_tenant success event above: the gem holds the
|
|
126
|
+
# full structured error here, so emit it for subscribers rather than
|
|
127
|
+
# stranding it in the returned Result. See migrate_tenant's rescue.
|
|
128
|
+
instrument_failure(tenant_name, e, duration)
|
|
129
|
+
|
|
124
130
|
Result.new(
|
|
125
131
|
tenant: tenant_name, status: :failed,
|
|
126
|
-
duration:
|
|
132
|
+
duration: duration, error: e, versions_run: []
|
|
127
133
|
)
|
|
128
134
|
end
|
|
129
135
|
|
|
@@ -166,9 +172,17 @@ module Apartment
|
|
|
166
172
|
end
|
|
167
173
|
end
|
|
168
174
|
rescue StandardError => e
|
|
175
|
+
duration = monotonic_now - start
|
|
176
|
+
# Failure counterpart to the :migrate_tenant success event. On SUCCESS the
|
|
177
|
+
# gem instruments; on FAILURE it previously only returned a failed Result,
|
|
178
|
+
# leaving adopters no hook to observe the (structured) error. This closes
|
|
179
|
+
# that asymmetry — generic payload (tenant + error + duration); routing to
|
|
180
|
+
# an error tracker is the subscriber's job.
|
|
181
|
+
instrument_failure(tenant, e, duration)
|
|
182
|
+
|
|
169
183
|
Result.new(
|
|
170
184
|
tenant: tenant, status: :failed,
|
|
171
|
-
duration:
|
|
185
|
+
duration: duration, error: e, versions_run: []
|
|
172
186
|
)
|
|
173
187
|
ensure
|
|
174
188
|
Apartment::Current.migrating = false
|
|
@@ -242,6 +256,34 @@ module Apartment
|
|
|
242
256
|
conn.instance_variable_set(ADVISORY_LOCKS_IVAR, original) if conn&.instance_variable_defined?(ADVISORY_LOCKS_IVAR)
|
|
243
257
|
end
|
|
244
258
|
|
|
259
|
+
# Emit the failure event without letting a raising subscriber break the
|
|
260
|
+
# Migrator's non-raising contract: the failed Result MUST still be returned.
|
|
261
|
+
# ActiveSupport::Notifications propagates subscriber exceptions through
|
|
262
|
+
# instrument (verified against AS 8.0), and this fires from inside a rescue
|
|
263
|
+
# block, so an un-isolated call would convert a captured per-tenant failure
|
|
264
|
+
# into an unhandled raise out of #run.
|
|
265
|
+
#
|
|
266
|
+
# Scope of the guarantee: a subscriber raising a StandardError is swallowed
|
|
267
|
+
# and warned. Process-control exceptions (SystemExit, SignalException /
|
|
268
|
+
# Interrupt) are deliberately NOT rescued — they must propagate so exit and
|
|
269
|
+
# Ctrl-C still work mid-migration; a subscriber raising a bare Exception
|
|
270
|
+
# subclass is itself a bug (Ruby errors should descend from StandardError).
|
|
271
|
+
# The warn is nested-rescued so a broken $stderr (IOError is a StandardError)
|
|
272
|
+
# cannot re-escape the handler. Success-path instrumentation is left
|
|
273
|
+
# un-isolated by design — only the failure path carries the hard no-raise
|
|
274
|
+
# guarantee, and swallowing there would mask real subscriber bugs.
|
|
275
|
+
def instrument_failure(tenant, error, duration)
|
|
276
|
+
Instrumentation.instrument(:migrate_tenant_failed, tenant: tenant, error: error, duration: duration)
|
|
277
|
+
rescue StandardError => e
|
|
278
|
+
# Nested rescue: a sibling `rescue` on this method would NOT catch an
|
|
279
|
+
# exception raised by warn (a broken $stderr), so the warn gets its own.
|
|
280
|
+
begin
|
|
281
|
+
warn "[Apartment::Migrator] migrate_tenant_failed subscriber raised #{e.class}: #{e.message}"
|
|
282
|
+
rescue StandardError
|
|
283
|
+
nil
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
245
287
|
def monotonic_now
|
|
246
288
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
247
289
|
end
|
data/lib/apartment/tasks/v4.rake
CHANGED
|
@@ -34,6 +34,13 @@ namespace :apartment do
|
|
|
34
34
|
else
|
|
35
35
|
Apartment::CLI::Migrations.new.invoke(:migrate)
|
|
36
36
|
end
|
|
37
|
+
# Parity with apartment:drop: `.new.invoke` bypasses Thor.start's
|
|
38
|
+
# exit_on_failure? handling, so an unrescued Thor::Error (raised on
|
|
39
|
+
# migration failure) would surface as a raw `rake aborted!` backtrace that
|
|
40
|
+
# buries the actionable message. abort re-emits it as a clean one-liner
|
|
41
|
+
# with a non-zero exit.
|
|
42
|
+
rescue Thor::Error => e
|
|
43
|
+
abort(e.message)
|
|
37
44
|
end
|
|
38
45
|
|
|
39
46
|
desc 'Seed all tenants'
|
data/lib/apartment/version.rb
CHANGED