<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Deltalog - Adi Polak</title><link>https://adipolak.github.io/adipolak-blog/tags/deltalog/</link><description>AI and Cloud expert sharing insights on AI systems, cloud computing, distributed systems, data analytics, and technical leadership</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>Adi Polak</managingEditor><copyright>Copyright &amp;#169; 2020 Adi Polak. All rights reserved.</copyright><lastBuildDate>Mon, 15 Feb 2021 00:00:00 +0000</lastBuildDate><atom:link href="https://adipolak.github.io/adipolak-blog/tags/deltalog/feed.xml" rel="self" type="application/rss+xml"/><item><title>Delta Lake essential Fundamentals: Part 3 - compaction and checkpoint</title><link>https://adipolak.github.io/adipolak-blog/post/delta-lake-essential-fundamentals---part-3/</link><pubDate>Mon, 15 Feb 2021 00:00:00 +0000</pubDate><author>Adi Polak</author><guid>https://adipolak.github.io/adipolak-blog/post/delta-lake-essential-fundamentals---part-3/</guid><description>Multi-part series that will take you from beginner to expert in Delta Lake</description><content:encoded><![CDATA[<p>Let&rsquo;s understand what are Delta Lake compact and checkpoint and why they are important.</p>
<h2 id="checkpoint">Checkpoint</h2>
<p>There are two known checkpoints mechanism in Apache Spark that can confuse us with DeltaLake checkpoint, so let&rsquo;s understand them and how they differ from each other:</p>
<h3 id="spark-rdd-checkpoint">Spark RDD Checkpoint</h3>
<p>Checkpoint in Spark RDD is a mechanism to persist current RDD to a file in a dedicated checkpoint directory while all references to its parent RDDs are removed.
This operation, by default, breaks data lineage when used without auditing.</p>
<h3 id="structured-streaming-checkpoint">Structured Streaming Checkpoint</h3>
<p>Structured Streaming is a scalable and fault-tolerant stream processing built on Spark SQL engine. The queries are processed using a micro-batch processing engine as a series of small-batch jobs. Structured Streaming enables exactly once fault-tolerant guarantees through checkpoint and writing ahead logs. The streaming engine record the offset range of the data that is being processed in each trigger. Hence if a trigger failed, we have the exact range of processed data there and can recover from it.</p>
<h3 id="deltalake-checkpoint">DeltaLake checkpoint</h3>
<p>On each Delta Table state compute, Delta reads the JSON files discussed in <a href="/post/delta-lake-essential-fundamentals-the-deltalog/">Delta Lake essential Fundamentals: Part 2 - The DeltaLog</a>. To avoid reading all the files and executing a long compute, every 10 commit files are being aggregated to a <em>checkpoint</em> file of type parquet. These checkpoint files save the entire state of the table at a point in time. It allows the Spark engine to avoid reprocessing thousands of tiny JSON files. This mechanism ensures that for computing table state, Spark only needs to read the latest parquet checkpoint file with up to 10 JSON files, it makes the computation faster and efficient.
Checkout the visualization of Delta Checkpoint file from Databricks site: <br>
<img class="responsive" src="../../images/Detla/checkpointfile.png" alt="drawing"></p>
<p>checkpoint files can be a one file for a specific table version or multiple files, it depends on what it contains.</p>
<p>In one part, table version(<code>n</code>) 10 the file name will be of the structure <code>n.checkpoint.parquet</code>:</p>
<pre tabindex="0"><code>00000000000000000010.checkpoint.parquet
</code></pre><p>In multi-part, table version(<code>n</code>) 10 the files name will be of a structure that Fragment <code>o</code> of <code>p</code>: <code>n.checkpoint.o.p.parquet</code>:</p>
<pre tabindex="0"><code>00000000000000000010.checkpoint.0000000001.0000000003.parquet
00000000000000000010.checkpoint.0000000002.0000000003.parquet
00000000000000000010.checkpoint.0000000003.0000000003.parquet
</code></pre><p>Snapshot of the function that is in charge of the writing the checkpoint files, the modulo operation is in charge of the checkpointInterval which can be updated in DeltaConfig.
<br></p>
<img class="responsive" src="../../images/Detla/delta-lake-postcommit.png" alt="drawing">
<img class="responsive" src="../../images/Detla/deltalake-interval-config.png" alt="drawing">
<p>Delta Lake configuration can be set as a Spark Configuration property, or Hadoop configuration depends on the LogStore, the cloud used, etc.</p>
<hr>
<p>I hope this provides more clarity into the differences between the three checkpoint mechanisms and their usage.</p>
<p>Next, let&rsquo;s examine the compact files mechanism Delta recommends as part of its best practices:</p>
<h2 id="delta-lake-compact-files">Delta Lake Compact files</h2>
<p>The same way Delta Lake handles its own small JSON DeltaLog files is creating, we as developers need to take care of the small files we might introduce to the system when adding data in small batches. Small batches can happen when we have Streaming workloads or continuous small batches of data ingesting without compacting it.</p>
<p>Small files can hurt the efficiency of table reads, and it can also affect the performance of the file system itself. Ideally, a large number of small files should be rewritten into a smaller number of larger files regularly. This is known as compaction.</p>
<p>We can compact a table by repartitioning it to a smaller number of files.</p>
<p>Delta Lake also introduces the ability to set the <code>dataChange</code> field false; this indicates that the operation did not change the data, only rearranges the data layout. But be careful with it, since if you are introducing a data change that is not only a layout, it can corrupt the data in the table.</p>
<hr>
<p>For exploring and learning about Delta, you are invited in joining me by watching the videos. Let me know if that is useful for you, and we can schedule twitch as well.</p>
<h1 id="whats-next">What&rsquo;s next?</h1>
<p>Next, scenarios and use cases for DeltaLake!</p>
<p>As always, I would love to get your comments and feedback on <a href="https://twitter.com/intent/follow?original_referer=http%3A%2F%2Flocalhost%3A1313%2F&amp;ref_src=twsrc%5Etfw&amp;region=follow_link&amp;screen_name=AdiPolak&amp;tw_p=followbutton">Adi Polak</a> 🐦.</p>

    <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Aq8bo6OR48A?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>If you would like to get monthly updates, consider <a href="https://sub.adipolak.com/subscribe">subscribing</a>.</p>
]]></content:encoded><category>open-source</category><category>apache spark</category><category>delta lake</category><category>distributed-systems</category><category>beginner</category><category>deltalog</category></item><item><title>Delta Lake essential Fundamentals: Part 2 - The DeltaLog</title><link>https://adipolak.github.io/adipolak-blog/post/delta-lake-essential-fundamentals---the-deltalog/</link><pubDate>Thu, 11 Feb 2021 00:00:00 +0000</pubDate><author>Adi Polak</author><guid>https://adipolak.github.io/adipolak-blog/post/delta-lake-essential-fundamentals---the-deltalog/</guid><description>Multi-part series that will take you from beginner to expert in Delta Lake</description><content:encoded><![CDATA[<p>In the previous part, you learned what <a href="/post/delta-lake-essential-fundamentals">ACID transactions</a> are.<br>
In this part, you will understand how Delta Transaction Log, named DeltaLog, is achieving ACID.</p>
<h2 id="transaction-log">Transaction Log</h2>
<p>A transaction log is a history of actions executed by a (TaDa 💡) database management system with the goal to guarantee <a href="/post/delta-lake-essential-fundamentals/">ACID properties</a> over a crash.</p>
<h2 id="deltalake-transaction-log---detlalog">DeltaLake transaction log - DetlaLog</h2>
<p>DeltaLog is a transaction log directory that holds an <strong>ordered</strong> record of every transaction committed on a Delta Lake table since it was created.
The goal of DeltaLog is to be the <strong>single</strong> source of truth for readers who read from the same table at the same time. That means, parallel readers read the <strong>exact</strong> same data.
This is achieved by tracking all the changes that users do: read, delete, update, etc. in the DeltaLog.</p>
<p>DeltaLog can also contain statistics on the data; depending on the type of the data/field/column, each column can have min/max values. Having this extra metadata can help with faster querying. DeltaTable read mechanism uses a simplified <a href="https://medium.com/microsoftazure/data-at-scale-learn-how-predicate-pushdown-will-save-you-money-7063b80878d7">push down predict</a>.</p>
<p>Here is a simplification of DeltaLog on the file systems from Databricks site: <br>
<img class="responsive" src="../../images/Detla/deltalake-deltalog.png" alt="drawing"></p>
<p>The DeltaLog itself is a folder that consists of multiple JSON files. When it reaches 10 files, DeltaTable does a checkpoint and compaction operations (we will dive into it in the next chapter).</p>
<p>Here is an example of a DeltaLog JSON file from the code source test resources, each entry in the file is on JSON:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{<span style="color:#f92672">&#34;remove&#34;</span>:{<span style="color:#f92672">&#34;path&#34;</span>:<span style="color:#e6db74">&#34;part-00001-f1cb1cf9-7a73-439c-b0ea-dcba5c2280a6-c000.snappy.parquet&#34;</span>,<span style="color:#f92672">&#34;dataChange&#34;</span>:<span style="color:#66d9ef">true</span>}}
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;remove&#34;</span>:{<span style="color:#f92672">&#34;path&#34;</span>:<span style="color:#e6db74">&#34;part-00000-f4aeebd0-a689-4e1b-bc7a-bbb0ec59dce5-c000.snappy.parquet&#34;</span>,<span style="color:#f92672">&#34;dataChange&#34;</span>:<span style="color:#66d9ef">true</span>}}
</span></span></code></pre></div><p>There was a total of two commits captured in this file:
<em>remove</em> -it can be a delete operation on a whole column or only specific values in it. In this operation the metadata field <em>dataChange</em> is set to true.</p>
<p>Here is a more complex JSON file example, each entry in the file is on JSON:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{<span style="color:#f92672">&#34;metaData&#34;</span>:{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#e6db74">&#34;2edf2c02-bb63-44e9-a84c-517fad0db296&#34;</span>,<span style="color:#f92672">&#34;format&#34;</span>:{<span style="color:#f92672">&#34;provider&#34;</span>:<span style="color:#e6db74">&#34;parquet&#34;</span>,<span style="color:#f92672">&#34;options&#34;</span>:{}},<span style="color:#f92672">&#34;schemaString&#34;</span>:<span style="color:#e6db74">&#34;{\&#34;type\&#34;:\&#34;struct\&#34;,\&#34;fields\&#34;:[{\&#34;name\&#34;:\&#34;id\&#34;,\&#34;type\&#34;:\&#34;integer\&#34;,\&#34;nullable\&#34;:true,\&#34;metadata\&#34;:{}},{\&#34;name\&#34;:\&#34;value\&#34;,\&#34;type\&#34;:\&#34;string\&#34;,\&#34;nullable\&#34;:true,\&#34;metadata\&#34;:{}}]}&#34;</span>,<span style="color:#f92672">&#34;partitionColumns&#34;</span>:[<span style="color:#e6db74">&#34;id&#34;</span>],<span style="color:#f92672">&#34;configuration&#34;</span>:{}}}
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;remove&#34;</span>:{<span style="color:#f92672">&#34;path&#34;</span>:<span style="color:#e6db74">&#34;part-00001-6d252218-2632-416e-9e46-f32316ec314a-c000.snappy.parquet&#34;</span>,<span style="color:#f92672">&#34;dataChange&#34;</span>:<span style="color:#66d9ef">true</span>}}
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;remove&#34;</span>:{<span style="color:#f92672">&#34;path&#34;</span>:<span style="color:#e6db74">&#34;part-00000-348d7f43-38f6-4778-88c7-45f379471c49-c000.snappy.parquet&#34;</span>,<span style="color:#f92672">&#34;dataChange&#34;</span>:<span style="color:#66d9ef">true</span>}}
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;add&#34;</span>:{<span style="color:#f92672">&#34;path&#34;</span>:<span style="color:#e6db74">&#34;id=5/part-00000-f1e0b560-ca00-409e-a274-f1ab264bc412.c000.snappy.parquet&#34;</span>,<span style="color:#f92672">&#34;partitionValues&#34;</span>:{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#e6db74">&#34;5&#34;</span>},<span style="color:#f92672">&#34;size&#34;</span>:<span style="color:#ae81ff">362</span>,<span style="color:#f92672">&#34;modificationTime&#34;</span>:<span style="color:#ae81ff">1501109076000</span>,<span style="color:#f92672">&#34;dataChange&#34;</span>:<span style="color:#66d9ef">true</span>}}
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;add&#34;</span>:{<span style="color:#f92672">&#34;path&#34;</span>:<span style="color:#e6db74">&#34;id=6/part-00000-adb59f54-6b8f-4bfd-9915-ae26bd0f0e2c.c000.snappy.parquet&#34;</span>,<span style="color:#f92672">&#34;partitionValues&#34;</span>:{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#e6db74">&#34;6&#34;</span>},<span style="color:#f92672">&#34;size&#34;</span>:<span style="color:#ae81ff">362</span>,<span style="color:#f92672">&#34;modificationTime&#34;</span>:<span style="color:#ae81ff">1501109076000</span>,<span style="color:#f92672">&#34;dataChange&#34;</span>:<span style="color:#66d9ef">true</span>}}
</span></span><span style="display:flex;"><span>{<span style="color:#f92672">&#34;add&#34;</span>:{<span style="color:#f92672">&#34;path&#34;</span>:<span style="color:#e6db74">&#34;id=4/part-00001-36c738bf-7836-479b-9cc1-7a4934207856.c000.snappy.parquet&#34;</span>,<span style="color:#f92672">&#34;partitionValues&#34;</span>:{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#e6db74">&#34;4&#34;</span>},<span style="color:#f92672">&#34;size&#34;</span>:<span style="color:#ae81ff">362</span>,<span style="color:#f92672">&#34;modificationTime&#34;</span>:<span style="color:#ae81ff">1501109076000</span>,<span style="color:#f92672">&#34;dataChange&#34;</span>:<span style="color:#66d9ef">true</span>}}
</span></span></code></pre></div><p>In this example, there is the <em>metadata</em> object entry - it represents a change in the table columns either an update to the table schema or that a new table was created.
Later we see two <em>remove</em> operations, followed by three <em>add</em> operations. These operation objects can have a <em>stat</em> field, which contains statistical information, such as the number of records, minValues, maxValues, and more.</p>
<p>These JSON files might also contain operation objects with fields such as - &ldquo;STREAMING UPDATE&rdquo;, &ldquo;NOTEBOOK&rdquo;  if the operation took place from a notebook, isolationLevel, etc.</p>
<p>This information is valuable for managing the table and avoiding redundant full scan on the storage.</p>
<p>To simplify the connection between DeltaTable and DeltaLog, it&rsquo;s easier to think about DeltaTable as a direct result of a set of actions audited by the DeltaLog.</p>
<h2 id="deltalog-and-atomicity">DeltaLog and Atomicity</h2>
<p>From <a href="/post/delta-lake-essential-fundamentals">part one</a>, you already know that atomicity means that a transaction, either happened or not. The DeltaLog itself consists of atomic operations; each line in the log (like the ones you saw above) represents an action, which is an atomic unit; These are called commits.
The transactions that took place on the data can be broken into multiple components in which each one individually represents a commit in the DeltaLog. These breaking complex operations into small transactions help with ensuring atomicity.</p>
<h2 id="deltalog-and-isolation">DeltaLog and Isolation</h2>
<p>Operations such as Update, Delete, Add can harm isolation; Hence, since we want to guarantee isolation with DeltaTable, readers only get access to the table snapshot. This guarantees all parallel readers read the exact data. For handling deletion operations, Delta postpones the actual delete operation on the files; it first tags the files as deleted and later, remove them when considered safe (similar to Cassandra, and ElasticSearch delete operations with a tombstone).</p>
<p>In DeltaLake 0.8.1 source code, there is a comment saying that it&rsquo;s recommended to have the delete retention set to at least 2 weeks or longer than a duration of a job. <br>
<em>Note:</em> This will impact streaming workload as well, because there will be a need to delete the actual files at some point, which might result in blocking the stream.
<img class="responsive" src="../../images/Detla/delta-tombston-retention.png" alt="drawing"></p>
<h2 id="deltalog-and-consistency">DeltaLog and Consistency</h2>
<p>Delta Lake solves the problem of consistency by solving conflicts with an optimistic concurrency algorithm.
The class in charge of this algorithm is the OptimisticTransaction class. It achieves it by using <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html">Java 8 ReentrantLock</a> that is controlled from a DeltaLog instance. <br>
Here is the code snippet: <br></p>
<img class="responsive" src="../../images/Detla/delta-log-optimistic-concurrency-algo.png" alt="drawing">
<p>A DeltaTable instance actively uses the ReentrantLock in the OptimisticTransaction under the <code>doCommitRetryIteratively</code> function.
The optimistic approach was chosen here because in the big data world there is a tendency to add more data than to update existing records.
It&rsquo;s rare to find and update a specific record, it is usually done when there was some data corruption on necessary data.</p>
<p>Here is the code snippet for the optimistic algorithm:
<img class="responsive" src="../../images/Detla/delta-log-OptimisticTransaction.png" alt="drawing"></p>
<p>Notice that in line 572, the program records the attempted version as the <code>commitVersion</code> instance which is of type <code>var</code>.
<code>var</code> in Scala represents a mutable object instance, which means we should expect its value to change.</p>
<p>In line 575, we start the algorithm:
it starts the <code>while(true)</code> loop and maintains an <code>attemptNumber</code> counter; if it&rsquo;s <code>==0</code>, it will try to commit; if it fails here, that means that a file with this <code>commitVersion</code> was already written/committed into the table and it will throw an exception. That exception is being caught in lines 592+593. From there, with each failure, the algorithm is increasing the attemptNumber by 1.
After the first failure, the program won&rsquo;t go into the first if statement on line 577; it will go straights into the <code>else if</code> on line 579.
If the program reached the state where <code>attemptNumber</code> is bigger than the maximum allowed/configured, it will throw a <code>DeltaErrors.maxCommitRetriesExceededException</code> exception.
maxCommitRetriesExceededException exception will provide information about the commit version, the first commit version attempt, the number of attempted commits, and total time spent attempting this commit in ms.
Otherwise, it will try to record this update with checkForConflict functionality in line 588.
Multiple scenarios can bring us to this state.</p>
<p>High-level pseudo-code:</p>
<pre tabindex="0"><code>while(tryCommit)
    if first attempt:
        do commit
    else if: attempt number &gt; max retries
            throw an exception - exit loop
        else:
            record retry operation
            try fixing logical conflicts - return valid commit version or throw an exception
            do commit
    retry on exceptions and attempt version +1
    if no exception - end loop
end     
</code></pre><p>To support the users, DeltaLake introduces a set of conflict exceptions that provide more information about the data and the conflicts:</p>
<img class="responsive" src="../../images/Detla/delte-concurrent-exceptions.png" alt="drawing">
<p>Let&rsquo;s look at some of the conflict scenarios.</p>
<h3 id="two-writers">Two Writers:</h3>
<p>This is the case of two writers who appends data to the same table simultaneously, without reading anything. In this scenario, one writer will commit, and the second writer will read the first one&rsquo;s updates before adding their own updates. Suppose it was only an append operation, like a counter which both are incrementing. In that case, there is no need to redo all computations, and it will automatically commit; if that&rsquo;s not the case, writer number two will need to redo the computation given the new information from writer one.</p>
<h3 id="delete-and-read">Delete and Read:</h3>
<p>In a more complex scenario like this one, there is no automated solution. For concurrent Delete-Read, there is a dedicated <code>ConcurentDeleteReadException</code>.
That means that if there is a request to delete a file that at the same time is being used for a read, the program throws an exception.</p>
<img class="responsive" src="../../images/Detla/ConcurrentDeleteReadException.png" alt="drawing">
<h3 id="delete-and-delete">Delete and Delete:</h3>
<p>When two operations delete the same file, it might be due to a compaction mechanism or other operation, here too an exception will occur.</p>
<h2 id="deltalog-and-durability">DeltaLog and Durability</h2>
<p>Since all transactions made on a DeltaTable are being stored directly to the disk/file system, durability is a given. All commits are being <em>persisted</em> to disk.  In case of a system failure, they can be restored from the disk.
(Unless there is a true disaster like fire etc and damage to the actual disks holding the information).</p>
<hr>
<p>For exploring and learning about Delta, I did a deep dive into the code source itself. If you are interested in joining me, I captured it through videos, let me know if that is useful for you.</p>
<h1 id="whats-next">What&rsquo;s next?</h1>
<p>Next, we will see more examples, scenarios and use cases for DeltaLake! We will learn about the compaction mechanism, schema enforcement and how it can enforce exactly once operation.</p>
<p>As always, I would love to get your comments and feedback on <a href="https://twitter.com/intent/follow?original_referer=http%3A%2F%2Flocalhost%3A1313%2F&amp;ref_src=twsrc%5Etfw&amp;region=follow_link&amp;screen_name=AdiPolak&amp;tw_p=followbutton">Adi Polak</a> 🐦.</p>

    <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/i24ZA6mmvDI?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>If you would like to get monthly updates, consider <a href="https://sub.adipolak.com/subscribe">subscribing</a>.</p>
]]></content:encoded><category>open-source</category><category>apache spark</category><category>delta lake</category><category>distributed-systems</category><category>beginner</category><category>deltalog</category></item></channel></rss>