fix(worker): keep DLQ failures within the canonical taxonomy
This commit is contained in:
@@ -15,7 +15,6 @@ ALLOWED_FAILURE_REASONS = {
|
|||||||
"invalid_epoch",
|
"invalid_epoch",
|
||||||
"malformed_json",
|
"malformed_json",
|
||||||
"normalization_error",
|
"normalization_error",
|
||||||
"ch_insert_failed",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -58,13 +57,21 @@ def route_to_dlq(
|
|||||||
)
|
)
|
||||||
envelope_bytes = json.dumps(envelope, default=str).encode("utf-8")
|
envelope_bytes = json.dumps(envelope, default=str).encode("utf-8")
|
||||||
|
|
||||||
producer.produce(topic=topic, key=raw_id, value=envelope_bytes).result(
|
safe_key = None
|
||||||
|
if raw_id is not None:
|
||||||
|
try:
|
||||||
|
raw_id.encode("utf-8")
|
||||||
|
safe_key = raw_id
|
||||||
|
except UnicodeError:
|
||||||
|
safe_key = None
|
||||||
|
|
||||||
|
producer.produce(topic=topic, key=safe_key, value=envelope_bytes).result(
|
||||||
timeout=config.kafka.delivery_timeout_seconds + 1
|
timeout=config.kafka.delivery_timeout_seconds + 1
|
||||||
)
|
)
|
||||||
worker_metrics.record_dlq(failure_reason, 1)
|
worker_metrics.record_dlq(failure_reason, 1)
|
||||||
|
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Record routed to DLQ: reason={failure_reason}, field={failed_field}",
|
f"Record routed to DLQ: reason={failure_reason}, field={failed_field}",
|
||||||
extra={"raw_id": raw_id, "event": "routed_to_dlq"},
|
extra={"raw_id": safe_key, "event": "routed_to_dlq"},
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -113,6 +113,11 @@ def normalize_cdr_payload(
|
|||||||
return None, "malformed_json", None, None
|
return None, "malformed_json", None, None
|
||||||
raw_id = payload.get("raw_id")
|
raw_id = payload.get("raw_id")
|
||||||
raw_id_str = raw_id if isinstance(raw_id, str) else None
|
raw_id_str = raw_id if isinstance(raw_id, str) else None
|
||||||
|
if raw_id_str is not None:
|
||||||
|
try:
|
||||||
|
raw_id_str.encode("utf-8")
|
||||||
|
except UnicodeError:
|
||||||
|
raw_id_str = None
|
||||||
try:
|
try:
|
||||||
validate_structure(payload)
|
validate_structure(payload)
|
||||||
except PayloadError as exc:
|
except PayloadError as exc:
|
||||||
|
|||||||
@@ -262,10 +262,10 @@ class TestProcessingWorker(unittest.TestCase):
|
|||||||
msg = MockKafkaMessage(json.dumps(payload).encode("utf-8"))
|
msg = MockKafkaMessage(json.dumps(payload).encode("utf-8"))
|
||||||
worker.process_single_message(msg)
|
worker.process_single_message(msg)
|
||||||
|
|
||||||
# Record should have fallen back to DLQ with ch_insert_failed
|
# Record should have fallen back to DLQ with normalization_error
|
||||||
self.assertEqual(len(self.dlq_producer.messages), 1)
|
self.assertEqual(len(self.dlq_producer.messages), 1)
|
||||||
dlq_data = json.loads(self.dlq_producer.messages[0]["value"].decode("utf-8"))
|
dlq_data = json.loads(self.dlq_producer.messages[0]["value"].decode("utf-8"))
|
||||||
self.assertEqual(dlq_data["failure_reason"], "ch_insert_failed")
|
self.assertEqual(dlq_data["failure_reason"], "normalization_error")
|
||||||
|
|
||||||
def test_internal_stats_and_metrics_endpoints(self):
|
def test_internal_stats_and_metrics_endpoints(self):
|
||||||
# Verify /internal/stats
|
# Verify /internal/stats
|
||||||
|
|||||||
Reference in New Issue
Block a user