Skip to content

Commit 9e6ae09

Browse files
authored
[uss_qualifier] Record failled queries (#1298)
1 parent b1322fa commit 9e6ae09

File tree

7 files changed

+68
-108
lines changed

7 files changed

+68
-108
lines changed

.basedpyright/baseline.json

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2576,78 +2576,6 @@
25762576
"endColumn": 67,
25772577
"lineCount": 1
25782578
}
2579-
},
2580-
{
2581-
"code": "reportAssignmentType",
2582-
"range": {
2583-
"startColumn": 17,
2584-
"endColumn": 35,
2585-
"lineCount": 1
2586-
}
2587-
},
2588-
{
2589-
"code": "reportOptionalMemberAccess",
2590-
"range": {
2591-
"startColumn": 23,
2592-
"endColumn": 30,
2593-
"lineCount": 1
2594-
}
2595-
},
2596-
{
2597-
"code": "reportOptionalMemberAccess",
2598-
"range": {
2599-
"startColumn": 28,
2600-
"endColumn": 49,
2601-
"lineCount": 1
2602-
}
2603-
},
2604-
{
2605-
"code": "reportOptionalMemberAccess",
2606-
"range": {
2607-
"startColumn": 25,
2608-
"endColumn": 40,
2609-
"lineCount": 1
2610-
}
2611-
},
2612-
{
2613-
"code": "reportPossiblyUnboundVariable",
2614-
"range": {
2615-
"startColumn": 46,
2616-
"endColumn": 48,
2617-
"lineCount": 1
2618-
}
2619-
},
2620-
{
2621-
"code": "reportOperatorIssue",
2622-
"range": {
2623-
"startColumn": 22,
2624-
"endColumn": 31,
2625-
"lineCount": 1
2626-
}
2627-
},
2628-
{
2629-
"code": "reportPossiblyUnboundVariable",
2630-
"range": {
2631-
"startColumn": 23,
2632-
"endColumn": 25,
2633-
"lineCount": 1
2634-
}
2635-
},
2636-
{
2637-
"code": "reportPossiblyUnboundVariable",
2638-
"range": {
2639-
"startColumn": 28,
2640-
"endColumn": 30,
2641-
"lineCount": 1
2642-
}
2643-
},
2644-
{
2645-
"code": "reportPossiblyUnboundVariable",
2646-
"range": {
2647-
"startColumn": 41,
2648-
"endColumn": 43,
2649-
"lineCount": 1
2650-
}
26512579
}
26522580
],
26532581
"./monitoring/monitorlib/fetch/evaluation.py": [

monitoring/mock_uss/versioning/routes.py

100644100755
File mode changed.

monitoring/monitorlib/clients/versioning/client_interuss.py

100644100755
File mode changed.

monitoring/monitorlib/fetch/__init__.py

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import copy
12
import datetime
23
import json
34
import os
45
import traceback
56
import uuid
67
from dataclasses import dataclass
78
from enum import Enum
8-
from typing import TypeVar
9+
from typing import Self, TypeVar
910
from urllib.parse import urlparse
1011

1112
import flask
@@ -458,6 +459,9 @@ class Query(ImplicitDict):
458459
query_type: QueryType | None
459460
"""If specified, the recognized type of this query."""
460461

462+
_previous_query: Self | None
463+
"""If specified, the previous, failling query that generated this query as a retry"""
464+
461465
@property
462466
def timestamp(self) -> datetime.datetime:
463467
"""Safety property to prevent crashes when Query.timestamp is accessed.
@@ -579,10 +583,12 @@ def describe_query(
579583
initiated_at: datetime.datetime,
580584
query_type: QueryType | None = None,
581585
participant_id: str | None = None,
586+
previous_query: Query | None = None,
582587
) -> Query:
583588
query = Query(
584589
request=describe_request(resp.request, initiated_at),
585590
response=describe_response(resp),
591+
_previous_query=previous_query,
586592
)
587593
if query_type is not None:
588594
query.query_type = query_type
@@ -618,10 +624,9 @@ def query_and_describe(
618624
Query object describing the request and response/result.
619625
"""
620626
if client is None:
621-
utm_session = False
622-
client = requests.session()
627+
_client = requests.session()
623628
else:
624-
utm_session = True
629+
_client = client
625630
req_kwargs = kwargs.copy()
626631
if "timeout" not in req_kwargs:
627632
req_kwargs["timeout"] = (
@@ -655,6 +660,36 @@ def get_location() -> str:
655660
.strip()
656661
)
657662

663+
previous_query = None
664+
665+
def build_failing_query(t0) -> Query:
666+
_req_kwargs = copy.deepcopy(req_kwargs)
667+
668+
if isinstance(_client, infrastructure.UTMClientSession):
669+
_req_kwargs = _client.adjust_request_kwargs(_req_kwargs)
670+
del _req_kwargs["timeout"]
671+
672+
req = requests.Request(verb, url, **_req_kwargs)
673+
prepped_req = _client.prepare_request(req)
674+
675+
t1 = datetime.datetime.now(datetime.UTC)
676+
677+
query = Query(
678+
request=describe_request(prepped_req, t0),
679+
response=ResponseDescription(
680+
code=None,
681+
failure="\n".join(failures),
682+
elapsed_s=(t1 - t0).total_seconds(),
683+
reported=StringBasedDateTime(t1),
684+
),
685+
participant_id=participant_id,
686+
_previous_query=previous_query,
687+
)
688+
if query_type is not None:
689+
query.query_type = query_type
690+
691+
return query
692+
658693
# Note: retry logic could be attached to the `client` Session by `mount`ing an HTTPAdapter with custom
659694
# `max_retries`, however we do not want to mutate the provided Session. Instead, retry only on errors we explicitly
660695
# consider retryable.
@@ -664,13 +699,14 @@ def get_location() -> str:
664699
if is_netloc_fake:
665700
failure_message = f"query_and_describe attempt {attempt + 1} from PID {os.getpid()} to {verb} {url} was not attempted because network location of {url} was identified as fake: {settings.fake_netlocs}\nAt {get_location()}"
666701
failures.append(failure_message)
667-
break
702+
return build_failing_query(t0)
668703

669704
return describe_query(
670-
client.request(verb, url, **req_kwargs),
705+
_client.request(verb, url, **req_kwargs),
671706
t0,
672707
query_type=query_type,
673708
participant_id=participant_id,
709+
previous_query=previous_query,
674710
)
675711
except (requests.Timeout, urllib3.exceptions.ReadTimeoutError) as e:
676712
failure_message = f"query_and_describe attempt {attempt + 1} from PID {os.getpid()} to {verb} {url} failed with timeout {type(e).__name__}: {str(e)}\nAt {get_location()}"
@@ -689,38 +725,28 @@ def get_location() -> str:
689725
if not expect_failure:
690726
logger.warning(failure_message)
691727
failures.append(failure_message)
728+
692729
if not retryable:
693-
break
730+
return build_failing_query(t0)
731+
694732
except requests.RequestException as e:
695733
failure_message = f"query_and_describe attempt {attempt + 1} from PID {os.getpid()} to {verb} {url} failed with non-retryable RequestException {type(e).__name__}: {str(e)}\nAt {get_location()}"
696734
if not expect_failure:
697735
logger.warning(failure_message)
698736
failures.append(failure_message)
699737

700-
break
701-
finally:
702-
t1 = datetime.datetime.now(datetime.UTC)
703-
704-
# Reconstruct request similar to the one in the query (which is not
705-
# accessible at this point)
706-
if utm_session:
707-
req_kwargs = client.adjust_request_kwargs(req_kwargs)
708-
del req_kwargs["timeout"]
709-
req = requests.Request(verb, url, **req_kwargs)
710-
prepped_req = client.prepare_request(req)
711-
result = Query(
712-
request=describe_request(prepped_req, t0),
713-
response=ResponseDescription(
714-
code=None,
715-
failure="\n".join(failures),
716-
elapsed_s=(t1 - t0).total_seconds(),
717-
reported=StringBasedDateTime(t1),
718-
),
719-
participant_id=participant_id,
720-
)
721-
if query_type is not None:
722-
result.query_type = query_type
723-
return result
738+
return build_failing_query(t0)
739+
740+
previous_query = build_failing_query(
741+
t0
742+
) # If we arrive there, query failled, but is retriable
743+
744+
if not previous_query:
745+
raise Exception(
746+
"Internal error: arrived after retried without any expected failled query"
747+
)
748+
749+
return previous_query # Previous query is the last failled one
724750

725751

726752
def describe_flask_query(

monitoring/uss_qualifier/scenarios/scenario.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,11 @@ def record_queries(self, queries: list[fetch.Query]) -> None:
423423

424424
def record_query(self, query: fetch.Query) -> None:
425425
self._expect_phase({ScenarioPhase.RunningTestStep, ScenarioPhase.CleaningUp})
426+
427+
# If the query has a previous one, record it first
428+
if "_previous_query" in query and query._previous_query:
429+
self.record_query(query._previous_query)
430+
426431
if "queries" not in self._step_report:
427432
self._step_report.queries = []
428433
for existing_query in self._step_report.queries:

schemas/manage_type_schemas.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import json
55
import os
66
import sys
7-
from typing import get_args, get_origin, get_type_hints
7+
from typing import Self, get_args, get_origin, get_type_hints
88

99
import implicitdict
1010
import implicitdict.jsonschema
@@ -78,7 +78,8 @@ def _make_type_schemas(
7878
pending_types.extend(get_args(pending_type))
7979
else:
8080
if (
81-
issubclass(pending_type, ImplicitDict)
81+
pending_type != Self
82+
and issubclass(pending_type, ImplicitDict)
8283
and fullname(pending_type) not in already_checked
8384
):
8485
_make_type_schemas(

uv.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)