<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://calhuskerfan.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://calhuskerfan.github.io/" rel="alternate" type="text/html" /><updated>2026-03-21T18:55:49+00:00</updated><id>https://calhuskerfan.github.io/feed.xml</id><title type="html">calhuskerfan</title><subtitle>github pages publication for calhuskerfan</subtitle><entry><title type="html">SR Queue Consumer Pattern - Part 3</title><link href="https://calhuskerfan.github.io/2026/03/05/SQRC-part3.html" rel="alternate" type="text/html" title="SR Queue Consumer Pattern - Part 3" /><published>2026-03-05T00:00:00+00:00</published><updated>2026-03-05T00:00:00+00:00</updated><id>https://calhuskerfan.github.io/2026/03/05/SQRC-part3</id><content type="html" xml:base="https://calhuskerfan.github.io/2026/03/05/SQRC-part3.html"><![CDATA[<blockquote>
  <p>Abstraction of Message Types and Transformation</p>
</blockquote>

<p>This post extends the queue consumer pattern <a href="/2025/09/28/SQRC.html">discussed here</a> and <a href="/2025/10/22/SQRC-part2.html">here</a>  to externalize the message types and the message processing.  This will create a reusable library allowing consumers to focus on the transformation domain.</p>

<p>In the previous examples the Queue Consumer Library had compile time knowledge of the MessageIn and MessageOut types, additionally the ‘transformation’ from MessageIn to MessageOut was coded into the Queue Processor.  While this works for a demonstration, it does not make the sytem very useful in practice.</p>

<p>In this update we abstract the MessageIn and MessageOut types as well as the Message Transformation Logic out of the Queue Processor and into the ‘domain’.  This will give us a re-usable Smugglers Run Queue Processor library.</p>

<p>The first step is to update IProcessingContainer and the implemented Pod class:</p>
<ol>
  <li>Templatize the MessageIn and Message out types.</li>
  <li>Pass in a Transformer at Pod creation time.  A Note here on Pod reuse:  I have not left out the possibility of passing the transformer factory (discussed later) all the way down to the Pod.  There are some tradeoffs here, particularly if we want to use a transformer that calls out to an external resource.</li>
</ol>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Pod</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;</span> <span class="p">:</span> <span class="n">IProcessingContainer</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="c1">//.......</span>

    <span class="k">public</span> <span class="nf">Pod</span><span class="p">(</span><span class="kt">int</span> <span class="n">idx</span><span class="p">,</span> <span class="n">Func</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;</span> <span class="n">transformer</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">//...</span>
    <span class="p">}</span>

    <span class="c1">//.......</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We also need to update our IProcessingSystem implementation to define Message Types.  Additionally an ITransformerFactory has been added, this enables the conduit to create a transformer for each pod.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Conduit</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;</span> <span class="p">:</span> <span class="n">IProcessingSystem</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="c1">//...</span>
    
    <span class="k">public</span> <span class="nf">Conduit</span><span class="p">(</span>
        <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">Conduit</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;&gt;</span> <span class="n">logger</span><span class="p">,</span>
        <span class="n">IOptions</span><span class="p">&lt;</span><span class="n">ConduitConfig</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">,</span>
        <span class="n">ITransformerFactory</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;</span> <span class="n">transformerFactory</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">//...</span>
    <span class="p">}</span>

    <span class="c1">//.......</span>
<span class="p">}</span>
</code></pre></div></div>

<p>ITransformerFactory is defined as follows.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">public</span> <span class="k">interface</span> <span class="nc">ITransformerFactory</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="n">Func</span><span class="p">&lt;</span><span class="n">TMessageIn</span><span class="p">,</span> <span class="n">TMessageOut</span><span class="p">&gt;</span> <span class="nf">GetTransformer</span><span class="p">();</span>
<span class="p">}</span>

</code></pre></div></div>

<h2 id="implementations">Implementations</h2>

<h3 id="common-concerns">Common Concerns</h3>

<p>MessageIn.cs and MessageOut.cs have been moved into a domain library so that they can be shared across examples.</p>

<p>A HostingExtensions library was added to refactor some commonality into all the host processses.</p>

<h3 id="self-contained">Self Contained</h3>

<p>The ./src/console project is self contained and has been updated to support the new design.  In addition, to demonstrate the extrnalization of the transformation process it includes two message transformer factories.</p>

<table>
  <thead>
    <tr>
      <th>Factory</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>DefaultTransformerFactory</td>
      <td>Default Transformation logic from Parts 1 and 2 externalized from the IProcessingContainer</td>
    </tr>
    <tr>
      <td>LoggingTransformerFactory</td>
      <td>A Transformer Factory with an ILogger&lt;&gt; Injected.</td>
    </tr>
  </tbody>
</table>

<p>As with previous versions the <a href="https://github.com/calhuskerfan/srqc/blob/main/README.md">Readme</a> contains details on running the project.  <a href="https://github.com/calhuskerfan/srqc/blob/main/docs/console.md">Console Details</a> includes all the information for running the various Transformer Factories.</p>

<h3 id="services">Services</h3>

<p>The ./src/Processor project has also been updated to support the new design.  It also includes a DefaultTransformerFactory and an ExternalServiceTransformerFactory which makes a call to a url as part of the processing.  <a href="https://github.com/calhuskerfan/srqc/blob/main/docs/service.md">Service Details</a> includes detailed instructions for running.</p>

<h2 id="next-steps">Next Steps</h2>

<p>There are still some improvements that can be made to the system</p>

<ol>
  <li>Force MessageOut to Implement Clone()</li>
  <li>Pass an IProcessingContainer Factory into Conduit so the entire system can be dependency injected and the Pod to Conduit dependency be removed.</li>
  <li>Observability into the processing performance and health.</li>
  <li>Extending ExternalServiceTransformerFactory to demonstrate other IO operations.</li>
</ol>

<h3 id="some-notes">Some Notes</h3>

<p>MessageReadEventArgs sends out some information about the pod.  I would probably eliminate the Idx at some point, it is mostly in for demonstration purposes. and there is still some ‘audit’ functionality for demonstration purposes.</p>]]></content><author><name></name></author><category term="dotnet" /><category term="csharp" /><category term="messaging-patterns" /><category term="rabbit-mq" /><summary type="html"><![CDATA[SR Queue Consumer]]></summary></entry><entry><title type="html">SR Queue Consumer Pattern - Part 2</title><link href="https://calhuskerfan.github.io/2025/10/22/SQRC-part2.html" rel="alternate" type="text/html" title="SR Queue Consumer Pattern - Part 2" /><published>2025-10-22T00:00:00+00:00</published><updated>2025-10-22T00:00:00+00:00</updated><id>https://calhuskerfan.github.io/2025/10/22/SQRC-part2</id><content type="html" xml:base="https://calhuskerfan.github.io/2025/10/22/SQRC-part2.html"><![CDATA[<blockquote>
  <p>Connecting SRQC to a Message Queue Service</p>
</blockquote>

<p>This post extends the queue consumer pattern <a href="/2025/09/28/SQRC.html">discussed here</a> to utilize a message brokering service.  The implementation described here is using <a href="https://www.rabbitmq.com/">RabbitMQ</a>.</p>

<p>Two queues will be used, one for inbound message handling and one for outbound message handling.  These queues will be operating as <a href="https://www.enterpriseintegrationpatterns.com/patterns/messaging/MessageChannel.html">Message Channels</a> to facilitate asynchronous communication between the components.  One channel is reponsible for the message producer to message processor <code class="language-plaintext highlighter-rouge">inbound path</code> and the other is responsible for the message processor <code class="language-plaintext highlighter-rouge">outbound path</code> to the message consumer.</p>

<p>The following diagram illustrates the described system.</p>

<p><img src="/assets/images/srloader-part2.drawio.png" alt="image" /></p>

<h2 id="operation">Operation</h2>

<p>The system consists of the following components</p>

<table>
  <thead>
    <tr>
      <th>Component</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Message Producer</td>
      <td>Console application that produces a configurable number of messages and Publishes them to the Inbound Queue</td>
    </tr>
    <tr>
      <td>Inbound Queue</td>
      <td>A typical inbound queue in a Message Orientated Middleware (MOM) style application.  A RabbitMQ Queue in this system.</td>
    </tr>
    <tr>
      <td>Message Processor</td>
      <td>An IHostedServive that receives Messages from the Inbound Queue, handles the SRQC libraries as described <a href="/2025/09/28/SQRC.html">here</a> for processing and publishes the results to the Outbound Queue.</td>
    </tr>
    <tr>
      <td>Outbound Queue</td>
      <td>A typical outbound queue in a Message Orientated Middleware (MOM) style application.  A RabbitMQ Queue in this system.</td>
    </tr>
    <tr>
      <td>Message Consumer</td>
      <td>Console application responsible for Receiving and Handling Messages placed in the Outbound Queue</td>
    </tr>
  </tbody>
</table>

<h3 id="rabbitmq-implementation-notes">RabbitMQ Implementation Notes</h3>

<p>For this implementation the use of RabbitMQ relies mostly on the defaults.  There is however one component that deserves some attention, and that is the use of the <a href="https://www.rabbitmq.com/docs/consumer-prefetch">Consumer Prefetch</a> count.</p>

<p>The consumer prefetch count in RabbitMQ controls the maximum number of unacknowledged messages a consumer can receive from RabbitMQ at any given time.  In this demonstration is set at the channel level with a call to the BasicQosAsync() function.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">await</span> <span class="n">channel</span><span class="p">.</span><span class="nf">BasicQosAsync</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="n">_channelReader</span><span class="p">.</span><span class="n">PrefetchCount</span><span class="p">,</span> <span class="k">false</span><span class="p">);</span>
</code></pre></div></div>

<p>Since we are using an async, event driven, consumer the prefetch count is used to limit the ‘inflight’ messages a consumer needs to handle.  This limits resources required and reduces messages that are ‘at risk’ during the handling process.</p>

<h3 id="running-the-project">Running the Project</h3>

<p>Detailed instructions for running the project can be found <a href="https://github.com/calhuskerfan/srqc/blob/main/docs/service.md">here</a>.</p>]]></content><author><name></name></author><category term="dotnet" /><category term="csharp" /><category term="messaging-patterns" /><category term="rabbit-mq" /><summary type="html"><![CDATA[SR Queue Consumer]]></summary></entry><entry><title type="html">SR Queue Consumer Pattern</title><link href="https://calhuskerfan.github.io/2025/09/28/SQRC.html" rel="alternate" type="text/html" title="SR Queue Consumer Pattern" /><published>2025-09-28T00:00:00+00:00</published><updated>2025-09-28T00:00:00+00:00</updated><id>https://calhuskerfan.github.io/2025/09/28/SQRC</id><content type="html" xml:base="https://calhuskerfan.github.io/2025/09/28/SQRC.html"><![CDATA[<blockquote>
  <p>FIFO in a Galaxy Far Far Away</p>
</blockquote>

<p>The Smugglers Run Queue Consumer (SRQC) is a First In First Out (FIFO) message queue consumer that increases system throughput using parallel message processing. Its primary benefit is low overhead to maintain message order. It is particularly efficient in message transformation use cases when transformation is isolated and the transformation time is a significant portion of the overall handling cycle.</p>

<p>It was partially inspired by Disney’s <code class="language-plaintext highlighter-rouge">Millennium Falcon: Smugglers Run</code>.</p>

<p>This document assumes some level of familiarity with <a href="https://en.wikipedia.org/wiki/Message-oriented_middleware">message orientated middleware</a> and <a href="https://www.enterpriseintegrationpatterns.com/">Enterprise Integration Pattern</a> concepts.</p>

<h2 id="tldr">TL;DR</h2>

<p>By encapsulating messages processing in a per message thread container, and placing the containers into a queue we benefit from parallel processing while only monitoring the last message in the queue to maintain FIFO order.</p>

<p>The system is most beneficial in systems where message transformation is a significant component of overall throughput.  It is not practical for systems where processing has side effects.</p>

<p>View the <a href="https://github.com/calhuskerfan/srqc/blob/main/README.md">readme</a> for a quickstart.</p>

<h2 id="background">Background</h2>

<p><a href="https://disneyland.disney.go.com/attractions/disneyland/millennium-falcon-smugglers-run/">Millennium Falcon: Smugglers Run</a> is an interactive motion simulator ride that puts a crew of 6 riders into the cockpit of the Millennium Falcon. An imaginative component of the ride is its loading and segmentation which allow for high throughput of riders (lower wait times) with an immersive loading experience.</p>

<p>Each crew waits their turn in the chess room of the Millennium Falcon before being loaded into their individual ‘cockpit’. To create the experience and the short(er) wait times there are 4 ride turntables each with 7 pods, where each pod encapsulates the ride experience for a crew. The turntable rotates the pods to the riders to maintain the sense that you are walking from the chess room into the cockpit.</p>

<p>This <a href="https://www.bizjournals.com/orlando/news/2019/10/04/how-it-works-patent-behind-disneys-millennium.html">article</a> has a detailed description (apologies if you hit a paywall). There is another, <a href="https://disneydiary.com/2019/10/new-disney-patent-shows-how-the-millennium-falcon-ride-works/">shorter description</a> available. Finally, the <a href="https://patents.google.com/patent/EP3628383A1/en">patent</a> is also available.</p>

<p>The analogous takeaway for message processing is that if we keep the messages in order on the ‘turntable’ we can process them in a pod ‘the ride’ along with others and unload them at the end.</p>

<h2 id="introduction">Introduction</h2>

<p>In a system that requires FIFO message processing improving throughput when transformation time is a significant bottleneck leaves a few popular choices:</p>

<ul>
  <li>Process messages using a competing consumer pattern with a SequenceId that re-assembles the order in a post processing step.  While efficient this requires additional re-assembly of message order and complicates error handling.</li>
  <li>Partitioning messages within a queue(s) to support parallel processing of each. Assuming partitioning is feasible for a use case, the speed for the entire population of messages increases, however messages in a partition are still handled serially.</li>
</ul>

<p>Taking inspiration from the segmented turntable described above a system is presented here that is able to increase throughput, maintain FIFO order, and eliminate any re-assembly.  Multiple messages are transformed at a time as they ‘move’ from entry to exit. They exit in the same order they arrived, but experienced the ride (transformation) separately.</p>

<h2 id="operation">Operation</h2>

<p>The system contains a configurable number of pods that each process a message via an assigned thread. The system loads and unloads the pods in order via a dedicated queue, referred to as the conduit. If a message finishes processing before it reaches the exit it simply waits. When a message reaches the end of the conduit it is unloaded into an outbound queue.  If a message reaches the exit before it has completed the unload operation waits for completion, unloads it, and messages continuing flowing through the system.</p>

<p><img src="/assets/images/srloader-Conduit.drawio.png" alt="image" /></p>

<p>So hypothetically if a message takes 100 msec to process then our maximum message rate is 10 messages per second if processed in a serial manner, however if we can process 3 ‘pods’ at a time we increase our rate to 30 messages per second. While we will not see the theoretical throughput due to sequencing overhead and message timing variation, we will see a significant improvement compared to serial processing.</p>

<table>
  <thead>
    <tr>
      <th>Component</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Pod</td>
      <td>Container that processes the message from inbound to outbound format on a thread.</td>
    </tr>
    <tr>
      <td>Conduit</td>
      <td>An organizational construct to maintain messages in their received order while they are processing.</td>
    </tr>
    <tr>
      <td>Message Loading</td>
      <td>Place waiting messages into a pod for processing.</td>
    </tr>
    <tr>
      <td>Message Unloading</td>
      <td>Monitors pods at the exit. If the message transformation is completed the message is put in the outbound queue. If it is still processing it will wait for completion.</td>
    </tr>
    <tr>
      <td>Inbound Queue</td>
      <td>A typical inbound queue in a Message Orientated Middleware (MOM) style application.  Simulated as an in memory array.</td>
    </tr>
    <tr>
      <td>Outbound Queue</td>
      <td>A typical outbound queue in a Message Orientated Middleware (MOM) style application.  Simulated as an in memory array.</td>
    </tr>
  </tbody>
</table>

<p>Pod count is a balance between consumed resources, context switching efficiency, and ‘at risk’ messages. ‘At risk’ messages in this context refers to overhead required for handling failures.  Assuming the inbound queue and outbound queues are resilient the more in flight messages we release the more care must be taken for message processing or system failures.</p>

<h2 id="limitations">Limitations</h2>

<p>A few notable limitations to the system.</p>

<ul>
  <li>Diminishing returns as the message process time reduces. Eventually the overhead of synchronization hampers performance.</li>
  <li>Performance degradation by ‘long pole’ messages. Messages that take a significantly longer amount of time to process than their peers may drag down the throughput relative to a re-sequencing competing consumer pattern.</li>
  <li>By allowing a greater number messages into the system simultaneously we lose some of the message resilience the inbound queue provides.</li>
  <li>The transformation of the message must be self contained, I.e. the only target is the exit queue. If any portion of the processing has external side effects that include FIFO restrictions this pattern would not suffice since there is no synchronization of ‘in flight’ transformations.</li>
</ul>

<h2 id="running-the-project">Running the Project</h2>

<p>For the demonstration project the message transformation is simply a string update and a Thread.Sleep() to simulate processing time.</p>

<p>Please refer to the project <a href="https://github.com/calhuskerfan/srqc/blob/main/README.md">Readme</a> for details on running the project.</p>

<p>When the simulation finishes running a summary is logged.  The output will resemble the following (timestamps and log level omitted):</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>010012:002:0000193:New outbound message is: 12 from pod 37c52e90-0f27-4476-8c0a-a9bf73b629cd
</code></pre></div></div>
<p>Where the columns are ‘:’ delimeted, representing:</p>

<ul>
  <li>The first column is the message id</li>
  <li>The second column is the index of the pod that processed the message</li>
  <li>the third is the configured delay (message processing simulation)</li>
  <li>The fourth column is the transformed message.</li>
</ul>

<p>The last line will contain summary information</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2 Total processing time: 721.4762 msec.  Accumulated 'Serial' Time: 2066 msec.  Ratio: 2.863573323693838
</code></pre></div></div>
<p>That includes:</p>

<ul>
  <li>Total processing time is the start to finish of the execution.</li>
  <li>Accumulated ‘Serial’ Time represents the time it would take to process all messages serially.</li>
  <li>Ratio is the Accumulated / Total; the estimated throughput improvement.</li>
</ul>

<h2 id="next-steps">Next Steps</h2>

<p>The project as it sits today is mostly for demonstration, following are a few next steps to consider for a more robust system.</p>

<ul>
  <li>Externalize the Types and logic for the pod and conduit. By templatizing the types for inbound and outbound messages and externalizing the pod processing logic the system can be re-used.</li>
  <li>Error Handling Patterns. What happens when a message fails, what should ripple upstream.</li>
  <li>Run as an IHostedService.</li>
  <li>Incorporate a common message queue, I.e. <a href="https://www.rabbitmq.com">rabbitmq</a>.</li>
</ul>

<h2 id="summary">Summary</h2>

<p>In the end this project was primarily a thought exercise, however it was successful with the requirements that I had envisioned for it, namely:</p>

<ul>
  <li>speeding up processing of a FIFO queue where the bottleneck was the message processing itself.</li>
  <li>not adding any external complexities to maintain message order.</li>
</ul>

<p>If nothing else it was an opportunity to look at a problem with a different lens and see what comes of it.</p>

<p>Your mileage may vary.</p>]]></content><author><name></name></author><category term="dotnet" /><category term="csharp" /><category term="messaging-patterns" /><summary type="html"><![CDATA[SR Queue Consumer]]></summary></entry></feed>