fix: dedupe host_silence problems under concurrent scans (0.4.11)
Treat acknowledged as active, dedupe by hostname, add PostgreSQL advisory lock and a unique index. Migration closes existing duplicate open problems. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -211,6 +211,123 @@ def test_scan_reopens_after_manual_cooldown_expires(db_session, scan_settings, m
|
||||
assert later[0].problem.resolved_by is None
|
||||
|
||||
|
||||
def test_scan_does_not_duplicate_acknowledged_problem(db_session, scan_settings):
|
||||
hb = _ingest(
|
||||
db_session,
|
||||
type="agent.heartbeat",
|
||||
category="agent",
|
||||
severity="info",
|
||||
title="hb",
|
||||
summary="heartbeat",
|
||||
)
|
||||
hb.received_at = datetime.now(timezone.utc) - timedelta(hours=2)
|
||||
db_session.flush()
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
first = run_host_silence_scan(db_session, now=now)
|
||||
assert first[0].created is True
|
||||
first[0].problem.status = "acknowledged"
|
||||
db_session.flush()
|
||||
|
||||
second = run_host_silence_scan(db_session, now=now + timedelta(minutes=5))
|
||||
assert len(second) == 1
|
||||
assert second[0].created is False
|
||||
assert second[0].problem.id == first[0].problem.id
|
||||
|
||||
problems = db_session.scalars(
|
||||
select(Problem).where(
|
||||
Problem.host_id == hb.host_id,
|
||||
Problem.rule_id == RULE_HOST_SILENCE,
|
||||
Problem.status.in_(("open", "acknowledged")),
|
||||
)
|
||||
).all()
|
||||
assert len(problems) == 1
|
||||
|
||||
|
||||
def test_scan_dedupes_by_hostname_across_duplicate_hosts(db_session, scan_settings):
|
||||
from app.models import Host
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
host_a = Host(
|
||||
hostname="COMM-PC",
|
||||
os_family="windows",
|
||||
product="rdp-login-monitor",
|
||||
ipv4="192.168.162.33",
|
||||
)
|
||||
host_b = Host(
|
||||
hostname="COMM-PC",
|
||||
os_family="windows",
|
||||
product="rdp-login-monitor",
|
||||
ipv4="192.168.162.33",
|
||||
)
|
||||
db_session.add_all([host_a, host_b])
|
||||
db_session.flush()
|
||||
|
||||
hb = _ingest(
|
||||
db_session,
|
||||
host={"hostname": host_a.hostname, "os_family": "windows", "ipv4": host_a.ipv4},
|
||||
type="agent.heartbeat",
|
||||
category="agent",
|
||||
severity="info",
|
||||
title="hb",
|
||||
summary="heartbeat",
|
||||
)
|
||||
hb.host_id = host_a.id
|
||||
hb.received_at = now - timedelta(hours=2)
|
||||
db_session.flush()
|
||||
|
||||
first = run_host_silence_scan(db_session, now=now)
|
||||
assert len(first) == 1
|
||||
assert first[0].created is True
|
||||
|
||||
hb_b = _ingest(
|
||||
db_session,
|
||||
event_id=str(uuid.uuid4()),
|
||||
host={"hostname": host_b.hostname, "os_family": "windows", "ipv4": host_b.ipv4},
|
||||
source={"product": "rdp-login-monitor", "product_version": "2.0.0-SAC"},
|
||||
type="agent.heartbeat",
|
||||
category="agent",
|
||||
severity="info",
|
||||
title="hb2",
|
||||
summary="heartbeat",
|
||||
)
|
||||
hb_b.host_id = host_b.id
|
||||
hb_b.received_at = now - timedelta(hours=2)
|
||||
db_session.flush()
|
||||
|
||||
second = run_host_silence_scan(db_session, now=now + timedelta(minutes=5))
|
||||
assert len(second) == 2
|
||||
assert all(not item.created for item in second)
|
||||
assert {item.problem.id for item in second} == {first[0].problem.id}
|
||||
|
||||
active = db_session.scalars(
|
||||
select(Problem).where(
|
||||
Problem.rule_id == RULE_HOST_SILENCE,
|
||||
Problem.status.in_(("open", "acknowledged")),
|
||||
)
|
||||
).all()
|
||||
assert len(active) == 1
|
||||
|
||||
|
||||
def test_scan_skipped_when_advisory_lock_not_acquired(db_session, scan_settings, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"app.services.host_silence_scan._host_silence_scan_lock",
|
||||
lambda _db: False,
|
||||
)
|
||||
hb = _ingest(
|
||||
db_session,
|
||||
type="agent.heartbeat",
|
||||
category="agent",
|
||||
severity="info",
|
||||
title="hb",
|
||||
summary="heartbeat",
|
||||
)
|
||||
hb.received_at = datetime.now(timezone.utc) - timedelta(hours=2)
|
||||
db_session.flush()
|
||||
|
||||
assert run_host_silence_scan(db_session) == []
|
||||
|
||||
|
||||
def test_scan_notifies_only_on_create(db_session, scan_settings):
|
||||
hb = _ingest(
|
||||
db_session,
|
||||
|
||||
Reference in New Issue
Block a user