From 00e9eaf2b8cf08d1e044b95c5a11fe8b80e10f24 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Tue, 7 Apr 2026 21:11:28 +0000 Subject: [PATCH 1/2] Reduce number of UTMClientSessions generated by DSSInstanceResource --- .../resources/astm/f3548/v21/dss.py | 28 +++++++++++++++---- .../v21/dss/DSSInstanceSpecification.json | 7 +++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py b/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py index f869667993..39168563f1 100644 --- a/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py +++ b/monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py @@ -41,10 +41,10 @@ ) from uas_standards.astm.f3548.v21.constants import Scope -from monitoring.monitorlib import infrastructure from monitoring.monitorlib.fetch import Query, QueryError, QueryType, query_and_describe from monitoring.monitorlib.fetch import scd as fetch from monitoring.monitorlib.fetch.scd import FetchedSubscription, FetchedSubscriptions +from monitoring.monitorlib.infrastructure import UTMClientSession from monitoring.monitorlib.inspection import calling_function_name, fullname from monitoring.monitorlib.mutate import scd as mutate from monitoring.monitorlib.mutate.scd import MutatedSubscription @@ -65,6 +65,9 @@ class DSSInstanceSpecification(ImplicitDict): supports_ovn_request: Optional[bool] """Whether this DSS instance supports the optional extension not part of the original F3548 standard API allowing a USS to request a specific OVN when creating or updating an operational intent.""" + timeout_seconds: Optional[float] + """If specified, number of seconds to allow before timing out requests to this DSS instance.""" + def __init__(self, *args, **kwargs): super().__init__(**kwargs) try: @@ -77,7 +80,7 @@ class DSSInstance: participant_id: str user_participant_ids: list[str] base_url: str - client: infrastructure.UTMClientSession + client: UTMClientSession _scopes_authorized: set[str] def __init__( @@ -85,13 +88,13 @@ def __init__( participant_id: str, user_participant_ids: list[str], base_url: str, - auth_adapter: infrastructure.AuthAdapter, + client: UTMClientSession, scopes_authorized: list[str], ): self.participant_id = participant_id self.user_participant_ids = user_participant_ids self.base_url = base_url - self.client = infrastructure.UTMClientSession(base_url, auth_adapter) + self.client = client self._scopes_authorized = set( s.value if isinstance(s, Enum) else s for s in scopes_authorized ) @@ -126,7 +129,11 @@ def with_different_auth( participant_id=self.participant_id, user_participant_ids=self.user_participant_ids, base_url=self.base_url, - auth_adapter=auth_adapter.adapter, + client=UTMClientSession( + self.base_url, + auth_adapter=auth_adapter.adapter, + timeout_seconds=self.client.timeout_seconds, + ), scopes_authorized=list(scopes_required), ) @@ -694,6 +701,7 @@ def delete_subscription(self, sub_id: str, sub_version: str) -> MutatedSubscript class DSSInstanceResource(Resource[DSSInstanceSpecification]): _specification: DSSInstanceSpecification _auth_adapter: AuthAdapterResource + _client: UTMClientSession def __init__( self, @@ -704,6 +712,14 @@ def __init__( super().__init__(specification, resource_origin) self._specification = specification self._auth_adapter = auth_adapter + timeout_seconds = ( + specification.timeout_seconds + if "timeout_seconds" in specification + else None + ) + self._client = UTMClientSession( + self._specification.base_url, auth_adapter.adapter, timeout_seconds + ) def can_use_scope(self, scope: str) -> bool: return scope in self._auth_adapter.scopes @@ -777,7 +793,7 @@ def get_instance(self, scopes_required: dict[str, str]) -> DSSInstance: else [] ), self._specification.base_url, - self._auth_adapter.adapter, + self._client, list(scopes_required), ) diff --git a/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json b/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json index a1ae7aaa8e..6712a3a77c 100644 --- a/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json +++ b/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json @@ -22,6 +22,13 @@ "null" ] }, + "timeout_seconds": { + "description": "If specified, number of seconds to allow before timing out requests to this DSS instance.", + "type": [ + "number", + "null" + ] + }, "user_participant_ids": { "description": "IDs of any participants using this DSS instance, apart from the USS responsible for this DSS instance.", "items": { From 5564c55e2f78c6bfd873d565df5ce2604fae5a2f Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Tue, 7 Apr 2026 21:23:40 +0000 Subject: [PATCH 2/2] Move UTMClientSession instantiation to resource constructor for F3411 --- monitoring/monitorlib/infrastructure.py | 4 ++++ monitoring/uss_qualifier/resources/astm/f3411/dss.py | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/monitoring/monitorlib/infrastructure.py b/monitoring/monitorlib/infrastructure.py index 8c38434f13..186710753f 100644 --- a/monitoring/monitorlib/infrastructure.py +++ b/monitoring/monitorlib/infrastructure.py @@ -84,6 +84,10 @@ class UTMClientSession(requests.Session): If the URL starts with '/', then automatically prefix the URL with the `prefix_url` specified on construction (this is usually the base URL of the DSS). + + When possible, a UTMClientSession should be reused rather than creating a + new one because an excessive number of UTMClientSessions can exhaust the + number of connections allowed by the system (see #1407). """ def __init__( diff --git a/monitoring/uss_qualifier/resources/astm/f3411/dss.py b/monitoring/uss_qualifier/resources/astm/f3411/dss.py index ac6e59eb07..72b7d40c41 100644 --- a/monitoring/uss_qualifier/resources/astm/f3411/dss.py +++ b/monitoring/uss_qualifier/resources/astm/f3411/dss.py @@ -5,6 +5,7 @@ from implicitdict import ImplicitDict from monitoring.monitorlib import infrastructure +from monitoring.monitorlib.infrastructure import UTMClientSession from monitoring.monitorlib.rid import RIDVersion from monitoring.uss_qualifier.reports.report import ParticipantID from monitoring.uss_qualifier.resources.communications import AuthAdapterResource @@ -40,12 +41,12 @@ def __init__( participant_id: ParticipantID, base_url: str, rid_version: RIDVersion, - auth_adapter: infrastructure.AuthAdapter, + client: UTMClientSession, ): self.participant_id = participant_id self.base_url = base_url self.rid_version = rid_version - self.client = infrastructure.UTMClientSession(base_url, auth_adapter) + self.client = client def is_same_as(self, other: DSSInstance) -> bool: return ( @@ -81,7 +82,9 @@ def __init__( specification.participant_id, specification.base_url, specification.rid_version, - auth_adapter.adapter, + infrastructure.UTMClientSession( + specification.base_url, auth_adapter.adapter + ), ) @classmethod