<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Edoardo Costantini]]></title><description><![CDATA[I am a Staff Software Engineer based in Rome, Italy.]]></description><link>https://edoardocostantini.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 02:06:35 GMT</lastBuildDate><atom:link href="https://edoardocostantini.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Mora Journal #0]]></title><description><![CDATA[Mora is my attempt at building a distributed event scheduler capable of notifying clients of scheduled events with sub-second precision.
Mora started a few months ago (sept. 2021) when I was hired by Prima where I had the chance to develop a personal...]]></description><link>https://edoardocostantini.dev/mora-journal-0</link><guid isPermaLink="true">https://edoardocostantini.dev/mora-journal-0</guid><category><![CDATA[Rust]]></category><category><![CDATA[distributed system]]></category><dc:creator><![CDATA[Edoardo Costantini]]></dc:creator><pubDate>Sun, 26 Feb 2023 14:33:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/lm0NmFbypcc/upload/ed4cbe137777ec0b9b4ede8222b1c044.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Mora</strong> is my attempt at building a distributed event scheduler capable of notifying clients of scheduled events with sub-second precision.</p>
<p><strong>Mora</strong> started a few months ago (sept. 2021) when I was hired by <a target="_blank" href="www.prima.it">Prima</a> where I had the chance to develop a personal project as an onboarding step, mainly to get confident with <em>Elixir</em>. I have to say <em>Elixir</em> grew on me pretty quickly, I was mesmerized by functional programming in general and the concurrency model of the language. I was able to make a very basic distributed scheduler in a matter of hours (~30). I then had to pause it for a few months due to personal circumstances. Fast forward six months and I just want to re-do everything in <strong><em>Rust</em></strong>. I think <strong>mora</strong> has to cover a lot of interesting topics to perform correctly, to name a few:</p>
<ul>
<li><p>Distributed transactions and storage</p>
</li>
<li><p>Clustering (with all its quirks such as clock synchronization)</p>
</li>
<li><p>Event scheduling</p>
</li>
<li><p>Client notification</p>
</li>
</ul>
<p>and while I don't see much potential in it as a commercial product I see it as an incredible learning tool and I would like to write my journey in these blog posts, to share them with others, but mainly to keep tracks of what I do.</p>
<h2 id="heading-why-rust">Why Rust?</h2>
<p>I had the pleasure of working with it at <strong>Prima</strong> in a real production environment. I was, once again, mesmerized, it conveys everything I was looking for in a language:</p>
<ul>
<li><p>Performance</p>
</li>
<li><p>Excellent Toolchain</p>
</li>
<li><p>Safety</p>
</li>
<li><p>TDD oriented</p>
</li>
</ul>
<p><strong>Mora</strong> to me has always been proving grounds for my programming endeavours, so I decided to reimplement everything in <strong>Rust</strong> to better appreciate the language and dive deep inside it. I'm going to try and document as much of it as possible, hence the mora journal (MJ).</p>
<h2 id="heading-implementing-a-priority-queue-in-rust">Implementing a priority queue in Rust</h2>
<p>Key aspect of <strong>mora</strong>'s internal engine is its priority queue structure that sorts scheduled events. Priority queues store items based on a priority key that sorts either in ascending or descending order. <code>pqueue</code>s in general are very useful whenever you have to enqueue items based on specific logic. Dequeueing from a <code>pqueue</code> means removing the highest priority from the queue.</p>
<p>We are going to start with a very dumb, inefficient, slow version of priority queue and work our way towards something more elaborate (eventually thread-safe).</p>
<p>:warning: I usually apply TDD whenever I code something and indeed I started from tests to implement this queue, nonetheless I feel like explaining things starting from tests it's not very blog-oriented. For blog's sake I will explain my design/code decision first and then show tests. :warning:</p>
<h3 id="heading-a-common-behavior-for-all-queues">A Common behavior for all queues</h3>
<p>First things first let's define a common trait our priority queues must implement in a dedicated <code>priority_queue.rs</code> file:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">PriorityQueue</span></span>&lt;K, V&gt; {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_empty</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">bool</span>;
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">len</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">usize</span>;
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">enqueue</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, key: K, value: V) -&gt; <span class="hljs-built_in">Option</span>&lt;V&gt;;
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">dequeue</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, count: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;V&gt;;
}
</code></pre>
<p>There's a lot to comment here, let's inspect line by line:</p>
<p>Here we are defining a public trait for our priority queue, a trait is something other structs can implement that makes it easy to code shared behaviors. Inside the <code>&lt;&gt;</code> we can see two generic types <code>K</code> (for key) <code>V</code> (for value). This means we can replace those types with whatever we want to store in the queue, such as <code>PriorityQueue&lt;u32,Foo&gt;</code>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">PriorityQueue</span></span>&lt;K, V&gt; {
</code></pre>
<p>This function should return whether our queue is empty or not, pretty straight forward. It will come in handy later on. <code>&amp;self</code> means that we need the reference of the struct that implements this trait. <code>-&gt; bool</code> means we will return a boolean value.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_empty</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">bool</span>;
</code></pre>
<p>Returns the amount of items currently in the queue in the form of a <code>usize</code> which is either a <code>u32</code> in 32 bit systems or a <code>u64</code> in 64 bit systems.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">len</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">usize</span>;
</code></pre>
<p>Here we have something new <code>&amp;mut</code> which means we need a mutable reference to our struct to invoke this function. We also return an <code>Option&lt;V&gt;</code> which means we will return either <code>Some(value)</code> or <code>None</code>. <code>Option</code>s are pretty useful and we will see later on why.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">enqueue</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, key: K, value: V) -&gt; <span class="hljs-built_in">Option</span>&lt;V&gt;;
</code></pre>
<p><code>enqueue</code>'s antagonist <code>dequeue</code> will take a mutable reference of our queue and dequeue as many values as <code>count</code> says. It return a <code>Vec</code>.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">dequeue</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, count: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;V&gt;;
</code></pre>
<p>Cool, we have defined a common behavior for our queues, now we need to implement a dumb <code>pqueue</code>.</p>
<h3 id="heading-a-dumb-priority-queue">A dumb priority queue</h3>
<p>Let's setup a very simple and easy to grasp priority queue. We are going to be using a simple hashmap as our data structure and we are going to implement our previously defined trait.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashMap;
<span class="hljs-keyword">use</span> std::hash::Hash;
<span class="hljs-keyword">use</span> crate::priority_queue::PriorityQueue;

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DumbPriorityQueue</span></span>&lt;K, V&gt;
<span class="hljs-keyword">where</span>
    K: <span class="hljs-built_in">Clone</span> + <span class="hljs-built_in">Eq</span> + Hash + <span class="hljs-built_in">Ord</span>,
    V: <span class="hljs-built_in">Clone</span>,
{
    map: HashMap&lt;K, V&gt;,
}
</code></pre>
<p>There's a couple things worth mentioning here:</p>
<ol>
<li><p>The first three lines of code are imports, we need to reference those structs and traits to be able to use them in our file.</p>
</li>
<li><p>the <code>where</code> clause means this struct needs to enforce a couple of rules to the relative types:</p>
<ul>
<li><p><code>K</code> must derive <code>Clone</code> (for cloning keys), <code>Eq</code> (for equality), <code>Hash</code> (for hashing and storing inside the map) and <code>Ord</code> (for sorting). Notice the <code>+</code> sign means all this restrictions must be applied at the same time.</p>
</li>
<li><p><code>V</code> just needs to be clonable.</p>
</li>
</ul>
</li>
</ol>
<p>Let's now implement our <code>PriorityQueue</code> trait:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span>&lt;K, V&gt; PriorityQueue&lt;K, V&gt; <span class="hljs-keyword">for</span> DumbPriorityQueue&lt;K, V&gt;
<span class="hljs-keyword">where</span>
    K: <span class="hljs-built_in">Clone</span> + <span class="hljs-built_in">Eq</span> + Hash + <span class="hljs-built_in">Ord</span>,
    V: <span class="hljs-built_in">Clone</span>,
{
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_empty</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">bool</span> {
        <span class="hljs-keyword">self</span>.map.is_empty()
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">len</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">usize</span> {
        <span class="hljs-keyword">self</span>.map.len()
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">enqueue</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, key: K, value: V) -&gt; <span class="hljs-built_in">Option</span>&lt;V&gt; {
        <span class="hljs-keyword">self</span>.map.insert(key, value)
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">dequeue</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, count: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;V&gt; {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> keys: <span class="hljs-built_in">Vec</span>&lt;K&gt; = <span class="hljs-keyword">self</span>.map.keys().cloned().collect::&lt;<span class="hljs-built_in">Vec</span>&lt;K&gt;&gt;();
        keys.sort();
        keys.iter()
            .take(count)
            .map(|k| <span class="hljs-keyword">self</span>.map.remove(k).unwrap())
            .collect::&lt;<span class="hljs-built_in">Vec</span>&lt;V&gt;&gt;()
    }
}
</code></pre>
<p>Here we can see syntax for implementing traits with generic types:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span>&lt;K, V&gt; PriorityQueue&lt;K, V&gt; <span class="hljs-keyword">for</span> DumbPriorityQueue&lt;K, V&gt;
</code></pre>
<p>This means that we are indeed implementing for <code>K</code> and <code>V</code> the trait <code>PriorityQueue</code> for the struct <code>DumbPriorityQueue</code>. We also need our <code>where</code> clauses for enforcing.</p>
<p>First three functions are pretty straight-forward so we won't cover them, let's see what <code>dequeue</code> does:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> keys: <span class="hljs-built_in">Vec</span>&lt;K&gt; = <span class="hljs-keyword">self</span>.map.keys().cloned().collect::&lt;<span class="hljs-built_in">Vec</span>&lt;K&gt;&gt;();
</code></pre>
<p>Here we clone all the map keys and collect them in a mutable Vec. We then sort our keys calling the <code>sort</code> function (provided by the <code>Ord</code> trait). As we stated earlier in this function we are holding a mutable reference to our struct, therefore we can indeed modify our internal map.</p>
<pre><code class="lang-rust">keys.iter()
    .take(count)
    .map(|k| <span class="hljs-keyword">self</span>.map.remove(k).unwrap())
    .collect::&lt;<span class="hljs-built_in">Vec</span>&lt;V&gt;&gt;()
</code></pre>
<p>With a bit of iterator magic we can iterate with <code>iter()</code>, <code>take</code> as many keys as <code>count</code> indicates, <code>map</code>ping those taken keys in values <code>remove</code>-ing them from the map at the same time and finally collect them in a <code>Vec&lt;V&gt;</code>.</p>
<h3 id="heading-testing-whitecheckmark">Testing :white_check_mark:</h3>
<p>That's a terrible priority queue, but how do we know if it works? Let's write some tests:</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[cfg(test)]</span>
<span class="hljs-keyword">mod</span> tests {
    <span class="hljs-keyword">use</span> super::*;

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new_queue_is_empty</span></span>() {
        <span class="hljs-keyword">let</span> pq = DumbPriorityQueue::&lt;<span class="hljs-built_in">u32</span>, <span class="hljs-built_in">u32</span>&gt;::default();
        <span class="hljs-built_in">assert!</span>(pq.is_empty())
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new_queue_has_zero_elements</span></span>() {
        <span class="hljs-keyword">let</span> pq = DumbPriorityQueue::&lt;<span class="hljs-built_in">u32</span>, <span class="hljs-built_in">u32</span>&gt;::default();
        <span class="hljs-built_in">assert_eq!</span>(pq.len(), <span class="hljs-number">0</span>)
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">enqueue_adds_element_to_queue</span></span>() {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> pq = DumbPriorityQueue::&lt;<span class="hljs-built_in">u32</span>, <span class="hljs-built_in">u32</span>&gt;::default();
        pq.enqueue(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
        <span class="hljs-built_in">assert_eq!</span>(pq.len(), <span class="hljs-number">1</span>);
        <span class="hljs-built_in">assert!</span>(!pq.is_empty());
    }

    <span class="hljs-meta">#[test]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">take_elments_returns_elements_ordered_by_key</span></span>() {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> pq = DumbPriorityQueue::&lt;<span class="hljs-built_in">u32</span>, <span class="hljs-built_in">u32</span>&gt;::default();
        pq.enqueue(<span class="hljs-number">4</span>, <span class="hljs-number">3</span>);
        pq.enqueue(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>);
        pq.enqueue(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
        pq.enqueue(<span class="hljs-number">3</span>, <span class="hljs-number">3</span>);
        <span class="hljs-keyword">let</span> values: <span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">u32</span>&gt; = pq.dequeue(<span class="hljs-number">3</span>);

        <span class="hljs-built_in">assert_eq!</span>(values.len(), <span class="hljs-number">3</span>);
        <span class="hljs-built_in">assert_eq!</span>(values, [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);
        <span class="hljs-built_in">assert_eq!</span>(pq.len(), <span class="hljs-number">1</span>)
    }
}
</code></pre>
<p>Rust tests are very easy to understand, we won't go into detail but the thing we're interested in is this:</p>
<pre><code class="lang-bash">running 4 tests
<span class="hljs-built_in">test</span> dumb_priority_queue::tests::new_queue_is_empty ... ok
<span class="hljs-built_in">test</span> dumb_priority_queue::tests::new_queue_has_zero_elements ... ok
<span class="hljs-built_in">test</span> dumb_priority_queue::tests::take_elments_returns_elements_ordered_by_key ... ok
<span class="hljs-built_in">test</span> dumb_priority_queue::tests::enqueue_adds_element_to_queue ... ok
<span class="hljs-built_in">test</span> result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished <span class="hljs-keyword">in</span> 0.00s
</code></pre>
<p>It works! Sure we could probably test other edge cases but for now this will have to do. If you are wondering why we built a priority queue that works on reverse (it yields items from lowest priority to higher) it will all make sense down the road.</p>
<h3 id="heading-benchmarking-bar-chart">Benchmarking :bar-chart:</h3>
<p>We said earlier that this implementation is dumb and not really performance-oriented, let's do some benchmarks so we can compare them with future queues we will develop.</p>
<p>For benchmarking I'll use <a target="_blank" href="http://criterion.rs">criterion.rs</a>, I'll spare you the details involved in setting <code>criterion</code> up since the library is very well documented, let's skip to our benchmarks directly.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> criterion::{black_box, criterion_group, criterion_main, Criterion};
<span class="hljs-keyword">use</span> mora_queue::{dumb_priority_queue::DumbPriorityQueue, priority_queue::PriorityQueue};

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">dumb_enqueue</span></span>(c: &amp;<span class="hljs-keyword">mut</span> Criterion) {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> pq = DumbPriorityQueue::&lt;<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>&gt;::default();
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> count = <span class="hljs-number">0</span>;
    c.bench_function(<span class="hljs-string">"dumb_enqueue"</span>, |b| {
        b.iter(|| {
            count = count + <span class="hljs-number">1</span>;
            pq.enqueue(black_box(count), black_box(-count))
        })
    });
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">dumb_dequeue</span></span>(c: &amp;<span class="hljs-keyword">mut</span> Criterion) {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> pq = DumbPriorityQueue::&lt;<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>&gt;::default();

    (<span class="hljs-number">0</span>..<span class="hljs-number">10000</span>).for_each(|x| <span class="hljs-keyword">match</span> x % <span class="hljs-number">2</span> {
        <span class="hljs-number">1</span> =&gt; {
            pq.enqueue(-x, x);
        }
        <span class="hljs-number">0</span> =&gt; {
            pq.enqueue(x, x);
        }
        _ =&gt; {}
    });

    c.bench_function(<span class="hljs-string">"dumb_dequeue"</span>, |b| b.iter(|| pq.dequeue(black_box(<span class="hljs-number">100</span>))));
}

criterion_group!(queue_benches, dumb_enqueue, dumb_dequeue);
criterion_main!(queue_benches);
</code></pre>
<p>To benchmark with <code>criterion</code> we define a function that take a <code>Criterion</code> struct that allows us to call <code>bench_function</code>. Now I'm an absolute begineer in a lot of things, especially benchmarking. I kept it extremely simple with both the <code>enqueue</code> and <code>dequeue</code> functions. By running <code>cargo bench --bench priority_queue -- -s dumb --verbose</code> we get the following:</p>
<pre><code class="lang-bash">Benchmarking dumb_enqueue
Benchmarking dumb_enqueue: Warming up <span class="hljs-keyword">for</span> 3.0000 s
Benchmarking dumb_enqueue: Collecting 100 samples <span class="hljs-keyword">in</span> estimated 5.0003 s (35309600 iterations)
Benchmarking dumb_enqueue: Analyzing
dumb_enqueue            time:   [116.44 ns 169.23 ns 285.70 ns]
                        change: [-39.540% -1.9099% +58.350%] (p = 0.83 &gt; 0.05)
                        No change <span class="hljs-keyword">in</span> performance detected.
Found 4 outliers among 100 measurements (4.00%)
  3 (3.00%) high mild
  1 (1.00%) high severe
slope  [116.44 ns 285.70 ns] R^2            [0.0004001 0.0003678]
mean   [112.54 ns 186.75 ns] std. dev.      [6.9781 ns 411.85 ns]
median [109.16 ns 113.14 ns] med. abs. dev. [4.1726 ns 8.2814 ns]

Benchmarking dumb_dequeue
Benchmarking dumb_dequeue: Warming up <span class="hljs-keyword">for</span> 3.0000 s
Benchmarking dumb_dequeue: Collecting 100 samples <span class="hljs-keyword">in</span> estimated 5.0009 s (11912950 iterations)
Benchmarking dumb_dequeue: Analyzing
dumb_dequeue            time:   [413.87 ns 415.25 ns 416.97 ns]
                        change: [+0.0958% +0.7162% +1.3969%] (p = 0.02 &lt; 0.05)
                        Change within noise threshold.
Found 8 outliers among 100 measurements (8.00%)
  5 (5.00%) high mild
  3 (3.00%) high severe
slope  [413.87 ns 416.97 ns] R^2            [0.9598277 0.9590296]
mean   [413.73 ns 417.35 ns] std. dev.      [4.2076 ns 14.543 ns]
median [412.60 ns 414.16 ns] med. abs. dev. [2.6068 ns 4.9808 ns]
</code></pre>
<p>which basically tells us that we run our <code>dumb_enqueue</code> function for a total of 36 million iterations resulting in an average time of execution of ~<code>169ns</code>, while our <code>dumb_dequeue</code> is a bit slower on ~<code>415ns</code> on 12 million iterations.</p>
<p>This sample clearly its useless as we don't have anything to compare it to but in the next post we'll go around a few implementations that would hopefully lower the average executions by a lot! Results for this implementation are available <a target="_blank" href="../../mora/benchmarks/0/report/index.html">here</a>.</p>
<h2 id="heading-whats-next">What's next?</h2>
<p>We are still a long way from the current <a target="_blank" href="https://www.github.com/edoardocostantinidev/mora">mora</a> capabilities, in the next post we will tackle some more efficient priority queue implementations, stay tuned!</p>
]]></content:encoded></item></channel></rss>