Files
telecom-cdr-pipeline/README.md

6.0 KiB

Telecom CDR Pipeline

High-throughput telecom Call Detail Record ingestion and analytics pipeline.

Architecture

The system consists of:

  • FastAPI ingestion API
  • Redpanda as a Kafka-compatible message broker
  • Processing and normalization worker
  • ClickHouse analytical storage
  • Dead Letter Queue for failed records

Current data flow:

Network Element
    ↓
FastAPI API
    ↓
Validation
    ↓
Bounded Async Queue
    ↓
Redpanda / cdr.raw
    ↓
Processing Worker
    ↓
Validation + Normalization
    ├── invalid → cdr.raw.dlq
    └── valid
          ↓
      Micro-Batch
          ↓
      ClickHouse

Infrastructure already available:

Redpanda
├── cdr.raw
└── cdr.raw.dlq

ClickHouse
└── telecom.cdr_records

worker processing is not connected yet.

Current Features

  • Dockerized Redpanda and ClickHouse
  • Automatic and idempotent Kafka topic provisioning
  • Automatic ClickHouse schema initialization
  • Persistent Redpanda and ClickHouse volumes
  • Raw and normalized CDR domain models
  • Required-field and type validation
  • RAT validation and normalization
  • E.164 phone-number normalization
  • Deterministic UUIDv5 cdr_id generation
  • Epoch and traffic-value validation
  • FastAPI POST /api/v1/cdr/raw
  • FastAPI GET /health
  • Structured JSON error responses
  • Unit and API integration tests
  • Bounded asynchronous ingestion queue for backpressure
  • Long-lived asynchronous Kafka producer
  • Kafka delivery acknowledgement before 202 Accepted
  • 429 Too Many Requests on ingestion queue saturation
  • 503 Service Unavailable on Kafka delivery failure
  • Kafka consumer with manual offset management
  • Worker-side JSON decoding and validation
  • Worker-side CDR normalization
  • DLQ routing for malformed and unprocessable Kafka records
  • DLQ records with failure reason, failed field, timestamp, and worker instance
  • Kafka offsets committed only after the current processing outcome succeeds
  • ClickHouse bulk storage through the official Python client
  • Configurable CDR micro-batching
  • Flush on 1,000 records or 2-second timeout
  • Kafka offsets committed only after successful ClickHouse batch writes
  • Remaining worker buffer flushed during graceful shutdown
  • ClickHouse insert retries with exponential backoff
  • Failed ClickHouse batches routed to the DLQ after three attempts
  • Whole-batch offset commits with no partial Kafka acknowledgement
  • Replay-safe storage using deterministic UUIDv5 identifiers and ReplacingMergeTree
  • Graceful worker shutdown flushes buffered records before exit
  • Separate Prometheus /metrics endpoints for the API and worker
  • API accepted-CDR counter
  • Worker processed and DLQ counters
  • Kafka-to-ClickHouse and batch flush latency histograms
  • Batch size and in-flight buffer metrics
  • Kafka consumer lag by partition
  • Aggregated human-readable /stats endpoint

Project Structure

telecom-cdr-pipeline/
├── infrastructure/
│   └── clickhouse/
│       └── init.sql
│
├── scripts/
│   ├── sample-valid-cdr.json
│   └── sample-invalid-rat.json
│
├── src/
│   └── core/
│       ├── __init__.py
│       ├── config.py
│       │
│       ├── api/
│       │   ├── __init__.py
│       │   ├── kafka_publisher.py
│       │   ├── main.py
│       │   ├── metrics.py
│       │   ├── publisher.py
│       │   ├── routes.py
│       │   └── stats.py
│       │
│       ├── domain/
│       │   ├── __init__.py
│       │   ├── errors.py
│       │   ├── models.py
│       │   ├── normalization.py
│       │   └── validation.py
│       │
│       └── worker/
│           ├── __init__.py
│           ├── batch.py
│           ├── clickhouse.py
│           ├── consumer.py
│           ├── dlq.py
│           ├── main.py
│           ├── metrics.py
│           ├── observability.py
│           ├── processor.py
│           └── stats.py
│
├── tests/
│   ├── integration/
│   │   └── test_ingestion_api.py
│   │
│   └── unit/
│       ├── test_batch.py
│       ├── test_dlq.py
│       ├── test_normalization.py
│       ├── test_project_setup.py
│       ├── test_validation.py
│       ├── test_worker_processor.py
│       └── test_worker_stats.py.py
│
├── .env.example
├── .gitignore
├── docker-compose.yml
├── pyproject.toml
└── README.md

Configuration

Configuration is provided through environment variables documented in .env.example.

This includes:

  • Redpanda version and topic settings
  • Kafka broker and consumer configuration
  • ClickHouse connection settings
  • Worker batch size and flush interval

Running Infrastructure

docker compose up -d

Verify services:

docker compose ps -a

Expected:

Redpanda       healthy
redpanda-init  exited successfully
ClickHouse     healthy

Running the API

uvicorn core.api.main:app --reload --host 0.0.0.0 --port 8000

Available endpoints:

API:

GET  /health
GET  /metrics
GET  /stats
POST /api/v1/cdr/raw

Worker:

GET /metrics
GET /internal/stats

A valid CDR currently returns:

{
  "status": "accepted",
  "raw_id": "raw-test-001"
}

Invalid payloads return structured 400 responses.

Testing

Run:

pytest -v

The current test suite covers domain validation, normalization, deterministic ID generation, and API request validation.

Delivery Semantics

The worker uses at-least-once delivery.

Kafka offsets are committed only after a ClickHouse batch is successfully written or, after exhausted retries, the complete failed batch is acknowledged by the DLQ.

Repeated records use deterministic UUIDv5 identifiers and ReplacingMergeTree to support replay-safe storage.