<?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://isurudevj.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://isurudevj.github.io/" rel="alternate" type="text/html" /><updated>2026-07-12T08:37:16+00:00</updated><id>https://isurudevj.github.io/feed.xml</id><title type="html">isuru’s blog</title><subtitle>This is about what I do for fun.</subtitle><entry><title type="html">Aeron Monitoring</title><link href="https://isurudevj.github.io/performance-engineering/2026/07/11/aeron-monitoring.html" rel="alternate" type="text/html" title="Aeron Monitoring" /><published>2026-07-11T16:00:00+00:00</published><updated>2026-07-11T16:00:00+00:00</updated><id>https://isurudevj.github.io/performance-engineering/2026/07/11/aeron-monitoring</id><content type="html" xml:base="https://isurudevj.github.io/performance-engineering/2026/07/11/aeron-monitoring.html"><![CDATA[<h1 id="aeron-monitoring">Aeron Monitoring</h1>]]></content><author><name>Isuru</name></author><category term="performance-engineering" /><summary type="html"><![CDATA[Aeron Monitoring]]></summary></entry><entry><title type="html">Bloggers to follow</title><link href="https://isurudevj.github.io/scratch/2024/07/19/bloggers-to-follow.html" rel="alternate" type="text/html" title="Bloggers to follow" /><published>2024-07-19T05:00:00+00:00</published><updated>2024-07-19T05:00:00+00:00</updated><id>https://isurudevj.github.io/scratch/2024/07/19/bloggers-to-follow</id><content type="html" xml:base="https://isurudevj.github.io/scratch/2024/07/19/bloggers-to-follow.html"><![CDATA[<h1 id="bloggers-to-follow">Bloggers to follow</h1>

<p><a href="https://psy-lob-saw.blogspot.com/">Psychosomatic, Lobotomy, Saw</a></p>]]></content><author><name>Isuru</name></author><category term="scratch" /><summary type="html"><![CDATA[Bloggers to follow]]></summary></entry><entry><title type="html">QuickFixJ - FIX Protocol Session Layer Implementation</title><link href="https://isurudevj.github.io/top/2024/06/01/fix-session-layer-messages.html" rel="alternate" type="text/html" title="QuickFixJ - FIX Protocol Session Layer Implementation" /><published>2024-06-01T22:00:00+00:00</published><updated>2024-06-01T22:00:00+00:00</updated><id>https://isurudevj.github.io/top/2024/06/01/fix-session-layer-messages</id><content type="html" xml:base="https://isurudevj.github.io/top/2024/06/01/fix-session-layer-messages.html"><![CDATA[<h1 id="fix-protocol-session-layer">FIX Protocol Session Layer</h1>

<p>FIX protocol messages are divided into two types.</p>

<ol>
  <li>Session Layer Messages</li>
  <li>Application Messages</li>
</ol>

<p>Session Layer Messages are for establishing, recovering and terminating FIX session. 
While application messages are to carry out business purposes such as buy, sell, sending/subscribing market data or sending execution reports.</p>

<p>FIX protocol session layer is a realization of layer 5 session layer of OSI model.</p>

<p><img src="/assets/img/fix_session_layer/osi_session_layer.jpg" alt="OSI Session Layer" />
Image Reference - <a href="https://web.mit.edu/modiano/www/6.263/Lecture1.pdf">Data Networks - MIT</a></p>

<p>FIX session can exist multiple sequential FIX connections. FIX connection can be terminated due to various reasons such as application or network outages.
But the FIX session can continue after re-establishing the FIX connection. 
There are various means provided in the FIX session layer specification to recover the session as well.
Such as keeping track of <strong>NextNumIn</strong>, <strong>NextNumOut</strong>, <strong>Retransmission</strong>, <strong>Gap-Fill</strong> and <strong>Resend</strong> of messages.</p>

<p><img src="/assets/img/fix_session_layer/session_lives_across_connections.png" alt="Session exists across sequential connections" /></p>

<p>Image Reference - <a href="https://www.fixtrading.org/standards/fix-session-layer-online/">Session Across Sequential Connections</a></p>

<h2 id="establishing-fix-connection">Establishing FIX Connection</h2>

<p>Establishing FIX connection has three parts.</p>

<ol>
  <li>Transport Layer Connection</li>
  <li>Acceptance with optional authentication</li>
  <li>Message synchronization</li>
</ol>

<h2 id="fix-session-time-span">FIX session time-span</h2>

<p>FIX session usually exists for agreed time-span by two peers (acceptor and initiator). It can be weekly or daily sessions. 
This weekly and daily session timing are agreed by <strong>out-of-band</strong> communications. Which means no session layer message will specify the duration of the session.
Which we called the <strong>in-band</strong> communication. Two counterparties agree upon session timing as part of their rules of engagement.</p>

<p>Since I briefly mention <strong>in-band</strong> communication. Example for <strong>in-band</strong> communication is the heart beat interval. 
Which used by peers to check on each other’s live-ness.
HeartBtInt(tag 108) is set in initiators logon request (35=A) to specify this value. Hence, its <strong>in-band</strong> communication.</p>

<h2 id="important-out-of-band-parameters">Important out-of-band parameters</h2>

<p>Following out-of-band parameters are important to the session layer.</p>

<table>
  <thead>
    <tr>
      <th>Configuration</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>TestRequestThreshold</td>
      <td>Amount of time expressed as a multiplier of heartbeat interval before TestRequest(35=1) send to the peer.</td>
    </tr>
    <tr>
      <td>SendingTimeThreshold</td>
      <td>Amount of time expressed in seconds in which SendingTime(tag 52) in an inbound message can differ from system time of the receiving peer.</td>
    </tr>
    <tr>
      <td>LogOutAckThreshold</td>
      <td>Amount of time express in seconds where FIX peer who has sent Logout(35=5) will wait for its peer to reply with Acknowledgement, if execeed it will terminate the transport layer connection.</td>
    </tr>
  </tbody>
</table>

<h1 id="heartbeat-interval-determination">Heartbeat interval determination</h1>

<p>In QuickFIXJ initiator will specify the HeartBtInt(108) tag in the logon request (35=A).
Acceptor which may or may not be QuickFIXJ based implementation, will also send HeartBeatInt(108) in its logon request. 
Acceptor may echo back the value send by the initiator, or send a different value. Or it can even reject the logon request. 
Initiator can also reject the logon message send by acceptor if it does not agree with the amend for the HeartBeatInt.</p>

<p><a href="https://www.quickfixj.org/usermanual/2.3.0/usage/configuration.html#Initiator">QFJ Configuration - Initiator</a></p>

<table>
  <thead>
    <tr>
      <th>Configuration</th>
      <th>Description</th>
      <th>Values</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>HeartBtInt</td>
      <td>Heartbeat interval in seconds. Only used for initiators.</td>
      <td>positive integer</td>
    </tr>
  </tbody>
</table>

<p>Following is a QFJ log from the perspective of the accepting peer. HeartBtInt configuration is mandatory for QFJ initiator peer.
Acceptor will switch to the heart beat interval set by the initiator in the logon request.</p>

<p><img src="/assets/img/fix_session_layer/heartbtint_determination.png" alt="QFJ HeartBtInt determination" /></p>

<p>Above screenshot is from the output of the <a href="https://github.com/busy-spin/qfj-fix-shell">qfj-fix-shell</a></p>

<h1 id="message-recovery">Message Recovery</h1>

<p>FIX message synchronization after logon request has been send considered to be recoverable if the followings are true.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>peer1.NextNumOut &gt;= peer2.NextNumIn
peer2.NextNumOut &gt;= peer1.NextNumIn
</code></pre></div></div>

<p>What this means is peer’s NextNumOut should be always equal or smaller than other peer’s expected NextNumIn. 
If there is need for message synchronization, then it can be done using ResendRequest(35=2) and SequenceReset(35=4) messages which 
follows the Logon(35=A) messages.</p>

<h2 id="scenario-1---acceptor-requires-retransmission-of-messages-from-initiator">Scenario 1 - Acceptor requires retransmission of messages from initiator</h2>

<p>Let’s take a scenario where Initiator’s NextNumOut is larger number than the Acceptor’s NextNumIn.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    initiator.NextNumOut &gt; acceaptor.NextNumIn 
    initiator.NextNumIn = acceaptor.NextNumOut
</code></pre></div></div>

<p><img src="/assets/img/fix_session_layer/recovery_1_sequence_numbers.png" alt="Sequence Numbers" /></p>

<p>Above figure shows a scenario I have created using <a href="https://github.com/busy-spin/qfj-fix-shell">qfj-fix-shell</a>.
Where initiator’s NextNumIn and acceptor’s NextNumOut matches(83), but initiator’s NextNumOut (199) is larger than acceptor’s NextNumIn (20).</p>

<p>Let’s assume on the initiator side message sequence for un-synced outgoing messages would look like this.</p>

<table>
  <thead>
    <tr>
      <th>Sequence Number(s)</th>
      <th>Type of Message</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>20-152</td>
      <td>Session Layer Messages</td>
    </tr>
    <tr>
      <td>153</td>
      <td>Application Layer Message</td>
    </tr>
    <tr>
      <td>154</td>
      <td>Session Layer Message</td>
    </tr>
    <tr>
      <td>155</td>
      <td>Application Layer Message</td>
    </tr>
    <tr>
      <td>156-199</td>
      <td>Session Layer Messages</td>
    </tr>
  </tbody>
</table>

<h3 id="recovery-process">Recovery process</h3>

<p><img src="/assets/img/fix_session_layer/recovery_1_fix_log.png" alt="Message Recovery" /></p>

<table>
  <thead>
    <tr>
      <th>Step</th>
      <th>Remark</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Initiator send logon(35=A) request</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Acceptor respond with logon(35=A) request</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Acceptor resend reqeust (35=2) for missing messages between 20 to <strong>infinity</strong> BeginSeqNo&lt;7&gt;=20 and EndSeqNo&lt;16&gt;=0.</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Initiator send sequence reset (35=4) with NewSeqNo&lt;36&gt;=153 and GapFill&lt;123&gt;=Y. Reason is there is no application messages till sequence number 152</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Initiator send application message NewOrderSingle(35=D) with PossDupFlag&lt;43&gt;=Y to denote that this is possible duplicate message.</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Initiator send sequence reset (35=4) with NewSeqNo&lt;36&gt;=155 and GapFill&lt;123&gt;=Y. Reason is there is no application messages till sequence number 154</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Initiator send applicatoin message NewOrderSingle(35=D) with PossDupFlag&lt;43&gt;=Y to denote that this is possible duplicate message.</td>
    </tr>
    <tr>
      <td>8</td>
      <td>Initiator send sequence reset (35=4) with NewSeqNo&lt;36&gt;=200 and GapFill&lt;123&gt;=Y. Reason is there is no application messages till sequence number 199</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Acceptor send HeartBeat message (35=0), and conclude the message recovery</td>
    </tr>
  </tbody>
</table>

<h2 id="scenario-2---initiator-requires-retransmission-of-messages-from-acceptor">Scenario 2 - Initiator requires retransmission of messages from acceptor</h2>

<p>Let’s take a scenario where Initiator’s NextNumOut is larger number than the Acceptor’s NextNumIn.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    initiator.NextNumOut = acceaptor.NextNumIn 
    initiator.NextNumIn &lt; acceaptor.NextNumOut
</code></pre></div></div>

<p><img src="/assets/img/fix_session_layer/recovery_2_sequence_numbers.png" alt="Sequence Numbers" /></p>

<p>Above figure shows a scenario I have created using <a href="https://github.com/busy-spin/qfj-fix-shell">qfj-fix-shell</a>.
Where initiator’s NextNumOut and acceptor’s NextNumIn matches(121), but initiator’s NextNumIn (50) is smaller than acceptor’s NextNumOut (122).</p>

<p>Let’s assume on the acceptor side message sequence for un-synced outgoing messages would look like this.</p>

<table>
  <thead>
    <tr>
      <th>Sequence Number(s)</th>
      <th>Type of Message</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>50-63</td>
      <td>Session Layer Messages</td>
    </tr>
    <tr>
      <td>64</td>
      <td>Application Layer Message</td>
    </tr>
    <tr>
      <td>65-73</td>
      <td>Session Layer Message</td>
    </tr>
    <tr>
      <td>74</td>
      <td>Application Layer Message</td>
    </tr>
    <tr>
      <td>75-122</td>
      <td>Session Layer Messages</td>
    </tr>
  </tbody>
</table>

<h3 id="recovery-process-1">Recovery process</h3>

<p><img src="/assets/img/fix_session_layer/recovery_2_fix_log.png" alt="Message Recovery" /></p>

<table>
  <thead>
    <tr>
      <th>Step</th>
      <th>Remark</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Initiator send logon(35=A) request</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Acceptor respond with logon(35=A) request</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Initiator resend reqeust (35=2) for missing messages between 50 to <strong>infinity</strong> BeginSeqNo&lt;7&gt;=50 and EndSeqNo&lt;16&gt;=0.</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Acceptor send sequence reset (35=4) with NewSeqNo&lt;36&gt;=64 and GapFill&lt;123&gt;=Y. Reason is there is no application messages till sequence number 63</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Acceptor send application message ExecutionReport(35=8) with PossDupFlag&lt;43&gt;=Y to denote that this is possible duplicate message.</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Acceptor send sequence reset (35=4) with NewSeqNo&lt;36&gt;=74 and GapFill&lt;123&gt;=Y. Reason is there is no application messages till sequence number 73</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Acceptor send applicatoin message ExecutionReport(35=8) with PossDupFlag&lt;43&gt;=Y to denote that this is possible duplicate message.</td>
    </tr>
    <tr>
      <td>8</td>
      <td>Acceptor send sequence reset (35=4) with NewSeqNo&lt;36&gt;=123 and GapFill&lt;123&gt;=Y. Reason is there is no application messages till sequence number 122</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Acceptor send HeartBeat message (35=0), and conclude the message recovery</td>
    </tr>
  </tbody>
</table>

<h2 id="scenario-3---both-initiator-requires-retransmission-of-messages-from-acceptor">Scenario 3 - Both initiator requires retransmission of messages from acceptor</h2>

<p>Let’s take a scenario where Initiator’s NextNumOut is larger number than the Acceptor’s NextNumIn.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    initiator.NextNumOut &gt; acceaptor.NextNumIn 
    initiator.NextNumIn &lt; acceaptor.NextNumOut
</code></pre></div></div>

<p><img src="/assets/img/fix_session_layer/recovery_3_sequence_numbers.png" alt="Sequence Numbers" /></p>

<p>Above figure shows a scenario I have created using <a href="https://github.com/busy-spin/qfj-fix-shell">qfj-fix-shell</a>.
Where initiator’s NextNumOut(59) is larger than acceptor’s NextNumIn(46), and initiator’s NextNumIn (43) is smaller than acceptor’s NextNumOut (59).</p>

<p>Let’s assume on the acceptor side message sequence for un-synced outgoing messages would look like this.</p>

<table>
  <thead>
    <tr>
      <th>Sequence Number(s)</th>
      <th>Type of Message</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>43-54</td>
      <td>Session Layer Messages</td>
    </tr>
    <tr>
      <td>55</td>
      <td>Application Layer Message</td>
    </tr>
    <tr>
      <td>56-60</td>
      <td>Session Layer Message</td>
    </tr>
  </tbody>
</table>

<p>Let’s assume on the initiator side message sequence for un-synced outgoing messages would look like this.</p>

<table>
  <thead>
    <tr>
      <th>Sequence Number(s)</th>
      <th>Type of Message</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>46-49</td>
      <td>Session Layer Messages</td>
    </tr>
    <tr>
      <td>50</td>
      <td>Application Layer Message</td>
    </tr>
    <tr>
      <td>51-60</td>
      <td>Session Layer Message</td>
    </tr>
  </tbody>
</table>

<h3 id="recovery-process-2">Recovery process</h3>

<p><img src="/assets/img/fix_session_layer/recovery_3_fix_log.png" alt="Message Recovery" /></p>

<p><img src="/assets/img/fix_session_layer/recovery_3_fix_log_part2.png" alt="Message Recovery - Part 2" /></p>

<table>
  <thead>
    <tr>
      <th>Step</th>
      <th>Remark</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Initiator send logon(35=A) request</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Acceptor respond with logon(35=A) request</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Acceptor send resend reqeust (35=2) for missing messages between 46 to <strong>infinity</strong> BeginSeqNo&lt;7&gt;=46 and EndSeqNo&lt;16&gt;=0.</td>
    </tr>
    <tr>
      <td>4</td>
      <td>Initiator send resend reqeust (35=2) for missing messages between 43 to <strong>infinity</strong> BeginSeqNo&lt;7&gt;=43 and EndSeqNo&lt;16&gt;=0.</td>
    </tr>
    <tr>
      <td>5</td>
      <td>Initiator send a gap fill message, followed by NewOrderSingle message</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Acceptor send a gap fill message, followed by ExecutionReport message</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Initiator send a gap fill message for session layer messages</td>
    </tr>
    <tr>
      <td>8</td>
      <td>Initiator send a gap fill message for session layer messages</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Initiator and acceptor start heart beating to each other to conclude the message recovery</td>
    </tr>
  </tbody>
</table>

<h1 id="best-practices">Best Practices</h1>

<h1 id="always-send-a-testrequest351-upon-login35a">Always send a TestRequest(35=1) upon Login(35=A)</h1>

<p>Initiator and Acceptor require to perform message synchronization after each peer received the logon request, and logon request 
SequenceNumber does not match with the expected NextNumIn for the respective peer. A good practice is to send TestRequest(35=1) to force message synchronization 
the peer to send a HeartBeat(35=0), before sending any queued application level messages.</p>

<h1 id="references">References</h1>

<p><a href="https://www.fixtrading.org/standards/fix-session-layer/">FIX Session Layer Test Cases</a></p>

<p><a href="https://www.fixtrading.org/standards/fix-session-layer-online/">FIX Session Layer Complete References</a></p>]]></content><author><name>Isuru</name></author><category term="top" /><summary type="html"><![CDATA[FIX Protocol Session Layer]]></summary></entry><entry><title type="html">Agrona Idle Strategy</title><link href="https://isurudevj.github.io/performance-engineering/top/2024/05/22/exponential-backoff-sleep.html" rel="alternate" type="text/html" title="Agrona Idle Strategy" /><published>2024-05-22T22:00:00+00:00</published><updated>2024-05-22T22:00:00+00:00</updated><id>https://isurudevj.github.io/performance-engineering/top/2024/05/22/exponential-backoff-sleep</id><content type="html" xml:base="https://isurudevj.github.io/performance-engineering/top/2024/05/22/exponential-backoff-sleep.html"><![CDATA[<h1 id="agrona">Agrona</h1>

<p>Agrona is a compilation of threading and data structures which essentially a utility library, which is the underpinning of <a href="https://github.com/real-logic/aeron">Aeron</a>, 
<a href="https://github.com/real-logic/simple-binary-encoding">SBE</a> and <a href="https://github.com/real-logic/artio">Artio</a>. 
These are all parts of the <a href="https://github.com/real-logic">real-logic stack</a> for building high performance financial applications.</p>

<h1 id="agentrunner-agent-duty-cycle-and-the-idle-strategy">AgentRunner, Agent, Duty-Cycle and the Idle Strategy</h1>

<p>Let’s visualize a new approach of writing business logic. Your logic resides in an Agent class. Which has a method <strong>doWork()</strong> that get executed in a loop. 
Each iteration is called a <strong>Duty Cycle</strong>. And each Agent is managed by an <strong>AgentRunner</strong>. AgentRunner is essentially a <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Runnable.html">Runnable</a>.
Using AgentRunners static method, AgentRunner can be executed in a thread using default non-daemon ThreadFactory.</p>

<p>The thread will run following piece of code in an infinite loop. Until AgentRunner is closed.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kt">int</span> <span class="n">workCount</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="na">doWork</span><span class="o">();</span>
<span class="n">idleStrategy</span><span class="o">.</span><span class="na">idle</span><span class="o">(</span><span class="n">workCount</span><span class="o">);</span>
</code></pre></div></div>

<p>doWork() method returns an integer value, the <strong>idiomatic</strong> usage of this return value is to indicate the number of work carried out by the Agent. 
For an example number of network messages handled by the Agent during the Duty Cycle. Idle Strategy is used by the AgentRunner to decide take breaks between Duty Cycles.
If during the previous Duty Cycle doWork indicate no work has been done, then the Idle Strategy will put the thread in to sleep.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">idle</span><span class="o">(</span><span class="kt">int</span> <span class="n">workCount</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">workCount</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">LockSupport</span><span class="o">.</span><span class="na">parkNanos</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">sleepPeriodNs</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>The rationale behind this approach is to free up the CPU cores if application has no new events to execute. Then release the CPU core by putting the thread in to sleep. 
This is what we called the voluntary context switching. It may be not the best choice of action in terms of the particular application.
But it’s good for overall system performance, where multiple applications run’s on same OS host.</p>

<h1 id="simple-example">Simple example</h1>

<p>We have simple Agent implementation called <strong>RandomWorkCountAgent</strong> which simulate Agent with duty cycles which work has been carried out 
and also duty cycles where no work has been carried out.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.agrona.concurrent.Agent</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Random</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RandomWorkCountAgent</span> <span class="kd">implements</span> <span class="nc">Agent</span> <span class="o">{</span>

    <span class="kd">private</span> <span class="kd">final</span> <span class="nc">Random</span> <span class="n">random</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Random</span><span class="o">();</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onStart</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">"[%s], Agent started\n"</span><span class="o">,</span> <span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getName</span><span class="o">());</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">doWork</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">random</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">3</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onClose</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">Agent</span><span class="o">.</span><span class="na">super</span><span class="o">.</span><span class="na">onClose</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">roleName</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="s">"random-agent"</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Idle strategy implementation is <strong>LogSleepIdleStrategy</strong> where is work count is zero it will make the thread sleep for 2 seconds.
And if work count is greater than zero, log the work count and return.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.agrona.concurrent.IdleStrategy</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">java.util.concurrent.TimeUnit</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.concurrent.locks.LockSupport</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">LogSleepIdleStrategy</span> <span class="kd">implements</span> <span class="nc">IdleStrategy</span> <span class="o">{</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">idle</span><span class="o">(</span><span class="kt">int</span> <span class="n">workCount</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">workCount</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">"[%s], Going for a sleep\n"</span><span class="o">,</span> <span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getName</span><span class="o">());</span>
            <span class="nc">LockSupport</span><span class="o">.</span><span class="na">parkNanos</span><span class="o">(</span><span class="nc">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">.</span><span class="na">toNanos</span><span class="o">(</span><span class="mi">2L</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">"[%s] - work count = %d\n"</span><span class="o">,</span> <span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getName</span><span class="o">(),</span> <span class="n">workCount</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">idle</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">reset</span><span class="o">()</span> <span class="o">{</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">alias</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nc">IdleStrategy</span><span class="o">.</span><span class="na">super</span><span class="o">.</span><span class="na">alias</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Main program.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.agrona.concurrent.AgentRunner</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">SimpleAgentSample</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">AgentRunner</span> <span class="n">agentRunner</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AgentRunner</span><span class="o">(</span><span class="k">new</span> <span class="nc">LogSleepIdleStrategy</span><span class="o">(),</span>
                <span class="n">throwable</span> <span class="o">-&gt;</span> <span class="o">{},</span> <span class="kc">null</span><span class="o">,</span> <span class="k">new</span> <span class="nc">RandomWorkCountAgent</span><span class="o">());</span>
        <span class="nc">AgentRunner</span><span class="o">.</span><span class="na">startOnThread</span><span class="o">(</span><span class="n">agentRunner</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Once you run the program you may observe something similar to bellow output. Where Idle strategy logs work count or indicate thread sleep. 
In duty cycles where work count is zero. Thread sleep before execute the next duty cycle.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>random-agent], Agent started
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 1
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 2
<span class="o">[</span>random-agent], Going <span class="k">for </span>a <span class="nb">sleep</span>
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 1
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 2
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 2
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 2
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 1
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 1
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 2
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 1
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 2
<span class="o">[</span>random-agent], Going <span class="k">for </span>a <span class="nb">sleep</span>
<span class="o">[</span>random-agent] - work count <span class="o">=</span> 1
<span class="o">[</span>random-agent], Going <span class="k">for </span>a <span class="nb">sleep</span>
</code></pre></div></div>

<h1 id="is-this-a-sensible-approach">Is this a sensible approach</h1>

<p>Answer to that question simply is <strong>NO</strong>. Our objective is to process events as fast as possible. Just because a duty cycle reported no work has been done, 
does not imply that next duty cycle will also have no events or work to be done. By making thread move to voluntary context switch we waste lot of CPU cycles and reload the CPU instructions. 
But it may be also impossible to predict how work count in one duty cycle could be used to predict the work count for next duty cycle.</p>

<p>There are many Idle wait strategies Agrona provide to cater different use cases. You can use them to be more CPU conservative or less CPU conservative. 
Choice is really up to you. Each come with their own perks.</p>

<p>However, decoupling the CPU conservativeness from business logic in my opinion is what makes Agrona Agent’s standout among other threading libraries.</p>

<h1 id="writing-custom-idle-wait-strategy">Writing Custom Idle Wait Strategy</h1>

<p>Imagine extra CPU conservative idle strategy, in which the idle time exponentially increase with more and more duty cycles reporting no work has been done.
This strategy is a suitable candidate for network reconnection, or low throughput high latency message processing.</p>

<p>As illustrated bellow, after first duty cycle with zero work count, thread sleep for 100ms, and as duty cycles keep producing zero work count. The sleep time increase to 200ms, 400ms.
We usually cap the maximum wait time as the wait time will increase to very large value by few iterations. And that big of a wait is meaningless.</p>

<p><img src="/assets/img/agrona-agents/backoff.png" alt="backoff" /></p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.agrona.concurrent.IdleStrategy</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.concurrent.locks.LockSupport</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">ExponentialBackOffIdleStrategy</span> <span class="kd">implements</span> <span class="nc">IdleStrategy</span> <span class="o">{</span>

    <span class="kd">private</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">maxBackOff</span><span class="o">;</span>

    <span class="kd">private</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">initialBackoff</span><span class="o">;</span>

    <span class="kd">private</span> <span class="kt">long</span> <span class="n">backoffCounter</span><span class="o">;</span>

    <span class="kd">private</span> <span class="kt">long</span> <span class="n">currentBackOff</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>

    <span class="kd">public</span> <span class="nf">ExponentialBackOffIdleStrategy</span><span class="o">(</span><span class="kt">long</span> <span class="n">initialBackoff</span><span class="o">,</span> <span class="kt">long</span> <span class="n">maxBackOff</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">maxBackOff</span> <span class="o">=</span> <span class="n">maxBackOff</span><span class="o">;</span>
        <span class="k">this</span><span class="o">.</span><span class="na">initialBackoff</span> <span class="o">=</span> <span class="n">initialBackoff</span><span class="o">;</span>
    <span class="o">}</span>


    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">idle</span><span class="o">(</span><span class="kt">int</span> <span class="n">workCount</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">workCount</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">backoffCounter</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
            <span class="n">currentBackOff</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">currentBackOff</span> <span class="o">&lt;</span> <span class="n">maxBackOff</span><span class="o">)</span> <span class="o">{</span>
                <span class="kt">long</span> <span class="n">sleepTime</span> <span class="o">=</span> <span class="n">initialBackoff</span> <span class="o">*</span> <span class="nc">Math</span><span class="o">.</span><span class="na">round</span><span class="o">(</span><span class="nc">Math</span><span class="o">.</span><span class="na">pow</span><span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="n">backoffCounter</span><span class="o">));</span>
                <span class="n">backoffCounter</span><span class="o">++;</span>
                <span class="n">currentBackOff</span> <span class="o">=</span> <span class="nc">Math</span><span class="o">.</span><span class="na">min</span><span class="o">(</span><span class="n">sleepTime</span><span class="o">,</span> <span class="n">maxBackOff</span><span class="o">);</span>
                <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">"A new backoff calculated %dms\n"</span><span class="o">,</span> <span class="nc">Math</span><span class="o">.</span><span class="na">round</span><span class="o">(</span><span class="n">currentBackOff</span> <span class="o">*</span> <span class="mi">1</span><span class="n">e</span><span class="o">-</span><span class="mi">6</span><span class="o">));</span>
            <span class="o">}</span>

            <span class="nc">LockSupport</span><span class="o">.</span><span class="na">parkNanos</span><span class="o">(</span><span class="n">currentBackOff</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">idle</span><span class="o">()</span> <span class="o">{</span>
        <span class="nc">LockSupport</span><span class="o">.</span><span class="na">parkNanos</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">initialBackoff</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">reset</span><span class="o">()</span> <span class="o">{</span>

    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">alias</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="nc">IdleStrategy</span><span class="o">.</span><span class="na">super</span><span class="o">.</span><span class="na">alias</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>]]></content><author><name>Isuru</name></author><category term="performance-engineering" /><category term="top" /><summary type="html"><![CDATA[Agrona]]></summary></entry><entry><title type="html">Web-log (a.k.a blog) using GitHub pages</title><link href="https://isurudevj.github.io/top/scratch/2024/05/13/writing-blogs-github.html" rel="alternate" type="text/html" title="Web-log (a.k.a blog) using GitHub pages" /><published>2024-05-13T06:00:00+00:00</published><updated>2024-05-13T06:00:00+00:00</updated><id>https://isurudevj.github.io/top/scratch/2024/05/13/writing-blogs-github</id><content type="html" xml:base="https://isurudevj.github.io/top/scratch/2024/05/13/writing-blogs-github.html"><![CDATA[<h1 id="writing-blogs-using-github-pages">Writing blogs using GitHub pages</h1>

<p>GitHub-pages is a way of hosting <a href="https://jekyllrb.com/">Jekyll</a> based static sites.
I decided to use it to host my tech blog. After all we are all living in the age of <strong>everything as code</strong>.</p>

<p>So why not <strong>blog as code</strong>.</p>

<p>This post is a <strong>TL;DR</strong> and a compilation of sources for more details.</p>

<h1 id="specification-for-a-blog-solution">Specification for a blog solution</h1>

<ol>
  <li>Should have ability to run locally and test before publishing (offline support)</li>
  <li>Version control</li>
  <li>Use markdown as the formatter for writing</li>
  <li>Have good templating support</li>
</ol>

<h1 id="solution-1---self-hosting">Solution 1 - Self Hosting</h1>

<p><a href="https://jekyllrb.com/">Jekyll</a> is site generator where you can write content for your site using markdown format. 
There are many <a href="https://jekyllrb.com/docs/themes/">themes</a> available as well, to bootstrap your site code. 
I used to host my blog by running jekyll server on <a href="https://aws.amazon.com/pm/ec2/">Amazon EC2</a> instance and using
<a href="https://aws.amazon.com/route53/">AWS Route 53</a> Authoritative DNS server to route traffic to the <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-instance-addressing.html#concepts-public-addresses">public IP</a> of my EC2 instance. 
To enable TLS I used <a href="https://letsencrypt.org/">Let’s Encrypt</a> to get a server certificate which is valid for 3 months.
And use the <a href="https://certbot.eff.org/">certbot</a> to renew my server certificate automatically.</p>

<p>That’s a lot of work, also recurring cost for AWS infrastructure.</p>

<h1 id="solution-2---using-github-pages">Solution 2 - Using GitHub Pages</h1>

<p>You have to create a new github repository with name <your-github-account-id>.github.io.</your-github-account-id></p>

<p>For an example, my GitHub username is busy-spin. And the repository I use to host my blog is <a href="https://github.com/busy-spin/busy-spin.github.io">busy-spin.github.io</a>.
Publishing content to your website is as simple as pushing content to GitHub repository.</p>

<p>I can run the blog site locally as both approaches use Jekyll site generator.</p>

<h1 id="testing-site-locally">Testing Site Locally</h1>

<p>Bellow is a complete guide on how to test your site before publishing.</p>

<p><a href="https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/testing-your-github-pages-site-locally-with-jekyll">Guide - Testing your side locally with Jekyll</a></p>

<h3 id="important-commands-are">Important commands are</h3>

<p>To install the required Ruby packages</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">install</span>
</code></pre></div></div>

<p>To run the jekyll server locally</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>jekyll serve
</code></pre></div></div>

<p>Jekyll process has a file watcher so once you update the content it will auto compile it to html code.</p>

<h1 id="extras">Extras</h1>

<h2 id="adding-mermaid-flow-chart-support-for-github-pages">Adding mermaid flow chart support for GitHub pages.</h2>

<p><a href="https://mermaid.live/">mermaid.js</a> chart support is available by default in GitHub wiki, GitHub projects. 
But enabling <a href="https://mermaid.live/">mermaid.js</a> for GitHub pages is not supported out of box and also not trivial to enable. 
I found this neat trick from fellow GitHub blogger <a href="https://github.com/JackGruber/jackgruber.github.io">jackgrubber</a> - <a href="https://jackgruber.github.io/2021-05-09-Embed-Mermaid-in-Jekyll-without-plugin/">Embed-Mermaid-in-Jekyll-without-plugin</a>.</p>

<pre>
```mermaid
flowchart TD
    A[Learn difficult concepts] --&gt;|Create Experiment| B{Experiment}
    B --&gt; | Too hard to understand | C[Read more and write about it]
    B --&gt; | Got a good grip of concept | D[Write about it]
    C --&gt; | Experiment Again | B
    D --&gt; | Level up again | A
```  
</pre>

<pre><code class="language-mermaid">flowchart TD
    A[Learn difficult concepts] --&gt;|Create Experiment| B{Experiment}
    B --&gt; | Too hard to understand | C[Read more and write about it]
    B --&gt; | Got a good grip of concept | D[Write about it]
    C --&gt; | Experiment Again | B
    D --&gt; | Level up again | A
</code></pre>

<h1 id="other-helpful-references">Other Helpful References</h1>

<p><a href="https://jekyllrb.com/docs/">Jekyll Quick Start</a></p>

<p><a href="https://docs.github.com/en/pages/quickstart">GitHub Pages - Quick Start</a></p>

<p><a href="https://docs.github.com/en/get-started/writing-on-github">Writing on GitHub</a> - A complete guide on markdown</p>

<p><a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax">GitHub - Basic Writing And Formatting Syntax</a></p>]]></content><author><name>Isuru</name></author><category term="top" /><category term="scratch" /><summary type="html"><![CDATA[Writing blogs using GitHub pages]]></summary></entry><entry><title type="html">Publishing java libraries to Sonatype</title><link href="https://isurudevj.github.io/top/scratch/2024/05/05/pubishing-maven-libraries-to-sonatype.html" rel="alternate" type="text/html" title="Publishing java libraries to Sonatype" /><published>2024-05-05T10:00:00+00:00</published><updated>2024-05-05T10:00:00+00:00</updated><id>https://isurudevj.github.io/top/scratch/2024/05/05/pubishing-maven-libraries-to-sonatype</id><content type="html" xml:base="https://isurudevj.github.io/top/scratch/2024/05/05/pubishing-maven-libraries-to-sonatype.html"><![CDATA[<h1 id="step-by-step-guide-to-publish-your-library-from-github-to-sonatype">Step-by-step guide to publish your library from GitHub to Sonatype</h1>

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

<p>As a developer I use Github to share my code with others. Being a java developer, I also see the advantage 
of using java libraries as a form of sharing our work. Once java library is developed with useful release features, 
it can be deployed to <a href="https://central.sonatype.com/">Sonatype</a> maven central repository. 
Github offers the <a href="https://docs.github.com/en/actions">GitHub Actions</a> where we can seamlessly automate from code commit to library publish.</p>

<h2 id="how">How</h2>

<ol>
  <li>Create sonatype account, get token to publish maven artefacts</li>
  <li>Claim the namespace so you can publish under maven <code class="language-plaintext highlighter-rouge">&lt;groupId&gt;</code></li>
  <li>Create gpg keys, include gpg private key in your build and add the public key in to key server, this is to perform integrity checks for libraries you publish</li>
  <li>Set up secrets for your repository including sonatype username, token, gpg private key, gpg passphrase</li>
  <li>Set up GitHub action workflow so you can publish libraries from GitHub to Sonatype</li>
</ol>

<h2 id="why">Why</h2>

<h3 id="claiming-your-namespace">Claiming your namespace</h3>

<p>All my libraries go under <code class="language-plaintext highlighter-rouge">&lt;groupId&gt;io.github.busy-spin&lt;/groupId&gt;</code> I should claim this namespace in Sonatype. 
Pretty much similar to how you get a username in GitHub or blog-space.</p>

<h3 id="generate-gpg-private-key-and-public-key">Generate GPG private key and public key</h3>

<p>Standard why to perform a integrity check for your libraries is to generate GPG key pair. 
Keep the private key with you (In this case keep it in GitHub Secrets).
And publish the public key to gpg key server. When you publish the artefacts, the artefacts will be signed using your private key. 
And Sonatype server will verify using the published public key.</p>

<h3 id="github-actions--github-secrets">GitHub Actions / GitHub Secrets</h3>

<p>As your library start getting traction, more and more people would want to collaborate with you. 
It’s easy to centralize the publishing instead of all contributors sharing GPG keys and other secrets and 
settings up the deployment related settings in their individual systems.</p>

<h2 id="steps">Steps</h2>

<h3 id="create-sonatype-account-and-get-token">Create sonatype account and get token</h3>

<p>Use following link to create account in Sonatype and create user token.</p>

<p><a href="https://central.sonatype.com/account">Generate Sonatype User Token</a></p>

<p><img src="/assets/img/2024-05-06_01/account_and_token.png" alt="Sonatype Token" /></p>

<h3 id="claim-your-namespace">Claim your namespace</h3>

<p>Use the following link to claim the namespace which is your maven <code class="language-plaintext highlighter-rouge">&lt;groupId&gt;</code></p>

<p><a href="https://central.sonatype.com/publishing/namespaces">Claim Namespace</a></p>

<p><img src="/assets/img/2024-05-06_01/claim_namespace.png" alt="Cliam Namespace" /></p>

<h3 id="generate-gpg-key">Generate GPG key</h3>

<p>Use following guide on generating gpg-key pair and publishing the public key to gpg key server</p>

<p><a href="https://central.sonatype.org/publish/requirements/gpg/">Generate GPG Keys</a></p>

<p>Use following command to copy your GPG private key, replace <code class="language-plaintext highlighter-rouge">id</code> with your GPG key pair id.
GPG private key and GPG passphrase will be later uploaded in to GitHub secrets.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg <span class="nt">--armor</span> <span class="nt">--export-secret-key</span> &lt;<span class="nb">id</span><span class="o">&gt;</span>
</code></pre></div></div>

<h3 id="setting-up-secrets">Setting up secrets</h3>

<p>Use following guide on more details on settings up secrets.</p>

<p><a href="https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions">Adding Github Secrets</a></p>

<p>I need to configure four secrets to support the GitHub Actions that I will later set up.</p>

<p><img src="/assets/img/2024-05-06_01/github_secrets.png" alt="Secrets" /></p>

<table>
  <thead>
    <tr>
      <th>Secret</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>OSSRH_USERNAME</td>
      <td>Sonatype token username</td>
    </tr>
    <tr>
      <td>OSSRH_TOKEN</td>
      <td>Sonatype token</td>
    </tr>
    <tr>
      <td>GPG_PASSPHRASE</td>
      <td>Passphrase use while generating GPG keypair</td>
    </tr>
    <tr>
      <td>GPG_PRIVATE_KEY</td>
      <td>GPG private key which we got using <code class="language-plaintext highlighter-rouge">--export-secret-key</code> option</td>
    </tr>
  </tbody>
</table>

<h3 id="github-acton-to-deploy-to-sonatype">GitHub acton to deploy to Sonatype</h3>

<p>Set up <a href="https://docs.github.com/en/actions">GitHub Actions</a> to deploy library to Sonatype</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Publish package to the Maven Central Repository</span>
<span class="na">on</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">workflow_dispatch</span><span class="pi">]</span>
<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up JDK </span><span class="m">17</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-java@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">distribution</span><span class="pi">:</span> <span class="s1">'</span><span class="s">temurin'</span>
          <span class="na">java-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">17'</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build with Maven</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">mvn -B package --file pom.xml</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Apache Maven Central</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-java@v4</span>
        <span class="na">with</span><span class="pi">:</span> <span class="c1"># running setup-java again overwrites the settings.xml</span>
          <span class="na">distribution</span><span class="pi">:</span> <span class="s1">'</span><span class="s">temurin'</span>
          <span class="na">java-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">17'</span>
          <span class="na">server-id</span><span class="pi">:</span> <span class="s">maven</span> <span class="c1"># Value of the distributionManagement/repository/id field of the pom.xml</span>
          <span class="na">server-username</span><span class="pi">:</span> <span class="s">MAVEN_USERNAME</span> <span class="c1"># env variable for username in deploy</span>
          <span class="na">server-password</span><span class="pi">:</span> <span class="s">MAVEN_CENTRAL_TOKEN</span> <span class="c1"># env variable for token in deploy</span>
          <span class="na">gpg-private-key</span><span class="pi">:</span> <span class="s">$</span> <span class="c1"># Value of the GPG private key to import</span>
          <span class="na">gpg-passphrase</span><span class="pi">:</span> <span class="s">MAVEN_GPG_PASSPHRASE</span> <span class="c1"># env variable for GPG private key passphrase</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Publish to Apache Maven Central</span>
        <span class="na">run</span><span class="pi">:</span>  <span class="s">mvn --batch-mode deploy -P publish-sonatype</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">MAVEN_USERNAME</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">MAVEN_CENTRAL_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">MAVEN_GPG_PASSPHRASE</span><span class="pi">:</span> <span class="s">$</span>

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

<p>Update you <code class="language-plaintext highlighter-rouge">pom.xml</code> to include gpg and sonatype publisher plugins</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nt">&lt;profiles&gt;</span>
    <span class="nt">&lt;profile&gt;</span>
        <span class="nt">&lt;id&gt;</span>publish-sonatype<span class="nt">&lt;/id&gt;</span>
        <span class="nt">&lt;build&gt;</span>
            <span class="nt">&lt;plugins&gt;</span>
                <span class="nt">&lt;plugin&gt;</span>
                    <span class="nt">&lt;groupId&gt;</span>org.sonatype.central<span class="nt">&lt;/groupId&gt;</span>
                    <span class="nt">&lt;artifactId&gt;</span>central-publishing-maven-plugin<span class="nt">&lt;/artifactId&gt;</span>
                    <span class="nt">&lt;version&gt;</span>0.4.0<span class="nt">&lt;/version&gt;</span>
                    <span class="nt">&lt;extensions&gt;</span>true<span class="nt">&lt;/extensions&gt;</span>
                    <span class="nt">&lt;configuration&gt;</span>
                        <span class="nt">&lt;publishingServerId&gt;</span>maven<span class="nt">&lt;/publishingServerId&gt;</span>
                        <span class="nt">&lt;tokenAuth&gt;</span>true<span class="nt">&lt;/tokenAuth&gt;</span>
                    <span class="nt">&lt;/configuration&gt;</span>
                <span class="nt">&lt;/plugin&gt;</span>
                <span class="nt">&lt;plugin&gt;</span>
                    <span class="nt">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="nt">&lt;/groupId&gt;</span>
                    <span class="nt">&lt;artifactId&gt;</span>maven-gpg-plugin<span class="nt">&lt;/artifactId&gt;</span>
                    <span class="nt">&lt;version&gt;</span>1.6<span class="nt">&lt;/version&gt;</span>
                    <span class="nt">&lt;executions&gt;</span>
                        <span class="nt">&lt;execution&gt;</span>
                            <span class="nt">&lt;id&gt;</span>sign-artifacts<span class="nt">&lt;/id&gt;</span>
                            <span class="nt">&lt;phase&gt;</span>verify<span class="nt">&lt;/phase&gt;</span>
                            <span class="nt">&lt;goals&gt;</span>
                                <span class="nt">&lt;goal&gt;</span>sign<span class="nt">&lt;/goal&gt;</span>
                            <span class="nt">&lt;/goals&gt;</span>
                            <span class="nt">&lt;configuration&gt;</span>
                                <span class="nt">&lt;gpgArguments&gt;</span>
                                    <span class="nt">&lt;arg&gt;</span>--pinentry-mode<span class="nt">&lt;/arg&gt;</span>
                                    <span class="nt">&lt;arg&gt;</span>loopback<span class="nt">&lt;/arg&gt;</span>
                                <span class="nt">&lt;/gpgArguments&gt;</span>
                            <span class="nt">&lt;/configuration&gt;</span>
                        <span class="nt">&lt;/execution&gt;</span>
                    <span class="nt">&lt;/executions&gt;</span>
                <span class="nt">&lt;/plugin&gt;</span>
            <span class="nt">&lt;/plugins&gt;</span>
        <span class="nt">&lt;/build&gt;</span>
    <span class="nt">&lt;/profile&gt;</span>
<span class="nt">&lt;/profiles&gt;</span>
</code></pre></div></div>

<p>Execute the GitHub Action</p>

<p><img src="/assets/img/2024-05-06_01/run_action.png" alt="GitHub Action Run" /></p>

<p>Lastly login to Sonatype to publish the uploaded library.</p>

<p><a href="https://central.sonatype.com/publishing/deployments">Publishing Deployment</a></p>

<p><img src="/assets/img/2024-05-06_01/publish_sonatype.png" alt="Publish Sonatype" /></p>

<h1 id="refrences">Refrences</h1>

<p>Refer to my <a href="https://github.com/busy-spin/aeron-maven-plugins">aeron-maven-plugins</a> GitHub project.</p>]]></content><author><name>Isuru</name></author><category term="top" /><category term="scratch" /><summary type="html"><![CDATA[Step-by-step guide to publish your library from GitHub to Sonatype]]></summary></entry></feed>