fix(simulation): retry temporary provider proxy failures
This commit is contained in:
@@ -36,6 +36,7 @@ IMAGE_DIGEST_PATTERN: Final = re.compile(r"^sha256:[a-f0-9]{64}$")
|
|||||||
DEFAULT_CHUNK_BYTES: Final = 8 * 1024 * 1024
|
DEFAULT_CHUNK_BYTES: Final = 8 * 1024 * 1024
|
||||||
MAX_JSON_RESPONSE_BYTES: Final = 32 * 1024 * 1024
|
MAX_JSON_RESPONSE_BYTES: Final = 32 * 1024 * 1024
|
||||||
MAX_RETRIES: Final = 3
|
MAX_RETRIES: Final = 3
|
||||||
|
RETRYABLE_PROVIDER_STATUS_CODES: Final = {502, 503, 504}
|
||||||
DEFAULT_INGEST_TIMEOUT_SECONDS: Final = 30 * 60.0
|
DEFAULT_INGEST_TIMEOUT_SECONDS: Final = 30 * 60.0
|
||||||
|
|
||||||
|
|
||||||
@@ -899,7 +900,10 @@ def _validate_runtime_provenance(document: Mapping[str, object]) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _unavailable(message: str, error: httpx.HTTPError) -> GaussianPipelineGatewayError:
|
def _unavailable(message: str, error: httpx.HTTPError) -> GaussianPipelineGatewayError:
|
||||||
if isinstance(error, httpx.TransportError):
|
if isinstance(error, httpx.TransportError) or (
|
||||||
|
isinstance(error, httpx.HTTPStatusError)
|
||||||
|
and error.response.status_code in RETRYABLE_PROVIDER_STATUS_CODES
|
||||||
|
):
|
||||||
return GaussianPipelineUnavailableError(message)
|
return GaussianPipelineUnavailableError(message)
|
||||||
return GaussianPipelineGatewayError(message)
|
return GaussianPipelineGatewayError(message)
|
||||||
|
|
||||||
@@ -922,4 +926,6 @@ def _provider_rejection(response: httpx.Response) -> GaussianPipelineGatewayErro
|
|||||||
message = f"Gaussian provider rejected request (HTTP {response.status_code})"
|
message = f"Gaussian provider rejected request (HTTP {response.status_code})"
|
||||||
if detail is not None:
|
if detail is not None:
|
||||||
message = f"{message}: {detail}"
|
message = f"{message}: {detail}"
|
||||||
|
if response.status_code in RETRYABLE_PROVIDER_STATUS_CODES:
|
||||||
|
return GaussianPipelineUnavailableError(message)
|
||||||
return GaussianPipelineGatewayError(message)
|
return GaussianPipelineGatewayError(message)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from k1link.simulation.gaussian_pipeline_gateway import (
|
|||||||
GaussianPipelineGateway,
|
GaussianPipelineGateway,
|
||||||
GaussianPipelineGatewayError,
|
GaussianPipelineGatewayError,
|
||||||
GaussianPipelineIntegrityError,
|
GaussianPipelineIntegrityError,
|
||||||
|
GaussianPipelineUnavailableError,
|
||||||
_discover_bundle_members,
|
_discover_bundle_members,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -256,6 +257,30 @@ def test_gateway_surfaces_bounded_provider_rejection_detail(tmp_path: Path) -> N
|
|||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("status_code", [502, 503, 504])
|
||||||
|
def test_gateway_classifies_temporary_provider_proxy_failures_as_unavailable(
|
||||||
|
tmp_path: Path,
|
||||||
|
status_code: int,
|
||||||
|
) -> None:
|
||||||
|
with (
|
||||||
|
GaussianPipelineGateway(
|
||||||
|
"http://gaussian.test",
|
||||||
|
_token_file(tmp_path),
|
||||||
|
transport=httpx.MockTransport(
|
||||||
|
lambda _request: httpx.Response(
|
||||||
|
status_code,
|
||||||
|
json={"error": "provider_unavailable"},
|
||||||
|
)
|
||||||
|
),
|
||||||
|
) as gateway,
|
||||||
|
pytest.raises(
|
||||||
|
GaussianPipelineUnavailableError,
|
||||||
|
match=rf"HTTP {status_code}.*provider_unavailable",
|
||||||
|
),
|
||||||
|
):
|
||||||
|
gateway.capabilities()
|
||||||
|
|
||||||
|
|
||||||
def test_gateway_rejects_incomplete_lcc_bundle(tmp_path: Path) -> None:
|
def test_gateway_rejects_incomplete_lcc_bundle(tmp_path: Path) -> None:
|
||||||
bundle = tmp_path / "bundle"
|
bundle = tmp_path / "bundle"
|
||||||
bundle.mkdir()
|
bundle.mkdir()
|
||||||
|
|||||||
Reference in New Issue
Block a user