CASE STUDIES LIST/ HOME
#01 / 25Case #02 →
CASE STUDY #01Phase 1: Foundations & IngestionCLASSIFICATION: ACTUAL

Why Moving Data From A to B Was the Easy Part

Designing DataPulse: From Monolithic Application DB to Data Reliability Platform

PostgreSQLPythonAzure ADLS Gen2Apache SparkSnowflake

1. The Problem

Running heavy multi-join analytical aggregations directly against the production PostgreSQL primary database caused severe CPU contention and API latency spikes during peak user traffic.

2. What I Initially Thought

"I initially assumed PostgreSQL was slow simply because I lacked indexes or hardware memory. But viewing EXPLAIN ANALYZE execution plans showed scanning wide tables for aggregates forced reading unneeded row data off disk, evicting transactional pages from buffer cache."

3. What I Learned

Operational DBs store data in row-oriented Heap Pages (optimal for single-row updates). Analytical queries aggregate specific columns across millions of rows (requiring columnar storage and decoupled processing layers).

OLTP vs OLAP WorkloadsMedallion Data Lakehouse ArchitectureResource Lock ContentionStorage Engine Decoupling

4. What I Built

Decoupled Medallion architecture for DataPulse: App DB → Python Batch Extractor → ADLS Gen2 Bronze Raw → PySpark Silver/Gold Delta Tables → Snowflake Analytical Serving.

def extract_incremental_batch(connection, last_watermark, batch_size=10000):
    query = """
        SELECT id, tenant_id, title, status, created_at, updated_at
        FROM tasks WHERE updated_at > %s ORDER BY updated_at ASC
    """
    with connection.cursor(name="batch_extract_cursor") as cursor:
        cursor.itersize = batch_size
        cursor.execute(query, (last_watermark,))
        while rows := cursor.fetchmany(batch_size):
            yield rows

5. The Experiment

BEFORE

Heavy 30-day rolling analytical aggregation query running directly on production PostgreSQL primary database during write traffic.

CHANGE APPLIED

Offloaded analytical queries by implementing asynchronous batch extraction to ADLS Gen2 staging and Spark Gold aggregations.

AFTER RESULT

Operational DB transactional write latency restored to baseline; analytical query scans isolated from application DB locks.

6. What Went Wrong

Initial extraction script ran full SELECT * FROM tasks every 10 minutes without watermark filtering, causing storage ballooning and linear extraction time growth.

7. Engineering Decision & Trade-offs

Chose asynchronous batch extraction to ADLS Gen2 and Spark instead of read-replicas because read-replicas still enforce row-oriented storage and do not scale for complex multi-year analytics.

8. What I Would Do Differently in Production

In a high-throughput production environment, implement Change Data Capture (CDC) using Debezium and Kafka to stream PostgreSQL write-ahead logs (WAL) without querying DB tables.

Questions I Can Now Answer Confidently in an Interview:

  • Why should you separate transactional (OLTP) and analytical (OLAP) workloads?
  • What is Medallion Architecture and what role does each layer (Bronze, Silver, Gold) serve?
  • How do row-oriented database storage engines differ from columnar storage engines during query execution?
  • What are the trade-offs between batch database extraction and Change Data Capture (CDC)?

Expected / Verified Evidence

•PostgreSQL EXPLAIN ANALYZE execution plan text logs
•sql/init_raw.sql operational schema
•System architecture diagram of DataPulse platform
•Measured query execution benchmark comparison
BACK TO ALL CASE STUDIESNEXT: CASE #02 (Designing the Database That Watches Other Databases)