Class: Usps::Support::HealthController

Inherits:
ActionController::Base
  • Object
show all
Defined in:
app/controllers/usps/support/health_controller.rb

Overview

Base controller for external synthetic health probes (see the infrastructure repo's doc/application_health_probes.md). Unlike Rails' /up (boot-only) and nginx's static /instance-health-check, this exercises an app's critical dependencies and returns the sentinel "HEALTHCHECK_OK " only when every check passes, so a Route 53 HTTPS_STR_MATCH probe asserting that prefix flips the Statuspage component even with no organic traffic.

Host apps subclass this and declare which dependencies to probe with a CHECKS constant — a list of symbols, each naming a #check_ method:

class HealthController < Usps::Support::HealthController
CHECKS = %i[database redis hq_database imis].freeze
end

and route to it (get 'health' => 'health#show'). For a bespoke dependency, define a #check_ in the subclass (e.g. wrapping #check_http(url)) and add to CHECKS.

Checks run on a background thread, not the request thread: a per-process refresher updates a cached snapshot every health_refresh_interval seconds and #show serves that snapshot, so a slow or wedged dependency can't tie up the Passenger worker pool. Each check also runs under health_check_timeout, and a raise or timeout reads as a failed check (its error captured) so the action always responds. See doc/health_check_refresh_design.md.

Per-component results (ok, latency, error) are handed to Usps::Support::HealthDiagnostics once per refresh which — when enabled — publishes them to CloudWatch so a past failure can be attributed to a specific component after it has recovered.

Inherits ActionController::Base directly — NOT the host's ApplicationController — to bypass Devise auth, Pundit authorization, PaperTrail, and especially allow_browser, whose modern-browser gate would 406 Route 53's non-browser prober and fail the probe for the wrong reason.

Defined Under Namespace

Classes: CheckTimeout

Constant Summary collapse

SENTINEL_PREFIX =

rubocop:disable Rails/ApplicationController

'HEALTHCHECK_OK'
CHECKS =

Symbols naming the dependency checks to run; each invokes the matching #check_. Override per app by defining a CHECKS constant. Default probes the primary database only, which presumably every app needs.

%i[database].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.health_monitorObject

One monitor per concrete subclass, per process. Built lazily so the background thread starts after Passenger forks its workers (threads do not survive fork).



61
62
63
64
65
66
67
68
69
# File 'app/controllers/usps/support/health_controller.rb', line 61

def health_monitor
  klass = self
  @health_monitor ||= HealthMonitor.new(
    interval: health_refresh_interval,
    max_age: health_snapshot_max_age,
    background: health_background_refresh,
    runner: -> { klass.new.send(:run_and_record) }
  )
end

.reset_health_monitor!Object

Drops the memoized monitor (stopping its thread). For tests.



72
73
74
75
# File 'app/controllers/usps/support/health_controller.rb', line 72

def reset_health_monitor!
  @health_monitor&.stop
  @health_monitor = nil
end

Instance Method Details

#showObject



78
79
80
81
82
83
# File 'app/controllers/usps/support/health_controller.rb', line 78

def show
  data = self.class.health_monitor.current
  return render_unavailable unless data

  render_health(data)
end