fix(UME): 兼容首批无marker场景并补充回归测试

告警分页在响应头缺失 marker 时直接结束迭代,避免首批已全量却重复翻页;同时补充对应单测并保持 HTTPTransport 传输方式与现网一致。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-08 20:13:10 +08:00
parent 2c0b52d972
commit 00afaaae3d
3 changed files with 35 additions and 4 deletions

View file

@ -177,8 +177,9 @@ class UMEClient:
def _client(self) -> httpx.Client:
# Use explicit HTTPTransport to keep behavior consistent with onsite validation.
# In this mode, requests run over HTTP/1.1 and avoid HTTP/2 negotiation issues.
transport = httpx.HTTPTransport()
return httpx.Client(verify=self.verify_tls, timeout=self.timeout_s, transport=transport)
transport = httpx.HTTPTransport(verify=self.verify_tls, http2=False)
return httpx.Client(transport=transport, timeout=self.timeout_s)
def _extract_token_and_ttl(self, payload: dict[str, Any]) -> tuple[str, int | None]:
token = ""

View file

@ -282,8 +282,12 @@ def _sync_alarms_common(
next_marker = str(diag.marker or "").strip()
if is_end_of_reply:
break
# marker paging: no marker and empty data means no next page.
if not next_marker and not rows:
# marker paging: if response header has no marker, treat as end of iteration.
# Some UME deployments omit marker when the first page already contains all rows.
if not next_marker:
if rows:
warnings.append("marker_missing_stop")
paging_note = "marker_missing_stop"
break
if not is_uncleared:

View file

@ -281,6 +281,32 @@ class UmeSyncServiceTests(unittest.TestCase):
self.assertEqual(job.pulled_count, 1)
self.assertIsNotNone(self.db.get(UmeAlarmCurrent, "AK-1"))
def test_sync_current_alarms_stop_when_marker_missing(self):
class _C:
def __init__(self):
self.calls = 0
def get_alarms(self, *, is_uncleared: bool, limit=None, marker=None):
self.calls += 1
class _D:
marker = ""
is_end_of_reply = None
return (
[
{"alarmKey": "AK-1", "ne-id": "NE-1", "perceivedSeverity": "major", "isCleared": "false"},
],
_D(),
)
c = _C()
job, _ = sync_alarms_current(self.db, c, trigger_mode="manual")
self.assertEqual(job.status, "done")
self.assertEqual(job.pulled_count, 1)
self.assertEqual(c.calls, 1)
self.assertIsNotNone(self.db.get(UmeAlarmCurrent, "AK-1"))
def test_derive_ne_id_from_alarmkey_formats(self):
alarm_hash = {"alarmkey": "00ceb960-1b62-478e-8303-0935ffea1d28#99010"}
alarm_csv = {"alarmkey": "00ceb960-1b62-478e-8303-0935ffea1d28, 4237, 79"}