Реализация на отдельных двух машинах с протестированным взаимодействием по проверке сообщений
This commit is contained in:
@@ -74,6 +74,49 @@ def safety_reply_message(dialog_id: uuid.UUID, content_kind: str) -> Message:
|
||||
)
|
||||
|
||||
|
||||
def safety_task_recovery_at(now: datetime, settings: Settings) -> datetime:
|
||||
"""Keep recovery behind the synchronous poller's worst-case final attempt."""
|
||||
return now + timedelta(
|
||||
seconds=settings.message_safety_task_poll_max_sec
|
||||
+ settings.message_safety_task_poll_interval_sec
|
||||
+ 5
|
||||
)
|
||||
|
||||
|
||||
async def ensure_delivery_outbox(
|
||||
session: AsyncSession,
|
||||
message_id: uuid.UUID,
|
||||
external_chat_id: uuid.UUID,
|
||||
payload_json: dict[str, Any],
|
||||
next_attempt_at: datetime,
|
||||
) -> DeliveryOutbox:
|
||||
"""Create the per-message outbox row or return the concurrent winner."""
|
||||
statement = (
|
||||
insert(DeliveryOutbox)
|
||||
.values(
|
||||
id=uuid.uuid4(),
|
||||
message_id=message_id,
|
||||
external_chat_id=external_chat_id,
|
||||
payload_json=payload_json,
|
||||
next_attempt_at=next_attempt_at,
|
||||
)
|
||||
.on_conflict_do_nothing(index_elements=[DeliveryOutbox.message_id])
|
||||
.returning(DeliveryOutbox.id)
|
||||
)
|
||||
outbox_id = (await session.execute(statement)).scalar_one_or_none()
|
||||
if outbox_id is not None:
|
||||
outbox = await session.get(DeliveryOutbox, outbox_id)
|
||||
else:
|
||||
outbox = (
|
||||
await session.execute(
|
||||
select(DeliveryOutbox).where(DeliveryOutbox.message_id == message_id)
|
||||
)
|
||||
).scalar_one()
|
||||
if outbox is None: # pragma: no cover - defensive guard for an invalid DB response
|
||||
raise RuntimeError("Delivery outbox row was not returned")
|
||||
return outbox
|
||||
|
||||
|
||||
class DomainError(Exception):
|
||||
def __init__(self, code: str, status: int, message: str, details: dict[str, Any] | None = None):
|
||||
self.code, self.status, self.message = code, status, message
|
||||
@@ -918,7 +961,9 @@ async def send_message(
|
||||
expires_at=datetime.fromisoformat(verdict["expires_at"].replace("Z", "+00:00")),
|
||||
deadline_at=now
|
||||
+ timedelta(seconds=settings.message_safety_task_poll_max_sec + 900),
|
||||
next_poll_at=now,
|
||||
# The request owns the first polling window. Recovery starts
|
||||
# afterwards if the request crashes before completing the task.
|
||||
next_poll_at=safety_task_recovery_at(datetime.now(UTC), settings),
|
||||
)
|
||||
session.add(task)
|
||||
await session.commit()
|
||||
@@ -987,7 +1032,8 @@ async def send_message(
|
||||
"bypassed" if verdict["processing_mode"] == "mock" else "clean"
|
||||
)
|
||||
message.safety_status = "allowed"
|
||||
outbox = DeliveryOutbox(
|
||||
outbox = await ensure_delivery_outbox(
|
||||
session,
|
||||
message_id=message.id,
|
||||
external_chat_id=dialog_id,
|
||||
payload_json={
|
||||
@@ -1019,7 +1065,6 @@ async def send_message(
|
||||
next_attempt_at=datetime.now(UTC)
|
||||
+ timedelta(seconds=settings.bitrix_local_app_http_timeout_sec + 5),
|
||||
)
|
||||
session.add(outbox)
|
||||
await session.commit()
|
||||
await publish_message_status(fanout, message, settings)
|
||||
delivery_payload = await fresh_openlines_payload(outbox.payload_json, s3)
|
||||
|
||||
Reference in New Issue
Block a user