<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Data Engineering ]]></title><description><![CDATA[Data Engineering ]]></description><link>https://thedatapipeplumber.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 28 Oct 2026 17:15:44 GMT</lastBuildDate><atom:link href="https://thedatapipeplumber.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Fire Up Your Data with Iceberg]]></title><description><![CDATA[Picture your data lake as a giant warehouse full of boxes (files). Everything's stored, but nobody really knows what's in which box, who's moving them, or…]]></description><link>https://thedatapipeplumber.hashnode.dev/fire-up-your-data-with-iceberg</link><guid isPermaLink="true">https://thedatapipeplumber.hashnode.dev/fire-up-your-data-with-iceberg</guid><category><![CDATA[iceberg]]></category><category><![CDATA[data]]></category><category><![CDATA[dataengineering]]></category><dc:creator><![CDATA[nadata]]></dc:creator><pubDate>Tue, 13 Oct 2026 02:19:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a69af954ee80916d8f885ad/aa0a26c4-aad6-4b50-82fe-a443a2e63ef5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Picture your data lake as a giant warehouse full of boxes (files). Everything's stored, but nobody really knows what's in which box, who's moving them, or what the warehouse looked like last Tuesday.</p>
<p><strong>Apache Iceberg</strong> is the clipboard-wielding manager that walks in and says: "Right. Let's make this place behave like an actual database."</p>
<p>It's an open table format that sits on top of your files (Parquet, ORC, etc.) and adds a smart metadata layer. That metadata is where all the magic happens:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a69af954ee80916d8f885ad/406b8d21-a960-4499-840f-331c93d99738.png" alt="" style="display:block;margin:0 auto" />

<p>Let's tour the highlights, using a food delivery app's <code>orders</code> table as our example. 🍜🛵</p>
<hr />
<h2>1. ACID Transactions: All or Nothing 💥</h2>
<p>The order service and the payment service both write to the <code>orders</code> table at the same time. In the old data lake world, that could mean corrupted or lost data.</p>
<p>Iceberg uses <strong>optimistic concurrency</strong>. It assumes writers won't clash, lets them work freely, and only checks for conflicts at commit time. The <strong>catalog</strong> acts as the referee:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a69af954ee80916d8f885ad/7272ecf2-8e3c-4542-aa4e-044c0d6ead1e.png" alt="" style="display:block;margin:0 auto" />

<p>Each write either fully commits or fully fails. No half-saved orders, no missing nasi lemak.</p>
<hr />
<h2>2. Row-Level Updates: Rewrite or Leave a Note? ✍️</h2>
<p>Files in a data lake are <strong>immutable</strong>: you can't edit them in place. So what happens when a customer cancels order <code>1234</code>?</p>
<p>Iceberg gives you two strategies:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a69af954ee80916d8f885ad/686aa2c3-183e-4fd4-bab9-104dd95c44e2.png" alt="" style="display:block;margin:0 auto" />

<table>
<thead>
<tr>
<th>Strategy</th>
<th>What happens</th>
<th>Best for</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Copy-on-Write (COW)</strong></td>
<td>Rewrite the whole file without that row</td>
<td>Read-heavy tables</td>
</tr>
<tr>
<td><strong>Merge-on-Read (MOR)</strong></td>
<td>Keep the file, write a small <strong>delete file</strong> saying "ignore this row"</td>
<td>Write-heavy / streaming tables</td>
</tr>
</tbody></table>
<p>With MOR, the "ignore this" note comes in two flavours:</p>
<ul>
<li><p><strong>Positional delete</strong>: "Skip row #234 in file_2." Precise and fast to read.</p>
</li>
<li><p><strong>Equality delete</strong>: "Skip any row where <code>order_id = 1234</code>." Super cheap to write, which makes it great for streaming.</p>
</li>
</ul>
<p><strong>Plot twist:</strong> What if the customer changes their mind and order <code>1234</code> gets placed again? Won't the old delete note hide it too?</p>
<p>Nope. Iceberg stamps every change with a <strong>sequence number</strong>, and a delete note only applies to data that came <em>before</em> it:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a69af954ee80916d8f885ad/3b94c099-94b4-4cc0-8ddd-d7a93c810830.png" alt="" style="display:block;margin:0 auto" />

<p>The cancelled order stays gone and the new one shows up. 🕰️</p>
<hr />
<h2>3. Partition Evolution: Change Your Mind for Free 🔄</h2>
<p>You partitioned orders by <strong>month</strong> back when the app had a few hundred users. Now you're doing thousands of orders an hour and want <strong>daily</strong> partitions.</p>
<p>In the old world, that meant rewriting the entire table. 😱 With Iceberg, you just update the partition spec:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a69af954ee80916d8f885ad/c4d0809a-ecf9-4b85-a5d4-e946fdb20c4e.png" alt="" style="display:block;margin:0 auto" />

<p>Old data stays where it is, new data lands in daily partitions, and queries handle both automatically. It's a metadata change, so it's quick and cheap.</p>
<hr />
<h2>4. Hidden Partitioning: Stop Thinking About Folders 🙈</h2>
<p>Old-school tables often had extra columns like <code>order_year</code>, <code>order_month</code>, <code>order_day</code> just for partitioning. If an analyst filtered on <code>order_timestamp</code> instead, the result was a <strong>full table scan</strong>. Ouch.</p>
<p>Iceberg partitions using <strong>transforms</strong> like <code>day(order_timestamp)</code>, <code>month()</code>, <code>bucket()</code>, or <code>truncate()</code>. You filter on the column you actually care about, and Iceberg handles pruning behind the scenes:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a69af954ee80916d8f885ad/0a23c7c8-14fa-42f7-b8c0-b4dd9ecdf2fa.png" alt="" style="display:block;margin:0 auto" />

<pre><code class="language-sql">-- Just write the natural query. Iceberg does the rest.
SELECT * FROM orders
WHERE order_timestamp &gt;= '2026-10-01';
</code></pre>
<hr />
<h2>5. Time Travel &amp; Rollback: Query the Past, Undo Mistakes ⏳⏪</h2>
<p>Every change creates an <strong>immutable snapshot</strong>, so the table keeps its own history:</p>
<p><strong>Time travel:</strong> Want to see exactly what orders looked like at the end of last month? Query that snapshot directly. You don't need to copy the table first.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a69af954ee80916d8f885ad/90846a2f-5d2f-41a3-b1f0-c998e5eea18d.png" alt="" style="display:block;margin:0 auto" />

<pre><code class="language-sql">SELECT * FROM orders FOR TIMESTAMP AS OF '2026-09-30 23:59:59';
</code></pre>
<p>(Exact syntax varies a little by engine.)</p>
<p><strong>Rollback:</strong> A buggy job loaded every order twice and now the dashboard says you sold 10,000 bubble teas before lunch? Roll the table back to the previous snapshot. Mistake undone. 🧋</p>
<hr />
<h2>6. Schema Evolution: Tables Grow Up 🌱</h2>
<p>Add a <code>delivery_rider_id</code> column, rename <code>addr</code> to <code>delivery_address</code>, or widen types. For example, when <code>order_id</code> outgrows an <code>int</code> because the app got popular, you can promote it to a <code>long</code> without rewriting your data.</p>
<hr />
<h2>TL;DR 🧊</h2>
<p>Apache Iceberg turns a messy pile of files into a proper table with:</p>
<ul>
<li><p>Safe concurrent writes (ACID)</p>
</li>
<li><p>Flexible updates and deletes (COW / MOR)</p>
</li>
<li><p>Painless partition changes</p>
</li>
<li><p>Queries that don't need partition trivia</p>
</li>
<li><p>Time travel and rollback</p>
</li>
<li><p>Schema changes without drama</p>
</li>
</ul>
<p>Your data lake isn't sinking anymore. It's floating. 🚢</p>
<p><em>Next up: I'll dig into how Iceberg's metadata layers actually work under the hood. Stay tuned!</em></p>
]]></content:encoded></item><item><title><![CDATA[Pandas Ran Out of Memory? Meet Dask 🐼  --> 🚀]]></title><description><![CDATA[Most data engineers today are best friends with Pandas, and many have started flirting with Polars. But there's a quiet, underrated library sitting in the…]]></description><link>https://thedatapipeplumber.hashnode.dev/pandas-ran-out-of-memory-meet-dask</link><guid isPermaLink="true">https://thedatapipeplumber.hashnode.dev/pandas-ran-out-of-memory-meet-dask</guid><dc:creator><![CDATA[nadata]]></dc:creator><pubDate>Wed, 05 Aug 2026 14:42:26 GMT</pubDate><content:encoded><![CDATA[<p>Most data engineers today are best friends with <strong>Pandas</strong>, and many have started flirting with <strong>Polars</strong>. But there's a quiet, underrated library sitting in the corner that most teams overlook: <strong>Dask</strong>.</p>
<p>Here's the problem it solves. Your pipeline is happily crunching food delivery orders… until one day the dataset grows bigger than your machine's memory. 💥</p>
<pre><code>MemoryError: Unable to allocate 48.2 GiB for an array
</code></pre>
<p>The usual answer? "Just use Spark." And Spark <em>is</em> great at big data. But it comes with baggage:</p>
<ul>
<li>🧠 You need a specialised skillset to tune it properly</li>
<li>☕ It runs on the JVM, which adds cluster overhead</li>
<li>🐍 Python users end up translating between two worlds</li>
</ul>
<p>What if you could keep writing Pandas-style code and just… make it bigger?</p>
<p>That's Dask.</p>
<hr />
<h2>So What Is Dask? 🤔</h2>
<p>Dask is a Python library for <strong>parallel and distributed computing</strong>. Instead of asking you to learn a new ecosystem, it scales the tools you already know: Pandas, NumPy, and plain Python.</p>
<p>The trick is simple. A Dask DataFrame is really <strong>many small Pandas DataFrames</strong> (called <em>partitions</em>) working together as a team.</p>
<table>
<thead>
<tr>
<th></th>
<th>Pandas</th>
<th>Dask</th>
</tr>
</thead>
<tbody><tr>
<td>Data size</td>
<td>Must fit in memory</td>
<td>Can be bigger than memory</td>
</tr>
<tr>
<td>Runs on</td>
<td>One CPU core</td>
<td>Many cores or many machines</td>
</tr>
<tr>
<td>Execution</td>
<td>Runs immediately</td>
<td>Lazy: plans first, runs later</td>
</tr>
<tr>
<td>API</td>
<td>Pandas</td>
<td>Almost the same as Pandas 😎</td>
</tr>
</tbody></table>
<p>Think of it like a delivery company. Pandas is one rider carrying every order. Dask is a fleet of riders, each taking a few orders at the same time. 🛵🛵🛵</p>
<hr />
<h2>Show Me the Code 👩‍💻</h2>
<p><strong>Pandas</strong>: works great… until it doesn't.</p>
<pre><code class="language-python">import pandas as pd

df = pd.read_parquet("orders/")
revenue = df.groupby("city")["amount"].sum()
</code></pre>
<p><strong>Dask</strong>: almost identical, but scales.</p>
<pre><code class="language-python">import dask.dataframe as dd

df = dd.read_parquet("orders/*.parquet")
revenue = df.groupby("city")["amount"].sum()

revenue.compute()  # 👈 this is where the magic happens
</code></pre>
<p>Spot the difference? Just the import and one <code>.compute()</code>. That's it.</p>
<hr />
<h2>Wait, What's <code>.compute()</code>? 💤➡️⚡</h2>
<p>Dask is <strong>lazy</strong>, in a good way.</p>
<p>When you write <code>df.groupby(...)</code>, Dask doesn't actually do anything yet. It just writes down a plan, called a <strong>task graph</strong>: "read these files, group them, sum them up."</p>
<p>Only when you call <code>.compute()</code> does Dask:</p>
<ol>
<li>🗺️ Look at the whole plan</li>
<li>🧩 Split the work into small tasks</li>
<li>⚡ Run independent tasks in parallel</li>
<li>🧺 Process data chunk by chunk, so it never loads everything into memory at once</li>
</ol>
<p>It's like writing your whole grocery list before going to the shop, instead of driving back and forth for every item.</p>
<hr />
<h2>It Plays Nicely With Your Stack ☁️</h2>
<p>Because Dask is pure Python, it slots right into a modern data platform:</p>
<ul>
<li><strong>Cloud storage</strong>: read straight from Azure Data Lake Storage or S3</li>
</ul>
<pre><code class="language-python">df = dd.read_parquet(
    "abfs://datalake/orders/",
    storage_options={"account_name": "mystorageaccount"}
)
</code></pre>
<ul>
<li><strong>Kubernetes</strong>: scale out with workers as pods when the workload grows</li>
<li><strong>Built-in dashboard</strong>: watch tasks, memory and workers live in your browser 📊</li>
</ul>
<pre><code class="language-python">from dask.distributed import Client

client = Client()            # starts a local cluster on your laptop
print(client.dashboard_link)  # open this and watch the magic
</code></pre>
<hr />
<h2>Dask vs Spark: Who Wins? 🥊</h2>
<p>Honestly? It depends. Dask isn't a Spark killer, and that's okay.</p>
<p><strong>Pick Dask when:</strong></p>
<ul>
<li>✅ Your team is Python-first</li>
<li>✅ You already love Pandas and want to scale it</li>
<li>✅ Your data is "big", but not "planet-sized"</li>
<li>✅ You don't want to babysit a JVM cluster</li>
</ul>
<p><strong>Stick with Spark when:</strong></p>
<ul>
<li>🏗️ You're processing massive, petabyte-scale workloads</li>
<li>🧮 Your pipelines are heavy on complex SQL</li>
<li>🌊 You need mature streaming support</li>
</ul>
<hr />
<h2>TL;DR 🧊</h2>
<ul>
<li>🐼 Pandas is great until your data outgrows memory</li>
<li>🚀 Dask gives you Pandas-style code that scales across cores and clusters</li>
<li>💤 It's lazy: it plans first, then runs efficiently with <code>.compute()</code></li>
<li>☁️ It fits naturally with cloud storage and Kubernetes</li>
<li>🥊 It's not a Spark replacement, but a lighter option for Python-first teams</li>
</ul>
<p>Next time you see a <code>MemoryError</code>, don't panic. Just call in the fleet. 🛵💨</p>
<p><em>Have you tried Dask in production? Tell me your war stories in the comments!</em></p>
]]></content:encoded></item></channel></rss>