<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <atom:link href="https://distantprovince.by/rss.xml" rel="self" type="application/rss+xml" />
  <title>All Posts</title>
  <link>https://distantprovince.by</link>
  <description>Feed of all posts</description>
  <language>en-us</language>
  <generator>Tableau v0.30.0</generator>
    <item>
       <title>Concurrent Reads, Serialized Writes with GenServer and Registry</title>
       <link>https://distantprovince.by/posts/concurrent-reads-serialized-writes-with-genserver-and-registry/</link>
       <pubDate>Wed, 13 May 2026 11:00:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/concurrent-reads-serialized-writes-with-genserver-and-registry/</guid>
       <description><![CDATA[ <h1><a href="#concurrent-reads-serialized-writes-with-genserver-and-registry" aria-hidden="true" class="anchor" id="concurrent-reads-serialized-writes-with-genserver-and-registry"></a>Concurrent Reads, Serialized Writes with GenServer and Registry</h1>
<p>Of all the OTP abstractions, GenServer is the most iconic one. As a result, it is
usually the first thing newcomers learn to use, and they inevitably run into some
common pitfalls. Let's talk about one of them.</p>
<p>GenServers are commonly used whenever there is a need for a stateful process
that will store some data and make it available to other processes. As an
example, let's imagine we want to fetch currency conversion rates and make them
available in our app. A naive approach could look something like this:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #953800;">Mix</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">install</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">[</span><span style="color: #0550ae;">:req</span><span style="color: #1f2328;">]</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">FXRates</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="4">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">arg</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">name</span> <span style="color: #0550ae;">\\</span> <span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="7">    <span style="color: #953800;">GenServer</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">arg</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">name: </span><span style="color: #1f2328;">name</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="8">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">get_rates</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">name_or_pid</span> <span style="color: #0550ae;">\\</span> <span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="11">    <span style="color: #953800;">GenServer</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">call</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">name_or_pid</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:get_rates</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="12">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="15">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">init</span><span style="color: #1f2328;">(</span><span style="color: #57606a;">_init_arg</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="16">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">[</span><span style="color: #1f2328;">]</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:continue</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:update_rates</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="17">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="18">
</div><div class="line" data-line="19">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="20">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">handle_continue</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:update_rates</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">_state</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="21">    <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">status: </span><span style="color: #0550ae;">200</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">body: </span><span style="color: #1f2328;">rates</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Req</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get!</span><span style="color: #1f2328;">(</span>
</div><div class="line" data-line="22">      <span style="color: #0a3069;">&quot;https://api.frankfurter.dev/v1/latest&quot;</span>
</div><div class="line" data-line="23">    <span style="color: #1f2328;">)</span>
</div><div class="line" data-line="24">
</div><div class="line" data-line="25">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">send_after</span><span style="color: #1f2328;">(</span><span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:refresh_rates</span><span style="color: #1f2328;">,</span> <span style="color: #6639ba;">to_timeout</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">minute: </span><span style="color: #0550ae;">1</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="26">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:noreply</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">rates</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="27">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="30">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">handle_info</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:refresh_rates</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="31">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:noreply</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:continue</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:update_rates</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="32">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="33">
</div><div class="line" data-line="34">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="35">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">handle_call</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:get_rates</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">_from</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="36">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:reply</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="37">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="38"><span style="color: #cf222e;">end</span>
</div><div class="line" data-line="39">
</div><div class="line" data-line="40"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">Main</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="41">  <span style="color: #cf222e;">def</span> <span style="color: #1f2328;">main</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="42">    <span style="color: #1f2328;">children</span> <span style="color: #0550ae;">=</span> <span style="color: #1f2328;">[</span>
</div><div class="line" data-line="43">      <span style="color: #953800;">FXRates</span>
</div><div class="line" data-line="44">    <span style="color: #1f2328;">]</span>
</div><div class="line" data-line="45">
</div><div class="line" data-line="46">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">_</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Supervisor</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">children</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">strategy: </span><span style="color: #0550ae;">:one_for_one</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="47">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="48"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>For convenience, let's put it into a single <code>genserver.exs</code> and play with it
using <code>iex -r genserver.exs</code>. Indeed, we can start the supervisor and call
<code>FXRates.get_rates()</code> to get the latest rates:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #6639ba;">iex</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">1</span><span style="color: #1f2328;">)</span><span style="color: #0550ae;">&gt;</span> <span style="color: #953800;">Main</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">main</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="2"><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">#PID&lt;0.249.0&gt;&rbrace;</span>
</div><div class="line" data-line="3"><span style="color: #6639ba;">iex</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">2</span><span style="color: #1f2328;">)</span><span style="color: #0550ae;">&gt;</span> <span style="color: #953800;">FXRates</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get_rates</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="4"><span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="5">  <span style="color: #0a3069;">&quot;amount&quot;</span> <span style="color: #0550ae;">=&gt;</span> <span style="color: #0550ae;">1.0</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="6">  <span style="color: #0a3069;">&quot;base&quot;</span> <span style="color: #0550ae;">=&gt;</span> <span style="color: #0a3069;">&quot;EUR&quot;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="7">  <span style="color: #1f2328;">...</span>
</div><div class="line" data-line="8"><span style="color: #1f2328;">&rbrace;</span>
</div></code></pre>
<p>Now, if you submit this for code review to any experienced engineer, they will
<em>immediately</em> point out that using <code>GenServer.call</code> for <em>reading</em> data is not
good. GenServer is a natural serialization point and concurrent callers will
have to form a line to get their FX rates. Not to mention that reads will block
the GenServer and might mess with its refresh timer.</p>
<p>But how do we make reads concurrent? Well, we just need to put rates somewhere
where they can be accessed concurrently.
<a href="https://hexdocs.pm/elixir/1.19.5/erlang-libraries.html#erlang-term-storage-ets">ETS</a>
will do. But the thing with raw ETS is that you need to manage the table's
lifecycle. And it also comes with a certain style of developer experience that
Erlang modules have. What if I told you there is a way to use ETS without using ETS?</p>
<figure>
<pre>
┌─────────────┐
│┌───────────┐│
││    The    ││
││ GenServer ││
││  is [IN]  ││
││           ││
││ ●         ││
││           ││   ╭──────╮ ╭───────╮ ╭──────╮ ╭───────╮ ╭──────╮
││           ││   │ read │ │ write │ │ read │ │ write │ │ read │
││           ││   ╰──────╯ ╰───────╯ ╰──────╯ ╰───────╯ ╰──────╯
│└───────────┘│
└─────────────┘
</pre>
<figcaption>
The GenServer will see you soon
</figcaption>
</figure>
<h2><a href="#meet-registry" aria-hidden="true" class="anchor" id="meet-registry"></a>Meet Registry</h2>
<p><a href="https://hexdocs.pm/elixir/Registry.html">Registry</a> is a way to register
processes. But that's not all! It also allows those registrered processes to
store arbitrary data. So, why don't we register our genserver in Registry and
put FXRates in it?</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #953800;">Mix</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">install</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">[</span><span style="color: #0550ae;">:req</span><span style="color: #1f2328;">]</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">FXRates</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="4">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #57606a;">_arg</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">name</span> <span style="color: #0550ae;">\\</span> <span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="7">    <span style="color: #1f2328;">via_tuple</span> <span style="color: #0550ae;">=</span> <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:via</span><span style="color: #1f2328;">,</span> <span style="color: #953800;">Registry</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">&lbrace;</span><span style="color: #953800;">FXRatesRegistry</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">name</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">rates: </span><span style="color: #1f2328;">[</span><span style="color: #1f2328;">]</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="8">    <span style="color: #953800;">GenServer</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">name</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">name: </span><span style="color: #1f2328;">via_tuple</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="9">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="10">
</div><div class="line" data-line="11">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">get_rates</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">name</span> <span style="color: #0550ae;">\\</span> <span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="12">    <span style="color: #1f2328;">[</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #57606a;">_pid</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">rates: </span><span style="color: #1f2328;">rates</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">]</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Registry</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">lookup</span><span style="color: #1f2328;">(</span><span style="color: #953800;">FXRatesRegistry</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">name</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="13">    <span style="color: #1f2328;">rates</span>
</div><div class="line" data-line="14">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="15">
</div><div class="line" data-line="16">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="17">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">init</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">name</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="18">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">name: </span><span style="color: #1f2328;">name</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">rates: </span><span style="color: #1f2328;">[</span><span style="color: #1f2328;">]</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:continue</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:update_rates</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="19">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="22">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">handle_continue</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:update_rates</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="23">    <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">status: </span><span style="color: #0550ae;">200</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">body: </span><span style="color: #1f2328;">rates</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Req</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get!</span><span style="color: #1f2328;">(</span>
</div><div class="line" data-line="24">      <span style="color: #0a3069;">&quot;https://api.frankfurter.dev/v1/latest&quot;</span>
</div><div class="line" data-line="25">    <span style="color: #1f2328;">)</span>
</div><div class="line" data-line="26">
</div><div class="line" data-line="27">    <span style="color: #953800;">Registry</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">update_value</span><span style="color: #1f2328;">(</span><span style="color: #953800;">FXRatesRegistry</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #0550ae;">.</span><span style="color: #1f2328;">name</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #57606a;">_current</span> <span style="color: #0550ae;">-&gt;</span>
</div><div class="line" data-line="28">      <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">rates: </span><span style="color: #1f2328;">rates</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="29">    <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="30">
</div><div class="line" data-line="31">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">send_after</span><span style="color: #1f2328;">(</span><span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:refresh_rates</span><span style="color: #1f2328;">,</span> <span style="color: #6639ba;">to_timeout</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">minute: </span><span style="color: #0550ae;">1</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="32">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:noreply</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">state</span> <span style="color: #0550ae;">|</span> <span style="color: #0550ae;">rates: </span><span style="color: #1f2328;">rates</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="33">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="34">
</div><div class="line" data-line="35">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="36">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">handle_info</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:refresh_rates</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="37">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:noreply</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">state</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:continue</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:update_rates</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="38">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="39"><span style="color: #cf222e;">end</span>
</div><div class="line" data-line="40">
</div><div class="line" data-line="41"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">Main</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="42">  <span style="color: #cf222e;">def</span> <span style="color: #1f2328;">main</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="43">    <span style="color: #1f2328;">children</span> <span style="color: #0550ae;">=</span> <span style="color: #1f2328;">[</span>
</div><div class="line" data-line="44">      <span style="color: #1f2328;">&lbrace;</span><span style="color: #953800;">Registry</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">keys: </span><span style="color: #0550ae;">:unique</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">name: </span><span style="color: #953800;">FXRatesRegistry</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="45">      <span style="color: #953800;">FXRates</span>
</div><div class="line" data-line="46">    <span style="color: #1f2328;">]</span>
</div><div class="line" data-line="47">
</div><div class="line" data-line="48">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">_</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Supervisor</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">children</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">strategy: </span><span style="color: #0550ae;">:one_for_one</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="49">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="50"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>There are a couple of changes here. First of all, we use a <a href="https://hexdocs.pm/elixir/Registry.html#module-using-in-via">via
tuple</a> to register
the GenServer. We also save <code>name</code> in the state: it acts as a Registry key, and
we need to know it in order to update the data associated with our server. Apart
from that, <code>get_rates</code> is not a GenServer call anymore! Try it in the IEx to
confirm it still works.</p>
<p>Ok, but where are our rates stored now? In ETS, of course! Registry uses ETS under the
hood, so the <code>Registry.lookup</code> call is just an ETS read. We made our reads
concurrent without having to manage ETS table lifecycle ourselves.</p>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>Registry is an amazing tool and is a joy to work with. I reach for it every
time I want to use a GenServer for serializing writes while keeping data
available for concurrent reads. Give it a try!</p> ]]></description>
    </item>
    <item>
       <title>A Farewell to Code Reviews</title>
       <link>https://distantprovince.by/posts/a-farewell-to-code-reviews/</link>
       <pubDate>Sat, 28 Feb 2026 12:00:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/a-farewell-to-code-reviews/</guid>
       <description><![CDATA[ <h1><a href="#a-farewell-to-code-reviews" aria-hidden="true" class="anchor" id="a-farewell-to-code-reviews"></a>A Farewell to Code Reviews</h1>
<p>Code reviews are dead and only now do we begin to notice. Hardly the
only or most important casualty of the great AI leap, but an important one
nonetheless.</p>
<p>For quite some time we had a good thing going and I will remember it. Maybe you
remember it too or maybe you never experienced the tech industry before 2025 and
want to know what it was like. Let me tell you how it was.</p>
<h2><a href="#the-author" aria-hidden="true" class="anchor" id="the-author"></a>The Author</h2>
<p>Code review is many things. On its mechanical, bodily level, it is an act of
reading through a diff. A pull request. A unit of change that is asking for
permission to enter the repository. It is a shared space and it must be kept in
order. So you need to show respect.</p>
<p>A pull request should be presentable. You have to think about it long before you
push your branch. It can't be too big – nobody likes that. It should have a
description – this is where you provide additional context and explain anything
that can't be easily inferred from the code. This is also the final destination
for future code archeologists who will smell your blood in git blame and judge
if your decisions stood the test of time.</p>
<p>So make it nice. Some pre-emptive comments are always appreciated, although
they might be used against you as evidence of code not being clear enough. &quot;I
see you felt the need to clarify this piece of code, interesting,&quot; some
reviewers might say.</p>
<p>Usually, there is also CI. A set of automated checks that run against your PR.
Compile, lint, test. The first line of defense. You'd better make sure the build
is green before assigning reviewers or you'll have to come up with sorry
excuses. &quot;Huh, CI is failing, I'll look into it later.&quot; Sloppy job and an
easy way to part ways with some street cred. Unless the tests are flaky, of
course, in which case you have a choice to rise to the occasion and fix them, or
meld into collective inaction. Some people might remember what you chose.</p>
<p>When all rituals are complete, it's time to assign reviewers. This, too, can be
delegated to a machine! Can be a single very important person, somebody with
authority. You can bet they are too busy these days to write code themselves.
Alternatively, a reviewer can be selected at random. Roll a die and let fate
decide. Might even add some round robin rules on top, to spread the burden more
evenly. What a terrible way to pick a reviewer!</p>
<p>My personal favorite is selecting reviewers myself. Some people assign the whole
team! Rookie mistake. A sure way to never get a review. No, you need to pick a
single person. One victim. Once you lock eyes, they will feel the burden <em>and</em>
the honor in its entirety. No escape from the duel.</p>
<h2><a href="#the-reviewer" aria-hidden="true" class="anchor" id="the-reviewer"></a>The Reviewer</h2>
<p>For the reviewer, the diff is a net loss. A nuisance. A necessary, but
fundamentally secondary activity. Congratulations, you're on gatekeeping duty!
Time to put off your actual work and load a bunch of completely new context into
your head. What torture! I just hope there is at least a description.</p>
<p>To be a reviewer is to be tempted. &quot;Cut corners, skim through, LGTM&quot; – you hear a
voice inside. Hm... No, no, you can't. It's your job. And there might be
consequences! If the code is bad, it poisons everything around it. One broken
window will lead to another. Toxins accumulate in the body. You need to be
strong. And that would be disrespectful, wouldn't it? The author put in the effort,
after all. Let's see.</p>
<p>A good reviewer starts from the top. What is this change for? What is it trying to
achieve? Is this the right thing? If so, is this the right approach? First
comments can land before a single line of code is even <em>read</em>. Those are brutal,
but most valuable.</p>
<p>Then, if everything looks good, time to look at the code. Don't look too closely
though, we're not quite there yet. Just the big picture. This is what it is now,
but <em>what could it be</em>? Maybe there's a better way to compose functions? A
simpler model? How would <em>you</em> do this? A costly mental exercise, but a good
author will appreciate it.</p>
<p>Finally, the trivial part. Just read the code, poke holes in the solution. This
is a mixed bag of cute little TILs and nits, variable names and code golf. A
token effort, but most common.</p>
<figure>
    <img src="/preview/farewell-to-code-reviews.jpg">
<figcaption>
    a couple of nits but otherwise lgtm
</figcaption>
</figure>
<h2><a href="#the-ritual" aria-hidden="true" class="anchor" id="the-ritual"></a>The Ritual</h2>
<p>Code review is many things. It is a form of asynchronous communication. An
avenue for knowledge sharing. A learning opportunity. A safety net.</p>
<p>It is also a battleground. And an asymmetric one! One side is defending, another
attacking. Things can get heated. You need to be careful with that. Wrap
comments in questions. Assume good faith and make self-deprecating jokes. Attach
GIFs. Use objective measures when possible – it's often hard to agree on what is
&quot;simpler&quot; or &quot;more declarative&quot;. And never invoke your authority directly. That's in very bad taste.</p>
<p>At times, code reviews were proving grounds for people who just joined the
team and are going through the inevitable period of friction. A &quot;storming&quot; phase,
if you will. Code reviews turn into a ritual dance of testing the new guy and
explaining to them our unspoken rules. A fresh pair of eyes is often quick to spot
weaknesses, but you'd be foolish to point at them right away. A newcomer needs
to be respectful and first demonstrate that they can learn existing ways and
contribute on par with the rest of the team. Only then can you voice your
concerns and propose changes. A lot of teams are aware of this, of course, and
do their best to hold an egalitarian stance and not be defensive. But there's
only so much success you can have against a primal force like this, so it's wise
to always respect those who came before you.</p>
<p>For me, personally, code reviews were a place of exchange. As an author, I bring
the product of my labor for other people to witness, hoping that all the effort
and love I put into it will be seen and appreciated. As a reviewer, I am looking
for the signs of this effort and in return, pay my respects to the author
through thoughtful comments. Tomorrow, we will switch roles and the exchange
will repeat.</p>
<h2><a href="#no-going-back" aria-hidden="true" class="anchor" id="no-going-back"></a>No going back</h2>
<p>Nothing about code reviews survived the AI transition. The code is not the
product of human labor anymore, not in the same sense at least. The diffs
aren't read with the same scrutiny. Some even argue the code isn't for human
eyes at all, and thus readability doesn't matter. No point in sophisticated
arguments and thoughtful comments: a coding agent will agree with anything you say
and will not learn a thing. And even if <em>you</em> choose to code by hand, you will
likely be met by an AI reviewer.</p>
<p>I know there is no going back. Even if some teams still review code the old
ways, those are merely uncontacted tribes of the AI age. LLMs invaded the spaces
previously inhabited exclusively by humans. They are omnipresent in every PR,
every comment and every diagram. Even a team of devoted tradcoders won't feel
the same, because they know what they are running from.</p>
<p>I don't know what will replace code reviews or how they will evolve, but I feel
like we lost something valuable. I know I have. And I'm mourning.</p> ]]></description>
    </item>
    <item>
       <title>AI Future Will Be Nothing Like Present</title>
       <link>https://distantprovince.by/posts/ai-future-will-be-nothing-like-present/</link>
       <pubDate>Tue, 20 Jan 2026 21:00:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/ai-future-will-be-nothing-like-present/</guid>
       <description><![CDATA[ <h1><a href="#ai-future-will-be-nothing-like-present" aria-hidden="true" class="anchor" id="ai-future-will-be-nothing-like-present"></a>AI future will be nothing like present</h1>
<p>There is a very popular opinion that coding agents just make senior devs more
productive. AI is just a tool that allows you to off-load the boring part of
writing code to the agent so you can focus on real engineering™. Nothing
dramatic is happening, just another addition to the toolbox.</p>
<p>And it's not like this isn't true. It's absolutely the case that great devs are
more productive with AI than ever. But in the long run this is irrelevant,
because developers as we know them are about to become a historical curiosity.</p>
<p>How do software engineers become &quot;great&quot;? Apparently, through a combination of
learning, working, tinkering, open sourcing, talking, and whatnot. All the normal
things we did before 2023, you know what I'm talking about. That was the journey
of <em>every single great engineer</em> of past and present. But this won't be the
journey of the future, because AI is already inserted in every single step of
this pipeline.</p>
<p>The world where you were forced to learn before you can do things quite
literally does not exist anymore. And I don't care if you still have what it
takes to read a book from start to finish, we're talking about
incentive structure change for our group <em>as a whole</em>. Sure enough, there will
be new things to learn and a new journey to take. Maybe it will be better or
worse, that's irrelevant for now. What's important is that it will be
<em>different</em> and it will produce a <em>different</em> kind of engineers.</p>
<p>We all know about valuable pre-2022 datasets, but there's also a very limited
stock of pre-2022 people. This means that the current situation where we have
thousands of competent engineers &quot;just using AI to be more productive&quot; <em>is a
historical anomaly</em>. We look at our own experience and extrapolate it, but
we're already a thing of the past. It doesn't matter how <em>we</em> use AI.</p>
<p>The current state of AI affairs simply can't continue past the current
generation. Something will have to change. Maybe we'll figure out a new way to
learn that is compatible with AI. Maybe AI will become so good that humanity
won't need people who understand how things actually work. Or maybe we'll move
universities off the grid <a href="https://en.wikipedia.org/wiki/Anathem">Anathem</a>
style. I don't know what it will be, but it will definitely <em>not</em> be what we see
now.</p>
<p>And yes, I know about fundamental technological advancements of the past. I don't care
how similar AI is to printing press, or steam engine, or photography, or internet,
it's also very different. AI is a genuinely novel technology and our generation
has a duty to figure it out. It might take updating our
<a href="/posts/its-rude-to-show-ai-output-to-people">etiquette</a> or developing <a href="https://www.pangram.com">new
tech</a>, but we need to take this seriously.</p> ]]></description>
    </item>
    <item>
       <title>PostHog Elixir SDK is Good</title>
       <link>https://distantprovince.by/posts/posthog-elixir-sdk-is-good/</link>
       <pubDate>Wed, 01 Oct 2025 09:00:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/posthog-elixir-sdk-is-good/</guid>
       <description><![CDATA[ <h1><a href="#posthog-elixir-sdk-is-good" aria-hidden="true" class="anchor" id="posthog-elixir-sdk-is-good"></a>PostHog Elixir SDK is good</h1>
<p>The PostHog Elixir SDK 2.0 <a href="https://hex.pm/packages/posthog">just dropped</a>.</p>
<p>What is PostHog? Why should you care about its SDK? Why do I think it's good? Bear
with me, I'm about to tell you how I spent my summer.</p>
<h2><a href="#dear-diary" aria-hidden="true" class="anchor" id="dear-diary"></a>Dear Diary</h2>
<p>In May 2025 I found myself with a lot of free time and an appetite for writing a
logging library from scratch. <em>How exactly</em> I developed this appetite is a whole
other story, but the bottom line is that I wasn't happy with how all existing logging
libraries shared a lot of design decisions and workarounds, a lot of which were
<a href="https://github.com/elixir-lang/elixir/pull/14380">based on solvable problems</a> or <a href="https://github.com/getsentry/sentry-elixir/issues/895">not relevant
anymore</a>.</p>
<p>And right when I was entertaining these thoughts, PostHog released its Error
Tracking solution to open beta. This was a perfect opportunity to write a
library.</p>
<p><del>So I bought a Claude Code subscription and one-shotted the whole thing in two
days</del>. Actually, no. I locked in and put a ton of effort into it. I learned
everything I could about Elixir logging. Whenever I hit a roadblock, I would
take a step back and fix it at the origin, whether it was in
<a href="https://github.com/elixir-plug/plug_cowboy/pull/108">Plug</a>,
<a href="https://github.com/Nebo15/logger_json/pull/149">LoggerJSON</a> or <a href="https://github.com/elixir-lang/elixir/pull/14354">Elixir
itself</a>. I also documented
everything and released it as an educational Hex package
<a href="https://hex.pm/packages/logger_handler_kit">LoggerHandlerKit</a>.</p>
<p>All of this resulted in <a href="https://hex.pm/packages/log_hog">LogHog</a> – an okay
library focused solely on PostHog Error Tracking.</p>
<p>It didn't take long for PostHog's very own Elixir champion <a href="https://posthog.com/community/profiles/32207">Rafa
Audibert</a> to reach out. Naturally,
error tracking would be a good addition to the official library. Things quickly
got out of control though, and a couple of months later we have a new,
completely overhauled version of the SDK on our hands. And, dear reader, I think
it's good.</p>
<h2><a href="#what-to-expect" aria-hidden="true" class="anchor" id="what-to-expect"></a>What to expect</h2>
<p>So what is PostHog and why does it need an SDK? After all, the Elixir ecosystem
is not big on SDKs. <a href="https://dashbit.co/blog/sdks-with-req-stripe">Just using
Req</a> is a great experience and
sets a pretty high bar to clear for any bespoke library.</p>
<p>I'm glad you asked! Let me walk you through what PostHog brings to the table for
an Elixir application.</p>
<h2><a href="#event-capture" aria-hidden="true" class="anchor" id="event-capture"></a>Event Capture</h2>
<p>I don't work for PostHog, so I'm going to tell you the truth. PostHog is a big
ClickHouse cluster run by a team whose life goal is to see how many products
they can build on top of it.</p>
<figure>
    <img src="/preview/posthog.png">
<figcaption>
*slaps roof of clickhouse* this bad boy can fit so many products in it
</figcaption>
</figure>
<p>The most important, core PostHog feature is the ability to send events to the
server, where they will be accessible through an SQL-like query language. Each
event can have properties attached to it. You can shove anything in there. The
only required property is <code>distinct_id</code>, which identifies users, persons, or
whatever you decide it does. Events happen to <em>someone</em>, after all.</p>
<p>Just emitting events is already great and is what we would call <em>product
analytics</em>. But PostHog has made events even more useful! <em>Some</em> events have special
meaning and experiences built for them. If you emit <code>$exception_list</code> events,
they will be available in <a href="https://posthog.com/error-tracking">Error Tracking</a>.
Capture <code>$ai_generation</code> and you have <a href="https://posthog.com/llm-analytics">LLM
Analytics</a>.</p>
<p>Events are very versatile, and PostHog is building more and more products every day.</p>
<h2><a href="#context" aria-hidden="true" class="anchor" id="context"></a>Context</h2>
<p>Sending analytics events is not as simple as sprinkling HTTP requests over your
codebase. You want to instrument your events with properties and they originate
in very different places. In order to note a user's IP address, you need to tap
into your Endpoint. For tenant ID, look inside the authentication stack.</p>
<p>If we had to pipe all relevant information through all the layers of our app, we
would go insane. This is why there is an <a href="https://hexdocs.pm/logger/1.18.4/Logger.html#module-metadata">established
pattern</a> in Elixir
to use the process dictionary for that kind of metadata.</p>
<p>The PostHog SDK comes with a context mechanism to address exactly this. You can set
PostHog context and it will be attached to events captured in the same process.</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #953800;">PostHog</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">set_context</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">distinct_id: </span><span style="color: #0a3069;">&quot;distinct_id_of_the_user&quot;</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="2"><span style="color: #953800;">PostHog</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">capture</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;page_opened&quot;</span><span style="color: #1f2328;">)</span>
</div></code></pre>
<h2><a href="#batching" aria-hidden="true" class="anchor" id="batching"></a>Batching</h2>
<p>Capturing events is important, but usually not important enough to block your
app code until the API request returns. That's why you want to offload sending
them to some background task. Sure, <code>Task.start</code> is always there, but it would
also be nice to batch events together...</p>
<p>PostHog SDK manages a pool of sender processes for you that automatically
batch and send all the events you capture. So when you call
<code>PostHog.capture</code>, it's merely a <code>GenServer.cast</code> under the hood.</p>
<h2><a href="#integrations" aria-hidden="true" class="anchor" id="integrations"></a>Integrations</h2>
<p>Some things are solved problems. PostHog gives special meaning to properties
like <code>$current_url</code> or <code>$host</code>, and the best place to grab those is from the
<code>Plug.Conn</code> struct. This knowledge is already baked into the SDK as
<code>PostHog.Integrations.Plug</code>. All you need to do is add it to your pipeline.</p>
<p>For now this is the only integration, but it won't be long until there are more.</p>
<h2><a href="#product-specific-api" aria-hidden="true" class="anchor" id="product-specific-api"></a>Product-specific API</h2>
<p>Sometimes you just need a little helper. The feature flags product, for example,
goes beyond simple event capture. The recommended flow to evaluate a feature flag is
the following:</p>
<ol>
<li>Make a call to <code>/flags</code> endpoint</li>
<li>Capture <code>$feature_flag_called</code> event</li>
<li>Use <code>$feature/&lt;flag_name&gt;</code> property for all events captured for this user.</li>
</ol>
<p>The SDK encapsulates all of this knowledge and exposes it as a single
<code>PostHog.FeatureFlags.check</code> function.</p>
<h2><a href="#error-tracking" aria-hidden="true" class="anchor" id="error-tracking"></a>Error Tracking</h2>
<p>Finally, the part that was transferred from LogHog. PostHog comes with a logger
handler that captures relevant log events and makes them accessible in PostHog
Error Tracking.</p>
<p>I have to say, though, that unlike other PostHog products, Error Tracking
requires language-specific backend support for the best experience, and Elixir
support is not yet implemented. But it works, so give it a try!</p>
<h2><a href="#test-infrastructure" aria-hidden="true" class="anchor" id="test-infrastructure"></a>Test Infrastructure</h2>
<p>You want to test your analytic events, right? <em>Right</em>?</p>
<p>Well, you're in luck because the PostHog SDK does the right thing and uses
<a href="https://hex.pm/packages/nimble_ownership">NimbleOwnership</a> to scope all
captured events to their owner processes. Expect your async tests to remain async:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;capture event&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="2">  <span style="color: #953800;">PostHog</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">capture</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;event&quot;</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">distinct_id: </span><span style="color: #0a3069;">&quot;distinct_id&quot;</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #6639ba;">assert</span> <span style="color: #1f2328;">[</span><span style="color: #1f2328;">event</span><span style="color: #1f2328;">]</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">PostHog.Test</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">all_captured</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="5">
</div><div class="line" data-line="6">  <span style="color: #6639ba;">assert</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="7">            <span style="color: #0550ae;">event: </span><span style="color: #0a3069;">&quot;event&quot;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="8">            <span style="color: #0550ae;">distinct_id: </span><span style="color: #0a3069;">&quot;distinct_id&quot;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="9">            <span style="color: #0550ae;">properties: </span><span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="10">            <span style="color: #0550ae;">timestamp: </span><span style="color: #57606a;">_</span>
</div><div class="line" data-line="11">          <span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #1f2328;">event</span>
</div><div class="line" data-line="12"><span style="color: #cf222e;">end</span>
</div></code></pre>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>The new PostHog SDK is here and I think it's good. It was written by people who
care about both Elixir and PostHog. It's fresh off the press and not
battle-tested yet, so tread with caution, but if you're looking to capture
events in your app, maybe give it a try.</p> ]]></description>
    </item>
    <item>
       <title>5-minute guide to Elixir caller tracking</title>
       <link>https://distantprovince.by/posts/5-minute-guide-to-elixir-caller-tracking/</link>
       <pubDate>Sat, 30 Aug 2025 16:00:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/5-minute-guide-to-elixir-caller-tracking/</guid>
       <description><![CDATA[ <h1><a href="#5-minute-guide-to-elixir-caller-tracking" aria-hidden="true" class="anchor" id="5-minute-guide-to-elixir-caller-tracking"></a>5-minute guide to Elixir caller tracking</h1>
<p>Caller tracking has been part of Elixir <a href="https://github.com/elixir-lang/elixir/issues/7995">since
2018</a>, and yet it remains a
relatively obscure mechanism. Let's fix this with a 5-minute guide.</p>
<h2><a href="#what-is-caller-tracking" aria-hidden="true" class="anchor" id="what-is-caller-tracking"></a>What is caller tracking</h2>
<p>Asynchronous tests are the best. They are possible because in many cases we can
trace what is happening in the application all the way back to the test. We say
that a test process &quot;owns&quot; things, and other processes may be &quot;allowed&quot; to
access those things.</p>
<p>Caller tracking is a convention that allows us to know which processes are
related to each other and automatically allow them to access things owned by a
test. Examples of such things are
<a href="https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html"><code>Ecto.Sandbox</code></a>
transactions and
<a href="https://hexdocs.pm/mox/Mox.html#module-multi-process-collaboration"><code>Mox</code></a>
mocks.</p>
<p>In some cases, caller tracking is handled automatically and you don't need to do
anything, such as if you use the <code>Task</code> module. But in a lot of other cases you need
to take care of caller tracking yourself. The most common example is a
GenServer.</p>
<h2><a href="#genserver-with-caller-tracking" aria-hidden="true" class="anchor" id="genserver-with-caller-tracking"></a>Genserver with caller tracking</h2>
<p>Whenever you start a GenServer, equip it with a <code>:&quot;$callers&quot;</code> value in the
process dictionary. It should include whatever the calling process had in its
process dictionary under the same key <em>and</em> the caller PID. Remember:
<code>start_link</code> is executed in the caller, while <code>init</code> runs in the server!</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyGenserver</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="2">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="3">  
</div><div class="line" data-line="4">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">args</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="5">    <span style="color: #1f2328;">callers</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:&quot;$callers&quot;</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">||</span> <span style="color: #1f2328;">[</span><span style="color: #1f2328;">]</span>
</div><div class="line" data-line="6">    <span style="color: #1f2328;">new_callers</span> <span style="color: #0550ae;">=</span> <span style="color: #1f2328;">[</span><span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">|</span> <span style="color: #1f2328;">callers</span><span style="color: #1f2328;">]</span>
</div><div class="line" data-line="7">    <span style="color: #953800;">GenServer</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">new_callers</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">args</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="8">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #953800;">GenServer</span>
</div><div class="line" data-line="11">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">init</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">callers</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">_args</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="12">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">put</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:&quot;$callers&quot;</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">callers</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="13">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">nil</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="14">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="15"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>Now whenever you start this GenServer in your test, it will be able to access
Ecto transactions without any additional setup.</p>
<figure>
    <img src="/preview/callers.jpg">
<figcaption>
It's dangerous to spawn alone! Take this.
</figcaption>
</figure>
<h2><a href="#further-reading" aria-hidden="true" class="anchor" id="further-reading"></a>Further reading</h2>
<p><a href="https://hexdocs.pm/elixir/1.18.4/Task.html#module-ancestor-and-caller-tracking">Ancestor and Caller Tracking bit in Task module documentation</a></p>
<p><a href="/posts/what-does-nimbleownership-do-anyway/">What Does NimbleOwnership Do Anyway?</a></p>
<p><a href="https://andrealeopardi.com/posts/async-tests-in-elixir/">How to Async Tests in Elixir</a></p>
<p><a href="https://gist.github.com/martosaur/3ba655232f7db7295a53bd367a0a4f90">Gist with full single file example</a></p> ]]></description>
    </item>
    <item>
       <title>It&#39;s rude to show AI output to people</title>
       <link>https://distantprovince.by/posts/its-rude-to-show-ai-output-to-people/</link>
       <pubDate>Fri, 04 Jul 2025 08:00:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/its-rude-to-show-ai-output-to-people/</guid>
       <description><![CDATA[ <h1><a href="#its-rude-to-show-ai-output-to-people" aria-hidden="true" class="anchor" id="its-rude-to-show-ai-output-to-people"></a>It's rude to show AI output to people</h1>
<blockquote>
<p>Imagine you're a scrambler.</p>
<p>Imagine that you encounter a signal. It is structured, and dense with
information. It meets all the criteria of an intelligent transmission. Evolution
and experience offer a variety of paths to follow, branch-points in the
flowcharts that handle such input. Sometimes these signals come from
conspecifics who have useful information to share, whose lives you'll defend
according to the rules of kin selection. Sometimes they come from competitors or
predators or other inimical entities that must be avoided or destroyed; in those
cases, the information may prove of significant tactical value. Some signals may
even arise from entities which, while not kin, can still serve as allies or
symbionts in mutually beneficial pursuits. You can derive appropriate responses
for any of these eventualities, and many others.</p>
<p>You decode the signals, and stumble:</p>
<p><em>I had a great time. I really enjoyed him. Even if he cost twice as much as any
other hooker in the dome—</em></p>
<p><em>To fully appreciate Kesey's Quartet—</em></p>
<p><em>They hate us for our freedom—</em></p>
<p><em>Pay attention, now—</em></p>
<p><em>Understand.</em></p>
<p>There are no meaningful translations for these terms. They are needlessly
recursive. They contain no usable intelligence, yet they are structured
intelligently; there is no chance they could have arisen by chance.</p>
<p>The only explanation is that something has coded nonsense in a way that poses as
a useful message; only after wasting time and effort does the deception becomes
apparent. The signal functions to consume the resources of a recipient for zero
payoff and reduced fitness. The signal is a virus.</p>
<p>Viruses do not arise from kin, symbionts, or other allies.</p>
<p>The signal is an attack.</p>
<p>– <cite>Peter Watts, &quot;Blindsight&quot;</cite></p>
</blockquote>
<p>In the sci-fi novel Blindsight by Peter Watts, humanity encounters scramblers –
an alien species that is intelligent, but lacks consciousness. Scramblers do not
attempt to contact humans and wage a unilateral total war against us. Why?
Because we talk too much. Our little planet just can't stop chatting about
nonsense, and scramblers know only one use for information: to perceive it.
They can't help but listen to all our yapping that only wastes their precious
brain cycles and reduces their chances of survival. No peaceful species would do this;
this is clearly an act of war.</p>
<p>When I first read Blindsight 10 years ago, I didn't understand how scramblers felt
about human slop. But now I do. I feel it every time I read AI text outside of
the privacy of my own chatroom.</p>
<h2><a href="#proof-of-thought" aria-hidden="true" class="anchor" id="proof-of-thought"></a>Proof-of-thought</h2>
<p>For the longest time, writing was more expensive than reading. If you
encountered a body of written text, you could be sure that at the very least,
a human spent <em>some</em> time writing it down. The text used to have an innate
proof-of-thought, a basic token of humanity.</p>
<p>Now, AI has made text very, very, very cheap. Not only text, in fact. Code, images,
video. All kinds of media. We can't rely on proof-of-thought anymore. Any text
can be AI slop. If you read it, you're injured in this war. You engaged and
replied – you're as good as dead. The dead internet is not just dead it's
<em>poisoned</em>. So what do we do?</p>
<p>Luckily for us, AI only talks <em>in response</em>. Unlike Earth, AI does not emit
comedy sketches into outer space on its own. To get AI slop, somebody needs to
ask for it. To send it further, someone needs to retransmit it. Our problem is
other humans, really.</p>
<p>There's nothing wrong with <em>using AI</em>. When you do, you know what you're getting. The
transaction is fully consensual. But whenever you <em>propagate AI output</em>, you're
at risk of intentionally or unintentionally legitimizing it with your good name,
providing it with a fake proof-of-thought. In some cases, it's fine, because you
did think it through and adopted the AI output as your own. But in other cases, it
is not, and our scrambler brain feels violated.</p>
<h2><a href="#ai-etiquette" aria-hidden="true" class="anchor" id="ai-etiquette"></a>AI etiquette</h2>
<figure>
<pre>
╭─────────────────────────╮
│ I asked ChatGPT and     │
│ here's what it told me: │
│ ...                     │
╰─────────────────────────╯
                                    ╭─────────╮
                                    │ blocked │
                                    ╰─────────╯
</pre>
<figcaption>
Time to learn AI manners
</figcaption>
</figure>
<p>I think that realistically, our main weapon in this war is AI etiquette. My own
take on AI etiquette is that AI output can only be relayed if it's either
adopted as your own or there is explicit consent from the receiving party.
There's plenty of discussion to be had about what adopting entails and what can
be considered consent, but I believe the core principle is sound.</p>
<blockquote>
<p>&quot;I asked ChatGPT and this is what it said: &lt;...&gt;&quot;.</p>
</blockquote>
<p>Whoa, let me stop you right here buddy, what you're doing here is extremely, horribly rude.</p>
<blockquote>
<p>&quot;I had a helpful chat with ChatGPT about this topic some time ago and can share a log with you if you want.&quot;</p>
</blockquote>
<p>Yeah, send it my way I'll take a look.</p>
<blockquote>
<p>&quot;I vibe-coded this pull request in just 15 minutes. Please review&quot;</p>
</blockquote>
<p>Well, why don't <em>you</em> review it first?</p>
<blockquote>
<p>&quot;Here's my PR, I did this and that for this and that reason.&quot;</p>
</blockquote>
<p>Thank you, I'll take a look.</p>
<p>Unlike scramblers, we're only exposed to meaningless noise by choice. Be polite,
and don't send humans AI text.</p> ]]></description>
    </item>
    <item>
       <title>Hide Your Nodes, AAAAAA@AAAAAAA is Coming</title>
       <link>https://distantprovince.by/posts/hide-your-nodes-aaaaaaaaaaaaa-is-coming/</link>
       <pubDate>Sat, 01 Mar 2025 11:00:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/hide-your-nodes-aaaaaaaaaaaaa-is-coming/</guid>
       <description><![CDATA[ <h1><a href="#hide-your-nodes-aaaaaaaaaaaaa-is-coming" aria-hidden="true" class="anchor" id="hide-your-nodes-aaaaaaaaaaaaa-is-coming"></a>Hide Your Nodes, AAAAAA@AAAAAAA is Coming</h1>
<blockquote>
<p>06:04:59.473 [error] ** :&quot;myapp@myhost&quot;: Connection attempt from node
:AAAAAA@AAAAAAA rejected since it cannot handle [~c&quot;UNLINK_ID&quot;,
~c&quot;HANDSHAKE_23&quot;, ~c&quot;BIG_CREATION&quot;, ~c&quot;BIT_BINARIES&quot;, ~c&quot;EXPORT_PTR_TAG&quot;].**</p>
</blockquote>
<p>This is NOT what you want to suddenly pop up in your logs at 10 PM.</p>
<p>I am a simple man. I host my silly little hobby projects on a single VPS
server behind an NGINX proxy. There is nothing sensitive there, no user data;
they don't even user cookies! I'm sure I'll be fine with the perfectly
sensible security defaults, right?</p>
<p>Well, yes and no. Yes, nothing bad happened so far. No, I'm not <em>totally</em> fine;
this log message almost gave me a heart attack.</p>
<figure>
<pre>
        ┌───┬───────┬───────┬───────┬───────┬───┐
        │   │       │       │       │       │   |
        ├───┴───┬───┴───┬───┴───┬───┴───┬───┴───┤
        │       │       │       │       │       |
        ├───┬───┴───┬───┴───┬───┴───┬───┴───┬───┤
        │   │   ┌───┴───────┴───────┴───┐   │   |
        ├───┴───┤       WANTED          ├───┴───┤
        │       ├───────────────────────┤       │
        ├───┬───┤                       ├───┬───┤
        │   │   │                       │   │   │
        ├───┴───┤                       ├───┴───┤
        │       │                       │       │
        ├───┬───┤   AAAAAA@AAAAAAA      ├───┬───┤
        │   │   │                       │   │   │
        ├───┴───┤                       ├───┴───┤
        │       │                       │       │
        ├───┬───┤                       ├───┬───┤
        │   │   ├───────────────────────┤   │   │
        ├───┴───┤ FOR UNAUTHORIZED      ├───┴───┤
        │       │ ACCESS ATTEMPT        │       │
        ├───┬───┴───┬───────┬───────┬───┴───┬───┤
        │   │       │       │       │       │   │
        ├───┴───┬───┴───┬───┴───┬───┴───┬───┴───┤
        │       │       │       │       │       │
        └───────┴───────┴───────┴───────┴───────┘
</pre>
<figcaption>
Have you seen this node?
</figcaption>
</figure>
<h2><a href="#who-is-aaaaaaaaaaaaa" aria-hidden="true" class="anchor" id="who-is-aaaaaaaaaaaaa"></a>Who is :AAAAAA@AAAAAAA?</h2>
<p>Ah yes, this is just the right amount of As to go and search for it on the
internet. It isn't hard, so I'll spare you the details. Apparently there was
a CouchDB <a href="https://www.exploit-db.com/exploits/50914">exploit</a> that allowed for
an attacker to perform a remote code execution by connecting to an Erlang node.
It used <code>AAAAAA@AAAAAAA</code> as the fake node name. And when there is an exploit,
there is automation. Somebody out there running this exploit (and no doubt many
others) looking for unsecured Erlang nodes to connect to. My app node rejected
the connection, but the attempt got close enough to be logged and we definitely
don't want that.</p>
<h2><a href="#i-am-the-aaaaaaaaaaaaa-now" aria-hidden="true" class="anchor" id="i-am-the-aaaaaaaaaaaaa-now"></a>I am the AAAAAA@AAAAAAA now</h2>
<p>Before we fix anything, we need to reproduce the problem. Theoretically, we can
just run the Python script from the link above, but that's no fun. Let's go step
by step. This will also help us implement security measures later.</p>
<p>First of all, <code>epmd</code>. The very first thing the hacker is doing is looking if
there's even an Erlang node on the server. For this, they just ask <code>epmd</code>
through the default port (4369). We can do this with netcat:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">echo</span> <span style="color: #1f2328;">-ne</span> <span style="color: #0a3069;">&quot;\x00\x01\x6e&quot;</span> <span style="color: #cf222e;">|</span> <span style="color: #0550ae;">nc</span> <span style="color: #cf222e;">&lt;</span><span style="color: #1f2328;">IP</span><span style="color: #cf222e;">&gt;</span> <span style="color: #0550ae;">4369</span>
</div><div class="line" data-line="2"><span style="color: #0550ae;">name</span> <span style="color: #1f2328;">my_app</span> <span style="color: #1f2328;">at</span> <span style="color: #1f2328;">port</span> <span style="color: #0550ae;">38255</span>
</div><div class="line" data-line="3"><span style="color: #0550ae;">name</span> <span style="color: #1f2328;">another_app</span> <span style="color: #1f2328;">at</span> <span style="color: #1f2328;">port</span> <span style="color: #0550ae;">43493</span>
</div></code></pre>
<p>Huh, you guys aren't hiding from the world at all, are you? Anyway, now we have
a list of nodes and their ports. Let's connect using the binary message from the
script:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">echo</span> <span style="color: #1f2328;">-ne</span> <span style="color: #0a3069;">&quot;\x00\x15n\x00\x07\x00\x03\x49\x9cAAAAAA@AAAAAAA&quot;</span> <span style="color: #cf222e;">|</span> <span style="color: #0550ae;">nc</span> <span style="color: #cf222e;">&lt;</span><span style="color: #1f2328;">IP</span><span style="color: #cf222e;">&gt;</span> <span style="color: #0550ae;">38255</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #0550ae;">snot_allowed</span>
</div></code></pre>
<p>Boom, we got it! The log message is the same, but we're the attacker now!</p>
<h2><a href="#hobby-grade-security-plan" aria-hidden="true" class="anchor" id="hobby-grade-security-plan"></a>Hobby-grade Security Plan</h2>
<p>Time to devise and implement our hobby-grade security plan. And I say
<em>hobby-grade</em> for a reason. If you're truly running a distributed cluster in
Kubernetes or what not, you won't find any advice here.</p>
<h4><a href="#1-disable-erlang-distribution" aria-hidden="true" class="anchor" id="1-disable-erlang-distribution"></a>1. Disable Erlang Distribution?</h4>
<p>If we're running a single node, we don't need Erlang distribution, right?
Welllll. Strictly speaking, we don't, but it's required for some nice <code>bin/myapp</code>
commands, such as <code>remote</code>. I personally can't give up on that, but if you can,
look for the <code>RELEASE_DISTRIBUTION</code> <a href="https://hexdocs.pm/mix/Mix.Tasks.Release.html#module-environment-variables">environment
variable</a>
in the release.</p>
<h4><a href="#2-disable-epmd" aria-hidden="true" class="anchor" id="2-disable-epmd"></a>2. Disable epmd</h4>
<p>OK, we might want to run a distribution, but can we at least not advertise our
nodes to the world with epmd? Yes! The keyword here is
<a href="https://hexdocs.pm/mix/Mix.Tasks.Release.html#module-epmd-less-deployment">&quot;epmdless&quot;</a>.
Basically, you can disable automatic epmd start and hardcode a port your app
will be using.</p>
<p>But don't be fooled, even though epmd isn't there to snitch, the port is still open and discoverable:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-bash" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #0550ae;">nmap</span> <span style="color: #1f2328;">-sV</span> <span style="color: #1f2328;">-Pn</span> <span style="color: #1f2328;">-n</span> <span style="color: #1f2328;">-T4</span> <span style="color: #1f2328;">-p-</span> <span style="color: #cf222e;">&lt;</span><span style="color: #1f2328;">IP</span><span style="color: #cf222e;">&gt;</span>
</div><div class="line" data-line="2"><span style="color: #1f2328;">Starting</span> <span style="color: #1f2328;">Nmap</span> <span style="color: #1f2328;">7.95</span> <span style="color: #1f2328;">(</span> <span style="color: #0550ae;">https://nmap.org</span> <span style="color: #1f2328;">)</span> <span style="color: #0550ae;">at</span> <span style="color: #1f2328;">2025-03-01</span> <span style="color: #1f2328;">13:01</span> <span style="color: #1f2328;">PST</span>
</div><div class="line" data-line="3"><span style="color: #0550ae;">Nmap</span> <span style="color: #1f2328;">scan</span> <span style="color: #1f2328;">report</span> <span style="color: #1f2328;">for</span> <span style="color: #cf222e;">&lt;</span><span style="color: #1f2328;">IP</span><span style="color: #cf222e;">&gt;</span>
</div><div class="line" data-line="4"><span style="color: #1f2328;">Host</span> <span style="color: #1f2328;">is</span> <span style="color: #1f2328;">up</span> <span style="color: #1f2328;">(</span><span style="color: #0550ae;">0.028s</span> <span style="color: #1f2328;">latency</span><span style="color: #1f2328;">)</span><span style="color: #cf222e;">.</span>
</div><div class="line" data-line="5"><span style="color: #0550ae;">Not</span> <span style="color: #1f2328;">shown:</span> <span style="color: #0550ae;">65529</span> <span style="color: #1f2328;">closed</span> <span style="color: #1f2328;">tcp</span> <span style="color: #1f2328;">ports</span> <span style="color: #1f2328;">(</span><span style="color: #0550ae;">conn-refused</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="6"><span style="color: #0550ae;">PORT</span>     <span style="color: #1f2328;">STATE</span> <span style="color: #1f2328;">SERVICE</span>        <span style="color: #1f2328;">VERSION</span>
</div><div class="line" data-line="7"><span style="color: #0550ae;">...</span>
</div><div class="line" data-line="8"><span style="color: #0550ae;">6789/tcp</span> <span style="color: #1f2328;">open</span>  <span style="color: #1f2328;">ibm-db2-admin?</span>
</div></code></pre>
<p>Nooooo, this means attackers can still attempt to connect to the node! It just got harder to do!</p>
<h4><a href="#3-firewall" aria-hidden="true" class="anchor" id="3-firewall"></a>3. Firewall</h4>
<p>I regret to inform you, you might actually have to configure a firewall. At
least that's what I did! I can't help you much here, except for maybe a little
glimpse of hope: it wasn't actually that hard to do.</p>
<h2><a href="#was-there-ever-a-threat" aria-hidden="true" class="anchor" id="was-there-ever-a-threat"></a>Was There Ever a Threat?</h2>
<p>Kind of. There are a couple of hoops the attacker would need to jump through in
order to actually connect, like dealing with short node names and knowing the
magic cookie, but Erlang
<a href="https://www.erlang.org/doc/system/distributed.html#security">documentation</a>
states that those mechanisms aren't designed to ensure security.</p>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>There is someone out there dressed up as an Erlang node going from server to
server knocking on port 4369. You're probably safe, but the knock is creepy
nonetheless. Make sure you keep your <code>epmd</code> under control and your ports closed,
otherwise... <code>:AAAAAA@AAAAAAA</code> WILL GET YOU!</p> ]]></description>
    </item>
    <item>
       <title>What Does NimbleOwnership Do Anyway?</title>
       <link>https://distantprovince.by/posts/what-does-nimbleownership-do-anyway/</link>
       <pubDate>Sat, 07 Dec 2024 10:00:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/what-does-nimbleownership-do-anyway/</guid>
       <description><![CDATA[ <h1><a href="#what-does-nimbleownership-do-anyway" aria-hidden="true" class="anchor" id="what-does-nimbleownership-do-anyway"></a>What Does NimbleOwnership Do Anyway?</h1>
<p>If you've worked with Elixir for a while, you know that there are two kinds of
tests: the good ones and the ones that need to be justified. The former being,
of course, asynchronous tests – fast and joyful, and the latter being synchronous
tests, the ones that get a side eye in code reviews.</p>
<p>Anyone who tries to write asynchronous tests sooner or later learns about
the concept of <em>ownership</em>. This concept exists in
<a href="https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html#module-collaborating-processes">Ecto</a>,
<a href="https://hexdocs.pm/mox/Mox.html#module-multi-process-collaboration">Mox</a>,
<a href="https://hexdocs.pm/req/Req.Test.html#module-concurrency-and-allowances">Req</a>,
and other libraries. In fact, it became so common that ownership management was
extracted into a separate library called
<a href="https://hex.pm/packages/nimble_ownership">NimbleOwnership</a>.</p>
<p>In this post, we'll see how the idea of ownership may come up naturally and what
part of it can be handled by NimbleOwnership.</p>
<h2><a href="#hell-is-other-tests" aria-hidden="true" class="anchor" id="hell-is-other-tests"></a>Hell Is Other Tests</h2>
<p>When we say &quot;ownership&quot; we mean &quot;resource ownership&quot;. You see, asynchronous
tests exist in a highly concurrent environment, where anything can happen. Other
tests might write to the same database (Ecto) or call the same mocks (Mox, Req)
as your target test. When this happens, it may really mess up your test
expectations.</p>
<p>To demonstrate this, let's look at another type of global resource: handlers -
specifically logger handlers. Let's say we have a logger handler that simply
counts the number of logs with an Agent process:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #953800;">ExUnit</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="2">
</div><div class="line" data-line="3"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">OtherTests</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="4">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="5">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">  <span style="color: #cf222e;">for</span> <span style="color: #1f2328;">i</span> <span style="color: #0550ae;">&lt;-</span> <span style="color: #0550ae;">1</span><span style="color: #0550ae;">..</span><span style="color: #0550ae;">10</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="8">    <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test </span><span style="color: #1f2328;">#&lbrace;</span><span style="color: #1f2328;">i</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #0a3069;">&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="9">      <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="10">      <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Other tests&quot;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="11">    <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="12">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="13"><span style="color: #cf222e;">end</span>
</div><div class="line" data-line="14">
</div><div class="line" data-line="15"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="16">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="17">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="18">
</div><div class="line" data-line="19">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">behaviour </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="20">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="21">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">log</span><span style="color: #1f2328;">(</span><span style="color: #57606a;">_event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="22">    <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">update</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">+</span> <span style="color: #0550ae;">1</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="23">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="24">
</div><div class="line" data-line="25">  <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test log&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="26">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">0</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="27">    <span style="color: #953800;">:logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">add_handler</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="28">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="29">    <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Foo&quot;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="30">    <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="31">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="32"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>With the power of <code>Process.sleep/1</code>, we can carefully craft this concurrency
train wreck. The test in the <code>MyTest</code> module attaches a logger handler that is
called whenever we log something. It then logs a single message and asserts that
the counter was, in fact, incremented. Unfortunately, there are other
asynchronous tests that aren't shy about logging messages themselves, and those
messages <em>also</em> call the same logger handler, which interferes with our test:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">1) test test log (MyTest)
</div><div class="line" data-line="2">   ex_unit.exs:24
</div><div class="line" data-line="3">   Assertion with == failed
</div><div class="line" data-line="4">   code:  assert Agent.get(agent, fn counter -&gt; counter end) == 1
</div><div class="line" data-line="5">   left:  2
</div><div class="line" data-line="6">   right: 1
</div><div class="line" data-line="7">   stacktrace:
</div><div class="line" data-line="8">     ex_unit.exs:29: (test)
</div></code></pre>
<p>We can't let this happen, can we?</p>
<h2><a href="#can-i-see-your-pid" aria-hidden="true" class="anchor" id="can-i-see-your-pid"></a>Can I See Your PID?</h2>
<p>Ok, all we need to do is not let other tests call our logger handler. Luckily
for us, handlers are executed in the same process, so we can just add the test PID
to the handler config and check it:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="2">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="3">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="4">  
</div><div class="line" data-line="5">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">behaviour </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="6">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="7">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">log</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">config: </span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">test_pid</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="8">    <span style="color: #cf222e;">with</span> <span style="color: #0550ae;">true</span> <span style="color: #0550ae;">&lt;-</span> <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">==</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="9">      <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">update</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">+</span> <span style="color: #0550ae;">1</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="10">    <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="11">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="12">  
</div><div class="line" data-line="13">  <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test log&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="14">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">0</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="15">    <span style="color: #953800;">:logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">add_handler</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="16">      <span style="color: #0550ae;">config: </span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="17">    <span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="18">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="19">    <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Foo&quot;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="20">    <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="21">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="22"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>Problem solved! No, seriously, problem solved. OK, OK, I hear what you're
saying - in real life, logger handler code is in your application and adding a
PID check to it is silly. That's fair. But we can always create a wrapper!</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyApp.LogHandler</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="2">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">behaviour </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="5">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">log</span><span style="color: #1f2328;">(</span><span style="color: #57606a;">_event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="6">    <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">update</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">+</span> <span style="color: #0550ae;">1</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="7">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="8"><span style="color: #cf222e;">end</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="11">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="12">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">behaviour </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="15">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="16">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">log</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">config: </span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">real_handler</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">og_cfg</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">test_pid</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #1f2328;">cfg</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="17">    <span style="color: #cf222e;">with</span> <span style="color: #0550ae;">true</span> <span style="color: #0550ae;">&lt;-</span> <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">==</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="18">      <span style="color: #1f2328;">real_handler</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">log</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #953800;">Map</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">put</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">cfg</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:config</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">og_cfg</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="19">    <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="20">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22">  <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test log&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="23">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">0</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="24">    <span style="color: #953800;">:logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">add_handler</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">__MODULE__</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="25">      <span style="color: #0550ae;">config: </span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #953800;">MyApp.LogHandler</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="26">    <span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="27">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="28">    <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Foo&quot;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="29">    <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="30">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="31"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>or even better, a <a href="https://www.erlang.org/doc/apps/kernel/logger_chapter.html#filters">logger filter</a>:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyApp.LogHandler</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="2">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">behaviour </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="5">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">log</span><span style="color: #1f2328;">(</span><span style="color: #57606a;">_event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="6">    <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">update</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">+</span> <span style="color: #0550ae;">1</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="7">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="8"><span style="color: #cf222e;">end</span>
</div><div class="line" data-line="9">
</div><div class="line" data-line="10"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="11">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="12">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="13">
</div><div class="line" data-line="14">  <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test log&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="15">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">0</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="16">
</div><div class="line" data-line="17">    <span style="color: #953800;">:logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">add_handler</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #953800;">MyApp.LogHandler</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="18">      <span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="19">      <span style="color: #0550ae;">filters: </span><span style="color: #1f2328;">[</span>
</div><div class="line" data-line="20">        <span style="color: #0550ae;">ownership_filter: </span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="21">          <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">-&gt;</span>
</div><div class="line" data-line="22">            <span style="color: #cf222e;">if</span> <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">==</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">do: </span><span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">else: </span><span style="color: #0550ae;">:stop</span>
</div><div class="line" data-line="23">          <span style="color: #cf222e;">end</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="24">          <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="25">        <span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="26">      <span style="color: #1f2328;">]</span>
</div><div class="line" data-line="27">    <span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="30">    <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Foo&quot;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="31">    <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="32">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="33"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>It works, but at this point you probably see the limitation. Not all processes
are our enemies. Some of them are alright, in fact. Really chill processes. But
our solution is relentless:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="2">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="3">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">  <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test log&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="6">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">0</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="7">    <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">=</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">    <span style="color: #953800;">:logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">add_handler</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #953800;">MyApp.LogHandler</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="10">      <span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="11">      <span style="color: #0550ae;">filters: </span><span style="color: #1f2328;">[</span>
</div><div class="line" data-line="12">        <span style="color: #0550ae;">ownership_filter: </span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="13">          <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">-&gt;</span>
</div><div class="line" data-line="14">            <span style="color: #cf222e;">if</span> <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">==</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">do: </span><span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">else: </span><span style="color: #0550ae;">:stop</span>
</div><div class="line" data-line="15">          <span style="color: #cf222e;">end</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="16">          <span style="color: #1f2328;">test_pid</span>
</div><div class="line" data-line="17">        <span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="18">      <span style="color: #1f2328;">]</span>
</div><div class="line" data-line="19">    <span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="22">    <span style="color: #1f2328;">task</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Task</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">async</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Foo&quot;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="23">    <span style="color: #953800;">Task</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">await</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">task</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="24">    
</div><div class="line" data-line="25">    <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="26">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="27"><span style="color: #cf222e;">end</span>
</div></code></pre>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #0550ae;">1</span><span style="color: #1f2328;">)</span> <span style="color: #6639ba;">test</span> <span style="color: #6639ba;">test</span> <span style="color: #6639ba;">log</span> <span style="color: #1f2328;">(</span><span style="color: #953800;">MyTest</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="2">   <span style="color: #1f2328;">ex_unit_spawn</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">exs</span>:<span style="color: #0550ae;">28</span>
</div><div class="line" data-line="3">   <span style="color: #953800;">Assertion</span> <span style="color: #1f2328;">with</span> <span style="color: #0550ae;">==</span> <span style="color: #1f2328;">failed</span>
</div><div class="line" data-line="4">   <span style="color: #6639ba;">code</span>:  <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="5">   <span style="color: #6639ba;">left</span>:  <span style="color: #0550ae;">0</span>
</div><div class="line" data-line="6">   <span style="color: #6639ba;">right</span>: <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="7">   <span style="color: #6639ba;">stacktrace</span>:
</div><div class="line" data-line="8">     <span style="color: #1f2328;">ex_unit_spawn</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">exs</span>:<span style="color: #0550ae;">53</span>: <span style="color: #1f2328;">(</span><span style="color: #1f2328;">test</span><span style="color: #1f2328;">)</span>
</div></code></pre>
<figure>
<pre>
╭─────────────────────╮
│ Don't talk to me or │
│ my son ever again   │
╰─────────────────────╯
                   │  ╭────────────────╮
                   ╰─ │                │
                      │ Logger Filter  │╭────────────────╮
                      │                ││ Logger Handler │
                      ╰────────────────╯╰────────────────╯
</pre>
<figcaption>
POV: You're trying to call the handler from a different process
</figcaption>
</figure>
<h2><a href="#sharing-is-caring" aria-hidden="true" class="anchor" id="sharing-is-caring"></a>Sharing Is Caring</h2>
<p>How do we allow other processes to access our beloved resources? Let's think
about it – we first create a filter that knows to only proceed if it is called
in a process with a specific pre-defined PID. So, if we want to allow another
PID, we need to expand this from a single PID to a list of allowed PIDs. The
problem is that spawned processes' PIDs aren't known at the time when we define
the filter. Well, in that case, we can tell the filter to check not the PID
itself, but some sort of secret. A token of process friendship that we'll put
into any spawned process's dictionary.</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="2">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="3">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">  <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test log&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="6">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">0</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="7">    <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">=</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">    <span style="color: #953800;">:logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">add_handler</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #953800;">MyApp.LogHandler</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="10">      <span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="11">      <span style="color: #0550ae;">filters: </span><span style="color: #1f2328;">[</span>
</div><div class="line" data-line="12">        <span style="color: #0550ae;">ownership_filter: </span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="13">          <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">_config</span> <span style="color: #0550ae;">-&gt;</span>
</div><div class="line" data-line="14">            <span style="color: #cf222e;">if</span> <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:friendship_token</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">do: </span><span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">else: </span><span style="color: #0550ae;">:stop</span>
</div><div class="line" data-line="15">          <span style="color: #cf222e;">end</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="16">          <span style="color: #0550ae;">nil</span>
</div><div class="line" data-line="17">        <span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="18">      <span style="color: #1f2328;">]</span>
</div><div class="line" data-line="19">    <span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="20">
</div><div class="line" data-line="21">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="22">
</div><div class="line" data-line="23">    <span style="color: #1f2328;">task</span> <span style="color: #0550ae;">=</span>
</div><div class="line" data-line="24">      <span style="color: #953800;">Task</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">async</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span>
</div><div class="line" data-line="25">        <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">put</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:friendship_token</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">true</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="26">        <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Foo&quot;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="27">      <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29">    <span style="color: #953800;">Task</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">await</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">task</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="30">    <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="31">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="32"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>See, it works! All we need to do is hand out this token to all processes that
are relevant to the test and make sure the filter knows where to look for it.</p>
<h2><a href="#do-you-know-who-my-dad-is" aria-hidden="true" class="anchor" id="do-you-know-who-my-dad-is"></a>Do You Know Who My Dad Is?</h2>
<p>Not going to lie, this does sound like a chore. But have no fear, as Elixir has
us covered! There is a <em>convention</em> that processes that are part of other
processes' hierarchy in a meaningful way keep track of their ancestor PIDs in
their dictionary. This list can be found under a magical key <code>$callers</code>. This is
called <a href="https://hexdocs.pm/elixir/1.17.3/Task.html#module-ancestor-and-caller-tracking">caller
tracking</a>,
and a lot of modules do this automatically. For example, the <code>Task</code> module or
<a href="https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html#start_supervised/2"><code>start_supervised/2</code></a>
ExUnit helper. We can use this to our advantage: instead of having a custom
token, we'll ask our filter to look for the test PID in the <code>$callers</code> list.</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="2">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="3">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5">  <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test log&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="6">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">0</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="7">    <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">=</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="8">
</div><div class="line" data-line="9">    <span style="color: #953800;">:logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">add_handler</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #953800;">MyApp.LogHandler</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="10">      <span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="11">      <span style="color: #0550ae;">filters: </span><span style="color: #1f2328;">[</span>
</div><div class="line" data-line="12">        <span style="color: #0550ae;">ownership_filter: </span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="13">          <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">test_pid</span> <span style="color: #0550ae;">-&gt;</span>
</div><div class="line" data-line="14">            <span style="color: #1f2328;">callers</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:&quot;$callers&quot;</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">||</span> <span style="color: #1f2328;">[</span><span style="color: #1f2328;">]</span>
</div><div class="line" data-line="15">            <span style="color: #cf222e;">if</span> <span style="color: #1f2328;">test_pid</span> <span style="color: #cf222e;">in</span> <span style="color: #1f2328;">[</span><span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">|</span> <span style="color: #1f2328;">callers</span><span style="color: #1f2328;">]</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">do: </span><span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">else: </span><span style="color: #0550ae;">:stop</span>
</div><div class="line" data-line="16">          <span style="color: #cf222e;">end</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="17">          <span style="color: #1f2328;">test_pid</span>
</div><div class="line" data-line="18">        <span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="19">      <span style="color: #1f2328;">]</span>
</div><div class="line" data-line="20">    <span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="21">
</div><div class="line" data-line="22">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="23">    <span style="color: #1f2328;">task</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Task</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">async</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Foo&quot;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="24">    <span style="color: #953800;">Task</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">await</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">task</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="25">    
</div><div class="line" data-line="26">    <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="27">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="28"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>Since we know that <code>Task.async/1</code> will add the test PID to the spawned process
callers list, we can look for it in the filter. <code>$callers</code> is basically a
name-dropping list. If you want to be at the party, you've got to tell them that
you know people.</p>
<h2><a href="#facebook-for-processes" aria-hidden="true" class="anchor" id="facebook-for-processes"></a>Facebook For Processes</h2>
<p>So what does NimbleOwnership do anyway? Oh, it's just a Facebook for processes!
It keeps track of friends lists in a large book. Here's a quick primer on what
NimbleOwnership lets us do:</p>
<ol>
<li><em>We</em> can <a href="https://hexdocs.pm/nimble_ownership/NimbleOwnership.html#start_link/1">start</a> a server, which is like a big book.</li>
<li><em>Processes</em> can
<a href="https://hexdocs.pm/nimble_ownership/NimbleOwnership.html#get_and_update/5">register</a>
arbitrary keys that symbolize resources. They are like different lists in the
book, with every list being tied to a process that registered it.</li>
<li><em>Owner processes</em> can
<a href="https://hexdocs.pm/nimble_ownership/NimbleOwnership.html#allow/5">allow</a> other
processes to access those keys. It's like adding a name to a list of invited
guests. But don't be fooled, &quot;allowance&quot; here is just a name on the list.
The server does not enforce this access in any way. It is a bookkeeper,
not a bouncer.</li>
<li><em>Anyone</em> can
<a href="https://hexdocs.pm/nimble_ownership/NimbleOwnership.html#fetch_owner/4">check</a>
if a process is on <em>any</em> list for a resource. This part is important and a
little counterintuitive; we'll take a closer look at it after an example.</li>
</ol>
<p>So, let's use NimbleOwnership for our test:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #953800;">Mix</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">install</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">[</span>
</div><div class="line" data-line="2">  <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:nimble_ownership</span><span style="color: #1f2328;">,</span> <span style="color: #0a3069;">&quot;~&gt; 1.0&quot;</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="3"><span style="color: #1f2328;">]</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="4">
</div><div class="line" data-line="5"><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">_</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">NimbleOwnership</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">name: </span><span style="color: #953800;">OwnershipServer</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="6"><span style="color: #953800;">ExUnit</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="7">
</div><div class="line" data-line="8"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">OtherTests</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="9">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="10">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="11">
</div><div class="line" data-line="12">  <span style="color: #cf222e;">for</span> <span style="color: #1f2328;">i</span> <span style="color: #0550ae;">&lt;-</span> <span style="color: #0550ae;">1</span><span style="color: #0550ae;">..</span><span style="color: #0550ae;">10</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="13">    <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test </span><span style="color: #1f2328;">#&lbrace;</span><span style="color: #1f2328;">i</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #0a3069;">&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="14">      <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="15">      <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Other tests&quot;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="16">    <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="17">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="18"><span style="color: #cf222e;">end</span>
</div><div class="line" data-line="19">
</div><div class="line" data-line="20"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyApp.LogHandler</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="21">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">behaviour </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="22">
</div><div class="line" data-line="23">  <span style="color: #0550ae;">@</span><span style="color: #0550ae;">impl </span><span style="color: #0550ae;">:logger_handler</span>
</div><div class="line" data-line="24">  <span style="color: #cf222e;">def</span> <span style="color: #6639ba;">log</span><span style="color: #1f2328;">(</span><span style="color: #57606a;">_event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="25">    <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">update</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">+</span> <span style="color: #0550ae;">1</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="26">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="27"><span style="color: #cf222e;">end</span>
</div><div class="line" data-line="28">
</div><div class="line" data-line="29"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">MyTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="30">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">async: </span><span style="color: #0550ae;">true</span>
</div><div class="line" data-line="31">  <span style="color: #cf222e;">require</span> <span style="color: #953800;">Logger</span>
</div><div class="line" data-line="32">
</div><div class="line" data-line="33">  <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;test log&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="34">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">agent</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">start_link</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">0</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="35">    
</div><div class="line" data-line="36">    <span style="color: #953800;">NimbleOwnership</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get_and_update</span><span style="color: #1f2328;">(</span>
</div><div class="line" data-line="37">      <span style="color: #953800;">OwnershipServer</span><span style="color: #1f2328;">,</span> <span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">nil</span> <span style="color: #0550ae;">-&gt;</span>
</div><div class="line" data-line="38">      <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">nil</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="39">    <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="40">
</div><div class="line" data-line="41">    <span style="color: #953800;">:logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">add_handler</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:handler_id</span><span style="color: #1f2328;">,</span> <span style="color: #953800;">MyApp.LogHandler</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="42">      <span style="color: #0550ae;">config: </span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="43">      <span style="color: #0550ae;">filters: </span><span style="color: #1f2328;">[</span>
</div><div class="line" data-line="44">        <span style="color: #0550ae;">ownership_filter: </span><span style="color: #1f2328;">&lbrace;</span>
</div><div class="line" data-line="45">          <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">event</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">handler_id</span> <span style="color: #0550ae;">-&gt;</span>
</div><div class="line" data-line="46">            <span style="color: #1f2328;">callers</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">:&quot;$callers&quot;</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">||</span> <span style="color: #1f2328;">[</span><span style="color: #1f2328;">]</span>
</div><div class="line" data-line="47">            
</div><div class="line" data-line="48">            <span style="color: #953800;">OwnershipServer</span>
</div><div class="line" data-line="49">            <span style="color: #0550ae;">|&gt;</span> <span style="color: #953800;">NimbleOwnership</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">fetch_owner</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">[</span><span style="color: #6639ba;">self</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">|</span> <span style="color: #1f2328;">callers</span><span style="color: #1f2328;">]</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">handler_id</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="50">            <span style="color: #0550ae;">|&gt;</span> <span style="color: #cf222e;">case</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="51">              <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:ok</span><span style="color: #1f2328;">,</span> <span style="color: #57606a;">_owner_id</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">event</span>
</div><div class="line" data-line="52">              <span style="color: #57606a;">_</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #0550ae;">:stop</span>
</div><div class="line" data-line="53">            <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="54">          <span style="color: #cf222e;">end</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="55">          <span style="color: #0550ae;">:handler_id</span>
</div><div class="line" data-line="56">        <span style="color: #1f2328;">&rbrace;</span>
</div><div class="line" data-line="57">      <span style="color: #1f2328;">]</span>
</div><div class="line" data-line="58">    <span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="59">
</div><div class="line" data-line="60">    <span style="color: #953800;">Process</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">sleep</span><span style="color: #1f2328;">(</span><span style="color: #0550ae;">100</span><span style="color: #1f2328;">)</span>    
</div><div class="line" data-line="61">    <span style="color: #1f2328;">task</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Task</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">async</span><span style="color: #1f2328;">(</span><span style="color: #cf222e;">fn</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #953800;">Logger</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">info</span><span style="color: #1f2328;">(</span><span style="color: #0a3069;">&quot;Foo&quot;</span><span style="color: #1f2328;">)</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="62">    <span style="color: #953800;">Task</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">await</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">task</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="63">    
</div><div class="line" data-line="64">    <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Agent</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">get</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">agent</span><span style="color: #1f2328;">,</span> <span style="color: #cf222e;">fn</span> <span style="color: #1f2328;">counter</span> <span style="color: #0550ae;">-&gt;</span> <span style="color: #1f2328;">counter</span> <span style="color: #cf222e;">end</span><span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #0550ae;">1</span>
</div><div class="line" data-line="65">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="66"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>There aren't many changes. Let's break them down.</p>
<p>First, at the very beginning, we start the <code>OwnershipServer</code> process - just a big book.</p>
<p>Then, at the beginning of the test, before we do anything of interest, we create
a key <code>:handler_id</code> in the server. This key is the resource we're talking about.
A list in the book with just one name on it – the test PID, which created the
list.</p>
<p>Our logger filter is both the bouncer and a partygoer. It finds itself inside a
task and all it knows is that it wants to knock on the door with a <code>:handler_id</code>
label on it, but <em>only if it is invited</em>. So it goes to the <code>OwnershipServer</code>
(everybody knows <code>OwnershipServer</code>, a nice chap and very popular) and
asks a question you won't normally hear at a party: &quot;Hey, has <em>anyone</em> allowed
me in? I know <code>$callers</code> by the way.&quot; And the answer is &quot;Yes,&quot; because, even
though our test did not allow the task explicitly, it allowed itself. Or rather,
it <em>owns</em> the place. So yeah, if you can name-drop the owner of the house, go
ahead.</p>
<p>Note that, in the general case, processes can create lists for the same key. So,
when we ask the server if anyone allowed us in, we don't really care <em>who</em>
allowed us. All we care about is that somebody, somewhere is expecting us. <span
class="emoji">🥰🥰🥰</span></p>
<p>For this reason, keys are often unique to the test, and that's what we rely on
in our example. If some other test creates a <code>:handler_id</code> resource, our filter
will allow it too. In some cases, this is useful, but for our handler example,
we wouldn't want that to happen.</p>
<h3><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h3>
<p>Most of the time, you won't have to deal with low-level ownership machinery. But
understanding what caller tracking is and how it propagates is vital to writing
asynchronous tests. That said, if you ever find yourself testing handlers or
other global resources, remember that it's very doable to limit access to them
even in a highly concurrent environment. You'll just need a good bouncer.</p> ]]></description>
    </item>
    <item>
       <title>List of True Things About Elixir Logger Handlers and Backends</title>
       <link>https://distantprovince.by/posts/list-of-true-things-about-elixir-logger-handlers-and-backends/</link>
       <pubDate>Mon, 30 Sep 2024 10:32:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/list-of-true-things-about-elixir-logger-handlers-and-backends/</guid>
       <description><![CDATA[ <h1><a href="#list-of-true-things-about-elixir-logger-handlers-and-backends" aria-hidden="true" class="anchor" id="list-of-true-things-about-elixir-logger-handlers-and-backends"></a>List of True Things About Elixir Logger Handlers and Backends</h1>
<p>Dear reader, I am easily confused by loggers. For a long time, the Elixir logger was
simple enough that I could live with it. But starting with version 1.15, something
changed, and now I'm confused.</p>
<p>Luckily for me, <a href="https://hexdocs.pm/logger/Logger.html">Elixir.Logger</a>
documentation does an excellent job explaining everything. The problem is that
it's a lengthy read and I need a refresher the moment I take my eyes off it.</p>
<p>This post is an attempt to complement the official docs by not adding more
documentation, but rather by taking things out.</p>
<h2><a href="#about-logger-handlers" aria-hidden="true" class="anchor" id="about-logger-handlers"></a>About Logger Handlers</h2>
<ul>
<li>Elixir Logging is configured using <em>logger handlers</em>.</li>
<li>A <em>logger handler</em> is an Erlang concept.</li>
<li>The default <em>logger handler</em> is <a href="https://www.erlang.org/doc/apps/kernel/logger_std_h.html"><code>:logger_std_h</code></a></li>
<li>Logger handlers are called in the client process.</li>
<li>It is recommended that logger handlers implement something called <em>overload protection</em>.</li>
<li><code>:logger_std_h</code> does implement overload protection.</li>
<li>Logger handlers are configured under your application's namespace:
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">config :my_app, :logger ...
</div></code></pre>
</li>
<li>The <em>default</em> logger handler can be configured under the <code>:logger</code> namespace:
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">config :logger, :default_handler ...
</div></code></pre>
</li>
<li>Non-default logger handlers need to be attached to your application on startup
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-plaintext" translate="no" tabindex="0"><div class="line" data-line="1">Logger.add_handlers(:my_app)
</div></code></pre>
</li>
</ul>
<h2><a href="#about-logger-backends" aria-hidden="true" class="anchor" id="about-logger-backends"></a>About Logger Backends</h2>
<ul>
<li><code>LoggerBackends</code> is a <a href="https://hex.pm/packages/logger_backends">Hex package</a>.</li>
<li><code>LoggerBackends</code> code is also included in core Elixir for backward compatibility.</li>
<li><code>LoggerBackends</code> was the primary logging mechanism in Elixir before version 1.15.</li>
<li><code>LoggerBackends</code> provides a <em>logger handler</em> called <code>LoggerBackends.Handler</code>.</li>
<li><code>LoggerBackends.Handler</code> implements <em>overload protection</em>.</li>
<li>A <em>logger backend</em> is a concept unique to <code>LoggerBackends</code>.</li>
<li>A <em>logger backend</em> is a module that can be plugged into <code>LoggerBackends.Handler</code>.</li>
<li>All <em>logger backends</em> benefit from <em>overload protection</em> implemented in <code>LoggerBackends.Handler</code>.</li>
<li><em>Logger backends</em> run in a single separate process.</li>
<li>It is possible to unknowingly use <code>LoggerBackends</code> via legacy configuration options.</li>
</ul>
<h2><a href="#about-extension-points" aria-hidden="true" class="anchor" id="about-extension-points"></a>About Extension Points</h2>
<ul>
<li>It is very rare that you need to implement your own <em>logger handler</em>.</li>
<li><em>Logger handlers</em> can be extended using <a href="https://www.erlang.org/doc/apps/kernel/logger_chapter.html#filters">Filters</a> and <a href="https://www.erlang.org/doc/apps/kernel/logger_chapter.html#formatters">Formatters</a>.</li>
<li>It is normal to employ multiple <em>logger handlers</em>.</li>
<li>An example of a custom <em>logger handler</em> use case is <a href="https://hexdocs.pm/sentry/Sentry.LoggerHandler.html"><code>Sentry.LoggerHandler</code></a>.</li>
<li>An example of a custom <em>logger formatter</em> use case is the <a href="https://hexdocs.pm/logger_json/readme.html"><code>LoggerJSON</code></a> suite.</li>
</ul>
<figure>
<pre>
┌──────┐
│ Logs │
└──┬───┘
   │
   ├───────────────────┬───────────────────┐
   │                   │                   │
   ▼                   ▼                   ▼
┌──────────────┐┌──────────────┐┌──────────────────────┐
│logger handler││logger handler││LoggerBackends.Handler│
└──────────────┘└──────────────┘└──────────┬───────────┘
                                           │
                                     ┌─────┴─────┐
                                     │           │
                                     ▼           ▼
                                ┌──────────┐┌──────────┐
                                │  logger  ││  logger  │
                                │ backend  ││ backend  │
                                └──────────┘└──────────┘
</pre>
<figcaption>
I imagine <code>LoggerBackends</code> as a mama duck who protects its little backendlings
from the need to implement overload protection
</figcaption>
</figure>
<h2><a href="#list-of-questionable-advice" aria-hidden="true" class="anchor" id="list-of-questionable-advice"></a>List of Questionable Advice</h2>
<ol>
<li>If you have a pre-1.15 Elixir app on your hands, migrate it to logger handlers.</li>
<li>Forget logger backends exist to avoid confusion.</li>
<li>Whenever you need to customize your logging stack, try doing it with filters and formatters.</li>
<li>If it doesn't work, consider remembering logger backends exist and implement your own to piggyback on built-in overload protection.</li>
<li>If you still need to implement a custom logger handler, godspeed <span class="emoji">🫡</span></li>
</ol> ]]></description>
    </item>
    <item>
       <title>Do Repeat Yourself</title>
       <link>https://distantprovince.by/posts/do-repeat-yourself/</link>
       <pubDate>Sat, 14 Sep 2024 14:23:00 UTC</pubDate>
       <guid>https://distantprovince.by/posts/do-repeat-yourself/</guid>
       <description><![CDATA[ <h1><a href="#do-repeat-yourself" aria-hidden="true" class="anchor" id="do-repeat-yourself"></a>Do Repeat Yourself</h1>
<p>The conventional wisdom stemming from the famous DRY (Don't Repeat Yourself)
principle is that if you notice yourself copying code around, it's a smell. And
I agree. Mostly. More often than not. Listen, it's complicated. I think this is
a good principle, but even a perfectly functioning analog clock can be wrong for
half a year, okay?</p>
<p>One of the few strong opinions I have is that it's not only acceptable but
highly desirable to copy code around when you're writing <strong>tests</strong>. And this has
very little to do with the code itself; it's mostly a people thing.</p>
<h2><a href="#sorry-tests" aria-hidden="true" class="anchor" id="sorry-tests"></a>Sorry, tests</h2>
<p>Let me tell you a little about myself. Before I discovered Elixir and decided
that this language was actually nice enough to make it my full-time job, I was a
test engineer. I was testing things for a living. I still wrote code, but that
was all test code. And I was writing a lot of it. Not only I, but other test
engineers were writing it too. And even regular developers. <em>Sometimes</em>.</p>
<p>When all you write is tests, all your engineering ambitions are funneled into
making them good, in a conventional sense. Clean code, SOLID code, and of
course, DRY code. But if you work long enough, you at some point face an
inevitable question: if our tests are so good, why don't <em>developers</em> write it
more often? They certainly have the skills; they deal with much more complex
code every day. And this is when I had a simple revelation:</p>
<ol>
<li>People see tests as fundamentally secondary part of the codebase.</li>
<li>Dealing with test code is, therefore, a chore that people will actively try
to avoid.</li>
</ol>
<p>This is it. That's the harsh truth. The content of the <code>lib/</code> folder is just not
the same as what's inside the <code>test/</code> folder.</p>
<p>&quot;But TDD,&quot; I hear you saying. &quot;It's very popular; people <em>love</em> tests and see
value in them.&quot; Of course! A lot of people love writing tests. <em>Writing</em>.
Write-time is the honeymoon phase for a test; it's when it's being cared for the
most. Unfortunately, it's all downhill from there. They will never see the same
treatment again, not even from their own creator.</p>
<figure>
<pre>
        ╭──────────────────────────╮
        │ i ain't reading all that │
        ╰──────────────────────────╯
        ╭───────────────────────╮
        │ i'm happy for you tho │
        ╰───────────────────────╯
        ╭────────────────────────╮
        │ or sorry that happened │
        ╰────────────────────────╯
╭────────╮
│ (⌐■_■) │
╰────────╯
</pre>
<figcaption>A common reaction to <code>mix test</code> output<a href="https://knowyourmeme.com/photos/1876233-i-aint-reading-all-that">.</a></figcaption>
</figure>
<h2><a href="#copy-copy-copy" aria-hidden="true" class="anchor" id="copy-copy-copy"></a>Copy copy copy</h2>
<p>But what does it have to do with DRY? The thing is, we know that tests are
immensely useful, so we need to reduce our own aversion to them as much as
possible. And one of the ways to make it easier is to ditch DRY and indulge in
self-repetition. Here's an incomplete list of well-intended things people often
overuse:</p>
<ol>
<li>Extracting common parts into helper functions</li>
<li>Sharing sophisticated setup between tests</li>
<li>Using third-party testing libraries with a steep learning curve</li>
</ol>
<p>Next time you are tempted to create a test helper, think twice. Maybe it makes
total sense now. Sure, it's just one click away... No! Stop! People don't open
test files because they just feel like doing it. They do it because a test
fails, and by this time they're already annoyed. Not only do they not want to
read more than one test at a time, they will actively avoid doing so! Every
function that's defined somewhere will make their eyes roll. Every setup they
need to scroll to will make them angry. And if their rushed fix to a shared
setup breaks another test? They may just delete it, because code reviewers have
even less incentive to read it.</p>
<p>When writing a test, you have to remember that this is the most effort anybody
will ever put into it. Everybody else, including future you, will see this test
as a burden. A time capsule with annoying ancient puzzles. So try to fill it
with love instead, make a test that's easy to follow, that doesn't require any
previous reading or knowledge of that fancy <code>assert_nested_in_any_order</code> custom
function used in exactly two places throughout the codebase.</p>
<h2><a href="#however" aria-hidden="true" class="anchor" id="however"></a>HOWEVER</h2>
<p>Of course, you have to exercise your best judgment. I would be crazy to advocate
for not having any helpers at all. Take my factories from me, and I'm helpless.
Make me create a <code>conn</code> struct without a Phoenix helper, and I don't know how!</p>
<p>In fact, I have a confession to make. Whenever I want to test a large number of
cases that can be represented as a test table, I love employing what is usually
called <em>parameterized tests</em>:</p>
<pre class="lumis" style="color: #1f2328; background-color: #ffffff;"><code class="language-elixir" translate="no" tabindex="0"><div class="line" data-line="1"><span style="color: #57606a;"># Let&#39;s say we want to test a function that checks user permissions</span>
</div><div class="line" data-line="2"><span style="color: #57606a;"># depending on user type and resource privacy</span>
</div><div class="line" data-line="3">
</div><div class="line" data-line="4"><span style="color: #cf222e;">defmodule</span> <span style="color: #953800;">PermissionsTest</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="5">  <span style="color: #cf222e;">use</span> <span style="color: #953800;">ExUnit.Case</span>
</div><div class="line" data-line="6">
</div><div class="line" data-line="7">  <span style="color: #1f2328;">test_table</span> <span style="color: #0550ae;">=</span> <span style="color: #1f2328;">[</span>
</div><div class="line" data-line="8">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:guest</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:public</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">true</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="9">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:guest</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:private</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">false</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="10">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:guest</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:system</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">false</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="11">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:user</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:public</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">true</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="12">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:user</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:private</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">true</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="13">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:user</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:system</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">false</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="14">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:admin</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:public</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">true</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="15">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:admin</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:private</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">true</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="16">    <span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">:admin</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">:system</span><span style="color: #1f2328;">,</span> <span style="color: #0550ae;">true</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="17">  <span style="color: #1f2328;">]</span>
</div><div class="line" data-line="18">
</div><div class="line" data-line="19">  <span style="color: #cf222e;">for</span> <span style="color: #1f2328;">&lbrace;</span><span style="color: #1f2328;">user</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">resource</span><span style="color: #1f2328;">,</span> <span style="color: #1f2328;">exp_result</span><span style="color: #1f2328;">&rbrace;</span> <span style="color: #0550ae;">&lt;-</span> <span style="color: #1f2328;">test_table</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="20">    <span style="color: #6639ba;">test</span> <span style="color: #0a3069;">&quot;</span><span style="color: #1f2328;">#&lbrace;</span><span style="color: #1f2328;">user</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #0a3069;"> access </span><span style="color: #1f2328;">#&lbrace;</span><span style="color: #1f2328;">resource</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #0a3069;"> resource&quot;</span> <span style="color: #cf222e;">do</span>
</div><div class="line" data-line="21">      <span style="color: #1f2328;">user</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Users</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">create_user</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">type: </span><span style="color: #cf222e;">unquote</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">user</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="22">      <span style="color: #1f2328;">resource</span> <span style="color: #0550ae;">=</span> <span style="color: #953800;">Resources</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">create_resource</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">%</span><span style="color: #1f2328;">&lbrace;</span><span style="color: #0550ae;">privacy: </span><span style="color: #cf222e;">unquote</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">resource</span><span style="color: #1f2328;">)</span><span style="color: #1f2328;">&rbrace;</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="23">
</div><div class="line" data-line="24">      <span style="color: #6639ba;">assert</span> <span style="color: #953800;">Permissions</span><span style="color: #0550ae;">.</span><span style="color: #6639ba;">has_access?</span><span style="color: #1f2328;">(</span>
</div><div class="line" data-line="25">        <span style="color: #1f2328;">user</span><span style="color: #1f2328;">,</span>
</div><div class="line" data-line="26">        <span style="color: #1f2328;">resource</span>
</div><div class="line" data-line="27">      <span style="color: #1f2328;">)</span> <span style="color: #0550ae;">==</span> <span style="color: #cf222e;">unquote</span><span style="color: #1f2328;">(</span><span style="color: #1f2328;">exp_result</span><span style="color: #1f2328;">)</span>
</div><div class="line" data-line="28">    <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="29">  <span style="color: #cf222e;">end</span>
</div><div class="line" data-line="30"><span style="color: #cf222e;">end</span>
</div></code></pre>
<p>As you see, I commit a serious crime of meta-programming in a test, but test
tables are just too good. In my defense here, this still reads like a single
independent test; it just has a neat little truth table that is right over here
in its fullness.</p>
<h2><a href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
<p>For better or worse, people tend to see test code as less important. And while
it may be tempting to fight for higher standards and better tests, I'd argue
it's wiser to accept the reality and give both tests and people some slack. It
doesn't matter if your test file is 2,000 lines long if you don't have to read
it all at once.</p> ]]></description>
    </item>
  </channel>
</rss>
