Show egress/ingress levels in the registry; freeze checks at the verdict
Registry: the "last result" column now also shows, per level (egress,
ingress), how many of the recorded checks of the latest cycle succeeded, split
by check family (tcp-22 and tcp-443 are both "tcp"). One grouped query per
chunk of addresses; new fields last_cycle_id, egress, ingress in
GET /admin/registry; the dashboard renders them under the verdict.
Verdict integrity (migration 0010):
- the prober is handed an address once per site and attempt, not on every
poll, so results are no longer overwritten by later probe rounds;
- UpsertCheckIfOpen refuses writes once the address is aggregating or has its
verdict, or for an older attempt; senders get {"ok":true,"ignored":N} and a
result_dropped event is recorded;
- the checking window counts from checking_started_at, not from assigned_at;
- checks.recorded_at (server clock) and checks.after_verdict (flag for rows
written after the verdict in existing data);
- the verdict rule is a pure function (computeVerdict) and the aggregated
event carries the egress/ingress check counts.
Rebuilt bin/control-api and bin/admin-dashboard to match. Plans and summaries
are in docs/changes; README, API, USAGE, DASHBOARD and DIAGRAMS are updated.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
db73409e8f
commit
864208238f
34 files changed
+1570
-72
No files matched your search
@@ -15,20 +15,41 @@ import (
|
||||
// per-address cycle rather than the ip_queue row's attempt_number, so it
|
||||
// survives that row being deleted and the address later resubmitted.
|
||||
func (d *DB) UpsertCheck(ctx context.Context, c Check) error {
|
||||
_, err := d.ExecContext(ctx, `
|
||||
_, err := d.UpsertCheckIfOpen(ctx, c)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpsertCheckIfOpen is UpsertCheck for the live write path. It stores the
|
||||
// check only while the address can still take results: the queue row exists,
|
||||
// c.AttemptNumber is its current attempt, and it has not reached the
|
||||
// aggregating state or a terminal one. Once the verdict is being computed the
|
||||
// checks are frozen, so the verdict always matches the stored rows and a late
|
||||
// probe of an already released floating IP cannot change them. It returns
|
||||
// false (and writes nothing) when the result was dropped. The state test and
|
||||
// the write are one statement, so they cannot interleave with SetAggregating.
|
||||
func (d *DB) UpsertCheckIfOpen(ctx context.Context, c Check) (bool, error) {
|
||||
now := timeToDB(Now())
|
||||
res, err := d.ExecContext(ctx, `
|
||||
INSERT INTO checks (registry_id, cycle_id, ip_id, ip_address, attempt_number, validator_id,
|
||||
source, check_type, target, success, latency_ms, detail, checked_at, created_at)
|
||||
VALUES ((SELECT registry_id FROM ip_queue WHERE id=?), (SELECT cycle_id FROM ip_queue WHERE id=?),
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
source, check_type, target, success, latency_ms, detail, checked_at, created_at, recorded_at)
|
||||
SELECT q.registry_id, q.cycle_id, q.id, ?, q.attempt_number, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
FROM ip_queue q
|
||||
WHERE q.id=? AND q.attempt_number=? AND q.state NOT IN (?, ?, ?, ?)
|
||||
ON CONFLICT(registry_id, cycle_id, source, check_type, target) DO UPDATE SET
|
||||
validator_id=excluded.validator_id,
|
||||
success=excluded.success,
|
||||
latency_ms=excluded.latency_ms,
|
||||
detail=excluded.detail,
|
||||
checked_at=excluded.checked_at
|
||||
`, c.IPID, c.IPID, c.IPID, c.IPAddress, c.AttemptNumber, c.ValidatorID, c.Source, c.CheckType, c.Target,
|
||||
c.Success, c.LatencyMS, c.Detail, timeToDB(c.CheckedAt), timeToDB(Now()))
|
||||
return err
|
||||
checked_at=excluded.checked_at,
|
||||
recorded_at=excluded.recorded_at
|
||||
`, c.IPAddress, c.ValidatorID, c.Source, c.CheckType, c.Target,
|
||||
c.Success, c.LatencyMS, c.Detail, timeToDB(c.CheckedAt), now, now,
|
||||
c.IPID, c.AttemptNumber, IPAggregating, IPDone, IPFailed, IPOccupied)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
n, err := res.RowsAffected()
|
||||
return n > 0, err
|
||||
}
|
||||
|
||||
const checksSelect = `
|
||||
|
||||
Reference in new issue
Block a user