Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[uss_qualifier] NET0460 evaluate display provider's flight data details endpoint performance #174

Merged
merged 1 commit into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion monitoring/monitorlib/fetch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from implicitdict import ImplicitDict, StringBasedDateTime
from monitoring.monitorlib import infrastructure

from monitoring.monitorlib.rid import RIDVersion

TIMEOUTS = (5, 5) # Timeouts of `connect` and `read` in seconds
ATTEMPTS = (
Expand Down Expand Up @@ -212,6 +212,15 @@ class QueryType(str, Enum):
)
F3548v21USSMakeUssReport = "astm.f3548.v21.uss.makeUssReport"

@staticmethod
def flight_details(rid_version: RIDVersion):
if rid_version == RIDVersion.f3411_19:
return QueryType.F3411v19aFlightDetails
elif rid_version == RIDVersion.f3411_22a:
return QueryType.F3411v22aFlightDetails
else:
raise ValueError(f"Unsupported RID version: {rid_version}")


class Query(ImplicitDict):
request: RequestDescription
Expand Down
27 changes: 18 additions & 9 deletions monitoring/monitorlib/rid.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,6 @@ def openapi_put_isa_response_path(self) -> str:
else:
raise ValueError(f"Unsupported RID version '{self}'")

@property
def read_scope(self) -> str:
if self == RIDVersion.f3411_19:
return v19.constants.Scope.Read
elif self == RIDVersion.f3411_22a:
return v22a.constants.Scope.DisplayProvider
else:
raise ValueError("Unsupported RID version '{}'".format(self))

@property
def realtime_period(self) -> timedelta:
if self == RIDVersion.f3411_19:
Expand Down Expand Up @@ -155,6 +146,24 @@ def dp_data_resp_percentile95_s(self) -> float:
else:
raise ValueError("Unsupported RID version '{}'".format(self))

@property
def dp_details_resp_percentile95_s(self) -> float:
if self == RIDVersion.f3411_19:
return v19.constants.NetDpDetailsResponse95thPercentileSeconds
elif self == RIDVersion.f3411_22a:
return v22a.constants.NetDpDetailsResponse95thPercentileSeconds
else:
raise ValueError("Unsupported RID version '{}'".format(self))

@property
def dp_details_resp_percentile99_s(self) -> float:
if self == RIDVersion.f3411_19:
return v19.constants.NetDpDetailsResponse99thPercentileSeconds
elif self == RIDVersion.f3411_22a:
return v22a.constants.NetDpDetailsResponse99thPercentileSeconds
else:
raise ValueError("Unsupported RID version '{}'".format(self))

@property
def dp_data_resp_percentile99_s(self) -> float:
if self == RIDVersion.f3411_19:
Expand Down
23 changes: 17 additions & 6 deletions monitoring/uss_qualifier/resources/netrid/observers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from implicitdict import ImplicitDict

from monitoring.monitorlib import fetch, infrastructure
from monitoring.monitorlib.fetch import QueryType
from monitoring.monitorlib.infrastructure import UTMClientSession
from monitoring.monitorlib.rid import RIDVersion
from monitoring.monitorlib.rid_automated_testing import observation_api
Expand All @@ -27,9 +28,6 @@ def __init__(
self.participant_id = participant_id
self.base_url = base_url

# TODO: Change observation API to use an InterUSS scope rather than re-using an ASTM scope
self.rid_version = RIDVersion.f3411_19

def observe_system(
self, rect: s2sphere.LatLngRect
) -> Tuple[Optional[observation_api.GetDisplayDataResponse], fetch.Query]:
Expand All @@ -40,7 +38,12 @@ def observe_system(
rect.hi().lng().degrees,
)
query = fetch.query_and_describe(
self.session, "GET", url, scope=self.rid_version.read_scope
self.session,
"GET",
url,
# TODO replace with 'uas_standards.interuss.automated_testing.rid.v1.constants.Scope.Observe' once
# the standard is updated with https://github.com/interuss/uas_standards/pull/11/files
scope="dss.read.identification_service_areas",
)
try:
result = (
Expand All @@ -56,11 +59,19 @@ def observe_system(
return result, query

def observe_flight_details(
self, flight_id: str
self, flight_id: str, rid_version: RIDVersion
) -> Tuple[Optional[observation_api.GetDetailsResponse], fetch.Query]:
query = fetch.query_and_describe(
self.session, "GET", f"/display_data/{flight_id}"
self.session,
"GET",
f"/display_data/{flight_id}",
# TODO replace with 'uas_standards.interuss.automated_testing.rid.v1.constants.Scope.Observe' once
# the standard is updated with https://github.com/interuss/uas_standards/pull/11/files
scope="dss.read.identification_service_areas",
)
# Record query metadata for later use in the aggregate checks
query.server_id = self.participant_id
query.query_type = QueryType.flight_details(rid_version)
try:
result = (
ImplicitDict.parse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ def run(self):
self._dp_display_data_times_step()

self.end_test_step()
self.begin_test_step("Performance of /display_data/<flight_id> requests")
self._dp_display_data_details_times_step()
self.end_test_step()

self.end_test_case()

# SP performance
Expand All @@ -105,6 +109,60 @@ def run(self):

self.end_test_scenario()

def _dp_display_data_details_times_step(self):
"""
Check performance of /display_data/<flight_id> requests and confirm they conform to
NetDpDetailsResponse95thPercentile (2s) and NetDpDetailsResponse99thPercentile (6s)
"""
for participant, all_queries in self._queries_by_participant.items():
relevant_queries: List[fetch.Query] = list()
for query in all_queries:

if (
query.status_code == 200
and query.has_field_with_value("query_type")
and (
query.query_type == QueryType.F3411v19aFlightDetails
or query.query_type == QueryType.F3411v22aFlightDetails
)
):
relevant_queries.append(query)

if len(relevant_queries) == 0:
# this may be a service provider
self.record_note(
f"{participant}/display_data/<flight_id>",
"skipped check: no relevant queries",
)
continue

# compute percentiles
durations = [query.response.elapsed_s for query in relevant_queries]
[p95, p99] = evaluation.compute_percentiles(durations, [95, 99])
with self.check(
"Performance of /display_data/<flight_id> requests", [participant]
) as check:
if p95 > self._rid_version.dp_details_resp_percentile95_s:
check.record_failed(
summary=f"95th percentile of durations for DP display_data details queries is higher than threshold",
severity=Severity.Medium,
participants=[participant],
details=f"threshold: {self._rid_version.dp_details_resp_percentile95_s}s, 95th percentile: {p95}s",
)
if p99 > self._rid_version.dp_details_resp_percentile99_s:
check.record_failed(
summary=f"99th percentile of durations for DP display_data details queries is higher than threshold",
severity=Severity.Medium,
participants=[participant],
details=f"threshold: {self._rid_version.dp_details_resp_percentile99_s}s, 99th percentile: {p99}s",
)

self.record_note(
f"{participant}/display_data/<flight_id>",
f"{participant}/display_data/<flight_id> stats computed on {len(durations)} queries "
f"95th percentile: {p95}s, 99th percentile: {p99}s",
)

def _sp_flights_area_times_step(self):
for participant, all_queries in self._queries_by_participant.items():
# identify successful flights queries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
RIDCommonDictionaryEvaluator,
)

from monitoring.monitorlib.fetch import Query
from monitoring.monitorlib.fetch import Query, QueryType
from monitoring.monitorlib.fetch.rid import (
all_flights,
FetchedFlights,
Expand Down Expand Up @@ -261,6 +261,16 @@ def evaluate_system_instantaneously(
query,
verified_sps,
)
# We also issue queries to the flight details endpoint in order to collect
# performance statistics, which are computed and checked at a later stage.
if query.status_code == 200:
# If there are multiple flights, we only issue a single details query for the first returned one,
# as we don't want to slow down the test we are piggy-backing on.
if len(observation.flights) > 0:
(_, detailQuery) = observer.observe_flight_details(
observation.flights[0].id, self._rid_version
)
self._test_scenario.record_query(detailQuery)

# TODO: If bounding rect is smaller than cluster threshold, expand slightly above cluster threshold and re-observe
# TODO: If bounding rect is smaller than area-too-large threshold, expand slightly above area-too-large threshold and re-observe
Expand Down Expand Up @@ -617,14 +627,14 @@ def _evaluate_clusters_observation(
check.record_failed(
summary="Error while evaluating clustered area view. Missing flight",
severity=Severity.Medium,
details=f"{expected_count-clustered_flight_count} (~{uncertain_count}) missing flight(s)",
details=f"{expected_count - clustered_flight_count} (~{uncertain_count}) missing flight(s)",
)
elif clustered_flight_count > expected_count + uncertain_count:
# Unexpected flight
check.record_failed(
summary="Error while evaluating clustered area view. Unexpected flight",
severity=Severity.Medium,
details=f"{clustered_flight_count-expected_count} (~{uncertain_count}) unexpected flight(s)",
details=f"{clustered_flight_count - expected_count} (~{uncertain_count}) unexpected flight(s)",
)
elif clustered_flight_count == expected_count:
# evaluate cluster obfuscation distance
Expand Down Expand Up @@ -674,7 +684,7 @@ def _evaluate_clusters_obfuscation_distance(
cluster_width, cluster_height = geo.flatten(
cluster_rect.lo(), cluster_rect.hi()
)
min_dim = 2 * observer.rid_version.min_obfuscation_distance_m
min_dim = 2 * self._rid_version.min_obfuscation_distance_m
if cluster_height < min_dim or cluster_width < min_dim:
# Cluster has a too small distance to the edge
check.record_failed(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ The observers to evaluate in the report.

## Performance of Display Providers requests test case

### Performance of /display_data/<flight_id> requests test step

For this step, all successful display data queries made during the execution of the previous scenarios are used to compute an aggregate statistic.

#### Performance of /display_data/<flight_id> requests check

**[astm.f3411.v19.NET0460](../../../../requirements/astm/f3411/v19.md) Checks that the DP response times for the
Display Application's flight details requests have a p95 and p99 that are respectively below
`NetDpDetailsResponse95thPercentileSeconds` (2 seconds) and `NetDpDetailsResponse99thPercentileSeconds` (6 seconds).

### Performance of /display_data requests test step
In this step, all successful display data queries made during the execution of the previous scenarios are aggregated per
observer and per request (identified by their URLs). For each of those, and using the session length
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
In this special scenario, the report of previously executed ASTM F3411-22a NetRID scenario(s) are evaluated for the
performances of the queries made during their execution.


## Resources

### report_resource
Expand All @@ -18,6 +17,16 @@ The observers to evaluate in the report.

## Performance of Display Providers requests test case

### Performance of /display_data/<flight_id> requests test step

For this step, all successful display data queries made during the execution of the previous scenarios are used to compute an aggregate statistic.

#### Performance of /display_data/<flight_id> requests check

**[astm.f3411.v22a.NET0460](../../../../requirements/astm/f3411/v22a.md) Checks that the DP response times for the
Display Application's flight details requests have a p95 and p99 that are respectively below
`NetDpDetailsResponse95thPercentileSeconds` (2 seconds) and `NetDpDetailsResponse99thPercentileSeconds` (6 seconds).

### Performance of /display_data requests test step
In this step, all successful display data queries made during the execution of the previous scenarios are aggregated per
observer and per request (identified by their URLs). For each of those, and using the session length
Expand Down