Merge pull request 'fix(vessels): protect Hormuz VesselAPI rows from AISStream eviction' (#25) from fix/vesselapi-hormuz-eviction into master
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m7s
All checks were successful
build-and-deploy / build-push-deploy (push) Successful in 3m7s
This commit is contained in:
commit
b6da7b7bd4
2 changed files with 45 additions and 1 deletions
|
|
@ -993,8 +993,18 @@ async def upsert_vessel(marker: dict) -> None:
|
|||
vessel_last_known[vid] = stored
|
||||
if len(vessel_last_known) > _MAX_VESSELS:
|
||||
excess = len(vessel_last_known) - int(_MAX_VESSELS * 0.9)
|
||||
# AISStream's live US-coast feed can crowd the shared store past
|
||||
# _MAX_VESSELS and evict the Hormuz VesselAPI rows (refreshed only
|
||||
# every VESSELAPI_INTERVAL) as "oldest". Keep src=vesselapi rows
|
||||
# resident so the blind-spot box stays visible between polls.
|
||||
evictable = [
|
||||
k for k, v in vessel_last_known.items()
|
||||
if (v.get("extra") or {}).get("src") != "vesselapi"
|
||||
]
|
||||
if len(evictable) < excess:
|
||||
excess = len(evictable)
|
||||
oldest = sorted(
|
||||
vessel_last_known,
|
||||
evictable,
|
||||
key=lambda k: vessel_last_known[k].get("seen_at", ""),
|
||||
)[:excess]
|
||||
for k in oldest:
|
||||
|
|
|
|||
|
|
@ -298,6 +298,40 @@ def _make_get_client(client):
|
|||
return _get_client
|
||||
|
||||
|
||||
def test_eviction_keeps_vesselapi_rows(monkeypatch):
|
||||
"""AISStream crowding past _MAX_VESSELS must not reap Hormuz VesselAPI rows."""
|
||||
from ws_manager import manager
|
||||
from live_layers import _MAX_VESSELS, upsert_vessel
|
||||
|
||||
_patch_side_effects(monkeypatch)
|
||||
monkeypatch.setattr("live_layers._MAX_VESSELS", 10)
|
||||
vessel_last_known.clear()
|
||||
manager._queues.clear()
|
||||
manager._viewports.clear()
|
||||
|
||||
# One Hormuz VesselAPI row with the oldest seen_at — the first thing the
|
||||
# old "evict oldest" logic would reap — plus enough AISStream rows to
|
||||
# exceed the cap.
|
||||
vessel_last_known["422050100"] = {
|
||||
"id": "422050100", "lat": 26.5, "lon": 56.3, "label": "HORMUZ STAR",
|
||||
"extra": {"src": "vesselapi"}, "seen_at": "2026-08-29T00:00:00+00:00",
|
||||
}
|
||||
for i in range(10):
|
||||
vid = f"3{i:08d}"
|
||||
vessel_last_known[vid] = {
|
||||
"id": vid, "lat": 35.0 + i * 0.01, "lon": -79.0, "label": vid,
|
||||
"extra": {"src": "aisstream"},
|
||||
"seen_at": f"2026-08-29T0{i}:00:00+00:00",
|
||||
}
|
||||
|
||||
# One more AISStream marker pushes past the cap and triggers eviction.
|
||||
asyncio.run(upsert_vessel({"id": "399999999", "lat": 36.0, "lon": -78.0, "label": "NEW"}))
|
||||
|
||||
assert "422050100" in vessel_last_known # VesselAPI row survives
|
||||
assert vessel_last_known["422050100"]["extra"]["src"] == "vesselapi"
|
||||
assert len(vessel_last_known) <= 10
|
||||
|
||||
|
||||
# ── Independent-provider idle behaviour ───────────────────────────────────
|
||||
# VesselAPI must never be gated on AISStream (or vice versa): a missing key on
|
||||
# one provider leaves the other running. ``_StopLoop`` is a BaseException so
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue