Class: Wurk::Health::Fleet

Inherits:
Object
  • Object
show all
Defined in:
lib/wurk/health.rb

Overview

The swarm-wide half of the listener: the /metrics body and the fleet check behind /ready. One Prometheus collector serves both, so a scrape and a probe landing in the same second cost one Redis refresh between them.

A swarm pod is one probe target however many children it runs, and the listener lives in whichever child won the port. Judging readiness on that child alone said "ready" while its siblings crash-looped. Ready now also needs min_ready children (default: half the fleet, rounded up) with a heartbeat inside ready_window — a majority-ish bar rather than all, so one slot in respawn backoff does not pull a pod that is still doing most of its work out of a rollout. /live stays local on purpose: failing it restarts the whole pod, and replacing one wedged child is the swarm parent's job (Swarm::Liveness).

Instance Method Summary collapse

Constructor Details

#initialize(config, ready_window) ⇒ Fleet

Returns a new instance of Fleet.



366
367
368
369
370
# File 'lib/wurk/health.rb', line 366

def initialize(config, ready_window)
  @config = config
  @ready_window = ready_window
  @collector = nil
end

Instance Method Details

#metrics_response ⇒ Object

[body, status, content type]; nil when metrics: false.



373
374
375
376
377
378
379
# File 'lib/wurk/health.rb', line 373

def metrics_response
  return nil if option(:metrics) == false

  body = collector.render(expected_children: Health.fleet_size, fresh_window: @ready_window,
                          swarm: Health.swarm_counters&.read)
  [body, 200, Metrics::Prometheus::CONTENT_TYPE]
end

#ready_response ⇒ Object

The /ready answer once the local checks (Redis, own heartbeat) passed.



382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/wurk/health.rb', line 382

def ready_response
  expected = Health.fleet_size
  return [Health.json('ok', check: 'ready'), 200] unless expected

  needed = min_ready(expected)
  fresh = collector.fresh_local(collector.snapshot, @ready_window).size
  if fresh >= needed
    [Health.json('ok', check: 'ready', children: fresh, expected: expected), 200]
  else
    [Health.json('down', check: 'ready', reason: 'too few live children', children: fresh,
                         needed: needed, expected: expected), 503]
  end
end