<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dor Farber</title>
    <description>The latest articles on DEV Community by Dor Farber (@dorfarber).</description>
    <link>https://dev.to/dorfarber</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F157352%2Fe5851d8f-ee74-4c47-b99d-5826f97851e2.jpeg</url>
      <title>DEV Community: Dor Farber</title>
      <link>https://dev.to/dorfarber</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dorfarber"/>
    <language>en</language>
    <item>
      <title>I built a real Spark event-log parser instead of faking it with sample data</title>
      <dc:creator>Dor Farber</dc:creator>
      <pubDate>Tue, 11 Aug 2026 08:17:58 +0000</pubDate>
      <link>https://dev.to/dorfarber/i-built-a-real-spark-event-log-parser-instead-of-faking-it-with-sample-data-542k</link>
      <guid>https://dev.to/dorfarber/i-built-a-real-spark-event-log-parser-instead-of-faking-it-with-sample-data-542k</guid>
      <description>&lt;p&gt;Quick context if you didn't see the earlier post: opti-pipe compares your pipeline's config against its actual run metrics and tells you what's over/under-provisioned — executor memory, shuffle partitions, cluster size — with the reasoning shown, not a black-box score. Rule-based, not ML, on purpose: I can point at the exact threshold that fired.&lt;/p&gt;

&lt;p&gt;dbt had a real integration from day one (reads your actual &lt;code&gt;target/run_results.json&lt;/code&gt;). Spark didn't — the rules existed, fully tested, but ran against hand-typed fixtures because there was no ingestion path. That's fixed now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an event log, not a live REST API reader
&lt;/h2&gt;

&lt;p&gt;Two real options existed: a live reader against Spark's REST API / History Server, or an uploaded event log (the file Spark writes when &lt;code&gt;spark.eventLog.enabled=true&lt;/code&gt;). The live path is more precise but needs a reachable driver or History Server — friction most solo setups don't want. The event log is self-serve, same shape as dbt's upload, ships without needing cluster connectivity. Picked the second one; the live reader stays on the roadmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parsing itself
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;parse_spark_event_log&lt;/code&gt; walks the real listener event stream — &lt;code&gt;SparkListenerApplicationStart/End&lt;/code&gt;, &lt;code&gt;SparkListenerEnvironmentUpdate&lt;/code&gt;, &lt;code&gt;SparkListenerStageSubmitted&lt;/code&gt;, &lt;code&gt;SparkListenerTaskEnd&lt;/code&gt; with its nested &lt;code&gt;Task Metrics&lt;/code&gt;/&lt;code&gt;Task End Reason&lt;/code&gt; — line by line (it's JSON-lines, one event per line, not a single document). From it: task runtime, GC pause time, record counts, OOM detection via matching &lt;code&gt;Task End Reason&lt;/code&gt; against OOM error signatures, queue delay from stage-submission-vs-task-launch timestamps, and heap usage as a percentage of configured executor memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bug worth mentioning
&lt;/h2&gt;

&lt;p&gt;Spark's event log has zero concept of CPU utilization — no listener emits it, it's an infra-level metric. My first pass defaulted that field to &lt;code&gt;0.0&lt;/code&gt;. That's a real bug: &lt;code&gt;0%&lt;/code&gt; CPU reads to the "overprovisioned cluster" rule as genuinely idle, so it would've spuriously recommended shrinking your cluster on every single real import regardless of actual load. Fixed with an explicit &lt;code&gt;CPU_UTIL_UNAVAILABLE&lt;/code&gt; sentinel that the aggregation and rule logic both treat as "no data," not "0%." Only surfaced because I stopped testing against fixtures I'd hand-written with plausible non-zero values and pointed it at something real.&lt;/p&gt;

&lt;p&gt;Try it against your own Spark job (upload guide included, walks through finding the event log on EMR/Databricks/Kubernetes/standalone): &lt;a href="https://opti-pipe.onrender.com" rel="noopener noreferrer"&gt;opti-pipe.onrender.com&lt;/a&gt;. Feedback — especially "this recommendation is wrong" — is genuinely the most useful thing anyone can send me right now.&lt;/p&gt;

</description>
      <category>spark</category>
      <category>dataengineering</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why I stopped guessing at Spark and dbt config values</title>
      <dc:creator>Dor Farber</dc:creator>
      <pubDate>Sat, 08 Aug 2026 21:24:07 +0000</pubDate>
      <link>https://dev.to/dorfarber/why-i-stopped-guessing-at-spark-and-dbt-config-values-1i2n</link>
      <guid>https://dev.to/dorfarber/why-i-stopped-guessing-at-spark-and-dbt-config-values-1i2n</guid>
      <description>&lt;p&gt;I've spent more than a decade building data pipelines, and the part nobody warns you about isn't the pipeline logic. It's the tuning. Executor memory, shuffle partitions, cluster size, thread counts. You pick numbers, ship it, and a few weeks later something breaks in a way that's obviously tuning-related but not obviously &lt;em&gt;what to change&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The pattern repeats enough times that you start recognizing it before you've even opened the logs. Job's slow, thousands of tiny shuffle tasks, someone way overestimated the partition count. Job dies on OOM, memory's set for last quarter's data volume, nobody updated it since. Cloud bill jumps, a cluster's been sized for peak load and just sits there mostly idle the other 20 hours a day. Every senior data engineer has this pattern-matching running in their head. It's tribal knowledge, and it lives in one or two people's heads on most teams, which means it doesn't scale and it definitely doesn't survive someone leaving.&lt;/p&gt;

&lt;p&gt;So I built a small tool to make that pattern-matching explicit instead of tribal: it reads your pipeline's config alongside its actual run metrics and tells you what's likely wrong, with the reasoning shown, not just a suggested number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why rules instead of a model
&lt;/h2&gt;

&lt;p&gt;The obvious move in 2026 is to reach for an ML model. I didn't, and it wasn't because I don't think ML has a place here eventually. It's that for this specific problem, a handful of threshold rules already gets you most of the value, and they're something you can actually audit.&lt;/p&gt;

&lt;p&gt;If a rule fires, I can point at the exact condition and the exact number: average heap usage 28%, peak 47%, five runs, no OOM errors, therefore memory's over-provisioned, shrink it by roughly a fifth. That's checkable. You can look at your own metrics and see whether the reasoning holds. A model's confidence score doesn't give you that, and for something that's about to change a production config, I want the person approving it to be able to say "yes, I see why" rather than "the model said so."&lt;/p&gt;

&lt;p&gt;There are seven rules right now: memory over/under-provisioned, shuffle partition sizing, cluster instance count over/under-provisioned, and two for dbt (thread count and tasks-per-thread). Each one is a pure function: current config plus recent run metrics in, a recommendation or nothing out. No side effects, no state, fully unit tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nothing applies itself
&lt;/h2&gt;

&lt;p&gt;This was a deliberate line, not a limitation I plan to remove later. Every recommendation requires a human click before anything changes. I don't trust an agent to touch a production Spark config unattended, and I don't think you should either, no matter how good the underlying reasoning is. The tool's job is to surface the diff and the reasoning behind it. Approving it is still yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually real today, and what isn't
&lt;/h2&gt;

&lt;p&gt;dbt is the one framework with a genuine integration. Upload your actual &lt;code&gt;target/run_results.json&lt;/code&gt; and you get recommendations computed from your real run history, not a fixture. Spark's rules exist and are fully tested, but there's no live reader yet, no Spark REST API or event-log ingestion, so it works on sample data today rather than your own cluster. I'd rather say that plainly than let the pitch imply more than the tool does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;There's a live dashboard with three real sample pipelines you can click into right now, no signup, no upload required to look around: &lt;a href="https://opti-pipe.onrender.com" rel="noopener noreferrer"&gt;opti-pipe.onrender.com&lt;/a&gt;. If you run dbt, you can upload your own &lt;code&gt;run_results.json&lt;/code&gt; and get recommendations against your actual data.&lt;/p&gt;

&lt;p&gt;I'd genuinely like to hear from anyone who tries it against a real pipeline, especially if a recommendation is wrong. That's the fastest way I'll find the next rule worth adding.&lt;/p&gt;

</description>
      <category>dataengineering</category>
      <category>dbt</category>
      <category>apachespark</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
