<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Blog Title</title>
  <subtitle>This is a longer description about your blog.</subtitle>
  <link href="https://example.com/slinkydeveloper.github.io/feed/feed.xml" rel="self" />
  <link href="https://example.com/slinkydeveloper.github.io/" />
  <updated>2025-10-03T00:00:00Z</updated>
  <id>https://example.com/slinkydeveloper.github.io/</id>
  <author>
    <name>Your Name</name>
  </author>
  <entry>
    <title>Designing APIs for vibe coding</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/" />
    <updated>2025-10-03T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/</id>
    <content type="html">&lt;p&gt;Vibe coding is a point of no return. The &lt;em&gt;fine art&lt;/em&gt; of designing APIs and crafting great DevEx? It needs to adapt to this new reality.&lt;/p&gt;
&lt;p&gt;So how do we actually do that?&lt;/p&gt;
&lt;h2 id=&quot;before-vibe-coding&quot;&gt;Before vibe coding&lt;/h2&gt;
&lt;p&gt;I&#39;ve been doing API design for a while now, nitpicking on parameter names, function overloads, you name it.
Over the years, I picked up from my mentors a few principles that I&#39;d call &lt;strong&gt;Developer Experience canon&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;First, your API needs to be &lt;strong&gt;idiomatic&lt;/strong&gt; for the target audience.
Being idiomatic covers a lot of ground: obvious stuff like naming conventions, but also the more subtle unwritten rules.
If factory methods in a language follow a certain pattern in popular libraries, you better have a damn good reason to do it differently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Integrability&lt;/strong&gt; is another big one. Your API doesn&#39;t live in a vacuum—it&#39;s used alongside other APIs.
If you&#39;re returning something iterable, use the standard library iterator type, as this most likely makes your type usable with other APIs.&lt;/p&gt;
&lt;p&gt;And finally, &lt;strong&gt;discoverability&lt;/strong&gt;: can people actually &lt;em&gt;find&lt;/em&gt; your API features?
Developers discover APIs in different ways: skimming docs, reading examples, or just downloading a template and &lt;code&gt;CTRL&lt;/code&gt; + &lt;code&gt;Space&lt;/code&gt;-ing their way through.
As an API designer, you need to optimize for one or more of these learning paths.&lt;/p&gt;
&lt;p&gt;To get concrete, let&#39;s look at the design choices behind the &lt;code&gt;Context&lt;/code&gt; API from the Restate TypeScript SDK:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; restate &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@restatedev/restate-sdk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;myFn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; restate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Output&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mySideEffect&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s the problem: &lt;strong&gt;people don&#39;t know what Restate can do&lt;/strong&gt; unless they read a good chunk of the docs and examples.
So we put all the important features in one type called &lt;code&gt;Context&lt;/code&gt; (a pretty common name for this kind of thing).
Users can just hit &lt;code&gt;CTRL&lt;/code&gt; + &lt;code&gt;SPACE&lt;/code&gt; and boom—all the features we want them to discover:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/uXMK5kUGQO-1112.avif 1112w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/uXMK5kUGQO-1112.webp 1112w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/uXMK5kUGQO-1112.png&quot; alt=&quot;Vs code autocompletion for Context&quot; title=&quot;Vs code autocompletion for Context&quot; width=&quot;1112&quot; height=&quot;536&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;You get &lt;code&gt;run&lt;/code&gt; for recording side effects, &lt;code&gt;sleep&lt;/code&gt; for delaying execution, &lt;code&gt;serviceClient&lt;/code&gt; for calling other services, and more.&lt;/p&gt;
&lt;p&gt;We went even further: when you define a restate function using &lt;code&gt;restate.serve({...options})&lt;/code&gt;, the &lt;code&gt;Context&lt;/code&gt; parameter is required by the type system—you&#39;ll get a type error without it.
Technically Restate functions don&#39;t &lt;em&gt;need&lt;/em&gt; the &lt;code&gt;Context&lt;/code&gt;, but we make it mandatory anyway so users &lt;strong&gt;know&lt;/strong&gt; it exists.&lt;/p&gt;
&lt;p&gt;This works well for us, but we could&#39;ve gone different routes.&lt;/p&gt;
&lt;p&gt;For example, we could&#39;ve just had individual functions you import and ditched the &lt;code&gt;Context&lt;/code&gt; entirely:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; restate &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@restatedev/restate-sdk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;myFn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Output&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; restate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mySideEffect&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sure, this looks cleaner in examples, but discoverability takes a hit. &lt;code&gt;CTRL&lt;/code&gt; + &lt;code&gt;Space&lt;/code&gt; would give you a ton of results, including stuff you shouldn&#39;t use inside a restate function (like &lt;code&gt;serve&lt;/code&gt;).
How do you fix that? Maybe another import like &lt;code&gt;import * as restateFeature from &amp;quot;@restatedev/restate-sdk/features&amp;quot;&lt;/code&gt;? But then users need to know about that import. And good luck naming that namespace!&lt;/p&gt;
&lt;p&gt;Every design has tradeoffs. With our &lt;code&gt;Context&lt;/code&gt; approach, you have to pass it through your code instead of just importing a function anywhere. There&#39;s no free lunch!&lt;/p&gt;
&lt;h2 id=&quot;what-changed-with-vibe-coding&quot;&gt;What changed with vibe coding&lt;/h2&gt;
&lt;p&gt;Let&#39;s look at the example that gave me the idea for this post:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/RFlYvsreOk-825.avif 825w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/RFlYvsreOk-825.webp 825w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/RFlYvsreOk-825.png&quot; alt=&quot;Vs code autocompletion for service client&quot; title=&quot;Vs code autocompletion for service client&quot; width=&quot;825&quot; height=&quot;205&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;This API is another example of optimizing for autocompletion: you pass the service to &lt;code&gt;ctx.serviceClient&lt;/code&gt;, and some TypeScript type manipulation plus &lt;code&gt;Proxy&lt;/code&gt; gives you a fully typed client with autocompletion.&lt;/p&gt;
&lt;p&gt;Let me run a test with Copilot using GPT as the model:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In my function, can you call anotherFn using the context?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/oI8kw20SuB-1228.avif 1228w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/oI8kw20SuB-1228.webp 1228w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/oI8kw20SuB-1228.png&quot; alt=&quot;First prompt result&quot; title=&quot;First prompt result&quot; width=&quot;1228&quot; height=&quot;215&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Hallucinating methods that don&#39;t exist. Let me try again:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/taHgiLnX9k-1102.avif 1102w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/taHgiLnX9k-1102.webp 1102w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/taHgiLnX9k-1102.png&quot; alt=&quot;Second prompt result&quot; title=&quot;Second prompt result&quot; width=&quot;1102&quot; height=&quot;182&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Still making things up. Different prompt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In my function, can you send a request to anotherFn using the context?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/ULD_RnLG3i-1256.avif 1256w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/ULD_RnLG3i-1256.webp 1256w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/ULD_RnLG3i-1256.png&quot; alt=&quot;Fourth prompt result&quot; title=&quot;Fourth prompt result&quot; width=&quot;1256&quot; height=&quot;207&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Switch to Claude Sonnet 4, and after a few tries, we finally get it right:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/LFWTTMGukp-627.avif 627w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/LFWTTMGukp-627.webp 627w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/designing-apis-for-vibe-coding/LFWTTMGukp-627.png&quot; alt=&quot;Third prompt result&quot; title=&quot;Third prompt result&quot; width=&quot;627&quot; height=&quot;1188&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;With older models, Copilot would make up even more unique stuff, trying to invent a &lt;code&gt;fetch&lt;/code&gt;-like API:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  service&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;anotherService&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  handler&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;anotherFn&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  body&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my input&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is creative, but completely made up.&lt;/p&gt;
&lt;p&gt;Sure, LLMs get better over time. Eventually they get trained on your APIs or pick up feedback. And yeah, they&#39;re probabilistic—they won&#39;t always nail it.&lt;/p&gt;
&lt;p&gt;But here&#39;s the thing: a vibe coder will throw prompts like these at your API, and you want them building features, not debugging LLM hallucinations.
They won&#39;t discover your API by reading docs—they&#39;ll discover it by &lt;strong&gt;asking the LLM to solve a problem&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This leads to a new design principle: maximize your API&#39;s &lt;strong&gt;vibe-ability&lt;/strong&gt;—the ease with which developers can &lt;strong&gt;discover and correctly use&lt;/strong&gt; your API through LLM-assisted coding.&lt;/p&gt;
&lt;h2 id=&quot;why-is-the-vibe-ability-so-low&quot;&gt;Why is the vibe-ability so low?&lt;/h2&gt;
&lt;p&gt;Not totally sure, but I&#39;ve got a few theories for &lt;code&gt;serviceClient&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, the type manipulation/proxy approach. The LLM needs specific training on that:
it can&#39;t just look at the &lt;code&gt;Context&lt;/code&gt; type, pick the right method, and fill in the parameters.
It needs to deeply understand TypeScript&#39;s type system, perhaps using the LSP output in a feedback loop. That, or it needs to be trained on your API examples.&lt;/p&gt;
&lt;p&gt;Second theory: in the concept space of &lt;em&gt;TypeScript&lt;/em&gt; and &lt;em&gt;requests&lt;/em&gt;,
&lt;code&gt;fetch&lt;/code&gt; and plain function calls are the closest neighbors the LLM can think of.
During training, the model probably saw &lt;code&gt;fetch&lt;/code&gt; way more than client APIs like ours.&lt;/p&gt;
&lt;h2 id=&quot;how-do-i-increase-vibe-ability&quot;&gt;How do I increase vibe-ability?&lt;/h2&gt;
&lt;p&gt;I don&#39;t have a scientific answer—and I&#39;d love to know if one exists.
But after testing different prompts against our APIs, my empirical takeaway is simple: &lt;strong&gt;flat and verbose&lt;/strong&gt; beats concise, &lt;strong&gt;conformity&lt;/strong&gt; beats originality.&lt;/p&gt;
&lt;p&gt;It&#39;s a bit unfortunate—this likely means less innovative API designs will make it into the wild. Perhaps even worse, adoption will become a tougher game for new programming languages.&lt;/p&gt;
&lt;p&gt;Now every API I design gets a final test: I throw it at ChatGPT, Gemini, and Claude, ask them to assume the API exists, then see how close they get to what I&#39;ve designed.
To check if the new API vibes for real 🔮👩‍💻🌌&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Rust macros are not just about DRY</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/rust-macros-are-not-just-about-dry/" />
    <updated>2025-02-05T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/rust-macros-are-not-just-about-dry/</id>
    <content type="html">&lt;p&gt;In the last weeks I&#39;ve been re-working some internals of &lt;a href=&quot;https://restate.dev/&quot;&gt;Restate&lt;/a&gt; that enable the SDKs used by our users to interact with the Restate server. At a glance, the Restate server talks with the SDKs using a protocol on top of HTTP that transports few Protobuf messages, prefixed by a header containing message type, length and flags. Easy enough.&lt;/p&gt;
&lt;p&gt;If you&#39;ve ever found yourself implementing something similar, you know there&#39;s a lot of boring &lt;code&gt;match&lt;/code&gt;es involved in the process: convert from the message type in the code to some &lt;code&gt;MessageType&lt;/code&gt; enum, then from &lt;code&gt;MessageType&lt;/code&gt; enum to the &lt;code&gt;prost::Message&lt;/code&gt; implementation, and so on and so forth. And often you also have some additional intrinsic properties of the Message themselves that you need to represent in the &lt;code&gt;MessageType&lt;/code&gt; enum, leading to again more &lt;code&gt;match&lt;/code&gt;es.&lt;/p&gt;
&lt;p&gt;Sounds like a perfect case for a nice shiny macro right?&lt;/p&gt;
&lt;h2 id=&quot;the-argument-against-macros&quot;&gt;The argument against macros&lt;/h2&gt;
&lt;p&gt;Let&#39;s face it, unless you&#39;re a macro magician, macros aren&#39;t super approachable to develop and maintain for all Rust developers. And I think this is true both for the world of procedural macros and for &lt;code&gt;macro_rules!&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In order to develop a procedural macro, you need to dig deep into the Rust syntax tree data model, requiring a lot of code to handle all the different cases correctly, especially when adding generics to the mix. On top, it requires to set up a different crate, perhaps leading you to other issues that you didn&#39;t need to deal with in the first place. If you&#39;ve never developed a Rust procedural macro, I suggest to look at &lt;a href=&quot;https://github.com/dtolnay/thiserror/tree/master/impl&quot;&gt;&lt;code&gt;thiserror&lt;/code&gt;&lt;/a&gt;, it&#39;s one of the best procedural macro implementations I&#39;ve read so far.&lt;/p&gt;
&lt;p&gt;When it comes to &lt;code&gt;macro_rules!&lt;/code&gt;, well well well... Substitutions can be challenging to understand, both from a syntax perspective but also because of the matching/expansion rules Rust applies, that sometimes are sort of unexpected. There&#39;s a reason why the Rust Macro book has a section only about &lt;a href=&quot;https://veykril.github.io/tlborm/decl-macros/minutiae.html&quot;&gt;Minutiae&lt;/a&gt;, which you definitely encounter and exploit too.&lt;/p&gt;
&lt;p&gt;At the end of the day, procedural macros and &lt;code&gt;macro_rules!&lt;/code&gt; are respectively a framework and a separate language within Rust, and as such it needs people that know them in order to mantain them.&lt;/p&gt;
&lt;p&gt;Because of that, it is fair that some people refrain from creating macros, especially when they don&#39;t bring enough value. You probably heard or read somewhere arguments like &amp;quot;if you need 100 lines of &lt;code&gt;macro_rules&lt;/code&gt; to generate 120 lines of code, then it&#39;s not worth it&amp;quot;. I think there&#39;s more to macros though, than just ratio between macro LOCs vs generated LOCs.&lt;/p&gt;
&lt;h2 id=&quot;macros-are-not-just-about-dry&quot;&gt;Macros are not just about DRY&lt;/h2&gt;
&lt;p&gt;Macros have way more benefits than just DRY, which I can sum up as follows: macros are an excellent tool to develop a DSL evaluated at compile time!&lt;/p&gt;
&lt;p&gt;Let&#39;s go back to our protocol: we need to describe the message types with their various intrinsic properties, and whether we achieve that through &lt;code&gt;if&lt;/code&gt;s, &lt;code&gt;match&lt;/code&gt;es, a bunch of traits with dynamic dispatch, that&#39;s just an implementation detail. This is exactly what I would use a DSL for!&lt;/p&gt;
&lt;p&gt;By skimming through the &lt;a href=&quot;https://github.com/restatedev/restate/blob/faa2376b5c0f8eb46aeff500d04107ee212d91da/crates/service-protocol-v4/src/message_codec/mod.rs&quot;&gt;protocol message macro&lt;/a&gt; here:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Syntax: &amp;lt;Message type&gt; &amp;lt;Message kind&gt; &amp;lt;flags&gt;* = &amp;lt;code&gt;,&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;gen_message!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Start&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Control&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Call&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Command&lt;/span&gt; allows_ack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x040D&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Call&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CompletionNotification&lt;/span&gt; noparse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x800D&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Signal&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Notification&lt;/span&gt; noparse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xFBFF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// [...]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can quite easily deduct for a given message type what is the message kind, the message code, the flags, and so on.
With the help of this macro, I can define with a simple DSL my data model with all its properties and the macro generates for me the code implementing it, including the &lt;code&gt;MessageType&lt;/code&gt; enum, methods like &lt;code&gt;MessageType::code()&lt;/code&gt;, &lt;code&gt;MessageType::requires_ack()&lt;/code&gt;, encoding/decoding of messages, and so on. Even better, I can now change and/or expand the generated code, without needing to go through the &lt;code&gt;match&lt;/code&gt; statements one by one fixing the individual bits.&lt;/p&gt;
&lt;p&gt;At the end of the day, macros are an awesome tool to define DSLs to describe your data model at compile time!&lt;/p&gt;
&lt;p&gt;We have other good examples of macros like this in Restate, for example the &lt;code&gt;define_table!&lt;/code&gt; macro, that we use to define our SQL tables in Datafusion:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;define_table!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sys_journal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// [Invocation ID](/operate/invocation#invocation-identifier).&lt;/span&gt;
    id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LargeUtf8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// The index of this journal entry.&lt;/span&gt;
    index&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UInt32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// The entry type. You can check all the available entry types in [`entries.rs`](https://github.com/restatedev/restate/blob/main/crates/types/src/journal/entries.rs).&lt;/span&gt;
    entry_type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LargeUtf8&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// [...]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This macro generates for us few data structures, including the &lt;code&gt;Schema&lt;/code&gt; used by DataFusion, and the &lt;code&gt;RowBuilder&lt;/code&gt; to fill the DataFusion rows during a table scan.&lt;/p&gt;
&lt;p&gt;What&#39;s even more interesting in this macro is that we reuse the &lt;code&gt;///&lt;/code&gt; doc comments by copying them in a static string (compiled behind a feature gate) and we read them later with a tool to generate our SQL documentation.&lt;/p&gt;
&lt;h2 id=&quot;use-with-moderation&quot;&gt;Use with moderation&lt;/h2&gt;
&lt;p&gt;I hope I gave you some good examples of how to use Rust macros. As every &amp;quot;advanced&amp;quot; feature in every programming language, you should ponder pragmatically whether your problem can use a macro or not.&lt;/p&gt;
&lt;p&gt;Few tips I can give are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Refrain from overcomplicated macro input syntaxes when you can and rather try to stick to familiar things such as assignment like syntax or struct/enum definition like syntax.&lt;/li&gt;
&lt;li&gt;Document the macro input syntax and the generated output (&lt;strong&gt;of course!&lt;/strong&gt;), no matter if it&#39;s an internal macro or something you expose in a Rust crate you publish/maintain.&lt;/li&gt;
&lt;li&gt;Before delving into proper &lt;code&gt;macro_rules!&lt;/code&gt; implementations, read the &lt;a href=&quot;https://veykril.github.io/tlborm/decl-macros/macros-methodical.html&quot;&gt;Rust macro book&lt;/a&gt;, and generally stick to the patterns described in the book. Most of the &lt;code&gt;macro_rules!&lt;/code&gt; I developed so far are a combination of push down accumulators and TT munchers.&lt;/li&gt;
&lt;li&gt;If you use RustRover/Intellij for Rust, crank up max heap before touching a macro code!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope you liked this post, I plan to do more of that in the future, so stay tuned for updates!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>JUnit 5, Parallel tests, Extensions and ThreadLocal</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/JUnit-5-Parallel-tests-Extensions-and-ThreadLocal/" />
    <updated>2022-02-19T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/JUnit-5-Parallel-tests-Extensions-and-ThreadLocal/</id>
    <content type="html">&lt;p&gt;In the &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink&lt;/a&gt; community we&#39;re in the process of &lt;a href=&quot;https://lists.apache.org/thread/jsjvc2cqb91pyh47d4p6olk3c1vxqm3w&quot;&gt;porting our huge test codebase to JUnit 5&lt;/a&gt;. In order to leverage as much as we can the new JUnit 5 features, in the past days I&#39;ve spent some time playing around with it.&lt;/p&gt;
&lt;p&gt;In this blog post I&#39;ll talk about my story about enabling the project to use the new JUnit 5 features, including parallel execution, parametrized tests and extensions, and how they&#39;re going to help us improve our test codebase.&lt;/p&gt;
&lt;h2 id=&quot;our-test-codebase&quot;&gt;Our test codebase&lt;/h2&gt;
&lt;p&gt;In Flink we&#39;ve all kind of tests you can think of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple unit tests with a certain degree of mocking&lt;/li&gt;
&lt;li&gt;Integration tests&lt;/li&gt;
&lt;li&gt;End-to-end tests&lt;/li&gt;
&lt;li&gt;Various other tests for utilities, such as tests for our bash scripts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In most of the codebase, we refer to integration tests as tests that define and run a streaming job, but use a mocked sink and source. While end-to-end tests are like integration tests, but they use real external systems, such as Kafka, deployed with &lt;a href=&quot;https://www.testcontainers.org/test_framework_integration/junit_5/&quot;&gt;TestContainers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In particular in Flink SQL, &lt;em&gt;unsurprisingly&lt;/em&gt;, we have a lot of integration tests, because each single feature requires to be &amp;quot;understood&amp;quot; by all the different moving parts of our stack. For example, take the built-in function &lt;code&gt;COALESCE&lt;/code&gt;: it has a runtime implementation, a Table API expression &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/JUnit-5-Parallel-tests-Extensions-and-ThreadLocal/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, a custom logic for arguments type inference and return type inference, an optimizer rule that removes the call when possible. Each of these single pieces need to work in harmony, and integration tests usually gives us the guarantee that everything fits together.&lt;/p&gt;
&lt;p&gt;Another aspect of JUnit 5 tests is that we have a lot of test bases and of parametrized test bases, à la Junit 4. This is due to the organic growth of the project and the effort to try to standardize certain test aspects, like starting and stopping a Flink &lt;code&gt;MiniCluster&lt;/code&gt;, the embedded Flink cluster to run test jobs.&lt;/p&gt;
&lt;h2 id=&quot;goals&quot;&gt;Goals&lt;/h2&gt;
&lt;p&gt;In porting to JUnit 5, we want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have less test bases, but more extensions, hence composition over inheritance. This simplifies contributing new tests, as adding a new &amp;quot;capability&amp;quot; to the test suite won&#39;t require new ad-hoc test bases.&lt;/li&gt;
&lt;li&gt;Improve error reporting and test cases separation, in order to make the contributor experience nicer both when running tests from the IDE and with Maven&lt;/li&gt;
&lt;li&gt;Speedup the test suite as much as possible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In particular the last point is a hot topic, as today a CI run usually takes between 1 and a half and 2 hours, having a significant impact on the development loop of the project.&lt;/p&gt;
&lt;h2 id=&quot;parallel-tests&quot;&gt;Parallel tests&lt;/h2&gt;
&lt;p&gt;Parallel tests are, in my opinion, the killer feature of JUnit 5 and the real incentive to port JUnit 4 tests to JUnit 5.&lt;/p&gt;
&lt;p&gt;With JUnit 4 you can parallelize test execution using the build tool, for example using &lt;a href=&quot;https://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html&quot;&gt;&lt;code&gt;maven-surefire-plugin&lt;/code&gt; fork JVM feature&lt;/a&gt;. It runs tests in parallel by spawning several JVM processes, where each of them gets assigned a split of the overall list of tests to run. JUnit 5 on the other hand runs all the tests within the same JVM: the test runner manages a thread pool and takes care of assigning test cases to threads.&lt;/p&gt;
&lt;p&gt;I think the JUnit 5 approach fits best in our use case, as spawning several JVMs is very resource intensive on constrained machines such as CI runners, so it&#39;s a constant source of issues. This statement is also valid for contributor&#39;s machines, as these days just running the browser with 20+ tabs open, Spotify, Slack and the IDE can easily eat up to 16Gb of RAM. Plus they work in any IDE without additional configuration, they can help you find out thread safety bugs and the granularity of the execution is easy and flexible to configure.&lt;/p&gt;
&lt;p&gt;To start using JUnit 5 parallel tests, we just had to create a file called &lt;code&gt;junit-platform.properties&lt;/code&gt; in our test resources and add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.config.strategy = dynamic
junit.jupiter.execution.parallel.mode.default = same_thread
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration enables to opt-in specific tests/classes to run in parallel. To flag a test class to run in parallel, we need to annotate it with &lt;code&gt;@Execution(CONCURRENT)&lt;/code&gt;. Thanks to this configuration we can gradually enable parallel execution only for tests we know are safe.&lt;/p&gt;
&lt;p&gt;JUnit 5 offers the ability to configure the granularity of the parallel test execution, e.g. run all test cases from all classes in parallel, run all test cases from a class sequentially but run the test classes in parallel, etc. Check out all the available options in the &lt;a href=&quot;https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution&quot;&gt;JUnit 5 documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So I&#39;ve decided I wanted to try this new feature with some complex parametrized test base. I ended up choosing &lt;a href=&quot;https://github.com/apache/flink/blob/271c59332c794d86d221b4fe10c4435cc37d652f/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java&quot;&gt;&lt;code&gt;CastRulesTest&lt;/code&gt;&lt;/a&gt;, a parametrized unit test base checking the runtime implementation of the &lt;code&gt;CAST&lt;/code&gt; logic. Because there is no shared state whatsoever, this test suite is embarrassingly parallelizable. Just adding the annotation &lt;code&gt;@Execution(CONCURRENT)&lt;/code&gt; gave me a 3x times faster execution time for the whole suite.&lt;/p&gt;
&lt;h2 id=&quot;integration-tests-in-parallel&quot;&gt;Integration tests in parallel&lt;/h2&gt;
&lt;p&gt;That 3x totally got my attention, so I wanted to try to apply the same annotation to integration tests as well. As my next target, I&#39;ve chosen &lt;a href=&quot;https://github.com/apache/flink/blob/3cf0393d8946df3aa7a8836f5b2291791c13f215/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/BuiltInFunctionTestBase.java&quot;&gt;&lt;code&gt;BuiltInFunctionTestBase&lt;/code&gt;&lt;/a&gt;. As the name implies, this is a parametrized integration test base we use to test the correct behaviour of our built-in functions. We have around 10 classes where we use this test base, for a total of 1200+ integration test cases.&lt;/p&gt;
&lt;h3 id=&quot;porting-the-test-suite-to-junit-5&quot;&gt;Porting the test suite to JUnit 5&lt;/h3&gt;
&lt;p&gt;The first thing I had to do was to port to JUnit 5 the base class and its inheritors. My initial thought was to use &lt;a href=&quot;https://junit.org/junit5/docs/current/user-guide/#writing-tests-dynamic-tests&quot;&gt;&lt;code&gt;@TestFactory&lt;/code&gt; feature&lt;/a&gt;, a new JUnit 5 feature to spawn dynamic tests, allowing you to group test cases. Think to it as a more powerful &lt;code&gt;@ParametrizedTest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This would have allowed me to have a nice nested view of the tests in the reports. For example, look at &lt;a href=&quot;https://github.com/apache/flink/blob/3cf0393d8946df3aa7a8836f5b2291791c13f215/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/MathFunctionsITCase.java&quot;&gt;&lt;code&gt;MathFunctionsITCase&lt;/code&gt;&lt;/a&gt;: by mixing &lt;code&gt;DynamicTest&lt;/code&gt; and &lt;code&gt;DynamicContainer&lt;/code&gt; I could have achieved a report like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MathFunctionsITCase&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PLUS&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;f0 + 6 = 20&lt;/code&gt;: Success&lt;/li&gt;
&lt;li&gt;&lt;code&gt;f0 + 6 = 10&lt;/code&gt;: Failure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MINUS&lt;/code&gt;: [...]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because the test base itself already had some models to define test cases, including input/output data, query expressions and configuration (see &lt;a href=&quot;https://github.com/apache/flink/blob/3cf0393d8946df3aa7a8836f5b2291791c13f215/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/BuiltInFunctionTestBase.java#L183&quot;&gt;&lt;code&gt;TestSpec&lt;/code&gt;&lt;/a&gt;), what I had to do was simply to convert these models to &lt;code&gt;DynamicTest&lt;/code&gt;/&lt;code&gt;DynamicContainer&lt;/code&gt;. A little refactor of the &lt;a href=&quot;https://github.com/apache/flink/blob/3cf0393d8946df3aa7a8836f5b2291791c13f215/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/BuiltInFunctionTestBase.java#L87&quot;&gt;&lt;code&gt;testFunction&lt;/code&gt; method&lt;/a&gt; to wrap the logic into &lt;code&gt;DynamicTest&lt;/code&gt; did it. Now every concrete test class just had to implement the abstract method &lt;code&gt;getTestSpecs&lt;/code&gt; to return the test cases defined with my &lt;code&gt;TestSpec&lt;/code&gt; class, so the final implementation of the &lt;code&gt;@TestFactory&lt;/code&gt; just looked like:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@TestFactory&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DynamicContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTestSpecs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TestSpec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toDynamicContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every implementation class provided a &lt;code&gt;DynamicContainer&lt;/code&gt;, containing a set of tests for a specific built-in function, like &lt;code&gt;PLUS&lt;/code&gt;, and each container had a set of &lt;code&gt;DynamicTest&lt;/code&gt; with the specific test cases for that built-in function, like &lt;code&gt;f0 + 6 = 20&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Last but not least, to let the query run, I needed the extension to set up &lt;code&gt;MiniCluster&lt;/code&gt; once per class. This is already available in our &lt;code&gt;flink-test-utils&lt;/code&gt;, so with some copy-paste I enabled it:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MiniClusterWithClientExtension&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MINI_CLUSTER_RESOURCE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MiniClusterWithClientExtension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MiniClusterResourceConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setNumberTaskManagers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@RegisterExtension&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AllCallbackWrapper&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MiniClusterWithClientExtension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ALL_WRAPPER&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AllCallbackWrapper&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MINI_CLUSTER_RESOURCE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tried a first run without parallel tests, and everything ran fine. Tried to add &lt;code&gt;@Execution(CONCURRENT)&lt;/code&gt;, and Intellij IDEA welcomed my idea with a long report full of red crosses.&lt;/p&gt;
&lt;h3 id=&quot;fixing-the-minicluster-extension-first&quot;&gt;Fixing the &lt;code&gt;MiniCluster&lt;/code&gt; extension first&lt;/h3&gt;
&lt;p&gt;Looking at the logs, it became evident how the problem was &lt;code&gt;MiniClusterWithClientExtension&lt;/code&gt;, given several tests were trying to push jobs to a &lt;code&gt;MiniCluster&lt;/code&gt; already shut down.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;MiniClusterWithClientExtension&lt;/code&gt; was developed by wrapping the JUnit 4 &lt;code&gt;MiniClusterWithClientResource&lt;/code&gt; rule in a custom interface defining &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt;. Then, to register it, you could either use &lt;code&gt;AllCallbackWrapper&lt;/code&gt; or &lt;code&gt;EachCallbackWrapper&lt;/code&gt; to define whether to have one &lt;code&gt;MiniCluster&lt;/code&gt; per test class, or per single test.&lt;/p&gt;
&lt;p&gt;In JUnit 4 rules have a &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; extension point, and then when you register them, depending on whether you use &lt;code&gt;@Rule&lt;/code&gt; or &lt;code&gt;@ClassRule&lt;/code&gt;, the rule is executed for each test or once per class. In JUnit 5 the user cannot pick whether the extension is used globally or per method: it&#39;s the extension itself that defines where it can hook in the lifecycle.&lt;/p&gt;
&lt;p&gt;An example of this shift of concept between JUnit 4 and 5 is provided by the JUnit 5 &lt;code&gt;TestContainers&lt;/code&gt; integration, which depending on whether the container field is static or not, decides to share it between test methods or not&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/JUnit-5-Parallel-tests-Extensions-and-ThreadLocal/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;AllCallbackWrapper&lt;/code&gt; or &lt;code&gt;EachCallbackWrapper&lt;/code&gt; were circumventing the new JUnit 5 Extension paradigm, bringing back the same semantics of &lt;code&gt;@Rule&lt;/code&gt; or &lt;code&gt;@ClassRule&lt;/code&gt;. And this worked fine, until I tried to use parallel execution.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/apache/flink/blob/78b231f60aed59061f0f609e0cfd659d78e6fdd5/flink-test-utils-parent/flink-test-utils/src/main/java/org/apache/flink/test/util/MiniClusterWithClientExtension.java#L68&quot;&gt;&lt;code&gt;MiniClusterWithClientExtension#before&lt;/code&gt;&lt;/a&gt; method was starting &lt;code&gt;MiniCluster&lt;/code&gt;, creating some &lt;code&gt;ClusterClient&lt;/code&gt; and then setting up a thread local for the environment configuration lookup.&lt;/p&gt;
&lt;p&gt;The interaction between our &lt;code&gt;ThreadLocal&lt;/code&gt; configuration when using &lt;code&gt;AllCallbackWrapper&lt;/code&gt; didn&#39;t sound right, so I tried to do a little experiment. Take this simple extension:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestExtension&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeforeEachCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BeforeAllCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AfterEachCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AfterAllCallback&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;beforeAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExtensionContext&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;before all: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExtensionContext&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;before &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;afterEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExtensionContext&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;after &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDisplayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;afterAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExtensionContext&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;after all: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is simply going to print the thread where the various hooks are executed, and it also prints the name of single test in case in &lt;code&gt;beforeEach&lt;/code&gt;/&lt;code&gt;afterEach&lt;/code&gt;. Then I tried to run this test:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@ExtendWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TestExtension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Execution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExecutionMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONCURRENT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ParameterizedTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{0}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;, thread: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is the result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;before all: ForkJoinPool-1-worker-1
before 2: ForkJoinPool-1-worker-3
before 3: ForkJoinPool-1-worker-4
before 5: ForkJoinPool-1-worker-6
before 9: ForkJoinPool-1-worker-1
before 4: ForkJoinPool-1-worker-5
before 7: ForkJoinPool-1-worker-0
before 6: ForkJoinPool-1-worker-7
before 1: ForkJoinPool-1-worker-2
test: 7, thread: ForkJoinPool-1-worker-0
test: 9, thread: ForkJoinPool-1-worker-1
test: 4, thread: ForkJoinPool-1-worker-5
test: 1, thread: ForkJoinPool-1-worker-2
test: 6, thread: ForkJoinPool-1-worker-7
test: 2, thread: ForkJoinPool-1-worker-3
test: 3, thread: ForkJoinPool-1-worker-4
test: 5, thread: ForkJoinPool-1-worker-6
after 4: ForkJoinPool-1-worker-5
after 7: ForkJoinPool-1-worker-0
after 6: ForkJoinPool-1-worker-7
after 1: ForkJoinPool-1-worker-2
after 5: ForkJoinPool-1-worker-6
after 9: ForkJoinPool-1-worker-1
after 3: ForkJoinPool-1-worker-4
after 2: ForkJoinPool-1-worker-3
before 8: ForkJoinPool-1-worker-3
test: 8, thread: ForkJoinPool-1-worker-3
before 10: ForkJoinPool-1-worker-6
after 8: ForkJoinPool-1-worker-3
test: 10, thread: ForkJoinPool-1-worker-6
after 10: ForkJoinPool-1-worker-6
after all: ForkJoinPool-1-worker-1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I was expecting, &lt;code&gt;beforeEach&lt;/code&gt; and &lt;code&gt;afterEach&lt;/code&gt; runs in the same thread of the test, as effectively they just &amp;quot;wrap&amp;quot; the test method, as described &lt;a href=&quot;https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeEachCallback.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In other words:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is no guarantee about which thread is used to execute &lt;code&gt;beforeAll&lt;/code&gt; and &lt;code&gt;afterAll&lt;/code&gt;, but&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beforeEach&lt;/code&gt; and &lt;code&gt;afterEach&lt;/code&gt; are guaranteed to be executed within the same thread of the test&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So here was the solution: we just had to set/unset that &lt;code&gt;ThreadLocal&lt;/code&gt; &lt;strong&gt;everytime&lt;/strong&gt; in &lt;code&gt;beforeEach&lt;/code&gt;/&lt;code&gt;afterEach&lt;/code&gt;, no matter whether the &lt;code&gt;MiniCluster&lt;/code&gt; instance was meant to be per test class or per test method.&lt;/p&gt;
&lt;p&gt;I was ready to declare victory, so I did some refactoring of &lt;code&gt;MiniClusterWithClientExtension&lt;/code&gt; to always create one &lt;code&gt;MiniCluster&lt;/code&gt; per test class, I removed the wrapping around &lt;code&gt;AllCallbackWrapper&lt;/code&gt; and then I tried to run again my tests: still everything was red.&lt;/p&gt;
&lt;h3 id=&quot;back-on-parametrizedtest&quot;&gt;Back on &lt;code&gt;@ParametrizedTest&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;After some investigation, I found out that &lt;code&gt;DynamicTest&lt;/code&gt; lifecycle doesn&#39;t work per &lt;code&gt;DynamicTest&lt;/code&gt; instance, while parallelization does. For example, for this test class:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@ExtendWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TestExtension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Execution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExecutionMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONCURRENT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@TestFactory&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DynamicTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rangeClosed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapToObj&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
                  &lt;span class=&quot;token class-name&quot;&gt;DynamicTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dynamicTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;, thread: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You get this output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;before all: ForkJoinPool-1-worker-1
before tests(): ForkJoinPool-1-worker-1
after tests(): ForkJoinPool-1-worker-1
test: 2, thread: ForkJoinPool-1-worker-3
test: 6, thread: ForkJoinPool-1-worker-7
test: 3, thread: ForkJoinPool-1-worker-4
test: 9, thread: ForkJoinPool-1-worker-1
test: 7, thread: ForkJoinPool-1-worker-0
test: 1, thread: ForkJoinPool-1-worker-2
test: 4, thread: ForkJoinPool-1-worker-5
test: 5, thread: ForkJoinPool-1-worker-6
test: 8, thread: ForkJoinPool-1-worker-3
test: 10, thread: ForkJoinPool-1-worker-2
after all: ForkJoinPool-1-worker-1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you see, each &lt;code&gt;DynamicTest&lt;/code&gt; is executed in parallel, as you would expect, but the &lt;code&gt;beforeEach&lt;/code&gt; and &lt;code&gt;afterEach&lt;/code&gt; is executed only once per &lt;code&gt;@TestFactory&lt;/code&gt;. Because of this behaviour, the tests were not picking the correct &lt;code&gt;ThreadLocal&lt;/code&gt;, hence failing the test because they could not find the &lt;code&gt;MiniCluster&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It seems like there is no solution to this problem, and there is an issue open in the &lt;a href=&quot;https://github.com/junit-team/junit5/issues/378&quot;&gt;JUnit 5 issue tracker&lt;/a&gt; about whether this should be supported or not.&lt;/p&gt;
&lt;p&gt;So I had to fall back to the good old &lt;code&gt;@ParametrizedTest&lt;/code&gt;: I just removed the nested &lt;code&gt;DynamicContainer&lt;/code&gt;, and I created my own &lt;code&gt;DynamicTest&lt;/code&gt; like data structure to wrap a test &lt;code&gt;Executable&lt;/code&gt; and its display name:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** Single test case. */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Executable&lt;/span&gt; executable&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Executable&lt;/span&gt; executable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;executable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; executable&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then I kept the same code as before in the test base, but I had to add a method to flatten the previously used &lt;code&gt;DynamicContainer&lt;/code&gt;s:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TestSpec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTestSpecs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTestCases&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTestSpecs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flatMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testSpec &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; testSpec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTestCases&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation punctuation&quot;&gt;@ParameterizedTest&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@MethodSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;getTestCases&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TestCase&lt;/span&gt; testCase&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    testCase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;executable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The report doesn&#39;t look as nice as with &lt;code&gt;@TestFactory&lt;/code&gt;, but it finally works fine with parallel execution.&lt;/p&gt;
&lt;h2 id=&quot;results-and-conclusion&quot;&gt;Results and conclusion&lt;/h2&gt;
&lt;p&gt;Before the parallelization, this test suite ran in around 50 seconds from Intellij IDEA on my i7 11th Gen packed with 16Gb of RAM. After the parallelization the suite runs in around 20 seconds&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/JUnit-5-Parallel-tests-Extensions-and-ThreadLocal/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;. Not bad.&lt;/p&gt;
&lt;p&gt;I think JUnit 5 is a very powerful tool, but you need to be careful when developing extensions that are supposed to work with parallel tests. These are my gotchas from this experience:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure thread locals are set in &lt;code&gt;beforeEach&lt;/code&gt;, so they&#39;re set in the right thread, and &lt;strong&gt;cleaned up&lt;/strong&gt; in &lt;code&gt;afterEach&lt;/code&gt;, to avoid next tests reusing that thread to pick up the old &lt;code&gt;ThreadLocal&lt;/code&gt; values&lt;/li&gt;
&lt;li&gt;Be aware that the method &lt;code&gt;beforeEach&lt;/code&gt; could be invoked in parallel on the same instance of the extension, so you need to make sure the access to extension fields is thread safe, or...&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;ExtensionContext.Store&lt;/code&gt;, as it&#39;s thread safe, it&#39;s hierarchically organized per hook context, and it&#39;s particularly useful to auto cleanup objects built in the &lt;code&gt;before[..]&lt;/code&gt; methods&lt;/li&gt;
&lt;li&gt;Rather than providing accessors in the extension class, use parameter injection implementing &lt;code&gt;ParameterResolver&lt;/code&gt; together with the &lt;code&gt;ExtensionContext.Store&lt;/code&gt;. This simplifies the implementation when storing your extension state in &lt;code&gt;ExtensionContext.Store&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And last but not least, don&#39;t use &lt;code&gt;@TestFactory&lt;/code&gt; in conjunction with parallel execution if your test requires &lt;code&gt;ThreadLocal&lt;/code&gt; or other thread dependant thing correctly configured.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;The DSL provided by Flink SQL to define relational queries without SQL &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/JUnit-5-Parallel-tests-Extensions-and-ThreadLocal/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.testcontainers.org/test_framework_integration/junit_5/&quot;&gt;TestContainers - JUnit 5 integration&lt;/a&gt; &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/JUnit-5-Parallel-tests-Extensions-and-ThreadLocal/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Please take these numbers with a grain of salt, my measurements were very empirical, and not proper benchmarks &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/JUnit-5-Parallel-tests-Extensions-and-ThreadLocal/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <title>Vert.x container images with jlink</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/Vert-x-Container-images-with-jlink/" />
    <updated>2020-10-08T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/Vert-x-Container-images-with-jlink/</id>
    <content type="html">&lt;p&gt;In this blog post I&#39;m gonna show you how I managed to reduce the container image size of an Eclipse Vert.x application, creating a smaller JDK with &lt;code&gt;jdeps&lt;/code&gt; and &lt;code&gt;jlink&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The application is the data plane of &lt;a href=&quot;https://github.com/knative-sandbox/eventing-kafka-broker&quot;&gt;Knative Eventing Kafka Broker&lt;/a&gt;, an implementation of Knative &lt;code&gt;Broker&lt;/code&gt; tailored on Kafka.&lt;/p&gt;
&lt;h2 id=&quot;fat-jar&quot;&gt;Fat-jar&lt;/h2&gt;
&lt;p&gt;We didn&#39;t handwritten nor generated a &lt;code&gt;module-info.java&lt;/code&gt; for our application, we just ship a fat-jar. To generate a fat-jar, configure the &lt;code&gt;maven-shade-plugin&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;groupId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;groupId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;artifactId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;maven-shade-plugin&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;artifactId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;${maven.shade.plugin.version}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
     &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;minimizeJar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;true&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;minimizeJar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;executions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
     &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;execution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;phase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;package&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;phase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;goals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
         &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;shade&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;goals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
         &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;transformers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
           &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;transformer&lt;/span&gt;
             &lt;span class=&quot;token attr-name&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;org.apache.maven.plugins.shade.resource.ManifestResourceTransformer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
             &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;mainClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
               ${main-class}
             &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;mainClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
           &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;transformer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
         &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;transformers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
       &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
     &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;execution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;executions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;jdeps&quot;&gt;Jdeps&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;jdeps&lt;/code&gt; is the tool you must use to figure which JDK modules you depend on. If you run &lt;code&gt;jdeps&lt;/code&gt; just with the fat-jar as argument, you&#39;ll get a list of all the packages in your jar and the JDK modules it depends on:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;% jdeps receiver/target/receiver-1.0-SNAPSHOT.jar
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.lang                                          java.base
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.lang.invoke                                   java.base
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.net                                           java.base
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.time                                          java.base
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.time.format                                   java.base
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.util                                          java.base
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.util.concurrent                               java.base
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.util.function                                 java.base
   dev.knative.eventing.kafka.broker.core             -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; java.util.stream                                   java.base
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that &lt;code&gt;jdeps&lt;/code&gt; analyzes only the imports so, if you perform some reflections at runtime of JDK classes, they won&#39;t be found by the tool.&lt;/p&gt;
&lt;p&gt;To get an output that you can directly pass to &lt;code&gt;jlink&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;% jdeps &lt;span class=&quot;token parameter variable&quot;&gt;-q&lt;/span&gt; --print-module-deps --ignore-missing-deps receiver/target/receiver-1.0-SNAPSHOT.jar
java.base,java.compiler,java.naming,java.security.jgss,java.security.sasl,java.sql,jdk.management,jdk.unsupported&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the list of jdk modules we depend on. Doing a quick check, I&#39;ve found that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java.base&lt;/code&gt; it&#39;s the module that contains all the core features of the jdk&lt;/li&gt;
&lt;li&gt;&lt;code&gt;java.compiler&lt;/code&gt; contains the compiler types. It&#39;s brought in by Guava and Vert.x CLI feature.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;java.naming&lt;/code&gt; contains some JNDI types, to perform names lookups. This is required to perform DNS queries by Vert.x&lt;/li&gt;
&lt;li&gt;&lt;code&gt;java.security.jgss&lt;/code&gt; and &lt;code&gt;java.security.sasl&lt;/code&gt; contains some security protocols implementation. They&#39;re used by the Java Kafka client.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;java.sql&lt;/code&gt; contains JDBC. Jackson databind and Google Gson (that we import transitively via Protobuf json) depends on it because they provide marshallers/unmarshallers for JDBC types.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jdk.management&lt;/code&gt; contains some interfaces to manage the JDK. This is used by Micrometer to instrument the JVM and collect metrics.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jdk.unsupported&lt;/code&gt; contains &lt;code&gt;sun.misc.Unsafe&lt;/code&gt;. Netty and Protobuf use it to perform off-heap allocations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;zero-days-since-it-was-dns&quot;&gt;Zero days since it was DNS&lt;/h2&gt;
&lt;p&gt;Because some reflection is happening behind the hood to choose the Vert.x DNS resolver, &lt;code&gt;jdeps&lt;/code&gt; doesn&#39;t discover the module &lt;code&gt;jdk.naming.dns&lt;/code&gt;, that you need in your Vert.x application to enable the DNS.&lt;/p&gt;
&lt;p&gt;To resolve the deps and add the dns module:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;MODS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;jdeps &lt;span class=&quot;token parameter variable&quot;&gt;-q&lt;/span&gt; --print-module-deps --ignore-missing-deps receiver/target/receiver-1.0-SNAPSHOT.jar&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Computed mods = &#39;&lt;span class=&quot;token variable&quot;&gt;$MODS&lt;/span&gt;&#39;&quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Patch adding the dns&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;MODS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$MODS&lt;/span&gt;,jdk.naming.dns&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;create-the-jdk&quot;&gt;Create the JDK&lt;/h2&gt;
&lt;p&gt;Now you just need to invoke &lt;code&gt;jlink&lt;/code&gt; to generate your custom JDK:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;% jlink &lt;span class=&quot;token parameter variable&quot;&gt;--verbose&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  --no-header-files &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  --no-man-pages &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--compress&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  --strip-debug &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  --add-modules &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$MODS&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; ./jdk
java.base file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.base.jmod
java.compiler file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.compiler.jmod
java.logging file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.logging.jmod
java.management file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.management.jmod
java.naming file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.naming.jmod
java.security.jgss file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.security.jgss.jmod
java.security.sasl file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.security.sasl.jmod
java.sql file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.sql.jmod
java.transaction.xa file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.transaction.xa.jmod
java.xml file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/java.xml.jmod
jdk.management file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/jdk.management.jmod
jdk.naming.dns file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/jdk.naming.dns.jmod
jdk.unsupported file:///usr/lib/jvm/java-15-openjdk-15.0.0.36-1.rolling.fc32.x86_64/jmods/jdk.unsupported.jmod

Providers:
  java.base provides java.nio.file.spi.FileSystemProvider used by java.base
  java.naming provides java.security.Provider used by java.base
  java.security.jgss provides java.security.Provider used by java.base
  java.security.sasl provides java.security.Provider used by java.base
  jdk.naming.dns provides javax.naming.spi.InitialContextFactory used by java.naming
  java.management provides javax.security.auth.spi.LoginModule used by java.base
  java.logging provides jdk.internal.logger.DefaultLoggerFinder used by java.base
  jdk.management provides sun.management.spi.PlatformMBeanProvider used by java.management&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will grab the modules you provided from your local machine and will create a JDK without manual, header files, debug symbols:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;jdk
├── bin
│   ├── &lt;span class=&quot;token function&quot;&gt;java&lt;/span&gt;
│   └── keytool
├── conf
│   ├── logging.properties
│   ├── net.properties
│   ├── sdp
│   └── security
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
├── lib
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
└── release&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;complete-script-and-dockerfile&quot;&gt;Complete script and Dockerfile&lt;/h2&gt;
&lt;p&gt;This is the complete Dockerfile (simplified) that builds the project and generates the JDK:&lt;/p&gt;
&lt;pre class=&quot;language-dockerfile&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; JAVA_IMAGE=docker.io/adoptopenjdk:14-jdk-hotspot&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; BASE_IMAGE=docker.io/ubuntu:bionic&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${JAVA_IMAGE}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; builder&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /app&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# https://github.com/AdoptOpenJDK/openjdk-docker/issues/260&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; apt-get update &amp;amp;&amp;amp; apt-get install -y binutils&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Copy all my project stuff inside the container&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; . .&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Build the project&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; ./mvnw package -DskipTests -Deditorconfig.skip --no-transfer-progress&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Generate the sdk using the script (shown below)&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; ./generate_jdk.sh /app/receiver/target/receiver-1.0-SNAPSHOT.jar&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Prod image&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${BASE_IMAGE}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; running&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create appuser and directories&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; groupadd -g 999 appuser &amp;amp;&amp;amp; useradd -r -u 999 -g appuser appuser &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;&#92;&lt;/span&gt;
  mkdir /tmp/vertx-cache &amp;amp;&amp;amp; chown -R appuser:appuser /tmp/vertx-cache &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;&#92;&lt;/span&gt;
  mkdir /app&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Copy jar and jdk inside /app&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; &lt;span class=&quot;token options&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;builder&lt;/span&gt;&lt;/span&gt; /app/jdk /app/jdk&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; &lt;span class=&quot;token options&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;builder&lt;/span&gt;&lt;/span&gt; /app/receiver/target/receiver-1.0-SNAPSHOT.jar /app/app.jar&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; chown -R appuser:appuser /app&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Set appuser and configure PATH&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;USER&lt;/span&gt; appuser&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /app&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Add jdk bin to the path, so you can run the java command&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENV&lt;/span&gt; PATH=&lt;span class=&quot;token string&quot;&gt;&quot;/app/jdk/bin:${PATH}&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the &lt;code&gt;generate_jdk.sh&lt;/code&gt; script:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/usr/bin/env sh&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$#&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;No arguments supplied, You must provide the jar&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Computing mods for &lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;MODS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;jdeps &lt;span class=&quot;token parameter variable&quot;&gt;-q&lt;/span&gt; --print-module-deps --ignore-missing-deps &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;s/^[ &#92;t]*//&#39;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Computed mods = &#39;&lt;span class=&quot;token variable&quot;&gt;$MODS&lt;/span&gt;&#39;&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Patch adding the dns&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;MODS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$MODS&lt;/span&gt;,jdk.naming.dns&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Patched modules shipped with the generated jdk = &#39;&lt;span class=&quot;token variable&quot;&gt;$MODS&lt;/span&gt;&#39;&quot;&lt;/span&gt;

jlink &lt;span class=&quot;token parameter variable&quot;&gt;--verbose&lt;/span&gt; --no-header-files --no-man-pages &lt;span class=&quot;token parameter variable&quot;&gt;--compress&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; --strip-debug --add-modules &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$MODS&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; /app/jdk&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can run the built container with:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;% &lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run imagename &lt;span class=&quot;token function&quot;&gt;java&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-jar&lt;/span&gt; /app/app.jar&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;jdeps&lt;/code&gt; and &lt;code&gt;jlink&lt;/code&gt; our container image size is 2.5x smaller, which is a great achievement for us. We also plan to reduce it even further, in fact we want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Investigate some modules we depend on, but that we probably don&#39;t need: &lt;code&gt;java.compiler&lt;/code&gt; and &lt;code&gt;java.sql&lt;/code&gt; are two good candidates.&lt;/li&gt;
&lt;li&gt;Rebase our base image on &lt;code&gt;alpine&lt;/code&gt;, which uses &lt;code&gt;musl&lt;/code&gt; as libc. Look at &lt;a href=&quot;https://openjdk.java.net/projects/portola/&quot;&gt;Project Portola&lt;/a&gt; for more details.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check out the full PR: https://github.com/knative-sandbox/eventing-kafka-broker/pull/265&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Kubernetes controllers - The Empire Strikes Back</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-The-Empire-Strikes-Back/" />
    <updated>2020-10-08T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-The-Empire-Strikes-Back/</id>
    <content type="html">&lt;p&gt;In this blog post I want to introduce you to some important steps forward we made in our &lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc&quot;&gt;Extending Kubernetes API In-Process project&lt;/a&gt;. If you don&#39;t know what I&#39;m talking about, check out the previous post: &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope&quot;&gt;Kubernetes Controllers - A new hope&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We implemented the high-level ABI to start watchers on Kubernetes resources, as argued in &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope#Next-Steps&quot;&gt;Next steps&lt;/a&gt; paragraph. This allows us to identify watch requests sent by controllers and to deduplicate them.&lt;/p&gt;
&lt;p&gt;Then we worked on modifying our ABI to make it &lt;strong&gt;asynchronous&lt;/strong&gt;, in order to invoke the controllers only &lt;strong&gt;on-demand&lt;/strong&gt;. Now, we don&#39;t spin up a thread for each module, but we wake controllers up asynchronously when there is a new task to process.&lt;/p&gt;
&lt;p&gt;Thanks to these changes, we&#39;re now able to use Rust&#39;s &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; inside the module. This allowed us to realign our fork of &lt;code&gt;kube-rs&lt;/code&gt;, bringing back all the original interfaces, and run &lt;code&gt;kube-runtime&lt;/code&gt; inside the modules.&lt;/p&gt;
&lt;h2 id=&quot;the-kube-watch-abi-abi&quot;&gt;The &lt;code&gt;kube-watch-abi&lt;/code&gt; ABI&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;kube-watch-abi&lt;/code&gt; ABI was originally composed by an import and an export:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[link(wasm_import_module = &lt;span class=&quot;token string&quot;&gt;&quot;kube-watch-abi&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Returns the watch identifier&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;watch_req_ptr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; watch_req_len&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;on_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;watch_id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ev_ptr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ev_len&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// handle event&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;watch_req&lt;/code&gt; is a description of the watch to register:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;WatchRequest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; resource&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; watch_params&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WatchParams&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;controller&quot;&gt;Controller&lt;/h3&gt;
&lt;p&gt;When the module invokes the &lt;code&gt;watch&lt;/code&gt; import, it registers a new watch and returns a watch identifier. This identifier is stored together with a reference to the &lt;code&gt;Stream&amp;lt;WatchEvent&amp;gt;&lt;/code&gt; in a global map.
In similar fashion to the &lt;code&gt;request&lt;/code&gt; ABI discussed in the &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope#The-ABI&quot;&gt;previous post&lt;/a&gt;, we serialize the &lt;code&gt;WatchRequest&lt;/code&gt; data structure in order to pass it to the host.&lt;/p&gt;
&lt;p&gt;Then, every time the host has a new &lt;code&gt;WatchEvent&lt;/code&gt; that the controller needs to handle, it will invoke &lt;code&gt;on_event&lt;/code&gt; with the serialized &lt;code&gt;WatchEvent&lt;/code&gt;. Using the watch identifier, the controller will get the associated &lt;code&gt;Stream&lt;/code&gt; from the global map and append the deserialized &lt;code&gt;WatchEvent&lt;/code&gt; to it.&lt;/p&gt;
&lt;h3 id=&quot;the-host&quot;&gt;The host&lt;/h3&gt;
&lt;p&gt;When the controller invokes &lt;code&gt;watch&lt;/code&gt;, the host checks if there is a registered watch for that resource. If there is, then it just registers the invoker as interested to that watch, otherwise it starts a new watch.
Every time a watch receives a new event from Kubernetes, the host resolves all the modules interested to that particular event. For each module, it allocates some module memory to pass the event and it finally wakes the controller up by invoking &lt;code&gt;on_event&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;rust-module-with-async-await&quot;&gt;Rust module with async-await&lt;/h2&gt;
&lt;h3 id=&quot;callbacks-are-bad&quot;&gt;Callbacks are bad!&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;kube-watch-abi&lt;/code&gt; is the de-facto an asynchronous API: &lt;code&gt;watch&lt;/code&gt; starts the asynchronous operation, &lt;code&gt;on_event&lt;/code&gt; notifies on the completion of the operation.&lt;/p&gt;
&lt;p&gt;In the initial implementation of the &lt;code&gt;watch&lt;/code&gt;, on module side, I just modified the kube client &lt;code&gt;watch&lt;/code&gt; to provide a callback:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WatchEvent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Send&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ListParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Invoke watch&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every time &lt;code&gt;on_event&lt;/code&gt; was invoked, the module resolved the callback from the watch identifier and invoked it.
This was fine as an initial approach, but we soon hit a problem: Porting all of the existing Kubernetes client and runtime code from &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; to the more &lt;em&gt;primitive&lt;/em&gt; callbacks could have caused a lot of issues, making the fork diverge too much from the upstream code.&lt;/p&gt;
&lt;h3 id=&quot;how-async-await-works&quot;&gt;How &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; works&lt;/h3&gt;
&lt;p&gt;Here&#39;s a little refresh on &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; from &lt;a href=&quot;https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html&quot;&gt;Asynchronous Programming in Rust book&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;async/.await is Rust&#39;s built-in tool for writing asynchronous functions that look like synchronous code. async transforms a block of code into a state machine that implements a trait called Future. Whereas calling a blocking function in a synchronous method would block the whole thread, blocked Futures will yield control of the thread, allowing other Futures to run._&lt;/p&gt;
&lt;p&gt;Although there are a lot of details about how Rust implements the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; feature, these are the relevant concepts for this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Future&lt;/code&gt; is the type that represents an asynchronous result, e.g. &lt;code&gt;async fn&lt;/code&gt; returns a &lt;code&gt;Future&lt;/code&gt;. You can wait for the result of a &lt;code&gt;Future&lt;/code&gt; using &lt;code&gt;.await&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Stream&lt;/code&gt; is the same as &lt;code&gt;Future&lt;/code&gt;, but it returns several elements before &lt;code&gt;None&lt;/code&gt;, which notifies the end of the stream.&lt;/li&gt;
&lt;li&gt;In order to use &lt;code&gt;.await&lt;/code&gt;, you &lt;strong&gt;must&lt;/strong&gt; be in an &lt;code&gt;async&lt;/code&gt; code block.&lt;/li&gt;
&lt;li&gt;In order to run an &lt;code&gt;async&lt;/code&gt; code block, you need to use a task executor.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;futures&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;executor&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;LocalPool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Create the executor&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; pool &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LocalPool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Spawn an async task&lt;/span&gt;
    poll&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spawner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// execute `my_async_fn()` and await for the result&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;my_async_fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Block the main thread until all the tasks are done&lt;/span&gt;
    pool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; implementation we implemented the &lt;code&gt;Future&lt;/code&gt; and &lt;code&gt;Stream&lt;/code&gt; traits, we reused &lt;a href=&quot;https://docs.rs/futures/0.3.6/futures/executor/struct.LocalPool.html&quot;&gt;LocalPool&lt;/a&gt; from the &lt;code&gt;futures&lt;/code&gt; crate as a task executor, and we generalized &lt;code&gt;on_event&lt;/code&gt; to notify the asynchronous operation completion.&lt;/p&gt;
&lt;h3 id=&quot;the-controller-lifecycle&quot;&gt;The controller lifecycle&lt;/h3&gt;
&lt;p&gt;We first analyzed the controller lifecycle:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When host invokes &lt;code&gt;run&lt;/code&gt;, the controller starts a bunch of watchers and then it waits for new events&lt;/li&gt;
&lt;li&gt;Every time a new event comes in, the host wakes the controller up by calling &lt;code&gt;on_event&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means that during the &lt;code&gt;run&lt;/code&gt; phase, the controller starts a bunch of asynchronous tasks, one or more of them waiting for asynchronous events. After the &lt;code&gt;run&lt;/code&gt; phase completes, &lt;strong&gt;there is no need to keep the module running&lt;/strong&gt;. When we wake the controller up again, we need to check for any tasks that can continue and run them up to the point where we have all the tasks waiting for an external event.&lt;/p&gt;
&lt;p&gt;To implement this lifecycle, we need to execute both on &lt;code&gt;run&lt;/code&gt; and on &lt;code&gt;on_event&lt;/code&gt; the executor method &lt;a href=&quot;https://docs.rs/futures/0.3.6/futures/executor/struct.LocalPool.html#method.run_until_stalled&quot;&gt;&lt;code&gt;run_until_stalled()&lt;/code&gt;&lt;/a&gt;, which will run all tasks in the executor pool and returns if no more progress can be made on any task. This allows us to implement &lt;code&gt;run&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Get the global executor&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; exec &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;kube&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;abi&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_mut_executor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Start the main task&lt;/span&gt;
    exec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;borrow_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spawner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Give a little push to the executor.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// This runs until all tasks up to when they&#39;re all waiting for async results completion.&lt;/span&gt;
    exec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;borrow_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run_until_stalled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;our-custom-future-stream&quot;&gt;Our custom &lt;code&gt;Future&lt;/code&gt;/&lt;code&gt;Stream&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;In order to encapsulate the pending asynchronous operation, we implemented our &lt;code&gt;Future&lt;/code&gt;. The implementations are straightforward and pretty much the same &lt;a href=&quot;https://rust-lang.github.io/async-book/02_execution/03_wakeups.html&quot;&gt;as explained in the Rust async book&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/// Shared state between the future and the waiting thread&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;AbiFutureState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The result value&lt;/span&gt;
    value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Was the future completed?&lt;/span&gt;
    completed&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Object that notifies the completion to the executor&lt;/span&gt;
    waker&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Waker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Future&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbiFuture&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Output&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Poll&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Look at the shared state to see if the timer has already completed.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; shared_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shared_state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; shared_state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;completed &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Poll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Ready&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;shared_state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            shared_state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;waker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Poll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Pending&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A similar implementation exists for the &lt;code&gt;Stream&lt;/code&gt; trait. To create a &lt;code&gt;Future&lt;/code&gt;, we use this method:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;start_future&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;async_operation_id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbiFuture&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Create the future state&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Mutex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;AbiFutureState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            completed&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            waker&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Insert in the global map of pending future states this future state&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;get_pending_futures&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;borrow_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;async_operation_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Return the new future&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;AbiFuture&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        shared_state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;generalizing-on-event-to-wakeup-future-wakeup-stream&quot;&gt;Generalizing &lt;code&gt;on_event&lt;/code&gt; to &lt;code&gt;wakeup_future&lt;/code&gt;/&lt;code&gt;wakeup_stream&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;At this point, we took the concept of &lt;code&gt;on_event&lt;/code&gt; and generalized to &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, introducing &lt;code&gt;wakeup_future&lt;/code&gt;/&lt;code&gt;wakeup_stream&lt;/code&gt; ABI exports.
Every time the controller invokes an asynchronous ABI import (like &lt;code&gt;watch&lt;/code&gt;), it gets the identifier that we use to instantiate our &lt;code&gt;Future&lt;/code&gt;/&lt;code&gt;Stream&lt;/code&gt; implementation.
When the host completed the asynchronous operation, it invokes &lt;code&gt;wakeup_future&lt;/code&gt;/&lt;code&gt;wakeup_stream&lt;/code&gt;. The controller marks the &lt;code&gt;Future&lt;/code&gt;/&lt;code&gt;Stream&lt;/code&gt; as completed, including the result value, and invokes &lt;code&gt;LocalPool::run_until_stalled()&lt;/code&gt; to wake up tasks waiting for that future/stream to complete.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;wakeup_future&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;future_id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ptr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; len&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Retrieve the global pending future states map&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; fut_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_pending_futures&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; waker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; state_arc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fut_state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;borrow_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;future_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state_arc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// If the pointer is not null, fill the Future result value&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ptr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_raw_parts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                    ptr &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    len &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    len &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;completed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;waker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Use the waker to notify the executor this future is completed&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;waker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; waker &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        waker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;wake&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Let&#39;s try to execute stuff up to the point where there isn&#39;t anything else to execute&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;get_mut_executor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;borrow_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run_until_stalled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-complete-flow&quot;&gt;The complete flow&lt;/h3&gt;
&lt;p&gt;You can find the complete code regarding &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; support here: &lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/blob/master/kube-rs/src/abi/executor.rs&quot;&gt;&lt;code&gt;executor.rs&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;more-async-abi-methods&quot;&gt;More async ABI methods!&lt;/h3&gt;
&lt;p&gt;After we implemented &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; in our WASM modules, we refactored the &lt;code&gt;request&lt;/code&gt; ABI discussed in &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/#The-ABI&quot;&gt;our previous post&lt;/a&gt; as an asynchronous ABI method:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[link(wasm_import_module = &lt;span class=&quot;token string&quot;&gt;&quot;http-proxy-abi&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ptr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; len&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the returned value is the asynchronous operation identifier and, to signal the completion of the request, the host invokes &lt;code&gt;wakeup_future&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We also included a new ABI method to sleep the execution of the module:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[link(wasm_import_module = &lt;span class=&quot;token string&quot;&gt;&quot;delay-abi&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;millis&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is necessary to run the kube-runtime, which performs some sleeps before synchronizing the internal cache again.&lt;/p&gt;
&lt;h2 id=&quot;redesigning-the-host-as-an-event-driven-application&quot;&gt;Redesigning the host as an event-driven application&lt;/h2&gt;
&lt;p&gt;The first implementation of the new &lt;code&gt;kube-watch-abi&lt;/code&gt; ABI was a little rough: a lot of blocking threads, shared memory across threads, some unsafe sprinkled here and there to make the code compiling.
Because of that, we redesigned the host to transform it in a full asynchronous application made of channels and message handlers. For every asynchronous ABI method there is a channel that delivers the &lt;em&gt;request&lt;/em&gt; to a message handler, which processes the request, computes one or more responses and sends them back to another channel. This last channel delivers messages to the &lt;code&gt;AsyncResultDispatcher&lt;/code&gt;, owner of the module instances, that invokes the &lt;code&gt;wakeup_future&lt;/code&gt;/&lt;code&gt;wakeup_stream&lt;/code&gt; of the interested controller.&lt;/p&gt;
&lt;p&gt;Today we have 3 different message handlers, one for each async ABI method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kube_watch::Watchers&lt;/code&gt; that controls the watch operation. This message handler is also able to deduplicate the watch operations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http::start_request_executor&lt;/code&gt; to execute HTTP requests&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delay::start_delay_executor&lt;/code&gt; to execute delay requests&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the host loads all the modules, it executes the ABI method &lt;code&gt;run&lt;/code&gt; for each module, then it transfers the ownership of module instances to &lt;code&gt;AsyncResultDispatcher&lt;/code&gt; that will start listening for new &lt;code&gt;AsyncResult&lt;/code&gt; messages on its ingress channel.&lt;/p&gt;
&lt;p&gt;Because all the message handlers and channels are &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; based, if all the handlers are in idle, virtually no resource is wasted with threads waiting.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;AsyncResultDispatcher&lt;/code&gt; controls all the different module instances, it avoids invoking the same controller in parallel: &lt;code&gt;LocalLoop&lt;/code&gt; is a single threaded async task executor, hence a module cannot process multiple async results in parallel.&lt;/p&gt;
&lt;h2 id=&quot;compiling-kube-runtime-to-wasm&quot;&gt;Compiling kube-runtime to WASM&lt;/h2&gt;
&lt;p&gt;Thanks to all the async changes, we managed to realign most of the APIs of &lt;code&gt;kube-rs&lt;/code&gt; to the original ones. This allowed us to port &lt;code&gt;kube-runtime&lt;/code&gt; to our WASM controllers.&lt;/p&gt;
&lt;p&gt;The kube_runtime crate contains sets of higher level abstractions on top of the Api and Resource types so that you don&#39;t have to do all the watch/resourceVersion/storage book-keeping yourself.&lt;/p&gt;
&lt;p&gt;The problem we experienced with compiling &lt;code&gt;kube-runtime&lt;/code&gt; to WASM is that it depends on &lt;code&gt;tokio::time::DelayQueue&lt;/code&gt;, a queue that yields components up to a specified deadline. &lt;code&gt;DelayQueue&lt;/code&gt; uses the &lt;code&gt;Future&lt;/code&gt; type called &lt;code&gt;Delay&lt;/code&gt; to effectively implement delays. The problem with this &lt;code&gt;Delay&lt;/code&gt; is that it&#39;s implemented using the internal ticker of the Tokio async task executor &lt;code&gt;Runtime&lt;/code&gt;, which we don&#39;t use inside WASM modules.&lt;/p&gt;
&lt;p&gt;In order to fix this issue, we had to fork the implementation of &lt;code&gt;tokio::time::DelayQueue&lt;/code&gt; and reimplement the &lt;code&gt;Delay&lt;/code&gt; type using the &lt;code&gt;delay&lt;/code&gt; ABI shown previously:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    fut&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;dyn&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Send&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    deadline&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Instant&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;new_timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;deadline&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Instant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; now &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Instant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; deadline&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checked_duration_since&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;now&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dur&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                fut&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;kube&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;abi&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register_delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dur&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                deadline
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                fut&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;futures&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;future&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                deadline&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; now
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// [...]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Future&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Delay&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Output&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Pin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;task&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Poll&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fut&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;as_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the custom implementation of &lt;code&gt;DelayQueue&lt;/code&gt;, the &lt;code&gt;rust-runtime&lt;/code&gt; compiled successfully to WASM, and we managed to port our controllers to use it!&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; simple_pods&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SimplePod&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;namespaced&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pods&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Pod&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;namespaced&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;simple_pods&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ListParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;owns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pods&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ListParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reconcile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error_policy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; client &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;for_each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;res&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; res &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Reconciled {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Reconcile error: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;show-me-the-code&quot;&gt;Show me the code!&lt;/h2&gt;
&lt;p&gt;You can check out the complete code of controllers today here: &lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/blob/master/ext-simple-pod/src/lib.rs&quot;&gt;simple-pod controller&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want to look at all the different changes the project went through, look at these PRs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/pull/6&quot;&gt;Event system and implementation of &lt;code&gt;watch&lt;/code&gt; ABI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/pull/7&quot;&gt;Refactor of the host as event-driven application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/pull/8&quot;&gt;&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; + &lt;code&gt;watch&lt;/code&gt; returns &lt;code&gt;Stream&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/pull/9&quot;&gt;Non blocking HTTP proxy ABI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/pull/10&quot;&gt;Non blocking &lt;code&gt;delay&lt;/code&gt; ABI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/pull/11&quot;&gt;Kube runtime port&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;Now the controller module looks like a real Kubernetes controller: the difference is minimal to a controller targeting the usual deployment style. We also opened up a door for important optimizations, thanks to the &lt;code&gt;watch&lt;/code&gt; ABI method. The host refactoring and the async ABI methods should also simplify the future interaction with Golang WASM controllers, because our ABI now resembles the asynchronous semantics of their WASM ABI.&lt;/p&gt;
&lt;p&gt;Our next goals are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hack the Golang compiler to fit our ABI (or a similar one). For more info check out the &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope#Wait-where-is-Golang&quot;&gt;previous blog post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Perform a comparison in terms of resource utilization between this deployment style using WASM modules and the usual one of 1 container per controller.&lt;/li&gt;
&lt;li&gt;Figure out how to handle the different &lt;code&gt;ServiceAccount&lt;/code&gt;s per controller&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stay tuned!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Kubernetes Controllers - A new hope</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/" />
    <updated>2020-07-28T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/</id>
    <content type="html">&lt;p&gt;Today I&#39;m going to introduce you an idea &lt;a href=&quot;https://github.com/markusthoemmes/&quot;&gt;Markus Thömmes&lt;/a&gt; and I had to package, deploy and operate Kubernetes controllers in a modern and efficient way that may have a fundamental impact on the Kubernetes ecosystem.&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Kubernetes is the standard de-facto container orchestration engine of these days. As every orchestrator of &lt;em&gt;&amp;quot;something&amp;quot;&lt;/em&gt; (bare metal machines, VMs, containers, etc), one day you need to expand the available abstractions the system provides to you. For Kubernetes, that day came around 3 years ago where they introduced the concept of &lt;a href=&quot;https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/&quot;&gt;Custom Resource&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To put it simply, everything you create/read/update/delete in Kubernetes is an API Resource. A &lt;strong&gt;CRD&lt;/strong&gt; (Custom Resource Definition) defines a new resource you can create/read/update/delete in your cluster, which is not included in the built-in ones, while a &lt;strong&gt;CR&lt;/strong&gt; (Custom Resource) is an instance of the CRD. For example, a &lt;code&gt;Pod&lt;/code&gt; is a Kubernetes resource from the &lt;code&gt;core&lt;/code&gt; API group, a &lt;code&gt;Broker&lt;/code&gt; is a CRD introduced when you install Knative Eventing on your Kubernetes cluster.
With a definition of a new resource, you need to implement the business logic to orchestrate it, that is to handle create/read/update/delete operations on the resource. For example, you might want spawn a new &lt;code&gt;etcd&lt;/code&gt; pod every time somebody creates a new &lt;code&gt;EtcdCluster&lt;/code&gt; CR. In order to do that, you need to implement a Kubernetes controller: an application that listens for events on one or more API resources and reacts performing some operations on the cluster.&lt;/p&gt;
&lt;p&gt;Since the introduction of CRDs, we&#39;ve seen a &lt;strong&gt;huge&lt;/strong&gt; expansion of extensions to Kubernetes APIs. Kubernetes vendors are using CRDs to provide distributions well integrated with their underlying systems, software vendors are developing controllers to simplify the operations on Kubernetes environment (for example &lt;a href=&quot;https://strimzi.io/&quot;&gt;Strimzi&lt;/a&gt; to deploy Kafka on Kubernetes), new projects are born to provide middleware and tooling to develop applications on top of Kubernetes (&lt;a href=&quot;https://knative.dev&quot;&gt;Knative&lt;/a&gt; to create serverless applications). Marketing around Kubernetes even forged a new name for the last use case: &lt;em&gt;Kubernetes native&lt;/em&gt; applications, aka applications where the API-surface relies on the CRDs. In OpenShift there is even a &lt;a href=&quot;https://docs.openshift.com/container-platform/4.5/operators/understanding_olm/olm-understanding-olm.html&quot;&gt;system&lt;/a&gt;, based on a bunch of CRDs, to install other CRDs with their respective controllers.&lt;/p&gt;
&lt;p&gt;Although there is a &lt;a href=&quot;https://stackoverflow.com/questions/47848258/kubernetes-controller-vs-kubernetes-operator&quot;&gt;distinction&lt;/a&gt; between controllers and operators, for the sake of simplicity in this post I&#39;m going to always refer to &lt;em&gt;controller&lt;/em&gt; as the entity that implements the business logic of a CRD.&lt;/p&gt;
&lt;p&gt;This concept works pretty well for Kubernetes, although there are still some open questions about the controller itself. In this post I&#39;m going to introduce you a solution &lt;a href=&quot;https://github.com/markusthoemmes/&quot;&gt;Markus Thömmes&lt;/a&gt; and I designed that may &lt;strong&gt;revolutionize&lt;/strong&gt; how we package, deploy and operate Kubernetes controllers.&lt;/p&gt;
&lt;h2 id=&quot;kubernetes-controllers-today&quot;&gt;Kubernetes controllers today&lt;/h2&gt;
&lt;p&gt;Today a Kubernetes controller consists of more or less:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A main loop that does long polling to Kubernetes to watch one or more resources&lt;/li&gt;
&lt;li&gt;On every event, a function called &lt;em&gt;reconciler&lt;/em&gt; is invoked to align the cluster state to the desired state. For example: if the desired state expects a Pod to be running, but for some reason there isn&#39;t, then the &lt;em&gt;reconciler&lt;/em&gt; creates it.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;reconciler&lt;/em&gt; optionally set the status of the CR, like &lt;em&gt;Ready&lt;/em&gt;, &lt;em&gt;SomeError&lt;/em&gt;, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After we implement the controller, we need to configure and apply the &lt;em&gt;Custom Resource Definition&lt;/em&gt; and we need to setup a &lt;em&gt;Service Account&lt;/em&gt;, that is an account to connect to our Kubernetes cluster in order to perform the operations the &lt;em&gt;reconciler&lt;/em&gt; does.&lt;/p&gt;
&lt;p&gt;During the years a lot of tooling was created, &lt;em&gt;mostly in Golang&lt;/em&gt;, to implement these controllers, depending on the user needs. To mention some of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kubernetes-sigs/kubebuilder&quot;&gt;Kubebuilder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sdk.operatorframework.io/&quot;&gt;Operator SDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other projects, like Knative, opt for creating and maintaining their &lt;a href=&quot;https://github.com/knative/pkg/tree/master/controller&quot;&gt;own tooling&lt;/a&gt; to implement controllers.&lt;/p&gt;
&lt;p&gt;These tools usually provide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A library to improve the event handling logic: &amp;quot;smart&amp;quot; events queue that deduplicate and filter events for example&lt;/li&gt;
&lt;li&gt;A code generator component that, starting from the CRD defined in the code, scaffolds the controller code and generates the client code to interact with the CRD&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In essence, all of these tools work the same though in that ultimately they will generate a binary containing the controller. Each binary can in theory contain a number of controllers, for example Knative&#39;s framework actively encourages that with a &lt;em&gt;main&lt;/em&gt; interface to start multiple controllers at same time. Crucially though, collapsing more than one controller into one binary and thus one process in the Kubernetes cluster needs to happen at &lt;strong&gt;compile time&lt;/strong&gt;.
With the systems commonly used, we&#39;ll get &lt;strong&gt;at least one process&lt;/strong&gt; (aka Pod) per project that extends Kubernetes (i.e. at least one Pod for Knative CRDs, at least one Pod for Strimzi CRDs etc.). Each of those processes will be mostly idle for most of their lifetime, but they will steadily consume resources nonetheless.
In short: &lt;strong&gt;We&#39;re flooding our Kubernetes clusters with applications that suck resources and remain in idle for most of their time!&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;extending-kubernetes-apis-in-process&quot;&gt;Extending Kubernetes APIs in process&lt;/h2&gt;
&lt;p&gt;The idea behind our solution is to create a runtime plugin system where every plugin is a Kubernetes controller.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/RAHqyulFir-301.avif 301w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/RAHqyulFir-301.webp 301w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/RAHqyulFir-301.png&quot; alt=&quot;Plugin system&quot; title=&quot;Plugin system&quot; width=&quot;301&quot; height=&quot;161&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;This plugin system should have certain traits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Plugins should be well isolated between each other so no single plugin&#39;s failure/bad behaviour can cause trouble in other plugins&lt;/li&gt;
&lt;li&gt;The host should be able to control and restrict what the plugin can do&lt;/li&gt;
&lt;li&gt;Users should be able to develop plugins with different languages&lt;/li&gt;
&lt;li&gt;The host should be able to load and unload plugins at runtime, without restarting the system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This plugin system can be seen as a &lt;em&gt;mega controller&lt;/em&gt;. It can run as its own process, or it could even be merged with the &lt;em&gt;kube-controller-manager&lt;/em&gt;, which is the process with all the controllers that manage the Kubernetes built-in resources:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/dKZ3dmymTK-441.avif 441w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/dKZ3dmymTK-441.webp 441w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/dKZ3dmymTK-441.png&quot; alt=&quot;kube-controller-manager including the plugin system&quot; title=&quot;kube-controller-manager including the plugin system&quot; width=&quot;441&quot; height=&quot;161&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2 id=&quot;isolated-multi-language-lightweight-plugin-system-that-sounds-hard&quot;&gt;Isolated, multi-language, lightweight plugin system? That sounds hard...&lt;/h2&gt;
&lt;p&gt;The requirements of the plugin system are not trivial, but lucky for us WebAssembly (Wasm) comes in rescue!&lt;/p&gt;
&lt;p&gt;Wasm is an instruction set, targetable from every programming language, that runs in an isolated Virtual Machine.&lt;/p&gt;
&lt;p&gt;From &lt;a href=&quot;https://webassembly.org/&quot;&gt;webassembly.org&lt;/a&gt;: &lt;em&gt;&amp;quot;WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;That sounds like the perfect fit for our idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Because it&#39;s isolated by default, we define all the possible interactions between the plugins (controllers) and the host, piece by piece&lt;/li&gt;
&lt;li&gt;Because it runs in a sandboxed virtual machine, it can easily isolate catastrophic failures and guarantee safe access to specific memory regions&lt;/li&gt;
&lt;li&gt;Because it&#39;s a general purpose instruction set, more similar to an ISA of a modern CPU other than the JVM bytecode, most languages can target it (GC-enabled, dynamic/static typed, ...).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A list of supported languages is available &lt;a href=&quot;https://webassembly.org/getting-started/developers-guide/&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://stackoverflow.com/questions/43540878/what-languages-can-be-compiled-to-webassembly-wasm&quot;&gt;here&lt;/a&gt;. Wasm is flexible enough that it can be executed with ahead of time (AOT) compilation or with a just in time (JIT) interpreter.&lt;/p&gt;
&lt;p&gt;Although WebAssembly sounds like a web or browser technology, in the last years several people are experimenting with this technology &lt;a href=&quot;https://webassembly.org/docs/use-cases/#outside-the-browser&quot;&gt;outside the browser&lt;/a&gt;. Most notably, CloudFlare Function as a Service offering called Workers &lt;a href=&quot;https://blog.cloudflare.com/introducing-wrangler-cli/&quot;&gt;uses Wasm&lt;/a&gt; to run C/Rust code using Google&#39;s V8 isolations.&lt;/p&gt;
&lt;p&gt;With WebAssembly, our plugin system looks like:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/uIktsbDx55-311.avif 311w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/uIktsbDx55-311.webp 311w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/uIktsbDx55-311.png&quot; alt=&quot;Kubernetes controllers architecture&quot; title=&quot;Kubernetes controllers architecture&quot; width=&quot;311&quot; height=&quot;201&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;When we develop a controller, the compiled artifact is a binary Wasm module. A Wasm engine loads this module, eventually compiles it in AOT cases, and executes it.&lt;/p&gt;
&lt;p&gt;At this point, we need to define the interaction semantics between the host and the plugins. In order to do that, we define the &lt;a href=&quot;https://en.wikipedia.org/wiki/Application_binary_interface&quot;&gt;ABI (Application Binary Interface)&lt;/a&gt; of our plugin system: the functions the host &lt;em&gt;imports&lt;/em&gt; in our plugin and the functions the plugin &lt;em&gt;exports&lt;/em&gt; in our host.&lt;/p&gt;
&lt;h2 id=&quot;show-me-your-abi&quot;&gt;Show me your ABI!&lt;/h2&gt;
&lt;p&gt;First of all, an example of a Wasm ABI. This is the Rust code for a Wasm module:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[link(wasm_import_module = &lt;span class=&quot;token string&quot;&gt;&quot;my-abi&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ptr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; len&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This Wasm module exports the function &lt;code&gt;foo&lt;/code&gt;, which means that, after the module has been loaded, the engine can invoke &lt;code&gt;foo&lt;/code&gt; to execute it. On the other side, when we load the module in the engine, we must link some logic to the function &lt;code&gt;println&lt;/code&gt; in order to run it, otherwise we&#39;ll get a link error.&lt;/p&gt;
&lt;p&gt;We can get all imports and exports of a module using a tool called &lt;a href=&quot;https://github.com/fitzgen/wasm-nm&quot;&gt;&lt;code&gt;wasm-nm&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;% wasm-nm module.wasm &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;#Get the imports&lt;/span&gt;
i println

% wasm-nm module.wasm &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;#Get the exports&lt;/span&gt;
e foo&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the most important design choice of the project: the ABI influences the capabilities the host needs to implement, the portability of existing controllers, the APIs on the plugin side and so on. When we design the ABI we also need to take the differences of the programming languages into account: as an example, some may require additional interfaces to the host to run asynchronous code. The async schedulers in Rust and the goroutine scheduler in Golang both require a syscall to &lt;code&gt;sched_yield&lt;/code&gt; to yield a thread execution in this example.&lt;/p&gt;
&lt;h3 id=&quot;wasi&quot;&gt;WASI&lt;/h3&gt;
&lt;p&gt;Lucky for us, part of the job that includes the &lt;em&gt;low-level&lt;/em&gt; primitives is covered by a work in progress spec called &lt;a href=&quot;https://wasi.dev/&quot;&gt;WASI (WebAssembly System Interface)&lt;/a&gt;. Their goal is to provide a reduced set of POSIX-like APIs: read/write files, get system clock, get environment variables and program arguments, read/write a socket and so on. Although, as they state in their &lt;a href=&quot;https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-rationale.md&quot;&gt;rationale document&lt;/a&gt;, it&#39;s not goal of WASI to include primitives to &lt;strong&gt;open sockets/files&lt;/strong&gt;, this is left to the users. From their docs:&lt;/p&gt;
&lt;p&gt;One of WebAssembly&#39;s unique attributes is the ability to run sandboxed without relying on OS process boundaries. Requiring a 1-to-1 correspondence between wasm instances and heavyweight OS processes would take away this key advantage for many use cases. Fork/exec are the obvious example of an API that&#39;s difficult to implement well if you don&#39;t have POSIX-style processes, but a lot of other things in POSIX are tied to processes too. So it isn&#39;t a simple matter to take POSIX, or even a simple subset of it, to WebAssembly.&lt;/p&gt;
&lt;p&gt;A socket is a process wide resource. This means that if module A opens a TCP socket, module B might have access to it if we don&#39;t implement proper security countermeasures.&lt;/p&gt;
&lt;h3 id=&quot;designing-the-abi&quot;&gt;Designing the ABI&lt;/h3&gt;
&lt;p&gt;Given our knowledge and studies of the current state of art for Wasm ABIs, we found out there are 3 possible non mutually exclusive approaches to the ABI design:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High level ABI&lt;/strong&gt;: Expose the Kubernetes client functionalities&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Medium level ABI&lt;/strong&gt;: Proxy the HTTP client functionalities (send HTTP request, receive HTTP response)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low level ABI&lt;/strong&gt;: Proxy the syscalls (&lt;code&gt;epoll&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;bind&lt;/code&gt;, ...)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We think that a good solution should mix all these 3 &lt;em&gt;&amp;quot;levels&amp;quot;&lt;/em&gt;. Some low-level APIs are always necessary for basic things like logging, getting configuration from environment, setting timeouts and so on.
Proxying HTTP can be useful to invoke services outside Kubernetes (e.g. to trigger a cloud vendor API to enable a service).
Exposing the Kubernetes client APIs unlocks a great potential to optimize the controllers beyond the optimized resource usage indicated above. Most prominently: the so called &lt;em&gt;informer&lt;/em&gt; infrastructure can be shared between all the controllers. An informer is the part of a controller that listens for events on given resources. It builds up local caches that effectively reflect the state of said resources in-memory.
In our case, the host would setup these informers and watches just &lt;strong&gt;once&lt;/strong&gt; per resource. If two controllers want to watch &lt;code&gt;ConfigMap&lt;/code&gt;s for example, the host would only need to setup one watch and keep one cache, reducing the amount of network traffic due to event delivery and memory consumption due to caching drastically. That effect becomes more and more pronounced as more extensions are being added.&lt;/p&gt;
&lt;p&gt;Following the above approach, the host has always the full control of process resources (open files, open sockets, etc), we allow the modules to perform only certain operations and, most important, we open up for &lt;strong&gt;huge optimizations&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;our-prototype&quot;&gt;Our prototype&lt;/h2&gt;
&lt;p&gt;Markus and I built a prototype of the host and of the 2 controllers, a simple pod spawner and the &lt;a href=&quot;https://sdk.operatorframework.io/docs/golang/quickstart/&quot;&gt;Memcached example&lt;/a&gt; from operator-sdk. You can find all the code here: https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc&lt;/p&gt;
&lt;p&gt;The host is implemented in Rust and &lt;a href=&quot;https://github.com/wasmerio/wasmer/&quot;&gt;Wasmer&lt;/a&gt;, a popular Wasm engine supporting different compilation backends and with several language bindings. We implemented the controllers with Rust using a hacked version of &lt;a href=&quot;https://github.com/clux/kube-rs&quot;&gt;&lt;code&gt;kube-rs&lt;/code&gt; client&lt;/a&gt;.
The host logic is pretty simple: when it starts, it reads the contents of the specified dir, looking for &lt;code&gt;.yaml&lt;/code&gt; containing the module manifests. An example manifest:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; memcached
&lt;span class=&quot;token key atrule&quot;&gt;abi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rust_v1alpha1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;name&lt;/code&gt; is the operator name and &lt;code&gt;abi&lt;/code&gt; is the abi the host should use to interact with the module. This allows to &lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/blob/ff4e07f2ca29ac7fd913cb3578749d64c25cb048/rust-host/src/abi/mod.rs#L19&quot;&gt;support different ABIs&lt;/a&gt; at the same time, mainly to overcome the differences between the programming languages and to support old modules running on new host versions.&lt;/p&gt;
&lt;p&gt;Then, for each module, it compiles and runs it in a separate thread invoking the exported &lt;code&gt;run&lt;/code&gt; function. The module, through the custom ABI we designed, can interact with Kubernetes to start watching resources and react to the events.&lt;/p&gt;
&lt;p&gt;The prototype contains several simplifications that we can overcome easily (like watching the modules directory more than loading all the modules once), while others require more engineering as discussed later in this post.&lt;/p&gt;
&lt;h3 id=&quot;the-abi&quot;&gt;The ABI&lt;/h3&gt;
&lt;p&gt;In order to create a running prototype, we ended up with a pretty simple ABI that mixes the low level WASI syscalls with a medium level HTTP client proxy functionality:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;% wasm-nm memcached.wasm &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt;
i request
i clock_time_get
i fd_write
i poll_oneoff
i random_get
i proc_exit
i environ_sizes_get
i environ_get

% wasm-nm memcached.wasm &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt;
e run
e allocate&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The exports are &lt;code&gt;run&lt;/code&gt; to start the controller and &lt;code&gt;allocate&lt;/code&gt; to allocate memory on the module. The implementer of the controller just implements the resources watch loop inside the &lt;code&gt;run&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;In Wasm the module cannot access to the host memory, but the host can access to the module memory and copy bytes inside it. Because the Wasm module might have a memory allocator, a GC or any other mechanism to manage memory, the module should export a function that allocates memory to let the host copy bytes back to the module.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;request&lt;/code&gt; is the function to perform a &lt;strong&gt;blocking and buffered&lt;/strong&gt; HTTP request, while all the other imports come from WASI. The user interacts with our hacked version of the Kubernetes client, which under the hood invokes &lt;code&gt;request&lt;/code&gt; to perform HTTP requests using &lt;a href=&quot;https://github.com/seanmonstar/reqwest&quot;&gt;&lt;code&gt;reqwest&lt;/code&gt; crate&lt;/a&gt;. All our APIs are blocking because currently there is no out of the box &lt;code&gt;async/await&lt;/code&gt; support in Wasm modules, I&#39;ll cover this limitation later.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;request&lt;/code&gt; flow should give you an idea of what it takes to implement an ABI:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/Wb3bENoqDY-1061.avif 1061w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/Wb3bENoqDY-1061.webp 1061w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-A-New-Hope/Wb3bENoqDY-1061.png&quot; alt=&quot;Kubernetes controllers flow&quot; title=&quot;Kubernetes controllers flow&quot; width=&quot;1061&quot; height=&quot;471&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The host takes care of handling the authentication, in order to avoid the controller having to have access to files at all.&lt;/p&gt;
&lt;h3 id=&quot;the-controller&quot;&gt;The controller&lt;/h3&gt;
&lt;p&gt;An example code for the controller looks like:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; foos&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SimplePod&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;namespaced&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; inform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Informer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ListParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pods&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Pod&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Api&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;namespaced&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; events &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; inform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;poll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Poll error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; e &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; events &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; e &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WatchEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Added&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WatchEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Modified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;o&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;reconcile_pod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;pods&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;spec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Reconcile error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WatchEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error event: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error event: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            _ &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can look at the full controller code for the above sample &lt;a href=&quot;https://github.com/slinkydeveloper/extending-kubernetes-api-in-process-poc/blob/master/ext-simple-pod/src/lib.rs&quot;&gt;here&lt;/a&gt;. The user potentially doesn&#39;t know that their code is run inside an isolation. Everything is hidden behind the usual Kubernetes client APIs. In other words: &lt;strong&gt;The programming model does not change.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;
&lt;p&gt;There are some things we still haven&#39;t tested, but that we&#39;re willing to do in order to give us a clear idea of how to grow this idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Re-design the ABI to support unbuffered &amp;amp; asynchronous HTTP requests. This requires several additions to both the Wasm engine and the host code, most notably the engine should have the ability to &lt;a href=&quot;https://github.com/wasmerio/wasmer/issues/1127&quot;&gt;yield the execution&lt;/a&gt; and the module should have an &lt;a href=&quot;https://rust-lang.github.io/async-book/02_execution/04_executor.html&quot;&gt;async executor&lt;/a&gt; to execute async operations.&lt;/li&gt;
&lt;li&gt;The proposed ABI mixes medium level and low level. We want to try to implement a high level function in our ABI for watching Kubernetes resources, in order to register a single watch for all the controllers.&lt;/li&gt;
&lt;li&gt;The actual ABI doesn&#39;t have error management at all, but that shouldn&#39;t be hard to implement: the serialized ABI data structures might be enums with error as one of the variants, so then the error is propagated back to the user as a &lt;code&gt;Result&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;wait-where-is-golang&quot;&gt;Wait, where is Golang?&lt;/h2&gt;
&lt;p&gt;Our &lt;em&gt;dream&lt;/em&gt; is to port existing controllers to this approach. Most of Kubernetes world today lives in Golang, but up until this point I didn&#39;t talk at all about Golang. Why?&lt;/p&gt;
&lt;p&gt;The reason is that we found some critical issues within the Wasm/Golang support, among the others Golang assumes that the Wasm engine runs inside a JS VM using &lt;a href=&quot;https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js&quot;&gt;wasm_exec.js&lt;/a&gt;. In order to understand the impact of this choice, let me go through the Golang standard library implementation in Wasm.&lt;/p&gt;
&lt;p&gt;Today in Golang, when you compile to Wasm, all the standard library I/O operations go through the &lt;a href=&quot;https://golang.org/pkg/syscall/js/&quot;&gt;&lt;code&gt;syscall/js&lt;/code&gt; module&lt;/a&gt;, which invokes a predefined set of imports in your module. From the doc: &lt;em&gt;Package js gives access to the WebAssembly host environment when using the js/wasm architecture. Its API is based on JavaScript semantics.&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://golang.org/pkg/syscall/js/#Global&quot;&gt;&lt;code&gt;Global()&lt;/code&gt;&lt;/a&gt; allows you to get the &lt;code&gt;global&lt;/code&gt; object&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://golang.org/pkg/syscall/js/#Value.Invoke&quot;&gt;&lt;code&gt;Invoke()&lt;/code&gt;&lt;/a&gt; allows you to use a &lt;code&gt;js.Value&lt;/code&gt; as a function and invoke it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now let&#39;s analyze how the &lt;code&gt;net/http&lt;/code&gt; client implementation on Wasm: When the user invokes &lt;code&gt;RoundTrip&lt;/code&gt;, the request &lt;a href=&quot;https://github.com/golang/go/blob/b56791cdea5caa87ffcd585d29c294bd3d08a06a/src/net/http/roundtrip_js.go#L60&quot;&gt;is transformed in a Javascript object&lt;/a&gt;, then using the syscall &lt;code&gt;Global&lt;/code&gt; and &lt;code&gt;Call&lt;/code&gt; the javascript global function &lt;a href=&quot;https://github.com/golang/go/blob/b56791cdea5caa87ffcd585d29c294bd3d08a06a/src/net/http/roundtrip_js.go#L106&quot;&gt;&lt;code&gt;fetch&lt;/code&gt; is invoked&lt;/a&gt; and finally, on the returned promise, the &lt;a href=&quot;https://github.com/golang/go/blob/b56791cdea5caa87ffcd585d29c294bd3d08a06a/src/net/http/roundtrip_js.go#L167&quot;&gt;two callbacks are set&lt;/a&gt; to handle response and error.&lt;/p&gt;
&lt;p&gt;The problem with this approach, although it works well in the browsers, is that the &lt;strong&gt;engine needs to implement all these JS functions used by Golang standard library&lt;/strong&gt;. If we&#39;re not using a Javascript based runtime, we need to reimplement this. Further, it requires us to &lt;strong&gt;secure access&lt;/strong&gt; to the &lt;em&gt;global&lt;/em&gt; object fields, in order to retain the isolation properties.&lt;/p&gt;
&lt;p&gt;To make things even worse, at the moment there is no way to define imports and exports within the module, so we cannot define and evolve an ABI that fits our needs. The only workable solution with the existing tools consists in define &amp;quot;fake&amp;quot; Javascript functions on the host side for the imports and setting &amp;quot;fake&amp;quot; fields on the &lt;a href=&quot;https://github.com/golang/go/issues/25612#issuecomment-506307400&quot;&gt;&amp;quot;fake&amp;quot; global object for the exports&lt;/a&gt;, but this approach makes the code far more complex and convoluted, both on host and Wasm module side.&lt;/p&gt;
&lt;p&gt;While the official Golang distribution &lt;a href=&quot;https://github.com/golang/go/issues/25612&quot;&gt;doesn&#39;t support&lt;/a&gt; definining imports and exports, &lt;a href=&quot;https://github.com/tinygo-org/tinygo/tree/master/src/examples/wasm&quot;&gt;TinyGo supports it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Markus managed to run the Memcached operator example of operator-sdk, using NodeJS as a host, hacking some polyfills to match the expectations of &lt;a href=&quot;https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js&quot;&gt;wasm_exec.js&lt;/a&gt;. The final module ABI is:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;% wasm-nm test.wasm &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt;
i debug
i runtime.resetMemoryDataView
i runtime.wasmExit
i runtime.wasmWrite
i runtime.nanotime1
i runtime.walltime1
i runtime.scheduleTimeoutEvent
i runtime.clearTimeoutEvent
i runtime.getRandomData
i syscall/js.finalizeRef
i syscall/js.stringVal
i syscall/js.valueGet
i syscall/js.valueSet
i syscall/js.valueIndex
i syscall/js.valueSetIndex
i syscall/js.valueCall
i syscall/js.valueNew
i syscall/js.valueLength
i syscall/js.valuePrepareString
i syscall/js.valueLoadString
i syscall/js.copyBytesToGo
i syscall/js.copyBytesToJS

% wasm-nm test.wasm &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt;
e run
e resume
e getsp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was initially puzzled by the &lt;code&gt;resume&lt;/code&gt; and &lt;code&gt;getsp&lt;/code&gt; exports of the Wasm module, but then digging in the &lt;a href=&quot;https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js&quot;&gt;wasm_exec.js&lt;/a&gt; codebase their existence was justified: using &lt;code&gt;resume&lt;/code&gt; and &lt;code&gt;getsp&lt;/code&gt; the host can yield the execution of the module, perform some async operations, and then resume the execution of the code. That&#39;s the very same solution I would love to implement in our Rust ABI prototype and, as far as I know, &lt;a href=&quot;https://github.com/WebAssembly/WASI/issues/276&quot;&gt;WASI is looking for a generalized solution to support async runtimes inside the modules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This experiment proves that Golang/Wasm support can definitely work and we think that, without the constraints of &lt;code&gt;syscall/js&lt;/code&gt; and with the ability to define our ABI, we could port all the existing controllers to compile in Wasm isolated modules.&lt;/p&gt;
&lt;p&gt;Some interesting issues to follow about Golang and Wasm related to this discussion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/31105&quot;&gt;Golang + WASI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tinygo-org/tinygo/issues/1189&quot;&gt;Ability to reduce js-centricness of compiled wasm modules in TinyGo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/27766&quot;&gt;Make syscall/js optional when compiling wasm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/25612&quot;&gt;re-use //export mechanism for exporting identifiers within wasm modules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;there-is-still-a-long-road-ahead&quot;&gt;There is still a long road ahead&lt;/h2&gt;
&lt;p&gt;Our long-term goal is to improve the Kubernetes ecosystem, creating a production ready plugin system with low footprint. We encourage the Kubernetes community to give us feedback on our findings and our prototype, so we can work together on shaping the future of Kubernetes controllers!&lt;/p&gt;
&lt;p&gt;Check out the second part of this blog post series: &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Kubernetes-controllers-The-Empire-Strikes-Back&quot;&gt;Kubernetes controllers: The Empire Strikes Back&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Reproducibility of benchmarks in Kubernetes</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/Reproducibility-of-benchmarks-in-Kubernetes/" />
    <updated>2019-12-02T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/Reproducibility-of-benchmarks-in-Kubernetes/</id>
    <content type="html">&lt;p&gt;Hi everybody! Today I&#39;m going to talk you about how I&#39;ve managed to run a benchmark &lt;strong&gt;inside&lt;/strong&gt; Kubernetes in a &lt;strong&gt;reproducible&lt;/strong&gt; manner.&lt;/p&gt;
&lt;h2 id=&quot;reproducibility-what-and-why&quot;&gt;Reproducibility: What and Why&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Reproducibility&lt;/strong&gt; means that different runs of the same benchmark, testing the same system, running in the same environment, should lead to similar results.&lt;/p&gt;
&lt;p&gt;This is one of the most important traits that every benchmark should respect, because without it, the test can&#39;t be trusted.&lt;/p&gt;
&lt;p&gt;For example, let&#39;s assume your boss gave you to optimize the most important system in production. You begin writing a benchmark to understand how it performs and, without caring about reproducibility, you jump to start searching where the performance hotspots are and operate to solve them. Now you run again the tests and results are better than the beginning but, while you&#39;re already feeling the bonus on the next paycheck, a colleague comments your &amp;quot;40% performance boost&amp;quot; PR saying &amp;quot;I tried to run the benchmark and results look worse than the beginning!&amp;quot;. What the heck? Did your PR improves the performance or not?&lt;/p&gt;
&lt;p&gt;You can&#39;t really answer that question, because your benchmark is not reproducible! You can try to run it several times but you&#39;ll continue to get deniable and not correlated results, that can answer positively or negatively to your question.&lt;/p&gt;
&lt;p&gt;Making the test reproducible, for a good part, depends on the environment where you run the test.&lt;/p&gt;
&lt;p&gt;Kubernetes is a virtualized environment designed to scale up &amp;amp; down workloads, depending on resource demands. So it can arbitrarily schedule your application to run where it wants, imposing precise cpu/memory resources. I&#39;ll show you   what countermeasures I&#39;ve took in my methodology to run benchmarks &lt;strong&gt;inside&lt;/strong&gt; K8s to prevent such problems.&lt;/p&gt;
&lt;h2 id=&quot;system-under-test-knative-eventing&quot;&gt;System under test: Knative Eventing&lt;/h2&gt;
&lt;p&gt;A couple of months I&#39;ve started working on a project called Knative Eventing, an event mesh for Kubernetes. One of the goals of Knative Eventing is to enable message consuming &amp;amp; producing through HTTP, acting as a bridge between a &amp;quot;traditional&amp;quot; messaging system (such as Kafka) and an HTTP application.&lt;/p&gt;
&lt;p&gt;I won&#39;t cover all aspects of Knative Eventing, If you want to learn more about it check out the &lt;a href=&quot;https://knative.dev/docs/eventing/&quot;&gt;Knative Eventing documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Knative, among the others, provides the concept of &lt;code&gt;Channel&lt;/code&gt;, a flow of events from one or more producers to one or more subscribed consumers:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Reproducibility-of-benchmarks-in-Kubernetes/EU-eVHX0V--767.avif 767w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Reproducibility-of-benchmarks-in-Kubernetes/EU-eVHX0V--767.webp 767w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Reproducibility-of-benchmarks-in-Kubernetes/EU-eVHX0V--767.png&quot; alt=&quot;From source to user application, through a Channel&quot; title=&quot;From source to user application, through a Channel&quot; width=&quot;767&quot; height=&quot;141&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;To push events into the channel you interact with its HTTP interface, while to receive events from the channel you subscribe to it, specifying at what HTTP endpoint the channel should send the events. Behind the hood, a pod called &lt;em&gt;dispatcher&lt;/em&gt; is actually serving the HTTP interface for inbound events, managing the interaction with the messaging system and dispatching the events to the subscribers.&lt;/p&gt;
&lt;p&gt;In this post I will use the test that calculates &lt;code&gt;KafkaChannel&lt;/code&gt;&#39;s throughput and latency.&lt;/p&gt;
&lt;h2 id=&quot;test-and-cluster&quot;&gt;Test &amp;amp; Cluster&lt;/h2&gt;
&lt;p&gt;The test components are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a sender container that forces a configurable load for a certain amount of time on the Channel&lt;/li&gt;
&lt;li&gt;a receiver container that subscribes to the channel&lt;/li&gt;
&lt;li&gt;an aggregator container that fetches results from sender and receiver containers and calculates latencies and throughputs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these three components runs inside the cluster.&lt;/p&gt;
&lt;p&gt;I won&#39;t spend words in this post to explain how such components, designed together with Knative community, work in deep. If you want to know more about it, look at the &lt;a href=&quot;https://github.com/knative/eventing/tree/master/test/test_images/performance&quot;&gt;&lt;code&gt;README&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The cluster where I&#39;m running the tests is composed by three bare metal machines in the same rack and they&#39;re running &lt;strong&gt;only these tests&lt;/strong&gt;. It&#39;s quite important to run on bare metal, otherwise you will need to make further steps to make your virtualization environment reproducible, depending on the VM system you use.&lt;/p&gt;
&lt;h2 id=&quot;step-0-how-to-determine-reproducibility&quot;&gt;Step 0: How to determine reproducibility?&lt;/h2&gt;
&lt;p&gt;The question that arises is: what metric should be used to determine reproducibility? A wise answer could be that the standard deviation of the metric used to determine a performance improvement should be used to determine reproducibility.&lt;/p&gt;
&lt;p&gt;In my case I&#39;m going to use standard deviation of the &lt;strong&gt;percentiles&lt;/strong&gt; of E2E latency (from sender to receiver) across several runs. The lower is the standard deviation, more reproducible is the test.&lt;/p&gt;
&lt;p&gt;To improve reproducibility, I&#39;ll start by configuring and running the test 5 times, to calculate a baseline standard deviation. Then I&#39;ll show you the tweaks I&#39;ve made to reduce the standard deviation to an acceptable value:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Configure the test to don&#39;t blow up the system&lt;/li&gt;
&lt;li&gt;Pin containers to nodes&lt;/li&gt;
&lt;li&gt;Restart the system after each run&lt;/li&gt;
&lt;li&gt;Configure the resource limits&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;step-1-configure-the-test-to-dont-blow-up-the-system&quot;&gt;Step 1: Configure the test to don&#39;t blow up the system&lt;/h2&gt;
&lt;p&gt;The first step is to configure the test to correctly generate a load that doesn&#39;t blow up the system. System must be stressed, but in such a way that doesn&#39;t lead to a complete degradation, or even a crash.&lt;/p&gt;
&lt;p&gt;I&#39;ve configured my test to force 500 requests per second for 30 seconds, which I&#39;ve found, experimentally, that is a good configuration the system can hold. Bear in mind that different &amp;quot;requests per second&amp;quot; configurations leads to different latencies!&lt;/p&gt;
&lt;p&gt;I&#39;ve collected the 99%, 99.9% and 99.99% percentiles but I&#39;ll focus on 99% percentile because I&#39;ve managed to do only few and short test runs, and in such situations outliers are more visible and not filtered out in higher percentiles. In a &amp;quot;production run&amp;quot; of the test, you should run it for more than 30 seconds, to understand if higher latencies happens frequently.&lt;/p&gt;
&lt;p&gt;After a first run, just configuring the test and running it for 5 times, I&#39;ve these results:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;P99&lt;/th&gt;
&lt;th&gt;P99.9&lt;/th&gt;
&lt;th&gt;P99.99&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run 1&lt;/td&gt;
&lt;td&gt;266.266179&lt;/td&gt;
&lt;td&gt;276.945500&lt;/td&gt;
&lt;td&gt;284.709000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 2&lt;/td&gt;
&lt;td&gt;264.750750&lt;/td&gt;
&lt;td&gt;278.127000&lt;/td&gt;
&lt;td&gt;283.149000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 3&lt;/td&gt;
&lt;td&gt;250.629000&lt;/td&gt;
&lt;td&gt;263.994500&lt;/td&gt;
&lt;td&gt;271.937000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 4&lt;/td&gt;
&lt;td&gt;250.594875&lt;/td&gt;
&lt;td&gt;261.605000&lt;/td&gt;
&lt;td&gt;272.635000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 5&lt;/td&gt;
&lt;td&gt;266.224393&lt;/td&gt;
&lt;td&gt;282.690500&lt;/td&gt;
&lt;td&gt;290.529000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The SD of P99 is 8.312 and, in particular, the relative standard deviation is 3.2%.&lt;/p&gt;
&lt;p&gt;From experimental evidence I&#39;ve found that the relative standard deviation is not linearly related with the test configuration, which means that the more stress is applied by the load generator, the more could be the &lt;strong&gt;relative&lt;/strong&gt; standard deviation.&lt;/p&gt;
&lt;p&gt;Let&#39;s try to dig into why these numbers are so different and how I&#39;ve lowered them.&lt;/p&gt;
&lt;h2 id=&quot;step-2-pin-containers-to-nodes&quot;&gt;Step 2: Pin containers to nodes&lt;/h2&gt;
&lt;p&gt;The first thing you can notice is that the third an fourth run performed with generally lower numbers than the others. Digging a bit with &lt;code&gt;kubectl describe nodes&lt;/code&gt; I&#39;ve found that Kubernetes was scheduling on each run pods in different nodes. Sometimes it scheduled the sender and receiver in the same node of Kafka Channel dispatcher, letting them communicate with lower latencies!&lt;/p&gt;
&lt;p&gt;To let Kubernetes deploy the pods always in the same nodes, I&#39;ve configured the affinity of sender, receiver and all SUTs (system under test, which in my case means the Kafka Channel dispatcher and the Kafka cluster).
To do it, I&#39;ve defined three labels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bench-role: kafka&lt;/code&gt;: Where Kafka cluster and Zookeeper are deployed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bench-role: eventing&lt;/code&gt;: Where the kafka dispatcher is deployed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bench-role: sender&lt;/code&gt;: Where both sender and receiver are deployed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then, I set these labels in my cluster using:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl label nodes node_name bench-role&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;eventing&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On every deployment/pod descriptor, I&#39;ve configured affinity in my various deployment descriptors.&lt;/p&gt;
&lt;p&gt;I deployed Kafka using &lt;a href=&quot;https://strimzi.io/&quot;&gt;Strimzi&lt;/a&gt; and, thanks to its &lt;code&gt;Kafka&lt;/code&gt; CRD, I can easily configure the &lt;a href=&quot;https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity&quot;&gt;affinity&lt;/a&gt; too (I&#39;ve omitted irrelevant parts of this config):&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;kafka&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;affinity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;nodeAffinity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;requiredDuringSchedulingIgnoredDuringExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;nodeSelectorTerms&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;matchExpressions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bench&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
                    &lt;span class=&quot;token key atrule&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; In
                    &lt;span class=&quot;token key atrule&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; kafka
&lt;span class=&quot;token key atrule&quot;&gt;zookeeper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;affinity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;nodeAffinity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;requiredDuringSchedulingIgnoredDuringExecution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;nodeSelectorTerms&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;matchExpressions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bench&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
                    &lt;span class=&quot;token key atrule&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; In
                    &lt;span class=&quot;token key atrule&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; kafka&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For &lt;code&gt;kafka-ch-dispatcher&lt;/code&gt;, I just modified the original &lt;a href=&quot;https://github.com/knative/eventing-contrib/blob/master/kafka/channel/config/500-dispatcher.yaml&quot;&gt;dispatcher yaml&lt;/a&gt; adding the &lt;a href=&quot;https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector&quot;&gt;&lt;code&gt;nodeSelector&lt;/code&gt;&lt;/a&gt; (which is in fact a short version of the &lt;code&gt;nodeAffinity&lt;/code&gt;) and I redeployed from source using ko:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kafka&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ch&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dispatcher
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; knative&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;eventing
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;nodeSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;bench-role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; eventing
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dispatcher&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the &lt;code&gt;sender&lt;/code&gt; and &lt;code&gt;receiver&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pod
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; channel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;perf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;send
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; perf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;eventing
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;nodeSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;bench-role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sender
  &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sender

&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pod
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; channel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;perf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;receive
  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; perf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;eventing
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;nodeSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;bench-role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sender
  &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; receiver&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;step-3-restart-the-system-after-each-run&quot;&gt;Step 3: Restart the system after each run&lt;/h2&gt;
&lt;p&gt;After pinning the workload to different nodes, I&#39;ve ran again the tests:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;P99&lt;/th&gt;
&lt;th&gt;P99.9&lt;/th&gt;
&lt;th&gt;P99.99&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run 1&lt;/td&gt;
&lt;td&gt;263.552250&lt;/td&gt;
&lt;td&gt;268.646500&lt;/td&gt;
&lt;td&gt;272.223000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 2&lt;/td&gt;
&lt;td&gt;266.060133&lt;/td&gt;
&lt;td&gt;280.979000&lt;/td&gt;
&lt;td&gt;285.983000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 3&lt;/td&gt;
&lt;td&gt;266.994500&lt;/td&gt;
&lt;td&gt;282.858000&lt;/td&gt;
&lt;td&gt;292.864000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 4&lt;/td&gt;
&lt;td&gt;268.234000&lt;/td&gt;
&lt;td&gt;297.516000&lt;/td&gt;
&lt;td&gt;326.862000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 5&lt;/td&gt;
&lt;td&gt;265.809929&lt;/td&gt;
&lt;td&gt;281.717000&lt;/td&gt;
&lt;td&gt;288.665000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;As you may notice, the first four runs looks incrementally worse. This happens because every run depends on the SUTs states caused by the previous run. The Kafka cluster and/or the Kafka channel dispatcher could be in a degradated state before a new run begins and this obviously reduces the chances to have same results over multiple runs. All systems involved in the road from sender to receiver must be reset, so every run starts stressing the system under the same conditions, ensuring that the latency of a run doesn&#39;t depend on previous runs.&lt;/p&gt;
&lt;p&gt;In my case just deleting all pods does the trick, since the &lt;code&gt;Deployment&lt;/code&gt;s spin up a new ones:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl delete pods &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; knative-eventing &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt;
kubectl delete kt --all-namespaces &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Delete all KafkaTopics&lt;/span&gt;
kubectl delete pods &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; kafka &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt;

kubectl &lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt; pod &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; knative-eventing &lt;span class=&quot;token parameter variable&quot;&gt;--for&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;condition&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Ready &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt;
kubectl &lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt; pod &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; kafka &lt;span class=&quot;token parameter variable&quot;&gt;--for&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;condition&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Ready &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;step-4-configure-the-resource-limits&quot;&gt;Step 4: Configure the resource limits&lt;/h2&gt;
&lt;p&gt;As explained at beginning of this post, Kubernetes is designed to scale up &amp;amp; down workloads. What if the scheduler decides to schedule up and down our benchmark resources while the test is running? The benchmark needs to have granted the resources it needs &amp;amp; these should not change while is running. To do so, resource &lt;code&gt;request&lt;/code&gt; &amp;amp; &lt;code&gt;limits&lt;/code&gt; must be configured the same for every test and SUT, like:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8Gi&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;limits&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8Gi&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This leads Kubernetes to schedule pods with QoS class &lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/#create-a-pod-that-gets-assigned-a-qos-class-of-guaranteed&quot;&gt;&lt;code&gt;Guaranteed&lt;/code&gt;&lt;/a&gt;, so it can&#39;t scale up &amp;amp; down resources.&lt;/p&gt;
&lt;p&gt;The nodes where I&#39;m running the benchmarks are configured with AMD EPYC 7401P 24 cores CPUs (so 48 logical cores) and 24Gb of RAM.
I&#39;ve tried to match these limits as following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kafka has 16 cpus and 8Gi of memory, same for Zookeeper&lt;/li&gt;
&lt;li&gt;Kafka channel dispatcher has 44 cpus and 22Gi of memory&lt;/li&gt;
&lt;li&gt;Sender has 16 cpus and 8Gi of memory, same for receiver&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem is, even if containers are configured with &lt;code&gt;Guaranteed&lt;/code&gt; QoS, there are no guarantees that the workload is pinned and it has exclusive access to the cores. By default, even in &lt;code&gt;Guaranteed&lt;/code&gt; QoS, Kubernetes can move the workload to different cores depending on whether the pod is throttled and which CPU cores are available at scheduling time. The Kube scheduler does it defining the &lt;a href=&quot;https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt&quot;&gt;CFS Quota&lt;/a&gt; for the running container, so it asks to the kernel scheduler to allocate a fixed time to such containers.&lt;/p&gt;
&lt;p&gt;Luckily there is a way to force the CPU pinning, enabling the &lt;a href=&quot;https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/#static-policy&quot;&gt;static CPU management&lt;/a&gt;. This can be done only configuring the &lt;a href=&quot;https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/&quot;&gt;Kubelet config file&lt;/a&gt; for each node. To do so:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the node is already running and connected to the cluster, it must be &lt;a href=&quot;https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#drain&quot;&gt;drained&lt;/a&gt; using &lt;code&gt;kubectl drain &amp;lt;node name&amp;gt; --delete-local-data --force --ignore-daemonsets&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Kubelet config file should contain the following entry: &lt;code&gt;cpuManagerPolicy: static&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Kubernetes system containers should have statically assigned resources. To do it, Kubelet config file should contain a configuration like:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;systemReserved&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1Gi&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;
&lt;p&gt;I&#39;ve tried to ran the tests after all these tweaks:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;P99&lt;/th&gt;
&lt;th&gt;P99.9&lt;/th&gt;
&lt;th&gt;P99.99&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run 1&lt;/td&gt;
&lt;td&gt;265.955238&lt;/td&gt;
&lt;td&gt;271.344500&lt;/td&gt;
&lt;td&gt;276.415000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 2&lt;/td&gt;
&lt;td&gt;264.850000&lt;/td&gt;
&lt;td&gt;271.462000&lt;/td&gt;
&lt;td&gt;279.283000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 3&lt;/td&gt;
&lt;td&gt;266.283643&lt;/td&gt;
&lt;td&gt;291.772500&lt;/td&gt;
&lt;td&gt;335.116000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 4&lt;/td&gt;
&lt;td&gt;266.065179&lt;/td&gt;
&lt;td&gt;272.497000&lt;/td&gt;
&lt;td&gt;279.553000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 5&lt;/td&gt;
&lt;td&gt;264.828300&lt;/td&gt;
&lt;td&gt;271.254500&lt;/td&gt;
&lt;td&gt;278.362000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This results looks far better! Now relative SD of P99 is down to 0.26% (0.7014114246) vs the initial 3.2%!&lt;/p&gt;
&lt;p&gt;I still have some outliers at higher percentiles, but now the results looks more trusty than the previous 3.2% of relative SD.&lt;/p&gt;
&lt;p&gt;To wrap up, I want to underline that these tweaks worked for me but they could not be enough for all benchmark configurations.&lt;/p&gt;
&lt;p&gt;Get in touch with me if you have more tweaks to show, and stay tuned for more updates!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Debts Manager Tutorial Part 3: Vert.x Web API Contract &amp; Service</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/" />
    <updated>2019-02-13T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/</id>
    <content type="html">&lt;p&gt;Today I&#39;m going to bootstrap the project starting from the contract I have already created in the &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/Debts-Manager-Tutorial-Contract-Design&quot;&gt;previous chapter&lt;/a&gt;. The aim of this chapter is to show you Vert.x Web, Vert.x Web API Contract and Vert.x Web API Service. The combination of these three packages provides all functionalities you need to create a REST API. Then I&#39;m going to use &lt;a href=&quot;https://vertx-starter.jetdrone.xyz/#maven&quot;&gt;pmlopes&#39; vertx-starter&lt;/a&gt; to scaffold the project.&lt;/p&gt;
&lt;p&gt;If you are new to Vert.x, before going further I strongly suggest you to read &lt;a href=&quot;https://vertx.io/docs/vertx-core/java/&quot;&gt;vertx-core documentation&lt;/a&gt; and &lt;a href=&quot;https://vertx.io/docs/vertx-web/java/&quot;&gt;vertx-web documentation&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;vert-x-web-api-contract-and-api-services&quot;&gt;Vert.x Web, API Contract and API Services&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Vert.x Web&lt;/strong&gt; is a library built on top of Vert.x to create web applications. It provides high level features like routing, request body handling, authorization, etc. The core concept of Vert.x Web is the &lt;code&gt;Router&lt;/code&gt; which an object that can route requests to one or more &lt;code&gt;Route&lt;/code&gt;s based on a set of rules like HTTP method, HTTP path, accepted content types, etc. On each &lt;code&gt;Route&lt;/code&gt; you can define one or more &lt;code&gt;Handler&lt;/code&gt;s that contain the logic to process the request. When a &lt;code&gt;Router&lt;/code&gt; receives a request it creates a &lt;code&gt;RoutingContext&lt;/code&gt; object which has all methods to read the request, write the response, call the next &lt;code&gt;Handler&lt;/code&gt; and fail the context. Each &lt;code&gt;Handler&lt;/code&gt; that you register consumes the request&#39;s &lt;code&gt;RoutingContext&lt;/code&gt;. Vert.x Web also provides some common handlers like the &lt;code&gt;BodyHandler&lt;/code&gt; that parses the request body and the &lt;code&gt;AuthHandler&lt;/code&gt; that manages authN/Z of the request&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/KWO_QD9yy6-809.avif 809w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/KWO_QD9yy6-809.webp 809w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/KWO_QD9yy6-809.png&quot; alt=&quot;Example architecture using Vert.x Web&quot; title=&quot;Example architecture using Vert.x Web&quot; width=&quot;809&quot; height=&quot;312&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vert.x Web API Contract&lt;/strong&gt; generates a &lt;code&gt;Router&lt;/code&gt; starting from an OpenAPI definition. Everything revolves around an object called &lt;code&gt;RouterFactory&lt;/code&gt;: you create the contract, you specify to the &lt;code&gt;RouterFactory&lt;/code&gt; what are the handlers for the defined operations and it generates the Vert.x Web &lt;code&gt;Router&lt;/code&gt; for you. The &lt;code&gt;RouterFactory&lt;/code&gt; does some magics behind the hood to provide you a &lt;code&gt;Router&lt;/code&gt; that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parses and validates the incoming requests (all kind of parameters and form or json bodies)&lt;/li&gt;
&lt;li&gt;Correctly routes the requests to the right operation handlers by generating the correct &lt;code&gt;Route&lt;/code&gt; instances&lt;/li&gt;
&lt;li&gt;Has the required security handlers mounted in the right &lt;code&gt;Route&lt;/code&gt;s&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/cyV3JeKjco-969.avif 969w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/cyV3JeKjco-969.webp 969w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/cyV3JeKjco-969.png&quot; alt=&quot;Example architecture using Vert.x Web &amp;amp; Vert.x Web API Contract&quot; title=&quot;Example architecture using Vert.x Web &amp;amp; Vert.x Web API Contract&quot; width=&quot;969&quot; height=&quot;302&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vert.x Web API Service&lt;/strong&gt; is a code generator based on concept of &lt;a href=&quot;https://vertx.io/docs/vertx-service-proxy/java/&quot;&gt;Vert.x Service Proxy&lt;/a&gt;. An &lt;a href=&quot;https://vertx.io/docs/vertx-core/java/#event_bus&quot;&gt;event bus&lt;/a&gt; service is a Java interface that helps you to define in a more natural way an event bus message consumer. This approach leads to different benefits like the straightforward process to test a message consumer like any other Java class. As every EB event consumer, a service can inhabit inside the same verticle or it can be deployed in another application somewhere else in your microservices network. You can use the Vert.x Web API Service in order to mix Contract Driven capabilites provided by Vert.x WAC and these event bus service features.
When you use EB API Services, you don&#39;t put the business logic inside the &lt;code&gt;Route&lt;/code&gt; handlers, but you can distribute it inside different services. Then by using Vert.x WAC, you can manage the linking between these services and the &lt;code&gt;Router&lt;/code&gt; instance&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/wyuYcxVI4q-1592.avif 1592w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/wyuYcxVI4q-1592.webp 1592w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/wyuYcxVI4q-1592.png&quot; alt=&quot;Example architecture using Vert.x Web &amp;amp; Vert.x Web API Contract &amp;amp; Vert.x Web API Service&quot; title=&quot;Example architecture using Vert.x Web &amp;amp; Vert.x Web API Contract &amp;amp; Vert.x Web API Service&quot; width=&quot;1592&quot; height=&quot;280&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2 id=&quot;define-the-api-services&quot;&gt;Define the API Services&lt;/h2&gt;
&lt;p&gt;Now I need to define how to group the API Operations defined inside the contract into different API Services. An important thing to keep in mind about API Services is that, &lt;em&gt;with a good design&lt;/em&gt;, you can turn each API Service into a microservice in a few minutes. Starting from this assumption, I want to group operations by different subdomains of my API. Debts Manager API handles users, transactions and status, so I&#39;m going to organize operations to create a &lt;em&gt;Users Service&lt;/em&gt;, a &lt;em&gt;Transactions Service&lt;/em&gt; and a &lt;em&gt;Status Service&lt;/em&gt;. Here is the mapping between services and operations:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transactions Service&lt;/th&gt;
&lt;th&gt;Users Service&lt;/th&gt;
&lt;th&gt;Status Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getTransactions&lt;/code&gt; &lt;br&gt; &lt;code&gt;getTransaction&lt;/code&gt; &lt;br&gt; &lt;code&gt;createTransaction&lt;/code&gt; &lt;br&gt; &lt;code&gt;updateTransaction&lt;/code&gt; &lt;br&gt; &lt;code&gt;deleteTransaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;login&lt;/code&gt; &lt;br&gt; &lt;code&gt;register&lt;/code&gt; &lt;br&gt; &lt;code&gt;getConnectedUsers&lt;/code&gt; &lt;br&gt; &lt;code&gt;connectUser&lt;/code&gt; &lt;br&gt; &lt;code&gt;getUsers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getUserStatus&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I need to assign an event bus address to each service. The interesting fact is that you can deploy more than one service instance on an address and the event bus will manage the load balancing between these. The event bus address could be any string in any format, although this it makes sense to use a domain like format to identify it. My choice is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TransactionService&lt;/code&gt; is available at &lt;code&gt;transactions.debts_manager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UsersService&lt;/code&gt; is available at &lt;code&gt;users.debts_manager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StatusService&lt;/code&gt; is available at &lt;code&gt;status.debts_manager&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;specify-the-services-inside-the-spec&quot;&gt;Specify the services inside the spec&lt;/h2&gt;
&lt;p&gt;In order to make the &lt;code&gt;RouterFactory&lt;/code&gt; been able to correcly link the &lt;code&gt;Router&lt;/code&gt; to the services, it must know what are the services&#39; addresses. To define these associations, you have a couple of &lt;a href=&quot;https://vertx.io/docs/vertx-web-api-service/java/#_mount_to_router_factory&quot;&gt;different methods&lt;/a&gt;. I&#39;m going to focus on the configuration based method: inside the OpenAPI document, for each operation, I define the related service event bus address; then just calling &lt;code&gt;mountServicesFromExtensions()&lt;/code&gt;, the &lt;code&gt;RouterFactory&lt;/code&gt; will inspect the OpenAPI document to find all those associations. E.G. the &lt;code&gt;getTransaction&lt;/code&gt; definition now looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Get a Transaction&#39;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; getTransaction
&lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;x-vertx-event-bus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transactions.debts_manager&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at &lt;a href=&quot;https://vertx.io/docs/vertx-web-api-service/java/#_using_the_extension_code_x_vertx_event_bus_code&quot;&gt;&lt;code&gt;x-vertx-event-bus&lt;/code&gt; documentation&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2 id=&quot;bootstrap-the-project&quot;&gt;Bootstrap the project&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vertx-starter&lt;/code&gt; contains a collection of different project templates called presets. I&#39;m going to focus on &lt;em&gt;&amp;quot;OpenAPI Server with Event Bus&amp;quot;&lt;/em&gt; preset to scaffold debts manager.&lt;/p&gt;
&lt;p&gt;To help the scaffolder generates the right service interfaces, you must define &lt;a href=&quot;https://github.com/pmlopes/vertx-starter/blob/master/dist/webdocs/OpenAPI_Server_With_Services.md&quot;&gt;the service address - service name mapping&lt;/a&gt;. Just a couple of entries inside Debts manager API spec and you are ready to scaffold the project:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;schemas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;securitySchemes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;x-vertx-service-gen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;transactions.debts_manager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; TransactionsService
    &lt;span class=&quot;token key atrule&quot;&gt;users.debts_manager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; UsersService
    &lt;span class=&quot;token key atrule&quot;&gt;status.debts_manager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; StatusService&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now open the &lt;a href=&quot;https://vertx-starter.jetdrone.xyz/#maven&quot;&gt;vertx-starter&lt;/a&gt; page and with just a couple of clicks you have a zip with the project scaffolded!&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/4Yx4umvAvy-1015.avif 1015w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/4Yx4umvAvy-1015.webp 1015w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/4Yx4umvAvy-1015.png&quot; alt=&quot;Vert.x Starter screenshot&quot; title=&quot;Vert.x Starter screenshot&quot; width=&quot;1015&quot; height=&quot;869&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Let&#39;s dig into the generated code:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/eN7t-8nHl3-660.avif 660w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/eN7t-8nHl3-660.webp 660w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service/eN7t-8nHl3-660.png&quot; alt=&quot;Vert.x Starter generated code tree&quot; title=&quot;Vert.x Starter generated code tree&quot; width=&quot;660&quot; height=&quot;936&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The scaffolder created for you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The POJOs which represents the API data models&lt;/li&gt;
&lt;li&gt;The service interfaces&lt;/li&gt;
&lt;li&gt;&lt;code&gt;package-info.java&lt;/code&gt; files required to trigger Vert.x annotation processing&lt;/li&gt;
&lt;li&gt;The stubs of service implementations&lt;/li&gt;
&lt;li&gt;The stubs of service tests&lt;/li&gt;
&lt;li&gt;The entrypoint of Vert.x application which is called &lt;code&gt;MainVerticle&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;ApiClient&lt;/code&gt; that can be used for testing purposes&lt;/li&gt;
&lt;li&gt;The build tool configuration file (&lt;code&gt;pom.xml&lt;/code&gt; in my case)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As every scaffolder, it is necessary to make some adjustments for the project needs.&lt;/p&gt;
&lt;h2 id=&quot;configure-the-project&quot;&gt;Configure the project&lt;/h2&gt;
&lt;p&gt;I made a couple of changes to adapt the project skeleton to my needs. In particular I configured in my pom:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://logback.qos.ch/&quot;&gt;Logback logging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Compilation with Java 11 with &lt;code&gt;maven-compiler-plugin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;JUnit 5 with &lt;code&gt;maven-surefire-plugin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactiverse.io/vertx-maven-plugin/&quot;&gt;&lt;code&gt;vertx-maven-plugin&lt;/code&gt;&lt;/a&gt; to execute and package my Vert.x application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also substituted the generated &lt;code&gt;openapi.json&lt;/code&gt; with my original &lt;code&gt;debts_manager_api.yaml&lt;/code&gt; to keep readibility of the spec document. The generated one is a bundled version with all &lt;code&gt;$ref&lt;/code&gt;s solved. If you need to bundle again your spec in future, I suggest you to use &lt;a href=&quot;http://speccy.io/&quot;&gt;Speccy&lt;/a&gt; which is a very good tool that can also convert Json Schema to OpenAPI Schema while bundling the spec.&lt;/p&gt;
&lt;h3 id=&quot;vertx-maven-plugin-and-application-properties&quot;&gt;&lt;code&gt;vertx-maven-plugin&lt;/code&gt; and application properties&lt;/h3&gt;
&lt;p&gt;Vert.x Maven plugin provides a couple of facilities to help you execute and package the Vert.x application. The usage is very simple: you must add it to your build plugins and then you must define the FQCN of the Verticle to run:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;vertx.verticle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;io.slinkydeveloper.debtsmanager.MainVerticle&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;vertx.verticle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-xml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;groupId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;io.reactiverse&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;groupId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;artifactId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;vertx-maven-plugin&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;artifactId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;1.0.18&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;executions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;execution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;vmp&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;goals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;initialize&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;package&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;goals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;execution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;executions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;redeploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;true&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;redeploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;test_config.json&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;test_config.json&lt;/code&gt; is a JSON containing application properties like PostgreSQL and Redis connection parameters. Vert.x Core includes this basic support to json configuration files: you can load it into your verticle with Vert.x command line, &lt;code&gt;DeploymentConfig&lt;/code&gt; or directly with &lt;code&gt;vertx-maven-plugin&lt;/code&gt;. If you want to configure a more complex properties management, there is a package called &lt;a href=&quot;https://vertx.io/docs/vertx-config/java&quot;&gt;vertx-config&lt;/a&gt; that enables you to load HOCON configuration files, load configuration from a remote server, etc.&lt;/p&gt;
&lt;h3 id=&quot;mainverticle&quot;&gt;&lt;code&gt;MainVerticle&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The generated &lt;code&gt;MainVerticle&lt;/code&gt; contains two methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;startHttpServer()&lt;/code&gt; to create the RouterFactory, define the various handlers, generate the &lt;code&gt;Router&lt;/code&gt; instance and start the HTTP server&lt;/li&gt;
&lt;li&gt;&lt;code&gt;startServices()&lt;/code&gt; to instantiate and mount the event bus services&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I said before, the HTTP Server and the corresponding &lt;code&gt;Router&lt;/code&gt; don&#39;t depend on the event bus services, so you can move these two methods into two separate verticles. For simplicity, I&#39;m going to mantain everything inside one verticle. In next chapters, we will spend time into splitting the verticle.&lt;/p&gt;
&lt;p&gt;To mount a service to the event bus I use an helper object called &lt;code&gt;ServiceBinder&lt;/code&gt; that lookups for the generated message handler and binds the service instance to the event bus:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;TransactionsService&lt;/span&gt; transactionsService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionsService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vertx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
serviceBinder
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;transactions.debts_manager&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransactionsService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transactionsService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I previously said, the &lt;code&gt;RouterFactory&lt;/code&gt; can lookup into the OpenAPI document for the associations between services and operations with &lt;code&gt;mountServicesFromExtensions()&lt;/code&gt;. This makes the code of &lt;code&gt;startHttpServer()&lt;/code&gt; quite simple for the moment:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;startHttpServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; future &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;future&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;OpenAPI3RouterFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;openapi.yaml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; openAPI3RouterFactoryAsyncResult &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;openAPI3RouterFactoryAsyncResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;succeeded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;OpenAPI3RouterFactory&lt;/span&gt; routerFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; openAPI3RouterFactoryAsyncResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// Mount services on event bus based on extensions&lt;/span&gt;
      routerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mountServicesFromExtensions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// Add security handlers&lt;/span&gt;
      routerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addSecurityHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;loggedUserToken&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LoggedUserTokenHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// Generate the router&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;Router&lt;/span&gt; router &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; routerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// Start the HTTP Server and bind Router&lt;/span&gt;
      server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vertx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHttpServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServerOptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;router&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      future&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;complete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Something went wrong during router factory initialization&lt;/span&gt;
      future&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;openAPI3RouterFactoryAsyncResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; future&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;configure-jwt-authn-z&quot;&gt;Configure JWT AuthN/Z&lt;/h3&gt;
&lt;p&gt;Vert.x Web already provides a good support for JWT thanks to &lt;a href=&quot;https://vertx.io/docs/vertx-auth-jwt/java/&quot;&gt;Vert.x Auth JWT&lt;/a&gt;, so I don&#39;t need to write an handler that manages the AuthN/Z.&lt;/p&gt;
&lt;p&gt;To get JWT running you need an RSA key pair to sign your tokens. I opted for the &lt;a href=&quot;https://tools.ietf.org/html/rfc7517&quot;&gt;JWK&lt;/a&gt; standard to store it and I generated the key pair and the key store using &lt;a href=&quot;https://mkjwk.org/&quot;&gt;mkjwk.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Vert.x Auth JWT provides &lt;code&gt;JWTAuth&lt;/code&gt; auth provider, which is the object that can authenticate, authorize and generate tokens. Vert.x Web has an handler called &lt;code&gt;JWTAuthHandler&lt;/code&gt; that uses this auth provider to validate and extract the payload of the token of incoming requests. I modify the &lt;code&gt;start()&lt;/code&gt; method of &lt;code&gt;MainVerticle&lt;/code&gt; to load the jwk from filesystem and create the &lt;code&gt;JWTAuth&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token function&quot;&gt;loadResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwkPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ar &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;failed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; future&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;JsonObject&lt;/span&gt; jwkObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ar&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toJsonObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;JWTAuth&lt;/span&gt; auth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JWTAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vertx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JWTAuthOptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addJwk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwkObject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;startServices&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;startHttpServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;future&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;completer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And of course I modify the &lt;code&gt;startHttpServer()&lt;/code&gt; to use &lt;code&gt;JWTAuthHandler&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;routerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addSecurityHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;loggedUserToken&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JWTAuthHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In next chapters you will see how to create a token during the login process.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The application is bootstrapped, now we are ready to deep down into application logic! In next two chapters I&#39;m going to show you how I have implemented the persistence layer and the event sourcing layer.&lt;/p&gt;
&lt;p&gt;Stay tuned for more updates!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Debts Manager Tutorial Part 2: Contract Design</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Contract-Design/" />
    <updated>2019-01-20T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Contract-Design/</id>
    <content type="html">&lt;p&gt;Hi guys! Welcome back to this tutorial!&lt;/p&gt;
&lt;p&gt;In this second chapter of Debts Manager Tutorial I would like to show you how I have designed the REST API of Debts Manager. I&#39;m going to follow the &lt;em&gt;API First&lt;/em&gt; approach, documenting all aspects of the API Design with OpenAPI 3.&lt;/p&gt;
&lt;p&gt;This post doesn&#39;t aim to provide you a full guide of how to design REST APIs: if you want more resources to learn it, &lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Contract-Design/#Some-resources-to-learn-Web-API-Design-and-OpenAPI&quot;&gt;look at the end of this post&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;analysis&quot;&gt;Analysis&lt;/h2&gt;
&lt;p&gt;The REST APIs, in contrast with RPC, are driven by the data the services wants to expose. In the previous chapter I gave you an idea of the entities we must expose. Now I tabulate these and the relative operations on it.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entity&lt;/th&gt;
&lt;th&gt;Create&lt;/th&gt;
&lt;th&gt;Retrieve&lt;/th&gt;
&lt;th&gt;Update&lt;/th&gt;
&lt;th&gt;Delete&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User relationship&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✔️&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This table is a pretty good starting point, but I must refine the analysis enforcing our methods with policies and logics.&lt;/p&gt;
&lt;p&gt;These policies are primarly based on &lt;em&gt;who is making the request&lt;/em&gt;. I&#39;m going to define a login phase together with JWT to provide authorization and authentication. Each endpoint, except &lt;code&gt;login&lt;/code&gt; and &lt;code&gt;register&lt;/code&gt;, is secured with a JWT auth. My objective is expose, for each user, only a subset of data relative to the user itself.&lt;/p&gt;
&lt;h2 id=&quot;models&quot;&gt;Models&lt;/h2&gt;
&lt;p&gt;Before defining the endpoints I must formally describe the data models representing the service entities. OpenAPI has its own Json Schema dialect to define models: &lt;a href=&quot;https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schemaObject&quot;&gt;OpenAPI Schema&lt;/a&gt;. This is an extended subset of &lt;a href=&quot;http://json-schema.org/specification-links.html#draft-5&quot;&gt;Json Schema Draft 5&lt;/a&gt;. Meanwhile I&#39;m writing, there is a proposal to allow usage of every version of Json Schema, including the newer versions, with an extension &lt;a href=&quot;https://github.com/OAI/OpenAPI-Specification/issues/1532&quot;&gt;https://github.com/OAI/OpenAPI-Specification/issues/1532&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I place these schemas in main OpenAPI file under &lt;code&gt;components&lt;/code&gt; and &lt;code&gt;schemas&lt;/code&gt; keywords. I can refeer to it using Json schema references (&lt;code&gt;$ref&lt;/code&gt; keyword).&lt;/p&gt;
&lt;p&gt;The simplest model here is the user. I want to expose only the username, so I represent it with a simple &lt;code&gt;string&lt;/code&gt;. This is the definition using OpenAPI Schema:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;Username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;minLength&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The status is represented by a map with users as keys and total debts&#92;credits as values. In OpenAPI Schema:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Map with username as keys and debt as value&#39;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
  &lt;span class=&quot;token key atrule&quot;&gt;additionalProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In JSON maps are usually represented or as json array of tuples key-value or as a json object. The json object is the natural way to represent it, but it has an important restriction: keys are strings. In my case I need to represent a map string → number, so json object representation fits good. The map values schema are defined using &lt;code&gt;additionalProperties&lt;/code&gt; and, only with Json Schema Draft 7 or newer, keys schema are defined using &lt;code&gt;propertyNames&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The main transaction model is described below:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;Transaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
  &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Insertion datetime&quot;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;time
      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;minLength&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
  &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; from
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; to
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; id
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; description
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; value
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; at&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;$ref&lt;/code&gt; keyword points to the &lt;code&gt;Username&lt;/code&gt; schema I defined before.&lt;/p&gt;
&lt;p&gt;This model doesn&#39;t fit good for my usage, because for each &lt;code&gt;Transaction&lt;/code&gt; endpoint I want to apply some policies. A very common example is the &lt;code&gt;id&lt;/code&gt; field: when user inserts a new transaction I want to designate the database to fill the &lt;code&gt;id&lt;/code&gt; value. When the user creates a new transaction it shouldn&#39;t add the &lt;code&gt;id&lt;/code&gt; field: that means that I can&#39;t use the &lt;code&gt;Transaction&lt;/code&gt; model to describe the &amp;quot;create transaction&amp;quot; request body. Let&#39;s look at all restrictions I want to apply on various transaction endpoints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt; and &lt;code&gt;at&lt;/code&gt; are filled by the backend when user adds a new transaction and they are immutable from the API perspective&lt;/li&gt;
&lt;li&gt;When user updates a transaction he can&#39;t update the &lt;code&gt;from&lt;/code&gt; (sender) and &lt;code&gt;to&lt;/code&gt; (receiver) fields&lt;/li&gt;
&lt;li&gt;When user adds a new transaction he doesn&#39;t need to fill the &lt;code&gt;from&lt;/code&gt; field because the backend fills it with the logged user&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To apply these restrictions I create a new model for each endpoint. I&#39;m going to refactor &lt;code&gt;Transaction&lt;/code&gt; into 3 different models: &lt;code&gt;UpdateTransaction&lt;/code&gt;, &lt;code&gt;NewTransaction&lt;/code&gt; and &lt;code&gt;Transaction&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These new models lead to a new problem: duplication of model fields definitions. Json schema solves the duplication with schema composition keywords: &lt;code&gt;allOf&lt;/code&gt;, &lt;code&gt;anyOf&lt;/code&gt; and &lt;code&gt;oneOf&lt;/code&gt;. In particular I will use &lt;code&gt;allOf&lt;/code&gt; to achieve inheritance of schemas.&lt;/p&gt;
&lt;p&gt;This is the final result:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;UpdateTransaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
  &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;minLength&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
&lt;span class=&quot;token key atrule&quot;&gt;NewTransaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;allOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/UpdateTransaction&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; to
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; description
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; value
      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
&lt;span class=&quot;token key atrule&quot;&gt;Transaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;allOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/NewTransaction&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; from
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; to
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; id
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; description
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; value
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; at
      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
      &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
        &lt;span class=&quot;token key atrule&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Insertion datetime&quot;&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;time
          &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The schemas inheritance tree is &lt;code&gt;UpdateTransaction&lt;/code&gt;←&lt;code&gt;NewTransaction&lt;/code&gt;←&lt;code&gt;Transaction&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;endpoints&quot;&gt;Endpoints&lt;/h2&gt;
&lt;p&gt;OpenAPI document structures the endpoint definitions as follow:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;/pathA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;/pathB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  /pathC/&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;paramA&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OpenAPI path strings allow path parameters using &lt;code&gt;{paramName}&lt;/code&gt; and doesn&#39;t require an explicit definition of query parameters.&lt;/p&gt;
&lt;p&gt;In OpenAPI terminology an &lt;strong&gt;operation&lt;/strong&gt; is an API endpoint identified by a path and an HTTP method. Every operation could be uniquely identified with an &lt;code&gt;operationId&lt;/code&gt;. The OpenAPI Specification (OAS) documents this field as optional, but I strongly suggest to specify it if you don&#39;t want to see your tooling explode. Most code generation tooling asserts that &lt;code&gt;operationId&lt;/code&gt; is present. If it&#39;s not present they try to infeer it from path and http method producing unexpected results.&lt;/p&gt;
&lt;p&gt;For each operation we are going to define:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;operationId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;parameters&lt;/code&gt; (if any): List of &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt;, &lt;code&gt;query&lt;/code&gt; and &lt;code&gt;cookie&lt;/code&gt; parameters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;requestBody&lt;/code&gt; (if any): Content type and content schema of request bodies&lt;/li&gt;
&lt;li&gt;&lt;code&gt;responses&lt;/code&gt;: Status code with response content type and schemas&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also fill the &lt;code&gt;security&lt;/code&gt; field for each operation to require a JWT token to execute it.&lt;/p&gt;
&lt;h3 id=&quot;transactions-and-status&quot;&gt;Transactions and Status&lt;/h3&gt;
&lt;p&gt;Let&#39;s start with transaction CRUDs:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;&lt;code&gt;operationId&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;CRUD&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;HTTP Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create a new transaction&lt;/td&gt;
&lt;td&gt;&lt;code&gt;createTransaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;C&lt;/strong&gt;reate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/transactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get a single transaction&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getTransaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;R&lt;/strong&gt;etrieve&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/transactions/{transactionId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get user related transactions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getTransactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;R&lt;/strong&gt;etrieve multiple&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/transactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update a transaction&lt;/td&gt;
&lt;td&gt;&lt;code&gt;updateTransaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;U&lt;/strong&gt;pdate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/transactions/{transactionId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete a transaction&lt;/td&gt;
&lt;td&gt;&lt;code&gt;deleteTransaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt;elete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/transactions/{transactionId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In OpenAPI:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;  &lt;span class=&quot;token key atrule&quot;&gt;/transactions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; getTransactions
      &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;200&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; array
                &lt;span class=&quot;token key atrule&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Transaction&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired token&#39;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; createTransaction
      &lt;span class=&quot;token key atrule&quot;&gt;requestBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/NewTransaction&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;201&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Successful response.&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired Token&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;403&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Trying to create a transaction with receiver not connected to logged user&quot;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;&#39;/transactions/{transactionId}&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; getTransaction
      &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;200&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Transaction&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired Token&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;403&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Trying to get a transaction where `from` or `to` is not the logged user&quot;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; updateTransaction
      &lt;span class=&quot;token key atrule&quot;&gt;requestBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/UpdateTransaction&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;202&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Successful response.&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired Token&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;403&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Trying to update a transaction where `from` is not the logged user&#39;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deleteTransaction
      &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;204&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Successful response.&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired Token &#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&#39;403&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Trying to remove a transaction where `from` is not the logged user&#39;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transactionId
        &lt;span class=&quot;token key atrule&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that for all operations under &lt;code&gt;/transactions/{transactionId}&lt;/code&gt; path I haven&#39;t redefined every time the parameter &lt;code&gt;transactionId&lt;/code&gt;: I have defined once at path level.&lt;/p&gt;
&lt;p&gt;Status has only the &lt;em&gt;retrieve&lt;/em&gt; operation, but I want to let user customize the output based on transactions insertion datetime: clients can use query parameter &lt;code&gt;till&lt;/code&gt; to ask the status till the date time provided, excluding newer transactions. You can use it to throw back in your house mate face that he didn&#39;t pay the bills for a quite long time.&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;/status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; getUserStatus
    &lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; till
        &lt;span class=&quot;token key atrule&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; query
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
          &lt;span class=&quot;token key atrule&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;date-time&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;200&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Status&#39;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired token&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;user-and-user-relationships&quot;&gt;User and User relationships&lt;/h3&gt;
&lt;p&gt;The service supports creation and retrieval of users and user relationships. For simplicity I avoided to include &lt;strong&gt;U&lt;/strong&gt; and &lt;strong&gt;D&lt;/strong&gt; operations for user and user relationships.&lt;/p&gt;
&lt;p&gt;I want to expose an endpoint to retrieve all registered users and an endpoint to retrieve only users that have a relationship with logged user:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;/users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; getUsers
    &lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; filter
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; query
        &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;200&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; array
              &lt;span class=&quot;token key atrule&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired token&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;/users/connected&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; getConnectedUsers
    &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;200&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
              &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;allowedTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Users that logged user can bill&quot;&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; array
                  &lt;span class=&quot;token key atrule&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;allowedFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Users that can bill the logged user&quot;&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; array
                  &lt;span class=&quot;token key atrule&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired token&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;getUsers&lt;/code&gt; we defined a very basic search functionality with the optional query parameter &lt;code&gt;filter&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;getConnectedUser&lt;/code&gt; I prefeered to define the request schema directly inside the request body definition because It&#39;s a schema strictly related to this operation and It isn&#39;t parent of any other schema.&lt;/p&gt;
&lt;p&gt;This is the endpoint to create a user connection (user relationship):&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;/users/connected/&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;userToConnect&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; connectUser
    &lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; userToConnect
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path
        &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;201&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;User connected&#39;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;401&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Expired token&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;login-registration-and-jwt&quot;&gt;Login, registration and JWT&lt;/h3&gt;
&lt;p&gt;When an user wants to start using this API he must &lt;strong&gt;authenticate&lt;/strong&gt; with his credentials following this process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User calls the &lt;code&gt;/login&lt;/code&gt; endpoint passing his credentials in the request body&lt;/li&gt;
&lt;li&gt;The backend checks if credentials are correct&lt;/li&gt;
&lt;li&gt;The backend writes the response with a JWT token containing the username inside the payload&lt;/li&gt;
&lt;li&gt;User stores the received JWT token&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For each request the server must &lt;strong&gt;authorize&lt;/strong&gt; the user. The user must include inside each request the header &lt;code&gt;Authorization: Bearer &amp;lt;jwt token&amp;gt;&lt;/code&gt;. When the backend receives the request it checks the signature validity and the token expiration time. If the token is valid It parses the payload, where It can read the username of the logged user.&lt;/p&gt;
&lt;p&gt;This is the &lt;code&gt;login&lt;/code&gt; operation definition:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;/login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; login
    &lt;span class=&quot;token key atrule&quot;&gt;requestBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; username
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; password
            &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
            &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
      &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;200&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Returns the JWT token&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;text/plain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;400&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Wrong username or password&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;register&lt;/code&gt; operation creates a new user and logins it:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;/register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;operationId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; register
    &lt;span class=&quot;token key atrule&quot;&gt;requestBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; username
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; password
            &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object
            &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;$ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#/components/schemas/Username&#39;&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
      &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;responses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;200&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Returns the JWT Token&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;text/plain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;&#39;400&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Username already exists&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don&#39;t cover in this tutorial the logout process, but I want to give you a tip: create a whitelist or blacklist of tokens.&lt;/p&gt;
&lt;p&gt;As you already saw, each secured operation has the &lt;code&gt;security&lt;/code&gt; field:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;security&lt;/code&gt; field is called &lt;strong&gt;security requirement&lt;/strong&gt; and it tells the user that he needs &lt;code&gt;loggedUserToken&lt;/code&gt; &lt;strong&gt;security schema&lt;/strong&gt; to access to this endpoint. Security schemas must be defined under &lt;code&gt;#/components/securitySchemes&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;securitySchemes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;loggedUserToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http
    &lt;span class=&quot;token key atrule&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bearer&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;some-resources-to-learn-web-api-design-and-openapi&quot;&gt;Some resources to learn Web API Design and OpenAPI&lt;/h2&gt;
&lt;p&gt;I give you a couple of useful links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.restapitutorial.com/&quot;&gt;Rest API Tutorial&lt;/a&gt;: Very simple and coincise tutorial for newbies of REST APIs world&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://apistylebook.com/&quot;&gt;API Stylebook&lt;/a&gt;: Collection of API styleguides from different IT companies&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/OAI/OpenAPI-Specification/&quot;&gt;OpenAPI Specification repository&lt;/a&gt;: Contains the spec and examples&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apis.guru/browse-apis/&quot;&gt;OpenAPI Directory&lt;/a&gt;: Collection of OpenAPIs of different public APIs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can find the complete OpenAPI definition here: &lt;a href=&quot;https://github.com/slinkydeveloper/debts-manager/blob/master/src/main/resources/debts_manager_api.yaml&quot;&gt;/src/main/resources/debts_manager_api.yaml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After you learnt how to design a REST API, approacching to OpenAPI is very simple. The operation definition is very intuitive because of 1:1 mapping with HTTP (methods, parameters, status codes, content types and so on). The tricky and magic part, for me, is definining and organizing the JSON Schemas. When you define simple models, you tend to put everything inside the same file. But when you raise the complexity using composed schemas, you get flooded by smaller and unclear schemas. My suggestion for you is to document the schemas with &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; keywords and organize these in multiple files.&lt;/p&gt;
&lt;p&gt;In next chapter I&#39;m going to bootstrap the project and start writing first Vert.x code, stay tuned!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Debts Manager Tutorial Part 1: Introduction</title>
    <link href="https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Introduction/" />
    <updated>2018-12-13T00:00:00Z</updated>
    <id>https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Introduction/</id>
    <content type="html">&lt;p&gt;Some months ago I decided to create a complete Vert.x application to show you capabilities of Vert.x for building Web APIs and, at the same time, I wanted to try some patterns I never used or applied. I&#39;m going to create a &lt;em&gt;production ready&lt;/em&gt; application to finally manage the debts with my house mate with a fully powered Vert.x application!&lt;/p&gt;
&lt;p&gt;Some notes before starting: I&#39;m going to make this guide as complete as possible, but keep in mind that this is a side project and It could contain bugs and It could be incomplete. I will try to cover all interesting aspects about API design, implementation, testing and I will show you how I implemented Event Sourcing and CQRS. I don&#39;t plan to write a frontend for it (I don&#39;t want to hurt your eyes), but if you want to help me I&#39;m glad to accept it!&lt;/p&gt;
&lt;p&gt;The code is already available on &lt;a href=&quot;https://github.com/slinkydeveloper/debts-manager&quot;&gt;GitHub&lt;/a&gt; but It could change while I&#39;m writing the guide.&lt;/p&gt;
&lt;h2 id=&quot;what-debts-manager-should-do&quot;&gt;What Debts Manager should do&lt;/h2&gt;
&lt;p&gt;The purpose of Debts Manager is to manage the debts between two users of the service. The idea is similar to &lt;a href=&quot;https://www.splitwise.com/&quot;&gt;Splitwise&lt;/a&gt;, but it will support only bills between two users. Every user should be registered to use the application. Then, if you want to receive bills from another user, you must &lt;strong&gt;connect&lt;/strong&gt; to that user. When you are connected, you can bill him creating a transaction. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User A registers to the platform&lt;/li&gt;
&lt;li&gt;User B registers to the platform&lt;/li&gt;
&lt;li&gt;User B allows user A to bill himself. It does connecting to user A&lt;/li&gt;
&lt;li&gt;User A bills user B of 5 Euros for last grocery shopping&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The final result is: user B now has a debt of 5 Euros with user A. Debts Manager will show to both users their status with various debts/credits&lt;/p&gt;
&lt;p&gt;The connection between users are unidirectional, which means that if users want to bill each other they must create two diffent connections. There is no group concept, I wanted to keep things as simple as possible.&lt;/p&gt;
&lt;h2 id=&quot;design&quot;&gt;Design&lt;/h2&gt;
&lt;p&gt;Before going further I want to show you a couple of things of the overall design of the application. These are required to undestand various aspects of the tutorial.&lt;/p&gt;
&lt;h3 id=&quot;persistence-and-event-sourcing&quot;&gt;Persistence &amp;amp; Event Sourcing&lt;/h3&gt;
&lt;p&gt;For persistence I choose PostgreSQL to store my data. The application stores into the database:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The users instances (&lt;em&gt;user&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;The connections between users (&lt;em&gt;user relationship&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;The bills (&lt;em&gt;transaction&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The DB access is provided by the blazing fast &lt;a href=&quot;https://github.com/reactiverse/reactive-pg-client&quot;&gt;reactive-pg-client library&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The application stores the &lt;em&gt;transactions&lt;/em&gt; between users (events). You can use it as a log of various bills, but you also want to look at a summary of various credits/debits between connected users. To build it, I aggregate the various transactions into one single structure that I call &lt;em&gt;status&lt;/em&gt;. Every user has a status and is represented as a map with users as keys and total credits/debits as values. This map is built incrementally every time a user adds/modifies/removes a transaction and is stored in a Redis cache.&lt;/p&gt;
&lt;h3 id=&quot;web-api&quot;&gt;Web API&lt;/h3&gt;
&lt;p&gt;The application exposes a Web REST API that you can interact with. It is documented with an OpenAPI 3 file and exposes most of CRUD endpoints for users, user connections and transactions (some are missing to keep things simple). It also has an endpoint to access status of users. The endpoints are protected with JWT tokens, so to use the application you must complete a login request and you get a token to use for the following requests. The Web API is implemented using &lt;a href=&quot;https://vertx.io/docs/vertx-web/java/&quot;&gt;vertx-web&lt;/a&gt;, &lt;a href=&quot;https://vertx.io/docs/vertx-web-api-contract/java/&quot;&gt;vertx-web-api-contract&lt;/a&gt; and &lt;a href=&quot;https://vertx.io/docs/vertx-web-api-service/java/&quot;&gt;vertx-web-api-service&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;testing&quot;&gt;Testing&lt;/h3&gt;
&lt;p&gt;Okay I admit it, I&#39;m lazy 😄 I tested only the minimum features! I built these tests primarly to show you how I faced and solved common async test problems. I used Junit5 together with &lt;a href=&quot;https://vertx.io/docs/vertx-junit5/java/&quot;&gt;vertx-junit5&lt;/a&gt; and &lt;a href=&quot;https://www.testcontainers.org/&quot;&gt;testcontainers&lt;/a&gt; to spin up Redis and PostgreSQL.&lt;/p&gt;
&lt;h2 id=&quot;tutorial-parts&quot;&gt;Tutorial parts&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Introduction/Debts-Manager-Tutorial-Contract-Design&quot;&gt;Contract design&lt;/a&gt;&lt;/strong&gt;: Design the OpenAPI 3 contract&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://example.com/slinkydeveloper.github.io/blog/Debts-Manager-Tutorial-Introduction/Debts-Manager-Tutorial-Vert-x-Web-API-Contract-Service&quot;&gt;Vert.x Web API Contract &amp;amp; Service&lt;/a&gt;&lt;/strong&gt;: Setup Vert.x project and bind Vert.x Event Bus services&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistence&lt;/strong&gt;: Design and implement persistence&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event Sourcing&lt;/strong&gt;: Develop the read model and CQRS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implement services logic&lt;/strong&gt;: Implement the services&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: Spin up test containers and write clean assertions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BONUS: Deploy to OpenShift&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BONUS: Refactor to microservices using Vert.x Event Bus&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Stay tuned for next chapter! And give me feedback about this tutorial!&lt;/p&gt;
</content>
  </entry>
</feed>