Real-Time Streaming with Kafka & Flink - Data Engineering Hub

← Back to Hub Real-Time Streaming Pipelines: Kafka, Flink & RisingWave Production patterns for event-driven architectures using Kafka, Flink, and RisingWave. Covers exactly-once semantics, windowing strategies, state management, backpressure handling, and operational best practices. Table of Contents Kafka Foundation Flink Native Streaming Exactly-Once Semantics Windowing Strategies State Management Backpressure Handling RisingWave: Streaming SQL Production Checklist Kafka Foundation Kafka is the backbone. Understanding its guarantees determines your streaming architecture: Topic Design for Streaming # Partition count = max parallelism you'll ever need # Replication factor = 3 for production # Retention: time + size based kafka-topics.sh --create --topic events.raw \ --partitions 100 --replication-factor 3 \ --config retention.ms=604800000 \ --config retention.bytes=107374182400 \ --config cleanup.policy=delete \ --config min.insync.replicas=2 # Compacted topics for current state (CDC, user profiles) kafka-topics.sh --create --topic users.current \ --partitions 50 --replication-factor 3 \ --config cleanup.policy=compact \ --config min.cleanable.dirty.ratio=0.5 Producer Configuration for Exactly-Once props = { 'bootstrap.servers': 'kafka:9092', 'acks': 'all', # Wait for all ISR 'enable.idempotence': True, # Deduplication via PID + sequence 'max.in.flight.requests.per.connection': 5, # Safe with idempotence 'retries': Integer.MAX_VALUE, 'transactional.id': 'etl-pipeline-1', # For transactions 'transaction.timeout.ms': 900000, # 15 min } Flink Native Streaming Flink's continuous dataflow model with event-time processing is the gold standard for stateful streaming: Event Time & Watermarks from pyflink.datastream import StreamExecutionEnvironment from pyflink.datastream.window import TumblingEventTimeWindows from pyflink.datastream.functions import ProcessWindowFunction from pyflink.common import WatermarkStrategy, Duration, Time env = StreamExecutionEnvironment.get_execution_environment() env.set_parallelism(4) # Watermark strategy: bounded out-of-orderness watermark = WatermarkStrategy\ .for_bounded_out_of_orderness(Duration.of_seconds(30))\ .with_timestamp_assigner(lambda event, ts: event.timestamp)\ .with_idleness(Duration.of_minutes(1)) events = env.add_source(kafka_source)\ .assign_timestamps_and_watermarks(watermark) # Windowed aggregation events.key_by(lambda e: e.user_id)\ .window(TumblingEventTimeWindows.of(Time.minutes(5)))\ .process(CountWindowFunction())\ .add_sink(kafka_sink) Process Functions for Complex Logic class SessionDetection(ProcessWindowFunction[Event, Session, String, TimeWindow]): def process(self, key, context, elements, out): events = sorted(elements, key=lambda e: e.timestamp) sessions = [] current = [] gap = 30 * 60 * 1000 # 30 min for e in events: if not current or e.timestamp - current[-1].timestamp <= gap: current.append(e) else: if len(current) >= 3: # Min events per session sessions.append(Session(key, current[0].timestamp, current[-1].timestamp, len(current))) current = [e] if len(current) >= 3: sessions.append(Session(key, current[0].timestamp, current[-1].timestamp, len(current))) for s in sessions: out.collect(s) Exactly-Once Semantics True exactly-once requires coordination across source, processing, and sink: Kafka Source: Consumer Offsets in Checkpoints from pyflink.datastream.connectors.kafka import KafkaSource, KafkaOffsetsInitializer kafka_source = KafkaSource.builder()\ .set_bootstrap_servers("kafka:9092")\ .set_topics("events.raw")\ .set_group_id("flink-consumer")\ .set_starting_offsets(KafkaOffsetsInitializer.committed_offsets())\ .set_value_only_deserializer(JsonDeserializer(Event))\ .build() # Enable checkpointing for exactly-once env.enable_checkpointing(60000) # 60 sec env.get_checkpoint_config().set_min_pause_between_checkpoints(10000) env.get_checkpoint_config().set_checkpoint_timeout(600000) env.get_checkpoint_config().set_tolerable_checkpoint_failure_number(3) env.get_checkpoint_config().set_externalized_checkpoint_cleanup( ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION ) # RocksDB state backend for large state env.set_state_backend(EmbeddedRocksDBStateBackend()) Two-Phase Commit Sink class ExactlyOnceKafkaSink(TwoPhaseCommitSink[KafkaTransaction, Void, Void]): def begin_transaction(self): producer = KafkaProducer(transactional_id=f"flink-sink-{uuid.uuid4()}") producer.init_transactions() producer.begin_transaction() return KafkaTransaction(producer) def invoke(self, transaction, value, context): transaction.producer.send("events.processed", value=value) def commit(self, transaction): transaction.producer.commit_transaction() transaction.producer.close() def abort(self, transaction): transaction.producer.abort_transaction() transaction.producer.close() def recover_and_commit(self, transaction): self.commit(transaction) End-to-End Exactly-Once Checklist ✅ Kafka source: committed offsets in checkpoints ✅ Processing: deterministic functions, no side effects ✅ State: RocksDB backend with incremental checkpoints ✅ Sink: two-phase commit (Kafka transactions, DB XA) ✅ Idempotent writes: deduplication keys in sink Windowing Strategies Tumbling Windows (Fixed, Non-Overlapping) # Fixed 5-minute windows .window(TumblingEventTimeWindows.of(Time.minutes(5))) # Daily windows aligned to midnight UTC .window(TumblingEventTimeWindows.of(Time.days(1), Time.hours(0))) Sliding Windows (Fixed Size, Overlapping) # 10-min window, sliding every 5 min .window(SlidingEventTimeWindows.of(Time.minutes(10), Time.minutes(5))) Session Windows (Activity-Based) # Dynamic windows based on activity gaps .window(EventTimeSessionWindows.with_gap(Time.minutes(30))) Custom Windows with Triggers # Early firing + late data handling .window(GlobalWindows.create())\ .trigger(ContinuousEventTimeTrigger.of(Time.minutes(1)))\ .evictor(TimeEvictor.of(Time.minutes(10)))\ .allowed_lateness(Time.hours(1))\ .side_output_late_data(late_tag) State Management Flink's keyed state is the foundation for stateful streaming: ValueState & ListState class AggregationFunction(KeyedProcessFunction[String, Event, Result]): # Single value per key count_state = ValueStateDescriptor("count", Types.LONG) # List of values per key events_state = ListStateDescriptor("events", Types.STRING) def open(self, parameters): self.count = get_runtime_context().get_state(self.count_state) self.events = get_runtime_context().get_list_state(self.events_state) def process_element(self, value, ctx, out): current = self.count.value() or 0 self.count.update(current + 1) self.events.add(value.payload) if current % 1000 == 0: # Periodic emit out.collect(Result(value.key, current + 1, list(self.events.get()))) MapState for Key-Value per Key # Per-user session data: session_id -> SessionData session_state = MapStateDescriptor("sessions", Types.STRING, Types.PICKLED_BYTE_ARRAY) def update_session(self, user_id, event): sessions = self.runtime_context.get_map_state(self.session_state) session = sessions.get(event.session_id) or SessionData() session.add_event(event) sessions.put(event.session_id, session) # TTL for automatic cleanup ttl_config = StateTtlConfig.new_builder(Time.hours(24))\ .set_update_type(StateTtlConfig.UpdateType.ON_READ_AND_WRITE)\ .build() session_state.enable_time_to_live(ttl_config) State Backend Selection Backend Use Case Checkpoint HashMapStateBackend Small state (< few GB), low latency Full snapshot EmbeddedRocksDBStateBackend Large state (TB), incremental checkpoints Incremental (S3/GCS) Backpressure Handling Flink's Credit-Based Flow Control # Network buffer configuration taskmanager.memory.network.fraction: 0.1 taskmanager.memory.network.min: 64mb taskmanager.memory.network.max: 1gb # Monitor backpressure via REST API # GET /jobs/:jobid/vertices/:vertexid/backpressure # Status: OK / LOW / HIGH # Backpressure detection in code from pyflink.datastream import RuntimeContext ctx = RuntimeContext.get_runtime_context() if ctx.get_metric_group().gauge("backpressure", lambda: "HIGH"): # Scale up or shed load Kafka Consumer Lag Monitoring # Prometheus metrics via JMX exporter kafka.consumer.fetch.manager.records.lag.max kafka.consumer.fetch.manager.records.lead.min # Alert on lag > threshold # groups: # - name: kafka-consumer-lag # rules: # - alert: ConsumerLagHigh # expr: kafka_consumer_lag_max > 100000 # for: 5m RisingWave: Streaming SQL RisingWave brings PostgreSQL-compatible SQL to streaming with materialized views: Source Creation CREATE SOURCE events_raw ( event_id VARCHAR, user_id VARCHAR, event_type VARCHAR, payload JSONB, timestamp TIMESTAMP, WATERMARK FOR timestamp AS timestamp - INTERVAL '30 SECOND' ) WITH ( connector = 'kafka', topic = 'events.raw', properties.bootstrap.server = 'kafka:9092', scan.startup.mode = 'earliest' ) FORMAT PLAIN ENCODE JSON; Materialized Views for Real-Time Aggregations -- 5-min tumbling window aggregation CREATE MATERIALIZED VIEW events_5min AS SELECT user_id, window_start(timestamp, INTERVAL '5 MINUTE') AS window_start, COUNT(*) AS event_count, COUNT(DISTINCT event_type) AS unique_types FROM events_raw GROUP BY user_id, window_start(timestamp, INTERVAL '5 MINUTE'); -- Session window (gap: 30 min) CREATE MATERIALIZED VIEW user_sessions AS SELECT user_id, session_start(timestamp, INTERVAL '30 MINUTE') AS session_start, session_end(timestamp, INTERVAL '30 MINUTE') AS session_end, COUNT(*) AS events_in_session FROM events_raw GROUP BY user_id, session_start(timestamp, INTERVAL '30 MINUTE'); Querying Materialized Views -- Real-time dashboard query (sub-second latency) SELECT * FROM events_5min WHERE window_start > NOW() - INTERVAL '1 HOUR' ORDER BY window_start DESC; -- Join with dimension table (lookup) CREATE SOURCE users_dim ( user_id VARCHAR PRIMARY KEY, name VARCHAR, tier VARCHAR ) WITH ( connector = 'postgres', url = 'jdbc:postgresql://pg:5432/analytics', table.name = 'users' ); SELECT e.window_start, e.user_id, u.name, u.tier, e.event_count FROM events_5min e JOIN users_dim u ON e.user_id = u.user_id WHERE e.window_start > NOW() - INTERVAL '1 HOUR'; Production Checklist Deployment ☐ Flink on Kubernetes (Flink Kubernetes Operator) or YARN ☐ High-availability: JobManager HA (ZooKeeper/Kubernetes) ☐ Checkpoint storage: S3/GCS with versioning ☐ Savepoint strategy: Daily automated savepoints for recovery Monitoring ☐ Flink metrics → Prometheus → Grafana dashboards ☐ Key dashboards: Checkpoint duration/size, backpressure, latency, throughput ☐ Kafka consumer lag alerts ☐ Business metrics: Event counts, error rates, data freshness Operations ☐ Rolling upgrades with savepoints ☐ Schema evolution: Avro/Protobuf with Schema Registry ☐ Dead letter queues for unprocessable events ☐ Capacity planning: State size growth, checkpoint interval vs. recovery time Related Guides Batch Processing at Scale Data Warehouse Architecture Data Quality Contracts Data Observability

Comments

Popular posts from this blog

Firmware Testing Strategies: Hardware-in-the-Loop, Simulation, and CI/CD Pipelines | Embedded Systems Hub

IoT Protocols Compared: MQTT, CoAP, HTTP/2, WebSocket - Performance, Overhead, and Use Cases | Embedded Systems Hub

Embedded Security: Secure Boot, ARM TrustZone, and OTA Update Integrity | Embedded Systems Hub