Production-inspired event-driven vector indexing using PostgreSQL CDC, Debezium, Apache Kafka, PySpark Structured Streaming, Ollama, and Qdrant.
Traditional RAG systems typically rely on scheduled ETL jobs to regenerate embeddings, introducing synchronization delays and unnecessary recomputation. This project demonstrates an event-driven alternative where every database mutation is captured directly from PostgreSQL's Write-Ahead Log (WAL) and propagated through a streaming pipeline, enabling near real-time semantic indexing.
- Real-time Change Data Capture (CDC) using PostgreSQL WAL
- Event-driven architecture with Debezium and Apache Kafka
- Continuous processing using PySpark Structured Streaming
- Local embedding generation with Ollama (
nomic-embed-text) - Automatic synchronization with Qdrant Vector Database
- Fully local deployment optimized for Apple Silicon
- No external LLM or embedding APIs required
Note: Architecture diagram can be added here.
PostgreSQL
β
Logical Replication (WAL)
β
βΌ
Debezium CDC
β
βΌ
Apache Kafka
β
βΌ
PySpark Structured Streaming
β
Parse β Embed β foreachBatch
β
βΌ
Ollama (nomic-embed-text)
β
βΌ
Qdrant
β
βΌ
Semantic Search
Configured with:
wal_level = logical
Acts as the transactional source of truth.
Continuously monitors PostgreSQL's Write-Ahead Log (WAL) and converts row-level database mutations into Kafka events without polling the database.
Acts as the immutable messaging layer that decouples producers from downstream consumers.
Consumes CDC events from Kafka and performs:
- JSON parsing
- Metadata removal
- Payload extraction
- Micro-batch processing
- Embedding generation
- Vector upsert into Qdrant
Runs the lightweight nomic-embed-text embedding model locally to generate 768-dimensional embeddings without relying on external APIs.
Stores vector embeddings and enables low-latency semantic retrieval.
.
βββ main/
β βββ spark_rag_consumer.py # Spark streaming job and Qdrant sink
β
βββ search/
β βββ search.py # Semantic search CLI
β
βββ docker-compose.yml # PostgreSQL, Kafka, Debezium, Qdrant
βββ README.md
βββ .gitignore
INSERT INTO PostgreSQL
β
βΌ
Write-Ahead Log (WAL)
β
βΌ
Debezium CDC
β
βΌ
Kafka Topic
β
βΌ
Spark Structured Streaming
β
βΌ
Generate Embeddings
β
βΌ
Qdrant
β
βΌ
Semantic Search
Instead of periodically polling PostgreSQL using queries like:
SELECT *
FROM articles
WHERE updated_at > ...Debezium reads directly from PostgreSQL's Write-Ahead Log (WAL), enabling near real-time synchronization.
Benefits
- Lower database load
- Low-latency event propagation
- Event-driven synchronization
- Automatic capture of every database mutation
Kafka serves as the event backbone of the architecture, decoupling producers and consumers while allowing additional downstream systems to subscribe without impacting the source database.
Although this workload could be processed by a lightweight Kafka consumer, Spark Structured Streaming was intentionally chosen to demonstrate production-grade streaming patterns such as:
- Fault tolerance
- Checkpointing
- Micro-batch execution
- Horizontal scalability
The architecture can scale to higher event throughput with minimal code changes.
Embedding generation runs entirely on the local machine.
Benefits
- No API cost
- Low latency
- Offline execution
- Improved data privacy
Embedding generation is executed inside the foreachBatch sink instead of a Spark UDF. This avoids Python serialization (cloudpickle) issues commonly encountered when network-heavy dependencies such as requests are executed inside distributed Spark workers.
The Spark application is intentionally configured with:
Driver Memory : 1 GB
Executor Memory : 1 GB
allowing the complete infrastructure to run comfortably on an Apple Silicon laptop with 8 GB unified memory.
- macOS (Apple Silicon recommended)
- Docker Desktop
- Python 3.10+
- OpenJDK 17
- Ollama
docker compose up -dollama pull nomic-embed-textpython3 -m venv venv
source venv/bin/activate
pip install pyspark==3.5.1 requests qdrant-clientexport _JAVA_OPTIONS="-Djava.net.preferIPv4Stack=true"
export SPARK_LOCAL_IP=127.0.0.1python main/spark_rag_consumer.pyInsert a record into PostgreSQL.
INSERT INTO articles (title, content)
VALUES (
'Event Streaming',
'Kafka processes data streams in real-time with low latency.'
);The record flows through:
PostgreSQL
β
Debezium
β
Kafka
β
Spark Structured Streaming
β
Ollama
β
Qdrant
β
Semantic Search
Run the semantic search client.
python search/search.pyThe streaming consumer initializes the Spark session, consumes CDC events from Kafka, generates embeddings using Ollama, and continuously pushes vectors into Qdrant through Spark micro-batches.
26/07/19 14:19:19 WARN NativeCodeLoader:
Unable to load native-hadoop library for your platform...
using builtin-java classes where applicable.
Setting default log level to "WARN".
26/07/19 14:19:21 WARN ResolveWriteToStream:
Temporary checkpoint location created.
26/07/19 14:19:21 WARN ResolveWriteToStream:
spark.sql.adaptive.enabled is not supported in
streaming DataFrames/Datasets and will be disabled.
π Batch 1: Embedded & pushed 1 vectors to Qdrant!
π Batch 2: Embedded & pushed 4 vectors to Qdrant!
The logs confirm that Spark Structured Streaming successfully consumes Kafka events, generates embeddings locally, and incrementally synchronizes vectors into Qdrant.
Run the following command after inserting records into PostgreSQL:
python search/search.pySearch Output
π Searching for: 'What are the benefits of real-time data streaming over batch?'
π― Match Score: 0.6827
π Title: AI in Data Engineering
π Content: Streaming CDC architectures reduce latency compared to batch ETL pipelines.
--------------------------------------------------
π― Match Score: 0.6200
π Title: The Evolution of CDC
π Content: Change Data Capture ensures that downstream systems stay in perfect sync with the primary database by streaming transaction logs.
--------------------------------------------------
This verifies that newly inserted PostgreSQL records become searchable shortly after flowing through the CDC β Kafka β Spark β Ollama β Qdrant pipeline.
- Support UPDATE and DELETE events
- Batch embedding generation
- Metadata filtering
- Hybrid keyword + vector search
- FastAPI search service
- Kubernetes deployment
- Monitoring with Prometheus and Grafana
Hritik Maheshwari
Data Engineer | Streaming Systems | Distributed Data Platforms | AI Infrastructure