<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Performance on Damian Maclennan</title>
    <link>http://damianm.com/tags/performance/</link>
    <description>Recent content in Performance on Damian Maclennan</description>
    <generator>Hugo</generator>
    <language>en-au</language>
    <managingEditor>damian@damianm.com (Damian Maclennan)</managingEditor>
    <lastBuildDate>Sat, 23 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://damianm.com/tags/performance/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Fun with Nimbus Benchmarks - NATS is Fast!</title>
      <link>http://damianm.com/posts/2026/fun-with-nimbus-benchmarks/</link>
      <pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate>
      <guid>http://damianm.com/posts/2026/fun-with-nimbus-benchmarks/</guid>
      <description>&lt;![CDATA[<p>For fun and curiosity, I recently decided to build a little console app to run some benchmarks across the various transports.</p>
<p>It was driven by finishing up the <a href="../nimbus-now-supports-nats-io">NATS implementation</a>, I knew that <a href="https://nats.io/">NATS</a> <em>is</em> fast, so I assumed that the Nimbus transport provider would demonstrate it, but wanted to know more.</p>
<p>Two interesting things came from this little exercise.</p>
<ol>
<li>I found a bug!</li>
<li>Yes, NATS is really really fast.</li>
</ol>
<h2 id="the-bug">The bug</h2>
<p>Well more of a &ldquo;opportunity for improvement&rdquo; than a bug, but in my AMQP transport I had a very naive (aka inefficient) approach of creating a new queue or topic Producer on every message send.</p>
<p>It got the transport working, and didn&rsquo;t go back once I had all my tests passing and re-visit it. Turns out that&rsquo;s a huge overhead.</p>
<p>So now I have a pool of senders, cached by the destination key, so repeat sends and publishes are fast. It took the AMQP transport from being quite slow, to very very fast.</p>
<p>So I&rsquo;ll call that a win for performance testing.</p>
<h2 id="nats-is-fast">NATS is Fast</h2>
<p>When we added the Redis transport, we were blown away by how much faster it was than Azure Service Bus. ASB from the average Australian internet connection back then wasn&rsquo;t setting any records, and Redis seemed incredible. It was at the expense of any kind of persistence or reliability.</p>
<p>What the results show is that NATS is incredibly quick. With the persistent JetStream option enabled, it&rsquo;s still almost twice as fast as Redis, and using the non-persistent NATS Core, it&rsquo;s almost as fast as our In Memory transport which we just use for testing. Amazing numbers.</p>
<h2 id="other-takeaways">Other takeaways</h2>
<p>To it&rsquo;s credit, ActiveMQ is also now a touch faster than Redis, and it has a much better persistence story.</p>
<p>So Redis has been a great thing for Nimbus, particularly in environments where you already have it and don&rsquo;t want another piece of infrastructure. But it&rsquo;s not the performance king anymore.</p>
<p>The database transports are not fast, which is to be expected. There&rsquo;s a polling loop in there which means the first message latency can be quite high. These transports exist for a world where we don&rsquo;t want to add any other messaging infrastructure, and there are a lot of scenarios where that is fine.</p>
<p>However, for throughput, Postgres does incredibly well under load. Is there anything that Postgres can&rsquo;t do well?</p>
<p>Another nice outcome, although not unexpected, every message that got sent was received. I&rsquo;d be concerned if that wasn&rsquo;t the case though.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Here are some of the numbers, I didn&rsquo;t run the SQL Server one for the same number of iterations as the others, it would have taken a while. Although the throughput numbers would have gone up a bit.
The tests only do a simple Send and Receive on a Queue too, and in a single process.</p>
<p>It&rsquo;s not hugely scientific, but it definitely achieved more than I set out to do.</p>
<table>
<thead>
<tr>
<th>Transport</th>
<th>Messages</th>
<th>TotalTime(ms)</th>
<th>Msg/s</th>
<th>Latency Min(ms)</th>
<th>Latency p99</th>
<th>Latency max</th>
</tr>
</thead>
<tbody>
<tr>
<td>InProcess</td>
<td>100000</td>
<td>3539</td>
<td>28260</td>
<td>0.02</td>
<td>1.75</td>
<td>88.82</td>
</tr>
<tr>
<td>Redis</td>
<td>100000</td>
<td>39656</td>
<td>2522</td>
<td>0.36</td>
<td>1.06</td>
<td>83.92</td>
</tr>
<tr>
<td>Nats</td>
<td>100000</td>
<td>3778</td>
<td>26467</td>
<td>0.17</td>
<td>3.15</td>
<td>114.25</td>
</tr>
<tr>
<td>Nats - JetStream</td>
<td>100000</td>
<td>21382</td>
<td>4677</td>
<td>0.13</td>
<td>0.8</td>
<td>101.96</td>
</tr>
<tr>
<td>ActiveMQ</td>
<td>100000</td>
<td>36681</td>
<td>2726</td>
<td>0.27</td>
<td>0.88</td>
<td>98.47</td>
</tr>
<tr>
<td>Postgres</td>
<td>100000</td>
<td>40470</td>
<td>2471</td>
<td>46.79</td>
<td>5619.45</td>
<td>5696.61</td>
</tr>
<tr>
<td>SqlServer</td>
<td>10000</td>
<td>16760</td>
<td>597</td>
<td>35.75</td>
<td>6407.37</td>
<td>6545.28</td>
</tr>
</tbody>
</table>
]]></description>
      <content:encoded>&lt;![CDATA[<p>For fun and curiosity, I recently decided to build a little console app to run some benchmarks across the various transports.</p>
<p>It was driven by finishing up the <a href="../nimbus-now-supports-nats-io">NATS implementation</a>, I knew that <a href="https://nats.io/">NATS</a> <em>is</em> fast, so I assumed that the Nimbus transport provider would demonstrate it, but wanted to know more.</p>
<p>Two interesting things came from this little exercise.</p>
<ol>
<li>I found a bug!</li>
<li>Yes, NATS is really really fast.</li>
</ol>
<h2 id="the-bug">The bug</h2>
<p>Well more of a &ldquo;opportunity for improvement&rdquo; than a bug, but in my AMQP transport I had a very naive (aka inefficient) approach of creating a new queue or topic Producer on every message send.</p>
<p>It got the transport working, and didn&rsquo;t go back once I had all my tests passing and re-visit it. Turns out that&rsquo;s a huge overhead.</p>
<p>So now I have a pool of senders, cached by the destination key, so repeat sends and publishes are fast. It took the AMQP transport from being quite slow, to very very fast.</p>
<p>So I&rsquo;ll call that a win for performance testing.</p>
<h2 id="nats-is-fast">NATS is Fast</h2>
<p>When we added the Redis transport, we were blown away by how much faster it was than Azure Service Bus. ASB from the average Australian internet connection back then wasn&rsquo;t setting any records, and Redis seemed incredible. It was at the expense of any kind of persistence or reliability.</p>
<p>What the results show is that NATS is incredibly quick. With the persistent JetStream option enabled, it&rsquo;s still almost twice as fast as Redis, and using the non-persistent NATS Core, it&rsquo;s almost as fast as our In Memory transport which we just use for testing. Amazing numbers.</p>
<h2 id="other-takeaways">Other takeaways</h2>
<p>To it&rsquo;s credit, ActiveMQ is also now a touch faster than Redis, and it has a much better persistence story.</p>
<p>So Redis has been a great thing for Nimbus, particularly in environments where you already have it and don&rsquo;t want another piece of infrastructure. But it&rsquo;s not the performance king anymore.</p>
<p>The database transports are not fast, which is to be expected. There&rsquo;s a polling loop in there which means the first message latency can be quite high. These transports exist for a world where we don&rsquo;t want to add any other messaging infrastructure, and there are a lot of scenarios where that is fine.</p>
<p>However, for throughput, Postgres does incredibly well under load. Is there anything that Postgres can&rsquo;t do well?</p>
<p>Another nice outcome, although not unexpected, every message that got sent was received. I&rsquo;d be concerned if that wasn&rsquo;t the case though.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Here are some of the numbers, I didn&rsquo;t run the SQL Server one for the same number of iterations as the others, it would have taken a while. Although the throughput numbers would have gone up a bit.
The tests only do a simple Send and Receive on a Queue too, and in a single process.</p>
<p>It&rsquo;s not hugely scientific, but it definitely achieved more than I set out to do.</p>
<table>
<thead>
<tr>
<th>Transport</th>
<th>Messages</th>
<th>TotalTime(ms)</th>
<th>Msg/s</th>
<th>Latency Min(ms)</th>
<th>Latency p99</th>
<th>Latency max</th>
</tr>
</thead>
<tbody>
<tr>
<td>InProcess</td>
<td>100000</td>
<td>3539</td>
<td>28260</td>
<td>0.02</td>
<td>1.75</td>
<td>88.82</td>
</tr>
<tr>
<td>Redis</td>
<td>100000</td>
<td>39656</td>
<td>2522</td>
<td>0.36</td>
<td>1.06</td>
<td>83.92</td>
</tr>
<tr>
<td>Nats</td>
<td>100000</td>
<td>3778</td>
<td>26467</td>
<td>0.17</td>
<td>3.15</td>
<td>114.25</td>
</tr>
<tr>
<td>Nats - JetStream</td>
<td>100000</td>
<td>21382</td>
<td>4677</td>
<td>0.13</td>
<td>0.8</td>
<td>101.96</td>
</tr>
<tr>
<td>ActiveMQ</td>
<td>100000</td>
<td>36681</td>
<td>2726</td>
<td>0.27</td>
<td>0.88</td>
<td>98.47</td>
</tr>
<tr>
<td>Postgres</td>
<td>100000</td>
<td>40470</td>
<td>2471</td>
<td>46.79</td>
<td>5619.45</td>
<td>5696.61</td>
</tr>
<tr>
<td>SqlServer</td>
<td>10000</td>
<td>16760</td>
<td>597</td>
<td>35.75</td>
<td>6407.37</td>
<td>6545.28</td>
</tr>
</tbody>
</table>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
