Add worker delivery acceptance probe
This commit is contained in:
@@ -4,6 +4,7 @@ from datetime import datetime, timedelta, timezone
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
from celery import Celery
|
||||
from celery.signals import (
|
||||
@@ -110,6 +111,14 @@ celery.conf.update(
|
||||
worker_prefetch_multiplier=1,
|
||||
task_acks_late=True,
|
||||
task_reject_on_worker_lost=True,
|
||||
task_track_started=True,
|
||||
broker_transport_options={
|
||||
"visibility_timeout": settings.celery_visibility_timeout_seconds,
|
||||
},
|
||||
result_backend_transport_options={
|
||||
"visibility_timeout": settings.celery_visibility_timeout_seconds,
|
||||
},
|
||||
visibility_timeout=settings.celery_visibility_timeout_seconds,
|
||||
beat_schedule={
|
||||
"calendar-outbox-every-minute": {
|
||||
"task": "govoplan.calendar.dispatch_outbox",
|
||||
@@ -308,6 +317,69 @@ def ping():
|
||||
return "pong"
|
||||
|
||||
|
||||
@celery.task(
|
||||
bind=True,
|
||||
name="govoplan.worker.acceptance",
|
||||
max_retries=1,
|
||||
acks_late=True,
|
||||
reject_on_worker_lost=True,
|
||||
track_started=True,
|
||||
)
|
||||
def worker_acceptance_probe(
|
||||
task,
|
||||
probe_id: str,
|
||||
*,
|
||||
mode: str = "complete",
|
||||
delay_seconds: float = 0.0,
|
||||
track_delivery: bool = False,
|
||||
):
|
||||
"""Exercise broker delivery semantics without touching business data."""
|
||||
|
||||
if mode not in {"complete", "retry_once"}:
|
||||
raise ValueError(f"Unsupported worker acceptance mode: {mode}")
|
||||
normalized_probe_id = str(probe_id).strip()
|
||||
if not normalized_probe_id or len(normalized_probe_id) > 120:
|
||||
raise ValueError("Worker acceptance probe_id must contain 1-120 characters")
|
||||
bounded_delay = max(0.0, min(float(delay_seconds), 120.0))
|
||||
delivery_count = _worker_acceptance_delivery_count(
|
||||
task,
|
||||
normalized_probe_id,
|
||||
) if track_delivery else None
|
||||
task.update_state(
|
||||
state="PROGRESS",
|
||||
meta={
|
||||
"phase": "started",
|
||||
"probe_id": normalized_probe_id,
|
||||
"retries": int(task.request.retries or 0),
|
||||
"delivery_count": delivery_count,
|
||||
},
|
||||
)
|
||||
if mode == "retry_once" and int(task.request.retries or 0) == 0:
|
||||
raise task.retry(countdown=0.2)
|
||||
if bounded_delay:
|
||||
time.sleep(bounded_delay)
|
||||
delivery_info = task.request.delivery_info or {}
|
||||
return {
|
||||
"probe_id": normalized_probe_id,
|
||||
"mode": mode,
|
||||
"retries": int(task.request.retries or 0),
|
||||
"redelivered": bool(delivery_info.get("redelivered")),
|
||||
"delivery_count": delivery_count,
|
||||
}
|
||||
|
||||
|
||||
def _worker_acceptance_delivery_count(task, probe_id: str) -> int:
|
||||
client = getattr(task.backend, "client", None)
|
||||
if client is None:
|
||||
raise RuntimeError(
|
||||
"Worker acceptance delivery tracking requires a Redis result backend"
|
||||
)
|
||||
key = f"govoplan:worker-acceptance:{probe_id}"
|
||||
count = int(client.incr(key))
|
||||
client.expire(key, 60 * 60)
|
||||
return count
|
||||
|
||||
|
||||
def _platform_registry() -> PlatformRegistry:
|
||||
raw_enabled_modules = load_startup_enabled_modules(settings.enabled_modules)
|
||||
candidate_modules = startup_candidate_module_ids(
|
||||
|
||||
@@ -115,6 +115,12 @@ class Settings(BaseSettings):
|
||||
migration_track: str = Field(default="release", alias="GOVOPLAN_MIGRATION_TRACK")
|
||||
redis_url: str = Field(default="redis://redis:6379/0", alias="REDIS_URL")
|
||||
celery_enabled: bool = Field(default=False, alias="CELERY_ENABLED")
|
||||
celery_visibility_timeout_seconds: int = Field(
|
||||
default=3600,
|
||||
ge=30,
|
||||
le=7 * 24 * 60 * 60,
|
||||
alias="CELERY_VISIBILITY_TIMEOUT_SECONDS",
|
||||
)
|
||||
|
||||
s3_endpoint_url: str = Field(default="http://garage:3900", alias="S3_ENDPOINT_URL")
|
||||
s3_region: str = Field(default="garage", alias="S3_REGION")
|
||||
|
||||
Reference in New Issue
Block a user