<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Distributed-Systems | AkitaOnRails.com</title><link>https://akitaonrails.github.io/en/tags/distributed-systems/</link><description>Fabio Akita's blog — tech, career, and assorted geek topics. English edition.</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Oct 2026 23:20:51 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/en/tags/distributed-systems/index.xml" rel="self" type="application/rss+xml"/><item><title>Async Jobs That Survive Chaos | Behind the Scenes of The M.Akita Chronicles</title><link>https://akitaonrails.github.io/en/2026/02/19/async-jobs-that-survive-chaos-behind-the-m-akita-chronicles/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/02/19/async-jobs-that-survive-chaos-behind-the-m-akita-chronicles/</guid><pubDate>Thu, 19 Feb 2026 01:10:08 +0000</pubDate><description>&lt;p&gt;This post is part of a series; follow along through the tag &lt;a href="https://akitaonrails.github.io/en/tags/the-m-akita-chronicles/"&gt;/themakitachronicles&lt;/a&gt;. This is part 5.&lt;/p&gt;
&lt;p&gt;And make sure to subscribe to my new newsletter &lt;a href="https://themakitachronicles.com/"target="_blank" rel="noopener"&gt;The M.Akita Chronicles&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&amp;ndash;&lt;/p&gt;
&lt;p&gt;Let me be blunt: most developers treat background jobs as if they were scripts that run once and that&amp;rsquo;s it. &amp;ldquo;Ah, throw it in a Sidekiq and we&amp;rsquo;re good.&amp;rdquo; No, &lt;strong&gt;we&amp;rsquo;re not good&lt;/strong&gt;. Jobs that handle important things — sending emails, publishing content, calling external APIs — need to be treated as first-class citizens in your architecture.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This post is part of a series; follow along through the tag <a href="/en/tags/the-m-akita-chronicles/">/themakitachronicles</a>. This is part 5.</p>
<p>And make sure to subscribe to my new newsletter <a href="https://themakitachronicles.com/"target="_blank" rel="noopener">The M.Akita Chronicles</a>!</p>
<p>&ndash;</p>
<p>Let me be blunt: most developers treat background jobs as if they were scripts that run once and that&rsquo;s it. &ldquo;Ah, throw it in a Sidekiq and we&rsquo;re good.&rdquo; No, <strong>we&rsquo;re not good</strong>. Jobs that handle important things — sending emails, publishing content, calling external APIs — need to be treated as first-class citizens in your architecture.</p>
<p>In this post I&rsquo;ll show how Rails 8 with ActiveJob and SolidQueue changed my perspective on async processing. This is not theory — these are patterns that emerged from a real system running in production every week.</p>
<h2>The Problem: Jobs Are Fragile by Nature<span class="hx:absolute hx:-mt-20" id="the-problem-jobs-are-fragile-by-nature"></span>
    <a href="#the-problem-jobs-are-fragile-by-nature" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Picture the scenario: you have a job that assembles a newsletter, publishes it on a blog through the GitHub API, waits for a podcast to be ready, and then fires off emails to hundreds of subscribers. Any step can fail. The API can time out. The podcast can take longer than expected. The server can restart in the middle of the delivery.</p>
<p>If you treat this as a linear script, you&rsquo;re going to suffer. The right question is not &ldquo;how do I make this job run?&rdquo; — it&rsquo;s <strong>&ldquo;how do I make this job recover when something goes wrong?&rdquo;</strong></p>
<h2>Pattern 1: retry_on With Specific Exceptions<span class="hx:absolute hx:-mt-20" id="pattern-1-retry_on-with-specific-exceptions"></span>
    <a href="#pattern-1-retry_on-with-specific-exceptions" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>ActiveJob&rsquo;s <code>retry_on</code> is absurdly powerful, but most people use it wrong. Look at the classic anti-pattern:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MeuJob</span> <span class="o">&lt;</span> <span class="no">ApplicationJob</span>
</span></span><span class="line"><span class="cl">  <span class="n">retry_on</span> <span class="no">StandardError</span><span class="p">,</span> <span class="ss">wait</span><span class="p">:</span> <span class="mi">5</span><span class="o">.</span><span class="n">seconds</span><span class="p">,</span> <span class="ss">attempts</span><span class="p">:</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This retries <strong>any</strong> error 3 times and gives up. It&rsquo;s useless for almost anything in the real world. The pattern that actually works is to create specific exceptions for transient states:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PodcastNotReady</span> <span class="o">&lt;</span> <span class="no">StandardError</span><span class="p">;</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PublishAndSendJob</span> <span class="o">&lt;</span> <span class="no">ApplicationJob</span>
</span></span><span class="line"><span class="cl">  <span class="n">retry_on</span> <span class="no">PodcastNotReady</span><span class="p">,</span> <span class="ss">wait</span><span class="p">:</span> <span class="mi">15</span><span class="o">.</span><span class="n">minutes</span><span class="p">,</span> <span class="ss">attempts</span><span class="p">:</span> <span class="mi">16</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">newsletter_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">metadata</span> <span class="o">=</span> <span class="n">check_podcast_metadata</span><span class="p">(</span><span class="n">newsletter_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="no">PodcastNotReady</span><span class="p">,</span> <span class="s2">&#34;Aguardando podcast&#34;</span> <span class="k">unless</span> <span class="n">metadata</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># keep working...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>See what&rsquo;s happening? The job waits up to <strong>4 hours</strong> (15min × 16) for the podcast to be ready, polling every 15 minutes. This is not an infinite loop — it has a clear limit. And when it hits that limit, you handle the timeout explicitly:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">rescue</span> <span class="no">PodcastNotReady</span> <span class="o">=&gt;</span> <span class="n">e</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">executions</span> <span class="o">&gt;=</span> <span class="mi">16</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Publish without podcast and notify the team</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish_without_podcast</span><span class="p">(</span><span class="n">newsletter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">notify_team</span><span class="p">(</span><span class="s2">&#34;Podcast não ficou pronto a tempo&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="c1"># re-raise so SolidQueue schedules the next retry</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is <strong>graceful degradation</strong>. The system doesn&rsquo;t stop because one component failed — it adapts and keeps going.</p>
<h2>Pattern 2: Distributed Locks<span class="hx:absolute hx:-mt-20" id="pattern-2-distributed-locks"></span>
    <a href="#pattern-2-distributed-locks" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>When you have multiple paths that can trigger the same job (a safety cron, a manual trigger, an API), you need to make sure only one instance runs at a time.</p>
<p>The concept is simple: a file-based (or database-based) lock that expires automatically:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">DeployLock</span>
</span></span><span class="line"><span class="cl">  <span class="no">LOCK_DIR</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">root</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;tmp/locks&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">DEFAULT_TTL</span> <span class="o">=</span> <span class="mi">30</span><span class="o">.</span><span class="n">minutes</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">with_lock</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="ss">ttl</span><span class="p">:</span> <span class="no">DEFAULT_TTL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">acquire!</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="ss">ttl</span><span class="p">:</span> <span class="n">ttl</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">yield</span>
</span></span><span class="line"><span class="cl">  <span class="k">ensure</span>
</span></span><span class="line"><span class="cl">    <span class="n">release</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>With SQLite, this is trivial — no Redis, no external coordination. The lock has a TTL so it doesn&rsquo;t hang forever if the process dies. And the <code>ensure</code> guarantees it releases even if the block raises an exception.</p>
<h2>Pattern 3: Atomic Email Delivery<span class="hx:absolute hx:-mt-20" id="pattern-3-atomic-email-delivery"></span>
    <a href="#pattern-3-atomic-email-delivery" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This is where most newsletter systems fail spectacularly. The scenario: you&rsquo;re sending 500 emails and the server restarts on email 247. What happens when the job re-runs?</p>
<p>If you just iterated a list, you&rsquo;re going to resend the first 247. Your subscribers will love getting the newsletter twice.</p>
<p>The solution is <strong>atomic claiming</strong> per recipient:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Before sending, create a record per subscriber</span>
</span></span><span class="line"><span class="cl"><span class="n">subscribers</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="nb">sub</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="no">EmailDelivery</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="ss">newsletter</span><span class="p">:</span> <span class="n">newsletter</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">subscriber</span><span class="p">:</span> <span class="nb">sub</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="ss">status</span><span class="p">:</span> <span class="s2">&#34;pending&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># At send time, do an atomic claim</span>
</span></span><span class="line"><span class="cl"><span class="n">delivery</span> <span class="o">=</span> <span class="no">EmailDelivery</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">status</span><span class="p">:</span> <span class="o">[</span><span class="s2">&#34;pending&#34;</span><span class="p">,</span> <span class="s2">&#34;failed&#34;</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">lock</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">delivery</span><span class="o">.</span><span class="n">update!</span><span class="p">(</span><span class="ss">status</span><span class="p">:</span> <span class="s2">&#34;sending&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">send_email</span><span class="p">(</span><span class="n">delivery</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">delivery</span><span class="o">.</span><span class="n">update!</span><span class="p">(</span><span class="ss">status</span><span class="p">:</span> <span class="s2">&#34;sent&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If the server dies between <code>sending</code> and <code>sent</code>, the record stays as &ldquo;sending&rdquo; — and a recovery job (<code>RecoverStaleDeliveriesJob</code>) periodically moves those records to &ldquo;unknown&rdquo; after a timeout, so they never get automatically resent. Ambiguous emails are <strong>never</strong> resent automatically. That&rsquo;s the kind of detail that separates a hobby system from a production system.</p>
<h2>Pattern 4: Orchestrator Jobs<span class="hx:absolute hx:-mt-20" id="pattern-4-orchestrator-jobs"></span>
    <a href="#pattern-4-orchestrator-jobs" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A common mistake is cramming too much logic into a single job. The pattern that works is having <strong>orchestrator jobs</strong> that delegate to specialized jobs:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>PublishAndSendJob (orchestrator)
  ├── Waits for podcast (retry_on PodcastNotReady)
  ├── PublishToBlogJob.publish(newsletter)
  └── SendNewsletterJob.perform_now(newsletter.id)
        └── SendSingleEmailJob (per subscriber)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The orchestrator coordinates the sequence. Each specialized job knows how to do exactly one thing and can be re-executed independently. If email sending fails, you can retrigger just the <code>SendNewsletterJob</code> without republishing the blog.</p>
<h2>Pattern 5: Safety Nets With Cron<span class="hx:absolute hx:-mt-20" id="pattern-5-safety-nets-with-cron"></span>
    <a href="#pattern-5-safety-nets-with-cron" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Don&rsquo;t trust a single execution chain. SolidQueue&rsquo;s cron (<code>config/recurring.yml</code>) acts as a safety net:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">send_newsletter</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">class</span><span class="p">:</span><span class="w"> </span><span class="l">SendNewsletterJob</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">schedule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;0 12 * * 1&#34;</span><span class="w">  </span><span class="c"># Monday 9am BRT (UTC-3)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If the 7am <code>PublishAndSendJob</code> failed catastrophically, the 9am cron fires the <code>SendNewsletterJob</code> as a fallback. The job checks whether the newsletter has already been sent and no-ops if so. <strong>Idempotency</strong> is the keyword here — the job can run as many times as you want and the result is always the same.</p>
<p>But watch out for the inverse: a job that reschedules itself infinitely when it finds no work is a ticking time bomb. If the job doesn&rsquo;t find a ready newsletter, it simply returns. The cron handles trying again the next week.</p>
<h2>Pattern 6: Status Notifications<span class="hx:absolute hx:-mt-20" id="pattern-6-status-notifications"></span>
    <a href="#pattern-6-status-notifications" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Every long-running job should communicate its progress. A simple concern handles it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">DiscordStatus</span>
</span></span><span class="line"><span class="cl">  <span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">notify_start</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">DiscordNotifier</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">channel</span><span class="p">:</span> <span class="n">status_channel</span><span class="p">,</span> <span class="ss">text</span><span class="p">:</span> <span class="s2">&#34;▶️ </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">notify_done</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">DiscordNotifier</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">channel</span><span class="p">:</span> <span class="n">status_channel</span><span class="p">,</span> <span class="ss">text</span><span class="p">:</span> <span class="s2">&#34;✅ </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">notify_error</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">DiscordNotifier</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="ss">channel</span><span class="p">:</span> <span class="n">status_channel</span><span class="p">,</span> <span class="ss">text</span><span class="p">:</span> <span class="s2">&#34;❌ </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Each job includes the concern and calls <code>notify_start</code> at the beginning, <code>notify_done</code> on success, <code>notify_error</code> on rescue. When something goes wrong at 3am, you wake up and see exactly where it stopped — no digging through logs required.</p>
<h2>SolidQueue: The End of Redis Dependency<span class="hx:absolute hx:-mt-20" id="solidqueue-the-end-of-redis-dependency"></span>
    <a href="#solidqueue-the-end-of-redis-dependency" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A note on SolidQueue, which ships as default in Rails 8: using the same SQLite database for jobs and application data <strong>drastically</strong> simplifies operations. No need for a separate Redis running. No need to worry about Redis restarting and losing jobs that were in memory.</p>
<p>Jobs are persisted in the database. If the server restarts, they&rsquo;re there waiting. The retry state is preserved. It&rsquo;s absurdly simpler than the alternative, and for 99% of cases, the performance is more than enough.</p>
<h2>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Rails 8 with ActiveJob and SolidQueue didn&rsquo;t invent anything revolutionary. What it did was make it ridiculously easy to implement patterns that used to require heavy infrastructure:</p>
<ul>
<li><strong>retry_on</strong> with specific exceptions and clear limits</li>
<li><strong>Distributed locks</strong> with automatic TTL</li>
<li><strong>Atomic claiming</strong> for operations that can&rsquo;t duplicate</li>
<li><strong>Orchestrator jobs</strong> that delegate and recover</li>
<li><strong>Safety crons</strong> as fallback</li>
<li><strong>Notifications</strong> for operational visibility</li>
</ul>
<p>None of these patterns is complicated on its own. Put them together, though, and that&rsquo;s what makes the difference between a system that breaks at the first failure and one that holds up in production — and lets you sleep peacefully on Monday.</p>
]]></content:encoded><category>the-m-akita-chronicles</category><category>rails</category><category>distributed-systems</category></item><item><title>Ex Manga Downloadr - Part 6: The Rise of FLOW</title><link>https://akitaonrails.github.io/en/2017/06/13/ex-manga-downloadr-part-6-the-rise-of-flow/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2017/06/13/ex-manga-downloadr-part-6-the-rise-of-flow/</guid><pubDate>Tue, 13 Jun 2017 15:59:00 -0300</pubDate><description>&lt;p&gt;It&amp;rsquo;s been way more than a year since I posted about my pet project &lt;a href="https://github.com/akitaonrails/ex_manga_downloadr"target="_blank" rel="noopener"&gt;Ex Manga Downloadr&lt;/a&gt;. Since then I just did small updates to keep up with the current Elixir and libraries versions.&lt;/p&gt;
&lt;p&gt;As a quick recap, the exercise is that I want to scrape MangaReader.net for a bunch of images, organized in pages and chapters, and in the end it should compile organized PDFs so I can load them on a Kindle device.&lt;/p&gt;</description><content:encoded><![CDATA[<p>It&rsquo;s been way more than a year since I posted about my pet project <a href="https://github.com/akitaonrails/ex_manga_downloadr"target="_blank" rel="noopener">Ex Manga Downloadr</a>. Since then I just did small updates to keep up with the current Elixir and libraries versions.</p>
<p>As a quick recap, the exercise is that I want to scrape MangaReader.net for a bunch of images, organized in pages and chapters, and in the end it should compile organized PDFs so I can load them on a Kindle device.</p>
<p>Web scraping is a simple loop of HTTP GETs over a ton of URLs, scraping the HTML, and fetching more URLs to download.</p>
<p>In many simple languages, people usually solve this naively in 2 ways:</p>
<ul>
<li>A simple nested loop. One single thread, sequential fetches. So if you have 5,000 links and each links takes 10 seconds to fetch, it&rsquo;s basically 10 * 5,000 = 50,000 seconds, which is a stupid long time.</li>
<li>A simple spawn of processes, fibers, threads or parallel I/O in a reactor, all at once. An attempt to parallelize all fetches at once.</li>
</ul>
<p>Everybody probably agrees that the first option is stupid. Now, the second one is tricky.</p>
<p>The tricky part is <strong>CONTROL</strong>.</p>
<p>Anyone in Go would say <em>&ldquo;oh, this is easy, just put a loop and spawn a bunch of goroutines&rdquo;</em> or anyone in Node.js would say <em>&ldquo;oh, this is easy, just put a loop, make the fetch - they all will run asynchronously - and add callbacks, a basic async/await.&rdquo;</em></p>
<p>They&rsquo;re not wrong, but this is still too naive an implementation. It&rsquo;s trivial to trigger hundreds or thousands of parallel requests. Now, what happens if one fails and you have to retry? What happens if the MangaReader has a throttling system that will either start cutting down connections or timing them out? Or if your internet bandwidth is just not enough, and after a certain amount of requests you start having diminishing returns and time outs?</p>
<p>The message is: it&rsquo;s damn trivial to spawn parallel stuff. it&rsquo;s damn complicated to control parallel stuff.</p>
<p>This is why, in my first implementation in Elixir, I introduced a complicated implementation using a combination of a custom GenServer, Elixir&rsquo;s own Task infrastructure for async/await pattern, and <a href="https://github.com/devinus/poolboy"target="_blank" rel="noopener">Poolboy</a> to control the rate of the parallelism. This is how you control the bottleneck out to slow resources: using pools and queues (which is why every good database has a connection pool, remember <a href="https://sourceforge.net/projects/c3p0/"target="_blank" rel="noopener">C3P0</a>?)</p>
<p>This is one snippet of my older implementation:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">chapter_page</span><span class="p">(</span><span class="o">[</span><span class="n">chapter_link</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Task</span><span class="o">.</span><span class="n">Supervisor</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="no">Fetcher</span><span class="o">.</span><span class="n">TaskSupervisor</span><span class="p">,</span> <span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:poolboy</span><span class="o">.</span><span class="n">transaction</span> <span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">{</span><span class="ss">:chapter_page</span><span class="p">,</span> <span class="n">chapter_link</span><span class="p">,</span> <span class="n">source</span><span class="p">},</span> <span class="vi">@genserver_call_timeout</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">,</span> <span class="vi">@task_async_timeout</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Yes, it&rsquo;s very ugly, and there are boilerplates for the GenServer, the custom Supervisor to initialize Poolboy and so on. And the higher level workflow code looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pages</span><span class="p">({</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">pages_list</span> <span class="o">=</span> <span class="n">chapter_list</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Worker</span><span class="o">.</span><span class="n">chapter_page</span><span class="p">(</span><span class="o">[&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="vi">@await_timeout_ms</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">list</span><span class="p">},</span> <span class="n">acc</span> <span class="o">-&gt;</span> <span class="n">acc</span> <span class="o">++</span> <span class="n">list</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="n">pages_list</span><span class="p">,</span> <span class="n">source</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, inside the <code>Worker</code> module each public method wraps the GenServer internal calls into a <code>Task async</code> and in the collection iteration we add <code>Task.await</code> to actually wait for all parallel calls to finish, so we can finally reduce the results.</p>
<p>Elixir now comes with this very interesting <a href="https://elixir-lang.org/blog/2016/07/14/announcing-genstage/"target="_blank" rel="noopener"><code>GenStage</code></a> infrastructure that promises to replace <code>GenEvent</code> and the use case is when you have a producer-consumer situation with back-pressure. Basically when you have slow endpoints and you would end up having to control bottlenecks.</p>
<p>Then, <a href="https://github.com/elixir-lang/flow"target="_blank" rel="noopener">Flow</a> is an easier high abstraction that you can use almost the same way you would use <code>Enum</code> in your collections, but instead of sequential mapping, it takes care of parallel traversing and control of batches. So the code is very similar but without you having to control the parallelization controls manually.</p>
<p>This is the <a href="https://github.com/akitaonrails/ex_manga_downloadr/commit/b117f5236098f6d37e332633acb787be46a09d84"target="_blank" rel="noopener">full commit</a> where I could remove Poolboy, remove my custom GenServer, reimplement the Worker as simple module of functions and then the workflow could get rid off the async/await pattern and use Flow instead:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pages</span><span class="p">({</span><span class="n">chapter_list</span><span class="p">,</span> <span class="n">source</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">pages_list</span> <span class="o">=</span> <span class="n">chapter_list</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Flow</span><span class="o">.</span><span class="n">from_enumerable</span><span class="p">(</span><span class="ss">max_demand</span><span class="p">:</span> <span class="vi">@max_demand</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Flow</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">MangaWrapper</span><span class="o">.</span><span class="n">chapter_page</span><span class="p">(</span><span class="o">[&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="n">source</span><span class="o">]</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Flow</span><span class="o">.</span><span class="n">partition</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Flow</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="o">[]</span> <span class="k">end</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">list</span><span class="p">},</span> <span class="n">acc</span> <span class="o">-&gt;</span> <span class="n">acc</span> <span class="o">++</span> <span class="n">list</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">to_list</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="n">pages_list</span><span class="p">,</span> <span class="n">source</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The only boilerplate left is the <code>Flow.from_enumerable()</code> and <code>Flow.partition()</code> wrapping the <code>Flow.map</code>, and that&rsquo;s it!</p>
<p>Notice I configured <code>@max_demand</code> to be 60. You must tweak it to be larger or smaller depending on your internet connection, you have to experiment it. By default, Flow will trigger 500 processes in parallel, which is way too much for web scraping on a normal home internet connection and you will suffer diminishing returns. That&rsquo;s what I had to do previously with Poolboy, by initiating a pool of around 60 transactions at most.</p>
<p>Unfortunately not everything is as straight forward as it seems. Running this new version on the test mode I get this result:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>58,85s user 13,93s system 37% cpu 3:13,78 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So a total time of more than 3 minutes, using around 37% of the available CPU.</p>
<p>My immediate previous version using all the shenanigans of Poolboy, Task.Supervisor, GenServer, etc still gives me this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>100,67s user 20,83s system 152% cpu 1:19,92 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Less than <strong>HALF</strong> the time, albeit using all my CPU cores. So my custom implementation still uses my resources to the maximum. There is still something in the Flow implementation I didn&rsquo;t quite get right. I already tried to bump up the <code>max_demand</code> from 60 up to 100 but that didn&rsquo;t improve anything. Leaving it to the default 500 slows everything down to more than 7 minutes.</p>
<p>All in all, at least it makes the implementation far easier on the eyes (hence, way easier to maintain), but either the Flow implementation has bottlenecks or I am using it wrong at this point. If you know what it is, let me know in the comments section below.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>performance</category><category>distributed-systems</category></item><item><title>Ex Pusher Lite - Part 3 - A Complete Solution</title><link>https://akitaonrails.github.io/en/2016/12/30/ex-pusher-lite-part-3-a-complete-solution/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/12/30/ex-pusher-lite-part-3-a-complete-solution/</guid><pubDate>Fri, 30 Dec 2016 17:38:00 -0200</pubDate><description>&lt;p&gt;It&amp;rsquo;s been over a year since I wrote the 2 pieces about my &amp;ldquo;Ex Pusher Lite&amp;rdquo; concept. The code from a year ago is already obsolete as I was still just learning my way through both Elixir and Phoenix.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve published an article about &lt;a href="http://www.akitaonrails.com/en/2016/12/06/coherence-e-exadmin-devise-e-activeadmin-para-phoenix"target="_blank" rel="noopener"&gt;ExAdmin and Coherence&lt;/a&gt; and another on &lt;a href="http://www.akitaonrails.com/en/2016/12/23/aplicativo-elixir-phoenix-deploy-balanceamento-carga-digitalocean"target="_blank" rel="noopener"&gt;Deploying Elixir to DigitalOcean&lt;/a&gt; this month.&lt;/p&gt;
&lt;p&gt;The idea is very simple, it is a homage to &lt;a href="https://pusher.com/"target="_blank" rel="noopener"&gt;Pusher&lt;/a&gt;. If you used Pusher before, this is very similar (albeit way less feature complete, of course).&lt;/p&gt;</description><content:encoded><![CDATA[<p>It&rsquo;s been over a year since I wrote the 2 pieces about my &ldquo;Ex Pusher Lite&rdquo; concept. The code from a year ago is already obsolete as I was still just learning my way through both Elixir and Phoenix.</p>
<p>I&rsquo;ve published an article about <a href="http://www.akitaonrails.com/en/2016/12/06/coherence-e-exadmin-devise-e-activeadmin-para-phoenix"target="_blank" rel="noopener">ExAdmin and Coherence</a> and another on <a href="http://www.akitaonrails.com/en/2016/12/23/aplicativo-elixir-phoenix-deploy-balanceamento-carga-digitalocean"target="_blank" rel="noopener">Deploying Elixir to DigitalOcean</a> this month.</p>
<p>The idea is very simple, it is a homage to <a href="https://pusher.com/"target="_blank" rel="noopener">Pusher</a>. If you used Pusher before, this is very similar (albeit way less feature complete, of course).</p>
<p>I built an entire solution inspired by Pusher, using the Phoenix framework, deployed to Digital Ocean and you can test it out right now, just sign up at <a href="http://expusherlite.cm42.io"target="_blank" rel="noopener">expusherlite.cm42.io</a>.</p>
<p>Once you sign up, you will have a secret token (don&rsquo;t disclose that, of course) and you will be under an Organization. Then you can go on and create Applications within that Organization. Each Application will have a unique token to identify it.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/576/expusherlite_cm42_io_registrations.png" alt="dashboard"  loading="lazy" /></p>
<p>Now, let&rsquo;s say you want to create a Rails application with a Chat feature. Any version of Rails, you don&rsquo;t need 5.0 and you don&rsquo;t need ActionCable.</p>
<p>First off, let&rsquo;s configure <code>config/secrets.yml</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>development:
  secret_key_base: b9a1...e7aa
  pusher_host: &#34;expusherlite.cm42.io&#34;
  org_id: acme-inc
  app_key: 0221...f193
  secret_token: 4036...f193
...
production:
  secret_key_base: &lt;%= ENV[&#34;SECRET_KEY_BASE&#34;] %&gt;
  pusher_host: &lt;%= ENV[&#39;PUSHER_LITE_HOST&#39;] %&gt;
  org_id: &lt;%= ENV[&#39;PUSHER_LITE_ORGANIZATION&#39;] %&gt;
  app_key: &lt;%= ENV[&#34;PUSHER_LITE_APP_KEY&#34;] %&gt;
  secret_token: &lt;%= ENV[&#34;PUSHER_LITE_SECRET_TOKEN&#34;] %&gt;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Replace the <code>pusher_host</code>, <code>org_id</code>, <code>app_key</code>, and <code>secret_token</code> for the ones you created before.</p>
<p>Now I want to add a <code>PageController</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;net/http&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;uri&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PageController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">&#34;http://</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_host</span><span class="si">}</span><span class="s2">/api/sessions&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;token&#34;</span> <span class="o">=&gt;</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">secret_token</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@guardian_token</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span><span class="o">[</span><span class="s2">&#34;jwt&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="no">Rails</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">info</span> <span class="vi">@guardian_token</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(If you&rsquo;re connecting to my online server, you must use SSL, so change the URL above for &ldquo;https&rdquo;)</p>
<p>What this piece does is submit the secret token in the server-side, to my service, to get a JSON Web Token (JWT) back. Now you can pass this JWT to the front-end to enable authentication.</p>
<p>In the front-end we can have this simple <code>app/views/page/index.html.erb</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>&lt;h1&gt;Ex Pusher Lite - Rails Integration Example&lt;/h1&gt;

&lt;script type=&#34;text/javascript&#34; charset=&#34;utf-8&#34;&gt;
  window.guardian_token = &#34;&lt;%= @guardian_token %&gt;&#34;;
  window.org_id = &#34;&lt;%= Rails.application.secrets.org_id %&gt;&#34;
  window.app_key = &#34;&lt;%= Rails.application.secrets.app_key %&gt;&#34;;
  window.pusher_host = &#34;&lt;%= Rails.application.secrets.pusher_host %&gt;&#34;;
&lt;/script&gt;

&lt;div id=&#34;chat&#34; class=&#34;fixedContainer&#34;&gt;
&lt;/div&gt;

&lt;input type=&#34;text&#34; name=&#34;name&#34; id=&#34;name&#34; value=&#34;&#34; placeholder=&#34;Name&#34;/&gt;
&lt;input type=&#34;text&#34; name=&#34;message&#34; id=&#34;message&#34; value=&#34;&#34; placeholder=&#34;Message&#34;/&gt;
&lt;input type=&#34;checkbox&#34; name=&#34;channel&#34; id=&#34;channel&#34; value=&#34;api&#34;/&gt;
&lt;label for=&#34;channel&#34;&gt;send through API&lt;/label&gt;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Super simple, we can tweak the CSS (<code>app/assets/stylesheets/application.css</code>) just to make it look nicer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">fixedContainer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">height</span><span class="p">:</span> <span class="mi">250</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">padding</span><span class="p">:</span><span class="mi">3</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="kc">black</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">margin</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">overflow</span><span class="p">:</span> <span class="kc">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">font-family</span><span class="p">:</span> <span class="n">Helvetica</span><span class="p">,</span> <span class="n">Arial</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, we need to load the main Javascript from the ExPusherLite server, so edit the layout at <code>app/views/layouts/application.html.erb</code> and add this line right after the closing <code>&lt;/body&gt;</code> tag:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;http://&lt;%= Rails.application.secrets.pusher_host %&gt;/js/pusher.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we can now use this Javascript in the <code>app/assets/javascripts/application.js</code> to hook everything up. This is the relevant bit:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">PusherLite</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s2">&#34;pusher_lite&#34;</span><span class="p">).</span><span class="k">default</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">pusher</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PusherLite</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">app_key</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">host</span><span class="o">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">pusher_host</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">jwt</span><span class="o">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">guardian_token</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">uniqueUserId</span><span class="o">:</span> <span class="s2">&#34;robot&#34;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ssl: true - if you&#39;re connecting to my online server
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">publicChannel</span> <span class="o">=</span> <span class="nx">pusher</span><span class="p">.</span><span class="nx">subscribe</span><span class="p">(</span><span class="s2">&#34;general&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">publicChannel</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="s2">&#34;new_message&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">chat</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#chat&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">chat</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s2">&#34;&lt;p&gt;&lt;strong&gt;&#34;</span> <span class="o">+</span> <span class="nx">payload</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s2">&#34;&lt;/strong&gt; &#34;</span> <span class="o">+</span> <span class="nx">payload</span><span class="p">.</span><span class="nx">message</span> <span class="o">+</span> <span class="s2">&#34;&lt;/p&gt;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">chat</span><span class="p">.</span><span class="nx">scrollTop</span><span class="p">(</span><span class="nx">chat</span><span class="p">.</span><span class="nx">prop</span><span class="p">(</span><span class="s2">&#34;scrollHeight&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">pusher</span><span class="p">.</span><span class="nx">joinAll</span><span class="p">();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We can now continue in the same file with the Javascript that binds to the message input field, listening to the &ldquo;Enter&rdquo; key press event to send the messages:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">message_element</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#message&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">message_element</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;keypress&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">keyCode</span> <span class="o">!=</span> <span class="mi">13</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">name_element</span>    <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#name&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">check_element</span>   <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;#channel&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">payload</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="nx">name_element</span><span class="p">.</span><span class="nx">val</span><span class="p">(),</span> <span class="nx">message</span><span class="o">:</span> <span class="nx">message_element</span><span class="p">.</span><span class="nx">val</span><span class="p">()</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">check_element</span><span class="p">.</span><span class="nx">prop</span><span class="p">(</span><span class="s2">&#34;checked&#34;</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">sendPusher</span><span class="p">(</span><span class="nx">payload</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">sendAPI</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">message_element</span><span class="p">.</span><span class="nx">val</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">window</span><span class="p">.</span><span class="nx">publicChannel</span> <span class="o">=</span> <span class="nx">publicChannel</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is how we send messages to ExPusherLite, either directly through the full-duplex WebSockets:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">sendPusher</span><span class="p">(</span><span class="nx">payload</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;sending through socket&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nb">window</span><span class="p">.</span><span class="nx">publicChannel</span><span class="p">.</span><span class="nx">trigger</span><span class="p">(</span><span class="s1">&#39;new_message&#39;</span><span class="p">,</span> <span class="nx">payload</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Or Posting to the available API:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>function sendAPI(payload) {
  console.log(&#34;sending through API&#34;)
  $.ajax({
    type : &#39;POST&#39;,
    crossDomain: true,
    url : makeURL(&#34;new_message&#34;),
    headers : { Authorization : &#39;Bearer &#39; &#43; window.guardian_token },
    data : payload,
    success : function(response) {
      console.log(response);
      console.log(&#34;sent through API successfully&#34;);
    },
    error : function(xhr, status, error) {
      console.log(error);
    }
  });
}

function makeURL(event) {
  return &#34;http://&#34; &#43; window.pusher_host &#43; &#34;/api/organizations/&#34; &#43; window.org_id &#43; &#34;/applications/&#34; &#43; window.app_key &#43; &#34;/event/&#34; &#43; event;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>By the way, you can send messages using the API from the server-side if you want. Specifically from an ActiveJob process so you can keep your Rails web application fast, and you can use the opportunity to store the message in your database, or apply any filters.</p>
<p>And this is it! You now have a Rails application with WebSockets. You can have your lunch and eat it too.</p>
<p>If you want to see this example working, I published a demo app <a href="http://ex-pusher-lite-demo.herokuapp.com/"target="_blank" rel="noopener">over at Heroku</a>. It&rsquo;s just a demo, it has no authentication, no cross-scripting sanitization, no nothing.</p>
<p><a href="http://ex-pusher-lite-demo.herokuapp.com/"target="_blank" rel="noopener"><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/577/Screen_Shot_2016-12-30_at_17.42.22.png" alt="chat demo"  loading="lazy" /></a></p>
<p>In summary: this is a Rails app (you could do it in Django, Laravel, ASP.NET MVC, it doesn&rsquo;t matter) talking through WebSocket + APIs to a Phoenix cluster.</p>
<h3>Next Steps<span class="hx:absolute hx:-mt-20" id="next-steps"></span>
    <a href="#next-steps" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Keep following my blog (or my Twitter at <a href="https://twitter.com/akitaonrails"target="_blank" rel="noopener">@akitaonrails</a> ) for more posts to come.</p>
<p>I am still considering if I will open the ExPusherLite code as open source, so let me know if you&rsquo;re interested.</p>
<p>I am also considering if I will keep the current servers online as a cheap service. You can use it for free right now to play with it, but don&rsquo;t use for production-level apps yet. As I am still heavily coding it, I will keep updating the servers, so there is no SLA. Let me know if you&rsquo;re interested in such a service that keeps the code open source so you can trust it better.</p>
<p>There are important features still missing, such as proper SSL support, encrypted channels, better Presence tracking APIs and so on, but what&rsquo;s available right now already covers most use cases for WebSockets.</p>
<p>And better: because this is Phoenix, because this is Elixir, and because this is Erlang, we get distributed PubSub for &ldquo;free&rdquo;. As I explained in my <a href="http://www.akitaonrails.com/en/2016/12/23/aplicativo-elixir-phoenix-deploy-balanceamento-carga-digitalocean"target="_blank" rel="noopener">deployment</a> post, this is a setup with a server in New York and another in London, just to showcase the distributed nature of Erlang.</p>
<p>It&rsquo;s been very fun to play with Elixir for the past few days and how fast I was able to put together a full-featured solution like this. There were many puzzles that made me scratch my head, figuring out how to deal with cross origin issues, how to make the nodes find each other through the edeliver deployment, figuring out the missing bits in replacing exrm for distillery (which is a transition still taking place in the community), etc.</p>
<p>Now I am quite comfortable with the basics, from bootstrapping a project all the way to deploying in a cluster scenario. And I hope this service proves useful to more people.</p>
<p>As this is possibly my last post of the year: Happy New Year! And I will see you again in 2017!</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>backend</category></item><item><title>Elixir Phoenix App deployed into a Load Balanced DigitalOcean setup</title><link>https://akitaonrails.github.io/en/2016/12/23/elixir-phoenix-app-deployed-into-a-load-balanced-digitalocean-setup/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/12/23/elixir-phoenix-app-deployed-into-a-load-balanced-digitalocean-setup/</guid><pubDate>Fri, 23 Dec 2016 15:07:00 -0200</pubDate><description>&lt;p&gt;One of the main advantages of building a Websockets enabled web application using Phoenix is how &amp;ldquo;easy&amp;rdquo; it is for Erlang to connect itself into a cluster.&lt;/p&gt;
&lt;p&gt;For starters, Erlang does not need multiple processes like Ruby (which is limited to one connection per process, or per thread if you&amp;rsquo;re using a threaded-server like Puma). One single Erlang process will take over the entire machine, if you need to. Internally it will keep one real thread per machine-core. And each thread will have its own Scheduler to manage as many micro-processes as you need. You can read all about it in my post titled &lt;a href="http://www.akitaonrails.com/en/en/2015/11/25/yocto-services-e-meu-primeiro-mes-com-elixir/"target="_blank" rel="noopener"&gt;&amp;ldquo;Yocto Services&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>One of the main advantages of building a Websockets enabled web application using Phoenix is how &ldquo;easy&rdquo; it is for Erlang to connect itself into a cluster.</p>
<p>For starters, Erlang does not need multiple processes like Ruby (which is limited to one connection per process, or per thread if you&rsquo;re using a threaded-server like Puma). One single Erlang process will take over the entire machine, if you need to. Internally it will keep one real thread per machine-core. And each thread will have its own Scheduler to manage as many micro-processes as you need. You can read all about it in my post titled <a href="http://www.akitaonrails.com/en/en/2015/11/25/yocto-services-e-meu-primeiro-mes-com-elixir/"target="_blank" rel="noopener">&ldquo;Yocto Services&rdquo;</a>.</p>
<p>Moreover, Erlang has built-in capabilities to form a cluster, where each Erlang instance acts as a peer-to-peer Node, without the need for a centralized coordinator. You can read all about it in my post about <a href="http://www.akitaonrails.com/en/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir/"target="_blank" rel="noopener">Nodes</a>. The power of Erlang is in how &ldquo;easy&rdquo; it is to form reliable distributed systems.</p>
<p>You can fire up many Phoenix instances and from one of the instances, it can broadcast messages to Users subscribed in Channels even if their sockets are connected to different instances. It&rsquo;s seamless and you don&rsquo;t need to do anything special in your code. Phoenix, Elixir and Erlang are doing all the heavy lifting for you behind the scenes.</p>
<h3>No Heroku for You :-(<span class="hx:absolute hx:-mt-20" id="no-heroku-for-you--"></span>
    <a href="#no-heroku-for-you--" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Because you want to take advantage of this scalability and high availability feature for distributed systems (in the small example of a real-time chat system) you will need to have more control over your infrastructure. This requirement rules out the majority of Platform as a Service (PaaS) offerings out there, such as Heroku. Heroku&rsquo;s model revolves around single, volatile processes in isolated containers. Those jailed processes (dynos) are not aware of other processes or the internal networking, so you can&rsquo;t fire up Dynos and have them form a cluster because they won&rsquo;t be able to find each other.</p>
<p>If you already know how to configure Linux related stuff: Postgresql, HAproxy, etc, go ahead directly to the <a href="#phoenix-setup">Phoenix-specific section</a>.</p>
<h3>IaaS (DigitalOcean) to the rescue!<span class="hx:absolute hx:-mt-20" id="iaas-digitalocean-to-the-rescue"></span>
    <a href="#iaas-digitalocean-to-the-rescue" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>You want long lived processes in network reachable servers (either through private networking, VPN, or plain simple - insecure! - public networks).</p>
<p>In this example I want to walk you through a very simple deployment using DigitalOcean (you can choose any IaaS, such as AWS, Google Cloud, Azure or whatever you feel more comfortable with).</p>
<p>I have created 4 droplets (all using the smallest size of 512Mb of RAM):</p>
<ul>
<li>1 Postgresql database (single point of failure: it&rsquo;s not the focus of this article to build a highly available, replicated database setup);</li>
<li>1 HAProxy server (single point of failure: again, it&rsquo;s not the focus to create a highly available load balancing scheme);</li>
<li>2 Phoenix servers - one in the NYC datacenter and another in the London datacenter, to demonstrate how easy it is for Erlang to form clusters even with geographically separated boxes.</li>
</ul>
<h3>Basic Ubuntu 16.04 configuration<span class="hx:absolute hx:-mt-20" id="basic-ubuntu-1604-configuration"></span>
    <a href="#basic-ubuntu-1604-configuration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Goals: configure locale, assure unattended updates are up, upgrade packages, install and configure Elixir and Node.</li>
<li>You should also do: change SSH to another port and install [fail2ban](<a href="https://www.digitalocean.com/community/tutorials/how-to-protect-ssh-with-fail2ban-on-ubuntu-14-04"target="_blank" rel="noopener">https://www.digitalocean.com/community/tutorials/how-to-protect-ssh-with-fail2ban-on-ubuntu-14-04</a>, disallow login through password).</li>
</ul>
<p>You will want to read my post about <a href="http://www.akitaonrails.com/en/2016/09/21/ubuntu-16-04-lts-xenial-no-vagrant-com-vmware-fusion"target="_blank" rel="noopener">configuring Ubuntu 16.04</a>. To summarize, start by configuring proper UTF-8:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo locale-gen &#34;en_US.UTF-8&#34;
sudo dpkg-reconfigure locales
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
echo &#39;LC_ALL=en_US.UTF-8&#39; | sudo tee -a /etc/environment
echo &#39;LANG=en_US.UTF-8&#39; | sudo tee -a /etc/environment</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Make sure you add a proper user into the sudo group and from now on do not use the root user. I will create a user named <code>pusher</code> and I will explain in another post why. You should create a username that suits your application.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>adduser pusher
usermod -aG sudo pusher</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now log out and log in again through this user. <code>ssh pusher@server-ip-address</code>. If you&rsquo;re on a Mac copy the public key of your SSH certificate like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ssh-copy-id -i ~/.ssh/id_ed25519.pub pusher@server-ip-address</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It creates the <code>.ssh/authorized_keys</code> if it doesn&rsquo;t exist, sets the correct permission bits and appends your public key. You can do it manually, of course.</p>
<p>DigitalOcean&rsquo;s droplets start without a swap file and I&rsquo;d recommend adding one, especially if you want to start with the smaller boxes with less than 1GB of RAM:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo cp /etc/fstab /etc/fstab.bak
echo &#39;/swapfile none swap sw 0 0&#39; | sudo tee -a /etc/fstab
sudo sysctl vm.swappiness=10
sudo sysctl vm.vfs_cache_pressure=50
echo &#39;vm.swappiness=10&#39; | sudo tee -a /etc/sysctl.conf
echo &#39;vm.vfs_cache_pressure=50&#39; | sudo tee -a /etc/sysctl.conf</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Make sure you have unattended upgrades configured. You will want at least to have security updates automatically installed when available.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt install unattended-upgrades</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, let&rsquo;s install Elixir and Node (Phoenix needs Node.js):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb &amp;&amp; sudo dpkg -i erlang-solutions_1.0_all.deb
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential nodejs esl-erlang elixir erlang-eunit erlang-base-hipe
sudo npm install -g brunch
mix local.hex
mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez # this is optional, install if you want to manually test phoenix in your box</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now you have an Elixir capable machine ready. Create a image snapshot over DigitalOcean, move it to the regions where you want to create your other droplets and use this image to create as many droplets as you need.</p>
<p>For this example, I created a second droplet in the London region, a third droplet for postgresql in the NYC1 region and a fourth droplet in the NYC3 region for HAProxy.</p>
<p>I will refer to their public IP addresses as <strong>&ldquo;nyc-ip-address&rdquo;</strong>, <strong>&ldquo;lon-ip-address&rdquo;</strong>, <strong>&ldquo;pg-ip-address&rdquo;</strong>, and <strong>&ldquo;ha-ip-address&rdquo;</strong>.</p>
<h3>Basic PostgreSQL configuration<span class="hx:absolute hx:-mt-20" id="basic-postgresql-configuration"></span>
    <a href="#basic-postgresql-configuration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Goal: basic configuration of Postgresql to allow the Phoenix servers to connect.</li>
<li>To do: create a secondary role just to connect to the application database and another superuser role to create the database and migrate the schema. Also lock down the machine and configure SSH tunnels or another secure way, at least private networking, rather than allowing plain 5432 TCP port connections from the public Internet.</li>
</ul>
<p>Now you can connect to <code>ssh pusher@pg-ip-address</code> and follow this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install postgresql postgresql-contrib</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You should create a new role with the same name of the user you added above (&ldquo;pusher&rdquo; in our example):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ sudo -u postgres createuser --interactive

Enter name of role to add: pusher
Shall the new role be a superuser? (y/n) y

$ sudo -u postgres createdb pusher</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Postgresql expects to find a database with the same name as the role and the role should have the same name as the Linux user. Now you can use <code>psql</code> to set a password for this new role:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ sudo -u postgres psql
\password pusher</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Register a secure password, take note and let&rsquo;s move on.</p>
<p>Postgresql comes locked down to external connections. One way to connect from the outside is to configure your servers to create an <a href="https://www.postgresql.org/docs/9.1/static/ssh-tunnels.html"target="_blank" rel="noopener">SSH tunnel</a> to the database server and keep external TCP connections through port 5432 disavowed.</p>
<p>But for this example, we will just allow connections from the public Internet to the 5432 TCP port. <strong>Warning:</strong> this is VERY insecure!</p>
<p>Edit the <code>/etc/postgresql/9.5/main/postgresql.conf</code> and find the <code>listen_addresses</code> configuration line and allow it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>listen_addresses = &#39;*&#39;    # what IP address(es) to listen on;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This should bind the server to the TCP port. Now edit <code>/etc/postgresql/9.5/main/pg_hba.conf</code> and edit the end to look like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># IPv4 local connections:
host    all             all             127.0.0.1/32            trust
host    all             all             your-local-machine-ip-address/24        trust
host    all             all             nyc-ip-address/24       trust
host    all             all             lon-ip-address/24       trust
# IPv6 local connections:
host    all             all             ::1/128                 trust</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Save the configuration file and restart the server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo service postgresql restart</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>See what I did there? I only allowed connections coming from the public IPs of the Phoenix servers. This does not make the server secure, just a little bit less vulnerable. If you&rsquo;re behind a DHCP/NAT based network, just Google for &ldquo;what&rsquo;s my IP&rdquo; to see your public facing IP address - which is probably shared by many other users, remember you&rsquo;re allowing connections from an insecure IP to your database server! Once you make initial tests, create your new schema, then you should remove that <code>your-local-machine-ip-address/24</code> line from the configuration.</p>
<p>From your Phoenix application, you can edit your local <code>config/prod.secret.exs</code> file to look like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Configure your database</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:your_app_name</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">adapter</span><span class="p">:</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Adapters</span><span class="o">.</span><span class="n">Postgres</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">username</span><span class="p">:</span> <span class="s2">&#34;pusher&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;your-super-secure-pg-password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">database</span><span class="p">:</span> <span class="s2">&#34;your-app-database-name&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">hostname</span><span class="p">:</span> <span class="s2">&#34;pg-ip-address&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">pool_size</span><span class="p">:</span> <span class="mi">20</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Replace the information for your server and database and now you can test it out like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>MIX_ENV=prod iex -S mix phoenix.server</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you see a <code>:econnrefused</code> message from postgrex, then you&rsquo;re in trouble. Re-check your configuration, restart the server and try again. If everything connects, you can run <code>MIX_ENV=prod mix do ecto.create, ecto.migrate</code> to prepare your database.</p>
<p>Finally, you will want to lock down the rest of your server with UFW, at the very least. UFW should come pre-installed in Ubuntu 16, so you can just do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ufw allow 5432
ufw allow ssh
ufw enable</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>That&rsquo;s it. And again, this does not make your server secure, it just makes it less insecure. There is a huge difference!</p>
<p>And by the way, if you&rsquo;re a Docker fan:</p>
<blockquote>
  <p>DO NOT INSTALL A DATABASE INSIDE A DOCKER CONTAINER!</p>

</blockquote>
<p><a href="http://patrobinson.github.io/2016/11/07/thou-shalt-not-run-a-database-inside-a-container/"target="_blank" rel="noopener">You have been warned</a>!</p>
<h3>Basic HAProxy configuration<span class="hx:absolute hx:-mt-20" id="basic-haproxy-configuration"></span>
    <a href="#basic-haproxy-configuration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Goals: Provide a simple solution to load balance between our 2 Phoenix servers.</li>
<li>To do: There is something wrong with session checking or something like that as sometimes I have to refresh my browser so I am not sent back to the login form in my application. Phoenix uses cookie-based sessions so I don&rsquo;t think it is missing sessions.</li>
</ul>
<p>Now let&rsquo;s <code>ssh pusher@ha-ip-address</code>. This one is easy, let&rsquo;s just install HAProxy:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install haproxy</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Edit <code>/etc/haproxy/haproxy.cfg</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
listen your-app-name
  bind 0.0.0.0:80
  mode http
  stats enable
  stats uri /haproxy?stats
  stats realm Strictly\ Private
  stats auth admin:some-secure-password-for-admin
  option forwardfor
  option http-server-close
  option httpclose
  balance roundrobin
  cookie SERVERID insert indirect nocache
  server us your_us_server_IP:8080 check cookie us1
  server uk your_uk_server_IP:8080 check cookie uk1</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You can avoid the <code>stats</code> lines if you have other means of monitoring, otherwise set a secure password for the <code>admin</code> user. One very important part is the <code>option http-server-close</code> as explained in <a href="http://blog.silverbucket.net/post/31927044856/3-ways-to-configure-haproxy-for-websockets"target="_blank" rel="noopener">this other blog post</a>, otherwise you may have trouble with Websockets.</p>
<p>For some reason I am having some trouble with my application after I login and it sets the session, sometimes I have to refresh to be sent to the correct page, not sure why yet and I believe it&rsquo;s something in the HAProxy configuration. If anyone knows what it is, let me know in the comments section below. For now, I am just relying on Sticky sessions (server affinity) by making HAProxy write back a cookie with the server.</p>
<p>Now you can restart the server and enable UFW as well:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo service haproxy restart
sudo ufw allow http
sudo ufw allow https
sudo ufw allow ssh
sudo ufw enable</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You can easily add SSL support to your application by configuring HAProxy (and not the Phoenix nodes). The <a href="https://www.digitalocean.com/community/tutorials/how-to-secure-haproxy-with-let-s-encrypt-on-ubuntu-14-04"target="_blank" rel="noopener">DigitalOcean documentation</a> is comprehensive, so just follow it. At the end, my <code>haproxy.cfg</code> looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>global
   log /dev/log    local0
   log /dev/log    local1 notice
   chroot /var/lib/haproxy
   stats socket /run/haproxy/admin.sock mode 660 level admin
   stats timeout 30s
   user haproxy
   group haproxy
   daemon
   maxconn 2048
   tune.ssl.default-dh-param 2048

defaults
   log global
   mode http
   option httplog
   option dontlognull
   option redispatch
   option forwardfor
   option http-server-close
   timeout connect 5000
   timeout client  50000
   timeout server  50000

frontend www-http
   bind your_ha_proxy_IP:80
   reqadd X-Forwarded-Proto:\ http
   default_backend www-backend

frontend www-https
   bind your_ha_proxy_IP:443 ssl crt /etc/haproxy/certs/your_domain.pem
   reqadd X-Forwarded-Proto:\ https
   acl letsencrypt-acl path_beg /.well-known/acme-challenge/
   use_backend letsencrypt-backend if letsencrypt-acl
   default_backend www-backend

backend www-backend
   redirect scheme https if !{ ssl_fc }
   # setting session stickiness
   cookie SERVERID insert indirect nocache
   server us your_us_server_IP:8080 check cookie us1
   server uk your_uk_server_IP:8080 check cookie uk1

backend letsencrypt-backend
   server letsencrypt 127.0.0.1:54321</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, I will assume you have a DNS server/service somewhere where you can register the IP of this HAproxy server as an A record so you can access it by a full name such as &ldquo;your-app-name.mydomain.com&rdquo;.</p>
<p><a name="phoenix-setup"></a></p>
<h3>Basic Phoenix Configuration<span class="hx:absolute hx:-mt-20" id="basic-phoenix-configuration"></span>
    <a href="#basic-phoenix-configuration" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>Goal: configure the Phoenix app to be deployable. Configure the servers to have the necessary configuration files.</li>
<li>To do: find out a way to cut down the super slow deployment times.</li>
</ul>
<p>Finally, we have almost everything in place.</p>
<p>I will assume that you have a working Phoenix application already in place, otherwise create one from the many tutorials out there.</p>
<p>I have assembled this information from <a href="https://medium.com/@diamondgfx/deploying-phoenix-applications-with-exrm-97a3867ebd04#.7qyuplncx"target="_blank" rel="noopener">posts</a> such as <a href="http://engineering.pivotal.io/post/how-to-set-up-an-elixir-cluster-on-amazon-ec2/"target="_blank" rel="noopener">this very helpful one</a> from Pivotal about an AWS-based deployment. In summary you must do a number of changes to your configuration.</p>
<p>When you&rsquo;re developing your application, you will notice that whenever you run it, it delta-compiles what changed. The binary bits are in the <code>_build/dev</code> or <code>_build/test</code> in the form of <code>.beam</code> binaries (similar to what <code>.class</code> are for Java).</p>
<p>Different from Ruby or Python or PHP, you are not deploying source-code to production servers. It&rsquo;s more akin to Java, where you must have everything compiled into binary bits and packaged into what&rsquo;s called a <strong>release</strong>. It&rsquo;s like a &ldquo;.war&rdquo; or &ldquo;.ear&rdquo; if you&rsquo;re from Java.</p>
<p>To create this package people usually use &ldquo;exrm&rdquo;, but it&rsquo;s being replaced by <a href="https://github.com/bitwalker/distillery"target="_blank" rel="noopener">&ldquo;distillery&rdquo;</a>, so we will use it.</p>
<p>Then, if you&rsquo;re from Ruby you&rsquo;re familiar with Capistrano. Or if you&rsquo;re from Python, you know Capistrano&rsquo;s clone, Fabric. Elixir has a similar tool (much simpler at this point), called <a href="https://github.com/boldpoker/edeliver"target="_blank" rel="noopener">&ldquo;edeliver&rdquo;</a>. It&rsquo;s your basic SSH automation tool.</p>
<p>You add them to <code>mix.exs</code> just like any other dependency:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">application</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span><span class="ss">mod</span><span class="p">:</span> <span class="p">{</span><span class="no">ExPusherLite</span><span class="p">,</span> <span class="o">[]</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="ss">applications</span><span class="p">:</span> <span class="o">[...</span><span class="p">,</span> <span class="ss">:edeliver</span><span class="o">]]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">deps</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[...</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="ss">:edeliver</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.4.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="ss">:distillery</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0&#34;</span><span class="p">}</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>From the Pivotal blog post, the important thing to not forget is to edit this part in the <code>config/prod.exs</code> file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ss">http</span><span class="p">:</span> <span class="o">[</span><span class="ss">port</span><span class="p">:</span> <span class="mi">8080</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="ss">url</span><span class="p">:</span> <span class="o">[</span><span class="ss">host</span><span class="p">:</span> <span class="s2">&#34;your-app-name.yourdomain.com&#34;</span><span class="p">,</span> <span class="ss">port</span><span class="p">:</span> <span class="mi">80</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:phoenix</span><span class="p">,</span> <span class="ss">:serve_endpoints</span><span class="p">,</span> <span class="kp">true</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You MUST hardcode the default PORT of the Phoenix web server and the allowed domain (remember the domain name you associated to your HAProxy server above? That one). And you MUST uncomment the <code>:serve_endpoints, true</code> line!</p>
<p>For edeliver to work you have to create a <code>.deliver/config</code> file like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>USING_DISTILLERY=true

# change this to your app name:
APP=&#34;your-app-name&#34;

# change this to your own servers IP and add as many as you want
US=&#34;nyc-ip-address&#34;
UK=&#34;lon-ip-address&#34;

# the user you created in your Ubuntu machines above
USER=&#34;pusher&#34;

# which server do you want to build the first release?
BUILD_HOST=$US
BUILD_USER=$USER
BUILD_AT=&#34;/tmp/edeliver/$APP/builds&#34;

# list the production servers declared above:
PRODUCTION_HOSTS=&#34;$US $UK&#34;
PRODUCTION_USER=$USER
DELIVER_TO=&#34;/home/$USER&#34;

# do not change here

LINK_VM_ARGS=&#34;/home/$USER/vm.args&#34;

# For *Phoenix* projects, symlink prod.secret.exs to our tmp source
pre_erlang_get_and_update_deps() {
  local _prod_secret_path=&#34;/home/$USER/prod.secret.exs&#34;
  if [ &#34;$TARGET_MIX_ENV&#34; = &#34;prod&#34; ]; then
    __sync_remote &#34;
      ln -sfn &#39;$_prod_secret_path&#39; &#39;$BUILD_AT/config/prod.secret.exs&#39;

      cd &#39;$BUILD_AT&#39;

      mkdir -p priv/static

      mix deps.get

      npm install

      brunch build --production

      APP=&#39;$APP&#39; MIX_ENV=&#39;$TARGET_MIX_ENV&#39; $MIX_CMD phoenix.digest $SILENCE
    &#34;
  fi
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Remember the information we&rsquo;ve been gathering since the beginning of this long recipe? These are the options you MUST change to your own. Just follow the comments in the file content above and add it to your git repository. By the way, your project is in a proper GIT repository, RIGHT??</p>
<p>If you like to use passphrase protected SSH private keys, then it&rsquo;s going to be a huge pain to deploy because for each command, edeliver will issue an SSH command that will keep asking for you passphrase, a dozen times through everything. You&rsquo;ve been warned! If you still don&rsquo;t mind that, and you&rsquo;re in a Mac you will have an extra trouble because the Terminal will not be able to create a prompt for you to input your passphrase. You must create an <code>/usr/local/bin/ssh-askpass</code> <a href="https://github.com/markcarver/mac-ssh-askpass"target="_blank" rel="noopener">script</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>#!/bin/bash
# Script: ssh-askpass
# Author: Mark Carver
# Created: 2011-09-14
# Licensed under GPL 3.0

# A ssh-askpass command for Mac OS X
# Based from author: Joseph Mocker, Sun Microsystems
# http://blogs.oracle.com/mock/entry/and_now_chicken_of_the

# To use this script:
#   Install this script running INSTALL as root
#
# If you plan on manually installing this script, please note that you will have
# to set the following variable for SSH to recognize where the script is located:
#   export SSH_ASKPASS=&#34;/path/to/ssh-askpass&#34;

TITLE=&#34;${SSH_ASKPASS_TITLE:-SSH}&#34;;
TEXT=&#34;$(whoami)&#39;s password:&#34;;
IFS=$(printf &#34;\n&#34;);
CODE=(&#34;on GetCurrentApp()&#34;);
CODE=(${CODE[*]} &#34;tell application \&#34;System Events\&#34; to get short name of first process whose frontmost is true&#34;);
CODE=(${CODE[*]} &#34;end GetCurrentApp&#34;);
CODE=(${CODE[*]} &#34;tell application GetCurrentApp()&#34;);
CODE=(${CODE[*]} &#34;activate&#34;);
CODE=(${CODE[*]} &#34;display dialog \&#34;${@:-$TEXT}\&#34; default answer \&#34;\&#34; with title \&#34;${TITLE}\&#34; with icon caution with hidden answer&#34;);
CODE=(${CODE[*]} &#34;text returned of result&#34;);
CODE=(${CODE[*]} &#34;end tell&#34;);
SCRIPT=&#34;/usr/bin/osascript&#34;
for LINE in ${CODE[*]}; do
      SCRIPT=&#34;${SCRIPT} -e $(printf &#34;%q&#34; &#34;${LINE}&#34;)&#34;;
done;
eval &#34;${SCRIPT}&#34;;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now do this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo chmod &#43;x /usr/local/bin/ssh-askpass
sudo ln -s /usr/local/bin/ssh-askpass /usr/X11R6/bin/ssh-askpass</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Remember, Macs only. And now every time you try to deploy you will receive a number of graphical prompt windows asking for the SSH private key passphrase. It&rsquo;s freaking annoying! And you must have <a href="https://www.xquartz.org/"target="_blank" rel="noopener">XQuartz</a> installed, by the way.</p>
<p>Now you must manually create 3 files in all Phoenix servers. Start with the <code>your-app-name/vm.args</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>-name us@nyc-ip-address
-setcookie @bCd&amp;fG
-kernel inet_dist_listen_min 9100 inet_dist_listen_max 9155
-config /home/pusher/your-app-name.config
-smp auto</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <code>/home/pusher/your-app-name</code> is the directory where the release will be uncompressed after edeliver deploys the release tarball.</p>
<p>You must create this file in all Phoenix machines, by the way, changing the <code>-name</code> bit for the same name you declared in the <code>.deliver/config</code> file. The <code>-setcookie</code> should be any name, as long as it&rsquo;s the same in all servers.</p>
<p>See the <code>-config /home/pusher/your-app-name.config</code>? Create that file with the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[{kernel,
  [
    {sync_nodes_optional, [&#39;uk@lon-ip-address&#39;]},
    {sync_nodes_timeout, 30000}
  ]}
].</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is an Erlang source-code. On the NYC machine you must declare the London name, and vice-versa. If you have several machines, all of them but the one you&rsquo;re in right now. Get it?</p>
<p>Finally, for the Phoenix app itself, you always have a <code>config/prod.secret.exs</code> that should never be <code>git add</code>ed to the repository, remember? This is where you put the Postgresql server information and random secret key to sign the session cookies:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>use Mix.Config

config :your_app_name, YourAppName.Endpoint,
  secret_key_base: &#34;...&#34;

# Configure your database
config :your_app_name, YourAppName.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: &#34;pusher&#34;,
  password: &#34;your-super-secure-pg-password&#34;,
  database: &#34;your-app-database-name&#34;,
  hostname: &#34;pg-ip-address&#34;,
  pool_size: 20

# if you have Guardian, for example:
config :guardian, Guardian,
  secret_key: &#34;...&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>How do you create a new random secret key? From your development machine just run: <code>mix phoenix.gen.secret</code> and copy the generated string into the file above.</p>
<p>So now you must have those 3 files in each Phoenix server, in the <code>/home/pusher</code> home folder:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>~/your-app-name/vm.args
~/prod.secret.exs
~/your-app-name.config</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As per <a href="https://hexdocs.pm/distillery/runtime-configuration.html#vm-args"target="_blank" rel="noopener">distillery</a> documentation, you have to set an environment variable to let it know about the <code>vm.args</code> file, and this is <strong>SUPER</strong> important otherwise it will generate a default one that doesn&rsquo;t set the proper name and cookie, so the nodes will not find each other after booting up.</p>
<p>Using <code>sudo</code>, edit the <code>/etc/environment</code> and add the line:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>RELEASE_CONFIG_DIR=/home/pusher/your-app-name
VMARGS_PATH=/home/pusher/your-app-name/vm.args</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You have to do it in all the phoenix servers.</p>
<p>In your local development directory you still have to run this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix release.init</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It will generate a standard <code>rel/config.exs</code> file that you must add to your git repository with the following changes to the bottom:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">environment</span> <span class="ss">:prod</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">plugin</span> <span class="no">Releases</span><span class="o">.</span><span class="n">Plugin</span><span class="o">.</span><span class="n">LinkConfig</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">release</span> <span class="ss">:your_app_name</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">set</span> <span class="ss">version</span><span class="p">:</span> <span class="n">current_version</span><span class="p">(</span><span class="ss">:your_app_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">set</span> <span class="ss">applications</span><span class="p">:</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">    <span class="ss">your_app_name</span><span class="p">:</span> <span class="ss">:permanent</span>
</span></span><span class="line"><span class="cl">  <span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The plugin should enable the release to find the local <code>vm.args</code> file in the server (this is super important, otherwise the remote commands such as start, stop, ping, etc will not work and the nodes will not load the correct information to boot up and form a cluster).</p>
<p>Finally, all set, you can issue this command:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ mix edeliver build release production --skip-mix-clean --verbose</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If it finishes without errors you will have a message like the following in the end:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
==&gt; Release successfully built!
    You can run it in one of the following ways:
      Interactive: _build/prod/rel/your-app-name/bin/your-app-name console
      Foreground: _build/prod/rel/your-app-name/bin/your-app-name foreground
      Daemon: _build/prod/rel/your-app-name/bin/your-app-name start
--&gt; Copying release 0.0.1 to local release store
--&gt; Copying your-app-name.tar.gz to release store

RELEASE BUILD OF YOUR-APP-NAME WAS SUCCESSFUL!</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, this will take an absurdly long time to deploy. That&rsquo;s because it will git clone the source code of your app, fetch all Elixir dependencies (every time!), it will have to compile everything, then it will run the super slow <code>npm install</code> (every time!), brunch your assets, create the so-called &ldquo;release&rdquo;, tar and gzip it, download it and SCP it to the other machines you configured.</p>
<p>In the <code>.deliver/config</code> file you set a <code>BUILD_HOST</code> option. This is the machine where all this process takes place, so you will want to have at least this machine be beefier than the others. As I am using small 512Mb droplets, the process takes forever.</p>
<p>The last command will download the generated tarball. It has to do it to make sure you have the <a href="http://erlang.org/doc/tutorial/nif.html"target="_blank" rel="noopener">NIFs</a> compiled in the native environment where they run, because if you use a Mac, binaries from Macs won&rsquo;t run on Linux.</p>
<p>Now we must upload and decompress this tarball in each server with the following command:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix edeliver deploy release to production</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The slower your network, the longer this will take, as it&rsquo;s uploading a big tarfile across the public internet, so make sure you&rsquo;re in a fast connection. And when it finishes, we can restart the servers (if you already had instances running):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix edeliver stop production
mix edeliver start production</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you do everything right, the edeliver process finishes without any error and it leaves a daemon running in your server, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/home/pusher/your-app-name/erts-8.2/bin/beam -- -root /home/pusher/your-app-name -progname home/pusher/your-app-name/releases/0.0.1/your-app-name.sh -- -home /home/pusher -- -boot /home/pusher/your-app-name/releases/0.0.1/your-app-name -config /home/pusher/your-app-name/running-config/sys.config -boot_var ERTS_LIB_DIR /home/pusher/your-app-name/erts-8.2/../lib -pa /home/pusher/your-app-name/lib/your-app-name-0.0.1/consolidated -name us@nyc-ip-address -setcookie ex-push&amp;r-l!te -kernel inet_dist_listen_min 9100 inet_dist_listen_max 9155 -config /home/pusher/your-app-name.config -mode embedded -user Elixir.IEx.CLI -extra --no-halt &#43;iex -- console</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It will still take a long time, but it should be easier. So this is a pro-tip for you, Linux users. Follow <a href="https://gist.github.com/mattweldon/2e8ecb953216438ad168"target="_blank" rel="noopener">this Gist</a> for more details, you must emulate what&rsquo;s run from the <code>.deliver/config</code> file&rsquo;s bottom half.</p>
<p>Also notice that I ran the migrations manually, but you can do it using <code>mix edeliver migrate</code>.</p>
<p>Read their documentation for more commands and configurations.</p>
<p>Also, do not forget to enable UFW:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo ufw allow ssh
sudo ufw allow 8080
sudo ufw allow 4369
sudo ufw allow proto tcp from any to any port 9100:9155
sudo ufw default allow outgoing
sudo ufw enable</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Debugging Production bugs<span class="hx:absolute hx:-mt-20" id="debugging-production-bugs"></span>
    <a href="#debugging-production-bugs" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Right after I deployed, obviously it failed. And the problem is that the <code>/home/pusher/your-app-name/log/erlang.log</code> files (they are automatically rotated so you may find several files ending in a number), you won&rsquo;t see much.</p>
<p>What I recommend you to do is to change the <code>config/prod.exs</code> file ONLY in your development machine and change the log setting to <code>config :logger, level: :debug</code>, use the same <code>prod.secret.exs</code> you edited in the servers above and run it locally with <code>MIX_ENV=prod iex -S mix phoenix.server</code>.</p>
<p>For example, in development mode I had a code in the controller that was checking the existence of an optional query string parameter like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;some_parameter&#34;</span><span class="o">]</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>That was working fine in development but crashing in production, so I had to change it to:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="no">Map</span><span class="o">.</span><span class="n">has_key?</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="s2">&#34;some_parameter&#34;</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Another thing was that Guardian was working normally in development, but in production I had to declare its application in the <code>mix.exs</code> like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">application</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span><span class="ss">mod</span><span class="p">:</span> <span class="p">{</span><span class="no">ExPusherLite</span><span class="p">,</span> <span class="o">[]</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="ss">applications</span><span class="p">:</span> <span class="o">[...</span><span class="p">,</span> <span class="ss">:guardian</span><span class="p">,</span> <span class="ss">:edeliver</span><span class="o">]]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I was getting <code>:econnrefused</code> errors because I forgot to run <code>MIX_ENV=prod mix do ecto.create, ecto.migrate</code> as I instructed above. Once I figured those out, my application was up and running through the <code>http://your-app-name.yourdomain.com</code>, HAProxy was correctly forwarding to the 8080 port in the servers and everything runs fine, including the WebSocket connections.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As I mentioned above, this kind of procedure makes me really miss an easy to deploy solution such as Heroku.</p>
<p>The only problem I am facing right now is that when I log in through Coherence&rsquo;s sign in page, I am not redirected to the correct URI I am trying (&quot;/admin&quot; in my case), sometimes reloading after sign in works, sometimes it doesn&rsquo;t. Sometimes I am inside a &ldquo;/admin&rdquo; page but when I click one of the links it sends me back to the sign in page even though I am already signed in. I am not sure if it&rsquo;s a bug in Coherence, ExAdmin, Phoenix itself or an HAProxy misconfiguration. I will update this post if I find out.</p>
<p>Edeliver also takes an obscene amount of time to deploy. Even waiting for sprockets to process in a <code>git push heroku master</code> deploy feels way faster in comparison. And this is for a very bare-bone Phoenix app. Having to fetch everything (because Hex doesn&rsquo;t keep a local global cache, all dependencies are statically vendored in the project directory). Having to run the super slow npm doesn&rsquo;t help either.</p>
<p>I still need to research if there are faster options, but for now what I have &ldquo;works&rdquo;.</p>
<p>And more importantly, now I have a scalable cluster for real-time bi-directional WebSockets, which is the main reason one might want to use Phoenix in the first place.</p>
<p>If you want to build a &ldquo;normal&rdquo; website, keep it simple and do it in Rails, Django, Express or whatever is your web framework of choice. If you want real-time communications the easy way, I might have a better solution. Keep an eye on my blog for news to come soon! ;-)</p>
]]></content:encoded><category>elixir-and-erlang</category><category>devops</category><category>cloud</category><category>distributed-systems</category></item><item><title>[Manga-Downloadr] Improving the Crystal/Ruby from bursts to pool stream</title><link>https://akitaonrails.github.io/en/2016/06/07/manga-downloadr-improving-the-crystal-ruby-from-bursts-to-pool-stream/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/06/07/manga-downloadr-improving-the-crystal-ruby-from-bursts-to-pool-stream/</guid><pubDate>Tue, 07 Jun 2016 17:13:00 -0300</pubDate><description>&lt;p&gt;Yesterday &lt;a href="http://www.akitaonrails.com/en/2016/06/06/manga-downloadr-portando-de-crystal-para-ruby-e-um-pouco-de-jruby"target="_blank" rel="noopener"&gt;I posted&lt;/a&gt; how I built a new implementation of the Manga Reader Downloader in Crystal, ported to Ruby, tested on JRuby and compared against Elixir. Just to recap, these were the results to fetch chapters, pages and image links of a sample manga:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MRI Ruby 2.3.1: 57 seconds&lt;/li&gt;
&lt;li&gt;JRuby 9.1.1.0: 45 seconds&lt;/li&gt;
&lt;li&gt;Crystal 0.17.0: 59 seconds&lt;/li&gt;
&lt;li&gt;Elixir: 14 seconds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also said how it was an unfair comparison as the Elixir version uses a different - and obviously more efficient - algorithm.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Yesterday <a href="http://www.akitaonrails.com/en/2016/06/06/manga-downloadr-portando-de-crystal-para-ruby-e-um-pouco-de-jruby"target="_blank" rel="noopener">I posted</a> how I built a new implementation of the Manga Reader Downloader in Crystal, ported to Ruby, tested on JRuby and compared against Elixir. Just to recap, these were the results to fetch chapters, pages and image links of a sample manga:</p>
<ul>
<li>MRI Ruby 2.3.1: 57 seconds</li>
<li>JRuby 9.1.1.0: 45 seconds</li>
<li>Crystal 0.17.0: 59 seconds</li>
<li>Elixir: 14 seconds</li>
</ul>
<p>I also said how it was an unfair comparison as the Elixir version uses a different - and obviously more efficient - algorithm.</p>
<p>It was the &ldquo;first-version-that-worked&rdquo; so I decided to go ahead and improve the implementations. In the Ruby/JRuby version I added the <a href="https://github.com/meh/ruby-thread"target="_blank" rel="noopener">thread</a> gem to have a good enough implementation of a Thread pool that works for Ruby and JRuby. I probably should have used Concurrent-Ruby but I was having some trouble making FixedThreadPool work.</p>
<p>Anyway, now all versions will have a constant pool of requests running and we can make a better comparison.</p>
<p>Another thing that may have skewed the results against Crystal is that it seems to have a <a href="https://github.com/crystal-lang/crystal/issues/2660"target="_blank" rel="noopener">faulty DNS resolver</a> implementation, so for now I just added the Manga Reader IP address directly to my <code>/etc/hosts</code> file to avoid <code>getaddrinfo: Name or service not known</code> exceptions.</p>
<p>To begin, let&rsquo;s test the same Elixir implementation, and as expected the result is still the same:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>11,49s user 0,82s system 77% cpu 15,827 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, the MRI Ruby with the &ldquo;batch burst&rdquo; algorithm was taking 57 seconds and this new implementation using a ThreadPool runs much better:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>12,67s user 0,92s system 50% cpu 27,149 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In Crystal, it&rsquo;s a bit more complicated as there is no way to implement the equivalent of a &ldquo;Fiber Pool&rdquo;. What we have to do is run an infinite loop until the last process signals a loop break. Within the loop we create a maximum number of fibers, wait for each one to signal that it finished through an individual channel and loop again to create a new fiber, and so on. Compared to yesterday&rsquo;s 59 seconds, this is much better:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>5,29s user 0,33s system 26% cpu 21,166 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The JRuby version is not so fast though. Still better than yesterday&rsquo;s 45 seconds but now it&rsquo;s losing even to MRI:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>49,24s user 1,41s system 146% cpu 34,602 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I tried to use the <code>--dev</code> flag for <a href="https://github.com/jruby/jruby/wiki/Improving-startup-time"target="_blank" rel="noopener">faster start up time</a> and it does improve a bit, getting it closer to MRI:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>22,26s user 0,99s system 76% cpu 30,320 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Not sure if it can be improved more though, any tips are welcome - don&rsquo;t forget to comment below.</p>
<p>So, Elixir is still at least twice as fast as Crystal at this point. But this also demonstrates how a different algorithm does make a huge difference where it matters. I can probably tweak it a bit more but this should suffice for now.</p>
<h3>Changing the Ruby implementation to use ThreadPool<span class="hx:absolute hx:-mt-20" id="changing-the-ruby-implementation-to-use-threadpool"></span>
    <a href="#changing-the-ruby-implementation-to-use-threadpool" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;thread/pool&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">MangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Concurrency</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">pool</span>    <span class="o">=</span> <span class="no">Thread</span><span class="o">.</span><span class="n">pool</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">mutex</span>   <span class="o">=</span> <span class="no">Mutex</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">collection</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">        <span class="n">pool</span><span class="o">.</span><span class="n">process</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">engine</span>  <span class="o">=</span> <span class="vi">@turn_on_engine</span> <span class="p">?</span> <span class="vi">@engine_klass</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">)</span> <span class="p">:</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">          <span class="n">reply</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span><span class="o">&amp;.</span><span class="n">flatten</span>
</span></span><span class="line"><span class="cl">          <span class="n">mutex</span><span class="o">.</span><span class="n">synchronize</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">            <span class="n">results</span> <span class="o">+=</span> <span class="p">(</span> <span class="n">reply</span> <span class="o">||</span> <span class="o">[]</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">end</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="n">pool</span><span class="o">.</span><span class="n">shutdown</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">results</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The idea is that we will have a fixed number of spawned native threads (in this case, determined by <code>@config.download_batch_size</code>). As one thread finishes it will pop a new link from the <code>collection</code> Array, essentially working as a &ldquo;queue&rdquo; to be depleted.</p>
<p>The results are accumulated in the <code>results</code> Array. Because many threads may want to modify it at once we have to synchronize access through a Mutex.</p>
<p>This way we always have a fixed amount of workers performing requests constantly instead of slicing the <code>collection</code> Array and doing bursts as in the previous version.</p>
<h3>Changing the Crystal implementation to simulate a Fibers Pool<span class="hx:absolute hx:-mt-20" id="changing-the-crystal-implementation-to-simulate-a-fibers-pool"></span>
    <a href="#changing-the-crystal-implementation-to-simulate-a-fibers-pool" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The Crystal version became a bit more complicated as I didn&rsquo;t find a pool library to pull in. I found a rough implementation of a pool in <a href="http://stackoverflow.com/a/30854065/1529907"target="_blank" rel="noopener">this stackoverflow post</a> and I was able to implement an improved version into a new shard so you can take advantage of it in your projects. Check out the source code at <a href="https://github.com/akitaonrails/fiberpool"target="_blank" rel="noopener">akitaonrails/fiberpool</a>. This is how I added it to my project:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">dependencies</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="l">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">fiberpool</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">github</span><span class="p">:</span><span class="w"> </span><span class="l">akitaonrails/fiberpool</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is how I used it. Notice that the logic itself is very close to the MRI Ruby version, but using a &ldquo;Fiber Pool&rdquo; instead of a Thread Pool.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;fiberpool&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">CrMangaDownloadr</span>
</span></span><span class="line"><span class="cl">  <span class="n">struct</span> <span class="no">Concurrency</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="vi">@config</span> <span class="p">:</span> <span class="no">Config</span><span class="p">,</span> <span class="vi">@turn_on_engine</span> <span class="o">=</span> <span class="kp">true</span><span class="p">);</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">collection</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">A</span><span class="p">)</span><span class="sc">?,</span> <span class="n">engine_class</span> <span class="p">:</span> <span class="n">E</span><span class="o">.</span><span class="n">class</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">:</span> <span class="n">A</span><span class="p">,</span> <span class="n">E?</span> <span class="o">-&gt;</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span><span class="sc">?)</span> <span class="p">:</span> <span class="nb">Array</span><span class="p">(</span><span class="n">B</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span> <span class="o">=</span> <span class="o">[]</span> <span class="n">of</span> <span class="n">B</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">collection</span>
</span></span><span class="line"><span class="cl">        <span class="n">pool</span> <span class="o">=</span> <span class="no">Fiberpool</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="vi">@config</span><span class="o">.</span><span class="n">download_batch_size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">pool</span><span class="o">.</span><span class="n">run</span> <span class="k">do</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">          <span class="n">engine</span> <span class="o">=</span> <span class="k">if</span> <span class="vi">@turn_on_engine</span>
</span></span><span class="line"><span class="cl">                     <span class="n">engine_class</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="vi">@config</span><span class="o">.</span><span class="n">domain</span><span class="p">,</span> <span class="vi">@config</span><span class="o">.</span><span class="n">cache_http</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                   <span class="k">end</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="n">reply</span> <span class="o">=</span> <span class="n">block</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">engine</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">results</span><span class="o">.</span><span class="n">concat</span><span class="p">(</span><span class="n">reply</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">end</span>
</span></span><span class="line"><span class="cl">        <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">      <span class="n">results</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But again, this is very I/O intensive and both the Ruby and Crystal versions take advantage of the fact that they can do more work while waiting for one HTTP request to finish.</p>
<p><a name="reproducing-tests"> </a></p>
<h3>Reproducing the Tests<span class="hx:absolute hx:-mt-20" id="reproducing-the-tests"></span>
    <a href="#reproducing-the-tests" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>I implemented a &ldquo;Test Mode&rdquo; in all 3 implementations. You can clone from my repositories:</p>
<ul>
<li><a href="https://github.com/akitaonrails/cr_manga_downloadr"target="_blank" rel="noopener">Crystal version</a></li>
<li><a href="https://github.com/akitaonrails/manga-downloadr"target="_blank" rel="noopener">Ruby/JRuby version</a></li>
<li><a href="https://github.com/akitaonrails/ex_manga_downloadr"target="_blank" rel="noopener">Elixir version</a></li>
</ul>
<p>And you can run the test mode like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># crystal:
time ./cr_manga_downloadr --test

# MRI:
time bin/manga-downloadr --test

# JRuby (you have to edit Gemfile to uncomment the JRuby engine):
time jruby --dev -S bin/manga-downloadr --test

# Elixir:
time ./ex_manga_downloadr --test</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This will run only the fetching of chapters, pages and image links, skipping the actual downloading of the images, optimization through mogrify and PDF compilation. Those skipped parts take too long and don&rsquo;t say anything about the tested languages.</p>
<p>And if you want to test just the CPU intensive parts and avoid all networking interference altogether, you can turn on HTTP Cache mode and run the tests twice so the first run will cache everything first, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># crystal:
time ./cr_manga_downloadr --test --cache

# MRI:
time bin/manga-downloadr --test --cache

# JRuby (you have to edit Gemfile to uncomment the JRuby engine):
time jruby --dev -S bin/manga-downloadr --test --cache

# Elixir:
time CACHE_HTTP=true ./ex_manga_downloadr --test</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, <strong>with all requests already cached</strong> these are the results:</p>
<p>Elixir:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>7,13s user 0,24s system 331% cpu 2,227 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>MRI Ruby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>5.590000   0.180000   5.770000 (  5.678714)
5,87s user 0,21s system 101% cpu 5,996 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>JRuby:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>10.580000   0.180000  10.760000 (  3.184472)
14,54s user 0,44s system 262% cpu 5,716 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Crystal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>1.610000   0.050000   1.660000 (  1.344123)
1,62s user 0,06s system 124% cpu 1,350 total</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The Ruby/JRuby/Crystal versions have internal benchmarks to take away startup time (this is why they have 2 lines of times).</p>
<p>So, Elixir is very fast. It takes roughly 2 seconds to parse all the 1,900 HTML files to find the links.</p>
<p>Ruby is the slowest, obviously. It takes almost 6 seconds.</p>
<p>The JRuby version also takes almost 6 seconds but internally it processes in 3 seconds, the rest is startup time and warming up of the JVM underneath.</p>
<p>And Crystal is the fastest, as you would expect because it&rsquo;s a super optimized binary doing CPU bound operations, clocking in at a bit more than 1 second.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Even though the algorithms are roughly similar, Elixir is still winning by a very large margin in the total process (with the external HTTP requests).</p>
<p>There is more than just interpreter/compiler speeds, there is more than single-thread, multi-thread, fibers infrastructure.</p>
<p>We also have the maturity of the respective standard libraries (including TCP stack, HTTP client libraries, String/Array/Regex operations, etc) and 3rd party libraries (libXML, Nokogiri, etc). So there is a lot that can interfere with the tests. I&rsquo;d guess that Crystal&rsquo;s standard library, especially the networking parts, are not battle-tested enough at this point (pre 1.0!).</p>
<p>So, the summary with the new results is this:</p>
<ul>
<li>MRI Ruby 2.3.1: 27 seconds</li>
<li>JRuby 9.1.1.0: 30 seconds</li>
<li>Crystal 0.17.0: 21 seconds</li>
<li>Elixir: 15 seconds</li>
</ul>
<p>Let me know if you have ideas to make them even faster!</p>
]]></content:encoded><category>anime-and-manga</category><category>performance</category><category>distributed-systems</category></item><item><title>Improving your Microservices Integration Performance with Memcache and ETAGs</title><link>https://akitaonrails.github.io/en/2016/03/23/improving-your-microservices-integration-performance-with-memcache-and-etags/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2016/03/23/improving-your-microservices-integration-performance-with-memcache-and-etags/</guid><pubDate>Wed, 23 Mar 2016 14:46:00 -0300</pubDate><description>&lt;p&gt;Everybody is into microservices. There is no way around it. In the Rails world we are well equipped to satisfy any trending Javascript Framework&amp;rsquo;s craving for API consumption.&lt;/p&gt;
&lt;p&gt;In summary, most people are just exposing their contents through simple JSON API endpoints and consuming them from other microservices through simple HTTP GETs. The more microservices they add to the chain, the longer the last endpoint takes to return. There are many techniques to improve this situation, but I want to show just a simple one that can solve many common situations without too much hassle.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Everybody is into microservices. There is no way around it. In the Rails world we are well equipped to satisfy any trending Javascript Framework&rsquo;s craving for API consumption.</p>
<p>In summary, most people are just exposing their contents through simple JSON API endpoints and consuming them from other microservices through simple HTTP GETs. The more microservices they add to the chain, the longer the last endpoint takes to return. There are many techniques to improve this situation, but I want to show just a simple one that can solve many common situations without too much hassle.</p>
<p>First of all, if you&rsquo;re dealing with caching, never try to expire cache entries. The most important thing to learn about caching is how to generate proper cache keys. Do it right and most problems with caching are gone.</p>
<p>Second, if you&rsquo;re using HTTP, try to use everything you can from it, instead of reinventing the wheel.</p>
<p>The &ldquo;TL;DR&rdquo; version is: make your APIs return proper ETAGs and handle &ldquo;If-None-Match&rdquo; headers properly, return the correct 304 status code instead of full blown 200 with full content body in the responses every time. And in the consumer end, cache the ETAG with the corresponding response body and use it from cache when you receive 304s. You will save at least expensive rendering time in the consumed end and slow bandwidth from the consumer end. In the end you should be able to at least be 100% faster, or more, with just a few tweaks.</p>
<h3>The Example Applications<span class="hx:absolute hx:-mt-20" id="the-example-applications"></span>
    <a href="#the-example-applications" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In a very very contrived example we could have a Rails API controller like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1">## 1st application</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Api</span><span class="o">::</span><span class="no">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="o">.</span><span class="n">page</span> <span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span> <span class="o">||</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="ss">json</span><span class="p">:</span> <span class="o">[</span><span class="ss">num_pages</span><span class="p">:</span> <span class="vi">@products</span><span class="o">.</span><span class="n">num_pages</span><span class="p">,</span> <span class="ss">products</span><span class="p">:</span> <span class="vi">@products</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>For the purposes of this contrived post example, we load it up on localhost port <strong>3001</strong>. Now, whenever we call &ldquo;http://localhost:3001/api/products?page=1&rdquo; the API server dumps something like this in the log:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/api/products?page=1&#34; for 127.0.0.1 at 2016-03-23 13:29:34 -0300
Processing by Api::ProductsController#index as */*
  Parameters: {&#34;page&#34;=&gt;&#34;1&#34;}
   (0.3ms)  SELECT COUNT(*) FROM &#34;products&#34;
  Product Load (0.9ms)  SELECT  &#34;products&#34;.* FROM &#34;products&#34; LIMIT 100 OFFSET 0
Completed 200 OK in 26ms (Views: 23.0ms | ActiveRecord: 1.2ms)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In summary, it&rsquo;s taking around <strong>26ms</strong> to send back a JSON with the first page of products of this application. Not too bad.</p>
<p>Then we can create another Rails API application that consumes this first one. Something also very contrived and stupid like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># 2nd application</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Api</span><span class="o">::</span><span class="no">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># never, ever, hard code hostnames like this, use dotenv-rails or secrets.yml</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;http://localhost:3001/api/products?page=?&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;1&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">get_response</span><span class="p">(</span><span class="no">URI</span><span class="p">(</span><span class="n">url</span><span class="p">))</span><span class="o">.</span><span class="n">body</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">[</span><span class="s2">&#34;Content-Type&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;application/json&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="ss">plain</span><span class="p">:</span> <span class="n">json</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We load this other app in localhost port <strong>3000</strong> and when we call &ldquo;http://localhost:3000/api/products?page=1&rdquo; the server dumps the following log:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/api/products?page=1&#34; for 127.0.0.1 at 2016-03-23 13:31:59 -0300
Processing by Api::ProductsController#index as HTML
  Parameters: {&#34;page&#34;=&gt;&#34;1&#34;}
  Rendered text template (0.0ms)
Completed 200 OK in 51ms (Views: 7.1ms | ActiveRecord: 0.0ms)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, this second application is taking twice the time compared to the first one. We can assume that part of those 51ms are the 26ms of the first application.</p>
<p>The more APIs we add on top of each other, the more time the entire flow will take. 26ms for the first, another 25ms for the second, and so on.</p>
<p>There are many things we could do. But I&rsquo;d argue that we should start simple: by actually using a bit more of the HTTP protocol.</p>
<h3>Sending proper ETAGs and handling &ldquo;If-None-Match&rdquo;<span class="hx:absolute hx:-mt-20" id="sending-proper-etags-and-handling-if-none-match"></span>
    <a href="#sending-proper-etags-and-handling-if-none-match" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In a nutshell, we can tag any HTTP response with an ETAG, an identifier for the content of the response. If the ETAG is the same, we can assume the content hasn&rsquo;t changed. Web browsers receive the ETAGs and send them back if we choose to refresh the content as a &ldquo;If-None-Match&rdquo; header. When a web server receives this header it compares against the ETAG of the response and doesn&rsquo;t send back any content, just a &ldquo;304 Not Modified&rdquo; HTTP header, which is much, much lighter and faster to transport back.</p>
<p>An ETAG can be as complicated as an entire SHA256 hexdigest of the entire response content or as simple as just the &ldquo;updated_at&rdquo; timestamp if this indicates that the record has changed (in a &ldquo;show&rdquo; controller action, for example). It must be a digest that represents the content and it must change if the content changes.</p>
<p>Rails has had support for ETAGs for a long time in the <a href="http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html"target="_blank" rel="noopener">ActionController::ConditionalGet</a> API.</p>
<p>In our contrived example, the 1st application on port 3001 fetches a page of ActiveRecord objects and sends back an array represented in JSON format. If we choose to digest the final content we would have to let ActionView do its job, but it is by far the most expensive operation so we want to avoid it.</p>
<p>One thing that we could do is just check the &ldquo;updated_at&rdquo; fields of all the records and see if they changed. If any one of them changed, we would need to re-render everything and send a new ETAG and a new response body. So the code could be like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Api</span><span class="o">::</span><span class="no">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">layout</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@products</span> <span class="o">=</span> <span class="no">Product</span><span class="o">.</span><span class="n">page</span> <span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span> <span class="o">||</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">stale?</span><span class="p">(</span><span class="n">freshness</span> <span class="vi">@products</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="ss">json</span><span class="p">:</span> <span class="o">[</span><span class="ss">num_pages</span><span class="p">:</span> <span class="vi">@products</span><span class="o">.</span><span class="n">num_pages</span><span class="p">,</span> <span class="ss">products</span><span class="p">:</span> <span class="vi">@products</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">freshness</span><span class="p">(</span><span class="n">collection</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">dates</span> <span class="o">=</span> <span class="n">collection</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:updated_at</span><span class="p">)</span><span class="o">.</span><span class="n">sort</span>
</span></span><span class="line"><span class="cl">    <span class="n">etag</span> <span class="o">=</span> <span class="n">dates</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:to_i</span><span class="p">)</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:+</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">etag</span><span class="p">:</span> <span class="no">Digest</span><span class="o">::</span><span class="no">MD5</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(</span><span class="n">etag</span><span class="o">.</span><span class="n">to_s</span><span class="p">),</span> <span class="ss">last_modified</span><span class="p">:</span> <span class="n">dates</span><span class="o">.</span><span class="n">last</span><span class="p">,</span> <span class="kp">public</span><span class="p">:</span> <span class="kp">true</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, when we try to &ldquo;curl -I http://localhost:3001/api/products?page=1&rdquo; we will see the following headers:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>HTTP/1.1 200 OK 
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Etag: &#34;ccf372c24cd259d0943fa3dc99830b10&#34;
Last-Modified: Wed, 23 Mar 2016 16:25:53 GMT
Content-Type: application/json; charset=utf-8
Cache-Control: public
X-Request-Id: 601f22bc-72a9-4960-97cb-c30a0b56dbf4
X-Runtime: 0.053529
Server: WEBrick/1.3.1 (Ruby/2.3.0/2015-12-25)
Date: Wed, 23 Mar 2016 17:00:13 GMT
Content-Length: 0
Connection: Keep-Alive</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Great! We have an ETAG that uniquely represents this page of products. Now we can go one step further and add the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Gemfile</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;dalli&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;rack-cache&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># config/environments/[development|production].rb</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">cache_store</span> <span class="o">=</span> <span class="ss">:dalli_store</span>
</span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="no">Dalli</span><span class="o">::</span><span class="no">Client</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">action_dispatch</span><span class="o">.</span><span class="n">rack_cache</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:metastore</span>    <span class="o">=&gt;</span> <span class="n">client</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:entitystore</span>  <span class="o">=&gt;</span> <span class="n">client</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">static_cache_control</span> <span class="o">=</span> <span class="s2">&#34;public, max-age=2592000&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This configuration is assuming that we have Memcached installed and running in the same localhost machine (our development environment), but in production you can follow this <a href="https://devcenter.heroku.com/articles/rack-cache-memcached-rails31"target="_blank" rel="noopener">good documentation from Heroku</a>.</p>
<p>Now, our 1st application has an internal HTTP cache, with the same role as something more advanced such as Varnish in front of it. It will cache all HTTP 200 responses from the application, together with the ETAGs. Whenever a new call comes for the same URI, it will check the cache first, and if the application sends back the same ETAG, it will send the content back from the cache.</p>
<p>So if we call the above &ldquo;curl&rdquo; command multiple times, we will see this from the Rails server log:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/api/products?page=1&#34; for 127.0.0.1 at 2016-03-23 14:05:16 -0300
Processing by Api::ProductsController#index as */*
  Parameters: {&#34;page&#34;=&gt;&#34;1&#34;}
  Product Load (0.8ms)  SELECT  &#34;products&#34;.* FROM &#34;products&#34; LIMIT 100 OFFSET 0
  Couldn&#39;t find template for digesting: products/index
   (1.0ms)  SELECT COUNT(*) FROM &#34;products&#34;
Completed 200 OK in 31ms (Views: 16.3ms | ActiveRecord: 1.8ms)
cache: [HEAD /api/products?page=1] miss, store</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice the last line: it says that it tried to find the returning ETAG in the cache and it &ldquo;missed&rdquo;, so it &ldquo;stored&rdquo; the new content. Now, if we run the same &ldquo;curl&rdquo; command again, we will see this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/api/products?page=1&#34; for 127.0.0.1 at 2016-03-23 14:05:59 -0300
Processing by Api::ProductsController#index as */*
  Parameters: {&#34;page&#34;=&gt;&#34;1&#34;}
  Product Load (0.5ms)  SELECT  &#34;products&#34;.* FROM &#34;products&#34; LIMIT 100 OFFSET 0
  Couldn&#39;t find template for digesting: products/index
Completed 304 Not Modified in 12ms (ActiveRecord: 0.5ms)
cache: [HEAD /api/products?page=1] stale, valid, store</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The simple curl command is not sending the &ldquo;If-None-Match&rdquo; header, so it expects to receive the full response body. But because we have Rack Cache it is adding the &ldquo;If-None-Match&rdquo; ETAG digest from the cache to the request before hitting the Rails app. The Rails app now compares the received &ldquo;If-None-Match&rdquo; digest through the &ldquo;stale?&rdquo; methods with the ETAG it just computed and because they match, it just sends an empty body response with the status code of 304. Rack Cache receives the 304 and fetches the cached JSON from Memcached and changes the HTTP response from the 304 to a normal 200 with the full body, which is what Curl can receive.</p>
<p>Because we are skipping the expensive ActionView rendering, the response time went from the previous 26ms to around 12ms: we are now <strong>twice</strong> as fast!</p>
<h3>Consuming APIs with ETAGs<span class="hx:absolute hx:-mt-20" id="consuming-apis-with-etags"></span>
    <a href="#consuming-apis-with-etags" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>But we can go one step further. If we change nothing about the 2nd application, it will keep receiving just HTTP 200 with full body responses from Rack Cache of the 1st application. Let&rsquo;s see the code again</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># 2nd application</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Api</span><span class="o">::</span><span class="no">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># never, ever, hard code hostnames like this, use dotenv-rails or secrets.yml</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;http://localhost:3001/api/products?page=?&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;1&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">get_response</span><span class="p">(</span><span class="no">URI</span><span class="p">(</span><span class="n">url</span><span class="p">))</span><span class="o">.</span><span class="n">body</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">[</span><span class="s2">&#34;Content-Type&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;application/json&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="ss">plain</span><span class="p">:</span> <span class="n">json</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We can do better. How about the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># 2nd application - upgrade!</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Api</span><span class="o">::</span><span class="no">ProductsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="n">page</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;1&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span> <span class="o">=</span> <span class="s2">&#34;http://localhost:3001/api/products?page=?&#34;</span> <span class="o">%</span> <span class="n">page</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 1 - fetch the ETAG for the URL</span>
</span></span><span class="line"><span class="cl">    <span class="n">etag</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 2 - fetch from external API or fetch from internal cache</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span> <span class="o">=</span> <span class="n">fetch_with_etag</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">etag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="o">[</span><span class="s2">&#34;Content-Type&#34;</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;application/json&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="ss">plain</span><span class="p">:</span> <span class="n">json</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">fetch_with_etag</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">etag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">req</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">::</span><span class="no">Get</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 3 - add the important If-None-Match header</span>
</span></span><span class="line"><span class="cl">    <span class="n">req</span><span class="o">[</span><span class="s1">&#39;If-None-Match&#39;</span><span class="o">]</span> <span class="o">=</span> <span class="n">etag</span> <span class="k">if</span> <span class="n">etag</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">res</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="n">uri</span><span class="o">.</span><span class="n">hostname</span><span class="p">,</span> <span class="n">uri</span><span class="o">.</span><span class="n">port</span><span class="p">)</span> <span class="p">{</span><span class="o">|</span><span class="n">http</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">http</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">etag</span> <span class="o">=</span> <span class="n">res</span><span class="o">[</span><span class="s1">&#39;ETAG&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">etag</span> <span class="o">=</span> <span class="n">etag</span><span class="o">[</span><span class="mi">1</span><span class="o">..-</span><span class="mi">2</span><span class="o">]</span> <span class="k">if</span> <span class="n">etag</span><span class="o">.</span><span class="n">present?</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">res</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Net</span><span class="o">::</span><span class="no">HTTPNotModified</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># 4 - if got a 304, then we already have the content in the internal cache</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rails</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">etag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elsif</span> <span class="n">res</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Net</span><span class="o">::</span><span class="no">HTTPSuccess</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># 5 - if we got a 200 it&#39;s new content to store in internal cache before returning</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rails</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">etag</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">Rails</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">etag</span><span class="p">,</span> <span class="n">res</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">res</span><span class="o">.</span><span class="n">body</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;{}&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I know, feels overwhelming, but it&rsquo;s actually quite simple. Let&rsquo;s go over it step-by-step:</p>
<ol>
<li>
<p>First we see if we already have an ETAG associated with the URL we want to fetch (be aware of query parameters!)</p>
</li>
<li>
<p>Now we call the separated &ldquo;fetch_with_etag&rdquo; method</p>
</li>
<li>
<p>This is all boilerplate &ldquo;Net::HTTP&rdquo; setup, but the important piece is that we add the &ldquo;If-None-Match&rdquo; header if we found an ETAG for the URL in the cache.</p>
</li>
<li>
<p>After we make the external call we can have 2 responses. The first being the very very short, body-less, header-only, &ldquo;304 Not Modified&rdquo;. In this case, it means that we already have the full content in the internal cache and it is still valid, so we use it.</p>
</li>
<li>
<p>In case we receive the normal HTTP &ldquo;200&rdquo; status code, we either didn&rsquo;t send any ETAG or the one we sent was invalidated and a new ETAG and content body were returned, so we must update them in our internal cache before exiting.</p>
</li>
</ol>
<p>Now, the first time we call &ldquo;curl http://localhost:3000/api/products?page=1&rdquo; for the 2nd application endpoint we will see this log:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/api/products?page=1&#34; for 127.0.0.1 at 2016-03-23 14:14:05 -0300
Processing by Api::ProductsController#index as */*
  Parameters: {&#34;page&#34;=&gt;&#34;1&#34;}
  Rendered text template (0.0ms)
Completed 200 OK in 62ms (Views: 5.6ms | ActiveRecord: 0.0ms)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Caches are cold, it is taking the same &ldquo;around 50ms&rdquo; like we had before, in this case, it&rsquo;s more like 62ms.</p>
<p>Just to recap, this call to the 2nd application made it call the 1st application API, which shows the following in its log:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/api/products?page=?&#34; for 127.0.0.1 at 2016-03-23 14:14:05 -0300
Processing by Api::ProductsController#index as */*
  Parameters: {&#34;page&#34;=&gt;&#34;?&#34;}
/&#34;32b82ebbd99854ea2ca0d49ff4a7c07c
  Product Load (0.9ms)  SELECT  &#34;products&#34;.* FROM &#34;products&#34; LIMIT 100 OFFSET 0
  Couldn&#39;t find template for digesting: products/index
   (1.2ms)  SELECT COUNT(*) FROM &#34;products&#34;
Completed 200 OK in 37ms (Views: 21.5ms | ActiveRecord: 2.2ms)
cache: [GET /api/products?page=?] miss, store</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Cache miss, new content stored!</p>
<p>Now, we call &ldquo;curl&rdquo; against the same URL for the 2nd application again and we now see what we wanted in the log:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/api/products?page=1&#34; for 127.0.0.1 at 2016-03-23 14:14:10 -0300
Processing by Api::ProductsController#index as */*
  Parameters: {&#34;page&#34;=&gt;&#34;1&#34;}
  Rendered text template (0.0ms)
Completed 200 OK in 24ms (Views: 0.3ms | ActiveRecord: 0.0ms)</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Down from 62ms to 24ms!! And in the 1st application log we see:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Started GET &#34;/api/products?page=?&#34; for 127.0.0.1 at 2016-03-23 14:14:10 -0300
Processing by Api::ProductsController#index as */*
  Parameters: {&#34;page&#34;=&gt;&#34;?&#34;}
&#34;ccf372c24cd259d0943fa3dc99830b10&#34;, ccf372c24cd259d0943fa3dc99830b10
  Product Load (1.2ms)  SELECT  &#34;products&#34;.* FROM &#34;products&#34; LIMIT 100 OFFSET 0
  Couldn&#39;t find template for digesting: products/index
Completed 304 Not Modified in 12ms (ActiveRecord: 1.2ms)
cache: [GET /api/products?page=?] stale, valid, store</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A cache hit! Content is stale and valid, so return just 304, the 2nd application acknowledges and fetches the still valid content from its own cache and returns to Curl.</p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If you remove ETAGs from the 1st application, the 2nd one will not break and vice-versa, because it&rsquo;s optional. If &ldquo;ETAG&rdquo; and &ldquo;If-None-Match&rdquo; headers are present in received HTTP response, we can use them, otherwise they will work as before.</p>
<p>If the 2nd application is itself another API you should also add ETAGs for it, and so on. In this example we didn&rsquo;t, just because I wanted to simplify the scenario. But instead of being just a simple one-to-one proxy, it could be one of those &ldquo;porcelain&rdquo; APIs that fetch data from several other smaller microservices, compile them down into a single structure and return it. You should create ETAGs that could be the returning ETAGs from all the other microservices digested together in a single ETAG, for example. Because you&rsquo;re just receiving headers and fetching their content from an internal cache, it&rsquo;s quite cheap. Something like this pseudo-code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">  <span class="n">url1</span> <span class="o">=</span> <span class="s2">&#34;http://somehost1.foo/some_endpoint/1&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">url2</span> <span class="o">=</span> <span class="s2">&#34;http://somehost1.foo/some_endpoint/2&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">etag1</span> <span class="o">=</span> <span class="n">etag_from</span><span class="p">(</span><span class="n">url1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">etag2</span> <span class="o">=</span> <span class="n">etag_from</span><span class="p">(</span><span class="n">url2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">etag</span> <span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">MD5</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">(</span><span class="n">etag1</span> <span class="o">+</span> <span class="n">etag2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">stale?</span><span class="p">(</span><span class="ss">etag</span><span class="p">:</span> <span class="n">etag</span><span class="p">,</span> <span class="kp">public</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">body1</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span> <span class="n">fetch_from</span><span class="p">(</span><span class="n">url1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">body2</span> <span class="o">=</span> <span class="no">JSON</span><span class="o">.</span><span class="n">parse</span> <span class="n">fetch_from</span><span class="p">(</span><span class="n">url2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">do_some_processing</span><span class="p">(</span><span class="n">body1</span><span class="p">,</span> <span class="n">body2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="ss">json</span><span class="p">:</span> <span class="n">result</span><span class="o">.</span><span class="n">to_json</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Another thing: you can add any vanilla HTTP Cache between your microservices, to add authorization, security, or just plain extra caching, it&rsquo;s just HTTP with proper headers. But the more you exchange &ldquo;304&rdquo; between your services, the less processing and the less bandwidth you&rsquo;re spending. It should be noticeably efficient in most cases. But again, it&rsquo;s not always cheap or trivial to generate the cache keys/ETAGs to begin with, so this is the point to take more care.</p>
<p>And if you&rsquo;re creating heavy Javascript apps that also consume those APIs, I &ldquo;believe&rdquo; the Ajax calls properly cache HTTP content and send back the correct &ldquo;If-None-Match&rdquo; and in case they receive 304s, your application should get the normal &ldquo;success&rdquo; triggers. I didn&rsquo;t test this when I was writing this post but I think this is the case indeed. So you should automatically get better performance in your front-end application for free if you add proper ETAGs in your APIs.</p>
<p>This is particularly useful for APIs that return data that doesn&rsquo;t change too often. If it changes every second, or every minute, you should not see too much gains. But if it&rsquo;s something like this example: products lists that only change once every day or every week, or ZIP code lists, or Previous Orders in an e-commerce. Any data that changes infrequently is a good candidate. And the larger the dataset, the larger the benefits you will see (if it&rsquo;s a megabyte long listing, for example). As usual, this is also no Silver Bullet, but in this case it is not so much work to add ETAGs and there are near-zero side-effects, so why not?</p>
<p>ETAG is just one of many other HTTP features you should be using, CORS is another one (Research <a href="https://github.com/cyu/rack-cors"target="_blank" rel="noopener">Rack Cors</a>).</p>
<p>If you&rsquo;re from Brazil, you should watch Nando Vieira&rsquo;s entire course on the broad subject of <a href="http://howtocode.com.br/cursos/rails-caching"target="_blank" rel="noopener">Rails Caching</a>.</p>
<p>To be honest, I&rsquo;m not sure how effective this technique can actually be in all kinds of scenarios so I am very interested in hearing your feedback in case you use something like this in your applications.</p>
]]></content:encoded><category>performance</category><category>rails</category><category>distributed-systems</category></item><item><title>Fixing DHH's Rails 5 Chat Demo</title><link>https://akitaonrails.github.io/en/2015/12/28/fixing-dhh-s-rails-5-chat-demo/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/28/fixing-dhh-s-rails-5-chat-demo/</guid><pubDate>Mon, 28 Dec 2015 16:11:00 -0200</pubDate><description>&lt;p&gt;So, Rails 5.0.0 Beta 1 has &lt;a href="http://weblog.rubyonrails.org/2015/12/18/Rails-5-0-beta1/"target="_blank" rel="noopener"&gt;just been released&lt;/a&gt; and the main new feature is &lt;a href="https://github.com/rails/rails/tree/master/actioncable"target="_blank" rel="noopener"&gt;Action Cable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s basically a complete solution on top of vanilla Rails so you can implement WebSocket based applications (the usual real time chats and notifications) with full access to your Rails assets (models, view templates, etc). For small to medium apps, this is a terrific solution that you might want to use instead of having to go to Node.js.&lt;/p&gt;</description><content:encoded><![CDATA[<p>So, Rails 5.0.0 Beta 1 has <a href="http://weblog.rubyonrails.org/2015/12/18/Rails-5-0-beta1/"target="_blank" rel="noopener">just been released</a> and the main new feature is <a href="https://github.com/rails/rails/tree/master/actioncable"target="_blank" rel="noopener">Action Cable</a>.</p>
<p>It&rsquo;s basically a complete solution on top of vanilla Rails so you can implement WebSocket based applications (the usual real time chats and notifications) with full access to your Rails assets (models, view templates, etc). For small to medium apps, this is a terrific solution that you might want to use instead of having to go to Node.js.</p>
<p>In summary you control Cable Channels that can receive messages sent through a WebSocket client wiring. The new Channel generator takes care of the boilerplate and you just have to fill in the blanks for what kinds of messages you want to send from the client, what you want to broadcast from the server, and to what channels that your clients are subscribed to.</p>
<p>For a more in-depth introduction, DHH himself published a bare-bones <a href="https://www.youtube.com/watch?v=n0WUjGkDFS0"target="_blank" rel="noopener">Action Cable screencast</a> that you should watch just to get a feeling of what the fuzz is all about. If you watched it already and have experience in programming, you may have spotted the problem I mention in the title, so just jump to <a href="#the-problem">&ldquo;The Problem&rdquo;</a> section below for a TL;DR.</p>
<p>In the end you will end up with a code base like the one I reproduced in my Github repository up until the <a href="https://github.com/akitaonrails/rails5-actioncable-demo/tree/end_of_dhh"target="_blank" rel="noopener">tag &ldquo;end_of_dhh&rdquo;</a>. You will have a (very) bare-bones single-room real time chat app for you to play with the main components.</p>
<p>Let&rsquo;s just list the main components here. First, you will have the ActionCable server mounted in the &ldquo;routes.rb&rdquo; file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/routes.rb</span>
</span></span><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;rooms#show&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Serve websocket cable requests in-process</span>
</span></span><span class="line"><span class="cl">  <span class="n">mount</span> <span class="no">ActionCable</span><span class="o">.</span><span class="n">server</span> <span class="o">=&gt;</span> <span class="s1">&#39;/cable&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is the main server component, the channel:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/channels/room_channel.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RoomChannel</span> <span class="o">&lt;</span> <span class="no">ApplicationCable</span><span class="o">::</span><span class="no">Channel</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">subscribed</span>
</span></span><span class="line"><span class="cl">    <span class="n">stream_from</span> <span class="s2">&#34;room_channel&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">unsubscribed</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Any cleanup needed when channel is unsubscribed</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">speak</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Message</span><span class="o">.</span><span class="n">create!</span> <span class="ss">content</span><span class="p">:</span> <span class="n">data</span><span class="o">[</span><span class="s1">&#39;message&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then you have the boilerplate Javascript:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/assets/javascripts/cable.coffee</span>
</span></span><span class="line"><span class="cl"><span class="c1">#= require action_cable</span>
</span></span><span class="line"><span class="cl"><span class="c1">#= require_self</span>
</span></span><span class="line"><span class="cl"><span class="c1">#= require_tree ./channels</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span>
</span></span><span class="line"><span class="cl"><span class="vi">@App</span> <span class="o">||=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="no">App</span><span class="o">.</span><span class="n">cable</span> <span class="o">=</span> <span class="no">ActionCable</span><span class="o">.</span><span class="n">createConsumer</span><span class="p">()</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the main client-side Websocket hooks:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/assets/javascripts/channels/room.coffee</span>
</span></span><span class="line"><span class="cl"><span class="no">App</span><span class="o">.</span><span class="n">room</span> <span class="o">=</span> <span class="no">App</span><span class="o">.</span><span class="n">cable</span><span class="o">.</span><span class="n">subscriptions</span><span class="o">.</span><span class="n">create</span> <span class="s2">&#34;RoomChannel&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">connected</span><span class="p">:</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Called when the subscription is ready for use on the server</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="ss">disconnected</span><span class="p">:</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Called when the subscription has been terminated by the server</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="ss">received</span><span class="p">:</span> <span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">$</span><span class="p">(</span><span class="s1">&#39;#messages&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">append</span> <span class="n">data</span><span class="o">[</span><span class="s1">&#39;message&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="ss">speak</span><span class="p">:</span> <span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@perform</span> <span class="s1">&#39;speak&#39;</span><span class="p">,</span> <span class="ss">message</span><span class="p">:</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">$</span><span class="p">(</span><span class="n">document</span><span class="p">)</span><span class="o">.</span><span class="n">on</span> <span class="s2">&#34;keypress&#34;</span><span class="p">,</span> <span class="s2">&#34;[data-behavior~=room_speaker]&#34;</span><span class="p">,</span> <span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">event</span><span class="o">.</span><span class="n">keyCode</span> <span class="n">is</span> <span class="mi">13</span>
</span></span><span class="line"><span class="cl">    <span class="no">App</span><span class="o">.</span><span class="n">room</span><span class="o">.</span><span class="n">speak</span> <span class="n">event</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">value</span>
</span></span><span class="line"><span class="cl">    <span class="n">event</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">event</span><span class="o">.</span><span class="n">preventDefault</span><span class="p">()</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The view template is a bare-bones HTML just to hook a simple form and div to list the messages:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/rooms/show.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Chat room<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;messages&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>%= render @messages %&gt;
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">form</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">label</span><span class="p">&gt;</span>Say something:<span class="p">&lt;/</span><span class="nt">label</span><span class="p">&gt;&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span> <span class="na">data-behavior</span><span class="o">=</span><span class="s">&#34;room_speaker&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><a name="the-problem"></a></p>
<h3>The Problem<span class="hx:absolute hx:-mt-20" id="the-problem"></span>
    <a href="#the-problem" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In the &ldquo;RoomChannel&rdquo;, you have the &ldquo;<tt>speak</tt>&rdquo; method that saves a message to the database. This is already a red flag for a WebSocket action that is supposed to have very short-lived, light processing. Saving to the database is to be considered heavyweight, specially under load. If this is processed inside EventMachine&rsquo;s reactor loop, it will block the loop and prevent other concurrent processing from taking place until the database releases the lock.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/channels/room_channel.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RoomChannel</span> <span class="o">&lt;</span> <span class="no">ApplicationCable</span><span class="o">::</span><span class="no">Channel</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">speak</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Message</span><span class="o">.</span><span class="n">create!</span> <span class="ss">content</span><span class="p">:</span> <span class="n">data</span><span class="o">[</span><span class="s1">&#39;message&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I would say that anything that goes inside the channel should be asynchronous!</p>
<p>To add insult to injury, this is what you have in the &ldquo;Message&rdquo; model itself:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Message</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">after_create_commit</span> <span class="p">{</span> <span class="no">MessageBroadcastJob</span><span class="o">.</span><span class="n">perform_later</span> <span class="nb">self</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>A model callback (avoid those as the plague!!) to broadcast the received message to the subscribed Websocket clients as an ActiveJob that looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MessageBroadcastJob</span> <span class="o">&lt;</span> <span class="no">ApplicationJob</span>
</span></span><span class="line"><span class="cl">  <span class="n">queue_as</span> <span class="ss">:default</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">ActionCable</span><span class="o">.</span><span class="n">server</span><span class="o">.</span><span class="n">broadcast</span>  <span class="s1">&#39;room_channel&#39;</span><span class="p">,</span> <span class="ss">message</span><span class="p">:</span> <span class="n">render_message</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">render_message</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">ApplicationController</span><span class="o">.</span><span class="n">renderer</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="ss">partial</span><span class="p">:</span> <span class="s1">&#39;messages/message&#39;</span><span class="p">,</span> <span class="ss">locals</span><span class="p">:</span> <span class="p">{</span> <span class="ss">message</span><span class="p">:</span> <span class="n">message</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It renders the HTML snippet to send back for the Websocket clients to append to their browser DOMs.</p>
<p>DHH even goes on to say <em>&ldquo;I&rsquo;d like to show it because this is how most apps will end up.&rdquo;</em></p>
<p>Indeed, the <strong>problem</strong> is that most people will just follow this pattern and it&rsquo;s a big trap. So, what&rsquo;s the solution instead?</p>
<h3>The Proper Solution<span class="hx:absolute hx:-mt-20" id="the-proper-solution"></span>
    <a href="#the-proper-solution" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>For just the purposes of a simple screencast, let&rsquo;s make a quick fix.</p>
<p>First of all, if at all possible you want your channel code to block as little as possible. Waiting for a blocking operation in the database (writing) is definitely not one of them. The Job is underused, it should be called straight from the channel &ldquo;speak&rdquo; method, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/channels/room_channel.rb</span>
</span></span><span class="line"><span class="cl"> <span class="k">class</span> <span class="nc">RoomChannel</span> <span class="o">&lt;</span> <span class="no">ApplicationCable</span><span class="o">::</span><span class="no">Channel</span>
</span></span><span class="line"><span class="cl">   <span class="o">...</span>
</span></span><span class="line"><span class="cl">   <span class="k">def</span> <span class="nf">speak</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="no">Message</span><span class="o">.</span><span class="n">create!</span> <span class="ss">content</span><span class="p">:</span> <span class="n">data</span><span class="o">[</span><span class="s1">&#39;message&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="no">MessageBroadcastJob</span><span class="o">.</span><span class="n">perform_later</span> <span class="n">data</span><span class="o">[</span><span class="s1">&#39;message&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">   <span class="k">end</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then, we move the model writing to the Job itself:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/jobs/message_broadcast_job.rb</span>
</span></span><span class="line"><span class="cl"> <span class="k">class</span> <span class="nc">MessageBroadcastJob</span> <span class="o">&lt;</span> <span class="no">ApplicationJob</span>
</span></span><span class="line"><span class="cl">   <span class="n">queue_as</span> <span class="ss">:default</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="no">ActionCable</span><span class="o">.</span><span class="n">server</span><span class="o">.</span><span class="n">broadcast</span>  <span class="s1">&#39;room_channel&#39;</span><span class="p">,</span> <span class="ss">message</span><span class="p">:</span> <span class="n">render_message</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">message</span> <span class="o">=</span> <span class="no">Message</span><span class="o">.</span><span class="n">create!</span> <span class="ss">content</span><span class="p">:</span> <span class="n">data</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="no">ActionCable</span><span class="o">.</span><span class="n">server</span><span class="o">.</span><span class="n">broadcast</span> <span class="s1">&#39;room_channel&#39;</span><span class="p">,</span> <span class="ss">message</span><span class="p">:</span> <span class="n">render_message</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="k">end</span>
</span></span><span class="line"><span class="cl">   <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And finally, we remove that horrible callback from the model and make it bare-bone again:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/models/message.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Message</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This returns quickly, defers processing to a background job and should sustain more concurrency out-of-the-box. The previous, DHH solution, has a built-in bottleneck in the speak method and will choke as soon as the database becomes the bottleneck.</p>
<p>It&rsquo;s by no means a perfect solution yet, but it&rsquo;s less terrible for a very quick demo and the code ends up being simpler as well. You can check out this code in <a href="https://github.com/akitaonrails/rails5-actioncable-demo/commit/0aaaaecc46ed14e98086bac5ce087df08d557456"target="_blank" rel="noopener">my Github repo commit</a>.</p>
<p>I may be wrong in the conclusion that the channel will block or if this is indeed harmful to concurrency. I didn&rsquo;t measure both solutions, it&rsquo;s just a gut feeling from older wounds. If you have more insight into the implementation of Action Cable, leave a comment down below.</p>
<p>By the way, be careful before considering migrating your Rails 4.2 app to Rails 5 just yet. Because of the hard coded dependencies on Faye, Eventmachine, Rails 5 right now rules out Unicorn (even Thin seems to be having problems booting up). It also rules out JRuby and MRI on Windows as well because of Eventmachine.</p>
<p>If you want the capabilities of Action Cable without having to migrate, you can use solutions such as <a href="http://developers.planningcenteronline.com/2014/09/23/live-updating-rails-with-react.js-and-pusher.html"target="_blank" rel="noopener">&ldquo;Pusher.com&rdquo;</a>, or if you want your own in-house solution, follow my evolution on the subject with my <a href="http://www.akitaonrails.com/pusher"target="_blank" rel="noopener">mini-Pusher clone</a> written in Elixir.</p>
]]></content:encoded><category>rails</category><category>backend</category><category>distributed-systems</category></item><item><title>Ex Pusher Lite - Part 2 - First Working Core!</title><link>https://akitaonrails.github.io/en/2015/12/14/ex-pusher-lite-part-2-first-working-core/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/14/ex-pusher-lite-part-2-first-working-core/</guid><pubDate>Mon, 14 Dec 2015 15:04:00 -0200</pubDate><description>&lt;p&gt;In &lt;a href="http://www.akitaonrails.com/en/2015/12/09/ex-pusher-lite-parte-1-phoenix-channels-e-aplicacoes-rails"target="_blank" rel="noopener"&gt;Part 1&lt;/a&gt; I basically started with &lt;a href="https://labs.opendoor.com/phoenix-on-rails-for-client-push-notifications"target="_blank" rel="noopener"&gt;Daniel Neighman&amp;rsquo;s&lt;/a&gt; tutorial.&lt;/p&gt;
&lt;p&gt;In Part 2 I will add the proper mechanisms to make a minimal core that is actually useful and deploy it to Heroku. In order to do that I need to implement the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a simple Administration authentication (a hardcoded admin_username and admin_password will do for now)&lt;/li&gt;
&lt;li&gt;an admin restricted &lt;strong&gt;&amp;quot;/api/admin/apps&amp;quot;&lt;/strong&gt; endpoint to manage new Applications. Each Application should have a randomly generated key and secret.&lt;/li&gt;
&lt;li&gt;the existing &amp;ldquo;/events&amp;rdquo; endpoint from Part 1 should be moved to &lt;strong&gt;&amp;quot;/api/apps/:app_id/events&amp;quot;&lt;/strong&gt; and have access restricted to the authentication of the key and secret of the Application identified as &amp;ldquo;app_id&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;for now, the Event will just broadcast to the appropriate topic. We want to be able to broadcast to everyone from an Application as well to specific topics within the Application scope.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As usual, the code for this section will be tagged as &lt;strong&gt;v0.2&lt;/strong&gt; in both the &lt;a href="https://github.com/akitaonrails/pusher_lite_demo/tree/v0.2"target="_blank" rel="noopener"&gt;client demo&lt;/a&gt; and &lt;a href="https://github.com/akitaonrails/ex_pusher_lite/tree/v0.2"target="_blank" rel="noopener"&gt;server-side&lt;/a&gt; Github repositories.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In <a href="http://www.akitaonrails.com/en/2015/12/09/ex-pusher-lite-parte-1-phoenix-channels-e-aplicacoes-rails"target="_blank" rel="noopener">Part 1</a> I basically started with <a href="https://labs.opendoor.com/phoenix-on-rails-for-client-push-notifications"target="_blank" rel="noopener">Daniel Neighman&rsquo;s</a> tutorial.</p>
<p>In Part 2 I will add the proper mechanisms to make a minimal core that is actually useful and deploy it to Heroku. In order to do that I need to implement the following:</p>
<ul>
<li>a simple Administration authentication (a hardcoded admin_username and admin_password will do for now)</li>
<li>an admin restricted <strong>&quot;/api/admin/apps&quot;</strong> endpoint to manage new Applications. Each Application should have a randomly generated key and secret.</li>
<li>the existing &ldquo;/events&rdquo; endpoint from Part 1 should be moved to <strong>&quot;/api/apps/:app_id/events&quot;</strong> and have access restricted to the authentication of the key and secret of the Application identified as &ldquo;app_id&rdquo;.</li>
<li>for now, the Event will just broadcast to the appropriate topic. We want to be able to broadcast to everyone from an Application as well to specific topics within the Application scope.</li>
</ul>
<p>As usual, the code for this section will be tagged as <strong>v0.2</strong> in both the <a href="https://github.com/akitaonrails/pusher_lite_demo/tree/v0.2"target="_blank" rel="noopener">client demo</a> and <a href="https://github.com/akitaonrails/ex_pusher_lite/tree/v0.2"target="_blank" rel="noopener">server-side</a> Github repositories.</p>
<h3>The App Resource<span class="hx:absolute hx:-mt-20" id="the-app-resource"></span>
    <a href="#the-app-resource" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>If this project of ours is to behave like Pusher.com, we need a way to create new &ldquo;Applications&rdquo;. Each client connecting to this service will be bound to this Application. Events should be restricted to the Application boundary. This is how we will isolate different clients connecting to the same server. So you can have one core serving several different web applications.</p>
<p>Once a new application is created, the client/consumer web app will have the pair of key and secret tokens that it will use to connect both the server-side triggers as well as the client-side Websocket listeners.</p>
<p>As a disclaimer, at this stage of development I will not implement any sophisticated authentication system such as OAuth2 or JWT. I will save this for posts to follow. For now I will use the Application&rsquo;s key and secret just as simple username and password in an HTTP Basic Auth. This should be good enough for our purposes for the time being.</p>
<p>So, the very first step is to create such an &ldquo;Application&rdquo; resource and we can resort to Phoenix&rsquo;s built-in JSON scaffold generator:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix phoenix.gen.json App apps name:string slug:string key:string secret:string active:boolean</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Most tutorials will show you the &ldquo;<tt>phoenix.gen.html</tt>&rdquo; generator, which behaves like Rails&rsquo;s &ldquo;scaffold&rdquo;, generating HTML templates for each of the CRUD verbs. This is similar but it skips HTML and assumes this is going to be a JSON CRUD API.</p>
<p>We need to manually update the &ldquo;<tt>web/router.ex</tt>&rdquo; file like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/router.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">scope</span> <span class="s2">&#34;/api&#34;</span><span class="p">,</span> <span class="no">ExPusherLite</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pipe_through</span> <span class="ss">:api</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span> <span class="s2">&#34;/apps/:app_slug/events&#34;</span><span class="p">,</span> <span class="no">EventsController</span><span class="p">,</span> <span class="ss">:create</span>
</span></span><span class="line"><span class="cl">  <span class="n">scope</span> <span class="s2">&#34;/admin&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">resources</span> <span class="s2">&#34;/apps&#34;</span><span class="p">,</span> <span class="no">AppController</span><span class="p">,</span> <span class="ss">except</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:edit</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The &ldquo;<tt>EventsController</tt>&rdquo; is the one we implemented in Part 1 and that we will overhaul during this Part 2.</p>
<p>The generator gave us this new &ldquo;<tt>AppController</tt>&rdquo;, and similarly to Rails&rsquo; routes, the DSL is remarkably similar here. If you&rsquo;re a Railer I bet you can instantly recognize the routes this DSL is generating.</p>
<p>The generator also created a proper migration for us:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># priv/repo/migrations/20151210131528_create_app.exs</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span><span class="o">.</span><span class="n">Migrations</span><span class="o">.</span><span class="n">CreateApp</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Migration</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">change</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">create</span> <span class="n">table</span><span class="p">(</span><span class="ss">:apps</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:slug</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:key</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:secret</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">      <span class="n">add</span> <span class="ss">:active</span><span class="p">,</span> <span class="ss">:boolean</span><span class="p">,</span> <span class="ss">default</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">timestamps</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">create</span> <span class="n">index</span><span class="p">(</span><span class="ss">:apps</span><span class="p">,</span> <span class="o">[</span><span class="ss">:name</span><span class="o">]</span><span class="p">,</span> <span class="ss">unique</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">create</span> <span class="n">index</span><span class="p">(</span><span class="ss">:apps</span><span class="p">,</span> <span class="o">[</span><span class="ss">:slug</span><span class="o">]</span><span class="p">,</span> <span class="ss">unique</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, remarkably similar to ActiveRecord&rsquo;s Migration DSL. Migrations behave as you expect. You must run:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix ecto.create # if you haven&#39;t already
mix ecto.migrate</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This App resource will need the ability to create slugs out of the names (which we will use as &ldquo;app_id&rdquo;) and also generate random key and secret values. So we must add these dependencies to the &ldquo;<tt>mix.exs</tt>&rdquo; file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># mix.exs</span>
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">deps</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[...</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="ss">:secure_random</span><span class="p">,</span> <span class="s2">&#34;~&gt; 0.2.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span><span class="ss">:slugger</span><span class="p">,</span> <span class="s2">&#34;~&gt; 0.0.1&#34;</span><span class="p">}</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <a href="https://github.com/akitaonrails/ex_pusher_lite/blob/v0.2/web/models/app.ex"target="_blank" rel="noopener">final App model</a> is quite long, so I will break it down for you:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/app.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">App</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:model</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">schema</span> <span class="s2">&#34;apps&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:slug</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:key</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:secret</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">    <span class="n">field</span> <span class="ss">:active</span><span class="p">,</span> <span class="ss">:boolean</span><span class="p">,</span> <span class="ss">default</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">timestamps</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="vi">@required_fields</span> <span class="o">~</span><span class="n">w</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@optional_fields</span> <span class="o">~</span><span class="n">w</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This block declares the model Schema. Be careful if you generate a migration and then change its fields settings: you must remember to update the schema in the model. In my first attempt I didn&rsquo;t include the &ldquo;slug&rdquo; field, so I rolled back the database migration (with &ldquo;<tt>mix ecto.rollback</tt>&rdquo;), changed the migration to add the &ldquo;slug&rdquo; field and re-ran the &ldquo;<tt>ecto.migrate</tt>&rdquo; task.</p>
<p>I was puzzled with the model not picking up the new field; after some time I remembered that Ecto models don&rsquo;t attempt to fetch the real database schema and generate accessors dynamically, instead it relies on the explicitly declared schema block as shown above. After I added the new &ldquo;slug&rdquo; field in the schema block, then the model would properly use the new field.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/app.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_by_slug</span><span class="p">(</span><span class="n">slug</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Repo</span><span class="o">.</span><span class="n">get_by!</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="ss">slug</span><span class="p">:</span> <span class="n">slug</span><span class="p">,</span> <span class="ss">active</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">hashed_secret</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Base</span><span class="o">.</span><span class="n">encode64</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">secret</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>These are just helper functions to use in the AppController. The odd bit might be &ldquo;<strong>MODULE</strong>&rdquo; but this is just a shortcut for the atom representation of the current module, which is &ldquo;<tt>ExPusherLite.App</tt>&rdquo;. This is how you make a simple query to the model, it resembles Rails&rsquo; &ldquo;<tt>App.get_by(slug: slug, active: true)</tt>&rdquo;.</p>
<p>In Elixir convention, Ecto has functions with and without bangs (&quot;<tt>get_by!</tt>&quot; and &ldquo;<tt>get_by</tt>&rdquo;). If you want to catch an error you use the version without bangs and it will return either a &ldquo;<tt>{:ok, result}</tt>&rdquo; tuple or a &ldquo;<tt>{:error, result}</tt>&rdquo; and you can pattern match them. Or you can use the bang version and it will raise an exception. Depends on what you want to do.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/app.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">changeset</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">params</span> <span class="p">\\</span> <span class="ss">:empty</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">model</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="vi">@required_fields</span><span class="p">,</span> <span class="vi">@optional_fields</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">validate_length</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">min</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="ss">max</span><span class="p">:</span> <span class="mi">255</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">unique_constraint</span><span class="p">(</span><span class="ss">:name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">generate_key</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">generate_secret</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">slugify</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">unique_constraint</span><span class="p">(</span><span class="ss">:slug</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Second only to the Schema block I mentioned above, this &ldquo;<tt>changeset/2</tt>&rdquo; function is the most important part of a Model.</p>
<p>In Rails you just have the concept of a &ldquo;Model&rdquo; which is considered &ldquo;Fat&rdquo; because it deals with database operations, business logic and framework hooks all in the same place. In Phoenix you have to deal with at least 3 different concepts:</p>
<ul>
<li>You have a <strong>Repo</strong>, which receives a Changeset and uses it to insert or update the designated rows in the database table. You will see &ldquo;<tt>Repo.get</tt>&rdquo; or &ldquo;<tt>Repo.insert</tt>&rdquo;, not &ldquo;<tt>App.find</tt>&rdquo; or &ldquo;<tt>App.save</tt>&rdquo;.</li>
<li>Then you have the <strong>Changeset</strong> which are just Elixir Maps (the ones with the syntax &ldquo;%<tt>{key =&gt; value}</tt>&rdquo;). It&rsquo;s just a Hash, a Dictionary, a collection of key-value pairs. A Repo accepts Maps for its operations.</li>
<li>Finally, you have the <strong>Model</strong> which actually give meaning and context to the raw Changesets. And the function &ldquo;<tt>changeset/2</tt>&rdquo; above is the one responsible for receiving a raw Map, piping it through a validation and transformation chain and return a valid Changeset for the Repo to use.</li>
</ul>
<p>So, in a controller you will usually find code like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/app_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sx">%{&#34;app&#34; =&gt; app_params}</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">changeset</span> <span class="o">=</span> <span class="no">App</span><span class="o">.</span><span class="n">changeset</span><span class="p">(</span><span class="o">%</span><span class="no">App</span><span class="p">{},</span> <span class="n">app_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">Repo</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">changeset</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">app</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:error</span><span class="p">,</span> <span class="n">changeset</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how you create a new, validated, changeset and then pass it to the Repo, treating the results in a pattern match block. Just compare the above changeset line with the beginning of the &ldquo;changeset/2&rdquo; function:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">changeset</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">params</span> <span class="p">\\</span> <span class="ss">:empty</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">model</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">(</span><span class="n">params</span><span class="p">,</span> <span class="vi">@required_fields</span><span class="p">,</span> <span class="vi">@optional_fields</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">validate_length</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">min</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="ss">max</span><span class="p">:</span> <span class="mi">255</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It maps the &ldquo;<tt>%App{}</tt>&rdquo; empty record to the &ldquo;model&rdquo; argument and the &ldquo;app_params&rdquo; that comes from the request (a map of the format &ldquo;<tt>%{name =&gt; &lsquo;foo&rsquo;, active: =&gt; &rsquo;true&rsquo;}</tt>&rdquo;) to the argument &ldquo;params&rdquo;. Then it pipes the model and params to the &ldquo;<tt>cast/4</tt>&rdquo; function which will copy the values from the params map to the model map/changeset. And it keep passing the resulting changeset to the following functions, such as &ldquo;<tt>validated_length/3</tt>&rdquo; below, and so on. If the chain ends with no exceptions, you end up with a clean, validated changeset that you can just pass to the Repo to blindly insert to the database.</p>
<p>In the above implementation we are chaining filters to generate the key, secret and slug, and this is the implementation as private functions:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/models/app.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">generate_key</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">get_field</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="ss">:key</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">put_change</span><span class="p">(</span><span class="ss">:key</span><span class="p">,</span> <span class="no">SecureRandom</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">generate_secret</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">get_field</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="ss">:secret</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">put_change</span><span class="p">(</span><span class="ss">:secret</span><span class="p">,</span> <span class="no">SecureRandom</span><span class="o">.</span><span class="n">uuid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">slugify</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">name</span> <span class="o">=</span> <span class="n">get_change</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="ss">:name</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">put_change</span><span class="p">(</span><span class="ss">:slug</span><span class="p">,</span> <span class="no">Slugger</span><span class="o">.</span><span class="n">slugify_downcase</span><span class="p">(</span><span class="nb">name</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">model</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The logic is set so new key/secret are generated only if the fields are empty and a new slug is generated only if the name has changes. And this is it, I told you the model code would be a bit large. You can see how to use the Slugger and SecureRandom libraries we added in the &ldquo;mix.exs&rdquo; before.</p>
<p>I also want to add the equivalent of a Rails seed file to create a test application so it&rsquo;s easier for newcomers to know what to do. Phoenix has seeds and you can implement it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># priv/repo/seeds.exs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">App</span>
</span></span><span class="line"><span class="cl"><span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># not using the App.changeset should just avoid all validations and generations</span>
</span></span><span class="line"><span class="cl"><span class="no">Repo</span><span class="o">.</span><span class="n">insert!</span> <span class="o">%</span><span class="no">App</span><span class="p">{</span> <span class="ss">slug</span><span class="p">:</span> <span class="s2">&#34;test-app&#34;</span><span class="p">,</span> <span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Test App&#34;</span><span class="p">,</span> <span class="ss">key</span><span class="p">:</span> <span class="s2">&#34;test-app-fake-key&#34;</span><span class="p">,</span> <span class="ss">secret</span><span class="p">:</span> <span class="s2">&#34;test-app-fake-secret&#34;</span><span class="p">,</span> <span class="ss">active</span><span class="p">:</span> <span class="kp">true</span> <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Remember how I detailed the role of the &ldquo;changeset/2&rdquo; function in creating a clean and validated changeset, which is just a Map? You can skip that function altogether and hand craft your own final Map and pass it to the Repo. The Repo doesn&rsquo;t care if this is a valid Map or not it will just try to insert it into the database regardless. And in this case the App Model prevents us from hardcoding keys and secrets, so this is how we do it in a seed file.</p>
<p>We can just run it directly like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mix run priv/repo/seeds.exs</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The AppController just needs 2 changes. The first is to search the App through the slug field instead of the default &lsquo;id&rsquo; field. This is simple enough, we just replace all calls to &ldquo;<tt>app = Repo.get!(App, id)</tt>&rdquo; to &ldquo;<tt>app = App.get_by_slug(id)</tt>&rdquo;, which is why we implemented this function in the model above.</p>
<p>The second thing is Authentication.</p>
<h3>Adding Authentication<span class="hx:absolute hx:-mt-20" id="adding-authentication"></span>
    <a href="#adding-authentication" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now that we have an App model that can generate secure random UUIDs for key and secret, I will add a second level of authentication for administrators to be able to create such Apps.</p>
<p>For that I will just hard-code a secret in the config file of the application itself to serve as a development default. Like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/config.exs</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">username</span><span class="p">:</span> <span class="s2">&#34;pusher_admin_username&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;pusher_admin_password&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">import_config</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Mix</span><span class="o">.</span><span class="n">env</span><span class="si">}</span><span class="s2">.exs&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>You must add this block before the &ldquo;<tt>import_config</tt>&rdquo; function. Then you can override those values in the &ldquo;config/prod.secret.exs&rdquo; file, for example, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/prod.secret.exs</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">username</span><span class="p">:</span> <span class="s2">&#34;14e86e5fee3335fa88b0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;2b94ff0f07ce9769567f&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Of course, generate your own pair of secure username and password and replace it in the production environment if you intend to actually use this. For Heroku, we will still have to tweak this further, so keep this in mind.</p>
<p>Just to make the process easier, I also added the following helper function:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># lib/ex_pusher_lite.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Return this applicaton administration Basic HTTP Auth hash</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">admin_secret</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">admin_username</span> <span class="o">=</span> <span class="no">Application</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">)</span><span class="o">[</span><span class="ss">:username</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">admin_password</span> <span class="o">=</span> <span class="no">Application</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">)</span><span class="o">[</span><span class="ss">:password</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">secret</span> <span class="o">=</span> <span class="no">Base</span><span class="o">.</span><span class="n">encode64</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">admin_username</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">admin_password</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how you fetch the configuration values. I am generating a simple Base64 encoded string out of the username concatenated with the password with a comma, which is what Basic HTTP Auth requires. I will use this admin hash for the &ldquo;AppController&rdquo; and each client must provide the key/secret in its own App instance to be able to trigger the &ldquo;EventsController&rdquo;.</p>
<p>For both controllers I will create a single Authentication Plug, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># lib/ex_pusher_lite/authentication.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Authentication</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">import</span> <span class="no">Plug</span><span class="o">.</span><span class="n">Conn</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">App</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">assigns</span> <span class="p">\\</span> <span class="o">[</span><span class="ss">admin</span><span class="p">:</span> <span class="kp">false</span><span class="o">]</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="n">assigns</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">assigns</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">token</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">assigns</span><span class="o">[</span><span class="ss">:admin</span><span class="o">]</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">        <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">admin_secret</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">params</span> <span class="o">=</span> <span class="n">fetch_query_params</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span><span class="o">.</span><span class="n">params</span>
</span></span><span class="line"><span class="cl">        <span class="n">params</span><span class="o">[</span><span class="s2">&#34;app_slug&#34;</span><span class="o">]</span> <span class="o">|&gt;</span> <span class="no">App</span><span class="o">.</span><span class="n">get_by_slug</span> <span class="o">|&gt;</span> <span class="no">App</span><span class="o">.</span><span class="n">hashed_secret</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Basic &#34;</span> <span class="o">&lt;&gt;</span> <span class="n">auth_token</span> <span class="o">=</span> <span class="n">hd</span><span class="p">(</span><span class="n">get_req_header</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="s2">&#34;authorization&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="no">Plug</span><span class="o">.</span><span class="n">Crypto</span><span class="o">.</span><span class="n">secure_compare</span><span class="p">(</span><span class="n">auth_token</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span> <span class="o">|&gt;</span> <span class="n">send_resp</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="n">halt</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As I explained in previous articles, a Plug is like a chainable Rails Middleware or even a Rack application. It must have a single &ldquo;<tt>call/2</tt>&rdquo; that receives a Plug.Conn structure and returns it back, allowing to form a chain/pipeline of Plugs.</p>
<p>We check if we want to compare with the Admin token or the App token and then retrieve the Basic HTTP authorization token that&rsquo;s in the HTTP request connection structure (we retrieve individual header values through the &ldquo;<tt>get_req_header/2</tt>&rdquo; function). Finally we make a secure compare between the tokens.</p>
<p>To enable this plug in the controllers we just add it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/app_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">AppController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">App</span>
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Authentication</span><span class="p">,</span> <span class="o">[</span><span class="ss">admin</span><span class="p">:</span> <span class="kp">true</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"> <span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">EventsController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="n">plug</span> <span class="ss">:authenticate</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="n">plug</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Authentication</span>
</span></span><span class="line"><span class="cl">   <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In Part 1 we had a simpler &ldquo;<tt>plug :authenticate</tt>&rdquo; in the EventsController. We can remove it and also the &ldquo;<tt>authenticate/2</tt>&rdquo; function. We just refactored it into a better function that also serves administration authentication now, but the idea is the same.</p>
<p>This is it: the basics for API authentication. Again, this is not the best solution as the username/password pair goes in the URL and it&rsquo;s open to man-in-the-middle attacks. SSL only encrypts the HTTP body but the URL is still open.</p>
<p>For example, if an administrator wants to create a new application, he must do the following:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>curl --data &#34;app[name]=foo-app&#34; http://pusher_admin_username:pusher_admin_password@localhost:4000/api/admin/apps</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this would be one example of the resulting JSON representation of the new app:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>{&#34;data&#34;:{&#34;slug&#34;:&#34;foo-app&#34;,&#34;secret&#34;:&#34;8ef69064-0d7e-c9ef-ac14-b6b1db303e7a&#34;,&#34;name&#34;:&#34;foo-app&#34;,&#34;key&#34;:&#34;9400ad21-eed8-117a-bce5-845262e0a09e&#34;,&#34;id&#34;:5,&#34;active&#34;:true}}%</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>With this new key and secret in hand, we can update our client demo to make use of the new app.</p>
<h3>Configuring the Client Demo<span class="hx:absolute hx:-mt-20" id="configuring-the-client-demo"></span>
    <a href="#configuring-the-client-demo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>We must start by adding the proper Application details in the &ldquo;<tt>.env</tt>&rdquo; file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>PUSHER_URL: &#34;localhost:4000&#34;
PUSHER_APP_ID: &#34;foo-app&#34;
PUSHER_KEY: &#34;9400ad21-eed8-117a-bce5-845262e0a09e&#34;
PUSHER_SECRET: &#34;8ef69064-0d7e-c9ef-ac14-b6b1db303e7a&#34;
PUSHER_CHANNEL: &#34;foo-topic&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We must also tweak the &ldquo;<tt>config/secrets.yml</tt>&rdquo; to reflect the new metadata (development, test, and production must follow this):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>development:
  secret_key_base: ded7c4a2a298c1b620e462b50c9ca6ccb60130e27968357e76cab73de9858f14556a26df885c8aa5004d0a7ca79c0438e618557275bdb28ba67a0ffb0c268056
  pusher_url: &lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;
  pusher_app_id: &lt;%= ENV[&#39;PUSHER_APP_ID&#39;] %&gt;
  pusher_key: &lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;
  pusher_secret: &lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;
  pusher_channel: &lt;%= ENV[&#39;PUSHER_CHANNEL&#39;] %&gt;
  ...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And we can create an initializer to make it easier to use this metadata properly:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/pusher_lite.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">PusherLite</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">uri</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span>    <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_key</span>
</span></span><span class="line"><span class="cl">    <span class="n">secret</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_secret</span>
</span></span><span class="line"><span class="cl">    <span class="n">app_id</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_app_id</span>
</span></span><span class="line"><span class="cl">    <span class="n">url</span>    <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_url</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">uri</span> <span class="o">=</span> <span class="s2">&#34;http://</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">secret</span><span class="si">}</span><span class="s2">@</span><span class="si">#{</span><span class="n">url</span><span class="si">}</span><span class="s2">/api/apps/</span><span class="si">#{</span><span class="n">app_id</span><span class="si">}</span><span class="s2">/events&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">uri</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> </span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, the Rails app will trigger the ExPusherLite server using the Basic HTTP Auth. Do not be fooled into thinking this is &ldquo;secure&rdquo;, it just &ldquo;feels a bit secure through obscurity&rdquo;. You have been warned, wait for the next articles on this subject. But this is usable in controlled environments.</p>
<p>To finalize the upgrades, we must change the client-side access to the new metadata, first changing the application layout:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/layouts/application.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">+  <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;pusher_host&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;&lt;%= Rails.application.secrets.pusher_url %&gt;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">-  <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;pusher_key&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;&lt;%= Rails.application.secrets.pusher_key %&gt;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">+  <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;pusher_app_id&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;&lt;%= Rails.application.secrets.pusher_app_id %&gt;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">   <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;pusher_channel&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;&lt;%= Rails.application.secrets.pusher_channel %&gt;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The javascript &ldquo;<tt>index.es6</tt>&rdquo; fetches from this meta headers, so we must change them there:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="err">#</span> <span class="nx">app</span><span class="o">/</span><span class="nx">assets</span><span class="o">/</span><span class="nx">javascripts</span><span class="o">/</span><span class="nx">application</span><span class="o">/</span><span class="nx">pages</span><span class="o">/</span><span class="nx">home</span><span class="o">/</span><span class="nx">index</span><span class="p">.</span><span class="nx">es6</span>
</span></span><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl">     <span class="kd">let</span> <span class="nx">guardianToken</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=guardian-token]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="kd">let</span> <span class="nx">csrfToken</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=guardian-csrf]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kd">let</span> <span class="nx">pusherHost</span>    <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_host]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="kd">let</span> <span class="nx">pusherKey</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_key]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kd">let</span> <span class="nx">pusherApp</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_app_id]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="kd">let</span> <span class="nx">pusherChannel</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_channel]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="kd">let</span> <span class="nx">socket</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Socket</span><span class="p">(</span><span class="s2">&#34;ws://localhost:4000/socket&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kd">let</span> <span class="nx">socket</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Socket</span><span class="p">(</span><span class="sb">`ws://</span><span class="si">${</span><span class="nx">pusherHost</span><span class="si">}</span><span class="sb">/socket`</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="nx">params</span><span class="o">:</span> <span class="p">{</span> <span class="nx">guardian_token</span><span class="o">:</span> <span class="nx">guardianToken</span><span class="p">,</span> <span class="nx">csrf_token</span><span class="o">:</span> <span class="nx">csrfToken</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">     <span class="p">})</span>
</span></span><span class="line"><span class="cl">     <span class="nx">socket</span><span class="p">.</span><span class="nx">connect</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">     <span class="c1">// Now that you are connected, you can join channels with a topic:
</span></span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="kd">let</span> <span class="nx">channel</span> <span class="o">=</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">channel</span><span class="p">(</span><span class="nx">pusherChannel</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="kd">let</span> <span class="nx">channel</span> <span class="o">=</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">channel</span><span class="p">(</span><span class="sb">`public:</span><span class="si">${</span><span class="nx">pusherApp</span><span class="si">}</span><span class="sb">`</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl">     <span class="nx">channel</span><span class="p">.</span><span class="nx">join</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">       <span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="s2">&#34;ok&#34;</span><span class="p">,</span> <span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Joined successfully&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">)</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">       <span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="s2">&#34;error&#34;</span><span class="p">,</span> <span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Unable to join&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">)</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="nx">channel</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nx">channel</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="sb">`</span><span class="si">${</span><span class="nx">pusherChannel</span><span class="si">}</span><span class="sb">:msg`</span><span class="p">,</span> <span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="kd">let</span> <span class="nx">new_line</span> <span class="o">=</span> <span class="sb">`&lt;p&gt;&lt;strong&gt;</span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">&lt;strong&gt;: </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="sb">&lt;/p&gt;`</span>
</span></span><span class="line"><span class="cl">       <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;.message-receiver&#34;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">new_line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="nx">channel</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="kd">let</span> <span class="nx">new_line</span> <span class="o">=</span> <span class="sb">`&lt;p&gt;&lt;strong&gt;Broadcast to all channels&lt;/strong&gt;: </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="sb">&lt;/p&gt;`</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;.message-receiver&#34;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">new_line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="p">})</span>
</span></span><span class="line"><span class="cl">   <span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>One important modification from Part 1 is that the WebSocket host was hardcoded to &ldquo;localhost&rdquo; and here we are making it configurable through the meta tags. Right now, for localhost tests, we are using the plain &ldquo;ws://&rdquo; protocol but when we deploy to Heroku we will change it to &ldquo;wss://&rdquo; for SSL. Same thing for the &ldquo;PusherLite&rdquo; initializer. Keep that in mind.</p>
<p>Now it&rsquo;s subscribing to a different format of topic/channel. In Part 1 it would be something like: &ldquo;<tt>public:test_channel</tt>&rdquo; now we are listening to &ldquo;<tt>public:foo-app</tt>&rdquo;, so the application is the Websocket subscription &ldquo;topic&rdquo;.</p>
<p>Then we are changing the socket listener to listen for 2 different events. The first one is in the format &ldquo;<tt>test_channel:msg</tt>&rdquo;. So this is how we must now send messages to a specific &ldquo;channel&rdquo; within an &ldquo;app/topic&rdquo;.</p>
<p>And last we still listen to the old &ldquo;msg&rdquo; event, but this serves as a &ldquo;broadcast&rdquo; event for all connected clients subscribed to this particular &ldquo;foo-app&rdquo; Application. Now web clients can listen to specific &ldquo;channels&rdquo; within the &ldquo;app&rdquo; but also receive system wide &ldquo;broadcast&rdquo; messages. This is a big improvement and it didn&rsquo;t require much on the Javascript side.</p>
<p>But what more does it take to make this <strong>&ldquo;channel-only and broadcast&rdquo;</strong> system work? First, we start changing the web form to allow a user to choose between sending a channel-only message or a broadcast, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/home/index.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">     <span class="err">&lt;</span>%= f.text_field :name, placeholder: &#34;Name&#34; %&gt;
</span></span><span class="line"><span class="cl">     <span class="err">&lt;</span>%= f.text_field :message, placeholder: &#34;Message&#34; %&gt;
</span></span><span class="line"><span class="cl">+    <span class="err">&lt;</span>%= f.check_box :broadcast %&gt;
</span></span><span class="line"><span class="cl">     <span class="err">&lt;</span>%= f.submit &#34;Send message&#34;, class: &#34;pure-button pure-button-primary&#34; %&gt;
</span></span><span class="line"><span class="cl">   <span class="p">&lt;/</span><span class="nt">fieldset</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now the EventsController must accept this new parameter:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/controllers/events_controller.rb</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">event_params</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:pusher_event</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">:broadcast</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, the Model must use this new information before posting to the ExPusherLite server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/models/pusher_event.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PusherEvent</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Model</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">:broadcast</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">save</span>
</span></span><span class="line"><span class="cl">    <span class="n">topic</span> <span class="o">=</span> <span class="k">if</span> <span class="n">broadcast</span> <span class="o">==</span> <span class="s2">&#34;1&#34;</span>
</span></span><span class="line"><span class="cl">              <span class="s2">&#34;#general&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span>
</span></span><span class="line"><span class="cl">              <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_channel</span>
</span></span><span class="line"><span class="cl">            <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span><span class="p">(</span><span class="no">PusherLite</span><span class="o">.</span><span class="n">uri</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;topic&#34;</span> <span class="o">=&gt;</span> <span class="n">topic</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;event&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;scope&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;public&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;payload&#34;</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="nb">name</span><span class="p">,</span> <span class="s2">&#34;message&#34;</span> <span class="o">=&gt;</span> <span class="n">message</span><span class="p">}</span><span class="o">.</span><span class="n">to_json</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I am just assuming a hard-coded &ldquo;<tt>#general</tt>&rdquo; string to serve as the broadcast trigger for the server. Now we must make the server accept this new protocol schema, so let&rsquo;s go back to Elixir.</p>
<p>First we must start with the counterpart for the previous POST trigger, <tt>ExPusherLite.EventsController</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/events_controller.ex</span>
</span></span><span class="line"><span class="cl"> <span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">EventsController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">   <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="n">plug</span> <span class="ss">:authenticate</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="n">plug</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Authentication</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="n">topic</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;topic&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="n">event</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;event&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="sx">%{&#34;app_slug&#34; =&gt; app_slug, &#34;event&#34; =&gt; event, &#34;topic&#34; =&gt; topic, &#34;scope&#34; =&gt; scope}</span> <span class="o">=</span> <span class="n">params</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">     <span class="n">message</span> <span class="o">=</span> <span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="s2">&#34;payload&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;{}&#34;</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="no">Poison</span><span class="o">.</span><span class="n">decode!</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span><span class="o">.</span><span class="n">broadcast!</span> <span class="n">topic</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">topic_event</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="k">if</span> <span class="n">topic</span> <span class="o">==</span> <span class="s2">&#34;#general&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="n">event</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="k">else</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>        <span class="s2">&#34;</span><span class="si">#{</span><span class="n">topic</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">event</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span><span class="o">.</span><span class="n">broadcast!</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">scope</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">app_slug</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span> <span class="n">topic_event</span><span class="p">,</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">     <span class="n">json</span> <span class="n">conn</span><span class="p">,</span> <span class="sx">%{}</span>
</span></span><span class="line"><span class="cl">   <span class="k">end</span>
</span></span><span class="line"><span class="cl">   <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The first difference is that I am pattern matching from the arguments directly to the &ldquo;topic&rdquo; and &ldquo;event&rdquo; variables. This function is also aware of the &ldquo;<tt>#general</tt>&rdquo; string the client can send to indicate an app-wide broadcast. And the new topic is the concatenation of &ldquo;topic&rdquo; and &ldquo;event&rdquo; to allow for &ldquo;channel-only&rdquo; messages.</p>
<p>To connect this all to the WebSocket handler, we must make the following changes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/channels/room_channel.ex</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span> <span class="o">=</span> <span class="sx">%{ topic: &#34;public:&#34; &lt;&gt; _ }</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>    <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="n">topic_event</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span> <span class="o">=</span> <span class="sx">%{ topic: &#34;public:&#34; &lt;&gt; _ }</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>    <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="n">topic_event</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span> <span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">   <span class="k">end</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="o">-</span>  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="n">topic_event</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">     <span class="n">claims</span> <span class="o">=</span> <span class="no">Guardian</span><span class="o">.</span><span class="n">Channel</span><span class="o">.</span><span class="n">claims</span><span class="p">(</span><span class="n">socket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="k">if</span> <span class="n">permitted_topic?</span><span class="p">(</span><span class="n">claims</span><span class="o">[</span><span class="ss">:publish</span><span class="o">]</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">topic</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>      <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>      <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="n">topic_event</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl">       <span class="p">{</span> <span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">   <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, the Channel does not pattern match on a specific event, it lets it through without further validation, trusting that the EventsController is doing the right thing. I will come back to this piece for improvements in the future, possibly.</p>
<h3>Deploying our first Phoenix app to Heroku!<span class="hx:absolute hx:-mt-20" id="deploying-our-first-phoenix-app-to-heroku"></span>
    <a href="#deploying-our-first-phoenix-app-to-heroku" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In this section we will just follow the <a href="http://www.phoenixframework.org/docs/heroku"target="_blank" rel="noopener">official documentation</a>, so read it if you want more details.</p>
<p>Let&rsquo;s get started:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku apps:create your-expusherlite --buildpack &#34;https://github.com/HashNuke/heroku-buildpack-elixir.git&#34;
heroku buildpacks:add https://github.com/gjaldon/heroku-buildpack-phoenix-static.git</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I am naming the application &ldquo;your-expusherlite&rdquo; but you should change it to your own name, of course. And the rest of the configuration data are all examples that you must change for your own needs.</p>
<p>Heroku relies on environment variables. So we start by erasing &ldquo;<tt>config/prod.secret.exs</tt>&rdquo; and change &ldquo;<tt>config/prod.exs</tt>&rdquo; to look like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">http</span><span class="p">:</span> <span class="o">[</span><span class="ss">port</span><span class="p">:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="s2">&#34;PORT&#34;</span><span class="p">}</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">url</span><span class="p">:</span> <span class="o">[</span><span class="ss">scheme</span><span class="p">:</span> <span class="s2">&#34;https&#34;</span><span class="p">,</span> <span class="ss">host</span><span class="p">:</span> <span class="s2">&#34;your-expusherlite.herokuapp.com&#34;</span><span class="p">,</span> <span class="ss">port</span><span class="p">:</span> <span class="mi">443</span><span class="o">]</span><span class="p">,</span> <span class="ss">force_ssl</span><span class="p">:</span> <span class="o">[</span><span class="ss">rewrite_on</span><span class="p">:</span> <span class="o">[</span><span class="ss">:x_forwarded_proto</span><span class="o">]]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">cache_static_manifest</span><span class="p">:</span> <span class="s2">&#34;priv/static/manifest.json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">secret_key_base</span><span class="p">:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;SECRET_KEY_BASE&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Configure your database</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Repo</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">adapter</span><span class="p">:</span> <span class="no">Ecto</span><span class="o">.</span><span class="n">Adapters</span><span class="o">.</span><span class="n">Postgres</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">url</span><span class="p">:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;DATABASE_URL&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="ss">pool_size</span><span class="p">:</span> <span class="mi">20</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:admin_authentication</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">username</span><span class="p">:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;PUSHER_ADMIN_USERNAME&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="ss">password</span><span class="p">:</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;PUSHER_ADMIN_PASSWORD&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># remove this line:</span>
</span></span><span class="line"><span class="cl"><span class="c1"># import_config &#34;prod.secret.exs&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now we must configure the environment variables &ldquo;SECRET_KEY_BASE&rdquo;, &ldquo;PUSHER_ADMIN_USERNAME&rdquo; and &ldquo;PUSHER_ADMIN_PASSWORD&rdquo;. Use the included &ldquo;<tt>mix phoenix.gen.secret</tt>&rdquo; to generate those.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku config:set SECRET_KEY_BASE=&#34;`mix phoenix.gen.secret`&#34;
heroku config:set PUSHER_ADMIN_USERNAME=&#34;FPO0QUkqbAP6EGjElqBzDQuMs8bhFS3&#34;
heroku config:set PUSHER_ADMIN_PASSWORD=&#34;n78DPGmK3DBQy8YAVyshiGqcXjjSXSD&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Then it&rsquo;s just a matter of waiting for the good old &ldquo;<tt>git push heroku master</tt>&rdquo; to finish compiling everything for the first time. And because this is the first deploy you should not forget to run &ldquo;<tt>heroku run mix ecto.migrate</tt>&rdquo; to create the database table.</p>
<p>Now, if I did everything right, as an Administrator that knows the above hardcoded secrets I should be able to create a new Application like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>curl --data &#34;app[name]=shiny-new-app&#34; https://FPO0QUkqbAP6EGjElqBzDQuMs8bhFS3:n78DPGmK3DBQy8YAVyshiGqcXjjSXSD@your-expusherlite.herokuapp.com/api/admin/apps</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is the result I got!</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>{&#34;data&#34;:{&#34;slug&#34;:&#34;shiny-new-app&#34;,&#34;secret&#34;:&#34;42560373-0fe1-506e-28ca-35ab5221fb3d&#34;,&#34;name&#34;:&#34;shiny-new-app&#34;,&#34;key&#34;:&#34;958c16e7-ab93-dac0-0fc6-6cb864e26358&#34;,&#34;id&#34;:1,&#34;active&#34;:true}}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Great, now that we have a valid Application key and secret we can configure our Rails Client Demo and deploy it to Heroku as well.</p>
<h3>Deploying the Rails Client to Heroku<span class="hx:absolute hx:-mt-20" id="deploying-the-rails-client-to-heroku"></span>
    <a href="#deploying-the-rails-client-to-heroku" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This is a simple Rails application, we can just create the app and deploy right away:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku create your-expusherlite-client
heroku config:set PUSHER_URL=your-expusherlite.herokuapp.com
heroku config:set PUSHER_APP_ID=shiny-new-app
heroku config:set PUSHER_KEY=958c16e7-ab93-dac0-0fc6-6cb864e26358
heroku config:set PUSHER_SECRET=42560373-0fe1-506e-28ca-35ab5221fb3d
heroku config:set PUSHER_CHANNEL=shiny-new-topic
git push heroku master</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I&rsquo;m assuming the readers of this post already know how to configure a Rails app properly for Heroku. Just to mention it, I configure this app with the 12 factor and Puma gems and added a proper Procfile. Another very small change was changing the &ldquo;pusher_lite.rb&rdquo; initializer to create a URI with &ldquo;https&rdquo; because the ExPusherLite we deployed to production requires SSL by default.</p>
<p>There is one more caveat. Being led by experienced web programmers, they made sure that, unlike this bare-bones exercise here, the Phoenix framework itself is secure. One such example is to disallow Websocket connections from different hosts.</p>
<p>Out of the box, the &ldquo;phoenix.js&rdquo; Socket will fail connection when we try to connect from the &ldquo;your-expusherlite-client.herokuapp.com&rdquo; Rails app host to the Phoenix app in &ldquo;your-expusherlite.herokuapp.com&rdquo; with the following error:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>WebSocket connection to &#39;wss://your-expusherlite.herokuapp.com/socket/websocket?guardian_token=N_YCG6hGK7…iOlsicHVibGljOioiXX0._j6s2LiaKde9rBhnTMxDkm0XV5u89pNh1AdLFY6Rlt8&amp;vsn=1.0.0&#39; failed: Error during WebSocket handshake: Unexpected response code: 403</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And in the Phoenix log we will see this very helpful message:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[error] Could not check origin for Phoenix.Socket transport.

This happens when you are attempting a socket connection to
a different host than the one configured in your config/
files. For example, in development the host is configured
to &#34;localhost&#34; but you may be trying to access it from
&#34;127.0.0.1&#34;. To fix this issue, you may either:

  1. update [url: [host: ...]] to your actual host in the
     config file for your current environment (recommended)

  2. pass the :check_origin option when configuring your
     endpoint or when configuring the transport in your
     UserSocket module, explicitly outlining which origins
     are allowed:

        check_origin: [&#34;https://example.com&#34;,
                       &#34;//another.com:888&#34;, &#34;//other.com&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Unless you know what a <a href="https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html"target="_blank" rel="noopener">Cross-Site Web Socket Hijacking</a> is, you will prefer to keep the default settings as they are. In a green-field Phoenix app, the Web part will connect to the Web Socket in the same app and, therefore, in the same host, so this is not an issue.</p>
<p>In this case I am making a separate micro-service to mimic Pusher.com behavior so it should be able to accept Web Socket connections from different hosts.</p>
<p>If you control the applications being created, you will likely prefer to make the &ldquo;<tt>check_origin</tt>&rdquo; setting read from your database for the exact hosts. As a feature for next time I could add a &ldquo;host&rdquo; field in the &ldquo;App&rdquo; model and use it to validate connections in the transport configuration. For the time being I will just make it accept any hosts:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># web/channels/user_socket.ex
defmodule ExPusherLite.UserSocket do
  use Phoenix.Socket

  ## Channels
  channel &#34;*&#34;, ExPusherLite.RoomChannel

  ## Transports
  transport :websocket, Phoenix.Transports.WebSocket, check_origin: false
  ...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is it! Now the Rails app should be able to connect and send messages! And you should be able to create any number of new apps and connect all of them to this same service.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/534/big_Screenshot_from_2015-12-14_14_58_22.png" alt="Final Heroku Client"  loading="lazy" /></p>
<h3>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Right now, we have a functional, albeit bare-bones, Pusher.com clone that will work for any number of use cases where Pusher.com would be used.</p>
<p>As I warned many times before, the security part is still flaky and needs work. I will still extend on what Daniel began with Guardian to authenticate Web Socket users to private channels as well. And the core should also receive auditing and reporting capabilities (to be able to report usage, number of active connections, throughput of events, keep at least a short history of events so new connections can retrieve the last sent messages, and so on).</p>
<p>But from here it&rsquo;s a matter of adding features to an already working core. And this is nothing more than Phoenix out-of-the-box without too much added on top of it! It says a lot about the current state of maturity of this very capable framework.</p>
<p>In terms of performance, for this very simple example, I spun up 1 free Heroku dyno for each app.</p>
<p>The Rails app is able to respond to the front-end user interface in around 2ms. And the Sucker Punch job - which does the heavy HTTP POST to ExPusherLite - takes in the order of 30ms or less.</p>
<p>The Phoenix server receives the HTTP POST and performs the broadcast in around less than 6ms. Also quite fast. The times will vary a lot because I believe the free dyno is not only slow but also stays in highly shared metal boxes, getting impact from other neighbor apps running in the same box.</p>
<p>Because we already have an administrative API to create and manage apps (create new ones, delete, update, etc) we can already create a separate application in any other framework to make a dashboard for admins or for a self-serving front-end for developers to register new apps and receive key/secret pair to add in their own applications.</p>
<p>Both the ExPusherLite server and the demo client are deployed in Heroku and you can test the client right now <a href="https://expusherlite-client.herokuapp.com/"target="_blank" rel="noopener">clicking here</a>. The admin keys are different from what I showed in this post, of course, so you won&rsquo;t be able to create new apps, but you can deploy it yourself to your own environment.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>devops</category></item><item><title>Ex Pusher Lite - Part 1: Phoenix Channels and Rails apps</title><link>https://akitaonrails.github.io/en/2015/12/09/ex-pusher-lite-part-1-phoenix-channels-and-rails-apps/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/09/ex-pusher-lite-part-1-phoenix-channels-and-rails-apps/</guid><pubDate>Wed, 09 Dec 2015 18:39:00 -0200</pubDate><description>&lt;p&gt;Finally, after a lengthy exercising period (and plenty of blogging!) I will start implementing the Elixir app I wanted from the very beginning.&lt;/p&gt;
&lt;p&gt;As a Rails developer there are a few things we can&amp;rsquo;t do in Rails. One of them is to deal with real-time messaging.&lt;/p&gt;
&lt;p&gt;Rails 5 will bring &lt;a href="https://github.com/rails/actioncable"target="_blank" rel="noopener"&gt;Action Cable&lt;/a&gt;, and it might be good enough for most cases. It uses &lt;a href="https://github.com/faye/faye"target="_blank" rel="noopener"&gt;Faye&lt;/a&gt;, which in turn is based on Eventmachine. You can implement a good enough solution for Websockets using Faye in your Rails 4.2 app right now.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Finally, after a lengthy exercising period (and plenty of blogging!) I will start implementing the Elixir app I wanted from the very beginning.</p>
<p>As a Rails developer there are a few things we can&rsquo;t do in Rails. One of them is to deal with real-time messaging.</p>
<p>Rails 5 will bring <a href="https://github.com/rails/actioncable"target="_blank" rel="noopener">Action Cable</a>, and it might be good enough for most cases. It uses <a href="https://github.com/faye/faye"target="_blank" rel="noopener">Faye</a>, which in turn is based on Eventmachine. You can implement a good enough solution for Websockets using Faye in your Rails 4.2 app right now.</p>
<p>Another option is to avoid the trouble altogether and use a messaging service. One option I always recommend for zero friction is to use <a href="https://pusher.com/"target="_blank" rel="noopener">Pusher.com</a>.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/533/big_Screenshot_from_2015-12-09_18_41_22.png" alt="Chat Example"  loading="lazy" /></p>
<h3>The Basics<span class="hx:absolute hx:-mt-20" id="the-basics"></span>
    <a href="#the-basics" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>You will want to clone from my <a href="https://github.com/akitaonrails/pusher_lite_demo/tree/v0.1"target="_blank" rel="noopener">example repository</a>, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>git clone https://github.com/akitaonrails/pusher_lite_demo
cd pusher_lite_demo
git checkout tags/v0.1 -b v0.1
bundle</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is a very very simple implementation of a real-time, websocket-based, chat using Pusher. The idea goes like this:</p>
<p>We start by having a front-end Form to send messages</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/home/index.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>%= form_for @event, url: events_path, remote: true, html: {class: &#34;pure-form pure-form-stacked&#34;} do |f| %&gt;
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">fieldset</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">legend</span><span class="p">&gt;</span>Send your message remotely<span class="p">&lt;/</span><span class="nt">legend</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= f.text_field :name, placeholder: &#34;Name&#34; %&gt;
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= f.text_field :message, placeholder: &#34;Message&#34; %&gt;
</span></span><span class="line"><span class="cl">    <span class="err">&lt;</span>%= f.submit &#34;Send message&#34;, class: &#34;pure-button pure-button-primary&#34; %&gt;
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">fieldset</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span>% end %&gt;</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s using Rails built-in jQuery support for Ajax posting the form to the &ldquo;EventsController#create&rdquo; method:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/controllers/events_controller.rb</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">EventsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="no">SendEventsJob</span><span class="o">.</span><span class="n">perform_later</span><span class="p">(</span><span class="n">event_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">event_params</span>
</span></span><span class="line"><span class="cl">    <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:pusher_event</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Just to annotate the process, the &ldquo;routes.rb&rdquo; looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/routes.rb</span>
</span></span><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:events</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">root</span> <span class="s1">&#39;home#index&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The HTML layout looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">&lt;!--</span> <span class="n">app</span><span class="o">/</span><span class="n">views</span><span class="o">/</span><span class="n">layout</span><span class="o">/</span><span class="n">application</span><span class="o">.</span><span class="n">html</span><span class="o">.</span><span class="n">erb</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;!</span><span class="no">DOCTYPE</span> <span class="n">html</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">html</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">head</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="n">title</span><span class="o">&gt;</span><span class="no">Pusher</span> <span class="no">Lite</span> <span class="no">Demo</span><span class="o">&lt;</span><span class="sr">/title&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;meta name=&#34;pusher_key&#34; content=&#34;&lt;%= Rails.application.secrets.pusher_key %&gt;&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;meta name=&#34;pusher_channel&#34; content=&#34;&lt;%= Rails.application.secrets.pusher_channel %&gt;&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;%= stylesheet_link_tag    &#39;application&#39;, media: &#39;all&#39;, &#39;data-turbolinks-track&#39; =&gt; true %&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;%= javascript_include_tag &#39;application&#39;, &#39;data-turbolinks-track&#39; =&gt; true %&gt;
</span></span></span><span class="line"><span class="cl"><span class="sr">  &lt;script src=&#34;https://akitaonrails.github.io/</span><span class="o">/</span><span class="n">js</span><span class="o">.</span><span class="n">pusher</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="mi">3</span><span class="o">.</span><span class="mi">0</span><span class="o">/</span><span class="n">pusher</span><span class="o">.</span><span class="n">min</span><span class="o">.</span><span class="n">js</span><span class="s2">&#34;&gt;&lt;/script&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;%= csrf_meta_tags %&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/head&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;body&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;div class=&#34;</span><span class="n">pure</span><span class="o">-</span><span class="n">menu</span> <span class="n">pure</span><span class="o">-</span><span class="n">menu</span><span class="o">-</span><span class="n">horizontal</span><span class="s2">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Pusher Client Demo
</span></span></span><span class="line"><span class="cl"><span class="s2">...
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;div class=&#34;</span><span class="n">pure</span><span class="o">-</span><span class="n">g</span><span class="o">-</span><span class="n">r</span><span class="s2">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;div class=&#34;</span><span class="n">pure</span><span class="o">-</span><span class="n">u</span><span class="o">-</span><span class="mi">1</span><span class="o">-</span><span class="mi">3</span> <span class="n">message</span><span class="o">-</span><span class="n">form</span><span class="s2">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">    &lt;%= yield %&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;div class=&#34;</span><span class="n">pure</span><span class="o">-</span><span class="n">u</span><span class="o">-</span><span class="mi">1</span><span class="o">-</span><span class="mi">3</span> <span class="n">message</span><span class="o">-</span><span class="n">receiver</span><span class="s2">&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">  &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/body&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">&lt;/html&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This layout imports the default &ldquo;application.js&rdquo; which configures Pusher, establishes the Websocket connection and subscribes to messages on a specific topic with specific events:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// app/assets/javascript/application.js
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery_ujs
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require turbolinks
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;page:change&#34;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">pusherKey</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_key]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">pusher</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Pusher</span><span class="p">(</span><span class="nx">pusherKey</span><span class="p">,</span> <span class="p">{</span> <span class="nx">encrypted</span><span class="o">:</span> <span class="kc">true</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">pusherChannel</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_channel]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">channel</span> <span class="o">=</span> <span class="nx">pusher</span><span class="p">.</span><span class="nx">subscribe</span><span class="p">(</span><span class="nx">pusherChannel</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="nx">channel</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="s1">&#39;new_message&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">new_line</span> <span class="o">=</span> <span class="s2">&#34;&lt;p&gt;&lt;strong&gt;&#34;</span> <span class="o">+</span> <span class="nx">data</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s2">&#34;&lt;strong&gt;: &#34;</span> <span class="o">+</span> <span class="nx">data</span><span class="p">.</span><span class="nx">message</span> <span class="o">+</span> <span class="s2">&#34;&lt;/p&gt;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;.message-receiver&#34;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">new_line</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It gets the configuration metadata from the layout meta tags which grabs the values from &ldquo;config/secrets.yml&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">development</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">ded7c4a2a298c1b620e462b50c9ca6ccb60130e27968357e76cab73de9858f14556a26df885c8aa5004d0a7ca79c0438e618557275bdb28ba67a0ffb0c268056</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="l">test_chat_channel</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">f51ff494801ff0f9e1711036ef6f2f6f1e13544b02326adc5629c6833ae90f1a476747fae94b792eba8a444305df8e7a5ad53f05ea4234692ac96cc44f372029</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="l">test_chat_channel</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Do not keep production secrets in the repository,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># instead read values from the environment.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">production</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#34;SECRET_KEY_BASE&#34;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_CHANNEL&#39;] %&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And as I&rsquo;m using dotenv-rails, the &ldquo;.env&rdquo; looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>PUSHER_URL: &#34;https://14e86e5fee3335fa88b0:2b94ff0f07ce9769567f@api.pusherapp.com/apps/159621&#34;
PUSHER_KEY: &#34;14e86e5fee3335fa88b0&#34;
PUSHER_CHANNEL: &#34;test_chat_channel&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pusher is configured on the server-side through this initializer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/pusher.rb</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;pusher&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_url</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">logger</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">logger</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Finally, the &ldquo;EventsController#create&rdquo; actually does an async call to a <a href="https://github.com/brandonhilkert/sucker_punch"target="_blank" rel="noopener">SuckerPunch</a> job:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SendEventsJob</span> <span class="o">&lt;</span> <span class="no">ActiveJob</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">queue_as</span> <span class="ss">:default</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">perform</span><span class="p">(</span><span class="n">event_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@event</span> <span class="o">=</span> <span class="no">PusherEvent</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">event_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@event</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>By the way, as a segue, <a href="https://github.com/brandonhilkert/sucker_punch"target="_blank" rel="noopener">SuckerPunch</a> is a terrific solution for in-process asynchronous tasks. It&rsquo;s a better option to start with it without having to implement a separate system with Sidekiq workers.</p>
<p>Once you have larger job queues or jobs that are taking too long, then go to Sidekiq. If you use ActiveJob, the transition is as simple as changing the following configuration line in the &ldquo;config/application.rb&rdquo; file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">active_job</span><span class="o">.</span><span class="n">queue_adapter</span> <span class="o">=</span> <span class="ss">:sucker_punch</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This job just calls the &ldquo;save&rdquo; method in the fake-model &ldquo;PusherEvent&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PusherEvent</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Model</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">save</span>
</span></span><span class="line"><span class="cl">    <span class="no">Pusher</span><span class="o">.</span><span class="n">trigger</span><span class="p">(</span><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_channel</span><span class="p">,</span> <span class="s1">&#39;new_message&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nb">name</span><span class="p">:</span> <span class="nb">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">message</span><span class="p">:</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As it&rsquo;s very simple, the Gemfile is equally simple:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;pusher&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;dotenv-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;purecss-rails&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sucker_punch&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So what it does is very simple:</p>
<ol>
<li>The application loads Pusher and the necessary configuration.</li>
<li>When the user goes to &ldquo;http://localhost:3000&rdquo; he is presented with the message Form.</li>
<li>When the user posts the message, it ends in &ldquo;EventsController#create&rdquo;, which calls SuckerPunch&rsquo;s &ldquo;SendEventsJob&rdquo;, instantiates a new &ldquo;PusherEvent&rdquo; model with the received form params and finally triggers the message to the Pusher server.</li>
<li>The same form page also loads the Pusher javascript client and connects to the &ldquo;test_chat_channel&rdquo; topic and listens to the &ldquo;new_message&rdquo; event, which is the same thing the &ldquo;Pusher.trigger&rdquo; call sends together with the form message params.</li>
<li>The Pusher server broadcasts the received trigger message to all subscribed Websocket clients.</li>
<li>The Pusher javascript client in the user&rsquo;s browser receives the new message and just formats the message and appends to the &ldquo;message-receiver&rdquo; div HTML block in the same page.</li>
</ol>
<p>Pusher has support for authenticated users, private channels and more, but this is 80% of the usage for most cases. You can implement this as a chat system, a notification system, or anything like that.</p>
<p>Your Rails app sets up the front-end HTML/Javascript to connect to Pusher, listening to certain topics and events and the same Rails app triggers Pusher on the server-side, posting new messages. Pusher receives messages and broadcasts to the clients that subscribe to its topics. That&rsquo;s it.</p>
<h2>Ex Pusher Lite - Part 1: Initial Phoenix-based Pusher replacement<span class="hx:absolute hx:-mt-20" id="ex-pusher-lite---part-1-initial-phoenix-based-pusher-replacement"></span>
    <a href="#ex-pusher-lite---part-1-initial-phoenix-based-pusher-replacement" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>My original idea was to make a drop-in replacement for the Pusher server, using the same Pusher client, but for now it was not easy to do so.</p>
<p>Instead, this Part 1 will focus on implementing an initial ExPusherLite server that also receives events triggered by the same Rails server-side controller process and broadcasting to the same Rails front-end component through WebSockets.</p>
<p>I followed <a href="https://labs.opendoor.com/phoenix-on-rails-for-client-push-notifications"target="_blank" rel="noopener">Daniel Neighman</a>&rsquo;s tutorial. I had to do a few adjustments to have it working (and as this is still Part 1, it&rsquo;s not a complete solution yet!)</p>
<p>You can clone the initial version from <a href="https://github.com/akitaonrails/ex_pusher_lite"target="_blank" rel="noopener">my other Github repository</a> like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>git clone https://github.com/akitaonrails/ex_pusher_lite
cd ex_pusher_lite
mix deps.get</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The tutorial implemented initial setup for <a href="https://github.com/hassox/guardian"target="_blank" rel="noopener">Guardian</a> and <a href="https://github.com/bryanjos/joken"target="_blank" rel="noopener">Joken</a> for JSON Web Tokens. I am still getting used to how <a href="http://www.phoenixframework.org/docs/channels"target="_blank" rel="noopener">channels</a> are implemented in Phoenix.</p>
<p>It already comes pre-configured with a single socket handler that multiplexes connections. You start through the EndPoint OTP application:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># lib/ex_pusher_lite/endpoint.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Endpoint</span><span class="p">,</span> <span class="ss">otp_app</span><span class="p">:</span> <span class="ss">:ex_pusher_lite</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">socket</span> <span class="s2">&#34;/socket&#34;</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">UserSocket</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This application is started by the main supervisor in &ldquo;lib/ex_pusher_lite.ex&rdquo;. It points the endpoint &ldquo;/socket&rdquo; to the socket handler &ldquo;UserSocket&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/channels/user_socket.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">UserSocket</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Socket</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">## Channels</span>
</span></span><span class="line"><span class="cl">  <span class="n">channel</span> <span class="s2">&#34;*&#34;</span><span class="p">,</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">RoomChannel</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">## Transports</span>
</span></span><span class="line"><span class="cl">  <span class="n">transport</span> <span class="ss">:websocket</span><span class="p">,</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Transports</span><span class="o">.</span><span class="n">WebSocket</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># transport :longpoll, Phoenix.Transports.LongPoll</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The &ldquo;channel&rdquo; function comes commented out, so I started by uncommenting it. You can pattern match the topic name like &ldquo;public:*&rdquo; to different Channel handlers. For this simple initial test I am sending everything to the &ldquo;RoomChannel&rdquo;, which I had to create:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">RoomChannel</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Phoenix</span><span class="o">.</span><span class="n">Channel</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Guardian</span><span class="o">.</span><span class="n">Channel</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># no auth is needed for public topics</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">join</span><span class="p">(</span><span class="s2">&#34;public:&#34;</span> <span class="o">&lt;&gt;</span> <span class="n">_topic_id</span><span class="p">,</span> <span class="n">_auth_msg</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">socket</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">join</span><span class="p">(</span><span class="n">topic</span><span class="p">,</span> <span class="sx">%{ claims: claims, resource: _resource }</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">permitted_topic?</span><span class="p">(</span><span class="n">claims</span><span class="o">[</span><span class="ss">:listen</span><span class="o">]</span><span class="p">,</span> <span class="n">topic</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="sx">%{ message: &#34;Joined&#34; }</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="ss">:error</span><span class="p">,</span> <span class="ss">:authentication_required</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">join</span><span class="p">(</span><span class="n">_room</span><span class="p">,</span> <span class="n">_payload</span><span class="p">,</span> <span class="n">_socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:error</span><span class="p">,</span> <span class="ss">:authentication_required</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span> <span class="o">=</span> <span class="sx">%{ topic: &#34;public:&#34; &lt;&gt; _ }</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_in</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">socket</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">claims</span> <span class="o">=</span> <span class="no">Guardian</span><span class="o">.</span><span class="n">Channel</span><span class="o">.</span><span class="n">claims</span><span class="p">(</span><span class="n">socket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">permitted_topic?</span><span class="p">(</span><span class="n">claims</span><span class="o">[</span><span class="ss">:publish</span><span class="o">]</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">topic</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">broadcast</span> <span class="n">socket</span><span class="p">,</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="n">payload</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="ss">:noreply</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span> <span class="ss">:reply</span><span class="p">,</span> <span class="ss">:error</span><span class="p">,</span> <span class="n">socket</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">permitted_topic?</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="n">_</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">permitted_topic?</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">_</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">permitted_topic?</span><span class="p">(</span><span class="n">permitted_topics</span><span class="p">,</span> <span class="n">topic</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">matches</span> <span class="o">=</span> <span class="n">fn</span> <span class="n">permitted_topic</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">pattern</span> <span class="o">=</span> <span class="nb">String</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">permitted_topic</span><span class="p">,</span> <span class="s2">&#34;:*&#34;</span><span class="p">,</span> <span class="s2">&#34;:.*&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">Regex</span><span class="o">.</span><span class="n">match?</span><span class="p">(</span><span class="o">~</span><span class="n">r</span><span class="o">/</span><span class="p">\</span><span class="n">A</span><span class="c1">#{pattern}\z/, topic)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="no">Enum</span><span class="o">.</span><span class="n">any?</span><span class="p">(</span><span class="n">permitted_topics</span><span class="p">,</span> <span class="n">matches</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is all straight from Daniel&rsquo;s original tutorial, the important bit for this example is the first &ldquo;join&rdquo; function, the others deal with permissions and authentication that came through a JWT claim. I will deal with this in Part 2.</p>
<p>To make this work, I had to add the dependencies in &ldquo;mix.exs&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># mix.exs</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Mixfile</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Mix</span><span class="o">.</span><span class="n">Project</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">deps</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span><span class="p">{</span><span class="ss">:phoenix</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0.3&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:phoenix_ecto</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.1&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:postgrex</span><span class="p">,</span> <span class="s2">&#34;&gt;= 0.0.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:phoenix_html</span><span class="p">,</span> <span class="s2">&#34;~&gt; 2.1&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:phoenix_live_reload</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0&#34;</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="ss">:dev</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:cowboy</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:joken</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0.0&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">     <span class="p">{</span><span class="ss">:guardian</span><span class="p">,</span> <span class="s2">&#34;~&gt; 0.7.0&#34;</span><span class="p">}</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And add the configuration at &ldquo;config.exs&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/config.exs</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:joken</span><span class="p">,</span> <span class="ss">config_module</span><span class="p">:</span> <span class="no">Guardian</span><span class="o">.</span><span class="n">JWT</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="ss">:guardian</span><span class="p">,</span> <span class="no">Guardian</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">issuer</span><span class="p">:</span> <span class="s2">&#34;ExPusherLite&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">ttl</span><span class="p">:</span> <span class="p">{</span> <span class="mi">30</span><span class="p">,</span> <span class="ss">:days</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="ss">verify_issuer</span><span class="p">:</span> <span class="kp">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">serializer</span><span class="p">:</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">GuardianSerializer</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">atoms</span><span class="p">:</span> <span class="o">[</span><span class="ss">:listen</span><span class="p">,</span> <span class="ss">:publish</span><span class="p">,</span> <span class="ss">:crews</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:id</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now I have to add a normal HTTP POST endpoint, first adding it to the router:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/router.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Router</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:router</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">pipeline</span> <span class="ss">:browser</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:accepts</span><span class="p">,</span> <span class="o">[</span><span class="s2">&#34;html&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:fetch_session</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:fetch_flash</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#plug :protect_from_forgery</span>
</span></span><span class="line"><span class="cl">    <span class="n">plug</span> <span class="ss">:put_secure_browser_headers</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="n">scope</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span> <span class="no">ExPusherLite</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pipe_through</span> <span class="ss">:browser</span> <span class="c1"># Use the default browser stack</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="s2">&#34;/&#34;</span><span class="p">,</span> <span class="no">PageController</span><span class="p">,</span> <span class="ss">:index</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="s2">&#34;/events&#34;</span><span class="p">,</span> <span class="no">EventsController</span><span class="p">,</span> <span class="ss">:create</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice that I totally disabled CSRF token verification in the pipeline because I am not sending back Phoenix CSRF token from the Rails controller. Now, the &ldquo;EventsController&rdquo; is also almost all from Daniel&rsquo;s tutorial:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># web/controllers/events_controller.ex</span>
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">EventsController</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Web</span><span class="p">,</span> <span class="ss">:controller</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">plug</span> <span class="ss">:authenticate</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">topic</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;topic&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">event</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="s2">&#34;event&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="s2">&#34;payload&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;{}&#34;</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="no">Poison</span><span class="o">.</span><span class="n">decode!</span>
</span></span><span class="line"><span class="cl">    <span class="no">ExPusherLite</span><span class="o">.</span><span class="n">Endpoint</span><span class="o">.</span><span class="n">broadcast!</span> <span class="n">topic</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">    <span class="n">json</span> <span class="n">conn</span><span class="p">,</span> <span class="sx">%{}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">authenticate</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">secret</span> <span class="o">=</span> <span class="no">Application</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="ss">:ex_pusher_lite</span><span class="p">,</span> <span class="ss">:authentication</span><span class="p">)</span><span class="o">[</span><span class="ss">:secret</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Basic &#34;</span> <span class="o">&lt;&gt;</span> <span class="n">auth_token</span> <span class="o">=</span> <span class="n">hd</span><span class="p">(</span><span class="n">get_req_header</span><span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="s2">&#34;authorization&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="no">Plug</span><span class="o">.</span><span class="n">Crypto</span><span class="o">.</span><span class="n">secure_compare</span><span class="p">(</span><span class="n">auth_token</span><span class="p">,</span> <span class="no">Base</span><span class="o">.</span><span class="n">encode64</span><span class="p">(</span><span class="n">secret</span><span class="p">))</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">conn</span> <span class="o">|&gt;</span> <span class="n">send_resp</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span> <span class="o">|&gt;</span> <span class="n">halt</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I had to change the authenticate function a bit because either I didn&rsquo;t understand Daniel&rsquo;s implementation or it expected something different. But in this version I am just expecting a simple Basic HTTP Authentication &ldquo;authorization&rdquo; header which is a string with the format &ldquo;Basic [base64 username:password]&rdquo;. Look how I am pattern matching the string, removing the Base64 and <a href="http://codahale.com/a-lesson-in-timing-attacks/"target="_blank" rel="noopener">&ldquo;secure comparing&rdquo;</a> it (a constant-time binary compare to avoid timing attacks, this comes built-in with Phoenix).</p>
<p>This is a simple authentication technique for the Rails controller to POST the message trigger just the same as in the Pusher version.</p>
<p>And this is it, this is all it takes for this initial Phoenix-based Pusher replacement.</p>
<h2>Ex Pusher Lite - Part 2: Changing the Rails application<span class="hx:absolute hx:-mt-20" id="ex-pusher-lite---part-2-changing-the-rails-application"></span>
    <a href="#ex-pusher-lite---part-2-changing-the-rails-application" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now that we have a bare-bones Phoenix app that we can start through &ldquo;mix phoenix.server&rdquo; and make it available at &ldquo;localhost:4000&rdquo; we can start changing the Rails application.</p>
<p>As I said in the beginning, my original wish was to use the same Pusher javascript client but change the endpoint, turns out it&rsquo;s more difficult than I thought, so I will start by removing the following line from the application layout:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;//js.pusher.com/3.0/pusher.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We can get rid of the Pusher gem in the Gemfile and the &ldquo;pusher.rb&rdquo; initializer as well.</p>
<p>Now, a replacement for the &ldquo;pusher.min.js&rdquo; is Phoenix&rsquo;s own &ldquo;phoenix.js&rdquo; that comes bundled in &ldquo;deps/phoenix/web/static/js/phoenix.js&rdquo;. The problem is that it is an ES6 javascript source that Phoenix passes through Brunch to be transpiled back to ES5 in every Phoenix application.</p>
<p>But I am copying this file directly to the Rails repository at &ldquo;app/assets/javascripts/phoenix.es6&rdquo;. I could change it to ES5 but I decided to go the more difficult path and just add Babel support to Rails Asset Pipeline using <a href="http://nandovieira.com/using-es6-with-asset-pipeline-on-ruby-on-rails"target="_blank" rel="noopener">Nando&rsquo;s very helpful tutorial</a> on the subject.</p>
<p>The gist goes like this, first we add the dependencies in the Gemfile:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Use SCSS for stylesheets</span>
</span></span><span class="line"><span class="cl"><span class="c1">#gem &#39;sass-rails&#39;, &#39;~&gt; 5.0&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sass-rails&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;rails/sass-rails&#39;</span><span class="p">,</span> <span class="ss">branch</span><span class="p">:</span> <span class="s1">&#39;master&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sprockets-rails&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;rails/sprockets-rails&#39;</span><span class="p">,</span> <span class="ss">branch</span><span class="p">:</span> <span class="s1">&#39;master&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;sprockets&#39;</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s1">&#39;rails/sprockets&#39;</span><span class="p">,</span> <span class="ss">branch</span><span class="p">:</span> <span class="s1">&#39;master&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;babel-transpiler&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="n">source</span> <span class="s1">&#39;https://rails-assets.org&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rails-assets-almond&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Babel needs some configuration:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/babel.rb</span>
</span></span><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">assets</span><span class="o">.</span><span class="n">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">env</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="n">babel</span> <span class="o">=</span> <span class="no">Sprockets</span><span class="o">::</span><span class="no">BabelProcessor</span><span class="o">.</span><span class="n">new</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;modules&#39;</span>    <span class="o">=&gt;</span> <span class="s1">&#39;amd&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;moduleIds&#39;</span>  <span class="o">=&gt;</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">env</span><span class="o">.</span><span class="n">register_transformer</span> <span class="s1">&#39;application/ecmascript-6&#39;</span><span class="p">,</span> <span class="s1">&#39;application/javascript&#39;</span><span class="p">,</span> <span class="n">babel</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And for some reason I had to manually redeclare application.js and application.css in the assets initializer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># config/initializers/assets.rb</span>
</span></span><span class="line"><span class="cl"><span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">assets</span><span class="o">.</span><span class="n">precompile</span> <span class="o">+=</span> <span class="sx">%w( application.css application.js )</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We need Almond in order to be able to import the Socket module from the Phoenix javascript package. Now, we change the &ldquo;application.js&rdquo;:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">//= require almond
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require jquery_ujs
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require turbolinks
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require phoenix
</span></span></span><span class="line"><span class="cl"><span class="c1">//= require_tree .
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">require</span><span class="p">([</span><span class="s1">&#39;application/boot&#39;</span><span class="p">]);</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It requires an &ldquo;app/assets/javascripts/application/boot.es6&rdquo; file, this is straight from Nando&rsquo;s tutorial:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">$</span> <span class="nx">from</span> <span class="s1">&#39;jquery&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">runner</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// All scripts must live in app/assets/javascripts/application/pages/**/*.es6.
</span></span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;body&#39;</span><span class="p">).</span><span class="nx">data</span><span class="p">(</span><span class="s1">&#39;route&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Load script for this page.
</span></span></span><span class="line"><span class="cl">  <span class="c1">// We should use System.import, but it&#39;s not worth the trouble, so
</span></span></span><span class="line"><span class="cl">  <span class="c1">// let&#39;s use almond&#39;s require instead.
</span></span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">require</span><span class="p">([</span><span class="nx">path</span><span class="p">],</span> <span class="nx">onload</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">onload</span><span class="p">(</span><span class="nx">Page</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// Instantiate the page, passing &lt;body&gt; as the root element.
</span></span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">page</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Page</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Set up page and run scripts for it.
</span></span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nx">setup</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">page</span><span class="p">.</span><span class="nx">setup</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">page</span><span class="p">.</span><span class="nx">run</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Handles exception.
</span></span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/undefined missing/</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="s1">&#39;missing module:&#39;</span><span class="p">,</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">).</span><span class="nx">pop</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">$</span><span class="p">(</span><span class="nb">window</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">ready</span><span class="p">(</span><span class="nx">runner</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">&#39;page:load&#39;</span><span class="p">,</span> <span class="nx">runner</span><span class="p">);</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And it relies on attributes in the body tag, so we change our layout template:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="c">&lt;!-- app/views/layouts/application.html.erb --&gt;</span>
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">body</span> <span class="na">data-route</span><span class="o">=</span><span class="s">&#34;application/pages/&lt;%= controller.controller_name %&gt;/&lt;%= controller.action_name %&gt;&#34;</span><span class="p">&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I didn&rsquo;t mention before but I also have a &ldquo;HomeController&rdquo; just to be the root path for the main HTML page, it has a single &ldquo;index&rdquo; method and &ldquo;index.html.erb&rdquo; template with the message form. So I will have the need for an &ldquo;application/pages/home/index.es6&rdquo; inside the &ldquo;app/assets/javascripts&rdquo; path:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span><span class="nx">Socket</span><span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;phoenix&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kr">class</span> <span class="nx">Index</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">constructor</span><span class="p">(</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">root</span> <span class="o">=</span> <span class="nx">root</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">setup</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// add event listeners
</span></span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;-&gt; Setting up Pusher Lite socket&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">guardianToken</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=guardian-token]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">csrfToken</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=guardian-csrf]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">pusherKey</span>     <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_key]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">pusherChannel</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;meta[name=pusher_channel]&#34;</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">&#34;content&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">socket</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Socket</span><span class="p">(</span><span class="s2">&#34;ws://localhost:4000/socket&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">params</span><span class="o">:</span> <span class="p">{</span> <span class="nx">guardian_token</span><span class="o">:</span> <span class="nx">guardianToken</span><span class="p">,</span> <span class="nx">csrf_token</span><span class="o">:</span> <span class="nx">csrfToken</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="nx">socket</span><span class="p">.</span><span class="nx">connect</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Now that you are connected, you can join channels with a topic:
</span></span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nx">channel</span> <span class="o">=</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">channel</span><span class="p">(</span><span class="nx">pusherChannel</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl">    <span class="nx">channel</span><span class="p">.</span><span class="nx">join</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="s2">&#34;ok&#34;</span><span class="p">,</span> <span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Joined successfully&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">)</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="s2">&#34;error&#34;</span><span class="p">,</span> <span class="nx">resp</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;Unable to join&#34;</span><span class="p">,</span> <span class="nx">resp</span><span class="p">)</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">channel</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;msg&#34;</span><span class="p">,</span> <span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kd">let</span> <span class="nx">new_line</span> <span class="o">=</span> <span class="sb">`&lt;p&gt;&lt;strong&gt;</span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="sb">&lt;strong&gt;: </span><span class="si">${</span><span class="nx">data</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="sb">&lt;/p&gt;`</span>
</span></span><span class="line"><span class="cl">      <span class="nx">$</span><span class="p">(</span><span class="s2">&#34;.message-receiver&#34;</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">new_line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// trigger initial action (e.g. perform http requests)
</span></span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;-&gt; perform initial actions&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This bit is similar to the Pusher javascript handling, but we are getting a bit more information from the meta tags, the &ldquo;guardian-token&rdquo; and &ldquo;guardian-csrf&rdquo; tokens. Because I was following Daniel&rsquo;s tutorial I also changed the name of the event from &ldquo;new_message&rdquo; to just &ldquo;msg&rdquo; and the topics now need to have a &ldquo;public:&rdquo; prefix in order for the Phoenix&rsquo;s RoomChannel handler to match the public topic name correctly.</p>
<p>First things first. In order for this new javascript to have the correct tokens I had to add the following helper in the views layout:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>%= csrf_meta_tags %&gt;
</span></span><span class="line"><span class="cl">  <span class="err">&lt;</span>%= guardian_token_tags %&gt;
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this &ldquo;guardian_token_tags&rdquo; is again straight from Daniel&rsquo;s tutorial:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">GuardianHelper</span>
</span></span><span class="line"><span class="cl">  <span class="no">ISSUER</span> <span class="o">=</span> <span class="s2">&#34;pl-web-</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">DIGEST</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">Digest</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;sha256&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">guardian_token_tags</span>
</span></span><span class="line"><span class="cl">    <span class="n">token</span> <span class="o">=</span> <span class="no">Base64</span><span class="o">.</span><span class="n">urlsafe_encode64</span><span class="p">(</span><span class="no">SecureRandom</span><span class="o">.</span><span class="n">random_bytes</span><span class="p">(</span><span class="mi">32</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;&lt;meta content=</span><span class="se">\&#34;</span><span class="si">#{</span><span class="n">jwt</span><span class="p">(</span><span class="n">token</span><span class="p">)</span><span class="si">}</span><span class="se">\&#34;</span><span class="s2"> name=</span><span class="se">\&#34;</span><span class="s2">guardian-csrf</span><span class="se">\&#34;</span><span class="s2"> /&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;&lt;meta content=</span><span class="se">\&#34;</span><span class="si">#{</span><span class="n">token</span><span class="si">}</span><span class="se">\&#34;</span><span class="s2"> name=</span><span class="se">\&#34;</span><span class="s2">guardian-token</span><span class="se">\&#34;</span><span class="s2"> /&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="o">].</span><span class="n">shuffle</span><span class="o">.</span><span class="n">join</span><span class="o">.</span><span class="n">html_safe</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">JWT</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">jwt_claims</span><span class="p">(</span><span class="n">token</span><span class="p">),</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_key</span><span class="p">,</span> <span class="s1">&#39;HS256&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt_claims</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="ss">aud</span><span class="p">:</span> <span class="ss">:csrf</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nb">sub</span><span class="p">:</span> <span class="n">jwt_sub</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">iss</span><span class="p">:</span> <span class="no">ISSUER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">iat</span><span class="p">:</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span><span class="o">.</span><span class="n">utc</span><span class="o">.</span><span class="n">to_i</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">exp</span><span class="p">:</span> <span class="p">(</span><span class="no">Time</span><span class="o">.</span><span class="n">now</span> <span class="o">+</span> <span class="mi">30</span><span class="o">.</span><span class="n">days</span><span class="p">)</span><span class="o">.</span><span class="n">utc</span><span class="o">.</span><span class="n">to_i</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">s_csrf</span><span class="p">:</span> <span class="n">guardian_signed_token</span><span class="p">(</span><span class="n">token</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="ss">listen</span><span class="p">:</span> <span class="n">jwt_listens</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">publish</span><span class="p">:</span> <span class="n">jwt_publish</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt_sub</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{}</span> <span class="k">unless</span> <span class="n">current_human</span><span class="o">.</span><span class="n">present?</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nb">id</span><span class="p">:</span> <span class="n">current_human</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nb">name</span><span class="p">:</span> <span class="n">current_human</span><span class="o">.</span><span class="n">full_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">email</span><span class="p">:</span> <span class="n">current_human</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">crews</span><span class="p">:</span> <span class="n">current_human</span><span class="o">.</span><span class="n">crews</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="ss">:identifier</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt_listens</span>
</span></span><span class="line"><span class="cl">    <span class="n">listens</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;deploys:web&#39;</span><span class="p">,</span> <span class="s1">&#39;public:*&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">listens</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="s1">&#39;private:*&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="n">current_human</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:in_crew?</span><span class="p">,</span> <span class="ss">:admins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">listens</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">jwt_publish</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;public:*&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="s1">&#39;private:*&#39;</span><span class="p">)</span> <span class="k">if</span> <span class="n">current_human</span><span class="o">.</span><span class="n">try</span><span class="p">(</span><span class="ss">:in_crew?</span><span class="p">,</span> <span class="ss">:admins</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">publish</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">guardian_signed_token</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_key</span>
</span></span><span class="line"><span class="cl">    <span class="n">signed_token</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">HMAC</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="no">DIGEST</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Base64</span><span class="o">.</span><span class="n">urlsafe_encode64</span><span class="p">(</span><span class="n">signed_token</span><span class="p">)</span><span class="o">.</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/={1,}$/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I had to tweak it a bit, specially to get the proper keys from the &ldquo;secrets.yml&rdquo; file which now looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">development</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">ded7c4a2a298c1b620e462b50c9ca6ccb60130e27968357e76cab73de9858f14556a26df885c8aa5004d0a7ca79c0438e618557275bdb28ba67a0ffb0c268056</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">http://&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;:&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;@&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_secret</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;public:test_chat_channel&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">f51ff494801ff0f9e1711036ef6f2f6f1e13544b02326adc5629c6833ae90f1a476747fae94b792eba8a444305df8e7a5ad53f05ea4234692ac96cc44f372029</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">http://&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;:&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;@&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_secret</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;public:test_chat_channel&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Do not keep production secrets in the repository,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># instead read values from the environment.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">production</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secret_key_base</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#34;SECRET_KEY_BASE&#34;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_url</span><span class="p">:</span><span class="w"> </span><span class="l">http://&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;:&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;@&lt;%= ENV[&#39;PUSHER_URL&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_key</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_KEY&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_secret</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_SECRET&#39;] %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pusher_channel</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= ENV[&#39;PUSHER_CHANNEL&#39;] %&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>My local &ldquo;.env&rdquo; file looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>PUSHER_URL: &#34;localhost:4000&#34;
PUSHER_KEY: &#34;14e86e5fee3335fa88b0&#34;
PUSHER_SECRET: &#34;2b94ff0f07ce9769567f&#34;
PUSHER_CHANNEL: &#34;public:test_chat_channel&#34; </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This bit needs more work, I know. I just copied Pusher&rsquo;s key and Pusher&rsquo;s password as KEY and SECRET. This is the bit I mentioned I tweaked in the RoomChannel&rsquo;s authenticate function in the Phoenix side.</p>
<p>Now that I have this in place, I have to change the &ldquo;PusherEvent&rdquo; model to trigger the message from the form to the Phoenix&rsquo;s EventsController, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># app/models/event.rb</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;net/http&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;uri&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PusherEvent</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">Model</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:message</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">save</span>
</span></span><span class="line"><span class="cl">    <span class="n">uri</span> <span class="o">=</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_url</span><span class="si">}</span><span class="s2">/events&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;topic&#34;</span> <span class="o">=&gt;</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">secrets</span><span class="o">.</span><span class="n">pusher_channel</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;event&#34;</span> <span class="o">=&gt;</span> <span class="s2">&#34;msg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;payload&#34;</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="s2">&#34;name&#34;</span> <span class="o">=&gt;</span> <span class="nb">name</span><span class="p">,</span> <span class="s2">&#34;message&#34;</span> <span class="o">=&gt;</span> <span class="n">message</span><span class="p">}</span><span class="o">.</span><span class="n">to_json</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>As I am doing this through SuckerPunch, I am using plain old &ldquo;Net::HTTP.post()&rdquo; to post the message to the Phoenix &ldquo;/events&rdquo; endpoint. Phoenix will properly authenticate because the &ldquo;pusher_url&rdquo; is sending the &ldquo;PUSHER_KEY:PUSHER_SECRET&rdquo; as HTTP Basic Auth. It will end up in the &ldquo;authorization&rdquo; header and Phoenix will properly authenticate the server side, then it will broadcast to the WebSocket connections subscribed to the topic.</p>
<p>The new javascript will subscribe to the &ldquo;public:test_chat_channel&rdquo; topic and listen to the &ldquo;msg&rdquo; event. Once it receives the payload, it just formats the message and, again, appends to the same place in the &ldquo;message-receiver&rdquo; div tag.</p>
<h3>Conclusion: Further Work<span class="hx:absolute hx:-mt-20" id="conclusion-further-work"></span>
    <a href="#conclusion-further-work" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>So, with this we have exactly the same behavior as the Pusher version, but now it&rsquo;s under my control.</p>
<p>The idea is for the Phoenix app to have apps, a real authentication for different apps. Then every Rails app I do can just connect to this same Phoenix service.</p>
<p>The next steps include properly implementing the Guardian/JWT pieces, then I can jump to private channels support and add HTTP APIs to list channels in apps and users online in channels.</p>
<p>I will then create a second companion Rails app as an administration dashboard to consume those APIs and be able to create or revoke apps and basic maintenance and reporting. This should be a good enough replacement for a Pusher-like messaging solution that is really fast.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>rails</category><category>distributed-systems</category></item><item><title>The Obligatory "Why Elixir?" Personal Take</title><link>https://akitaonrails.github.io/en/2015/12/01/the-obligatory-why-elixir-personal-take/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/12/01/the-obligatory-why-elixir-personal-take/</guid><pubDate>Tue, 01 Dec 2015 13:26:00 -0200</pubDate><description>&lt;p&gt;So, I&amp;rsquo;ve been studying and exercising quite a bit with Elixir. José Valim recently announced the new features of the upcoming &lt;a href="https://twitter.com/josevalim/status/670595716776116224"target="_blank" rel="noopener"&gt;Elixir 1.2&lt;/a&gt;. The language design is already elegant, lean, and it keeps gradually polishing itself.&lt;/p&gt;
&lt;p&gt;First and foremost, I am a Web Applications Developer. I deal with Ruby on Rails applications and infrastructure. So, I am not a Desktop developer, a Mobile developer, Games developer or a Tools developer. This is very important to get out of the way right from the start.&lt;/p&gt;</description><content:encoded><![CDATA[<p>So, I&rsquo;ve been studying and exercising quite a bit with Elixir. José Valim recently announced the new features of the upcoming <a href="https://twitter.com/josevalim/status/670595716776116224"target="_blank" rel="noopener">Elixir 1.2</a>. The language design is already elegant, lean, and it keeps gradually polishing itself.</p>
<p>First and foremost, I am a Web Applications Developer. I deal with Ruby on Rails applications and infrastructure. So, I am not a Desktop developer, a Mobile developer, Games developer or a Tools developer. This is very important to get out of the way right from the start.</p>
<p>And by the way, as I will focus my programming efforts more and more into Elixir, it does not mean I am &ldquo;switching&rdquo; from Ruby to Elixir. I don&rsquo;t need to make exclusive choices, and in my mind, at least for a period of time, Rails joining forces with Phoenix will be a very difficult combo to beat for my web development strategies.</p>
<p>I know, Phoenix is built like Rails so why not just switch altogether: because most content-based sites don&rsquo;t need the concurrency aspects of Phoenix, and I said &ldquo;for a period of time&rdquo; because Rails still has a humongous ecosystem with more mature gems that make development easier and faster. This can change in the future, but for now the combo makes sense as I can build a normal website in Rails as I would normally do (with Devise, ActiveAdmin, Spree or whatever) and add Phoenix for stuff like WebSockets (real-time notifications, real-time chatting, background jobs that can be run more efficiently than Sidekiq, etc).</p>
<p>This article will summarize my personal take on 2 fronts:</p>
<ul>
<li><a href="#developer-roles">Other Developer Roles than just the Web</a></li>
<li><a href="#functional-concepts">&ldquo;Functional&rdquo; Concepts that Really Matter</a>
<ul>
<li><a href="#immutability">Immutability and Opaque Message Passing are VERY important</a></li>
<li><a href="#coroutines">Coroutines and Schedulers</a></li>
<li><a href="#static-dynamic">Static vs Dynamic Typing is still controversial</a></li>
<li><a href="#fault-tolerance">Fault Tolerance: Don&rsquo;t fear your code</a></li>
</ul>
</li>
<li><a href="#summary">Summary</a></li>
</ul>
<p>As anything that I want to argue about, this is <strong>lengthy</strong>, but it wouldn&rsquo;t be nice to just state something without further elaboration. In this quest for understanding, I may have confused a bit or two, so let me know in the comments section below if there are things to fix or to explain in more detail.</p>
<p>Let&rsquo;s get started.</p>
<p><a name="developer-roles"></a></p>
<h3>Other Developer Roles than just the Web<span class="hx:absolute hx:-mt-20" id="other-developer-roles-than-just-the-web"></span>
    <a href="#other-developer-roles-than-just-the-web" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In a Desktop environment, you will definitely want to make a combination of <a href="https://github.com/nwjs/nw.js/"target="_blank" rel="noopener">Node-Webkit</a> with native libraries. If you&rsquo;re in specific corporate environments, you won&rsquo;t have any choices other than plain WFC based .NET development or Java Swing. Your options have been set for quite some time, and even Visual Basic.NET still has its place. Specific toolchains will be dictated by Microsoft and Oracle/Java Community Process.</p>
<p>In Linux environments you will still use wrappers around GTK+, Qt or similar toolkits. There aren&rsquo;t a lot of ways around this.</p>
<p>If you&rsquo;re a Mobile-first developer, you <strong>do</strong> need to learn your way into Swift and Objective-C (to some extent) for iOS and the specific Java flavor for Android&rsquo;s Dalvik/ART. But I will argue that you have a lot to gain in native development if you use <a href="http://www.rubymotion.com/"target="_blank" rel="noopener"><strong>RubyMotion</strong></a>. Or, you can simply keep following Facebook&rsquo;s <a href="https://facebook.github.io/react-native/"target="_blank" rel="noopener">React Native</a> endeavor. There&rsquo;s a lot of fragmentation in this environment, you can do as little as possible with Web Mobile and Phonegap/Cordova, and build compelling apps with tools such as Ionic Framework. The only consensus is that if you really want to build the advanced next gen stuff, you want to dive deep into the native frameworks each platform has to offer.</p>
<p>If you&rsquo;re a Game Developer you want to be as close to the metal as possible. It&rsquo;s definitely feasible to write perfectly playable mobile games using any number of Javascript libraries together with HTML 5&rsquo;s technologies such as the Canvas and WebGL. But for the next gen blockbuster you will either use mature engines such as Unity or Unreal or even build your own if you&rsquo;re really invested in this field. This will require you to actually know your way into C/C++ programming. Really depends on how deep you want to go down the rabbit hole.</p>
<p>If you&rsquo;re a Tools Developer you will have the benefits from both old and new generation of languages. If you&rsquo;re closer to the Linux Kernel you will really need C/C++ in your baggage. But for the new generation of light containers (LXC), Docker, you can benefit from <strong>Go</strong>, an application development environment suited to make life easier than having to handle C/C++ idiosyncrasies. <strong>Rust</strong> is another great new choice to make it easier to - among other things - write memory-leak free small libraries and tools (I am mentioning that in particular because it&rsquo;s important for languages like Ruby or Python to be able to add performance by binding to C-based native libraries and Rust makes this task easier).</p>
<p>Different languages have different teams and different long term goals, which is why it&rsquo;s not apples and oranges to compare languages. Go, for instance, is heavier than Rust, but both are good for command-line tools, specialized daemons, and in the case of Go, networking-heavy and concurrency-heavy endeavors.</p>
<p>In my eyes, Go is a &ldquo;better&rdquo; Java or C++. And don&rsquo;t interpret this wrong: Java is still a very fine language and platform. There is hardly anything that comes close to the maturity of the JVM and the extensive ecosystem behind it. I would not think for a second of trying to rewrite complex systems written in Java such as the Lucene library or Elasticsearch/SOLR solutions, for example.</p>
<p>But the power of Standard Java is difficult to unleash without some warming up for the HotSpot to pick up steam. It makes it not a great solution for command-line tools. But now you have a good middle ground with Go. You also don&rsquo;t have a good time embedding Java into other platforms, and then you would need to go back to C, but now you have another good middle ground with Rust.</p>
<p>If you want to unleash different programming models, specially those more suited to concurrent abstractions such as the <a href="https://en.wikipedia.org/wiki/Actor_model_and_process_calculi_history"target="_blank" rel="noopener">Hoare&rsquo;s CSP</a> like Actor model, you can try <strong>Scala</strong> with <a href="http://akka.io/"target="_blank" rel="noopener">Akka</a> (which is now the standard actor library) and <strong>Clojure</strong>&rsquo;s <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">Pulsar/Quasar</a>. Akka and Quasar are the ones that come &ldquo;close&rdquo; (but can never match) Erlang&rsquo;s built-in OTP platform.</p>
<p>For the Web at large, you can do just fine with the current (ever changing, unstable) Node.js ecosystem, Python (Django, Plone), Ruby (Ruby on Rails, Sinatra), PHP (Zend, Laravel), even Perl has its place. Combined with mature services in different platforms (Elasticsearch in Java, PostgreSQL in C, RabbitMQ in Erlang), any big and complex Web Application can be written with any number of best-of-breed tools and combinations that best suit your needs.</p>
<p>This is an unfair, short overview, of course. I didn&rsquo;t cover every aspect of computer science or industry. There are several other active and useful languages such as Lua, Haskell, Fortran, Ada, Julia, R. The message being: you don&rsquo;t need to choose a single language, it will really depend on what you&rsquo;re going to deliver. And a true craftsman will master many tools to get the job done in the best way possible.</p>
<p><a name="functional-concepts"></a></p>
<h3>&ldquo;Functional&rdquo; Concepts that Really Matter<span class="hx:absolute hx:-mt-20" id="functional-concepts-that-really-matter"></span>
    <a href="#functional-concepts-that-really-matter" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><a href="http://www.akitaonrails.com/2015/10/28/personal-thoughts-on-the-current-functional-programming-bandwagon"target="_blank" rel="noopener">I wrote about my opinions</a> on the current surge in the so called Functional style of programming. I recommend you read it before continuing.</p>
<p>There are some aspects that are adamant if you really want to go beyond the academic research and into the real world of productivity.</p>
<p><a name="immutability"></a></p>
<h4>Immutability and Opaque Message Passing are VERY important<span class="hx:absolute hx:-mt-20" id="immutability-and-opaque-message-passing-are-very-important"></span>
    <a href="#immutability-and-opaque-message-passing-are-very-important" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>In order for computation to be fast, we are used to share data between routines. We move pointers around and change data in place.</p>
<p>It&rsquo;s not particularly fast to make things immutable and not shared. The more you make data mutable and the more you share, the harder it is to make your code run concurrently.</p>
<p>It is an important <strong>trade-off</strong>: if you see mutable data and shared state, you&rsquo;re optimizing for performance.</p>
<p>One can&rsquo;t say that a language is essentially good or bad for having mutable or immutable data. But for what it&rsquo;s worth, my personal opinion is that it&rsquo;s harder to convince users to follow conventions like <em>&ldquo;share a little as possible, mutate as little as possible.&rdquo;</em> Most will not even know about it if it&rsquo;s not built-in and enforced. I prefer having immutability enforced by default.</p>
<p>In Erlang, data is <strong>immutable</strong>. Similar to Java it <a href="http://erlang.org/pipermail/erlang-questions/2013-March/072760.html"target="_blank" rel="noopener">passes values by reference</a> in routine calls (it is not copying values between calls, as many misunderstand it).</p>
<p>And in the case of recursion it optimizes through <a href="http://learnyousomeerlang.com/recursion"target="_blank" rel="noopener">Tail Call Optimization</a> to make it faster. By the way, this is one optimization that the Java VM just can&rsquo;t quite do yet. Clojure needs special &lsquo;recur&rsquo; and &rsquo;trampoline&rsquo; calls, for example. Scala can rewrite the tail recursion to a loop in compile time, with the &lsquo;@tailrec&rsquo; annotation. <a href="http://ferd.ca/erlang-s-tail-recursion-is-not-a-silver-bullet.html"target="_blank" rel="noopener">Erlang has its own traps</a> as well, so not so black and white at the moment.</p>
<p>In Erlang, as I explained before, you run functions within completely isolated processes. If the function recurs or blocks, it stays isolated. Processes can only communicate by sending (immutable and opaque) messages to each other. Messages get queued in a &ldquo;run queue&rdquo; or &ldquo;mailbox&rdquo; and the function can choose to receive and respond to those messages. That&rsquo;s it.</p>
<p>So, you can pass values by reference between routines, or you can share data in a third party process as the mediator of data. One such infrastructure built-in to Erlang is the <strong>ETS</strong>, the <a href="http://elixir-lang.org/getting-started/mix-otp/ets.html"target="_blank" rel="noopener">Erlang Term Storage</a>, which is part of the so called OTP platform. Think of ETS as a very simple and very fast built-in key value storage like Memcached. You use it for the same use cases as a cache and it&rsquo;s as simple as just doing this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">table</span> <span class="o">=</span> <span class="ss">:ets</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:my_fancy_cache</span><span class="p">,</span> <span class="o">[</span><span class="ss">:set</span><span class="p">,</span> <span class="ss">:protected</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="ss">:ets</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="p">{</span><span class="s2">&#34;some_key&#34;</span><span class="p">,</span> <span class="n">some_value</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="ss">:ets</span><span class="o">.</span><span class="n">lookup</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="s2">&#34;some_key&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Many might argue that Erlang&rsquo;s <strong>rigid process isolation</strong> and communication strictly restricted to opaque message passing are overkill and that you can pass by using something akin to Clojure&rsquo;s <a href="http://clojure.org/refs"target="_blank" rel="noopener">MVCC Software Transaction Memory</a>, or STM. You do have STM in Erlang, with the other built-in OTP tool, built on top of the ETS, called <strong>Mnesia</strong>. It offers the equivalent of ACID database transactions in-memory. It&rsquo;s not a new concept, but STM is not available as a language feature and it&rsquo;s still uncertain if it really is a good choice to have it.</p>
<p>An inspired result, I believe, from Clojure&rsquo;s choice of having transactional memory with history queue and snapshot isolation is shown in its crown jewel, <a href="http://www.datomic.com/"target="_blank" rel="noopener">Datomic</a>. The idea is not revolutionary by any stretch of the imagination as you have many other <a href="http://www.xaprb.com/blog/2013/12/28/immutability-mvcc-and-garbage-collection/"target="_blank" rel="noopener">prior art</a> such as RethinkDB, CouchDB, and extensions for existing databases. Good for a service, still I don&rsquo;t think it&rsquo;s a good thing to share state, even if you have a transactor around that state. Erlang&rsquo;s immutability with rigid process isolation still has no match.</p>
<p><a name="coroutines"></a></p>
<h4>Coroutines and Schedulers<span class="hx:absolute hx:-mt-20" id="coroutines-and-schedulers"></span>
    <a href="#coroutines-and-schedulers" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>You already know sub-routines, you do it all the time by partitioning large portions of code into smaller functions or methods that call each other. You may already know about a specialized kind of Coroutines in the form of <a href="http://bit.ly/1lVuLFJ"target="_blank" rel="noopener">Fibers</a> (as first implemented in Windows circa 1997).</p>
<p>Fibers offer a way for your current function execution to &ldquo;yield&rdquo; back to its caller, preserving its current state, and then the caller can &ldquo;resume&rdquo; the suspended Fiber to continue its execution from where it last yielded. This allows for non-preempted, cooperative multitasking. We have Fibers in Python, Ruby, and other languages and it allows the creation of constructions like Generators. Even Javascript can have some form of Fibers if you add libraries like <a href="https://github.com/laverdet/node-fibers"target="_blank" rel="noopener">node-fibers</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">Fiber</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fibers&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">sleep</span><span class="p">(</span><span class="nx">ms</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">fiber</span> <span class="o">=</span> <span class="nx">Fiber</span><span class="p">.</span><span class="nx">current</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fiber</span><span class="p">.</span><span class="nx">run</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="nx">ms</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">Fiber</span><span class="p">.</span><span class="k">yield</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The call to &lsquo;yield&rsquo; suspends the current execution until the function in the &lsquo;setTimeout&rsquo; is called. Then it calls &lsquo;run&rsquo;, which resumes the previously yielded function. This is still &ldquo;rudimentary&rdquo; compared to coroutines: because the function itself has to yield control out to the reactor event loop, in the case of a Node.js application. If you don&rsquo;t, you will block the event loop in a single threaded Node.js process, and therefore you block everything until the function finishes, defeating the whole purpose. And this is one of those &ldquo;conventions&rdquo; that &ldquo;good&rdquo; programmers should follow, but most will forget.</p>
<p>Fibers are useful to make it less ugly to program in a rudimentary Reactor environment, where you depend on callbacks calling callbacks and you end up with the anti-pattern of <a href="https://bjouhier.wordpress.com/2012/03/11/fibers-and-threads-in-node-js-what-for/"target="_blank" rel="noopener">callback pyramid of doom</a>. With Fibers you can program as you would in a synchronous imperative language transforming this ugly Javascript code:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">archiveOrders</span><span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">db</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">conn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="s2">&#34;selectom orders where date &lt; ?&#34;</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl">               <span class="p">[</span><span class="nx">date</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">orders</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">helper</span><span class="p">.</span><span class="nx">each</span><span class="p">(</span><span class="nx">orders</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">order</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;insert into archivedOrders ...&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                     <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="p">...],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;delete from orders where id=?&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                       <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">],</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">return</span> <span class="nx">cb</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nx">next</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;orders been archived&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">cb</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Into this more manageable thing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">archiveOrders</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">date</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">conn</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">connect</span><span class="p">().</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="nx">conn</span><span class="p">.</span><span class="nx">query</span><span class="p">(</span><span class="s2">&#34;selectom orders where date &lt; ?&#34;</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl">             <span class="p">[</span><span class="nx">date</span><span class="p">]).</span><span class="nx">wait</span><span class="p">().</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">order</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;insert into archivedOrders ...&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                 <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="p">...]).</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">conn</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="s2">&#34;delete from orders where id=?&#34;</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                 <span class="p">[</span><span class="nx">order</span><span class="p">.</span><span class="nx">id</span><span class="p">]).</span><span class="nx">wait</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">&#34;orders been archived&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}).</span><span class="nx">future</span><span class="p">();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The entire <a href="https://en.wikipedia.org/wiki/Futures_and_promises"target="_blank" rel="noopener">Promises, Futures</a> debacle depends in part on proper Fibers. Javascript, being a very poor design, does not come with anything built-in and hence the proliferation of Fibers, Deferreds, Promises, Futures implementations that can never reach neither consensus nor people actually using them at large.</p>
<p>So, Fibers are ok. Coroutines are better because you have multiple points of suspending a function and more. And even better, in Erlang one doesn&rsquo;t need to even think about rudimentary Reactor loops (yes, reactors are a rudimentary construction for concurrency when you have no other good choice): it has <strong>transparent asynchronous calls</strong>. Everything in Erlang is asynchronous and non-blocking but you don&rsquo;t deal with callback pyramids because there is something better underneath: the <strong>Scheduler</strong>.</p>
<p>By the way, I find it very frustrating that <a href="http://stackoverflow.com/questions/18058164/is-golang-goroutine-a-coroutine"target="_blank" rel="noopener">Go&rsquo;s &ldquo;goroutines&rdquo; <strong>are not</strong> proper &ldquo;coroutines&rdquo;</a>.</p>
<p>For each Erlang process with SMP (symmetric multiprocessing) support there will be <strong>one</strong> real thread per CPU core available in your system, and for each thread there will be one single Scheduler to manage the internal green threads (processes) and run-queue.</p>
<p>As a programmer, I don&rsquo;t have to &ldquo;remember&rdquo; to yield control back to a passive event loop. The scheduler will take care of balancing computation time to each concurrent process. If a process is taking too long the Scheduler can choose to suspend it and give time to other routines. Erlang defines a <a href="http://erlang.org/pipermail/erlang-questions/2001-April/003132.html"target="_blank" rel="noopener">&ldquo;reduction&rdquo;</a> and that there are different priority levels of functions. If a function takes more than 2,000 reductions, the Scheduler can choose to suspend it. If you have 8 CPU cores, but the computation on the processes are not heavy, the VM can choose to just use 1 or 2 Schedulers and leave the other 6 idle so the hardware can turn the cores off to save energy (!!). Yep, Erlang is even Eco Friendly!</p>
<p>And we need to repeat this again: because each process is <strong>rigidly isolated</strong>, with immutable data and no shared state, it&rsquo;s easier to suspend a running process. In the case of the JVM this is usually implemented by raising checked exceptions and have everybody implement some Suspendable interface. It can be done using this <a href="http://www.matthiasmann.de/content/view/24/26/"target="_blank" rel="noopener">3rd party Java continuation library</a> where you yield by raising an Exception (!) Nasty stuff.</p>
<p>Rust is <a href="https://github.com/rustcc/coroutine-rs"target="_blank" rel="noopener">still implementing</a> something for coroutines as well, but still nothing as mature. But again, coroutines is just part of the story, you would need a heavier system with userland schedulers for it to make sense. Go is a better candidate to incorporate such a system in its runtime, but it also comes up short on being able to implement all this. There is <a href="http://www.jerf.org/iri/post/2930"target="_blank" rel="noopener">Suture</a>, an attempt to have some of OTP in Go, but it can&rsquo;t be done. Even Akka, the first mainstream OTP clone for Scala, can&rsquo;t come close because of the JVM shortcomings. <a href="http://blog.paralleluniverse.co/2013/05/02/quasar-pulsar/"target="_blank" rel="noopener">Clojure with Pulsar/Quasar</a>, comes <strong>closer</strong>, but is not there yet.</p>
<p>Now, the Erlang Scheduler is not only capable of suspending and resuming processes but it also takes care of message passing between them. So each Scheduler has its own run-queue to queue and dispatch messages. Again, because data is immutable, you only need some form of locking when you want another Scheduler (in another real thread) to take over a few processes in order to balance processing between cores. Erlang has supported SMP <a href="http://erlang.org/pipermail/erlang-questions/2008-September/038231.html"target="_blank" rel="noopener">since OTP R12B</a> (we are at R18 right now, and still evolving).</p>
<p>Most languages still rely on the OS real threads preemptive model to do multitasking. And this is heavy and slow because of all the context switching involved and all the locking logic most programmers will do wrong (the best practice for concurrency is: do not use threads, chances are you will screw up). Again, we make the right assumptions first: programmers can&rsquo;t do proper multithreading, so let a Scheduler do it, when necessary, and avoiding slow OS context switching as much as possible. Suspendable green threads combined with a userland Scheduler coordinating cooperative switching is a way faster and safer choice.</p>
<p>If you want to learn more about coroutines this <a href="http://www.inf.puc-rio.br/~roberto/docs/corosblp.pdf"target="_blank" rel="noopener">Lua Paper</a> on the subject explains in more details what I just elaborated:</p>
<blockquote>
Implementing a multitasking application with Lua coroutines is straightforward. Concurrent tasks can be modeled by Lua coroutines. When a new task is created, it is inserted in a list of live tasks. A simple task dispatcher can be implemented by a loop that continuously iterates on this list, resuming the live tasks and removing the ones that have finished their work (this condition can be signalled by a predefined value returned by the coroutine main function to the dispatcher). Occasional fairness problems, which are easy to identify, can be solved by adding suspension requests in time-consuming tasks.
</blockquote>
<p><a name="static-dynamic"></a></p>
<h4>Static vs Dynamic Typing is still controversial<span class="hx:absolute hx:-mt-20" id="static-vs-dynamic-typing-is-still-controversial"></span>
    <a href="#static-vs-dynamic-typing-is-still-controversial" class="subheading-anchor" aria-label="Permalink for this section"></a></h4><p>We saw a heavier movement from bureaucratic static typed systems (mainly Java prior to 6, C# prior to 4, C++) to purely dynamic languages such as Perl in the late 80&rsquo;s, Python in the late 90&rsquo;s, Ruby in mid 2000&rsquo;s. We tried to go from <em>&ldquo;making the compiler happy&rdquo;</em> to <em>&ldquo;making programmers happy&rdquo;</em>, which makes more sense if you ask me.</p>
<p>Scala, Groovy, Haskell, Swift brought a very practical middle ground with <a href="https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"target="_blank" rel="noopener">Hindley-Milner</a> derived <strong>Type Inference</strong> systems, in a way that we can code kinda like in dynamic languages but with the compiler doing more work to infer types for us before generating the final executable bytecode.</p>
<p>But there is a <strong>big</strong> catch: it&rsquo;s very difficult to <strong>hot swap</strong> code inside the runtime if you have static signatures. I&rsquo;m not saying that it is impossible, but a lot more difficult. You can reload code in Java (one example being <a href="https://github.com/spring-projects/spring-loaded"target="_blank" rel="noopener">Spring Loaded</a>) or Haskell (there is a hotswap plugin and other alternatives). You don&rsquo;t do granular reloads in a statically typed language because if you want to change the signature of a method, you have to change the graph that depends on that signature. It&rsquo;s doable, albeit, cumbersome.</p>
<p>In Erlang, because there is no such hard dependencies, and again because of inherent advantages of it only having immutable data with no shared state, and dependencies limited to opaque message passing, you can granularly reload one single module and most important of all: you can implement simple callbacks to <strong>transform the old state of a process into a new state structure</strong>, because just reloading the code is half the story if you will have hundreds of old processes restarting into the new code but having to deal with previous state. In an Erlang GenServer, you just implement this single callback:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">code_change</span><span class="p">(</span><span class="no">OldVersion</span><span class="p">,</span> <span class="no">CurrentState</span><span class="p">,</span> <span class="n">_Extra</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="no">NewState</span><span class="p">}</span><span class="o">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, while Type Inference is a nice middle ground, the flexibility of Dynamic Typing goes beyond being easy for programmers to use. As with Python, Ruby, Javascript, Perl and other dynamic typed languages, you will want to cover your code with proper test suites - which should not be optional in strong typed languages anyway. There is no doubt a compiler&rsquo;s static analysis helps a lot, but it&rsquo;s my personal take that dynamic typing allows me more flexibility.</p>
<p><a name="fault-tolerance"></a></p>
<h3>Fault Tolerance: Don&rsquo;t fear your code<span class="hx:absolute hx:-mt-20" id="fault-tolerance-dont-fear-your-code"></span>
    <a href="#fault-tolerance-dont-fear-your-code" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>With Dynamic Typing we will end up again in the endless discussion of <em>&ldquo;programmers never do it right, we need a compiler to enforce static rules&rdquo;</em>. And you are almost right: programmers screw up, but a compiler will not save your day anyway, and worst: it might give you a false sense of security. There is no worse security hole than false sense of security. A test suite is way better to assert proper implementation, and it&rsquo;s still no hard guarantee.</p>
<p>I said that this is controversial, as a more rigid static typing system is more productive than having to unit test every input and output types of every function. But if you thought that, you&rsquo;re at least wrong in that this is not what unit tests are for: it&rsquo;s for testing unit behaviors, in spite of what the types are. Testing types is what we call &ldquo;chicken typing&rdquo; and it&rsquo;s another form of defensive programming. You must test for behavior, not for stuff that a compiler would check. And again, having the static typing hinges my flexibility because now I have to constantly fight the typing, add more boilerplate, and ultimately if the behavior is wrong, the code is wrong, despite the type checks.</p>
<p>For a system to be <strong>&ldquo;tolerant to buggy code or uncaught failures&rdquo;</strong> is the opposite of littering your code with guarding statements like &ldquo;try/catch&rdquo; or &ldquo;if err == x&rdquo; or having some form static level checking with Result monads (that most people will just unwrap anyway). Guards can only go so far. And yes, this is anecdotal as there are no statistics that &ldquo;everybody will unwrap&rdquo; and most good programmers won&rsquo;t, but if experience tells me one thing, it&rsquo;s that bad programmers will fall for &ldquo;try/catch&rdquo; the whole thing when they don&rsquo;t know what to do anyway.</p>
<p>You need a system that allows any buggy code to fail and not crash the environment around it, which would bring it into an inconsistent, corrupt, state.</p>
<p>The problem with faulty code is that it leaves the state in a position where it has nowhere else to go. And if this state is shared, you leave every other bit of code in a position where they can&rsquo;t decide where to go next. You will need to shut everything down and restart from the last known good state. Almost all &ldquo;continuous delivery&rdquo; workflows are implemented around restarting everything.</p>
<p>Instead of having to bring everything down in case you forget to try/catch something, you can rely on Erlang&rsquo;s underpinnings of not sharing state, having immutable data, and - most importantly - having the rigidly isolated lightweight process system to make the process that is holding the faulty code to <strong>shut down and warn its Supervisor</strong>. This is the general idea behind the so called OTP in Erlang.</p>
<p>The idea of a Supervisor is to have a small process that monitors other processes. The Supervisor has as little code as possible in order to rarely (or never) fail (the best kind of code is &ldquo;no code&rdquo;). Once a faulty process crashes because of uncaught exception or other undeterministic reasons, it sends a notification message to the Supervisor run-queue mailbox, and then die cleanly. The Supervisor then chooses what to do based on its underlying restart strategy.</p>
<p>Let&rsquo;s say you have a list of URLs you&rsquo;re scraping. But you didn&rsquo;t anticipate dirty structures in your parsing logic. The process doing the scraping crashes and dies. The Supervisor is notified and chooses to restart the process, giving the new process the previous state - the URL list - and now that the faulty URL is not there anymore, the new process can happily continue the job with the next URL in the list.</p>
<p>This is a simple example of the dynamic between the Erlang VM, the Supervisor and its children workers. You can go further and have several Applications, which in turn start up new Supervisors, which in turn start up children processes. And you have a Supervisor Tree that can trap exits and restart granular bits of your runtime code without bringing the rest of the system into an inconsistent state.</p>
<p>Such is the beauty of the rigid process isolation concept.</p>
<p>Every I/O in the system is wrapped in <a href="http://www.erlang.org/doc/tutorial/c_port.html"target="_blank" rel="noopener">Ports</a>, which obey Async/callback logic transparently without you having to create pyramids of callbacks. The process consuming such ports just gets suspended by the Scheduler until the Async call returns and it can resume work. No callback pyramid hell. No need for rudimentary Fiber implementations to allow for rudimentary Promises/Futures systems. Just coroutines inside processes that can be suspended and resumed by the Scheduler. Fewer opportunities for programmer errors to stack up.</p>
<p>So, a programmer will forget to code every possible branch of execution and because he knows that, the <strong>very worst thing</strong> that can happen is not if he forgets an uncaught exception, but if he decides to program <strong>defensively</strong> and add general conditions to trap <strong>any</strong> error and never raise it. You&rsquo;ve seen it before, when you find code that is trapped inside generic try/catch blocks, trying to avoid every error possible. But what really happens is that the system may not crash but your logic and your processing is faulty at its core. And you won&rsquo;t find out about it, because it doesn&rsquo;t crash, therefore no one is ever notified! You will not end up with fewer bugs, you will end up with a mountain of logic bugs that are never noticed because they are all swallowed!!</p>
<p>This is the core of Fault Tolerance: do not fear Erlang, fear the programmers! Instead, we should do what Joe Armstrong presented in his seminal paper <a href="http://www.erlang.org/download/armstrong_thesis_2003.pdf"target="_blank" rel="noopener">&ldquo;Making reliable distributed systems in the presence of software errors&rdquo;</a>. This is both a detailed guide into Erlang and OTP but also his arguments on how to write fault tolerant systems by making the programmer write code as clearly as he originally intended, without the need to be defensive, with the confidence that if he misses something, OTP will be there to catch it and not let the system die, but instead to give a chance to fix it and reload it without disrupting other good parts of the system.</p>
<p>This is the ultimate goal of good programming: not being defensive, not putting try/catch everywhere because you&rsquo;re afraid.</p>
<p><a name="summary"></a></p>
<h3>Summary<span class="hx:absolute hx:-mt-20" id="summary"></span>
    <a href="#summary" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This short summary is the reason why Erlang sounds very compelling for my Web Development needs, or for any scalable complex system, at least for me.</p>
<p>Every other language had to make trade-offs. Every new language on top of the JVM has to deal with limitations inherent to how the JVM was originally architected. Rust still can build better abstractions, but its scope is for smaller tools and libraries, not distributed complex systems. Yes, eventually it can do whatever C/C++ can do, and Mozilla is actually basing the core of its next generation browser core on Rust. This will bootstrap better higher level libraries and frameworks, the same way Apple using Objective-C created the entire set of Core frameworks that make implementing complex applications much easier.</p>
<p>Go made a choice for keeping familiarity with its C++ inheritance. Of course, it has tons of useful features, in particular the built-in goroutines that make concurrent coding much easier than in previous languages.</p>
<p>Haskell is too strict for most programmers (yes, Monads, still difficult for the average programmer to fully grasp) and despite contrary opinions, to me it still feels like it appeals more to researchers than everyday developers. Other dynamic languages such as Ruby, Python, may still go the way of Erlang with Ruby adding some immutability (<a href="https://bugs.ruby-lang.org/issues/11473"target="_blank" rel="noopener">in 2.3</a> with immutable String by default), but it&rsquo;s still a long way to go.</p>
<p>Erlang has everything, as Joe Armstrong envisioned a fault tolerant system to be. It started as an exercise implemented in Prolog in 1986. It migrated from the previous JAM compiler to the current BEAM VM in 1998. It added SMP around 2008. It has been gradually evolving, polishing its rough edges, being really battle tested in really mission critical systems for decades. It&rsquo;s ready for us, right now.</p>
<p>It has Fault Tolerance guaranteed by the principles of immutable data, no shared state, pure opaque message passing, and suspendable processes, all managed by Schedulers. This guarantees that faulty routines can crash one single process but not the entire system and definitely not bringing state of other process to inconsistent, corrupted, states.</p>
<p>Then you can instrument your virtual machine and check that Supervisors are restarting children more than you want and decide to fix the buggy code. And once you do, you can choose <strong>not</strong> to shut down and restart the entire system to reload the code fixes, you can do it granularly, on the fly, with running processes that will pick up the fixes once the Supervisor restarts it. The granular hot swap of code is guaranteed because there is no hierarchy of types to care about.</p>
<p>And because you have proper coroutines with no hierarchy of shared state dependencies, you can have <strong>asynchronous exceptions</strong> that can forcefully shut down processes without creating side effects to other running processes. A Supervisor may choose to restart its entire children list when one of its children crashes and another of its children depended on that previous process. You can switch from &ldquo;one for one&rdquo; restart strategy to &ldquo;one for all&rdquo; (like the Musketeers).</p>
<p>There is only one problem with Erlang: it was not designed for &ldquo;programmer happiness&rdquo;, a concept we&rsquo;ve come to take for granted because of Ruby and newer languages.</p>
<p>Erlang has its roots in Prolog and it shows. Once you step up and really dive deep exercising with the language you can possibly get used to it. But if you came from more modern dynamic languages such as Ruby, Python, Groovy, you will definitely miss the comfortable modern constructions.</p>
<p>Elixir is the missing piece, the Philosopher&rsquo;s Stone if you will, that can unlock all the 30 years of refinements, maturity, industry battle tested technologies in large scales, to the average programmer.</p>
<p>It brings many modern constructs such as making macros easier in order to allow for Domain Specific Languages, having testable comments in your code, adding a more modern standard library that is easily recognizable from a Ruby or Clojure point of view, polymorphism through Protocols, and so on.</p>
<p>This is one example of Elixir straight from its source code tests:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Code</span><span class="o">.</span><span class="n">require_file</span> <span class="s2">&#34;../test_helper.exs&#34;</span><span class="p">,</span> <span class="n">__DIR__</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">Mix</span><span class="o">.</span><span class="n">ArchiveTest</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">MixTest</span><span class="o">.</span><span class="n">Case</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">doctest</span> <span class="no">Mix</span><span class="o">.</span><span class="n">Archive</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;archive&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">in_fixture</span> <span class="s2">&#34;archive&#34;</span><span class="p">,</span> <span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">File</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;.elixir&#34;</span><span class="p">,</span> <span class="s2">&#34;~&gt; 1.0.0&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">Mix</span><span class="o">.</span><span class="n">Archive</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s2">&#34;.&#34;</span><span class="p">,</span> <span class="s2">&#34;sample.ez&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">archive</span> <span class="o">=</span> <span class="s1">&#39;sample.ez&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/.elixir&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/priv/not_really_an.so&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/ebin/Elixir.Mix.Tasks.Local.Sample.beam&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="s1">&#39;sample/ebin/local_sample.app&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">has_zip_file?</span><span class="p">(</span><span class="n">archive</span><span class="p">,</span> <span class="nb">name</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">files</span><span class="p">}</span> <span class="o">=</span> <span class="ss">:zip</span><span class="o">.</span><span class="n">list_dir</span><span class="p">(</span><span class="n">archive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">Enum</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">files</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">match?</span><span class="p">({</span><span class="ss">:zip_file</span><span class="p">,</span> <span class="o">^</span><span class="nb">name</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">},</span> <span class="o">&amp;</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Code that can be written like this automatically makes me grin.</p>
<p>This is the perfect combination. Again, it is not without shortcomings. Erlang is by no means a fast language. It is way faster than Ruby, Python, or other interpreted languages. It can be made a bit faster with native compilation through the HIPE compiler, but still nowhere near the speeds of Go, Rust, or any Java derivative such as Scala, Groovy or Clojure.</p>
<p>So, if you really need raw power of computation, you will want Go or Java. Again, I&rsquo;m not saying there are only those 2, but it&rsquo;s the usual choices if you don&rsquo;t want to go down to C/C++. Haskell has terrific performance, but its learning curve is less than stellar.</p>
<p>Erlang is a whole system, it has its own scheduling system, it controls living, breathing processes that each have its own garbage collector, it controls system signal trapping and so on. It was designed to be a full server. It is much smaller than a full blown Java Enterprise Edition container, so small that you can actually package command line tools that do start up fast enough. But this is not the sweet spot. For that purpose you will be better off with Go or even Rust.</p>
<p>For the same reasons it was not made to be an embeddable language the way Lua is. It was not made to create libraries that can be easily linked to via FFI or C-like function exports, the way Rust can.</p>
<p>There are ways to create desktop-class applications, specially with cross-platform <a href="http://www.erlang.org/doc/apps/wx/chapter.html"target="_blank" rel="noopener">wxWidgets</a>, the way the built-in Observer instrumentation application is done, but Erlang was not built to be a desktop toolkit.</p>
<p>Also because it prioritizes correctness, rigidly isolated processes communicating just by opaque messages, immutable and non-shared states, it means that Erlang is not suited to hard data science processing. So I doubt it&rsquo;s the best choice for Big Data analytics, DNA sequencing and other hard stuff that tools like Julia, R, Fortran, are better choices. It&rsquo;s not the same thing to say that it can&rsquo;t be a good database core, Riak and CouchDB proved that already. But complex queries on top of high volumes of data is not the sweet spot as well.</p>
<p>So, Erlang is good for distributed systems, with high concurrency of opaque message exchange and proxying. The exact scenario where the Web is. Web Applications heavy load of throughput that need real time chats and notifications, heavy and time consuming payment transactions, gathering of data from many sources in order to reduce them to consumable HTML or JSON responses.</p>
<p>But for the average Web developer (and by &ldquo;average&rdquo; I mean minimally able to architect the kinds of complex systems we deal with everyday in web development, not simple static website construction), Erlang used to be a real challenge, and now we can have the comfort of a real modern language with hints of Ruby and Clojure, without the complexities of strong typing but with the security of its built-in Fault Tolerance constructions in order to deliver highly reliable, highly scalable, modern Web applications.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>"Yocto Services"! And My First Month with Elixir!</title><link>https://akitaonrails.github.io/en/2015/11/25/yocto-services-and-my-first-month-with-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/25/yocto-services-and-my-first-month-with-elixir/</guid><pubDate>Wed, 25 Nov 2015 18:04:00 -0200</pubDate><description>&lt;p&gt;Wow, for the past month (almost, from Oct 27 to Nov 25) I decided that it was past time to dive deep and actually learn Elixir. I did just that, I am still a beginner but I feel very confident that I can tackle Elixir based projects now.&lt;/p&gt;
&lt;p&gt;For my learning process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I read the entire &lt;a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener"&gt;official Elixir Documentation&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;I read the entire Dave Thomas&amp;rsquo; &lt;a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener"&gt;Programming Elixir&lt;/a&gt; book;&lt;/li&gt;
&lt;li&gt;I watched almost all &lt;a href="http://elixirsips.com/"target="_blank" rel="noopener"&gt;Elixir Sips&lt;/a&gt; screencasts;&lt;/li&gt;
&lt;li&gt;I read the entire Benjamin Tan Wei Hao&amp;rsquo;s &lt;a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener"&gt;The Little Elixir &amp;amp; OTP Guidebook&lt;/a&gt; book;&lt;/li&gt;
&lt;li&gt;I did a number of tutorials and exercises.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And finally, I documented everything I learned almost every day in the following articles:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Wow, for the past month (almost, from Oct 27 to Nov 25) I decided that it was past time to dive deep and actually learn Elixir. I did just that, I am still a beginner but I feel very confident that I can tackle Elixir based projects now.</p>
<p>For my learning process:</p>
<ol>
<li>I read the entire <a href="http://elixir-lang.org/getting-started/introduction.html"target="_blank" rel="noopener">official Elixir Documentation</a>;</li>
<li>I read the entire Dave Thomas&rsquo; <a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener">Programming Elixir</a> book;</li>
<li>I watched almost all <a href="http://elixirsips.com/"target="_blank" rel="noopener">Elixir Sips</a> screencasts;</li>
<li>I read the entire Benjamin Tan Wei Hao&rsquo;s <a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener">The Little Elixir &amp; OTP Guidebook</a> book;</li>
<li>I did a number of tutorials and exercises.</li>
</ol>
<p>And finally, I documented everything I learned almost every day in the following articles:</p>
<ul>
<li><a href="http://www.akitaonrails.com/2015/10/27/how-fast-is-elixir-phoenix"target="_blank" rel="noopener">How Fast is Elixir/Phoenix?</a></li>
<li><a href="http://www.akitaonrails.com/2015/10/28/personal-thoughts-on-the-current-functional-programming-bandwagon"target="_blank" rel="noopener">Personal Thoughts on the Current Functional Programming Bandwagon</a></li>
<li><a href="http://www.akitaonrails.com/2015/10/29/phoenix-experiment-holding-2-million-websocket-clients"target="_blank" rel="noopener">Phoenix Experiment: Holding 2 Million Websocket clients!</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/03/minha-primeira-semana-aprendendo-elixir"target="_blank" rel="noopener">My first week learning Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/18/ex-manga-downloadr-um-exercicio-com-elixir"target="_blank" rel="noopener">Ex Manga Downloader, an exercise with Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Ex Manga Downloadr - Part 2: Poolboy to the rescue!</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/20/phoenix-15-minute-blog-comparacao-com-ruby-on-rails"target="_blank" rel="noopener">Phoenix &ldquo;15 Minute Blog&rdquo; comparison to Ruby on Rails</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/22/observando-processos-em-elixir-the-little-elixir-otp-guidebook"target="_blank" rel="noopener">Observing Processes in Elixir - The Little Elixir &amp; OTP Guidebook</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">ExMessenger Exercise: Understanding Nodes in Elixir</a></li>
<li><a href="http://www.akitaonrails.com/en/2015/11/25/elixir-101-apresentando-a-sintaxe"target="_blank" rel="noopener">Elixir 101 - Introducing the Syntax</a></li>
</ul>
<p>Yes, I am prolific, and highly focused. I did the learning and writing all within less than a month (it would&rsquo;ve been less if I could have used night time and weekends). So I&rsquo;d say that the average developer would take at least 3 to 4 months to cover the same material.</p>
<h3>Initial Thinking: Yocto Services!<span class="hx:absolute hx:-mt-20" id="initial-thinking-yocto-services"></span>
    <a href="#initial-thinking-yocto-services" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>For good or for worse, we are in the dawn of the Micro Services architecture. In summary it&rsquo;s fragmenting your monolith into smaller applications that respond to HTTP based endpoints - that people call &ldquo;APIs&rdquo; - and that are responsible for a very narrow set of responsibilities. Then you create a &ldquo;front-end&rdquo; web application that will consume those services, such as analytics, payment methods, user authentication directories, and so on.</p>
<p>This is no novelty, of course, having HTTP APIs that return JSON structures is just a fancier way of doing the same, good, old <strong>Remote Procedure Calls</strong>, or RPCs, a technology we have had for decades to interconnect clients and servers in a network. But I digress.</p>
<p>If this is what people call &ldquo;Micro&rdquo; services, I think of Elixir processes as <strong>&ldquo;Yocto&rdquo;</strong> Services! (Milli &gt; Micro &gt; Nano &gt; Pico &gt; Femto &gt; Atto &gt; Zepto &gt; Yocto, by the way - I may have just invented a new term here!)</p>
<p>I described a bit of the Processes infrastructure, how to spawn them, how to exchange messages between them, and how to link them together. So, go read <a href="http://www.akitaonrails.com/en/2015/11/22/observando-processos-em-elixir-the-little-elixir-otp-guidebook"target="_blank" rel="noopener">my previous post</a> if you haven&rsquo;t already.</p>
<p>Inside an Elixir app you will find many processes, some from the VM running the show and some from your own app. If you architected it correctly, you&rsquo;ve implemented an OTP application, with proper groups of Supervisors and Children, all organized in a Supervision Tree. A small worker dies, its supervisor knows how to deal with it.</p>
<p>Now, here&rsquo;s the mental model: think of an Elixir process as a tiny micro-service - a <strong>Yocto service</strong>, if you will! - inside your application. In a Phoenix application, for example, you don&rsquo;t import a &ldquo;database library&rdquo; you actually spin up a &ldquo;Database Service&rdquo; (which is an Ecto Repo) that runs in parallel with the Endpoint application that responds to HTTP requests coming from the internet. The code in your controllers and models &ldquo;consume&rdquo; and send messages to the Ecto Repo &ldquo;Service&rdquo;. This is how you can visualize what&rsquo;s going on.</p>
<p>I have shown the Observer in the previous article as well. You will find a large tree if you open it up inside the IEx shell of a Phoenix application:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/531/big_phoenix-observer.png" alt="Observer Phoenix"  loading="lazy" /></p>
<p>In summary you will find the following section in the Pxblog Phoenix App Supervision Tree:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Pxblog.Supervisor
- Pxblog.Endpoint
    &#43; Pxblog.Endpoint.Server
    &#43; Pxblog.PubSub.Supervisor
        * Pxblog.PubSub
        * Pxblog.PubSub.Local
- Pxblog.Repo
    &#43; Pxblog.Repo.Pool</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is the Pxblog I explained in the <a href="http://www.akitaonrails.com/en/2015/11/20/phoenix-15-minute-blog-comparacao-com-ruby-on-rails"target="_blank" rel="noopener">Phoenix and Rails comparison article</a> I published a few days ago.</p>
<p>I still haven&rsquo;t read the source code of Phoenix, but if I am interpreting the Observer correctly, the Endpoint.Server controls a pool of processes that are TCP listeners that the application is ready to accept requests from, concurrently, with overflowing to accept more connections (I believe it&rsquo;s a pool implementation like Poolboy, which I explained in <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Part 2 of the Ex Manga Downloader article</a>).</p>
<p>Then, you have the PubSub.Supervisor and PubSub.Local applications that I believe support the WebSocket channels.</p>
<p>The Repo alone controls 10 initial processes in its pool, possibly a database connection pool. Notice how Endpoint and Repo groups are in parallel branches in the supervision tree. If Repo fails for some external database problems, the Endpoint group does not have to fail. This is what&rsquo;s declared in the Pxblog Application definition at <tt>lib/pxblog.ex</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>defmodule Pxblog do
  use Application

  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      # Start the endpoint when the application starts
      supervisor(Pxblog.Endpoint, []),
      # Start the Ecto repository
      worker(Pxblog.Repo, []),
      # Here you could define other workers and supervisors as children
      # worker(Pxblog.Worker, [arg1, arg2, arg3]),
    ]

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Pxblog.Supervisor]
    Supervisor.start_link(children, opts)
  end
  ...
end</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>See how it defines Endpoint and Repo under the Pxblog.Supervisor.</p>
<p>I can go on and forcefully kill the entire Pxblog.Repo node from the Supervision Tree using the Observer, like I did in the previous article, and the proper strategy kicks in, the Phoenix Supervisor successfully restarts the Repo, and no one will ever notice something crashed underneath.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/532/big_phoenix_kill_repo.png" alt="Kill Repo"  loading="lazy" /></p>
<p>From the IEx I can still make more calls to the Repo like this and it responds as if it had never crashed:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(4)&gt; Pxblog.Repo.all(Pxblog.User)
[debug] SELECT u0.&#34;id&#34;, u0.&#34;username&#34;, u0.&#34;email&#34;, u0.&#34;password_digest&#34;, u0.&#34;inserted_at&#34;, u0.&#34;updated_at&#34; FROM &#34;users&#34; AS u0 [] OK query=78.2ms queue=3.2ms
[%Pxblog.User{__meta__: #Ecto.Schema.Metadata&lt;:loaded&gt;,
  email: &#34;akitaonrails@me.com&#34;, id: 1,
  inserted_at: #Ecto.DateTime&lt;2015-11-20T14:01:09Z&gt;, password: nil,
  password_confirmation: nil,
  password_digest: &#34;...&#34;,
  posts: #Ecto.Association.NotLoaded&lt;association :posts is not loaded&gt;,
  updated_at: #Ecto.DateTime&lt;2015-11-20T14:01:09Z&gt;, username: &#34;akitaonrails&#34;}]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And the way I think about this is: my IEx shell is sending a message to the Yocto Service called Pxblog.Repo (in reality it&rsquo;s forwarding messages to the database adapter that then checks out a process from the pool). Just like I would consume external Micro Services through HTTP APIs.</p>
<p>So the landscape of your application is comprised of a series of processes and groups of supervised processes all working to compose a larger structure. As I said in previous articles, should one group of process collapse, its Supervisor kicks in, traps the error and uses its strategy to, for example, restart all of its children processes, bringing the app to a consistent state once more, and without you having to restart the entire Elixir application.</p>
<p>So each process can be a full blown Yocto Service, running online and waiting for other services to consume it, such as the Repo&rsquo;s workers.</p>
<h3>Divide and Conquer<span class="hx:absolute hx:-mt-20" id="divide-and-conquer"></span>
    <a href="#divide-and-conquer" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Again, as a disclaimer, I am still new to Elixir but the way I find easier to understand it, is like this:</p>
<ul>
<li>
<p>If you must deal with external resources, be it a File, or a Network Connection, or anything outside of the Erlang VM, you will want it to be a GenServer.</p>
</li>
<li>
<p>Then, if you have a GenServer, you want to start it up under a Supervisor (usually, the simple boilerplate that defines the children and restart strategy).</p>
</li>
<li>
<p>The number of GenServer processes you want to start up depends on how many parallel processes you want to have running. For example, if it is a database service, there is no point in starting up more than the available number of maximum connections your database allows. If you have several files to process, you want at most the number of available files or - in practice - just a few processes to deal with batches of files. You will usually want a pool of GenServer processes, and in this case you want to use Poolboy.</p>
</li>
<li>
<p>One GenServer may call other GenServers. You don&rsquo;t want to use a try/catch exception handling mechanism because you just need the particular GenServer process to crash if something goes wrong: if the file is corrupted or doesn&rsquo;t exist or if the network goes unstable or disconnects. The Supervisor will replace that process with a new GenServer process in its place and refill the Pool if needed.</p>
</li>
<li>
<p>You can make GenServers <a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">talk remotely</a> using the Node feature I explained 2 posts ago, with the ExMessenger example. Then it would be like a normal Micro Services architecture, but where the inside Yocto Services are actually doing the talking.</p>
</li>
<li>
<p>Any transformation with no side-effects (transforming a simple input into a simple output), like getting an HTML body string and parsing it into a List of tuples, can be organized in a normal Module. Refer to libraries like <a href="https://github.com/philss/floki"target="_blank" rel="noopener">Floki</a>, to see how they are organized.</p>
</li>
</ul>
<p>Each Erlang VM (called BEAM) is a single OS process, that manages a single real thread per CPU core available in your machine. Each real thread is managed by its own BEAM Scheduler, which will slice processing time between the lightweight processes inside.</p>
<p>Each BEAM process has its own mailbox to receive messages (more correctly called a <strong>run-queue</strong>). I/O operations such as file management will run asynchronously and not block the scheduler, which will manage to run other processes while waiting for I/O.</p>
<p>Each BEAM process also has its own separated heap and garbage collector (a generational, 2 stage copy collector). Because each process has very little state (variables in a function) each garbage collector stop is super short and runs fast.</p>
<p>So, each BEAM VM can be thought of as an entire application infrastructure, with many Yocto Services available for your application to call.</p>
<p>And as I said in the <a href="http://www.akitaonrails.com/en/2015/11/25/exercicio-exmessenger-entendendo-nodes-em-elixir"target="_blank" rel="noopener">previous article</a>, each BEAM VM can remotely call other BEAM VMs and exchange messages between them as if they were in the same VM. The semantics are almost the same and you have distributed computing the easier way.</p>
<p>Erlang implemented a fantastic set of primitives that quickly scale as large as you want or need, with the proper wirings to not let you down. And Elixir fixes the only problem most people have with Erlang: its <a href="http://www.akitaonrails.com/en/2015/11/25/elixir-101-apresentando-a-sintaxe"target="_blank" rel="noopener">old Prolog-inspired weird syntax</a>. Elixir is a thin and effective coat of modern language design, partly inspired by Ruby (although not a port by any means, it&rsquo;s still Erlang underneath).</p>
<p>I hope this series helped shed some light on why Elixir is the best choice among the new generation of high concurrency enabled languages and I also hope I made myself clear on why just high concurrency is not enough: you want high reliability as well. And in this aspect, the OTP architecture, built-in with Erlang, has no competition.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>ExMessenger Exercise: Understanding Nodes in Elixir</title><link>https://akitaonrails.github.io/en/2015/11/25/exmessenger-exercise-understanding-nodes-in-elixir/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/25/exmessenger-exercise-understanding-nodes-in-elixir/</guid><pubDate>Wed, 25 Nov 2015 13:19:00 -0200</pubDate><description>&lt;p&gt;I was working through this &lt;a href="http://drew.kerrigan.io/ditributed-elixir/"target="_blank" rel="noopener"&gt;old 2014 blog post&lt;/a&gt; by Drew Kerrigan where he builds a bare-bones, command-line-based chat application, with a client that sends messages and commands to a server.&lt;/p&gt;
&lt;p&gt;This is Elixir pre-1.0, and because it&amp;rsquo;s an exercise I refactored the original code and merged the server (ex_messenger) and client (ex_messenger_client) projects into an &lt;a href="http://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html"target="_blank" rel="noopener"&gt;Elixir Umbrella&lt;/a&gt; project and you can find my code on &lt;a href="https://github.com/akitaonrails/ex_messenger_exercise/blob/master/apps/ex_messenger_client/lib/ex_messenger_client.ex"target="_blank" rel="noopener"&gt;Github here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have multiple applications that work together and share the same dependencies you can use the Umbrella convention to have them all in the same codebase. If you &lt;tt&gt;mix compile&lt;/tt&gt; from the umbrella root, it compiles all the apps (which are independent Elixir mix projects as well). It&amp;rsquo;s just a way to have related apps in the same place instead of in multiple different repositories.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I was working through this <a href="http://drew.kerrigan.io/ditributed-elixir/"target="_blank" rel="noopener">old 2014 blog post</a> by Drew Kerrigan where he builds a bare-bones, command-line-based chat application, with a client that sends messages and commands to a server.</p>
<p>This is Elixir pre-1.0, and because it&rsquo;s an exercise I refactored the original code and merged the server (ex_messenger) and client (ex_messenger_client) projects into an <a href="http://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html"target="_blank" rel="noopener">Elixir Umbrella</a> project and you can find my code on <a href="https://github.com/akitaonrails/ex_messenger_exercise/blob/master/apps/ex_messenger_client/lib/ex_messenger_client.ex"target="_blank" rel="noopener">Github here</a>.</p>
<p>If you have multiple applications that work together and share the same dependencies you can use the Umbrella convention to have them all in the same codebase. If you <tt>mix compile</tt> from the umbrella root, it compiles all the apps (which are independent Elixir mix projects as well). It&rsquo;s just a way to have related apps in the same place instead of in multiple different repositories.</p>
<p>The code shown here is in <a href="https://github.com/akitaonrails/ex_messenger_exercise"target="_blank" rel="noopener">my personal Github repository</a> if you want to clone it.</p>
<h3>Nodes 101<span class="hx:absolute hx:-mt-20" id="nodes-101"></span>
    <a href="#nodes-101" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Before we check out the exercise, there is one more concept I need to clear up. In the previous article I explained how you can start processes and exchange messages, and how you can use the OTP GenServer and Supervisor to create more robust and fault-tolerant processes.</p>
<p>But this is just the beginning of the story. You&rsquo;ve probably heard how Erlang is great for distributed computing as well. Each Erlang VM (or BEAM) is network-enabled.</p>
<p>Again, this is one more concept I am still just beginning to properly learn, and you will want to read Elixir&rsquo;s website documentation on <a href="http://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html"target="_blank" rel="noopener">Distributed tasks and configuration</a>, that does an excellent job explaining how all this works.</p>
<p>But just to get started you can simply start 2 IEx sessions. From one terminal you can do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --sname fabio --cookie chat

Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl&#43;C to exit (type h() ENTER for help)
iex(fabio@Hal9000u)1&gt; </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And from a different terminal you can do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --sname akita --cookie chat

Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.1.1) - press Ctrl&#43;C to exit (type h() ENTER for help)
iex(akita@Hal9000u)1&gt; </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice how the IEx shell shows different Node names for each instance: &ldquo;fabio@Hal9000u&rdquo; and &ldquo;akita@Hal9000u&rdquo;. It&rsquo;s the sname concatenated with your machine name. From one instance you can ping the other, for example:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@Hal9000u)2&gt; Node.ping(:&#34;fabio@Hal9000u&#34;)
:pong</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If the name is correct and the other instance is indeed up, it responds to the ping with a <tt>:pong</tt>. This works only for nodes on the same machine, but what if I need to connect to an instance on a remote machine?</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@Hal9000u)3&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)

11:02:46.152 [error] ** System NOT running to use fully qualified hostnames **
** Hostname 192.168.1.13 is illegal **</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <tt>&ndash;sname</tt> option sets a name only reachable within the same subnet; for a fully qualified domain name you need to use <tt>&ndash;name</tt>, for example like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex  --name fabio@192.168.1.13 --cookie chat</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And for the other node:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --name akita@192.168.1.13 --cookie chat</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And from this second terminal you can ping the other node the same way as before:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@192.168.1.13)1&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)
:pong</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And you might be wondering, what is this &ldquo;<tt>&ndash;cookie</tt>&rdquo; thing? Just spin up a third terminal with another client name, but without the cookie, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex --name john@192.168.1.13</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And if you try to ping one of the first two nodes you won&rsquo;t get a <tt>:pong</tt> back:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(john@192.168.1.13)1&gt; Node.ping(:&#34;fabio@192.168.1.13&#34;)
:pang</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The cookie is just an atom to identify relationships between nodes. In a pool of several servers you can make sure you&rsquo;re not trying to connect different applications to each other. And as a result you get a <tt>:pang</tt>. Instead of an IP address you can use a fully qualified domain name.</p>
<p>And just by having the node &ldquo;akita@&rdquo; pinging &ldquo;fabio@&rdquo; we can see that they are aware of each other:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(fabio@192.168.1.13)2&gt; Node.list
[:&#34;akita@192.168.1.13&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(akita@192.168.1.13)2&gt; Node.list
[:&#34;fabio@192.168.1.13&#34;]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If one of the nodes crashes or quits, the Node list is automatically refreshed to reflect only nodes that are actually alive and responding.</p>
<p>You can check the official API Reference for the <a href="http://elixir-lang.org/docs/stable/elixir/Node.html#summary"target="_blank" rel="noopener">Node</a> for more information. But this should give you a hint for the next section.</p>
<h3>Creating a Chat Client<span class="hx:absolute hx:-mt-20" id="creating-a-chat-client"></span>
    <a href="#creating-a-chat-client" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Back to the exercise: the ExMessenger server has &ldquo;ExMessenger.Server&rdquo;, which is a GenServer, and the &ldquo;ExMessenger.Supervisor&rdquo; that starts it up. The ExMessenger.Server is globally registered as <tt>:message_server</tt>, started and supervised by the &ldquo;ExMessenger.Supervisor&rdquo;.</p>
<p>The &ldquo;ExMessengerClient&rdquo; starts up the unsupervised &ldquo;ExMessengerClient.MessageHandler&rdquo;, which is also a GenServer, and globally registered as <tt>:message_handler</tt>.</p>
<p>The Tree for both apps looks roughly like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ExMessenger
- ExMessenger.Supervisor
    &#43; ExMessenger.Server
ExMessengerClient
- ExMessengerClient.MessageHandler</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We start them separately, first the message server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cd apps/ex_messenger
iex --sname server --cookie chocolate-chip -S mix run</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice that for this example we are starting with a simple name &ldquo;server&rdquo;, for the local subnet, and a cookie. It will respond as &ldquo;server@Hal9000u&rdquo; (Hal9000u being my local machine&rsquo;s name).</p>
<p>Then, we can start the client app:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cd apps/ex_messenger_client
server=server@Hal9000u nick=john elixir --sname client -S mix run</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Here we are setting 2 environment variables (that we can retrieve inside the app using <tt>System.get_env/1</tt>) and also setting a local node name &ldquo;client&rdquo;. You can spin up more client nodes using a different &ldquo;sname&rdquo; and a different &ldquo;nick&rdquo; from another terminal, as many as you want, all linking to the same &ldquo;server@Hal9000u&rdquo; message server.</p>
<p>I&rsquo;m starting up like this instead of using a command-line escript like I did in ExMangaDownloadr because I didn&rsquo;t find any way to set the <tt>&ndash;sname</tt> or <tt>&ndash;name</tt> the same way I can set <tt>&ndash;cookie</tt> using <tt>Node.set_cookie</tt>. If anyone knows how to set it up differently, let me know in the comments section down below.</p>
<p>Notice that I said &ldquo;linking&rdquo; and not &ldquo;connecting&rdquo;. From the &ldquo;ExMessengerClient&rdquo; we start like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Application</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">CLI</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span><span class="p">(</span><span class="n">_type</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get_env</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">connect</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">start_message_handler</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="n">join_chatroom</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">CLI</span><span class="o">.</span><span class="n">input_loop</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The <tt>get_env</tt> private function is just a wrapper to handle the &ldquo;server&rdquo; and &ldquo;nick&rdquo; environment variables that we passed:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">get_env</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">server</span> <span class="o">=</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;server&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">to_atom</span>
</span></span><span class="line"><span class="cl">  <span class="n">nick</span> <span class="o">=</span> <span class="no">System</span><span class="o">.</span><span class="n">get_env</span><span class="p">(</span><span class="s2">&#34;nick&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, we try to connect to the remote server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Connecting to </span><span class="si">#{</span><span class="n">server</span><span class="si">}</span><span class="s2"> from </span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2"> ...&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">Node</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="p">,</span> <span class="ss">:&#34;chocolate-chip&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">Node</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="kp">true</span> <span class="o">-&gt;</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">    <span class="n">reason</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Could not connect to server, reason: </span><span class="si">#{</span><span class="n">reason</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The important piece here is that we are setting the client instance&rsquo;s cookie with <tt>Node.set_cookie/1</tt> (notice that we didn&rsquo;t pass it in the command-line options like we did with the server instance). Without setting the cookie, the next line with <tt>Node.connect(server)</tt> would fail to connect, as I explained in the previous section.</p>
<p>Then, we start the &ldquo;ExMessengerClient.MessageHandler&rdquo; GenServer, linking with the Message Server instance:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">start_message_handler</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">MessageHandler</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">server</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Connected&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The Message Handler GenServer itself is very simple. It just sets the server as the state, handles incoming messages from the server and prints them out in the client&rsquo;s terminal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">MessageHandler</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:gen_server</span><span class="o">.</span><span class="n">start_link</span><span class="p">({</span> <span class="ss">:local</span><span class="p">,</span> <span class="ss">:message_handler</span> <span class="p">},</span> <span class="n">__MODULE__</span><span class="p">,</span> <span class="n">server</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">server</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_cast</span><span class="p">({</span> <span class="ss">:message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">},</span> <span class="n">server</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">message</span> <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="si">#{</span><span class="n">server</span><span class="si">}</span><span class="s2">&gt; </span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2">&gt; &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">server</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Going back to the main &ldquo;ExMessengerClient&rdquo; module, after starting the (unsupervised) GenServer that receives incoming messages, we proceed to join the pseudo-chatroom in the server:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">join_chatroom</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">case</span> <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Joined the chatroom *&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Users in the room: </span><span class="si">#{</span><span class="n">users</span><span class="si">}</span><span class="s2"> *&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;* Type /help for options *&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">reason</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Could not join chatroom, reason: </span><span class="si">#{</span><span class="n">reason</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I defined this &ldquo;ServerProcotol&rdquo; module, which is just a convenience wrapper for <tt>GenServer.call/3</tt> and <tt>GenServer.cast/2</tt> calls, to send messages to the remote GenServer called <tt>:message_server</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">connect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">call</span><span class="p">({</span><span class="ss">:connect</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">disconnect</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">call</span><span class="p">({</span><span class="ss">:disconnect</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">list_users</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:list_users</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">private_message</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">},</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:private_message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">say</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">},</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">server</span> <span class="o">|&gt;</span> <span class="n">cast</span><span class="p">({</span><span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">({</span><span class="ss">:message_server</span><span class="p">,</span> <span class="n">server</span><span class="p">},</span> <span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">cast</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">({</span><span class="ss">:message_server</span><span class="p">,</span> <span class="n">server</span><span class="p">},</span> <span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pretty straightforward. Then, the main ExMessengerClient calls the recursive <tt>input_loop/1</tt> function from the CLI module, which just receives user input and handles the proper commands using pattern matching, like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">CLI</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">alias</span> <span class="no">ExMessengerClient</span><span class="o">.</span><span class="n">ServerProcotol</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">input_loop</span><span class="p">({</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">})</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Node</span><span class="o">.</span><span class="n">self</span><span class="si">}</span><span class="s2">&gt; &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">line</span> <span class="o">=</span> <span class="no">IO</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="ss">:line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">rstrip</span>
</span></span><span class="line"><span class="cl">    <span class="n">handle_command</span> <span class="n">line</span><span class="p">,</span> <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">input_loop</span> <span class="p">{</span><span class="n">server</span><span class="p">,</span> <span class="n">nick</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/help&#34;</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Available commands:
</span></span></span><span class="line"><span class="cl"><span class="s2">      /leave
</span></span></span><span class="line"><span class="cl"><span class="s2">      /join
</span></span></span><span class="line"><span class="cl"><span class="s2">      /users
</span></span></span><span class="line"><span class="cl"><span class="s2">      /pm &lt;to nick&gt; &lt;message&gt;
</span></span></span><span class="line"><span class="cl"><span class="s2">      or just type a message to send
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/leave&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;You have exited the chatroom, you can rejoin with /join or quit with /quit&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/quit&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">System</span><span class="o">.</span><span class="n">halt</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/join&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Joined the chatroom&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;/users&#34;</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">list_users</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">_args</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="n">_args</span><span class="p">),</span> <span class="k">do</span><span class="p">:</span> <span class="ss">:ok</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_command</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">String</span><span class="o">.</span><span class="n">contains?</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="s2">&#34;/pm&#34;</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">}</span> <span class="o">=</span> <span class="n">parse_private_recipient</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">private_message</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="no">ServerProcotol</span><span class="o">.</span><span class="n">say</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defp</span> <span class="n">parse_private_recipient</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span><span class="n">to</span><span class="o">|</span><span class="n">message</span><span class="o">]</span> <span class="o">=</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">slice</span><span class="p">(</span><span class="mi">4</span><span class="o">..-</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">split</span>
</span></span><span class="line"><span class="cl">    <span class="n">message</span> <span class="o">=</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">List</span><span class="o">.</span><span class="n">foldl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">acc</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">acc</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">x</span><span class="si">}</span><span class="s2">&#34;</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="nb">String</span><span class="o">.</span><span class="n">lstrip</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="n">to</span><span class="p">,</span> <span class="n">message</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this wraps up the Client.</p>
<h3>Creating a Chat Server<span class="hx:absolute hx:-mt-20" id="creating-a-chat-server"></span>
    <a href="#creating-a-chat-server" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The Chat Client sends GenServer messages to a remote <tt>{:message_server, server}</tt>, and in the example, <tt>server</tt> is just the sname &ldquo;server@Hal9000u&rdquo; atom.</p>
<p>Now, we need this <tt>:message_server</tt> and this is the &ldquo;ExMessenger.Server&rdquo; GenServer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">ExMessenger</span><span class="o">.</span><span class="n">Server</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="no">Logger</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="ss">:gen_server</span><span class="o">.</span><span class="n">start_link</span><span class="p">({</span> <span class="ss">:local</span><span class="p">,</span> <span class="ss">:message_server</span> <span class="p">},</span> <span class="n">__MODULE__</span><span class="p">,</span> <span class="o">[]</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">new</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And that&rsquo;s it. When the &ldquo;ExMessenger.Supervisor&rdquo; starts this GenServer it registers it globally in this instance as <tt>:message_server</tt>. And this is how we address messages from what we called &ldquo;clients&rdquo; (the ExMessengerClient application).</p>
<p>When the ExMessengerClient calls the <tt>ServerProtocol.connect/1</tt>, it sends the <tt>{:connect, nick}</tt> message to the server. In the Server we handle it like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span> <span class="ss">:connect</span><span class="p">,</span> <span class="n">nick</span> <span class="p">},</span> <span class="p">{</span><span class="n">from</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span> <span class="p">,</span> <span class="n">users</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">cond</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">nick</span> <span class="o">==</span> <span class="ss">:server</span> <span class="ow">or</span> <span class="n">nick</span> <span class="o">==</span> <span class="s2">&#34;server&#34;</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:nick_not_allowed</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="no">HashDict</span><span class="o">.</span><span class="n">has_key?</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:nick_in_use</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kp">true</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">new_users</span> <span class="o">=</span> <span class="n">users</span> <span class="o">|&gt;</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">node</span><span class="p">(</span><span class="n">from</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="n">user_list</span> <span class="o">=</span> <span class="n">log</span><span class="p">(</span><span class="n">new_users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="s2">&#34;has joined&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="p">{</span> <span class="ss">:ok</span><span class="p">,</span> <span class="n">user_list</span> <span class="p">},</span> <span class="n">new_users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>First, it checks if the nick is &ldquo;server&rdquo; and disallows it. Second, it checks if the nickname already exists in the internal <a href="http://elixir-lang.org/docs/stable/elixir/HashDict.html"target="_blank" rel="noopener">HashDict</a> (a key/value dictionary) and refuses if it already exists. Finally, third, it puts the pair of nickname and node name (like &ldquo;client@Hal9000u&rdquo;) in the HashDict and broadcasts through the <tt>log/3</tt> private function to all other nodes in the HashDict dictionary.</p>
<p>The <tt>log/3</tt> just creates a log message concatenating the nicknames of all clients and prints it out, then broadcasts it to the Message Handler of all the clients listed in the HashDict:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">log</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">user_list</span> <span class="o">=</span> <span class="n">users</span> <span class="o">|&gt;</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">keys</span> <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s2">&#34;:&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">, user_list: </span><span class="si">#{</span><span class="n">user_list</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">say</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">user_list</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">say</span><span class="p">(</span><span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">(</span><span class="ss">:message_server</span><span class="p">,</span> <span class="p">{</span> <span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="s2">&#34;* </span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2"> *&#34;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_cast</span><span class="p">({</span> <span class="ss">:say</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">},</span> <span class="n">users</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">ears</span> <span class="o">=</span> <span class="no">HashDict</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="no">Logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">nick</span><span class="si">}</span><span class="s2"> said </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">broadcast</span><span class="p">(</span><span class="n">ears</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="ss">:noreply</span><span class="p">,</span> <span class="n">users</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Up to this point it just casts a message to itself, the <tt>{:say, nick, message}</tt> tuple, which is handled by the GenServer and calls the <tt>broadcast/3</tt> function defined like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">broadcast</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">fn</span> <span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">node</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="no">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">send_message_to_client</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">send_message_to_client</span><span class="p">(</span><span class="n">client_node</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="no">GenServer</span><span class="o">.</span><span class="n">cast</span><span class="p">({</span> <span class="ss">:message_handler</span><span class="p">,</span> <span class="n">client_node</span> <span class="p">},</span> <span class="p">{</span> <span class="ss">:message</span><span class="p">,</span> <span class="n">nick</span><span class="p">,</span> <span class="n">message</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It maps the list of users and fires up an asynchronous Elixir <tt>Task</tt> (which is itself just a GenServer, as I explained before in the Ex Manga Downloader series). Because it&rsquo;s a broadcast, it makes sense to make all of them parallel.</p>
<p>The important bit is the <tt>send_message_to_client/3</tt> which casts a message to the tuple <tt>{ :message_handler, client_node }</tt> where &ldquo;client_node&rdquo; is just &ldquo;client@Hal9000u&rdquo; or whatever &ldquo;&ndash;sname&rdquo; you used to start up each client node.</p>
<p>Now, this is how the clients send GenServer message calls/casts to <tt>{:message_server, server}</tt> and it sends messages back to <tt>{:message_handler, client}</tt>.</p>
<h3>This is not your traditional TCP Client/Server example!<span class="hx:absolute hx:-mt-20" id="this-is-not-your-traditional-tcp-clientserver-example"></span>
    <a href="#this-is-not-your-traditional-tcp-clientserver-example" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Now, we are calling the &ldquo;ExMessenger.Server&rdquo; a Chat &ldquo;Server&rdquo; and the &ldquo;ExMessengerClient&rdquo; a Chat &ldquo;Client&rdquo;. Although we have been calling them &ldquo;Server&rdquo; and &ldquo;Client&rdquo;, they don&rsquo;t refer to the usual &ldquo;TCP Server&rdquo; and &ldquo;TCP Client&rdquo; examples you may be familiar with!</p>
<p>The ExMessenger.Server is indeed a Server (an OTP GenServer) but the ExMessengerClient.MessageHandler is also a Server (another OTP GenServer)! Because they <strong>both</strong> have Node behavior, it&rsquo;s more like they are 2 peer-to-peer nodes instead of your old-school, simple client-server relationship. They can have client behavior (the Server sends messages to the MessageHandler) and server behavior (the Server receives messages from ExMessengerClient).</p>
<p>Let this concept sink in for a moment: built into the language you get a full-blown, easy-to-use, peer-to-peer network distribution model. You don&rsquo;t need to have one single node elected as the sole &ldquo;master&rdquo;; you could have all nodes in a ring coordinating between them, avoiding single points of failure.</p>
<p>I believe this is maybe how Erlang-based services such as <a href="http://manpages.ubuntu.com/manpages/hardy/man8/ejabberd.8.html"target="_blank" rel="noopener">ejabberd</a> and <a href="https://www.rabbitmq.com/clustering.html"target="_blank" rel="noopener">RabbitMQ</a> work.</p>
<p>In the <a href="https://github.com/processone/ejabberd/blob/master/src/ejabberd_cluster.erl"target="_blank" rel="noopener">case of ejabberd</a>, I can see that it keeps the state of the cluster in Mnesia tables (Mnesia being another component of OTP, a built-in distributed NoSQL database!) and it indeed uses the Node facilities to coordinate distributed nodes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="nf">join</span><span class="p">(</span><span class="nv">Node</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="p">{</span><span class="nb">node</span><span class="p">(),</span> <span class="nn">net_adm</span><span class="p">:</span><span class="nf">ping</span><span class="p">(</span><span class="nv">Node</span><span class="p">)}</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="nv">Node</span><span class="p">,</span> <span class="p">_}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="p">{</span><span class="n">not_master</span><span class="p">,</span> <span class="nv">Node</span><span class="p">}};</span>
</span></span><span class="line"><span class="cl">        <span class="p">{_,</span> <span class="n">pong</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">stop</span><span class="p">(</span><span class="n">ejabberd</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">stop</span><span class="p">(</span><span class="n">mnesia</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">delete_schema</span><span class="p">([</span><span class="nb">node</span><span class="p">()]),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">start</span><span class="p">(</span><span class="n">mnesia</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">change_config</span><span class="p">(</span><span class="n">extra_db_nodes</span><span class="p">,</span> <span class="p">[</span><span class="nv">Node</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">change_table_copy_type</span><span class="p">(</span><span class="n">schema</span><span class="p">,</span> <span class="nb">node</span><span class="p">(),</span> <span class="n">disc_copies</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nb">spawn</span><span class="p">(</span><span class="k">fun</span><span class="p">()</span>  <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nn">lists</span><span class="p">:</span><span class="nf">foreach</span><span class="p">(</span><span class="k">fun</span><span class="p">(</span><span class="nv">Table</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">                            <span class="nv">Type</span> <span class="o">=</span> <span class="n">call</span><span class="p">(</span><span class="nv">Node</span><span class="p">,</span> <span class="n">mnesia</span><span class="p">,</span> <span class="n">table_info</span><span class="p">,</span> <span class="p">[</span><span class="nv">Table</span><span class="p">,</span> <span class="n">storage_type</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">                            <span class="nn">mnesia</span><span class="p">:</span><span class="nf">add_table_copy</span><span class="p">(</span><span class="nv">Table</span><span class="p">,</span> <span class="nb">node</span><span class="p">(),</span> <span class="nv">Type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="k">end</span><span class="p">,</span> <span class="nn">mnesia</span><span class="p">:</span><span class="nb">system_info</span><span class="p">(</span><span class="n">tables</span><span class="p">)</span><span class="o">--</span><span class="p">[</span><span class="n">schema</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">                <span class="k">end</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="nn">application</span><span class="p">:</span><span class="nf">start</span><span class="p">(</span><span class="n">ejabberd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="n">error</span><span class="p">,</span> <span class="p">{</span><span class="n">no_ping</span><span class="p">,</span> <span class="nv">Node</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is what a snippet of pure Erlang source code looks like, by the way. You should have enough Elixir in your head right now to be able to abstract away the ugly Erlang syntax and see that it&rsquo;s a <tt>case</tt> pattern matching on the <tt>{_, :pong}</tt> tuple, using Node&rsquo;s ping facilities to assert the connectivity of the node and updating the Mnesia table and other setups.</p>
<p>Also in the <a href="https://github.com/rabbitmq/rabbitmq-server/blob/6f70dcbe05dbba35f7d950674d293a4c7d867d44/src/rabbit_control_main.erl"target="_blank" rel="noopener">source code of the RabbitMQ-Server</a> you will find a similar thing:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-erlang" data-lang="erlang"><span class="line"><span class="cl"><span class="nf">become</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nn">error_logger</span><span class="p">:</span><span class="nf">tty</span><span class="p">(</span><span class="n">false</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">ok</span> <span class="o">=</span> <span class="nn">net_kernel</span><span class="p">:</span><span class="nf">stop</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nn">net_adm</span><span class="p">:</span><span class="nf">ping</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">)</span> <span class="k">of</span>
</span></span><span class="line"><span class="cl">        <span class="n">pong</span> <span class="o">-&gt;</span> <span class="nb">exit</span><span class="p">({</span><span class="n">node_running</span><span class="p">,</span> <span class="nv">BecomeNode</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="n">pang</span> <span class="o">-&gt;</span> <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;  * Impersonating node: </span><span class="si">~s</span><span class="s">...&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">BecomeNode</span><span class="p">]),</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span><span class="n">ok</span><span class="p">,</span> <span class="p">_}</span> <span class="o">=</span> <span class="nn">rabbit_cli</span><span class="p">:</span><span class="nf">start_distribution</span><span class="p">(</span><span class="nv">BecomeNode</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34; done</span><span class="si">~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[]),</span>
</span></span><span class="line"><span class="cl">                <span class="nv">Dir</span> <span class="o">=</span> <span class="nn">mnesia</span><span class="p">:</span><span class="nb">system_info</span><span class="p">(</span><span class="n">directory</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                <span class="nn">io</span><span class="p">:</span><span class="nf">format</span><span class="p">(</span><span class="s">&#34;  * Mnesia directory  : </span><span class="si">~s~n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">[</span><span class="nv">Dir</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">.</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Again, pinging nodes, using Mnesia for the server state. Erlang&rsquo;s syntax is uncommon for most of us: variables start with a capitalized letter (we intuitively think they&rsquo;re constants instead), statements end with a dot, instead of dot-notation to call a function from a module it uses a colon &ldquo;:&rdquo;, unlike Elixir the parentheses are not optional, and so on. Trying to read code like this shows the value of having Elixir to unleash Erlang&rsquo;s hidden powers.</p>
<p>So, up to this point, you know how processes are spawned internally, how they are orchestrated within the OTP framework, and now how they can interact remotely through the <strong>Node</strong> peer-to-peer abstraction. And again, this is all built into the language. No other language comes even close.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-architecture</category></item><item><title>Observing Processes in Elixir - The Little Elixir &amp; OTP Guidebook</title><link>https://akitaonrails.github.io/en/2015/11/22/observing-processes-in-elixir-the-little-elixir-otp-guidebook/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/22/observing-processes-in-elixir-the-little-elixir-otp-guidebook/</guid><pubDate>Sun, 22 Nov 2015 14:57:00 -0200</pubDate><description>&lt;p&gt;In my journey to really understand how a proper Elixir application should be written, I am working through Benjamin Tan Wei Hao&amp;rsquo;s excellent &lt;a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener"&gt;The Little Elixir &amp;amp; OTP Guidebook&lt;/a&gt;. If you&amp;rsquo;re just getting started, this is a no-brainer: buy and study this guidebook. Yes, it will help if you&amp;rsquo;ve already read Dave Thomas&amp;rsquo; &lt;a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener"&gt;Programming Elixir&lt;/a&gt; book first.&lt;/p&gt;
&lt;p&gt;In my &lt;a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener"&gt;Ex Manga Downloadr Part 2&lt;/a&gt; article I explored adding better process pool control using the excellent and robust Poolboy library. One of the guidebook&amp;rsquo;s main exercises is to build a simpler version of Poolboy in pure Elixir (Poolboy is written in good, old, Erlang).&lt;/p&gt;</description><content:encoded><![CDATA[<p>In my journey to really understand how a proper Elixir application should be written, I am working through Benjamin Tan Wei Hao&rsquo;s excellent <a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener">The Little Elixir &amp; OTP Guidebook</a>. If you&rsquo;re just getting started, this is a no-brainer: buy and study this guidebook. Yes, it will help if you&rsquo;ve already read Dave Thomas&rsquo; <a href="https://pragprog.com/book/elixir/programming-elixir"target="_blank" rel="noopener">Programming Elixir</a> book first.</p>
<p>In my <a href="http://www.akitaonrails.com/en/2015/11/19/ex-manga-downloadr-parte-2-poolboy-ao-resgate"target="_blank" rel="noopener">Ex Manga Downloadr Part 2</a> article I explored adding better process pool control using the excellent and robust Poolboy library. One of the guidebook&rsquo;s main exercises is to build a simpler version of Poolboy in pure Elixir (Poolboy is written in good, old, Erlang).</p>
<p>The main goal of this article is to introduce what <strong>Fault Tolerance</strong> in Erlang/Elixir means, and it is also an excuse for me to show off Erlang&rsquo;s observer:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/528/big_observer-kill.png" alt="Observer"  loading="lazy" /></p>
<p>Yes, Erlang allows us not just to see what&rsquo;s going on inside its runtime environment, but we can even take action on individual Processes running inside it! How cool is that?</p>
<p>But before we can show Fault Tolerance and the Observer I need to explain what Processes are, and why they matter. You <strong>must</strong> understand the following concepts to successfully understand Elixir programming:</p>
<ol>
<li>You don&rsquo;t have &ldquo;objects&rdquo;, which are runtime instances of classes (or prototypical objects, which are copies of other objects). Instead of &ldquo;Classes&rdquo; you have collections of functions organized in modules, with no dependency on internal state. And instead of &ldquo;objects&rdquo; we have, roughly speaking, &ldquo;processes&rdquo;. For example:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">MyProcess</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">accepting_messages</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">accepting_messages</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">receive</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">message</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Hello, </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">accepting_messages</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">new_state</span> <span class="o">=</span> <span class="n">state</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;New state is </span><span class="si">#{</span><span class="n">new_state</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">accepting_messages</span><span class="p">(</span><span class="n">new_state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">_</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;What?&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">accepting_messages</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ol start="2">
<li>We can execute a function inside another process. This is how we can spawn a brand new, concurrent, lightweight process:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">spawn</span> <span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">MyProcess</span><span class="o">.</span><span class="n">start</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.87.0&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>When <tt>accepting_messages/1</tt> is called, it stops at the <tt>receive/0</tt> block, waiting to receive a new message. Then we can send messages like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="s2">&#34;world&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">Hello</span><span class="p">,</span> <span class="n">world</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It receives the <tt>{:hello, &ldquo;world&rdquo;}</tt> tuple message, pattern matches the value <tt>&ldquo;world&rdquo;</tt> into the <tt>message</tt> variable, and concatenates it into the <tt>&ldquo;Hello, world&rdquo;</tt> string, which it prints out with <tt>IO.puts/1</tt>, and then recurses to itself again. We call the <tt>receive/0</tt> block again and block, waiting for further messages:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">4</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>We send the <tt>{:counter}</tt> message to the same process pid again, and when it receives this message, it gets the <tt>state</tt> value from the function argument, increments it by 1, prints out the new state, and calls itself again passing the new state as the new argument. It blocks again, waiting for further messages, and each time it receives the <tt>{:counter}</tt> message, it increases the previous state by one again and recurses.</p>
<p>This is basically how we can maintain state in Elixir. If we kill this process and spawn a new one, it restarts with zero (which is what the <tt>start/0</tt> function does).</p>
<ol start="3">
<li>
<p>So, while you don&rsquo;t have &ldquo;objects&rdquo;, you do, however, have Processes. Superficially, a process behaves like an &ldquo;object&rdquo;. Be careful not to think that a Process is like a heavyweight Thread though. Erlang has its own internal scheduler that controls the concurrency, and you can load as many as 16 billion lightweight processes if your hardware allows it. Threads are super heavy, Erlang processes are super light.</p>
</li>
<li>
<p>As we saw in the example, each process has its own internal mechanism to receive messages from other processes. Those messages accumulate in an internal &ldquo;mailbox&rdquo; and you can choose to <tt>receive</tt> and pattern match through those messages, recursing to itself in order to receive new messages if you want.</p>
</li>
<li>
<p>Processes can be linked to or monitor other processes. For example, within an IEx shell, we are inside an Elixir process, so we could do:</p>
</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">self</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.98.0&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">spawn</span> <span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">MyProcess</span><span class="o">.</span><span class="n">start</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.105.0&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">alive?</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">link</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">true</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>With <tt>self</tt> we can see that the current process id for the IEx shell is &ldquo;0.98.0&rdquo;. Then we spawn a process that calls <tt>MyProcess.start/0</tt>, which will block in the receive call. This new process has a different id, &ldquo;0.105.0&rdquo;.</p>
<p>We can assert that the new process is indeed alive and we can link the IEx shell with the &ldquo;0.105.0&rdquo; pid process. Now, whatever happens to this process will cascade to the shell.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="ss">:kill</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">**</span> <span class="p">(</span><span class="no">EXIT</span> <span class="n">from</span> <span class="c1">#PID&lt;0.98.0&gt;) killed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">Interactive</span> <span class="no">Elixir</span> <span class="p">(</span><span class="mi">1</span><span class="o">.</span><span class="mi">1</span><span class="o">.</span><span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="n">press</span> <span class="no">Ctrl</span><span class="o">+</span><span class="n">C</span> <span class="n">to</span> <span class="nb">exit</span> <span class="p">(</span><span class="n">type</span> <span class="n">h</span><span class="p">()</span> <span class="no">ENTER</span> <span class="k">for</span> <span class="n">help</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="sr">/home/</span><span class="n">akitaonrails</span><span class="o">/.</span><span class="n">iex</span><span class="o">.</span><span class="n">exs</span><span class="p">:</span><span class="mi">1</span><span class="p">:</span> <span class="ss">warning</span><span class="p">:</span> <span class="n">redefining</span> <span class="k">module</span> <span class="nn">R</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">self</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.109.0&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And indeed, if we forcefully send a kill message to the &ldquo;0.105.0&rdquo; process, the IEx shell is also killed in the process. IEx restarts and its new pid is &ldquo;0.109.0&rdquo; instead of the old &ldquo;0.98.0&rdquo;. By the way, this is one way a process is different from a normal object. It behaves more like an operating system process, where a crash in a process does not affect the whole system, because it does not hold external shared state that can corrupt the system&rsquo;s state.</p>
<p>The important concept is that we now have a mechanism to define a Parent Process (IEx in this example) and Children processes linked to it.</p>
<ol start="6">
<li>Parent processes don&rsquo;t need to stupidly kill themselves because their children screwed up. Instead, they can trap exits and decide what to do later:</li>
</ol>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">flag</span><span class="p">(</span><span class="ss">:trap_exit</span><span class="p">,</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">false</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">pid</span> <span class="o">=</span> <span class="n">spawn_link</span> <span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">MyProcess</span><span class="o">.</span><span class="n">start</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.118.0&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="n">pid</span><span class="p">,</span> <span class="p">{</span><span class="ss">:counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:counter</span><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>First, we declare that the IEx shell will trap exits and not just die. Then we spawn a new process and link it. The <tt>spawn_link/1</tt> function has the same effect as <tt>spawn/1</tt> followed by <tt>Process.link/1</tt>. We can send a message to the new pid and check that it is indeed still working.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="ss">:kill</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">alive?</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">false</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span><span class="o">&gt;</span> <span class="n">flush</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:EXIT</span><span class="p">,</span> <span class="c1">#PID&lt;0.118.0&gt;, :killed}</span>
</span></span><span class="line"><span class="cl"><span class="ss">:ok</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now we forcefully kill the new process again, but IEx does not crash this time, as it is explicitly trapping those errors. If we check the killed pid, we can assert that it is indeed dead. But now we can also inspect IEx&rsquo;s own process mailbox (in this case, just flushing what&rsquo;s queued in the inbox) and see that it just received a message saying that its child was killed.</p>
<p>From here we could make the IEx process handle this message and decide to just mourn for its deceased child and kill itself, or move on and spawn_link a new one. We have <strong>choice</strong> in the face of disaster.</p>
<h3>OTP Workers<span class="hx:absolute hx:-mt-20" id="otp-workers"></span>
    <a href="#otp-workers" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Setting aside the grim metaphor, we learned that we have Parent and Child processes, but more importantly they can fit the roles of Supervisors and supervised Workers, respectively.</p>
<p>Workers are where we put our code. This code can have bugs, it can depend on external stuff that can make our code crash for unexpected reasons. In a normal language we would start using the dreaded <tt>try/catch</tt> blocks, which are just ugly and <strong>wrong</strong>! Don&rsquo;t catch errors in Elixir, just let it crash!</p>
<p>As I explained in my previous article, everything in Elixir ends up being a so-called &ldquo;OTP application&rdquo;. The example above is just a very simple contraption that we can expand upon. Let&rsquo;s rewrite the same thing as an OTP GenServer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">MyFancyProcess</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">name</span><span class="p">:</span> <span class="n">__MODULE__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">## Public API</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">message</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">counter</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="ss">:counter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">## GenServer callbacks</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">start_counter</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">start_counter</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">message</span><span class="p">},</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Hello, </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:noproc</span><span class="p">,</span> <span class="n">state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_call</span><span class="p">(</span><span class="ss">:counter</span><span class="p">,</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_state</span> <span class="o">=</span> <span class="n">state</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;New state is </span><span class="si">#{</span><span class="n">new_state</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="ss">:noproc</span><span class="p">,</span> <span class="n">new_state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This new <tt>MyFancyProcess</tt> is essentially the same as <tt>MyProcess</tt> but with OTP GenServer on top of it. There are Public API functions and GenServer callbacks.</p>
<p>Benjamin&rsquo;s book goes to great lengths to detail every bit of what I just implemented. But for now just understand some basics:</p>
<ol>
<li>
<p>The module does &ldquo;<tt>use GenServer</tt>&rdquo; to import all the necessary GenServer bits for your convenience. In essence one of the things it will do is create that <tt>receive</tt> block we did in the first version to wait for messages.</p>
</li>
<li>
<p>The <tt>start_link/1</tt> function will create the instance of this GenServer and return the linked process. Internally it will call back to the <tt>init/1</tt> function to set the initial state of this worker. This is a flexible language; we have multiple ways of doing the same thing, and this is good. Having just a single way of writing code is boring.</p>
</li>
<li>
<p>The convention is to have one public function that calls the internal <tt>handle_call/3</tt> (for synchronous calls), <tt>handle_cast/2</tt> (for asynchronous calls), and <tt>handle_info/2</tt>. You could just call <tt>handle_call</tt> from the outside, but that&rsquo;s just ugly, so you will find this convention everywhere.</p>
</li>
</ol>
<p>Once we have this in place, we can start calling it directly:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">11</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="c1">#PID&lt;0.261.0&gt;}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">hello</span><span class="p">(</span><span class="s2">&#34;world&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="no">Hello</span><span class="p">,</span> <span class="n">world</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">15</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And this is much cleaner than the version where we manually <tt>spawn_link</tt> and <tt>send</tt> messages to a pid. This is all handled nicely by the GenServer underneath it. And as I said, the results are the same as the initial crude <tt>MyProcess</tt> example.</p>
<p>In fact, this convention does make us type a lot of boilerplate many times over. There is a library called <a href="https://github.com/sasa1977/exactor"target="_blank" rel="noopener">ExActor</a> that greatly simplifies a GenServer implementation, turning our previous code into something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">MyFancyProcess</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExActor</span><span class="o">.</span><span class="n">GenServer</span><span class="p">,</span> <span class="ss">initial_state</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defcall</span> <span class="n">hello</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">state</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;Hello, </span><span class="si">#{</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">noreply</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">defcall</span> <span class="n">counter</span><span class="p">,</span> <span class="n">state</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_counter</span> <span class="o">=</span> <span class="n">state</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="no">IO</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&#34;New state is </span><span class="si">#{</span><span class="n">new_counter</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_state</span><span class="p">(</span><span class="n">new_counter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is way cleaner, but as we are just using IEx, I&rsquo;m not using this version for the next section. Stick with the longer version of <tt>MyFancyProcess</tt> listed at the beginning of this section!</p>
<h2>OTP Supervisor<span class="hx:absolute hx:-mt-20" id="otp-supervisor"></span>
    <a href="#otp-supervisor" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Now that we have a worker, we can create a Supervisor to supervise it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">MyFancySupervisor</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Supervisor</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Supervisor</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">children</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="n">worker</span><span class="p">(</span><span class="no">MyFancyProcess</span><span class="p">,</span> <span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">opts</span> <span class="o">=</span> <span class="o">[</span><span class="ss">strategy</span><span class="p">:</span> <span class="ss">:one_for_one</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">supervise</span><span class="p">(</span><span class="n">children</span><span class="p">,</span> <span class="n">opts</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is just simple boilerplate that most Supervisors will have. There are many details you must learn, but for this article&rsquo;s purposes the important bits are, first, the definition of the <tt>children</tt> specification, saying that this Supervisor should start the <tt>MyFancyProcess</tt> GenServer instead of us having to call <tt>MyFancyProcess.start_link</tt> manually. And the second important bit is the <tt>opts</tt> list which defines the strategy of <tt>:one_for_one</tt>, meaning that if the Supervisor detects that the child has died, it should restart it.</p>
<p>From a clean IEx, we can copy and paste both the <tt>MyFancyProcess</tt> and <tt>MyFancySupervisor</tt> above and start playing with it in the IEx shell:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span><span class="o">&gt;</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">sup_pid</span><span class="p">}</span> <span class="o">=</span> <span class="no">MyFancySupervisor</span><span class="o">.</span><span class="n">start_link</span>   
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="c1">#PID&lt;0.124.0&gt;}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">hello</span><span class="p">(</span><span class="s2">&#34;foo&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="no">Hello</span><span class="p">,</span> <span class="n">foo</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>     
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">MyFancyProcess</span><span class="o">.</span><span class="n">counter</span>
</span></span><span class="line"><span class="cl"><span class="no">New</span> <span class="n">state</span> <span class="n">is</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="ss">:noproc</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>This is how we start the Supervisor and you can see that right away we can start sending messages to the <tt>MyFancyProcess</tt> GenServer because the Supervisor successfully started it for us.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Supervisor</span><span class="o">.</span><span class="n">count_children</span><span class="p">(</span><span class="n">sup_pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="sx">%{active: 1, specs: 1, supervisors: 0, workers: 1}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Supervisor</span><span class="o">.</span><span class="n">which_children</span><span class="p">(</span><span class="n">sup_pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="p">{</span><span class="no">MyFancyProcess</span><span class="p">,</span> <span class="c1">#PID&lt;0.125.0&gt;, :worker, [MyFancyProcess]}]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Using the Supervisor PID that we captured right when we started it, we can ask it to count how many children it is monitoring (1, in this example) and we can ask for the details of each child as well. We can see that the <tt>MyFancyProcess</tt> started with the pid of &ldquo;0.125.0&rdquo;</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span><span class="o">&gt;</span> <span class="o">[</span><span class="p">{</span><span class="n">_</span><span class="p">,</span> <span class="n">worker_pid</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">}</span><span class="o">]</span> <span class="o">=</span> <span class="no">Supervisor</span><span class="o">.</span><span class="n">which_children</span><span class="p">(</span><span class="n">sup_pid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span><span class="p">{</span><span class="no">MyFancyProcess</span><span class="p">,</span> <span class="c1">#PID&lt;0.125.0&gt;, :worker, [MyFancyProcess]}]</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span><span class="o">&gt;</span> <span class="no">Process</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">worker_pid</span><span class="p">,</span> <span class="ss">:kill</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kp">true</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, we can grab the Worker pid and manually force it to crash as we did before. We should be screwed, right? Nope:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>iex(15)&gt; Supervisor.which_children(sup_pid)                          
[{MyFancyProcess, #PID&lt;0.139.0&gt;, :worker, [MyFancyProcess]}]

iex(16)&gt; MyFancyProcess.counter
New state is 1
:noproc
iex(17)&gt; MyFancyProcess.counter
New state is 2
:noproc</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If we ask the Supervisor again for the list of its children, we will see that the old &ldquo;0.125.0&rdquo; process did indeed vanish but a new one, &ldquo;0.139.0&rdquo;, was spawned in its place by the Supervisor&rsquo;s <tt>:one_for_one</tt> strategy as we defined before.</p>
<p>We can continue making calls to <tt>MyFancyProcess</tt>, but you will see that the previous state was lost and it restarts from zero. We can add state management in the GenServer using a number of different persistent storages such as the built-in <a href="http://elixir-lang.org/getting-started/mix-otp/ets.html"target="_blank" rel="noopener">ETS</a> (think of ETS as a built-in Memcache service), but I think you get the idea by now.</p>
<h3>Graphically visualizing Processes<span class="hx:absolute hx:-mt-20" id="graphically-visualizing-processes"></span>
    <a href="#graphically-visualizing-processes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This entire article was motivated by just this simple thing in Benjamin&rsquo;s book: by the end of page 139 you will have built a very simple pool system that is able to start 5 processes in the pool, guarded by a supervisor. And from there he goes on to show off the Observer.</p>
<p>Erlang has a built-in inspector tool called Observer. You can use the Supervisor built-in functions to inspect processes as I demonstrated before, but it&rsquo;s much cooler to see it visually. Assuming you installed Erlang Solutions properly, in Ubuntu you have to:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb &amp;&amp; sudo dpkg -i erlang-solutions_1.0_all.deb</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Only then, you can start the observer directly from the IEx shell like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>:observer.start</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And a graphical window will show up with some stats first.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/530/big_observer-system.png" alt="Observer"  loading="lazy" /></p>
<p>This is <strong>very</strong> powerful because you have insight into and control over the entire Erlang runtime! Notice that this status window even shows you &ldquo;uptime&rdquo;; it&rsquo;s like a UNIX system: it is made to stay up no matter what. Processes have their own garbage collector and they all behave nicely towards the entire system.</p>
<p>You can hook a remote Observer to remote Erlang runtimes as well, if you were wondering. Now you can jump to the Applications tab to see what the &ldquo;Pooly&rdquo; exercise looks like with 5 children under its pool:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/527/big_observer-pooly.png" alt="Pooly"  loading="lazy" /></p>
<p>Because those children are supervised with proper restart strategies, we can visually kill one of them, the one with the pid labeled &ldquo;0.389.0&rdquo;:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/528/big_observer-kill.png" alt="Kill process"  loading="lazy" /></p>
<p>And as the Observer immediately shows, the Supervisor took action, spawned a new child and added it to its pool, bringing the count back to 5:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/529/big_observer-restart.png" alt="Respawn"  loading="lazy" /></p>
<p>This is what <strong>Fault Tolerance</strong> with proper controls means when using OTP!</p>
<p>With the bits I explained in this article you should have enough concepts to finally grasp what all the Erlang high-reliability fuss is about. The basic concepts are very simple, hooking your application into OTP is also a no-brainer, and what OTP has implemented under the hood is what makes your application much more reliable.</p>
<p>There are clear guidelines on how to design your application. Who supervises what. What should happen to the application state if workers are restarted? How do you divide responsibilities between different groups of Supervisors/Children?</p>
<p>Your application is supposed to look like a Tree, a <strong>Supervision Tree</strong>, where failure in one leaf does not bring the other branches down and everything knows how to behave and how to recover, elegantly. It&rsquo;s really like a UNIX operating system: when you <tt>kill -9</tt> one process, it doesn&rsquo;t bring your system down, and if it&rsquo;s an <tt>initd</tt>-monitored service, it gets respawned.</p>
<p>Most importantly: this is not an optional feature, a 3rd-party library that you choose to use. It&rsquo;s built into Erlang; you must use it if you want to play. There is no other choice and this is the best choice. Any such pattern that is not implemented in a concurrent language, to me, represents a big failure of the language. This is Elixir&rsquo;s strength.</p>
<p>This is high-level control you won&rsquo;t find anywhere else. And we still didn&rsquo;t even talk about how OTP applications can exchange messages across the wire in really distributed systems, and how the Erlang runtime can reload code while an application is running, with zero downtime, akin to what IEx itself is capable of and how Phoenix allows development mode with code reloading! OTP gives all this for free, so it&rsquo;s well worth learning all the details.</p>
<p>We went through processes, pids, sending a kill message to a process, trapping exits, parents having child processes. It feels very similar to how UNIX works. If you know UNIX, you can easily grasp how all this fits together, including Elixir&rsquo;s pipe operator &ldquo;|&gt;&rdquo; compared to UNIX&rsquo;s own pipe &ldquo;|&rdquo;; it&rsquo;s similar.</p>
<p>Finally, The Little Elixir &amp; OTP Guidebook is a very easy-to-read, very hands-on small book. You can read it all in a couple of days and grasp everything I quickly summarized here and much more. I highly encourage you to buy it right now.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>distributed-systems</category><category>software-engineering</category></item><item><title>Ex Manga Downloadr - Part 2: Poolboy to the rescue!</title><link>https://akitaonrails.github.io/en/2015/11/19/ex-manga-downloadr-part-2-poolboy-to-the-rescue/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2015/11/19/ex-manga-downloadr-part-2-poolboy-to-the-rescue/</guid><pubDate>Thu, 19 Nov 2015 14:35:00 -0200</pubDate><description>&lt;p&gt;If you read my &lt;a href="http://www.akitaonrails.com/en/2015/11/18/ex-manga-downloadr-um-exercicio-com-elixir"target="_blank" rel="noopener"&gt;previous article&lt;/a&gt; I briefly described my exercise building a MangaReader downloader. If you didn&amp;rsquo;t read it yet, I recommend you do so before continuing.&lt;/p&gt;
&lt;p&gt;In the mid-section of the article I described how I was still puzzled about what would be the best way to handle several HTTP requests to an unstable external source where I can&amp;rsquo;t control timeout or other network problems.&lt;/p&gt;
&lt;p&gt;A big Manga such as Naruto or Bleach has dozens of chapters with dozens of pages each, accounting for thousands of necessary HTTP requests. Elixir/Erlang allow me to fire up as many parallel HTTP requests as I want. But doing that makes the HTTP requests time out very quickly (it&amp;rsquo;s like doing a distributed denial of service attack against MangaReader).&lt;/p&gt;</description><content:encoded><![CDATA[<p>If you read my <a href="http://www.akitaonrails.com/en/2015/11/18/ex-manga-downloadr-um-exercicio-com-elixir"target="_blank" rel="noopener">previous article</a> I briefly described my exercise building a MangaReader downloader. If you didn&rsquo;t read it yet, I recommend you do so before continuing.</p>
<p>In the mid-section of the article I described how I was still puzzled about what would be the best way to handle several HTTP requests to an unstable external source where I can&rsquo;t control timeout or other network problems.</p>
<p>A big Manga such as Naruto or Bleach has dozens of chapters with dozens of pages each, accounting for thousands of necessary HTTP requests. Elixir/Erlang allow me to fire up as many parallel HTTP requests as I want. But doing that makes the HTTP requests time out very quickly (it&rsquo;s like doing a distributed denial of service attack against MangaReader).</p>
<p>By trial and error I found that firing up fewer than 100 HTTP requests at once allows me to finish. I capped it at 80 to be sure, but it really depends on your environment.</p>
<p>Then I had to manually chunk my list of pages into groups of 80 elements and process them in parallel, finally reducing the resulting lists back into a larger list to pass through to the next steps in the Workflow. The code gets convoluted like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">images_sources</span><span class="p">(</span><span class="n">pages_list</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pages_list</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="n">chunk</span><span class="p">(</span><span class="vi">@maximum_fetches</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">fn</span> <span class="n">pages_chunk</span><span class="p">,</span> <span class="n">acc</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="n">result</span> <span class="o">=</span> <span class="n">pages_chunk</span>
</span></span><span class="line"><span class="cl">        <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="no">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">Page</span><span class="o">.</span><span class="n">image</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">)</span> <span class="k">end</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">        <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="vi">@http_timeout</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">        <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">image</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">image</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">acc</span> <span class="o">++</span> <span class="n">result</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now I was able to reimplement this aspect and the same code now looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">images_sources</span><span class="p">(</span><span class="n">pages_list</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">pages_list</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Worker</span><span class="o">.</span><span class="n">page_image</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="vi">@await_timeout_ms</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">image</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">image</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Wow! Now this is a big improvement and it&rsquo;s way more obvious what it&rsquo;s doing.</p>
<p>Best of all: downloading a Manga the size of Akira (around 2,200 pages long) took less than <strong>50 seconds</strong>. And this is not because Elixir is super fast, it&rsquo;s because MangaReader can&rsquo;t keep up if I extend the Pool size. It&rsquo;s hitting at a constant rate of 50 concurrent connections!</p>
<p>This makes my 4-core machine, sitting on a 40 Mbps connection, use less than 30%-40% of CPU and no more than around 3.5 Mbps. If MangaReader could keep up we could easily fetch all pages 2 or 3 times faster without breaking a sweat.</p>
<p>It was fast with the previous strategy, but I guess it got at least twice as fast as a bonus now. But how did I accomplish that?</p>
<h3>Open Telecom Platform<span class="hx:absolute hx:-mt-20" id="open-telecom-platform"></span>
    <a href="#open-telecom-platform" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>In the previous article I also said that I didn&rsquo;t want to dive into &ldquo;OTP and GenServers&rdquo; just yet. But if you&rsquo;re a beginner like me you probably didn&rsquo;t understand what this means.</p>
<p>OTP is what makes Erlang (and consequently, Elixir) different from pretty much every other language platform but, maybe, Java.</p>
<p>Many new languages today let you do many tasks in parallel through convoluted Reactor patterns (Node.js, EventMachine/Ruby, Tornado/Twisted/Python, etc) or through (cleaner) green threads (Scala, Go).</p>
<p>But none of this matters. It&rsquo;s not difficult to launch millions of lightweight processes, but it is not trivial to actually <strong>CONTROL</strong> them all. It doesn&rsquo;t matter how fast you can exhaust your hardware if you can&rsquo;t control it. You end up with millions of dumb minions wreaking havoc without an adult to coordinate them.</p>
<p>Erlang solved this problem decades ago through the development of OTP, initially called Open Systems, within Ericsson in 1995. By itself OTP is a subject that can easily fill an entire fat book and you will still not be able to call yourself an expert.</p>
<p>Just so I don&rsquo;t get too boring here:</p>
<ul>
<li>Start with <a href="http://learnyousomeerlang.com/what-is-otp"target="_blank" rel="noopener">this very brief summary</a>;</li>
<li>Then read <a href="http://elixir-lang.org/getting-started/mix-otp/supervisor-and-application.html"target="_blank" rel="noopener">Elixir&rsquo;s tutorial on Supervisors</a> and the page on <a href="http://elixir-lang.org/getting-started/processes.html"target="_blank" rel="noopener">Processes</a> first if you haven&rsquo;t already;</li>
<li>Finally, you can go further with <a href="https://www.manning.com/books/the-little-elixir-and-otp-guidebook"target="_blank" rel="noopener">The Elixir &amp; OTP Guidebook</a> from Manning Publications.</li>
</ul>
<p>Now, below is my personal point of view. As I am a beginner myself, let me know in the comments section below if I got some concept wrong.</p>
<p>OTP is a collection of technologies and frameworks. The part that interests us the most is to understand that this is a sophisticated collection of patterns to achieve the Nirvana of highly reliable, highly scalable, distributed systems. You know? That thing every new platform promises you but fails to actually deliver.</p>
<p>For our very simple intents and purposes, let&rsquo;s pick up where I said before: it&rsquo;s trivial to fire up millions of small processes. We call them &ldquo;workers&rdquo;. OTP provides the means to control them: Supervisors. And then it also provides the concept of Supervisor Trees (Supervisors that supervise other Supervisors). This is the gist of it.</p>
<p>Supervisors are responsible for starting up the workers and also for recovering from exceptions coming from the workers (which is why in Erlang/Elixir we don&rsquo;t do ugly try/catch stuff: let the error be raised and caught by the Supervisor). Then we can configure the Supervisor to deal with a faulty worker by, for example, restarting it.</p>
<p>We already touched on this OTP stuff before. An Elixir <a href="https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/task.ex"target="_blank" rel="noopener">Task</a> is just a high-level abstraction. It internally starts its own <a href="https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/task/supervisor.ex"target="_blank" rel="noopener">supervisor</a> to watch over asynchronous tasks.</p>
<p>There are so many subjects and details that it&rsquo;s difficult to even get started. One concept that is important to know is about <strong>state</strong>. There is no global state! (Yay, no JavaScript globals nightmare.) Each function has its own state and that&rsquo;s it. There is no concept of an &ldquo;object&rdquo; that holds state and then methods that can modify that state.</p>
<p>But there is the concept of Erlang processes. Now, a process does have state, it&rsquo;s a lightweight piece of state that exists only at runtime. To execute a function in a separate, parallel process, you can just do:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="o">&gt;</span> <span class="n">spawn</span> <span class="n">fn</span> <span class="o">-&gt;</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="c1">#PID&lt;0.43.0&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Unlike an object, a process does not have a set of methods that access its inner &ldquo;this&rdquo; or &ldquo;self&rdquo; state. Instead each process has a <strong>mailbox</strong>. When you start (or &ldquo;spawn&rdquo; in Erlang lingo) a new process, it returns a pid (process ID). You can now send messages to the process through its pid. Each process has a mailbox and you can choose to respond to incoming messages and send responses back to the pid that sent the message. This is how you can send a message to the IEx console and receive the messages in its mailbox:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">iex</span><span class="o">&gt;</span> <span class="nb">send</span> <span class="nb">self</span><span class="p">(),</span> <span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="s2">&#34;world&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="s2">&#34;world&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">iex</span><span class="o">&gt;</span> <span class="n">receive</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl"><span class="o">...&gt;</span>   <span class="p">{</span><span class="ss">:hello</span><span class="p">,</span> <span class="n">msg</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">msg</span>
</span></span><span class="line"><span class="cl"><span class="o">...&gt;</span>   <span class="p">{</span><span class="ss">:world</span><span class="p">,</span> <span class="n">msg</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="s2">&#34;won&#39;t match&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">...&gt;</span> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;world&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In essence, it&rsquo;s almost like an &ldquo;object&rdquo; that holds state. Each process has its own garbage collector, so when it dies it&rsquo;s individually collected. And each process is isolated from other processes; they don&rsquo;t bleed state out, which makes them much easier to reason about.</p>
<p>The <a href="http://elixir-lang.org/getting-started/processes.html"target="_blank" rel="noopener">Getting Started page on Processes</a> from the Elixir website shows examples of what I just explained and I recommend you follow it thoroughly.</p>
<p>In summary, a process can hold internal state by blocking indefinitely while waiting for an incoming message in its mailbox and then recursing into itself! This is a mind-blowing concept at first.</p>
<p>But a simple process is just too damn weak. This is where you get <a href="http://elixir-lang.org/getting-started/mix-otp/genserver.html"target="_blank" rel="noopener">OTP&rsquo;s GenServer</a>, which is a much more accomplished process. OTP exposes Behaviours for you to implement in order to add your own code but it takes care of the dirty infrastructure stuff so you don&rsquo;t have to.</p>
<h3>Deferring the Heavy Workflow calls to a Worker<span class="hx:absolute hx:-mt-20" id="deferring-the-heavy-workflow-calls-to-a-worker"></span>
    <a href="#deferring-the-heavy-workflow-calls-to-a-worker" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>All that said, we know that in the Workflow we implemented before, we have trouble with the <tt>Page.image/1</tt> and <tt>Workflow.download_image/2</tt> functions. This is why we made them asynchronous processes and we wait for batches of 80 calls every time.</p>
<p>Now, let&rsquo;s start by moving this logic away to a GenServer Worker, for example, in the <tt>ex_manga_downloadr/pool_management/worker.ex</tt> file:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">PoolManagement</span><span class="o">.</span><span class="n">Worker</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">GenServer</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">ExMangaDownloadr</span><span class="o">.</span><span class="n">MangaReader</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="vi">@timeout_ms</span> <span class="mi">1_000_000</span>
</span></span><span class="line"><span class="cl">  <span class="vi">@transaction_timeout_ms</span> <span class="mi">1_000_000</span> <span class="c1"># larger just to be safe</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">GenServer</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span><span class="ss">:page_image</span><span class="p">,</span> <span class="n">page_link</span><span class="p">},</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="no">Page</span><span class="o">.</span><span class="n">image</span><span class="p">(</span><span class="n">page_link</span><span class="p">),</span> <span class="n">state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">handle_call</span><span class="p">({</span><span class="ss">:page_download_image</span><span class="p">,</span> <span class="n">image_data</span><span class="p">,</span> <span class="n">directory</span><span class="p">},</span> <span class="n">_from</span><span class="p">,</span> <span class="n">state</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:reply</span><span class="p">,</span> <span class="no">Page</span><span class="o">.</span><span class="n">download_image</span><span class="p">(</span><span class="n">image_data</span><span class="p">,</span> <span class="n">directory</span><span class="p">),</span> <span class="n">state</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>I first moved the <tt>Workflow.download_image/2</tt> to <tt>Page.download_image/2</tt> just for consistency&rsquo;s sake. But this is a GenServer in a nutshell. We have some setup in the <tt>start_link/1</tt> function and then we have to implement <tt>handle_call/3</tt> functions to handle each kind of argument it might receive. We separate them through pattern matching the arguments.</p>
<p>As a convention, we can add public functions that are just prettier versions that call each <tt>handle_call/3</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">page_image</span><span class="p">(</span><span class="n">page_link</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Task</span><span class="o">.</span><span class="n">async</span> <span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:poolboy</span><span class="o">.</span><span class="n">transaction</span> <span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">{</span><span class="ss">:page_image</span><span class="p">,</span> <span class="n">page_link</span><span class="p">},</span> <span class="vi">@timeout_ms</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span><span class="p">,</span> <span class="vi">@transaction_timeout_ms</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">page_download_image</span><span class="p">(</span><span class="n">image_data</span><span class="p">,</span> <span class="n">directory</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Task</span><span class="o">.</span><span class="n">async</span> <span class="n">fn</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:poolboy</span><span class="o">.</span><span class="n">transaction</span> <span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">fn</span><span class="p">(</span><span class="n">server</span><span class="p">)</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="no">GenServer</span><span class="o">.</span><span class="n">call</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="p">{</span><span class="ss">:page_download_image</span><span class="p">,</span> <span class="n">image_data</span><span class="p">,</span> <span class="n">directory</span><span class="p">},</span> <span class="vi">@timeout_ms</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span><span class="p">,</span> <span class="vi">@transaction_timeout_ms</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But we are not just calling the previous <tt>handle_call/3</tt> functions. First there is the <tt>Task.async/1</tt> we were already using in the Workflow functions to make the parallel batches. But inside the Task calls there is this other strange thing: <tt>:poolboy</tt>.</p>
<h3>Controlling Pools of Processes through Poolboy<span class="hx:absolute hx:-mt-20" id="controlling-pools-of-processes-through-poolboy"></span>
    <a href="#controlling-pools-of-processes-through-poolboy" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>The entire OTP ordeal I wrote here was just an introduction so I could show off <a href="https://github.com/devinus/poolboy"target="_blank" rel="noopener">Poolboy</a>.</p>
<p>Repeating myself again: it&rsquo;s trivial to fire up millions of processes. OTP is how we control failures to those processes. But there is another problem: the computation within each process may be so heavy we can either bring down the machine or, in our case, do a Distributed Denial of Service (DDoS) against the poor MangaReader website.</p>
<p>My initial idea was to just do parallel requests in batches. But the logic is convoluted.</p>
<p>Instead, we can use a process pool! It queues up our requests for new processes. Whenever a process finishes, it is returned to the pool and a new computation can take over the available process. This is how pools work (you probably have an intuition of how it works from traditional database connection pools). Pools and queues are useful software constructs to deal with limited resources.</p>
<p>By doing this we can remove the chunking of the large list into batches and process every element of the large list in parallel at once, repeating again the initial version:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">pages_list</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="n">chunk</span><span class="p">(</span><span class="vi">@maximum_fetches</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="o">[]</span><span class="p">,</span> <span class="n">fn</span> <span class="n">pages_chunk</span><span class="p">,</span> <span class="n">acc</span> <span class="o">-&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">pages_chunk</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="no">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">Page</span><span class="o">.</span><span class="n">image</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">)</span> <span class="k">end</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="vi">@http_timeout</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">      <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">image</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">image</span> <span class="k">end</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">acc</span> <span class="o">++</span> <span class="n">result</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, removing the chunking and reducing logic:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">pages_list</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="no">Task</span><span class="o">.</span><span class="n">async</span><span class="p">(</span><span class="n">fn</span> <span class="o">-&gt;</span> <span class="no">Page</span><span class="o">.</span><span class="n">image</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">)</span> <span class="k">end</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="p">(</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="vi">@http_timeout</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">image</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">image</span> <span class="k">end</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And finally, replacing the direct <tt>Task.async/1</tt> call with the GenServer worker we just implemented above:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">pages_list</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Worker</span><span class="o">.</span><span class="n">page_image</span><span class="o">/</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="no">Task</span><span class="o">.</span><span class="n">await</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">,</span> <span class="vi">@await_timeout_ms</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="o">|&gt;</span> <span class="no">Enum</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">fn</span> <span class="p">{</span><span class="ss">:ok</span><span class="p">,</span> <span class="n">image</span><span class="p">}</span> <span class="o">-&gt;</span> <span class="n">image</span> <span class="k">end</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now, Poolboy will require a Supervisor that monitors our Worker. Let&rsquo;s put it under <tt>ex_manga_downloadr/pool_management/supervisor.ex</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">PoolManagement</span><span class="o">.</span><span class="n">Supervisor</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Supervisor</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start_link</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">Supervisor</span><span class="o">.</span><span class="n">start_link</span><span class="p">(</span><span class="n">__MODULE__</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">init</span><span class="p">(</span><span class="o">[]</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">pool_options</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="nb">name</span><span class="p">:</span> <span class="p">{</span><span class="ss">:local</span><span class="p">,</span> <span class="ss">:worker_pool</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="ss">worker_module</span><span class="p">:</span> <span class="no">PoolManagement</span><span class="o">.</span><span class="n">Worker</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">size</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="ss">max_overflow</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">children</span> <span class="o">=</span> <span class="o">[</span>
</span></span><span class="line"><span class="cl">      <span class="ss">:poolboy</span><span class="o">.</span><span class="n">child_spec</span><span class="p">(</span><span class="ss">:worker_pool</span><span class="p">,</span> <span class="n">pool_options</span><span class="p">,</span> <span class="o">[]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">supervise</span><span class="p">(</span><span class="n">children</span><span class="p">,</span> <span class="ss">strategy</span><span class="p">:</span> <span class="ss">:one_for_one</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>More OTP goodness here. We had a rogue Worker, now we have a responsible Supervisor deferring responsibility to Poolboy. We start with a pool that can hold a maximum of 50 processes within (without overflowing). This number comes from trial and error again. And the Supervisor will use a strategy of <tt>:one_for_one</tt>, which means that if the Worker dies it restarts it.</p>
<p>Now, we must add Poolboy to the <tt>mix.exs</tt> as a dependency and run <tt>mix deps.get</tt> to fetch it:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defp</span> <span class="n">deps</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="ss">:poolboy</span><span class="p">,</span> <span class="ss">github</span><span class="p">:</span> <span class="s2">&#34;devinus/poolboy&#34;</span><span class="p">,</span> <span class="ss">tag</span><span class="p">:</span> <span class="s2">&#34;1.5.1&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In the same <tt>mix.exs</tt> we make the main Application (surprise: which is <a href="https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/application.ex"target="_blank" rel="noopener">already a supervised OTP application</a>) start the PoolManagement.Supervisor for us:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">application</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">[</span><span class="ss">applications</span><span class="p">:</span> <span class="o">[</span><span class="ss">:logger</span><span class="p">,</span> <span class="ss">:httpotion</span><span class="p">,</span> <span class="ss">:porcelain</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="ss">mod</span><span class="p">:</span> <span class="p">{</span><span class="no">PoolManagement</span><span class="p">,</span> <span class="o">[]</span><span class="p">}</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>But we also need to have this <tt>PoolManagement</tt> module for it to call. We may call it <tt>pool_management.ex</tt>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">defmodule</span> <span class="no">PoolManagement</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">use</span> <span class="no">Application</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">start</span><span class="p">(</span><span class="n">_type</span><span class="p">,</span> <span class="n">_args</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="no">PoolManagement</span><span class="o">.</span><span class="n">Supervisor</span><span class="o">.</span><span class="n">start_link</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>Summary<span class="hx:absolute hx:-mt-20" id="summary"></span>
    <a href="#summary" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Let&rsquo;s summarize:</p>
<ol>
<li>the <tt>ExMangaDownloadr</tt> application will start up and fire up the <tt>PoolManagement</tt> application;</li>
<li>the <tt>PoolManagement</tt> application fires up the <tt>PoolManagement.Supervisor</tt>;</li>
<li>the <tt>PoolManagement.Supervisor</tt> fires up Poolboy and assigns <tt>PoolManagement.Worker</tt> to it, configuring its pool size to 50 and responding to the pool name <tt>:worker_pool</tt>;</li>
<li>now we start to fetch and parse through the Manga&rsquo;s chapters and pages until <tt>ExMangaDownloadr.Workflow.images_sources/1</tt> is called;</li>
<li>it will call the <tt>PoolManagement.Worker.page_image/1</tt> function which in turn fires up a <tt>Task.async/1</tt>, calling <tt>:poolboy.transaction(:worker_pool, fn -&gt; &hellip; end)</tt>;</li>
<li>if a process is available in Poolboy&rsquo;s pool it starts right away; otherwise it waits for a process to become available, up to the <tt>@transaction_timeout_ms</tt> timeout configuration.</li>
<li>the process maps the entire <tt>pages_list</tt>, creating one async Task for each page in the list. We end up with a ginormous list of Task pids, then we <tt>Task.await/2</tt> for them all to return.</li>
</ol>
<p>Now, this application is much more reliable and faster as it fires up a new HTTP connection as soon as the first one responds and it returns the process back to the pool. Instead of firing up 80 connections at a time, in batches, we start with 50 at the same time and then we fire up one at a time for each process returned to the pool.</p>
<p>Through trial and error I set the <tt>@http_timeout</tt> to wait at most 60 seconds. I also set the <tt>timeout_ms</tt>, which is the time to wait for the GenServer worker call handler to return, and <tt>transaction_timeout_ms</tt>, which is the time Poolboy waits for a new process in the pool, both to around 16 minutes (1,000,000 ms).</p>
<p>This is putting 25 years of Erlang experience in the Telecom industry to good use!</p>
<p>And to make it crystal clear: OTP is the thing that sets Erlang/Elixir apart from all the rest. It&rsquo;s not the same thing, but it&rsquo;s like if the standard were to write everything in Java as an EJB, ready to run inside a JEE Container. What comes to mind is: heavy.</p>
<p>In Erlang, an OTP application is lightweight, you can just build and use it ad hoc, without bureaucracy, without having to set up complicated servers. As in our case, it&rsquo;s a very simple command line tool, and within it, the entire power of a JEE Container! Think about it.</p>
]]></content:encoded><category>elixir-and-erlang</category><category>performance</category><category>distributed-systems</category></item><item><title>Chatting with Blaine Cook (Twitter)</title><link>https://akitaonrails.github.io/en/2008/06/17/chatting-with-blaine-cook-twitter/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2008/06/17/chatting-with-blaine-cook-twitter/</guid><pubDate>Tue, 17 Jun 2008 00:29:00 -0300</pubDate><description>&lt;p&gt;Ruby on Rails is &lt;strong&gt;big&lt;/strong&gt;. Twitter is &lt;strong&gt;big&lt;/strong&gt;. And because of that they became easy targets for the media and the frustrated pundits wanting a few more pageviews. &amp;ldquo;Blaine Cook&amp;rdquo; was one of Twitter&amp;rsquo;s developers, and he kindly agreed to participate in one of my interviews. And, of course, he will answer the question &lt;em&gt;&amp;ldquo;Does Rails Scale?&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/hyku/2299866775/"target="_blank" rel="noopener"&gt;&lt;img src="http://s3.amazonaws.com/akitaonrails/assets/2008/6/17/2299866775_385f7af555.jpg" alt="" loading="lazy" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AkitaOnRails:&lt;/strong&gt; So, let me first explain that my main goal when I interview high profile people like you is to bring good information for our growing Rails community in Brazil. It is always good to ask about your background, how did you get into Rails and what was your experience with it.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Ruby on Rails is <strong>big</strong>. Twitter is <strong>big</strong>. And because of that they became easy targets for the media and the frustrated pundits wanting a few more pageviews. &ldquo;Blaine Cook&rdquo; was one of Twitter&rsquo;s developers, and he kindly agreed to participate in one of my interviews. And, of course, he will answer the question <em>&ldquo;Does Rails Scale?&rdquo;</em></p>
<p><a href="http://www.flickr.com/photos/hyku/2299866775/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/6/17/2299866775_385f7af555.jpg" alt=""  loading="lazy" /></a></p>
<p><strong>AkitaOnRails:</strong> So, let me first explain that my main goal when I interview high profile people like you is to bring good information for our growing Rails community in Brazil. It is always good to ask about your background, how did you get into Rails and what was your experience with it.</p>
<p><strong>Blaine Cook:</strong> I started with Rails on a small personal project back in December 2004, after Evan Henshaw-Plath (<a href="http://2008.xtech.org/public/schedule/speaker/283"target="_blank" rel="noopener">rabble</a>) got really excited about Ruby &amp; Rails.</p>
<p><a href="http://www.flickr.com/photos/foolswisdom/43021932/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/6/17/43021932_545efed570.jpg" alt=""  loading="lazy" /></a></p>
<p>Shortly afterwards, I started working on <a href="http://www.odeo.com"target="_blank" rel="noopener">Odeo</a>. I really like Rails, and I&rsquo;ve really enjoyed my experience with it. Building web-apps used to be a real pain (i.e., boring) because you ended up building the same pieces over and over again.</p>
<p>Rails fixed that. There were frameworks before, but Rails reframed the question in a humane way, and we see that in all the other frameworks that have been created afterwards.</p>
<p><strong>AkitaOnRails:</strong> So, what were you doing before Rails then? Enterprise Java? Microsoft stuff? Other open source projects?</p>
<p><strong>Blaine Cook:</strong> I mostly did PHP and Perl, in the context of open source projects and consulting jobs.</p>
<p><strong>AkitaOnRails:</strong> So, <strong>Odeo</strong> and <strong>Twitter</strong> are your biggest projects in Rails then? Before digging into Twitter, can you tell us how was your work for Odeo?</p>
<p><strong>Blaine Cook:</strong> Sure – Odeo was fine – the big problem there was with focus. Podcasting hadn&rsquo;t been proven (and the form that podcasting takes now is much different than what people envisioned in 2004 / 2005), so it was a struggle to gain traction for the product.</p>
<p>We had all sorts of cool things built, many of which are just becoming products now. Things like multi-track mixing on the web, video messaging (like <a href="http://www.seesmic.com/"target="_blank" rel="noopener">seesmic</a>), etc.</p>
<p><strong>AkitaOnRails:</strong> This is an interesting side-subject: podcasting. I do a <a href="http://podcast.rubyonrails.pro.br/"target="_blank" rel="noopener">Brazilian Rails podcast</a> with a friend and nowadays people are very acquainted with podcasts. What was the vision 3, 4 years ago about what podcasting would become?</p>
<p><strong>Blaine Cook:</strong> I think that&rsquo;s fairly similar, but it seems like most of the traction (i.e., listeners) for podcasting is established thru radio shows that are seeking a wider distribution.</p>
<p>I think there was a sentiment that podcasting would be much more popular amongst bloggers in general. Everyone was going to have a podcast.</p>
<p>But in reality it&rsquo;s a medium with far fewer publishers than blogging.</p>
<p><a href="http://www.flickr.com/photos/jolieani/2303816926/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/6/17/2303816926_beafddbb5c.jpg" alt=""  loading="lazy" /></a></p>
<p><strong>AkitaOnRails:</strong> You helped start Odeo, I mean were you one of the creators? Or were you hired to solve their problem? And why did you leave Odeo?</p>
<p><strong>Blaine Cook:</strong> No – I started at Odeo a few months after development had started. I never really left Odeo – the product was sold, and the company continued on as Obvious. Twitter was a side-project at Odeo.</p>
<p><strong>AkitaOnRails:</strong> Ok, and Twitter is big nowadays. Could you give us a glimpse of how the whole idea of <em>&ldquo;What are you doing&rdquo;</em> started? Did you think at that time that it would become such a success as it is now?</p>
<p><strong>Blaine Cook:</strong> Twitter was started as a side project at Odeo, but really it was an idea that <a href="http://www.stonetemple.com/articles/interview-jack-dorsey.shtml"target="_blank" rel="noopener">Jack</a> had been kicking around in various forms for several years.</p>
<p>He and <strong>Noah Glass</strong> (the founder of Odeo) created a small sub-company within Odeo to work on it, in spring 2006. Initially, I hated the idea, mostly because it was intended to be SMS-only.</p>
<p>It&rsquo;s hard to say looking back, but at the time I&rsquo;d say Twitter was a long shot; I think that its success had a lot to do with luck and openness.</p>
<p><strong>AkitaOnRails:</strong> So, maybe one of the biggest reasons for Twitter&rsquo;s success was also its Achilles&rsquo; heels? I mean the paramount API based access it has today?</p>
<p><strong>Blaine Cook:</strong> Yes, absolutely.</p>
<p><strong>AkitaOnRails:</strong> So, getting to the hairy stuff, it is quite obvious that <a href="http://www.techcrunch.com/2008/04/23/amateur-hour-over-at-twitter/"target="_blank" rel="noopener">Michael Arrington</a> dislikes Twitter, period. And especially he has something <a href="http://www.alleyinsider.com/2008/4/lead_architect_blaine_cook_out_at_twitter"target="_blank" rel="noopener">against you</a> personally, at least considering the tone of his articles on Techcrunch. Can you comment on that?</p>
<p><strong>Blaine Cook:</strong> Not really; I don&rsquo;t know why he has a grudge. It&rsquo;s mildly annoying, but in the end it makes Techcrunch look more like a gossip rag and less like a respectable news outlet, so it&rsquo;s his loss.</p>
<p><strong>AkitaOnRails:</strong> And not only that but it also seems that some tech related news media wants to correlate Twitter&rsquo;s instability with the fact that part of its front-end is powered by Ruby on Rails, hence the discussion <em>&ldquo;Does Rails scale?&rdquo;</em>. As a Railer this seems quite obvious that relating Rails and instability generates lots of pageviews. Do you think that&rsquo;s the case?</p>
<p><strong>Blaine Cook:</strong> I think that&rsquo;s at least part of it. Obviously page views are a huge draw.</p>
<p><strong>AkitaOnRails:</strong> Maybe it would be good for someone like you to explain to our audience the difference between <strong>&lsquo;performance&rsquo;</strong> and <strong>&lsquo;scalability&rsquo;</strong> as it seems that most people don&rsquo;t know the difference between the two.</p>
<p><strong>Blaine Cook:</strong> Performance and scalability are very different things. Performance is like the speed limit; it&rsquo;s a question of how quickly you can get from point A to point B. In this case, we&rsquo;re talking about how quickly you can get a page rendered and delivered to the user.</p>
<p>Scalability is a question of how many users you can handle. Highways intrinsically don&rsquo;t scale, for example, because when they&rsquo;re full, you get traffic jams. In web architectures, what we aim to provide are systems that will expand (usually by adding more hardware) to handle more traffic.</p>
<p>Obviously they&rsquo;re related – if you have a traffic jam, then the effective speed limit is lower than the theoretical limit. But increasing the speed limit won&rsquo;t make traffic jams any better.</p>
<p>There are a whole bunch of ways to make traffic less congested – adding more lanes to the highway, encouraging people to use public transit, or better yet encouraging people to work closer to home.</p>
<p>Likewise, there are many techniques for making web sites more scalable, and most of them don&rsquo;t involve making things much &ldquo;faster&rdquo;.</p>
<p><a href="http://www.flickr.com/photos/gavinbell/2476619970/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/6/17/2476619970_af72edc5ef.jpg" alt=""  loading="lazy" /></a></p>
<p><strong>AkitaOnRails:</strong> Great explanation. Twitter was involved in some not very good controversy since last year, and I think you were not involved in most of them, but maybe you could shed some light on those issues as some people don&rsquo;t follow every news story all the time. The first one being that interview where <a href="http://www.radicalbehavior.com/5-question-interview-with-twitter-developer-alex-payne/"target="_blank" rel="noopener">Alex Payne</a> complained about Rails not supporting multiple databases. <strong>David Hansson</strong> ranted about it. Then <a href="http://drnicwilliams.com/2007/04/12/magic-multi-connections-a-facility-in-rails-to-talk-to-more-than-one-database-at-a-time/"target="_blank" rel="noopener">Dr. Nic</a> came to the beginning of a solution. Can you explain this matter for our audience?</p>
<p><strong>Blaine Cook:</strong> Sure – it&rsquo;s true that Rails doesn&rsquo;t support multiple databases out of the box, but this is easily fixed. I think the interview with Alex didn&rsquo;t get into the details very well, and mis-represented our challenges.</p>
<p>If it was just a matter of supporting multiple database connections, that&rsquo;s something that&rsquo;s easily solved. The real problem was much more complex, and had to do with custom sharding for our particular dataset.</p>
<p><strong>Eran Hammer-Lahav</strong> has an excellent series (parts <a href="http://www.hueniverse.com/hueniverse/2008/03/on-scaling-a-mi.html"target="_blank" rel="noopener">1</a>, <a href="http://www.hueniverse.com/hueniverse/2008/03/scaling-a-micro.html"target="_blank" rel="noopener">2</a>, <a href="http://www.hueniverse.com/hueniverse/2008/04/scaling-a-micro.html"target="_blank" rel="noopener">3</a>) in which he describes some of the challenges associated with building microblogging sites to scale.</p>
<p>None of the things he describes in the article are hard to do with Rails. In fact, I think that David (Hansson) is wrong when he says that Rails isn&rsquo;t flexible – it&rsquo;s actually much more flexible than most other frameworks.</p>
<p><strong>AkitaOnRails:</strong> That&rsquo;s right. And then the other recent controversy is, of course, because of <a href="http://www.techcrunch.com/2008/05/31/hey-twitter-i-have-a-few-questions-too/"target="_blank" rel="noopener">TechCrunch</a> again, where they state that Twitter only has like half a dozen servers and that&rsquo;s why it falls down frequently. Can you roughly describe how is Twitter&rsquo;s hardware architecture?</p>
<p><strong>Blaine Cook:</strong> Twitter has a lot of servers, definitely more than half a dozen. I can&rsquo;t go into more detail about the hardware setup, but I can say that the back end is an asynchronous messaging-based system. <a href="http://rubyforge.org/projects/starling/"target="_blank" rel="noopener">Starling</a> (the queue server that I wrote that Twitter released in January) is the mechanism used to pass messages between processes.</p>
<p>The biggest problem is that Twitter deals with many, many often expensive API requests due to the frequent polling required to stay up to date with your friends&rsquo; most recent tweets. The big challenge was to come up with an inexpensive way to make those expensive API requests cheap.</p>
<p>We used memcache extensively, and had a whole bunch of caching in place well over a year ago, but it wasn&rsquo;t enough.</p>
<p><strong>AkitaOnRails:</strong> Last year Alex and Britt spoke at RailsConf I think about <a href="http://www.slideshare.net/eraz/scaling-rails-presentation/"target="_blank" rel="noopener">Scaling Rails</a> where it felt like Twitter&rsquo;s problem would end soon. It was mostly about caching and optimizations like that, and one of the main messages was <em>&ldquo;cache the hell out of it&rdquo;</em>. I saw you speaking this year about database sharding and how different it is to architect for 1k people and for 1 million people and that you can&rsquo;t predict what to do before that. Twitter still goes down sometimes, so what changed between last year and now about Scaling Rails?</p>
<p><a href="http://www.flickr.com/photos/x180/2329021397/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/6/17/2329021397_3dde02123a.jpg" alt=""  loading="lazy" /></a></p>
<p><strong>Blaine Cook:</strong> Yes – we knew for a long time that database sharding was important, but for various reasons weren&rsquo;t able to move forward with it.</p>
<p>The thing with sharding is that until someone comes out with something like what Google has (and I don&rsquo;t mean Bigtable), the sort of shape your application takes before sharding is very different than the shape afterwards. Sharding makes doing some things harder, and is generally expensive.</p>
<p>When people say &ldquo;sharding&rdquo; or &ldquo;denormalization&rdquo;, what they mean is <em>&ldquo;duplicating data in particular ways to make certain lookups faster.&rdquo;</em></p>
<p>That always involves additional cost. If you&rsquo;re building a new app that will either never see hundreds of thousands of users, or if you don&rsquo;t know if it will or not, it&rsquo;s generally not worth sinking lots of money into hardware and engineering time to build a &ldquo;scalable&rdquo; architecture.</p>
<p>The reality is that most developers know SQL and can build CGI applications using PHP or Rails or whatever, but relatively few have experience taking those straightforward, normalized data sets and breaking them up into much more complex sharded systems.</p>
<p><strong>AkitaOnRails:</strong> Of course, sometimes people feel like they will actually build the &rsquo;next&rsquo; Twitter. I <a href="/2008/6/5/railsconf-2008-brazil-rails-podcast-special-edition">interviewed Ezra</a>, from Engine Yard, at RailsConf and he seems to acknowledge that the friends-of-friends graph is a problem so complex that whoever figures it out will make a ton of money I mean, isn&rsquo;t it the case that traditional SQL designs don&rsquo;t work all the time?</p>
<p><strong>Blaine Cook:</strong> Of course – the question is, what else are you going to use? Google has an architecture that lets them spend infinite amounts of money to build denormalized lists of everything, and access those lists quickly. There are a number of projects – HBase, Hypertable, CouchDB, and many others – that aim to solve these problems, but none of them are ready for production use.</p>
<p>The reality is that 10 years ago, people were building web applications with tcsh, and now we have better tools.</p>
<p><strong>AkitaOnRails:</strong> Where is Twitter hosted? For some reason I saw Engine Yard pitching a lot about scalability issues throughout the whole RailsConf this year. Do you think this whole &ldquo;Rails doesn&rsquo;t scale&rdquo; meme is making Railers worried about future prospects? Even though it was not supposed to scare that much as we do have higher traffic websites such as Scribd and YellowPages in the top of the list – though the architectures are world apart, of course. What do you think about this issue?</p>
<p><strong>Blaine Cook:</strong> I&rsquo;m not sure, but I think Twitter actually does more traffic than YellowPages or Scribd; the API is a real killer.</p>
<p><strong>AkitaOnRails:</strong> Ah, I see, Alexa and others don&rsquo;t account for API request, right?</p>
<p><strong>Blaine Cook:</strong> Twitter is hosted at NTT / Verio. I think there was a blog post about it in February. Right, Alexa doesn&rsquo;t account for API / SMS / XMPP traffic at all.</p>
<p>As far as the perspective that Rails doesn&rsquo;t scale – I think it&rsquo;s an unfortunate side-effect of Twitter&rsquo;s issues, to be sure, and I think some people worry about it. To put things into perspective, though, Friendster was written in Java to start, and switched to PHP. Myspace was written in ColdFusion and transitioned to ASP.NET.</p>
<p>When people run into problems scaling sites they often think that the language is the problem, but I think it&rsquo;s rarely the case.</p>
<p>Ruby is slower than PHP or Java (depending on which framework you use) or, well, most languages, but it&rsquo;s getting faster. And &ldquo;slower&rdquo; really only translates to additional cost.</p>
<p><strong>AkitaOnRails:</strong> Which reminds me of the last controversy which was <a href="http://www.techcrunch.com/2008/05/01/twitter-said-to-be-abandoning-ruby-on-rails/"target="_blank" rel="noopener">Twitter switching off from Rails</a>. The case is that you already have a mixed environment, right? Most pundits think they &lsquo;know&rsquo; how to solve Twitter&rsquo;s problems, though they have absolutely no idea what they are talking about. Some blame Rails, others blame MySQL (<em>&ldquo;go to Oracle&rdquo;</em>, they say). Can you elaborate on that?</p>
<p><strong>Blaine Cook:</strong> Right – the majority of our development was in Ruby / Rails, but there was code in other langauges. Python, Java, and there were some experiments with Scala. Which ignores the fact that we ran significant portions of our infrastructure on non-Ruby code – Apache, Mongrel, MySQL, memcached, ejabberd, etc. are all great applications and critical to building scalable sites.</p>
<p>Oracle definitely has some of these things figured out, but it&rsquo;s much easier to hire for MySQL, and Oracle licensing and support fees are nothing to scoff at.</p>
<p>Building your architecture around a system that will only get cheaper with time, rather than more expensive, is always a good idea.</p>
<p><strong>AkitaOnRails:</strong> And I have to ask, can you disclose some meaningful numbers, or orders of magnitude, such as tweets per day, average downtime, or something like this, just so we can have an idea of the size of Twitter&rsquo;s problems? Coming from someone that has actually had to deal with such a huge situation – which most people probably will never have to face -, what would you say are Ruby/Rails&rsquo; strongest features for you?</p>
<p><strong>Blaine Cook:</strong> Sadly, no – there are plenty of estimates on the web, most notably compete, and Dave Winer&rsquo;s fan-out numbers.</p>
<p>I think Ruby&rsquo;s best feature is that it&rsquo;s <strong>fun to develop</strong> with. It never stops being fun, either. It&rsquo;s an expressive and powerful language. Rails just makes web development painless, compared to doing everything by hand. That&rsquo;s something that shouldn&rsquo;t be underestimated, or whose value forgotten.</p>
<p>On top of that, they&rsquo;re really flexible. We needed to build a denormalization tool, for example, and doing it in Ruby was just as easy / hard as any other language, except when it came to tying it in to the rest of the system – no search and replace, no subclassing or anything. Just turn it on, and it works.</p>
<p><a href="http://www.flickr.com/photos/niallkennedy/440202833/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/6/17/440202833_77152b8080.jpg" alt=""  loading="lazy" /></a></p>
<p><strong>AkitaOnRails:</strong> Finally can you tell us why you left Twitter? What are your new endeavors? Twitter survived WWDC last week which is saying a lot considering such huge events would usually bring it down in the past.</p>
<p><strong>Blaine Cook:</strong> I&rsquo;m moving to the UK, and Twitter is / has moved into a position where their primary focus is reliability. When I left, the hope was that their system was stabilized, and the roadmap was clear as far as putting them in a better position overall. At the time, they&rsquo;d had an extended period of uninterrupted uptime, and had survived with flying colours <a href="http://sxsw.com/"target="_blank" rel="noopener">SXSW</a>. Unfortunately, it didn&rsquo;t last, but it looks like things are better again.</p>
<p>I&rsquo;m not yet ready to reveal what&rsquo;s next, but I have a number of exciting things going on. I&rsquo;ll continue maintaining Starling and <a href="http://code.google.com/p/xmpp4r-simple/"target="_blank" rel="noopener">xmpp4r-simple</a>, and I&rsquo;m still active with the OAuth spec. working group.</p>
<p><strong>AkitaOnRails:</strong> In the end, Twitter became almost part of our culture already (at least in our tech world). People complain when it is down because now they depend on it so much. People like Michael Arrington, Robert Scoble, Leo Laporte are always complaining, saying they would move to Pownce or Jaiku, but they are still around. Do you have any guess on why Twitter users are so loyal?</p>
<p><strong>Blaine Cook:</strong> Nope! I think the really rich landscape of tools that you can use to interact with Twitter on your own terms is a really big part of it. A lot of the users are personally invested, and they want to see that investment pay out.</p>
<p><strong>AkitaOnRails:</strong> Ok, I think this covers everything. Any closing remarks for our young Brazilian Rails community?</p>
<p><strong>Blaine Cook:</strong> The web is remarkably young, and there&rsquo;s plenty of space for really amazing things to happen. I&rsquo;m always excited to see what people are working on, and what&rsquo;s possible with this whole Internet thing. For all of Arrington&rsquo;s petty and misinformed comments, I think sites like Techcrunch and <a href="http://www.mashable.com"target="_blank" rel="noopener">Mashable</a> and <a href="http://www.alleyinsider.com"target="_blank" rel="noopener">Silicon Alley Insider</a> are interesting because they present a never ending stream of amazing things that people are building on the web.</p>
<p>The financial focus is unfortunate, as there are plenty of apps that survive and provide income for their founders without ever needing to scale to galactic proportions.</p>
<p>The bottom line is that there&rsquo;s plenty to do, and don&rsquo;t worry about scaling or business model or anything – unless you&rsquo;re passionate about what you&rsquo;re doing, none of it matters.</p>
<p>If you are passionate about what you&rsquo;re doing, there&rsquo;s a really good chance you&rsquo;ll figure the details out.</p>
<p><strong>AkitaOnRails:</strong> Fantastic! Thank you very much for your kindness, I am sure my audience will enjoy it. And just to let you know that we, Brazilians, <strong>do</strong> tweet a lot! :-) Thanks!</p>
<p><strong>Blaine Cook:</strong> Excellent, glad to hear! No problem, thanks for being a great interviewer.</p>
<p><a href="http://www.flickr.com/photos/briandewitt/2300104137/"target="_blank" rel="noopener"><img src="http://s3.amazonaws.com/akitaonrails/assets/2008/6/17/2300104137_96c83ac13d.jpg" alt=""  loading="lazy" /></a></p>
]]></content:encoded><category>interviews</category><category>performance</category><category>distributed-systems</category></item></channel></rss>