Class: Pgbus::Doctor
- Inherits:
-
Object
- Object
- Pgbus::Doctor
- Defined in:
- lib/pgbus/doctor.rb
Overview
Preflight diagnostics for a pgbus deployment — the single command that
answers "is this environment healthy enough to run?". Runs nine checks and
returns a machine-readable result plus a human report, so pgbus doctor
and rake pgbus:doctor can gate a deploy or CI run (exit 0 on success,
1 on any failure).
Doctor never touches PGMQ or PostgreSQL directly: every probe goes through Pgbus::Client (DB, PGMQ schema, queues, NOTIFY) or Pgbus::Web::DataSource (process liveness, via Pgbus::MCP::HealthAnalyzer). It also never raises — a broken environment turns into :fail results, never a crash — so it is safe to run against a database that is down or a half-installed schema.
Defined Under Namespace
Classes: Check
Constant Summary collapse
- STATUS_ICON =
{ ok: "✓", warn: "!", fail: "✗" }.freeze
- CHECKS =
The ordered check suite, name → method. The name strings are the public, stable identity of each check (used in the report and to select subsets).
{ "Configuration" => :check_configuration, "Database" => :check_database, "PGMQ schema" => :check_pgmq_schema, "Queues" => :check_queues, "LISTEN/NOTIFY" => :check_notify, "Process liveness" => :check_processes, "GlobalID allowlist" => :check_allowed_global_id_models, "Broadcast queue" => :check_broadcast_queue, "Primary affinity" => :check_primary }.freeze
- BOOT_SKIP =
Process liveness reads the pgbus_processes table (via HealthAnalyzer), so it is only meaningful once workers have registered. The supervisor boot preflight (issue #347) runs BEFORE forking any worker, so it excludes this check — otherwise stale prior-generation worker rows plus a visible backlog would produce a false STALLED verdict on a redeploy.
["Process liveness"].freeze
- STRICT_FATAL =
The subset of checks whose :fail is genuinely deploy-fatal AND not a transient the supervisor is designed to ride out. Only these abort a
:strictboot. Configuration#validate! failing is a real config bug; an absent PGMQ schema means the migrations never ran. Deliberately NOT Queues/Database: the lenient queue bootstrap swallows a boot-time DB blip so children crash-and-backoff and recover, and verify_connection! already gated a hard-down DB before the preflight — making either strict-fatal would turn a tolerated transient into a fleet-wide lockstep boot abort. ["Configuration", "PGMQ schema"].freeze
Instance Method Summary collapse
-
#boot_checks ⇒ Object
The checks safe to run inside the booting supervisor before any worker is forked: everything except the worker-dependent process-liveness check.
-
#boot_ok? ⇒ Boolean
True unless a strict-fatal check (see STRICT_FATAL) failed.
-
#boot_report ⇒ Object
Human-readable report for the boot preflight — the boot_checks subset.
-
#config_summary ⇒ Object
Resolved-config summary for the report.
-
#initialize(config: Pgbus.configuration, client: Pgbus.client, data_source: nil) ⇒ Doctor
constructor
A new instance of Doctor.
-
#report(checks = run) ⇒ Object
Human-readable report: one line per check, then a resolved-config summary with passwords redacted.
-
#run ⇒ Object
Run all checks and return an array of result hashes: { name:, status: :ok|:warn|:fail, detail: }.
-
#success? ⇒ Boolean
True when no check failed.
Constructor Details
#initialize(config: Pgbus.configuration, client: Pgbus.client, data_source: nil) ⇒ Doctor
Returns a new instance of Doctor.
59 60 61 62 63 |
# File 'lib/pgbus/doctor.rb', line 59 def initialize(config: Pgbus.configuration, client: Pgbus.client, data_source: nil) @config = config @client = client @data_source = data_source end |
Instance Method Details
#boot_checks ⇒ Object
The checks safe to run inside the booting supervisor before any worker is forked: everything except the worker-dependent process-liveness check. Runs a genuine SUBSET — check_processes is never invoked — so there is no pre-fork HealthAnalyzer/DataSource round-trip.
83 84 85 |
# File 'lib/pgbus/doctor.rb', line 83 def boot_checks @boot_checks ||= run_checks(CHECKS.keys - BOOT_SKIP) end |
#boot_ok? ⇒ Boolean
True unless a strict-fatal check (see STRICT_FATAL) failed. Warnings and
transient-shaped failures (Queues, Database) never block a :strict boot.
89 90 91 |
# File 'lib/pgbus/doctor.rb', line 89 def boot_ok? boot_checks.none? { |c| c[:status] == :fail && STRICT_FATAL.include?(c[:name]) } end |
#boot_report ⇒ Object
Human-readable report for the boot preflight — the boot_checks subset.
94 95 96 |
# File 'lib/pgbus/doctor.rb', line 94 def boot_report report(boot_checks) end |
#config_summary ⇒ Object
Resolved-config summary for the report. Password-bearing fields (database_url, connection_params) are redacted.
117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/pgbus/doctor.rb', line 117 def config_summary { queue_prefix: @config.queue_prefix, default_queue: @config.default_queue, pgmq_schema_mode: @config.pgmq_schema_mode, resolved_pool_size: safe { @config.resolved_pool_size }, listen_notify: @config.listen_notify, roles: @config.roles || "all", capsules: capsule_summary, database_url: redacted_database_url, connection_params: redacted_connection_params }.compact end |
#report(checks = run) ⇒ Object
Human-readable report: one line per check, then a resolved-config summary with passwords redacted. Suitable for stdout in the CLI and rake task. Defaults to the full run; pass a filtered result array (e.g. boot_checks) to render a subset.
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/pgbus/doctor.rb', line 102 def report(checks = run) lines = ["Pgbus Doctor", "=" * 40] checks.each do |check| icon = STATUS_ICON.fetch(check[:status], "?") lines << format("%<icon>s %<name>-22s %<detail>s", icon: icon, name: check[:name], detail: check[:detail]) end lines << "" lines << "Configuration" lines << ("-" * 40) config_summary.each { |key, value| lines << format(" %<key>-20s %<value>s", key: key, value: value) } lines.join("\n") end |
#run ⇒ Object
Run all checks and return an array of result hashes:
{ name:, status: :ok|:warn|:fail, detail: }
67 68 69 |
# File 'lib/pgbus/doctor.rb', line 67 def run @run ||= run_checks(CHECKS.keys) end |
#success? ⇒ Boolean
True when no check failed. Warnings do not fail the run — they surface a concern (e.g. an outdated PGMQ schema) without blocking a deploy.
73 74 75 |
# File 'lib/pgbus/doctor.rb', line 73 def success? run.none? { |c| c[:status] == :fail } end |