<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Tim Gent on Medium]]></title>
        <description><![CDATA[Stories by Tim Gent on Medium]]></description>
        <link>https://medium.com/@timgent?source=rss-a8ec0762469b------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*03MnJ1WSZkogIHUG.jpg</url>
            <title>Stories by Tim Gent on Medium</title>
            <link>https://medium.com/@timgent?source=rss-a8ec0762469b------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 19:42:51 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@timgent/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[How to speed up your Elixir compile times (part 3) — strategies to improve your compile times]]></title>
            <link>https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-3-strategies-to-improve-your-compile-times-fc0c448b5558?source=rss-a8ec0762469b------2</link>
            <guid isPermaLink="false">https://medium.com/p/fc0c448b5558</guid>
            <category><![CDATA[compilation]]></category>
            <category><![CDATA[phoenix]]></category>
            <category><![CDATA[compilers]]></category>
            <category><![CDATA[elixir]]></category>
            <category><![CDATA[elixirlang]]></category>
            <dc:creator><![CDATA[Tim Gent]]></dc:creator>
            <pubDate>Fri, 16 Sep 2022 08:57:29 GMT</pubDate>
            <atom:updated>2023-02-04T02:35:50.892Z</atom:updated>
            <content:encoded><![CDATA[<h3>How to speed up your Elixir compile times (part 3) — strategies to improve your compile times</h3><p>In this post I’ll share some strategies for improving the speed of recompilation. It’s important you understand the fundamentals first, so please check out <a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-1-understanding-elixir-compilation-64d44a32ec6e">part 1</a> and ideally <a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-2-test-your-understanding-f6ff3de5eb5d">part 2</a> first!</p><h3>Reducing compile time dependencies</h3><h4>Avoiding module attributes calling other modules</h4><p>The big thing here is to <strong>avoid using module attributes that call other modules. </strong>It’s a great way to reduce compile time dependencies. Instead just call those functions where you need them.</p><p>The only downside here is that you can’t call functions in your module headers (for example if you want to pattern match on them). Instead consider using a cond or read on to see some other ways to handle this.</p><h4>Use alias instead of import</h4><p>When you use import you create an export dependency between 2 files. By simply using alias instead no export dependencies are created.</p><h4>Sometimes you have to have compile-time dependencies though right?</h4><p>It’s absolutely true that sometimes you have to have compile time dependencies. Perhaps you have want to use module attributes as you want to pattern match against them in your function headers like this:</p><pre>@active Status.active()<br>@disabled Status.disabled()<br>@old Status.old()</pre><pre>def handle_status(@active), do: ...<br>def handle_status(@disabled), do: ...<br>def handle_status(@old), do: ...</pre><p>Or maybe you are implementing a behaviour, using the use keyword, or otherwise calling a macro.</p><p>In these cases the key things to make sure of are</p><ul><li><strong>The file you have a compile-time dependency on has as few runtime dependencies as possible (ideally none)</strong></li><li><strong>And any runtime dependencies it does have aren’t part of a dependency cycle</strong></li></ul><p>Let’s take an example. Imagine you have these files:</p><pre># a.ex<br>defmodule A do<br> @active Status.active()<br>  ...<br>end</pre><pre># status.ex<br>defmodule Status do<br>  def active, do: &quot;active&quot;<br>  def do_other_stuff, do: B.other_stuff()<br>end</pre><pre># b.ex<br>defmodule B do<br>  # Let&#39;s imagine B calls other modules that form a circular dependency<br>  def other_stuff, do: ...<br>end</pre><pre># ...etc. Assume modules C and D here that form a dependency cycle</pre><p>This results in the following dependencies:</p><figure><img alt="A compile time dependency on Status. Status has a runtime dependency on B, B has one on C, C has one on D, and D has one on B" src="https://cdn-images-1.medium.com/max/1024/1*MfLWlY73OI6O6FU5lDlHYQ.png" /></figure><p>This is now truly awful! B, C, and D are now transitive compile time dependencies of A. That means anytime B, C, or D change A will also need to be recompiled.</p><p>How could we fix this? In this case simply splitting the Status module in 2 — one containing our constants, and one containing functions that call out to other modules, like this:</p><pre># a.ex<br>defmodule A do<br> @active Status.Consts.active()<br>  ...<br>end</pre><pre># status/consts.ex<br>defmodule Status.Consts do<br>  def active, do: &quot;active&quot;<br>end</pre><pre># status.ex<br>defmodule Status do<br>  def do_other_stuff, do: B.other_stuff()<br>end</pre><pre># b.ex<br>...etc</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Lp63GxV9LjeTVi6GDeBgww.png" /></figure><p>Now the only changes that will cause a.ex to be recompiled are changes to a.ex itself or changes to status/const.ex. Woohoo!</p><p>A similar principle applies for defining behaviours or macros — try to remove as many runtime dependencies from those modules as possible!</p><p>The mix xref graph — label compile-connected command can help you find these troublesome cases where you have transitive compile-time dependencies.</p><h3>Reducing dependency cycles</h3><p>As mentioned in part 1, dependency cycles are bad, especially big ones. They can lead to many more transitive compile-time dependencies, meaning more files need to be recompiled when you modify files. Here’s how to stop this happening:</p><h4>Step 1 — identify your longest dependency cycles</h4><p>mix xref will come in handy for this:</p><pre>mix xref graph --format cycles</pre><h4>Step 2 — identify where dependencies don’t make sense</h4><p>Looking at a long cycle of dependencies is a bit intimidating. Start by trying to identify a single call-chain that doesn’t feel like it would be necessary. For example some common smells are things like:</p><ul><li>A sub-module depending on it’s parent</li><li>A module reaching into a context that doesn’t seem strictly related</li><li>Generally any dependency where it doesn’t make intuitive sense from the file names, when you just think “but why would this need to depend on that??”</li></ul><p><strong>Let’s take a real example from a cycle in our codebase</strong>:</p><pre>lib/platform/companies/events/company_created/publisher.ex<br>lib/platform/companies/events/json_serialiser.ex<br>lib/platform/companies/company.ex<br>lib/platform/roles/role.ex<br>lib/platform/roles/slug.ex<br>lib/platform/companies/companies.ex<br>lib/platform/companies/events/company_created/publisher.ex</pre><p>One thing jumps out to me here — should slug (which generates a string to use in the URL) really need to know about companies? Surely it would get passed everything it needs to generate a slug?</p><h4>Step 3 — Remove those dependencies where you can</h4><p>Generally this means refactoring the code, which could mean:</p><ul><li>Changing where you call functions from to remove a troublesome dependency</li><li>Changing where the function causing the dependency cycle lives — perhaps moving it into a new module for example</li></ul><p>In the example above, it turned out that a function in slug.ex was being passed a company_id, and then using the companies context to look up the details for the company. By instead passing the required company details to the function we could remove the function call in slug, and hence remove this dependency cycle.</p><h4>Step 4— check your progress</h4><p>In smaller projects running mix xref graphs --format cycles is easy enough to see progress.</p><p>However if you have a lot of cycles it can be hard to feel like you’re making progress — often fixes don’t even reduce the number of loops, just reduce the number of steps per loop. You can use this nifty one liner to count the total steps across all your loops. You’ll be amazed how fast you can drive this number down:</p><pre>mix xref graph --format cycles | grep &quot;Cycle of length&quot; | sed &#39;s/[^0-9]//g&#39; | paste -sd+ - | bc</pre><h4>Step 5— stop dependency cycles re-occuring in your CI pipeline</h4><p>mix xref has this handy command which you can use in your CI pipeline to make sure you’re not introducing new dependency cycles.</p><pre>mix xref graph --format cycles --fail-above X</pre><h3>Identifying slow-compiling files</h3><p>This command will help you identify slow-compiling files:</p><pre>mix compile --force --profile time</pre><p>Use this to help focus your efforts on refactoring, or at least reducing compile-time dependencies for the slowest files. Some other tools you might find helpful for debugging your dependencies are:</p><ul><li>mix compile --verbose will tell you which files are being recompiled, so you can change a single file and run a compilation in verbose mode to see what files that causes to be recompiled</li><li>mix xref trace FILE for identifying what dependencies a given file has</li></ul><h3>Tips for Phoenix compile times</h3><p>When you call a macro (such as the get macro used in Phoenix routers) if you pass a full module name it creates a compile-time dependency on that module. For example this code has a compile-time dependency on CandidatesWeb.LoginController.</p><pre>scope &quot;/candidates&quot; do    <br>  get &quot;/&quot;, CandidatesWeb.LoginController, :delete<br>end</pre><p>With Phoenix we can pass the outer module to scope. So with this code there is no compile-time dependency!</p><pre>scope &quot;/candidates&quot;, CandidatesWeb do    <br>  get &quot;/&quot;, LoginController, :delete<br>end</pre><p>NOTE: I believe this will be fixed in a future Phoenix release so the first example above doesn’t create a compile-time dependency — watch this space!</p><h3>Inevitable dependency cycles with Phoenix</h3><p>Phoenix has a design that naturally creates dependency cycles, because:</p><ul><li>A <strong>router</strong> contains the routes for your application, and depends on <strong>controllers</strong> to handle them</li><li><strong>Controllers</strong> have the logic for the route, and depend on contexts and <strong>views </strong>to display things to the user</li><li><strong>Views </strong>depend on <strong>templates </strong>to render things to the user</li><li><strong>Templates </strong>often contain links to other places in the site. These links tend to use Phoenix path helpers to ensure they are up to date. And of course, these path helpers depend on the <strong>router</strong>, and thus the cycle is formed</li><li>Just to note, this dependency cycle still exists even if you use the new <a href="https://github.com/phoenixframework/phoenix/blob/master/CHANGELOG.md#introduction-of-verified-routes">Verified Routes functionality</a> instead of the usual path helpers</li></ul><p>So far we’ve not found a way to avoid these. Instead we follow the advice earlier in this guide and ensure that there are never compile-time dependencies on a file that is part of a runtime dependency cycle.</p><h3>Summary</h3><p>I hope you’ve found this series helpful, and would love to hear any suggestions for more strategies to improve compilation times!</p><p>Also <a href="https://www.multiverse.io/en-GB/careers">Multiverse is hiring</a> so if you love working on interesting technical challenges and would like to help us build an outstanding alternative to university we’d love to hear from you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fc0c448b5558" width="1" height="1" alt=""><hr><p><a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-3-strategies-to-improve-your-compile-times-fc0c448b5558">How to speed up your Elixir compile times (part 3) — strategies to improve your compile times</a> was originally published in <a href="https://medium.com/multiverse-tech">multiverse-tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to speed up your Elixir compile times (part 2) — test your understanding!]]></title>
            <link>https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-2-test-your-understanding-f6ff3de5eb5d?source=rss-a8ec0762469b------2</link>
            <guid isPermaLink="false">https://medium.com/p/f6ff3de5eb5d</guid>
            <category><![CDATA[compilation]]></category>
            <category><![CDATA[phoenix-framework]]></category>
            <category><![CDATA[phoenix]]></category>
            <category><![CDATA[compilers]]></category>
            <category><![CDATA[elixir]]></category>
            <dc:creator><![CDATA[Tim Gent]]></dc:creator>
            <pubDate>Wed, 31 Aug 2022 08:49:21 GMT</pubDate>
            <atom:updated>2022-09-16T09:02:03.403Z</atom:updated>
            <content:encoded><![CDATA[<h3>How to speed up your Elixir compile times (part 2) — test your understanding!</h3><p>This post gives a few examples to help you get your head around how the Elixir compiler deals with dependencies. Try your best to work out the answers without reading ahead — it’ll do wonders for your understanding! You can find the <a href="https://github.com/timgent/compile_examples">examples on Github</a>. I also recommend reading <a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-1-understanding-elixir-compilation-64d44a32ec6e">part 1 of the series</a> which explains some key details of how Elixir compilation works. <a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-3-strategies-to-improve-your-compile-times-fc0c448b5558">Part 3 is now available which covers strategies for improving your compile times</a>.</p><h3>Challenges (solutions below)</h3><p><strong>1. Starting simple</strong></p><p>If we edit a.ex below which files will be recompiled?</p><pre># a.ex<br>defmodule A do<br>  def a, do: &quot;a&quot;<br>end</pre><pre># b.ex<br>defmodule B do<br>  def b, do: A.a()<br>end</pre><p><strong>2. Little harder…</strong></p><p><strong>Part a)</strong> If we edit a.ex below which files will be recompiled?<br><strong>Part b)</strong> If we edit b.ex below which files will be recompiled?</p><pre># a.ex<br>defmodule A do<br>  def a, do: &quot;a&quot;<br>end</pre><pre># b.ex<br>defmodule B do<br>  <a href="http://twitter.com/a">@a</a> A.a()<br>  def b, do: <a href="http://twitter.com/a">@a</a><br>end</pre><p><strong>3. Keep that brain working…<br></strong>If file a.ex is changed which files get recompiled?</p><pre># a.ex<br>defmodule A do<br>  def a, do: &quot;a&quot;<br>end</pre><pre># b.ex<br>defmodule B do<br>  def b, do: A.a()<br>end</pre><pre># c.ex<br>defmodule C do<br>  <a href="http://twitter.com/b">@b</a> B.b()<br>  def c, do: <a href="http://twitter.com/b">@b</a><br>end</pre><p><strong>4. Still with me?<br></strong>If file b.ex is changed which files will be recompiled?</p><pre># a.ex<br>defmodule A do<br>  def a, do: &quot;a&quot;<br>end</pre><pre># b.ex<br>defmodule B do<br>  def b, do: A.a()<br>end</pre><pre># c.ex<br>defmodule C do<br>  def c, do: B.b()<br>end</pre><pre># d.ex<br>defmodule D do<br>  <a href="http://twitter.com/a">@a</a> A.a()<br>  def d, do: <a href="http://twitter.com/a">@a</a><br>end</pre><p><strong>5. Let’s go round again…<br>Part a)<br></strong>If file b.ex is changed which files will be recompiled?</p><pre># a.ex<br>defmodule A do<br>  def a, do: C.c()<br>end</pre><pre># b.ex<br>defmodule B do<br>  def b, do: A.a()<br>end</pre><pre># c.ex<br>defmodule C do<br>  def c, do: B.b()<br>end</pre><pre># d.ex<br>defmodule D do<br>  <a href="http://twitter.com/a">@a</a> A.a()<br>  def d, do: <a href="http://twitter.com/a">@a</a><br>end</pre><p><strong>Part b)<br></strong>Same question but with the following tweak to the files:</p><pre># a.ex<br>defmodule A do<br>  def a, do: C5.c()<br>  def a2, do: &quot;a&quot;<br>end</pre><pre># b.ex<br>defmodule B do<br>  def b, do: A.a()<br>end</pre><pre># c.ex<br>defmodule C do<br>  def c, do: B.b()<br>end</pre><pre># d.ex<br>defmodule D do<br>  <a href="http://twitter.com/a">@a</a> A.a2()<br>  def d, do: <a href="http://twitter.com/a">@a</a><br>end</pre><p><strong>6. Let’s think about structs</strong></p><pre># a.ex<br>defmodule A do<br>  defstruct [:name, :age]</pre><pre>  def a, do: &quot;a&quot;<br>end</pre><pre>#b.ex<br>defmodule B6 do<br>  def b(%A{name: name}), do: name<br>end</pre><p><strong>Part a) </strong>If the field height were added to the struct defined in a.ex which file(s) would be recompiled?</p><p><strong>Part b) </strong>If A.a() were updated to return &quot;b&quot; then which file(s) would be recompiled?</p><p><strong>7. More structs…</strong></p><p>If the field height were added to the struct defined in a.ex which file(s) would be recompiled?</p><pre># a.ex<br>defmodule A do<br>  defstruct [:name, :age]</pre><pre>  def a, do: &quot;a&quot;<br>end</pre><pre>#b.ex<br>defmodule B6 do<br>  def b(%{name: name}), do: name<br>end</pre><p><strong>8. What about using import?</strong></p><pre># a.ex<br>defmodule A do<br>  def a, do: &quot;a&quot;<br>end</pre><pre>#b.ex<br>defmodule B6 do<br>  import A<br>  def b, do: a()<br>end</pre><p><strong>Part a) </strong>Which files would be recompiled if change A.a() so it takes 1 argument?</p><p><strong>Part b) </strong>Which files would be recompiled if we add a new function foo to the A module?</p><p><strong>Part c) </strong>Which files would be recompiled if we make a return &quot;a2&quot; instead of just returning &quot;a&quot;?</p><p><strong>9. And Macros?</strong></p><pre># a.ex<br>defmodule A9 do<br>  defmacro a(_clause), do: &quot;X&quot;</pre><pre>  def woo, do: &quot;woo&quot;<br>end</pre><pre># b.ex<br>defmodule B9 do<br>  alias A9<br>  require A9<br>  def b, do: A9.a(&quot;some clause&quot;)<br>end</pre><p><strong>Part a) </strong>If we change the implementation of the a macro which files will be recompiled?</p><p><strong>Part b) </strong>If we change the implementation of the woo function which files will be recompiled?</p><h3>Solutions</h3><p>Have you answered all of the above challenges? Check below to see how you got on!</p><ol><li>Only a.ex will be recompiled. b.ex only has a runtime dependency on a.ex, not a compile-time dependency</li><li>a)a.ex and b.ex will both be recompiled because b.ex has a compile-time dependency on a.ex. Remember that any code outside of a method will be called at compile-time.<br>b) Only b.ex will be recompiled — the dependency is only from b to a, not the other way around</li><li>a.ex will be recompiled because it was modified. c.ex will be recompiled because it has a transitive compile-time dependency on C. Remember anything with a compile-time dependency on another file, will also have a transitive compile-time dependency on all the other files runtime dependencies</li><li>Only b.ex will be recompiled, no other files have a compile-time or transitive compile-time dependency on it!</li><li><strong>Part a) </strong>This is a bit of a trick question. What will happen is the compiler will hang indefinitely in an infinite loop. At compile-time module D needs to call A.a() , which calls C.c(), which calls B.b() which calls A.a(), and so on in a never-ending loop.<br><strong>Part b) </strong>Fixes this issue, so in that case b.ex and d.ex will both be recompiled. d.ex will be recompiled because it has a compile-time dependency on a.ex. Because a.ex has a runtime dependency on c.ex, and c.ex depends on b.ex, d.ex therefore has transitive compile-time dependencies on a.ex, b.ex, and c.ex. You may have noticed a, b, and c form a dependency cycle, which is something to avoid if you can!</li><li><strong>Part a) </strong>Both a.ex and b.ex would be recompiled — a.ex because it has been modified. b.ex because it depends on the struct from a.ex and that struct has been modified.<br><strong>Part b) </strong>Only a.ex would be recompiled. Because b.ex has an export dependency (because it depends on the struct from A), it will need to be recompiled only if the struct itself changes. Other details of A can change and not impact B at all</li><li>Only a.ex would be recompiled — as B no longer references the struct from A it doesn’t have any dependencies on A.</li><li><strong>Part a) </strong>a.ex<strong> </strong>and b.ex would both be recompiled. When you use import then if any of the function signatures (i.e. names of functions or the number of arguments) change then <br><strong>Part b) </strong>a.ex<strong> </strong>and b.ex would both be recompiled. Again because we used import any change in the function signatures (including adding a new function) <br><strong>Part c) </strong>Only a.ex would be recompiled because only the implementation changed — the function signatures in A didn’t change (all function names and numbers of arguments remained the same)</li><li><strong>Part a) </strong>b.ex has a compile time dependency on a.ex because it calls the macro from a.ex, which is executed at compile-time<br><strong>Part b) </strong>As with part a, both files will be recompiled. Remember compile-time dependencies are just known about at the file level, so even though we haven’t changed the macro itself, b.ex still needs to be recompiled. Of course the compiled version of b.ex will be identical, but the compiler doesn’t know that</li></ol><h3>What next?</h3><p>In the next final article in this series I’ll share strategies to help improve compile times. Also <a href="https://www.multiverse.io/en-GB/careers">Multiverse is hiring</a> so if you love working on interesting technical challenges and would like to help us build an outstanding alternative to university we’d love to hear from you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f6ff3de5eb5d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-2-test-your-understanding-f6ff3de5eb5d">How to speed up your Elixir compile times (part 2) — test your understanding!</a> was originally published in <a href="https://medium.com/multiverse-tech">multiverse-tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to speed up your Elixir compile times (part 1) — understanding Elixir compilation]]></title>
            <link>https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-1-understanding-elixir-compilation-64d44a32ec6e?source=rss-a8ec0762469b------2</link>
            <guid isPermaLink="false">https://medium.com/p/64d44a32ec6e</guid>
            <category><![CDATA[compilers]]></category>
            <category><![CDATA[phoenix]]></category>
            <category><![CDATA[compilation]]></category>
            <category><![CDATA[elixir]]></category>
            <dc:creator><![CDATA[Tim Gent]]></dc:creator>
            <pubDate>Wed, 17 Aug 2022 08:29:00 GMT</pubDate>
            <atom:updated>2022-09-16T09:01:14.785Z</atom:updated>
            <content:encoded><![CDATA[<h3>How to speed up your Elixir compile times (part 1) — understanding Elixir compilation</h3><p>Struggling with slow Elixir compile times? If you’re working in a large Elixir codebase you may find that the compile times start to suffer. You change just a single file and re-run your test, but it compiles tens of files and takes ages. Part 1 of these posts will explain why this happens. <a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-2-test-your-understanding-f6ff3de5eb5d">Part 2 will help you check your understanding</a>, and <a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-3-strategies-to-improve-your-compile-times-fc0c448b5558">part 3 will provide a practical guide for reducing your incremental compile times</a>.</p><h3>Why does Elixir need to compile so many files when I change just one?</h3><h4>Run-time vs compile time</h4><p>When you write Elixir code some of it will be run at compile-time, and some will be run at runtime. For example if you use module attributes they are evaluated at compile time, and the output is written into the .beam files that the compiler produces. For example this:</p><pre>defmodule Dinner do<br> @dinner_steps [&quot;make food&quot;, &quot;make drinks&quot;]<br> def dinner_steps, do: @dinner_steps<br>end</pre><p>Will be evaluated, so the code that would be at runtime would be just:</p><pre>defmodule Dinner do<br>  def dinner_steps, do: [&quot;make food&quot;, &quot;make drinks&quot;]<br>end</pre><p>Of course this would be a .beam file rather than Elixir code, but you get the point. In this example Dinner doesn’t depend on any other module, so the only time it ever needs to be recompiled is if this file itself is modified.</p><h4>Compile-time dependencies</h4><p>But what if our module attribute depended on another module?</p><pre>defmodule Dinner do<br>  @dinner_steps Food.make() ++ Drinks.make()<br>  def dinner_steps, do: @dinner_steps<br>end</pre><p>Now if the Food or Drinks modules change it could change the .beam file produced for Dinner! Also, this applies even if you change something other than the make function in Food or Drink — the Elixir compiler only looks at file level dependencies and file level changes. So now if we modify Food it will cause both Food and Dinner to be recompiled. We say that Dinner has a compile-time dependency on both Food and Drinks.</p><h4>Transitive compile-time dependencies</h4><p>Now imagine the Food module calls on another module MainCourse . We say that Food has a runtime dependency on MainCourse.</p><pre>defmodule Food do<br>  def make, do: MainCourse.make()<br>end</pre><p>Now if MainCourse changes that may impact what the Dinner module gets compiled to, so Dinner will be recompiled every time MainCourse changes too. We can say Dinner has a transitive compile-time dependency on MainCourse. To summarise the dependency tree now looks like:</p><figure><img alt="Dinner depends on food and drink (compile-time). Food depends on Main Course (runtime). Dinner has a transitive compile-time dependency on Main Course" src="https://cdn-images-1.medium.com/max/1024/1*IoeDU5_8Z1LcFx6fAoNMtw.png" /></figure><p><strong>As you can imagine, the more compile-time or transitive compile-time dependencies a file has, the more likely it will need to be recompiled when other files change. If you have too many then even changing a single file can cause 10s or hundreds of other files to get recompiled, slowing you down.</strong></p><h3>The plot thickens — circular dependencies</h3><p>Now imagine we wrote some code like this in our MainCourse module:</p><pre>defmodule MainCourse do<br>  def make, do: Dinner.microwave(&quot;pot noodle&quot;)<br>end</pre><p>Now MainCourse has a runtime dependency on Dinner and we’ve created a circular dependency. This is bad!</p><figure><img alt="Dinner depends on food and drink (compile-time). Food depends on MainCourse (runtime). Dinner has a transitive compile-time dependency on MainCourse. MainCourse has a runtime dependency on Dinner" src="https://cdn-images-1.medium.com/max/1024/1*Z70nGGPzJ5XSYX7gMWVgJg.png" /></figure><p>What it means is that <strong>every file in the loop has a runtime dependency on every other file in the loop, massively increasing the chances of transitive compile-time dependencies.</strong></p><p>In our example it actually doesn’t have an impact because Dinner already had a transitive compile-time dependency on the other files. But imagine if the loop were 100 files long. Now any file that has a compile-time dependency on any of those files will also have a transitive compile-time dependency on the other 99 files. Overall it increases the number of files needing to be recompiled with each change, which is bad news for your incremental compile times.</p><p>In the example diagram below you can see there is a runtime dependency cycle between A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; A -&gt; ...etc</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1006/1*I13e29j43AaLmDYcYMHzvQ.png" /></figure><p>F has a compile time dependency on D, but because of the dependency cycle it means F also has a transitive compile-time dependency on A, B, C, and E! Now whenever any of those files change, F would also need to be recompiled.</p><h3>How do I know what type my dependencies are?</h3><p>There are actually 3 types of dependencies:</p><ul><li>Compile-time, which happen in 2 main cases:<br>1) When writing any code in the module body — i.e. outside of a function, like our example with module attributes<br>2) When using Macros</li><li>Run-time, which happen for any usual function calls</li><li>Export dependencies — these are compile-time dependencies, but re-compilation is only needed for them when the module API changes (i.e. function args/arities change or a struct definition changes). They occur when you reference a struct from another module, or import the module</li></ul><h3>What next?</h3><p>In the next 2 articles we’ll go through examples to help test your understanding, before finally sharing strategies to help improve compile times. Also <a href="https://www.multiverse.io/en-GB/careers">Multiverse is hiring</a> so if you love working on interesting technical challenges and would like to help us build an outstanding alternative to university we’d love to hear from you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=64d44a32ec6e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/multiverse-tech/how-to-speed-up-your-elixir-compile-times-part-1-understanding-elixir-compilation-64d44a32ec6e">How to speed up your Elixir compile times (part 1) — understanding Elixir compilation</a> was originally published in <a href="https://medium.com/multiverse-tech">multiverse-tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why I Built an Opensource Tool for Big Data Testing and Quality Control]]></title>
            <link>https://medium.com/swlh/why-i-built-an-opensource-tool-for-big-data-testing-and-quality-control-182a14701e8d?source=rss-a8ec0762469b------2</link>
            <guid isPermaLink="false">https://medium.com/p/182a14701e8d</guid>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[data-quality]]></category>
            <category><![CDATA[apache-spark]]></category>
            <category><![CDATA[scala]]></category>
            <category><![CDATA[spark]]></category>
            <dc:creator><![CDATA[Tim Gent]]></dc:creator>
            <pubDate>Tue, 11 Aug 2020 16:07:58 GMT</pubDate>
            <atom:updated>2020-08-23T21:23:16.596Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FWyOxIkH4WNIPGZz.jpg" /></figure><p>I’ve developed an open-source data testing and a quality tool called <a href="https://timgent.github.io/data-flare">data-flare</a>. It aims to help data engineers and data scientists assure the data quality of large datasets using Spark. In this post I’ll share why I wrote this tool, why the existing tools weren’t enough, and how this tool may be helpful to you.</p><h3>Who spends their evenings writing a data quality tool?</h3><p>In every data-driven organisation, we must always recognise that without confidence in the quality of our data, that data is useless. Despite that there are relatively few tools available to help us ensure our data quality stays high.</p><p>What I was looking for was a tool that:</p><ul><li>Helped me write high performance checks on the key properties of my data, like the size of my datasets, the percentage of rows that comply with a condition, or the distinct values in my columns</li><li>Helped me track those key properties over time, so that I can see how my datasets are evolving, and spot problem areas easily</li><li>Enabled me to write more complex checks to check other facets of my data that weren’t simple to incorporate in a property, and enabled me to compare between different datasets</li><li>Would scale to huge volumes of data</li></ul><p>The tools that I found were more limited, constraining me to simpler checks defined in yaml or json, or only letting me check simpler properties on a single dataset. I wrote <a href="https://timgent.github.io/data-flare">data-flare</a> to fill in these gaps, and provide a one-stop-shop for our data quality needs.</p><h3>Show me the code</h3><p><a href="https://timgent.github.io/data-flare">data-flare</a> is a Scala library built on top of Spark. It means you will need to write some Scala, but I’ve tried to keep the interface simple so that even a non-Scala developer could quickly pick it up.</p><p>Let’s look at a simple example. Imagine we have a dataset containing orders, with the following attributes:</p><ul><li>CustomerId</li><li>OrderId</li><li>ItemId</li><li>OrderType</li><li>OrderValue</li></ul><p>We can represent this in a Dataset[Order] in Spark, with our order being:</p><pre>case class Order(customerId: String, orderId: String, itemId: String, orderType: String, orderValue: Int)</pre><h4>Checks on a single dataset</h4><p>We want to check that our orders are all in order, including checking:</p><ul><li>orderType is “Sale” at least 90% of the time</li><li>orderTypes of “Refund” have order values of less than 0</li><li>There are 20 different items that we sell, and we expect orders for each of those</li><li>We have at least 100 orders</li></ul><p>We can do this as follows (here orders represents our Dataset[Order]):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3a4ff22386e5e4d09777cc20e249d53d/href">https://medium.com/media/3a4ff22386e5e4d09777cc20e249d53d/href</a></iframe><p>As you can see from this code, everything starts with a ChecksSuite. You can then pass in all of your checks that operate on single datasets using the singleDsChecks. We’ve been able to do all of these checks using SingleMetricChecks — these are efficient and perform all checks in a single pass over the dataset.</p><p>What if we wanted to do something that we couldn’t easily express with a metric check? Let’s say we wanted to check that no customer had more than 5 orders with an orderType of “Flash Sale”. We could express that with an Arbitrary Check like so:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/25dcf2b29217c182e50392b0c8ad2856/href">https://medium.com/media/25dcf2b29217c182e50392b0c8ad2856/href</a></iframe><p>The ability to define arbitrary checks in this way gives you the power to define any check you want. They won’t be as efficient as the metric based checks, but the flexibility you get can make it a worthwhile trade-off.</p><h4>Checks on a pair of datasets</h4><p>Let’s imagine we have a machine learning algorithm that predicts which item each customer will order next. We are returned another Dataset[Order] with predicted orders in it.</p><p>We may want to compare metrics on our predicted orders with metrics on our original orders. Let’s say that we expect to have an entry in our predicted orders for every customer that has had a previous order. We could check this using Flare as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fbdcce2f2519fad8a28e61b10c266f64/href">https://medium.com/media/fbdcce2f2519fad8a28e61b10c266f64/href</a></iframe><p>We can pass in dualDsChecks to a ChecksSuite. Here we describe the datasets we want to compare, the metrics we want to calculate for each of those datasets, and a MetricComparator which describes how those metrics should be compared. In this case we want the number of distinct customerIds in each dataset to be equal.</p><h4>What happens when you run your checks?</h4><p>When you run your checks all metrics are calculated in a single pass over each dataset, and check results are calculated and returned. You can then decide yourself how to handle those results. For example if one of your checks gives an error you could fail the spark job, or send a failure notification.</p><h3>What else can you do?</h3><ul><li>Store your metrics and check results by passing in a metricsPersister and qcResultsRepository to your ChecksSuite (ElasticSearch supported out the box, and it’s extendable to support any data store)</li><li>Graph metrics over time in Kibana so you can spot trends</li><li>Write arbitrary checks for pairs of datasets</li></ul><p>For more information check out <a href="https://timgent.github.io/data-flare">the documentation</a> and the <a href="https://github.com/timgent/data-flare">code</a>!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=182a14701e8d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/why-i-built-an-opensource-tool-for-big-data-testing-and-quality-control-182a14701e8d">Why I Built an Opensource Tool for Big Data Testing and Quality Control</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vanmoof Electrified X2 Review]]></title>
            <link>https://medium.com/@timgent/vanmoof-electrified-x2-review-35703a6070b5?source=rss-a8ec0762469b------2</link>
            <guid isPermaLink="false">https://medium.com/p/35703a6070b5</guid>
            <category><![CDATA[review]]></category>
            <category><![CDATA[vanmoof]]></category>
            <category><![CDATA[cycling]]></category>
            <category><![CDATA[electric-bicycles]]></category>
            <category><![CDATA[electric-bike]]></category>
            <dc:creator><![CDATA[Tim Gent]]></dc:creator>
            <pubDate>Wed, 21 Aug 2019 18:23:09 GMT</pubDate>
            <atom:updated>2019-08-21T18:44:07.203Z</atom:updated>
            <content:encoded><![CDATA[<p>Which electric bike to buy was a tough decision — I went for the early bird offer on the Vanmoof Electrified X2 and wanted to share some honest feedback.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*spI2_1xloDl2m2TxAiYzJw.jpeg" /></figure><h3>Summary</h3><p>There is a lot to love about the X2, and for some that might be enough. For those who want to be able to put in a decent amount of their own power the gearing will likely be a turn off. For those after a convenient, low effort and speedy commute option it could be just the thing.</p><p>For me — I’m still undecided. I love that I can commute in my work clothes sweat free, saving about 25% of time compared to my touring bike, but the gearing is a constant annoyance. We’ll see if I return it within the 30 day return period..!</p><p><strong>Pros:</strong></p><ul><li>Fantastic ride quality</li><li>Well designed app and bike technology (locking, power control, battery indicator, speedo)</li><li>Use of the boost button makes up for some of the weaker points</li><li>Excellent customer service (London store anyway)</li><li>Built in lights</li></ul><p><strong>Cons:</strong></p><ul><li>2 speed automatic gear box shifts too early, making your cadence very low at times and putting undue pressure on your knees</li><li>Some aspects of design not thought through — no water bottle holder, rear rack not ideal</li></ul><h3>Ride Quality</h3><p>The ride quality on the X2 is superb. Power is smoothly applied, the seating position is comfortable. It’s probably one of the most comfortable bikes I’ve ridden! The boost button is also handy for getting quickly up to speed off the lights, whilst still feeling controlled and natural. I’ve not had anyone overtake me since I got the bike! Also generally it just feels like a well-designed, premium product.</p><h3>The anti-theft technology</h3><p>The X2 comes with Vanmoof’s famous anti-theft technology — a clever integrated lock which will alert you and the Vanmoof team if your bike is stolen. Thankfully I’ve not had to put this into action but certainly from a day to day use perspective it seems well thought through.</p><h3>The gearing</h3><p>The X2 has 2 speed automatic (yep you read that right) hub gearing. While this makes your cadence quite low when starting off, it’s not a big issue as you very quickly can up to a higher speed.</p><p>The HUGE issue with the gearing is that it shifts up to the higher gear at far too low a speed. Just as your cadence starts being reasonable the gear shift hits you. It’s slightly irritating when accelerating on the flat, pretty annoying on a small hill, but is an absolute killer on a big hill. You simply can’t put any power in with your legs without killing your knees, leading to gradual deceleration unless you just hammer the boost button on big hills.</p><p>Once I’m up to 15mph the ride cadence in the higher gear is ok, but from 8/9mph upwards it’s terrible. You can’t configure when it shifts, there is no option to adjust it, and for me it’s a real pity.</p><h3>Other minor points</h3><p><strong>The X2 has built-in lights </strong>— great for a commuter who doesn’t want to worry about constantly removing and replacing lights, running out of batteries, etc.</p><p><strong>The chain guard </strong>— it’s a minor point but genuinely really helpful, especially when commuting in your work clothes. One could be fitted to most other bikes though.</p><p><strong>There are no bosses for attaching a water bottle</strong>. You could probably rig something up but I still feel I shouldn’t have to — this has never been an issue with previous bikes!</p><p><strong>The rear rack is generally ok, but with some niggles</strong>. First off it is quite slim, though for most people that would be ok providing you don’t want to strap large boxes down on it. The bigger issue is my Ortlieb panniers are in danger of hitting the locking button on the wheel (in the shop they advised putting my pannier on the other side!), which would be rather terrifying. However their engineers did offer to fit a bracket to stop that happening, an offer which I’ve not yet taken them up on.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=35703a6070b5" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Serialization challenges with Spark and Scala — Part 2— Now for something really challenging…]]></title>
            <link>https://medium.com/onzo-tech/serialization-challenges-with-spark-and-scala-part-2-now-for-something-really-challenging-bd0f391bd142?source=rss-a8ec0762469b------2</link>
            <guid isPermaLink="false">https://medium.com/p/bd0f391bd142</guid>
            <category><![CDATA[apache-spark]]></category>
            <category><![CDATA[big-data]]></category>
            <category><![CDATA[spark]]></category>
            <category><![CDATA[scala]]></category>
            <dc:creator><![CDATA[Tim Gent]]></dc:creator>
            <pubDate>Wed, 19 Sep 2018 12:13:03 GMT</pubDate>
            <atom:updated>2018-09-19T12:13:03.956Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*76KgfJNpKDSxUzCIFs9F5w.jpeg" /></figure><p>Following on from <a href="https://medium.com/onzo-tech/serialization-challenges-with-spark-and-scala-a2287cd51c54">the introductory post on serialization with spark</a>, this post gets right into the thick of it with a tricky example of serialization with Spark. I highly recommend attempting to get this working yourself first, you’ll learn a lot!</p><p>Each example steps through some ways you may try to debug the problems, eventually resulting in a working solution. Once again the code samples can be found on <a href="https://github.com/onzo-com/spark-demo/">ONZO’s Github</a>, and the numbering on this article should match up with the code there :)</p><h4>9 — base example</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1bea9734ac1d8a0da6d7fa9d6ca63939/href">https://medium.com/media/1bea9734ac1d8a0da6d7fa9d6ca63939/href</a></iframe><p><strong><em>**FAILS**</em></strong></p><p>Now for some practice! This example is relatively complex and needs a few changes to work successfully. Can you figure out what they are? Kudos if so! The next few examples walk through a solution step by step, and some things you may try.</p><h4>10 — make classes serializable</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7b62074e76c1a5000f0b3f829ea34176/href">https://medium.com/media/7b62074e76c1a5000f0b3f829ea34176/href</a></iframe><p><strong><em>**FAILS**</em></strong></p><p>One approach to serialization issues can be to make everything Serializable. However in this case you will find it doesn’t solve the issue. You’ll find it easier (but not that easy..!) to spot why if you look at the complete examples. It’s because when trying to serialize the classes it will find references to testRddand also the shouldBe method. This will trigger serialization of the test class (<a href="https://github.com/onzo-com/spark-demo/blob/master/src/test/scala/sparky/SparkSerializationSpec.scala#L116">you can see the full code in Github</a>) that contains these, and the test class is not serializable.</p><h4>11a — use anon function</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e14ede752c9e8307897a85ea8dac997e/href">https://medium.com/media/e14ede752c9e8307897a85ea8dac997e/href</a></iframe><p><strong><em>**FAILS**</em></strong></p><p>In order to debug this you might try simplifying things by replacing the WithFunction class with a simple anonymous function. However in this case we still have an a failure, can you spot the issue now?</p><h4>11b — use anon function, with enclosing</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4f95ae214f881cc49a10d575dddf99e1/href">https://medium.com/media/4f95ae214f881cc49a10d575dddf99e1/href</a></iframe><p><strong><em>**PASSES**</em></strong></p><p>Did you spot it? By enclosing the reduceInts method the map function can now access everything it needs in that one closure, no need to serialize the other classes!</p><h4>12a — use function with def</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5f451185ce4db000a3af0d52b68da2d6/href">https://medium.com/media/5f451185ce4db000a3af0d52b68da2d6/href</a></iframe><p><strong><em>**FAILS** <br></em></strong>Taking small steps, we now replace the anonymous function with a function declared with a def. Again you will find this fails, but seeing why isn’t easy. It is because of the intricacies of how def works. Essentially a method defined with def contains an implicit reference to this, which in this case is an object<br>which can’t be serialized. You can find out more about <a href="https://alvinalexander.com/scala/fp-book-diffs-val-def-scala-functions">the differences between def and val here</a>.</p><h4>12b — use function with val</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ac0fbbe0febb65a67f4a73ffef0d5a25/href">https://medium.com/media/ac0fbbe0febb65a67f4a73ffef0d5a25/href</a></iframe><p><strong>**PASSES</strong>**<br>Declaring the method with val works. A val method equates to a Function1 object, which is serializable, and doesn’t contain an implicit reference to this, stopping the attempted serialization of the Example object.</p><h4>12c — use function with val explained part 1</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cb346d8bf13b8d67bacbbf90b7267f21/href">https://medium.com/media/cb346d8bf13b8d67bacbbf90b7267f21/href</a></iframe><p><strong>**FAILS**</strong><br>This example serves to illustrate the point more clearly. Here the addOnefunction references the one value. As we saw earlier this will cause the whole Exampleobject to be serialized, which will fail.</p><p><strong>**BONUS POINTS**</strong> <br>One helpful experiment to try here is to resolve this by making the Exampleobject serializable.<br>You will note that you still get a serialization error. Can you see why? There are actually 2 reasons:<br>1) testRddis referenced inside the WithSparkMap class, leading to the whole Spec trying to be serialized (please see Github link for full code which will explain this more!)<br>2) The shouldBemethod is also referenced, again leading to the whole Spec trying to be serialized</p><h4>12d —use function with val explained part 2</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0a21d8cffbf2757d092c983ac25d6e90/href">https://medium.com/media/0a21d8cffbf2757d092c983ac25d6e90/href</a></iframe><p><strong><em>**PASSES** <br></em></strong>As above, the best way to fix the issue is to reference values only in the more immediate scope. Here we have added oneEnc, which prevents the serialization of the whole Exampleobject.</p><h4>13 — back to the problem, no class params</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e6d0b6781778cf6c076eff2bf28840e0/href">https://medium.com/media/e6d0b6781778cf6c076eff2bf28840e0/href</a></iframe><p><strong><em>**PASSES** </em></strong><br>Coming back from the issue we originally had, now we understand a little more let&#39;s introduce our WithFunction class back in. To simplify things we&#39;ve taken out the constructor parameter here. We&#39;re also using a val for the method rather than a def. No serialization issues now!</p><h4>14 — back to the problem, with class params</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a35d541c20b9af53f91886b02c3a9afd/href">https://medium.com/media/a35d541c20b9af53f91886b02c3a9afd/href</a></iframe><p><strong><em>**FAILS**<br></em></strong>We’ve now added back in the class params. Can you spot why this fails? The plusOnefunction references num, outside of the immediate scope,<br>again causing more objects to be serialized which is failing.</p><h4>15a — back to the problem, with class params, and enclosing</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d3d98a78eaf01ca7befc15a69cbc5887/href">https://medium.com/media/d3d98a78eaf01ca7befc15a69cbc5887/href</a></iframe><p><strong><em>**PASSES**<br></em></strong>This is now a simple fix, and we can enclose the num value with encNum which resolves the last of our serialization issues. Finally, this is a complete working example that is equivalent to our first implementation that failed!</p><h4>15b — adding some complexity e.g.7b — testing understanding</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f913e765f13910b2070f3f676283ea23/href">https://medium.com/media/f913e765f13910b2070f3f676283ea23/href</a></iframe><p><strong><em>**FAILS** <br></em></strong>One more failing example! Can you see why the above fails?</p><p>The issue is that encNumwon’t be evaluated until plusOne is actually called, effectively within the map function. At this point then the numvalue will need to be accessed, causing additional serialization of the containing object and the failure here.</p><h3>Conclusion</h3><p>Hopefully these examples have made a little clearer how serialization of functions works with Spark and Scala, good luck with your Spark serialization challenges!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd0f391bd142" width="1" height="1" alt=""><hr><p><a href="https://medium.com/onzo-tech/serialization-challenges-with-spark-and-scala-part-2-now-for-something-really-challenging-bd0f391bd142">Serialization challenges with Spark and Scala — Part 2— Now for something really challenging…</a> was originally published in <a href="https://medium.com/onzo-tech">ONZO Technology</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Serialization challenges with Spark and Scala]]></title>
            <link>https://medium.com/onzo-tech/serialization-challenges-with-spark-and-scala-a2287cd51c54?source=rss-a8ec0762469b------2</link>
            <guid isPermaLink="false">https://medium.com/p/a2287cd51c54</guid>
            <category><![CDATA[apache-spark]]></category>
            <category><![CDATA[scala]]></category>
            <category><![CDATA[spark]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[big-data]]></category>
            <dc:creator><![CDATA[Tim Gent]]></dc:creator>
            <pubDate>Wed, 05 Sep 2018 10:44:10 GMT</pubDate>
            <atom:updated>2018-09-19T12:23:08.215Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*arBqq7O7umskV4O7JjhdrA.jpeg" /></figure><p><a href="https://spark.apache.org/">Apache Spark</a> is a great tool for high performance, high volume data analytics. When working with Spark and Scala you will often find that your objects will need to be serialized so they can be sent to the Spark worker nodes. Whilst the rules for serialization seem fairly simple, interpreting them in a complex code base can be less than straightforward! If you get things wrong then far more than you intended can end up being Serialized, and this can easily lead to run time exceptions where the objects aren’t serializable.</p><p>This post will talk through a number of motivating examples to help explain what will be serialized and why. There will shortly be a follow up post to work through a much more complex example too if you would like a challenge!</p><h3>Serialization Rules</h3><p>Before we get into examples let’s explore the basic rules around serialization with respect to Spark code.</p><h4>When will objects need to be Serialized?</h4><p>When you perform a function on an RDD (Spark’s Resilient Distributed Dataset), or on anything that is an abstraction on top of this (e.g. Dataframes, Datasets), it is common that this function will need to be serialized so it can be sent to each worker node to execute on its segment of the data.</p><p><strong>What gets Serialized?</strong></p><p>The rules for what is Serialized are the same as in Java more generally — only objects can be serialized.</p><p>The function being passed to <strong><em>map</em></strong> (or similar Spark RDD function) itself will need to be Serialized (note this function is itself an object). If references to other objects are made within this function then those objects will also need to be serialized. The whole of these objects will be serialized, even when accessing just one of their fields.</p><h3>Examples</h3><p>Examples including code and explanations follow, though I strongly encourage you to try running the examples yourself and trying to figure out why each one works or doesn’t work — you’ll learn much more this way! All the examples along with explanations can be found on <a href="https://github.com/onzo-com/spark-demo/">ONZO’s Github here</a>.</p><p>For each of these examples assume we have a <strong>testRdd </strong>containing Integers.</p><pre><strong>val</strong> testRdd<strong>:</strong> RDD[Int]</pre><h3>Basic(ish) Examples</h3><p>We’ll start with some basic examples that draw out the key principles of Serialization in Spark.</p><h4>1 — basic spark map</h4><pre><strong>object</strong> Example {<br><strong>  def</strong> myFunc <strong>=</strong> testRdd.map(<strong>_</strong> + 1)<br>}</pre><p><strong><em>**PASSES**</em></strong></p><p>A very simple example — in this case the only thing that will be serialized is a Function1 object which has an apply method that adds 1 to it’s input. The Example object won’t be serialized.</p><h4>2 — spark map with external variable</h4><pre><strong>object</strong> Example {<br>  <strong>val</strong> num <strong>=</strong> 1<br>  <strong>def</strong> myFunc <strong>=</strong> testRdd.map(<strong>_</strong> + num)<br>}</pre><p><strong><em>**FAILS**</em></strong></p><p>Very similar to the above, but this time within our anonymous function we’re accessing the <strong><em>num </em></strong>value. Therefore the whole of the containing Example object will need to be serialized, which will actually fail because it isn’t serializable.</p><h4>3 — spark map with external variable — the first way to fix it</h4><pre><strong>object</strong> Example <strong>extends</strong> Serializable {<br>  <strong>val</strong> num <strong>=</strong> 1 <br>  <strong>def</strong> myFunc <strong>=</strong> testRdd.map(<strong>_</strong> + num)<br>}</pre><p><strong><em>**PASSES**</em></strong></p><p>One solution people often jump to is to make the object in question Serializable. It works, but may not be desirable as ideally we want to be serializing as little as possible.</p><h4>4 — spark map with external variable — a flawed way to fix it</h4><pre><strong>object</strong> Example {<br>  <strong>val</strong> num <strong>=</strong> 1<br>  <strong>def</strong> myFunc <strong>=</strong> {<br>    <strong>lazy</strong> <strong>val</strong> enclosedNum <strong>=</strong> num<br>    testRdd.map(<strong>_</strong> + enclosedNum)<br>  }<br>}</pre><p><strong><em>**FAILS**</em></strong></p><p>In this case we create an enclosedNum value inside the scope of myFunc — when this is referenced it should stop trying to serialize the whole object because it can access everything required the scope of myFunc. However because enclosedNum is a lazy val this still won’t work, as it still requires knowledge of num and hence will still try to serialize the whole of the Example object.</p><h4>5 — spark map with external variable — properly fixed!</h4><pre><strong>object</strong> Example {<br>  <strong>val</strong> num <strong>=</strong> 1<br>  <strong>def</strong> myFunc <strong>=</strong> {<br>    <strong>val</strong> enclosedNum <strong>=</strong> num<br>    testRdd.map(<strong>_</strong> + enclosedNum)<br>  }<br>}</pre><p><strong><em>**PASSES**</em></strong></p><p>Similar to the previous example, but this time with enclosedNum being a val, which fixes the previous issue.</p><h3>Upping the difficulty — examples with nested objects</h3><p>The same principles apply in the following examples, just with the added complexity of a nested object.</p><h4>6 — nested objects, a simple example</h4><pre><strong>object</strong> Example {<br>  <strong>val</strong> outerNum <strong>=</strong> 1<br>  <strong>object</strong> NestedExample <strong>extends</strong> Serializable {<br>    <strong>val</strong> innerNum <strong>=</strong> 10<br>    <strong>def</strong> myFunc <strong>=</strong> testRdd.map(<strong>_</strong> + innerNum)<br>  }<br>}</pre><p><strong><em>**PASSES**</em></strong></p><p>A slightly more complex example but with the same principles. Here innerNum is being referenced by the map function. This triggers serialization of the whole of the NestedExample object. However this is fine because it extends Serializable. You could use the same enclosing trick as before to stop the serialization of the NestedExample object too.</p><h4>7 — nested objects gone wrong</h4><pre><strong>object</strong> Example {<br>  <strong>val</strong> outerNum <strong>=</strong> 1<br>  <strong>object</strong> NestedExample <strong>extends</strong> Serializable {<br>    <strong>val</strong> innerNum <strong>=</strong> 10<br>    <strong>def</strong> myFunc <strong>=</strong> testRdd.map(<strong>_</strong> + outerNum)<br>  }<br>}</pre><p><strong>**FAILS**<br></strong>In this case outerNum is being referenced inside the map function. This means the whole Example object would have to be serialized, which will fail as it isn&#39;t Serializable.</p><h4>8 — nested objects, using enclosing in the inner object</h4><pre><strong>object</strong> Example {<br>  <strong>val</strong> outerNum <strong>=</strong> 1<br>  <strong>object</strong> NestedExample <strong>extends</strong> Serializable {<br>    <strong>val</strong> innerNum <strong>=</strong> 10<br>    <strong>val</strong> encOuterNum <strong>=</strong> outerNum<br>    <strong>def</strong> myFunc <strong>=</strong> testRdd.map(<strong>_</strong> + encOuterNum)<br>  }<br>}</pre><p><strong><em>**PASSES**</em></strong></p><p>In this example we have fixed the previous issue by providing encOuterNum. Now the map references only values in the NestedExample object, which can be serialized.</p><h3>What’s next?</h3><p>Stay tuned for the next post which will walk through a much more complex example, truly testing your understanding of serialization in Spark.</p><p><a href="https://medium.com/onzo-tech/serialization-challenges-with-spark-and-scala-part-2-now-for-something-really-challenging-bd0f391bd142">New post now available here!</a><br><a href="https://medium.com/onzo-tech/serialization-challenges-with-spark-and-scala-part-2-now-for-something-really-challenging-bd0f391bd142">https://medium.com/onzo-tech/serialization-challenges-with-spark-and-scala-part-2-now-for-something-really-challenging-bd0f391bd142</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a2287cd51c54" width="1" height="1" alt=""><hr><p><a href="https://medium.com/onzo-tech/serialization-challenges-with-spark-and-scala-a2287cd51c54">Serialization challenges with Spark and Scala</a> was originally published in <a href="https://medium.com/onzo-tech">ONZO Technology</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>