feat: prepare ARR for controlled public deployment
This commit is contained in:
124
arr_ingestion/direct_service.py
Normal file
124
arr_ingestion/direct_service.py
Normal file
@@ -0,0 +1,124 @@
|
||||
"""One-call direct structured-result orchestration owned by ARR."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Protocol
|
||||
|
||||
from arr_ingestion.contracts import IngestionError
|
||||
from arr_ingestion.direct_contracts import (
|
||||
DirectSubmissionReceipt,
|
||||
DirectSubmissionRequest,
|
||||
ReceivedDirectSubmission,
|
||||
SubmissionGrant,
|
||||
VerifiedDirectSubmission,
|
||||
)
|
||||
|
||||
|
||||
class DirectSubmissionRepository(Protocol):
|
||||
def assert_ready(self) -> None:
|
||||
...
|
||||
|
||||
def issue_grant(
|
||||
self,
|
||||
job_id: str,
|
||||
attempt_no: int,
|
||||
*,
|
||||
ttl_seconds: int,
|
||||
) -> SubmissionGrant:
|
||||
...
|
||||
|
||||
def receive(
|
||||
self,
|
||||
request: DirectSubmissionRequest,
|
||||
) -> ReceivedDirectSubmission:
|
||||
...
|
||||
|
||||
def commit(
|
||||
self,
|
||||
verified: VerifiedDirectSubmission,
|
||||
) -> DirectSubmissionReceipt:
|
||||
...
|
||||
|
||||
def reject(
|
||||
self,
|
||||
submission: ReceivedDirectSubmission,
|
||||
failure_code: str,
|
||||
) -> None:
|
||||
...
|
||||
|
||||
def expire_stale(self, *, limit: int) -> int:
|
||||
...
|
||||
|
||||
|
||||
class DirectSubmissionValidator(Protocol):
|
||||
def validate(
|
||||
self,
|
||||
submission: ReceivedDirectSubmission,
|
||||
) -> VerifiedDirectSubmission:
|
||||
...
|
||||
|
||||
|
||||
_RETRYABLE_VALIDATION_CODES = frozenset(
|
||||
{
|
||||
"ARTIFACT_NOT_FOUND",
|
||||
"ARTIFACT_UNREADABLE",
|
||||
"DIRECT_REPLAY_UNAVAILABLE",
|
||||
"OBJECT_STORE_UNAVAILABLE",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class DirectSubmissionService:
|
||||
"""Persist, replay, and atomically commit one complete structured payload."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
validator: DirectSubmissionValidator,
|
||||
repository: DirectSubmissionRepository,
|
||||
) -> None:
|
||||
self._validator = validator
|
||||
self._repository = repository
|
||||
|
||||
def issue_grant(
|
||||
self,
|
||||
job_id: str,
|
||||
attempt_no: int,
|
||||
*,
|
||||
ttl_seconds: int = 900,
|
||||
) -> SubmissionGrant:
|
||||
return self._repository.issue_grant(
|
||||
job_id,
|
||||
attempt_no,
|
||||
ttl_seconds=ttl_seconds,
|
||||
)
|
||||
|
||||
def assert_ready(self) -> None:
|
||||
self._repository.assert_ready()
|
||||
|
||||
def submit(self, raw_request: Any) -> DirectSubmissionReceipt:
|
||||
request = DirectSubmissionRequest.from_dict(raw_request)
|
||||
submission = self._repository.receive(request)
|
||||
if submission.receipt is not None:
|
||||
return submission.receipt
|
||||
try:
|
||||
verified = self._validator.validate(submission)
|
||||
except IngestionError as error:
|
||||
if (
|
||||
not error.retryable
|
||||
and error.code not in _RETRYABLE_VALIDATION_CODES
|
||||
):
|
||||
self._repository.reject(submission, error.code)
|
||||
raise
|
||||
return self._repository.commit(verified)
|
||||
|
||||
def expire_stale(self, *, limit: int = 100) -> int:
|
||||
if (
|
||||
not isinstance(limit, int)
|
||||
or isinstance(limit, bool)
|
||||
or not 1 <= limit <= 1000
|
||||
):
|
||||
raise IngestionError(
|
||||
"DIRECT_SUBMISSION_INVALID",
|
||||
"direct submission expiry limit is invalid",
|
||||
)
|
||||
return self._repository.expire_stale(limit=limit)
|
||||
Reference in New Issue
Block a user