↩
Apache Spark WTF??? ·

Apache Spark WTF??? 🪳Bug Fixion🐛

The project was simple — child’s play, really. But, as these things inevitably go, the client started asking for more. Then a bit more…

4 min read
0% read·4 min in this view

🧮 Just Applying a Formula?

The brief sounded harmless: build a PySpark module that loads millions of rows from an Oracle table, applies a fixed (but, naturally, parameterizable) formula per row, and writes the result to another Oracle table.

A nap-worthy task, right? Yeah… no. A few commits later, the sweet little ETL job was gone — replaced by something darker, heavier, and just a bit mean.

  • Stage 1 — Triage the universe: Pull 12M and 26M-row CSVs from an S3-ish bucket to decide what to process and which formula each row deserves.
  • Stage 2 — IDs from the abyss: Mint a monotonically increasing UUID for each new row, seeded from the previous run’s max.
  • Stage 3 — Surprise, Surprise … more math: Read a tiny Oracle table of formulas.
  • Stage 4 — Key the chaos: Join those three sources to map keys ➜ formulas.
  • Stage 5 — Welcome to heavy metal: Scan 1.7B rows from one Oracle table and 65M from another.
  • Stage 6 — Not in the spec: Perform several joins and aggregations to compute sums.
  • Stage 7 — The Aftermath: Write six output tables — the new max UUID, the two input CSVs, processed results plus their shiny aggregations.

Every “just one more thing” came with its own boss fight. We paused, we plotted, we whispered, “why me?” — as our tiny three-executor Kubernetes cluster begged for mercy.

Roll up your sleeves. Time to bleed a little.

🎒 Preparing for the Long Journey

To experience the suffering safely, we’ll recreate the setup — test environment, fake data, and all. Not for realism, but for scale: millions of CSV rows and something similar to 1.7 billion in Oracle.

🧪 Prepare the Test Dataset

Before we could stress-test our tiny cluster, we needed data — lots of it. Handcrafting millions of rows sounded noble, but also like a one-way ticket to madness. So instead, we reached for TPC-H dbgen, the battle-tested tool for generating scalable synthetic datasets.

We’ll use its modern Rust-based incarnation, tpchgen-cli — fast, simple, and conveniently painful to install on the first try.

🐧 On Linux

# Update and install dependencies  
sudo apt update  
sudo apt install -y build-essential pkg-config libssl-dev cargo  
  
# Install Rust if you don't have it yet  
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh  
source $HOME/.cargo/env  
  
# Verify versions (optional, but reassuring)  
rustc --version  
cargo --version  
  
# (Optional) Set a custom build directory to avoid path issues  
export CARGO_TARGET_DIR=$HOME/.cargo/tmp_build  
  
# Install tpchgen-cli  
cargo install tpchgen-cli  
  
# Generate our synthetic dataset  
tpchgen-cli -s 100 --format parquet --output-dir ./tpch_data

🪟 On Windows (via WSL). If you’re living dangerously and using Ubuntu on WSL, the process is nearly identical — just remember, you’re technically in Linux land.

# Update your package lists  
sudo apt update  
  
# Install the essentials: compiler tools, pkg-config, SSL dev libraries, and Cargo (Rust’s package manager)  
sudo apt install -y build-essential pkg-config libssl-dev cargo  
  
# Install Rust (this script handles the full setup)  
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh  
  
# Reload your environment so Cargo is available immediately  
source $HOME/.cargo/env  
  
# Verify Rust and Cargo were installed correctly  
rustc --version  
cargo --version  
  
# (Optional) Use a temp build directory to avoid permission issues inside WSL  
export CARGO_TARGET_DIR=$HOME/.cargo/tmp_build  
  
# Install tpchgen-cli from crates.io  
cargo install tpchgen-cli  
  
# Generate our synthetic dataset  
tpchgen-cli -s 100 --format parquet --output-dir ./tpch_data

And that’s it — our shiny new +38.6 GB folder of Parquet files is ready for battle.

⚙️ Prepare the Environment

With the data ready, it was time to summon the beast — a local environment that looked just enough like production to hurt. What followed was less of a setup and more of a ritual: Docker, Java, Hadoop, Oracle, S3 emulators, PyCharm tweaks, and enough environment variables to summon Cthulhu.

Below is the condensed chaos — for Windows (via WSL) or Linux users who want to recreate the crime scene.

🐳 1. Spin up Oracle in Docker. Because Spark loves JDBC — and JDBC loves pain — we start with an Oracle container:

docker run --name oracle-test -d -p 1521:1521 -e ORACLE_PASSWORD=test gvenzl/oracle-free

This gives you an Oracle 23c instance running locally, ready for all your testing (and emotional damage).

🩸 When the “E” in ETL Stands for Excruciating Exports

Reading from S3

oye, estaba contestando a uno que ha escrito un artículo en Linkedin diciendo algo que no es cierto ..

Y he caído … oye, ¿después de cargar los csvs, hacíamos repartition, no?

igual tuneando estos parámetros, nos lo podemos ahorrar

Reading from Oracle

yyyy

extraer datos particionados via jdbc no implica que Spark sea consciente de que los datos están particionados por ese campo

https://chatgpt.com/c/69fd9561-3cec-83eb-8442-2529766c2692

😖 When the ‘T’ in ETL stands for Torturous Transformations

uuid

filter

join: broadcast a 3 bandas?

applying the formula

😩 When the “L” in ETL Stands for Lamentable Loads

truncate

🔨 Nailing It In Kubernetes


“La-da-dee-da-da-dee-da
I’m dead serious
Let those peaks blow your mind
La-da-dee-da-da-dee-da
I’m dead serious
The Spark session starts and ends so fast”
(adapted from “Pump it” by Black Eyed Peas)

89Published
44,161Visits
21,693Read
VisitsReadLAST 5 WEEKS
Week ending 16 Sep · WholeStageCodegen · 90 visits · 5 readsWeek ending 9 Sep · 62 visits · 13 readsWeek ending 2 Sep · 433 visits · 75 readsWeek ending 26 Aug · 295 visits · 19 readsWeek ending 19 Aug · 398 visits · 49 readsWeek ending 16 Sep · 5 readsWeek ending 9 Sep · 13 readsWeek ending 2 Sep · 75 readsWeek ending 26 Aug · 19 readsWeek ending 19 Aug · 49 reads