This episode covers integrating Spark with other systems: connectors to Kafka, Cassandra, and Elasticsearch, Spark's role as the ETL engine for data lakes and data warehouses, BI tool integration, and data ingestion and change data capture patterns.

Spark never works alone. In a real data architecture, Spark sits at the center of an ecosystem: reading from Kafka, writing to Cassandra, sending documents to Elasticsearch, and populating data warehouses. Episode 18 covers how Spark connects to these systems.
This integration ability is what makes Spark the "glue of data engineering." An engineer who masters integration can design a complete data flow — from source, through Spark, to consumer — without depending on special tools for every pair of systems.
This episode covers four topics: connectors to Kafka, Cassandra, and Elasticsearch; Spark as an ETL engine for data lakes and warehouses; BI tool integration; and data ingestion and CDC patterns.
Kafka is the most common message queue system in the Spark ecosystem. As a source, Spark reads via Structured Streaming as in episode 10. As a sink, streaming results are sent back to a topic:
from pyspark.sql import functions as F
kafka_in = spark.readStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.option("subscribe", "orders") \
.load()
hasil = kafka_in.selectExpr("CAST(value AS STRING) AS value")
kafka_out = hasil.writeStream \
.format("kafka") \
.option("kafka.bootstrap.servers", "localhost:9092") \
.option("topic", "orders-enriched") \
.option("checkpointLocation", "data/ckpt-kafka") \
.start()selectExpr("CAST(value AS STRING) AS value") ensures the value column is in string format before being written back. Kafka provides an exactly-once guarantee when combined with correct checkpoints.
Cassandra is a distributed wide-column database popular for write-heavy workloads. Spark integrates through the DataStax connector:
df.write \
.format("org.apache.spark.sql.cassandra") \
.option("keyspace", "analitik") \
.option("table", "ringkasan") \
.mode("append") \
.save()df.write.format("org.apache.spark.sql.cassandra") writes a batch to Cassandra. This connector manages token ranges and batch sizes automatically — keep in mind that performance depends heavily on the primary key design and partition count of the Cassandra table.
Elasticsearch is used for text search and dashboards. Spark sends documents through the connector:
df.write \
.format("org.elasticsearch.spark.sql") \
.option("es.nodes", "es-node:9200") \
.option("es.resource", "analitik") \
.mode("overwrite") \
.save()es.resource determines the destination index. For streaming pipelines, the Elasticsearch sink is also supported through the same connector.
The most classic role: Spark pulls data from various sources, cleans and transforms it, then writes it to the data lake in Parquet or Delta format:
source (DB, API, Kafka) → Spark (extract + transform) → data lake (Parquet/Delta)Spark's advantages for ETL: one engine for batch and streaming, connectors to almost every source, and transformations that can be tested repeatedly.
For modern warehouses like Snowflake or BigQuery, Spark writes through official connectors:
df.write \
.format("net.snowflake.spark.snowflake") \
.option("sfUrl", "...") \
.option("sfUser", "...") \
.option("sfDatabase", "dw") \
.option("sfSchema", "public") \
.save()A lighter alternative: Spark writes Parquet to staging in object storage, then the warehouse runs COPY INTO — a pattern that reduces the load on direct connectors and leverages the warehouse's native speed.
BI tools don't read Spark data directly. The common flows:
/opt/spark/sbin/start-thriftserver.shstart-thriftserver.sh starts a JDBC/ODBC endpoint on port 10000 — tools like Tableau or Superset can then connect to it like a regular database.
At scale, BI access should go through a warehouse or open engine, not ad-hoc Spark sessions — so resources stay isolated and no analyst accidentally runs a giant query on the production cluster.
CDC (Change Data Capture) captures database changes (insert, update, delete) and streams them for processing. The common pattern:
MERGE.from pyspark.sql import functions as F
perubahan = spark.readStream.format("kafka").option("subscribe", "db-changes").load()
target = spark.read.format("delta").load("data/target_delta")
def proses_batch(df, epoch_id):
df.createOrReplaceTempView("perubahan")
target.sparkSession.sql("""
MERGE INTO data/target_delta AS t
USING perubahan AS s ON t.id = s.id
WHEN MATCHED AND s.op = 'delete' THEN DELETE
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *
""")
query = perubahan.writeStream.foreachBatch(proses_batch).start()foreachBatch(proses_batch) allows full batch logic (including MERGE) to be applied to each micro-batch — the most flexible pattern for CDC in Spark.
Database changes have an important order. When applying CDC, watch out for:
Info
Understanding how CDC works is a highly valuable skill: most modern industry pipelines are built on database changes flowing into the data lake, not merely re-imported daily snapshots.
Episode 18 equips you with integration skills: the Kafka, Cassandra, and Elasticsearch connectors link Spark to consumer systems, Spark's role as an ETL engine populates data lakes and warehouses, BI tools reach data through a Thrift Server or warehouse, and CDC patterns keep data fresh at the target.
Key takeaways:
In the next episode, episode 19, we'll discuss operational readiness and runbooks — writing runbooks for job failure and recovery, incident response for clusters and data corruption, backing up configuration and artifacts, and chaos testing to verify pipeline resilience.