7.9 KiB
Purpose
Defines stream consumption, safe parsing, schema normalization, micro-batching into ClickHouse, and at-least-once offset management for raw telecom CDRs.
ADDED Requirements
Requirement: Safe Parsing and Fault Tolerance
The Processing Worker SHALL consume messages from cdr.raw and process them without crashing, routing malformed or unprocessable payloads to cdr.raw.dlq.
Scenario: Malformed or unparseable JSON payload
- WHEN a message consumed from
cdr.rawcannot be parsed as valid JSON - THEN the worker wraps the raw message in a DLQ envelope with
failure_reason: "malformed_json"and publishes it tocdr.raw.dlqwithout terminating the process
Scenario: Missing required field
- WHEN a consumed record is missing any required field (
raw_id,device_id,caller_num,svc,rat,epoch_start,epoch_end,up_bytes,down_bytes,lac,ci) or contains null for a required field - THEN the worker routes the record to
cdr.raw.dlqwithfailure_reason: "missing_required_field"andfailed_fieldset to the missing field name
Scenario: Inverted epoch timestamps
- WHEN a consumed record has
epoch_start > epoch_end - THEN the worker routes the record to
cdr.raw.dlqwithfailure_reason: "invalid_epoch"andfailed_field: "epoch_start"
Scenario: Negative byte counts
- WHEN a consumed record has
up_bytes < 0ordown_bytes < 0 - THEN the worker routes the record to
cdr.raw.dlqwithfailure_reason: "normalization_error"andfailed_fieldset to the negative byte field
Requirement: E.164 Phone Number Normalization
The Processing Worker SHALL normalize caller_num into a valid E.164 format msisdn using the phonenumbers library.
Scenario: Leading international prefix normalization
- WHEN a valid record contains
caller_numstarting with00or011 - THEN the worker replaces the prefix with
+, parses the number usingphonenumbers, and setsmsisdnto the standardized E.164 string
Scenario: Existing E.164 number preserved
- WHEN a record contains
caller_numalready formatted with a leading+ - THEN the worker parses and validates the number using
phonenumbersand outputs the verified E.164 string
Scenario: Unparseable phone number routed to DLQ
- WHEN
caller_numcannot be normalized or validated byphonenumbers - THEN the worker routes the record to
cdr.raw.dlqwithfailure_reason: "normalization_error"andfailed_field: "caller_num"
Requirement: Radio Access Type Mapping
The Processing Worker SHALL map the integer rat field to standard string representations.
Scenario: Supported RAT values mapped
- WHEN a record has
ratequal to1,6, or9 - THEN the worker maps
radio_access_typeto"3G","LTE_4G", or"5G_NR"respectively
Scenario: Unsupported RAT value routed to DLQ
- WHEN a record has
ratnot equal to 1, 6, or 9 - THEN the worker routes the record to
cdr.raw.dlqwithfailure_reason: "invalid_rat_value"andfailed_field: "rat"
Requirement: Schema Transformation and Deterministic Identity
The Processing Worker SHALL transform valid raw CDRs into the normalized schema with deterministic identity derivation.
Scenario: Deterministic UUIDv5 cdr_id generation
- WHEN a raw record with
raw_idis normalized - THEN the worker computes
cdr_idasuuid.uuid5(uuid.NAMESPACE_DNS, raw_id)ensuring identical IDs for reprocessed duplicate records
Scenario: Computed metrics and cell identification
- WHEN raw fields are mapped to the normalized schema
- THEN
cell_idis set tolac + "_" + ci,total_bytesis computed asup_bytes + down_bytes,duration_secondsis computed asepoch_end - epoch_start,session_start_timeandsession_end_timeare converted to UTC DateTimes, andsource_ipdefaults to"0.0.0.0"if absent
Requirement: Micro-Batching Buffer Strategy
The Processing Worker SHALL buffer normalized records in memory and flush batches to ClickHouse based on size or elapsed time thresholds.
Scenario: Batch flushed on count threshold
- WHEN the in-memory normalized record buffer reaches
BATCH_SIZE(default 1,000 records) - THEN the worker immediately flushes the batch to ClickHouse
Scenario: Batch flushed on time interval threshold
- WHEN
BATCH_FLUSH_INTERVAL_SECONDS(default 2 seconds) elapses since the last flush and the buffer contains at least one record - THEN the worker flushes the accumulated buffer to ClickHouse
Requirement: At-Least-Once Delivery and Offset Management
The Processing Worker SHALL commit Kafka consumer offsets only after ClickHouse successfully acknowledges the batch write.
Scenario: Offsets committed after successful write
- WHEN a micro-batch is written and acknowledged by ClickHouse
- THEN the worker commits the highest offsets corresponding to the records in that batch
Scenario: ClickHouse write timeout retry with exponential backoff
- WHEN a batch write to ClickHouse fails or times out
- THEN the worker retries the write up to
CH_RETRY_MAX_ATTEMPTS(default 3) with exponential backoff (CH_RETRY_INITIAL_DELAY_MSmultiplied byCH_RETRY_BACKOFF_FACTOR); if all attempts fail, the worker routes all records in the batch tocdr.raw.dlqand commits offsets to avoid pipeline deadlock
Requirement: Internal Runtime Statistics Endpoint
The Processing Worker SHALL expose a lightweight internal HTTP endpoint (e.g., GET /internal/stats) to provide its runtime statistics to the Ingestion API.
Scenario: Internal stats queried
- WHEN an HTTP request is made to the worker's internal stats endpoint
- THEN the worker returns HTTP status
200 OKwith JSON containingtotal_processed,total_dlq,in_flight_buffer,last_flush_at, and breakdown counts by service type, RAT, and DLQ reason
Requirement: Graceful Shutdown Without Data Loss
The Processing Worker SHALL handle SIGTERM and SIGINT signals to flush buffered records before terminating.
Scenario: SIGTERM received during operation
- WHEN a
SIGTERMsignal is received by the worker process - THEN the worker stops consuming new messages, flushes any remaining in-memory buffered records to ClickHouse, commits the associated Kafka offsets, and exits cleanly with exit code 0
Requirement: Safe partition progress and confirmed DLQ
The worker SHALL disable automatic offset storage, commit explicit next offsets, and await all destination acknowledgements.
Scenario: Valid record precedes an invalid record
- WHEN an invalid record follows buffered valid records
- THEN flush and commit those valid records before acknowledging and committing the invalid record to DLQ
- THEN route the invalid record to DLQ and defer committing its offset until buffered valid records are flushed, or commit immediately if no preceding uncommitted records exist on that partition
Scenario: Destination or commit failure
- WHEN DLQ delivery or any partition commit fails
- THEN stop nonzero without advancing unsafe offsets or reclassifying the error as a ClickHouse failure; fatal cleanup does not flush the buffer
Scenario: Exhausted ClickHouse retries
- WHEN three configured insert attempts fail
- THEN publish original vendor payloads with
normalization_errorand commit only after all DLQ deliveries succeed - THEN publish original vendor payloads with normalization_error and commit only after all DLQ deliveries succeed
Requirement: Storage-safe normalization
The worker SHALL reject numeric booleans, fractional numeric fields, out-of-range UInt64 and UInt32 values including total bytes, invalid IPv4, and timestamps outside DateTime range without crashing.
Scenario: Invalid scalar or conversion
- WHEN any field fails its type, range or conversion check
- THEN route only that record to DLQ with a field-specific reason and preserve other valid records