Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Kafka Data Integration

Patterns for integrating Apache Kafka with data systems across the enterprise.


ApproachDescriptionUse Case
Kafka ConnectDeclarative connectorsStandard integrations
Custom Producers/ConsumersApplication codeComplex logic
Kafka StreamsStream processingTransformations

Traditional ETL with transformation before loadingTraditional ETL with transformation before loadingSourceETL ToolData WarehouseTransform before loadingBatch processingTool-specificExtractTransformLoad
Streaming ETL using Kafka Connect and Kafka StreamsStreaming ETL using Kafka Connect and Kafka StreamsSourcesKafkaApplication LogsREST APIsRaw EventsTransformedKafka StreamsCassandraTransform in motionReal-time processingScalableKafka ConnectconsumeproduceKafka Connect
ELT with raw data loaded to a data lake before transformationELT with raw data loaded to a data lake before transformationSourcesKafkaData Lake(S3)Transform Engine(Spark/Trino)Data WarehouseStore raw data firstTransform on demandSchema-on-readExtractLoad (raw)TransformLoad (modeled)

Streaming pipeline from event sources to data sinksStreaming pipeline from event sources to data sinksEvent SourcesIngestion LayerKafka ClusterProcessing LayerServing LayerData SinksApp LogsAPIsIoTKafka ConnectSource Connectorsraw-eventsenriched-eventsaggregated-eventsKafka StreamsKafka ConnectSink ConnectorsCassandraS3Elasticsearch

One source feeds multiple sinks for different use cases.

Fan-out from one topic to multiple sink connectorsFan-out from one topic to multiple sink connectorsKafka Connect SinksS3 Sink(archive)Cassandra Sink(operational)ES Sink(search)eventsSame events servemultiple purposes

Multiple sources aggregate into unified topics.

Fan-in from regional sources into a global topicFan-in from regional sources into a global topicRegional SourcesUS EventsEU EventsAPAC Eventsglobal-eventsUnified view ofall regional data

Enrich events with reference data.

Enrichment by joining order events with customer reference dataEnrichment by joining order events with customer reference dataraw-orderscustomer-tableKafka StreamsJoinenriched-ordersJoin order events withcustomer reference dataKTable

PatternDescriptionExample
PollingPeriodically fetch dataHTTP Source
PushReceive pushed eventsMQTT Source
Log tailingStream log filesFileStream Source
Message bridgeBridge message systemsJMS Source
PatternDescriptionExample
UpsertInsert or update recordsCassandra Sink
AppendAppend-only writesS3 Sink
IndexUpdate search indexElasticsearch Sink
Time-partitionedPartition by timeS3 Sink with partitioner
Exactly-once sink using idempotent upserts into CassandraExactly-once sink using idempotent upserts into CassandraeventsCassandra Sink(idempotent)CassandraIdempotent writes:Same key = same resultregardless of retriesconsumeupsert by key

{
"name": "s3-sink",
"config": {
"connector.class": "io.confluent.connect.s3.S3SinkConnector",
"tasks.max": "3",
"topics": "events",
"s3.bucket.name": "data-lake",
"s3.region": "us-east-1",
"storage.class": "io.confluent.connect.s3.storage.S3Storage",
"format.class": "io.confluent.connect.s3.format.parquet.ParquetFormat",
"partitioner.class": "io.confluent.connect.storage.partitioner.TimeBasedPartitioner",
"partition.duration.ms": "3600000",
"path.format": "'year'=YYYY/'month'=MM/'day'=dd/'hour'=HH",
"rotate.interval.ms": "600000",
"flush.size": "10000"
}
}
s3://data-lake/topics/events/
year=2024/
month=01/
day=15/
hour=10/
events+0+0000000000.parquet
events+1+0000000000.parquet
hour=11/
events+0+0000000100.parquet

{
"name": "cassandra-sink",
"config": {
"connector.class": "com.datastax.oss.kafka.sink.CassandraSinkConnector",
"tasks.max": "3",
"topics": "events",
"contactPoints": "cassandra1,cassandra2,cassandra3",
"loadBalancing.localDc": "datacenter1",
"port": "9042",
"topic.events.keyspace.table.mapping": "analytics.events_by_time",
"topic.events.keyspace.table.consistencyLevel": "LOCAL_QUORUM",
"topic.events.keyspace.table.ttlTimeUnit": "DAYS",
"topic.events.keyspace.table.ttl": "365"
}
}
CREATE TABLE analytics.events_by_time (
event_date date,
event_time timestamp,
event_id uuid,
event_type text,
payload text,
PRIMARY KEY ((event_date), event_time, event_id)
) WITH CLUSTERING ORDER BY (event_time DESC);

PracticeRationale
Use Schema RegistryEnsure compatibility across pipeline
Prefer Avro or ProtobufCompact, schema evolution support
Version schemas explicitlyTrack changes, enable rollback
StrategyImplementation
Dead letter queueRoute failed records for investigation
Error toleranceConfigure errors.tolerance=all
MonitoringAlert on DLQ growth
OptimizationConfiguration
BatchingIncrease batch.size, linger.ms
CompressionUse compression.type=lz4
ParallelismIncrease tasks.max
PartitioningAlign with downstream partitioning