status: don't call a site down if it never answered
All checks were successful
Build and Deploy to Production / test (push) Successful in 17s
Build and Deploy to Production / build (push) Successful in 48s
Build and Deploy to Production / deploy (push) Successful in 33s

DataLine NORD showed "not answering" from its first round. Its only
target, JustHost's "Moscow, DataLine" test IP, is listed but doesn't
answer from abroad at all, like most of JustHost's Russian test IPs
(only St Petersburg does). The control hosts sit on other networks, so
they couldn't flag it.

Mark a site down only if its targets answered at some point in the
24-hour window; one that never answered reads as unknown.

Assisted-by: Claude:opus-5-5
This commit is contained in:
Sergei Poljanski 2026-10-18 10:43:20 +04:00
commit 7fb11211d7
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
2 changed files with 12 additions and 2 deletions

View file

@ -329,11 +329,14 @@ module Bingo
end end
# state: up (≥80 % of probes answered), degraded, down (nothing answered # state: up (≥80 % of probes answered), degraded, down (nothing answered
# in the last two buckets), unknown (worker stale, vantage lost, or the # in the last two buckets), unknown (worker stale, vantage lost, the
# site wasn't probed in the newest bucket). # site wasn't probed in the newest bucket, or its targets never answered
# in the window: a host that is filtered from abroad would otherwise
# read as a dark site).
def site_state(rows, latest, previous, trusted) def site_state(rows, latest, previous, trusted)
return 'unknown' unless latest && trusted return 'unknown' unless latest && trusted
cur = rows[latest] or return 'unknown' cur = rows[latest] or return 'unknown'
return 'unknown' if rows.each_value.sum { |r| r[:ok] }.zero?
prev = previous && rows[previous] prev = previous && rows[previous]
return 'down' if cur[:ok].zero? && (prev.nil? || prev[:ok].zero?) return 'down' if cur[:ok].zero? && (prev.nil? || prev[:ok].zero?)
cur[:ok] >= cur[:checks] * 0.8 ? 'up' : 'degraded' cur[:ok] >= cur[:checks] * 0.8 ? 'up' : 'degraded'

View file

@ -29,6 +29,8 @@ class StatusTest < Minitest::Test
end end
def two_buckets(base = B, **controls) def two_buckets(base = B, **controls)
bucket(base - 3600, **controls) # the dark site answered an hour earlier
sample(@down, base - 3600, 5)
[base - 300, base].each do |at| [base - 300, base].each do |at|
bucket(at, **controls) bucket(at, **controls)
sample(@up, at, 5) sample(@up, at, 5)
@ -48,6 +50,11 @@ class StatusTest < Minitest::Test
assert_equal 'ok', r.state assert_equal 'ok', r.state
end end
def test_target_that_never_answered_is_unknown_not_down
[B - 300, B].each { |at| bucket(at); sample(@down, at, 0) }
assert_equal 'unknown', states(Bingo::Status.load(NOW))['dc-down']
end
def test_one_dark_bucket_after_a_good_one_is_not_yet_down def test_one_dark_bucket_after_a_good_one_is_not_yet_down
bucket(B - 300); sample(@down, B - 300, 5) bucket(B - 300); sample(@down, B - 300, 5)
bucket(B); sample(@down, B, 0) bucket(B); sample(@down, B, 0)