7.6 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
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 ch_insert_failed 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