From 7fb11211d787cfae908a8af24325a9a5448a7f89 Mon Sep 17 00:00:00 2001 From: Sergei Poljanski Date: Sun, 18 Oct 2026 10:43:20 +0400 Subject: [PATCH] status: don't call a site down if it never answered 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 --- lib/bingo.rb | 7 +++++-- test/status_test.rb | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/bingo.rb b/lib/bingo.rb index 7be2c78..b359ebe 100644 --- a/lib/bingo.rb +++ b/lib/bingo.rb @@ -329,11 +329,14 @@ module Bingo end # state: up (≥80 % of probes answered), degraded, down (nothing answered - # in the last two buckets), unknown (worker stale, vantage lost, or the - # site wasn't probed in the newest bucket). + # in the last two buckets), unknown (worker stale, vantage lost, the + # 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) return 'unknown' unless latest && trusted cur = rows[latest] or return 'unknown' + return 'unknown' if rows.each_value.sum { |r| r[:ok] }.zero? prev = previous && rows[previous] return 'down' if cur[:ok].zero? && (prev.nil? || prev[:ok].zero?) cur[:ok] >= cur[:checks] * 0.8 ? 'up' : 'degraded' diff --git a/test/status_test.rb b/test/status_test.rb index 6022189..739777b 100644 --- a/test/status_test.rb +++ b/test/status_test.rb @@ -29,6 +29,8 @@ class StatusTest < Minitest::Test end 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| bucket(at, **controls) sample(@up, at, 5) @@ -48,6 +50,11 @@ class StatusTest < Minitest::Test assert_equal 'ok', r.state 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 bucket(B - 300); sample(@down, B - 300, 5) bucket(B); sample(@down, B, 0)