<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ghost's Blog]]></title><description><![CDATA[About software and data]]></description><link>https://sgmoratilla.com</link><generator>GatsbyJS</generator><lastBuildDate>Mon, 02 Feb 2026 19:23:58 GMT</lastBuildDate><item><title><![CDATA[AI and a personal project]]></title><description><![CDATA[Yes, another AI post. I apologize, but I think this one will be worth it. I’m tired of posts about companies downsizing because of “AI…]]></description><link>https://sgmoratilla.com/2026-02-01-ai-and-a-persona-project/</link><guid isPermaLink="false">https://sgmoratilla.com/2026-02-01-ai-and-a-persona-project/</guid><pubDate>Sun, 01 Feb 2026 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yes, another AI post. I apologize, but I think this one will be worth it. I’m tired of posts about companies downsizing because of “AI” (when they really mean LLMs) too, or engineers bragging about workflows with 5 parallel agents (only to throw 95% of the generated PRs in the trash). I’m also tired of companies trying to taking away the joy we found in building. Definitely, we can’t have nice things.&lt;/p&gt;
&lt;p&gt;The thing is, today I want to tell you about a project that AI has actually enabled me to do something I wouldn’t have done without these tools: the transcription of a handwritten diary my grandfather left behind. My grandfather wrote this in 1989. They are his memoirs of the Spanish Civil War. He passed away 20 years ago and, when I inherited this notebook, I tried to transcribe it. It’s full of spelling mistakes, common for people of his generation, and the handwriting is quite difficult to read. It took me a whole day just to poorly transcribe a couple of pages. I abandoned the project.&lt;/p&gt;
&lt;p&gt;But the other day, I decided to feed the first two pages to ChatGPT. And “magic” happened: on its own, it managed to enhance the image, run it through OCR, and get most of the content right. It still makes plenty of mistakes, which is normal given the spelling and the handwriting, but it has allowed me to decipher the script and finish the sentences myself.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1814px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5e47754410a4ea71f75523a7218e7cb1/97d01/project-notebook.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAACzUlEQVQ4y23T3U/bZhTHcf+bu9zVNm0X0yYNdWopmcSyNRAIIcSOkzixHTt27EAIAda3qZ3a7R+oaN7L6wUCkQCCBALf6TGjHRtHOoplKR/9nuccS8PhkMvLS0RdX1/f6aurq+D9h26HSPghlbLJ6rJNxdfwiwplR6HiqVS8NFU/gxJ7hHR8fMzu7i79fp+Li4s78C1Y33jHN198xquXS/z1psbrVxV+f1nm6XqRp+sW1SWNkqMyF/kBqd/r0Wq12Nvb4/DwEJH4v2Dj/TtCj77lz7fPeP6iytpvFWpriyyvuJSXLBxXw7YzyPHQTcJutxu0SHovWN/gSfhH3r5Zp7ZaplIrU1os4i8VsV2TXF7FNFVSyZ+Rer0e9XqdnZ0d2u02JycnH499CzYbG0xHxvnj9Rorqz6uX6RUtv8BDXRDxbJSGPlZJAE0Go0APDo6unOPn8D3zEZDPH+2iFe2cTybYsnE8QoBqOUVTFPBKiRuwE6nExx5c3Pz3oStZp2ZaIj1NRfPL1Ao6pi2Hvw6JR3dVDEMGbeYRBLTFaDADg4OOD8//x/YaTdZSPzCStXCdnRc38SwcuQMDdPKkssraNkEJUe+AUU6gYnn2/o32O20SCkRalUxUR3HMwJYdMHSKFjp4A59V7kBm80mW1tbwdoMBoMAE/Vpsdtk01FWlk3ckk7BzlHyDdxSHtvRcN00npti0ZORTk9Pg+mKI+/v798Lbn7ooGWiLJd1NE0ml09hBPeWQtcVVHWOfDaKpceQjvt9GvUG29vbCPy+L0UklOfDeJZCWo2jKHMsLMyiKDEymTjZTIxMahozF0USfxLLLJKNRiPuK5FwfiaEkZlBladJJadIJSPIC7+STISR5ydJzE4gxyeQzgcDBsPhR1RM+bbPzs4Yja6CxZ6aHGN+eoJ4dILY1DhT4TGeTH7PTw+/5vGDrxgf+5IH333O35Z2qyHZ1PPcAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;First page transcribed&quot;
        title=&quot;First page transcribed&quot;
        src=&quot;/static/5e47754410a4ea71f75523a7218e7cb1/97d01/project-notebook.png&quot;
        srcset=&quot;/static/5e47754410a4ea71f75523a7218e7cb1/0eb09/project-notebook.png 500w,
/static/5e47754410a4ea71f75523a7218e7cb1/1263b/project-notebook.png 1000w,
/static/5e47754410a4ea71f75523a7218e7cb1/97d01/project-notebook.png 1814w&quot;
        sizes=&quot;(max-width: 1814px) 100vw, 1814px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;video controls width=&quot;100%&quot;&gt;
  &lt;source src=&quot;/6349fcc8003454e0e35ff671604dc503/project-notebook-chatgpt.mp4&quot; type=&quot;video/mp4&quot;&gt;
  Your browser does not support the video tag.
&lt;/video&gt;
&lt;p&gt;I don’t know where this project will lead me. My boss told me that when you start worrying about your past, it’s a sign that you’ve officially grown old. And I couldn’t agree more :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The value in execution]]></title><description><![CDATA[As you grow in your career, you tend to move away from execution roles. You increasingly spend more time doing management and less time…]]></description><link>https://sgmoratilla.com/2025-11-05-the-value-in-execution/</link><guid isPermaLink="false">https://sgmoratilla.com/2025-11-05-the-value-in-execution/</guid><pubDate>Wed, 05 Nov 2025 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As you grow in your career, you tend to move away from execution roles. You increasingly spend more time doing management and less time executing. Sometimes, you even get promoted right into a position where you become totally useless.&lt;/p&gt;
&lt;p&gt;Companies tend to push you into these management roles if you want to grow. The Individual Contributor (IC) path often has a shorter trajectory, while the management path is usually longer. Perhaps this makes sense at an organizational level (you manage more people), but in terms of impact, I have serious doubts. Every day, I see engineers who have far more cross-departmental impact than positions supposedly designed for it. The difference is that the engineers have the power of execution, while many managers actually don’t: others execute for them.&lt;/p&gt;
&lt;p&gt;That’s why I believe it’s so crucial to remain capable of getting down to execution (or &lt;em&gt;getting your hands dirty&lt;/em&gt;, as some dismissively call it). I’m not saying you have to be constantly executing (which many would label as micromanagement), but there are critical projects where you absolutely must support your team.&lt;/p&gt;
&lt;p&gt;I see in my own teams that when managers share the actual workload, or are capable of supporting their team’s execution during tough times, the manager is better regarded and the team becomes more cohesive. Supporting execution doesn’t necessarily mean just writing code. It can mean simplifying a problem, helping to minimize a trade-off, or assisting with a complex technical decision.&lt;/p&gt;
&lt;p&gt;I’m writing this today because I watched two independent teams collaborate incredibly fast on a common problem, and I believe it had a lot to do with the fact that the managers got our &lt;em&gt;hands dirty&lt;/em&gt; quickly.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cold calls and closed doors]]></title><description><![CDATA[Several salespeople have contacted me because of this blog. On one hand, I like that you try new ways to get our attention. On the other, it…]]></description><link>https://sgmoratilla.com/2025-09-14-cold-calls-closed-doors/</link><guid isPermaLink="false">https://sgmoratilla.com/2025-09-14-cold-calls-closed-doors/</guid><pubDate>Sun, 14 Sep 2025 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Several salespeople have contacted me because of this blog. On one hand, I like that you try new ways to get our attention. On the other, it’s obvious you don’t care about what I write: it’s just an excuse for a hook.&lt;/p&gt;
&lt;p&gt;For years I’ve been bombarded with calls, even to my personal number. At this point, if you’re not in my contacts, I don’t answer. That sometimes makes me miss important calls: medical or other urgent ones. So you’ll understand why I’m defensive when I get this type of call.&lt;/p&gt;
&lt;p&gt;I’ve never been in sales, but I’ve always thought cold calls can’t really work: you’re trying to sell to someone who doesn’t want to buy. Not because your product isn’t interesting, but because we’re not in the mood to be interrupted anytime.&lt;/p&gt;
&lt;p&gt;Another big annoyance: urgency. Tech leaders are usually very busy. It’s unrealistic to expect us to have time this same week to meet.&lt;/p&gt;
&lt;p&gt;If you’re a salesperson reading this and want my honest advice: get introduced by someone. If you have no common contacts, try an email, but know that we already get dozens like yours. And please, don’t call us unless we’ve explicitly given you permission (spoiler: what your marketing tool says isn’t it, even if it’s legally covered).&lt;/p&gt;
&lt;p&gt;Most likely, you’ll just close a door forever.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Starting to fail]]></title><description><![CDATA[Two things happened to me this month: I missed a meeting and I missed a train. That had never happened to me in 20 years, and for most…]]></description><link>https://sgmoratilla.com/2025-06-20-starting-to-fail/</link><guid isPermaLink="false">https://sgmoratilla.com/2025-06-20-starting-to-fail/</guid><pubDate>Fri, 20 Jun 2025 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Two things happened to me this month: I missed a meeting and I missed a train.&lt;/p&gt;
&lt;p&gt;That had never happened to me in 20 years, and for most people, it would probably just be another anecdote. But I feel there may be something else. I may be hitting my ceiling.&lt;/p&gt;
&lt;p&gt;I’ve been feeling for several months now that I’m trying to swallow more than I should. It has worked so far. I see how these two examples may seem pretty small, but I think they were a good warning: stop and delegate before it’s too late.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Thoughs on my way home]]></title><description><![CDATA[The 21st century really is amazing. I’m on an AVE train back to Madrid, coding on a laptop that weighs less than 1 kg, connected to the…]]></description><link>https://sgmoratilla.com/2025-04-19-thoughts-on-my-way-home/</link><guid isPermaLink="false">https://sgmoratilla.com/2025-04-19-thoughts-on-my-way-home/</guid><pubDate>Sat, 19 Apr 2025 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The 21st century really is amazing. I’m on an AVE train back to Madrid, coding on a laptop that weighs less than 1 kg, connected to the Internet. I barely type —an AI does the heavy lifting. I give it commands, review what it gives me, make corrections, and I don’t forget to say please and thank you.&lt;/p&gt;
&lt;p&gt;Seven years ago, I was on another AVE headed to Valencia. Back then, my laptop was twice as heavy and had barely any Internet. I wrote the foundations of Playtomic’s payment flow in those 2.5 hours. All in Java + Spring Boot, hand-coded in IntelliJ. The DB was local. Today, I’m in Cursor with Next.js/TypeScript, and the DB is remote on NeonDB.&lt;/p&gt;
&lt;p&gt;I used to prep offline work for train rides. Now? Nothing. If I don’t have a connection, it’s not worth working —better to wait until I arrive.&lt;/p&gt;
&lt;p&gt;Makes me really think about how much our profession is going to change in the coming years.&lt;/p&gt;
&lt;p&gt;Also, this is the first post I write in Spanish on this blog and I’m asking ChatGPT to translate it for me, in my style.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[8 years at Playtomic.io]]></title><description><![CDATA[I made 8 years at Playtomic today. I feel old :) I’ve been reading my post from last year. Well… this year has not been well-balanced at all…]]></description><link>https://sgmoratilla.com/2025-02-14-8-years-at-playtomic/</link><guid isPermaLink="false">https://sgmoratilla.com/2025-02-14-8-years-at-playtomic/</guid><pubDate>Fri, 14 Feb 2025 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I made 8 years at Playtomic today. I feel old :)&lt;/p&gt;
&lt;p&gt;I’ve been reading &lt;a href=&quot;/2024-02-13-7-years-at-playtomic/&quot;&gt;my post from last year&lt;/a&gt;. Well… this year has not been well-balanced at all 🥲.&lt;/p&gt;
&lt;p&gt;I’ve been responsible for Tech since August. My predecessor left me with a lot of debt to pay, so it has been a year to surface it. We are better than ever in processes, resilience, security, and, especially, internal communication. Still work to do, but I see light at the end of the tunnel.&lt;/p&gt;
&lt;p&gt;One of the things I enjoyed the most is the connections I’ve made with many other players in the industry. Many of our providers use Playtomic, and they don’t say it just to gain our favor: they really use it. It’s always an honor when some of our main providers (AWS, Google, MongoDB, Datadog) want to visit us directly in our offices.&lt;/p&gt;
&lt;p&gt;What I enjoyed the least was the amount of spam I received on my personal phone number. If you are trying to reach me to sell me something and you read this, be warned: you will be automatically banned from working with me in the future. Yes, I am pretty mad about this.&lt;/p&gt;
&lt;p&gt;For 2025, my expectation is to move from this reactive/internal work to a more proactive/external approach. Let’s see how it goes.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What I Learned in 2024]]></title><description><![CDATA[I don’t usually write year-end reviews, but I opened this blog and saw that I haven’t posted since February. The drop in time I spend…]]></description><link>https://sgmoratilla.com/2025-01-01-2024-balance/</link><guid isPermaLink="false">https://sgmoratilla.com/2025-01-01-2024-balance/</guid><pubDate>Wed, 01 Jan 2025 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I don’t usually write year-end reviews, but I opened this blog and saw that I haven’t posted since February. The drop in time I spend writing is concerning, but it’s something I plan to change in 2025.&lt;/p&gt;
&lt;p&gt;I’m rereading that post, and back in February, I mentioned having a good work-life balance and even getting back to my hobbies. Well, all of that went out the window in April for a good reason: I was promoted to Senior Director of Technology! I am really proud of leading the technical decisions at one of Spain’s most promising companies: Playtomic. But it came with a cost: it has taken all my energy, and all my free time has gone toward staying sane. Sun, exercise, enough sleep, and friends. Many would envy that, but I usually dedicate a lot of time to personal projects (don’t worry, they’re not coding-related).&lt;/p&gt;
&lt;p&gt;The point is, I want to make a list of things I’ve learned or applied this year:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Work is probably the most important thing you’ll do daily, but your health is more important.&lt;/li&gt;
&lt;li&gt;Outside of work, disconnect. Eight hours are more than enough, and you need to be at your best the next day. It’s a marathon, not a sprint.&lt;/li&gt;
&lt;li&gt;You’ll be a better leader if you can show vulnerability: sometimes you won’t have the answer, and the best thing is to say it openly.&lt;/li&gt;
&lt;li&gt;Share your doubts with your team.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I don’t buy into the “loneliness of leadership” idea&lt;/strong&gt;. I’ve never felt more supported.&lt;/li&gt;
&lt;li&gt;Everyone communicates worse than they think (especially managers).&lt;/li&gt;
&lt;li&gt;Everyone understands what they want to some extent (especially employees).&lt;/li&gt;
&lt;li&gt;Everyone is more sensitive than they would like to admit (especially… everyone).&lt;/li&gt;
&lt;li&gt;Everyone should learn to put misunderstandings into perspective.&lt;/li&gt;
&lt;li&gt;Want to be a role model? It’s not enough to be right, you have to convince others.&lt;/li&gt;
&lt;li&gt;You don’t have to fight every battle.&lt;/li&gt;
&lt;li&gt;Some battles are for others to learn (even if you think they will lose).&lt;/li&gt;
&lt;li&gt;Quit Twitter/X. This should have been first.&lt;/li&gt;
&lt;li&gt;Attend more tech events and get out of your cave, I mean, office.&lt;/li&gt;
&lt;li&gt;Your email, phone, and LinkedIn will get bombarded. You own your time, and you don’t have to reply to everyone.&lt;/li&gt;
&lt;li&gt;That said, respond to contacts you want to maintain, even if it is to say now isn’t the right time.&lt;/li&gt;
&lt;li&gt;There are projects necessary for the company, and there are projects necessary for your team.&lt;/li&gt;
&lt;li&gt;Learn to find time for the latter and learn to sell them to your company.&lt;/li&gt;
&lt;li&gt;Photography made me a better engineer.&lt;/li&gt;
&lt;li&gt;We don’t want to read texts written by AI. We want to hear the writer’s voice.&lt;/li&gt;
&lt;li&gt;Learn to write in your own voice. In the coming years, everyone will sound the same. Like ChatGPT. This will be to your advantage.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I am sure I am forgetting some things, but 2025 looks promising, with exciting professional and personal projects that I hope I will finally have time to write about here. I definitely have to write about how having another passion has made me a better professional.&lt;/p&gt;
&lt;p&gt;Happy 2025!&lt;/p&gt;
&lt;p&gt;Kelly Sikkema &lt;a href=&quot;https://unsplash.com/photos/white-spiral-notebook-on-brown-wooden-table-2q_frVRXWfQ&quot;&gt;https://unsplash.com/photos/white-spiral-notebook-on-brown-wooden-table-2q_frVRXWfQ&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[7 years at Playtomic.io]]></title><description><![CDATA[This year I haven’t written much. Time flew by; I blinked a few times, and suddenly, a whole year had passed. I’ve added only 2 posts to…]]></description><link>https://sgmoratilla.com/2024-02-13-7-years-at-playtomic/</link><guid isPermaLink="false">https://sgmoratilla.com/2024-02-13-7-years-at-playtomic/</guid><pubDate>Wed, 14 Feb 2024 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This year I haven’t written much. Time flew by; I blinked a few times, and suddenly, a whole year had passed. I’ve added only 2 posts to this blog. And Today also marks another year at Playtomic. That makes seven.&lt;/p&gt;
&lt;p&gt;This was the year of AI, or more precisely, the year of generative AI. I still believe there is a lot of IA from the 90s and 00s that can be applied to many of our current challenges in the industry. With just a touch of linear regression, we could significantly simplify our lives :)&lt;/p&gt;
&lt;p&gt;One of my previous employers told me that five years in enough in a company to learn most of you can get from that company. I somewhat agree with him, but this is the first year that I have felt that my role is well-balanced with my workload. I want to savor this moment. I still have ton of work, but now there are people managing the day-to-day. I can’t thank them enough.&lt;/p&gt;
&lt;p&gt;For the first time in many years, I have found the energy to resume my other passion: exploring the world through the viewfinder of a camera. I might share some of my work here :)
I just wanted to let you know that I am happy, doing well and looking forward for another year here.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[This is Playtomic's Anemone]]></title><description><![CDATA[We, tech companies, don’t share a lot about our internals. But I think we should share more often, especially if we are proud of what we…]]></description><link>https://sgmoratilla.com/2023-09-25-this-is-anemone/</link><guid isPermaLink="false">https://sgmoratilla.com/2023-09-25-this-is-anemone/</guid><pubDate>Mon, 25 Sep 2023 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We, tech companies, don’t share a lot about our internals. But I think we should share more often, especially if we are proud of what we have built.
So this a post about sharing (or bragging): let me introduce you to Anemone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Anemone&lt;/strong&gt; is the system that provides the data and operations to all Playtomic products. The Backend.
It comprises 60 services, more than 150 instances, 3 different types of databases, 1 kafka cluster. All services share a common 1 core library (to rule them all) that provides
common functionality as converters or security.&lt;/p&gt;
&lt;p&gt;We wanted to build it so it is resilient, scalable, and easy to change. Sea Anemones inspired the name. They are capable of splitting completely on their own and keep living. Resilient beings. &lt;strong&gt;It’s a bit nerdy but beautiful&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I wondered how our anemone would look. So I asked &lt;a href=&quot;https://openai.com/blog/dall-e/&quot;&gt;DALL-E&lt;/a&gt; to draw it for me. And this is what it came up with. Again, a bit nerdy but beautiful.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAADH0lEQVQ4y22R2YrcRhSGBd09rdIutVSLalVpa6u38XS3Z8lkMWaMcRxIYo+D7Ux8lWUgIQvkIpiAXyD4LfKcodRusCeGj1PnVP1/1eGUNbSzgZ0NP8RgzxD0/E9g9QvsQUMAdwwMaGAjEwEagv7IhnuBsQyM2RToPZx3EgfdPN3fNQDQequ4gYsNNzbBu7lpwZhHfT1y0cjFIxcf9HHk9ZgcffgBB1lDd2/z8IFPDF4fg9wOGYjFOGQHfr7TDN33rrNG/t5jwHZEQSLsmNkT5eI2Ko9RdZbQ2TjIRz4xeG+bGrrYGgdkHObjiIJMebQDsIz4POkuvObckStfrlanL+4sH+ZiaYd0HJCDHf1LlnGGuR0zD5dkfq+9/xs+eU7OL/HhJ3S+Pvr0+++evbncPuZ8BRJu78Q94yC3QMJ22DGL8/b0s59Pn76Gt9uzu0/ufvPq2dU/1w+uz9ViVWwS0u7FFMTUjqjlQh7k0oM85rOzzeOfvnz95y//Pvr695cv3zy9+vv63tVFve6K45k6ZnLlYeUhYYDMSanlYxHkMiAqkW2u1hfrr/56+Osfl69+fPTDt9sv1sVmKjaEHxHSQXkYMRVREVIeEOYjbnmIx0KhaRNLbSeEsyVK1Z1ye1xsOVn4MYdkHqAWJNKHIlUKVsVEqYjzWEgrLTVdtGLVwbqOaOFnwklEkGo/LVJyy88KJ2ZuIt2UTWRBppp2lfEXKqsKi69aumjYoqHLNmsqH0uQMCctfFw7KfOQ8LFpLdUaNZp2JW50zHlIWcS5BdsyKZXH2fzjZblZ6OP5R09O9NE84hpPS9Rq1Go2r2lXZ1rnjU4LFVAWMR5SagHIHMQAzNXhtD6Zbz8/evDidH3/Nqw0bjSsFKoUrgvaVXzVotqUqZIxMzOzQMYMkEVS0lnFFjVuNGl13pVZrd2MxVxkWmWVorMynxakUXlT4FpFlFt2TJyMumbmMlUy0zIRPGF8omRIJdJlSMREKFoqqEVWCFipRIowZyFhlpNSMKEeZFH/ezEXsZAB4c6EOimLmAyJCLGYUBFg5qN8Ny0nISAi/wHf9nu5xyNL2wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;An Anemone made by Rembrandt, according to DALL-E&quot;
        title=&quot;An Anemone made by Rembrandt, according to DALL-E&quot;
        src=&quot;/static/92ae952663b51a0e15c6c2bed33d18dd/42a19/rembrandt-anemone.png&quot;
        srcset=&quot;/static/92ae952663b51a0e15c6c2bed33d18dd/0eb09/rembrandt-anemone.png 500w,
/static/92ae952663b51a0e15c6c2bed33d18dd/1263b/rembrandt-anemone.png 1000w,
/static/92ae952663b51a0e15c6c2bed33d18dd/42a19/rembrandt-anemone.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to add a listener port to Undertow in Spring Boot]]></title><description><![CDATA[Super short post today. I couldn’t believe none has tried this before. We wanted to add a management port to separate traffic and…]]></description><link>https://sgmoratilla.com/2023-03-29-how-to-add-a-listener-port-undertow/</link><guid isPermaLink="false">https://sgmoratilla.com/2023-03-29-how-to-add-a-listener-port-undertow/</guid><pubDate>Wed, 29 Mar 2023 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Super short post today. I couldn’t believe none has tried this before. We wanted to add a management port to separate traffic and healthchecks ports. But during the migration, we wanted to have both ports listening to healthchecks so we make everything back-compatible.&lt;/p&gt;
&lt;p&gt;Once you active the Spring Boot management port, /actuator endpoints are moved there. This is the easiest way to add an extra port to the Undertow server.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;@ConditionalOnProperty(name = “server.extra-port”)
@Configuration
@Slf4j
public class UndertowConfiguration implements WebServerFactoryCustomizer&lt;UndertowServletWebServerFactory&gt; {&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;@Value(&quot;${server.extra-port:-1}&quot;)
private int httpPort;

@Override
public void customize(@Nonnull UndertowServletWebServerFactory factory) {
    factory.addBuilderCustomizers(builder -&gt; {
        if (httpPort &gt; 0) {
            log.info(&quot;Using extra port {} for the traffic Undertow&quot;, httpPort);
            builder.addHttpListener(httpPort, &quot;0.0.0.0&quot;);
        }
    });
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;
More info: https://docs.spring.io/spring-boot/docs/2.1.10.RELEASE/reference/html/howto-embedded-web-servers.html#howto-configure-webserver

[^1]: Matt Paul Catalano https://unsplash.com/photos/0QEG_xOoY7Y&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[6 years at Playtomic.io]]></title><description><![CDATA[I started this post 6 months ago but never finished it. Today, I make 6 years in Playtomic. Everything goes so fast that it feels like…]]></description><link>https://sgmoratilla.com/2023-02-13-6-years-at-playtomic/</link><guid isPermaLink="false">https://sgmoratilla.com/2023-02-13-6-years-at-playtomic/</guid><pubDate>Mon, 13 Feb 2023 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I started this post 6 months ago but never finished it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Today, I make 6 years in Playtomic&lt;/strong&gt;. Everything goes so fast that it feels like 6 decades. It goes so fast that &lt;a href=&quot;/2019-05-27-2-years-at-playtomic/&quot;&gt;it took me 4 more years to gather the energy to write the sequel&lt;/a&gt;. Honestly, I didn’t think I would still be here. Neither that this blog would still be alive too.&lt;/p&gt;
&lt;p&gt;It feels like it was 6 decades, but at the same time feels like I am at the same vital point. Crypto has exploded and decayed several times during these 4 years. I have seen NFTs artists rise and fall. AI Art and language models are shaking our world up.&lt;/p&gt;
&lt;p&gt;And we are still here, trying to revolutionize the world of racket sports. Still trying. Feels so much more mundane compared to that previous paragraph.&lt;/p&gt;
&lt;p&gt;Don’t get me wrong, I don’t want to diminish our progress. We have more than 800.000 players and 6.000 clubs. We are present in more than 30 countries. The growth has been insane, as our current mental is. We have suffered layoffs, as most tech companies based on venture capital. And several reorganizations.&lt;/p&gt;
&lt;p&gt;I have changed and expanded my role so many times that I am trying to remember what I was supposed to do at the beginning. I have acted as Head of the Backend of a team of 22 people. During some period, I had to balance that role with the Engineer Manager (EM) role of a group of 5 developers. I had suffered outages while I was 2500 km away from home. I have fixed and optimized countless amount of problems in our systems. I have discussed functionality and roadmap worth for a lifetime.&lt;/p&gt;
&lt;p&gt;Luckily, we have help now and can count on two of the best EMs I know. My responsibilities are mainly technical again, and it is a relief. Executing and managing for long periods is insane at this pace.&lt;/p&gt;
&lt;p&gt;Where will I be in a year? Honestly, I don’t know. But I hope in a place where I can keep writing on this blog O:)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to migrate a cluster in production]]></title><description><![CDATA[One of the most complex operations in tech is upgrades or migrations without disrupting the service.
I always compare it to upgrading a…]]></description><link>https://sgmoratilla.com/2023-02-10-how-to-migrate-a-cluster-in-production/</link><guid isPermaLink="false">https://sgmoratilla.com/2023-02-10-how-to-migrate-a-cluster-in-production/</guid><pubDate>Fri, 10 Feb 2023 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the most complex operations in tech is upgrades or migrations without disrupting the service.
I always compare it to upgrading a plane when it is flying.&lt;/p&gt;
&lt;p&gt;Last year we faced a problem: Docker Swarm (Community) seems stalled. Besides, autoscaling and other tools that other container clusters provide out of the box are very manual.&lt;/p&gt;
&lt;p&gt;Our setup at that moment was:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Docker Swarm cluster&lt;/li&gt;
&lt;li&gt;~50 Spring Boot services running on that cluster&lt;/li&gt;
&lt;li&gt;Deployed on AWS EC2&lt;/li&gt;
&lt;li&gt;API access via Application Load Balancers&lt;/li&gt;
&lt;li&gt;Every service was mapped to a Target Group&lt;/li&gt;
&lt;li&gt;No discovery service: We use hostnames to find the services internally.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are many ways of deploying a microservice architecture. This is the simplest we found, and it worked like a solid rock for 5 years. This is what it looks like:
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1340px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ea28089dedfaf311e55d76d3c10363e5/d3b48/cluster-migration-initial-setup.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABTUlEQVQoz5WRy27bMBBF/f9/FDRBumnRTRLbieHKCdoURSXZVkpJfIkvnYJM1UU2QS8wwIAYnrm8XAEka4lSEo3hfxRTxEfPFBxpTuVslbwvsDJgDFFrlDFM04TWGikV3nuc8xhrMcaijSGEQG9H6qHlp/hVwAWYAXOM/7a6lxeGYUAqxXZ7z25fcTye+PT5CxcfLrm8uubq+iN109Crnt9aME4S6y1pnl+B0Vpc0xR3QSmY5+LgYbfj6dszQvRobRB9jxCCfhhwzmGC5SQ7lNO04wntzF+H81wqaxKC8/nMuetYbzbsqyfqumF7/8DN3Zr1Zsvt3ZqmbZFG0QxHBisLLGe6StNEzjErP704zH1KdF1H3bSMUvL9+QdVdeBweGT/tSqxLM5yjm7JMF8M40gGv83zPeXfzaAMDim8AhdnGbjAlgjeq2V2Ue7/AFAyaSr+YRQ5AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Playtomic&amp;#39;s Architecture before 2023&quot;
        title=&quot;Playtomic&amp;#39;s Architecture before 2023&quot;
        src=&quot;/static/ea28089dedfaf311e55d76d3c10363e5/d3b48/cluster-migration-initial-setup.png&quot;
        srcset=&quot;/static/ea28089dedfaf311e55d76d3c10363e5/0eb09/cluster-migration-initial-setup.png 500w,
/static/ea28089dedfaf311e55d76d3c10363e5/1263b/cluster-migration-initial-setup.png 1000w,
/static/ea28089dedfaf311e55d76d3c10363e5/d3b48/cluster-migration-initial-setup.png 1340w&quot;
        sizes=&quot;(max-width: 1340px) 100vw, 1340px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As you can see, Internet traffic comes from a Load Balancer. Traffic between services is internal. As part of the internal traffic, there is an event platform (Kafka).&lt;/p&gt;
&lt;p&gt;Now that I think about it twice, it probably looked more like this:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1272px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9bf16958a950a517c7022968e159c885/9c259/cluster-migration-honest-setup.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABhElEQVQoz42SW2/TQBBG/f+fEVIFlNBGiAekqhcqeIMG6IWLSJQ0RW2dBprQOIkd2+usvbv2QbtqTNUXOtJodqRPZ+fmAZR5jo5jTJbZlKqqnP/P7mts7lVaO5g1s1xipKwFWmuMMS4qZV2RF4WLK5hUOYlM0aV2uWeEoDLmH+QW7vsDLv0B4z83nF9c8v6gRevTZw6PTzg++UJRFE6X5CmTZFp/4IBlUbCKWgiUlLQ7HTrdU66Gvxxg7ckzGhtNmi9f8fzFJlEUkQnB0cVXdnrv8OdDlFF4dn4WVASBg+osIw1DfrTbdHt9BldDPhx85NHjNZ6uN2hsbDr4zWTCIoxo9Q9pfntNyz9iJkI8u4iqLOuWTZq6CvtnZ/R/nnM9GtPtnbK1vcvO3j7bu2/Y239LEAROf70Y8/13h3kW3bacppRK1VtyM6wqhBCMRmMWccxSSqbTKbPZnDCMiJOkniEVrtWVebY6Nz8p0Uni2n+o3T2v1dtzSVm6W7xb6UP8vtZW+Bf78qr2/IUUbQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Playtomic&amp;#39;s Architecture prior 2023 - Mess&quot;
        title=&quot;Playtomic&amp;#39;s Architecture prior 2023 - Mess&quot;
        src=&quot;/static/9bf16958a950a517c7022968e159c885/9c259/cluster-migration-honest-setup.png&quot;
        srcset=&quot;/static/9bf16958a950a517c7022968e159c885/0eb09/cluster-migration-honest-setup.png 500w,
/static/9bf16958a950a517c7022968e159c885/1263b/cluster-migration-honest-setup.png 1000w,
/static/9bf16958a950a517c7022968e159c885/9c259/cluster-migration-honest-setup.png 1272w&quot;
        sizes=&quot;(max-width: 1272px) 100vw, 1272px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For our new cluster, we wanted something standard and, preferably, managed. So we decided to move out to EKS (the managed Kubernetes service on AWS). The problem is not writing the new descriptors to deploy on Kubernetes. The hard part is how to migrate the platform without stopping the service.&lt;/p&gt;
&lt;p&gt;We could define a maintenance window in the early morning for a few hours, do the switch, and continue. But that process is stressful and very error-prone. What happens if we find an error? Rollback? Go forward?
So we decided to do it progressively. It would be longer but safer. And we would learn a lot.&lt;/p&gt;
&lt;p&gt;In summary, we saw two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up the new architecture in the new cluster and do the switch: faster but harder to do without errors and stopping the service.&lt;/li&gt;
&lt;li&gt;Migrate service step by step: slower but safer. No need to stop the service.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;How to connect two clusters&lt;/h1&gt;
&lt;p&gt;There are two common points for all the services in our architecture: the gateway (the load balancer in our case) and the data stores (sql, mongodb, kafka, …).&lt;/p&gt;
&lt;p&gt;If we start a service in the cluster, we need to consider that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It might receive traffic from the Load Balancer.&lt;/li&gt;
&lt;li&gt;It might receive events from Kafka.&lt;/li&gt;
&lt;li&gt;It might call other services that… might not be in the same cluster.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you have a discovery service, you could set up both clusters in reachable networks and register the services in the same discovery service. But we do not have such a piece.&lt;/p&gt;
&lt;p&gt;The only way is to pass all the http traffic through the load balancer and split the traffic into both clusters. The load balancer will be our on/off switch.
Drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it is a bit slower compared to the internal traffic (although nothing critical on our setup)&lt;/li&gt;
&lt;li&gt;more expensive, as AWS charges for the traffic handled by the load balancer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So this is the setup during the migration:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1400px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/28202c99300cdadafd555e7f89463bf2/3643c/cluster-migration-setup.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAACKUlEQVQ4y3WTi26jMBRE+f//WnWVNGm7adpEVZ4kIQkYbLAxfjEr3ySoW2mNLBsQh7l3xgkAeKXgpYTjHH0I+D6MtzQ9PML9cr2nd50zyCUDUyWst/QsiYAIi6O3Fk1ZouICWXbGfLlAVp5RVAyv7zOMX6YYTSe0r1UDJkvsihRMVdC2uwOdI4UE9B62rml/PGVYfq0ghECnO6TpAZvNFqvVGvv0AOccVKegO42+75HVF1KZhK4j0APo7mpPpwyfyy9YayGlJMh2t8N6s8U+TeGdR+c6uOAR+oBrU8B5hySqC9ZS76j8tiVgejjg19NvVIqDC47PxRJ/Zu+Yfyzw/vEJxhlKxXFkGQrFoMztuyQCokqb5wTzWg8lP43GqBpOJnTBwPQWtncI6NFZg1rVONdXmsqoO1CpoWRy/A48HI8YjSdQrSI3C8FQNhUqycHqEq3REFKAtwJCN5QEAgath6h8L/ma51itt7jwHAVneH2bYfryRuts/gEubrBtvgfXgkqO5iSxf+GuKirVnEMqRSVvdnty2HSGenq+XClOpyyDNTGfBj7cqjvyjCpJIsQ3zZBDHWNiDAHniyXS6xEFKzAaP+N5MiVjXl7fkBcFqpbjUl2hrYbQ9c3lx0khsJRDP6uKY73dohQljDXIqwI5Zyh4iUuVo24b1F1DhsTIDCdl6J2UCMbclPY9/jdi5h4jKjLOUB6Nexy9Hx8/7uP6c6LHP+9+/jya8hdAyeeQxrGz2QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Playtomic&amp;#39;s Architecture during the migration&quot;
        title=&quot;Playtomic&amp;#39;s Architecture during the migration&quot;
        src=&quot;/static/28202c99300cdadafd555e7f89463bf2/3643c/cluster-migration-setup.png&quot;
        srcset=&quot;/static/28202c99300cdadafd555e7f89463bf2/0eb09/cluster-migration-setup.png 500w,
/static/28202c99300cdadafd555e7f89463bf2/1263b/cluster-migration-setup.png 1000w,
/static/28202c99300cdadafd555e7f89463bf2/3643c/cluster-migration-setup.png 1400w&quot;
        sizes=&quot;(max-width: 1400px) 100vw, 1400px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;One target group per service and cluster. The load balancer splits the traffic into those target groups (the dotted lines). Services call each other going through the load balancer (the green lines). That allows us to control what services are deployed on the new cluster.&lt;/p&gt;
&lt;p&gt;When every service is running on the new cluster, then we can stop the traffic to the old one:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1480px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d8248234d1b258e7a250d2a8763d55ce/163ec/cluster-migration-finishing.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABYlAAAWJQFJUiTwAAAB3klEQVQoz22Ti3KbMBBF/f8/1jZNpx3XD8CxjZPGdhrxkEACvbidXcCeSbMzC4gRh713VwsAiM4htC18VSH2PWIcEEKAtRZDjBiGcU0ZQ4D3nj5D73sIXeJvI3gPxYIuBKMPeZNSKKsaoiiwP+bQWqO3Fkm6w/L3Cuttws/eeTSmxZ/yjMrUsMGNQALFruMFVzI911LikD+joqpj5B9cLle8vp7xLgTvMa6DDx4hRlzUG1zwWAwkxRjMEbTme1VLLFdrSKWglMLuaY9tkiLbPSHJMv5J5zo0XYvO96g7iTjECaj1mMbcgKIo8fXhEapp2LM8PyFNMwbmpxP7OUsmH++SPwD9DVjgy7cHSCnxWRBQmQat02j6lqXfPez7/yQT8OevJcslb8uyQl1LlFWFoiwRfIDUCrl44S6T3HuFUyMYSH7GyN4dTy/cHAqCf3/8wblab+B94KrO9ZWbo61GiGGskCDDBLRKQQiBy/UNh+OJu0weJmmG9WbL9/3+AGMMrLcMpKaIdvRxMcukSgnumobBJIvmjaTSgFOHCbjZpkizHc+nsQb5+zMqI7lasoaBg/c83AyeToFzDlKq26n4LGhsZKd4sEnu6OF0ZAg0w+Z3c9D6Y5JHNIuBM9yO3j+klZ1htuGIZQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Playtomic&amp;#39;s Architecture finishing the migration&quot;
        title=&quot;Playtomic&amp;#39;s Architecture finishing the migration&quot;
        src=&quot;/static/d8248234d1b258e7a250d2a8763d55ce/163ec/cluster-migration-finishing.png&quot;
        srcset=&quot;/static/d8248234d1b258e7a250d2a8763d55ce/0eb09/cluster-migration-finishing.png 500w,
/static/d8248234d1b258e7a250d2a8763d55ce/1263b/cluster-migration-finishing.png 1000w,
/static/d8248234d1b258e7a250d2a8763d55ce/163ec/cluster-migration-finishing.png 1480w&quot;
        sizes=&quot;(max-width: 1480px) 100vw, 1480px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Again, as the traffic is controlled on the load balancer, we can stop services on our previous clusters while removing access from the load balancer.&lt;/p&gt;
&lt;p&gt;And finally, we could delete all the components related to our previous cluster:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1492px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/08bbcde51f3e70abe3dbfe67d6c8cff0/37cfc/cluster-migration-finished.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABYlAAAWJQFJUiTwAAABiklEQVQoz3VTCU7DMBDs/x+GuNpyChBCHAWR2mnS2I5ve9C6CS2lHWljJ4pHM7vjSc4ZhNj3iEoBw/suxn8OrbtFmNAjWYuoNbL3CH0PoRSEEGC8hjHm4OF90nE/GdXlGMtHJyWMNlguGR6fntGu1ziGvOPmV2FOqRCOIKWEddfh9e0DTdsixohea1hrobWGEBI5Zfjo0fQtuFqV/ZaQerdHyOsaF9MZBCk2BpezOS6nc1xd35ZyzsM4g++2QmcEjLcby4VQa4Su29SglgjPL6allwSlVCkpZVkJPnhwuYINFpVgRWUhTMYgh1BqJFwyhpPTM8jhMOc1vqsKjHEwzmGNhXYGi/oLayPQO42U01bhvuVV0+L+4RE9xSlGzOZXmM7I8g1ubu/QdR1MsEVhrRq4Pz2kyAxTCmRNCFRLhrf3RRkOEaaU/k05plRsUoUUtjkshENsvJSlT2T5+eUVq6b5m7cDwd+NzibYzv32kUJOoMl+LD4hpToa7IM3ZdwQYQn4kduwH+Rj+AHq4KbJM4y86gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Playtomic&amp;#39;s Architecture finishing the migration&quot;
        title=&quot;Playtomic&amp;#39;s Architecture finishing the migration&quot;
        src=&quot;/static/08bbcde51f3e70abe3dbfe67d6c8cff0/37cfc/cluster-migration-finished.png&quot;
        srcset=&quot;/static/08bbcde51f3e70abe3dbfe67d6c8cff0/0eb09/cluster-migration-finished.png 500w,
/static/08bbcde51f3e70abe3dbfe67d6c8cff0/1263b/cluster-migration-finished.png 1000w,
/static/08bbcde51f3e70abe3dbfe67d6c8cff0/37cfc/cluster-migration-finished.png 1492w&quot;
        sizes=&quot;(max-width: 1492px) 100vw, 1492px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That is, we removed the mapping on the load balancer. That allowed us to delete the previous cluster completely. On the new cluster, we restored the internal traffic. Profit!&lt;/p&gt;
&lt;h1&gt;Conclusions&lt;/h1&gt;
&lt;p&gt;The plan was sound and easy to follow.&lt;/p&gt;
&lt;p&gt;The only drawback that I see is the number of manual steps we needed to perform:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Move the internal traffic to the load balancer (one change in every service).&lt;/li&gt;
&lt;li&gt;Deploy the service on the new cluster.&lt;/li&gt;
&lt;li&gt;Create new target groups and map them on the load balancer.&lt;/li&gt;
&lt;li&gt;Split the traffic to both target groups for every route mapped.&lt;/li&gt;
&lt;li&gt;Check that the service behaves as expected.&lt;/li&gt;
&lt;li&gt;Remove the previous load balancer.&lt;/li&gt;
&lt;li&gt;Move the traffic back from the load balancer to the cluster.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some of the steps were mostly automated on our CI+CD pipeline (deployments, creation of the target groups, etc.), but some required us to work with the AWS CLI / Web console.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Denormalizing the core of Playtomic]]></title><description><![CDATA[Yes, another post about optimization and MongoDB. I started this post in July, and I couldn’t find the time to finish this. I was optimizing…]]></description><link>https://sgmoratilla.com/2022-09-25-denormalize-to-optimize/</link><guid isPermaLink="false">https://sgmoratilla.com/2022-09-25-denormalize-to-optimize/</guid><pubDate>Sun, 25 Sep 2022 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yes, another post about optimization and MongoDB. I started this post in July, and I couldn’t find the time to finish this. I was optimizing something O:)&lt;/p&gt;
&lt;p&gt;Our core product at Playtomic is what we call Matches.
A match is an entity that represents when, where, and who plays a game.
That is the date and time, the location, and the players.&lt;/p&gt;
&lt;p&gt;That data structure is stored in a MongoDB collection. At the moment I am writing this,
that collection stores 27M documents and takes 100GB of data, 32GB of disk, and 14GB of indexes. It’s one of the biggest collections that I have managed in a transactional system consumed online by thousands of users at the same time (we have 800k active users).&lt;/p&gt;
&lt;p&gt;At Playtomic, there are two main types of users: players and clubs.
Broadly speaking, players want to find people and book a court.
Clubs want to manage their courts and occupancy.&lt;/p&gt;
&lt;p&gt;Focusing on matches again, that collection is both consumed by players and clubs in several ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Players looking for matches to join.&lt;/li&gt;
&lt;li&gt;Players checking their history.&lt;/li&gt;
&lt;li&gt;Players uploading results.&lt;/li&gt;
&lt;li&gt;Clubs checking their current schedule.&lt;/li&gt;
&lt;li&gt;Clubs checking their history.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a screenshot of what you can find on Playtomic’s application:
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1125px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/168b742b5d22f851e319624b5419960d/b1bc2/denormalize-mongodb-app.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 216.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAArCAYAAAB4pah1AAAACXBIWXMAAAsTAAALEwEAmpwYAAAHWUlEQVRIx62W+ZNU1RXH+29K8kvyS2IVlcoPilUSqzAmgFFLBDQooE4xrDJKCALCKIODgsiiKSpuDAJhWAYVx2EdBnCAWXp6en/9Xr/37tuXT+re7ukZlmhSya369l26z+lzz/K9JxPHMZZlYdu2mtM05b8ZSZKg67qC1JXxfZ+hoSEGBwcZHR1VhzOH/IOZmDmCIKBcLjM2NkZ/fz9CCDL8D+NBt8lIk7Wa/kBL5Hf9F67w/aWrDF7/gWs3htXZvcpmyigLoyj6t74z6iYVraZmiZ8amXuFS5Uqk4UiulF/oID0m2nZCMfBsm21DsOoZWnGsoU6lPjh1ggXLg/y9XcX1PXkmRSyhSBp3sBxXQqlMjXdoFLVFDzPn7ZQWiKFHMdFCEdZZgsH3w/UD+W+qtWUZfe6Zcp3MjOiJjJpmjA6oXHiTD+/e3w+f1z4Er9+eC4LFq/g4OEv+O2ceTz53FLmzH+en/3mEVa98RabO7t5aPYT6uyxeQuZ/YdnFX6/YBEZGZCJyQIDlwd5dmkb7R1vsWDRctZs3MbxU33Me34ZS9vW89q6v/LEMy/S/eEhDh7+nD8vWcGyVR0sa9/Ayys38MIra3jh1bVkwiiimQn/l5GR0XIcE9e11Ox7Nq5rYlv6NGyjgeZe2AaOqLcghImwhSrfjCt0XFGjblQx61U0rYiul0lj98FIPELfxnXqeK6ERRhohMEdHGGQ8T2DcqXC95eGyOVLjIznGM8VfvRalu2QL1Yolqvk8jVMZwzLO4ErLJnYAT3HvmJNx5sqsR3HoVrTyRdL5AtFlejFckXlXS5fUEqyuTx1c6pqmqmUgCM0Mp5roBs1ihUNP0rl+X2MI+s3ThJUGqaBUnJfpaYRjq2Rscyq2n/1j4O88/oSquPX1f7KSIkzNyuUDKcl47o231wrcO3miFJanMzy2YHdDPSdJI0DHFEj4zkGtmOwvn0xXesWcfHYAbRalUdmP8afFjxF9969LYU3z/VwZNcOzuzvwqqMcfKLT+hc9xI71i+nmBshDGwyUmsceXz6+Res2dLJtxcGlPCmDatY/NQc+r851VJYLV3m9LmF7Ny+nOxEgcmxYQ6/v40jH+8h8ASuo5OxrcaVy1WdvvP9qobVvqIxcPHyXb6KErhxe4ibt4eRbvbDWPFkuWooFwjpQ/mRJKESlGUonS83MhBRFLcIdXrMDJj8t6ARYiKVz0phmkb/UVlZIuHEtxFHzzXQ0zeFmONfe1Q1TVZKDdcV6oqS5/LFsqIvyYGS1jzfx7SESotiNeXlTR5t2zzad/isfsdnzbs+qzoD2rbajOeaCj1PUKnWlMJSuYKQ3Og4SrHkQTnLq5Y0WLbZZ+V2n9WdAet2BqzvClj7bsjKtwXjE1UyQlj4bo04qBMHJnFoEng6oWcQ+oZaB2qto2kax86W6TldpudUiS97p3GiTz4bJhnHsfEcrSU4NcszV2h4Tk2t5Rx4NYhqkMiomtNILYglYZiNK0srJ/KydkvcHh3nzmj2R4NjWg6lSo2qplOumFhuFtvrxRUmmSQSXBm8ykcfH1Y+lO+HJAb5QEkCkP5zPY+ZpSsDJYlEfq/V6ni+gR+O4kr6IvU4c+4su/bsI0wgCEP1MFlSURCh6YYS/smeJ22yjW1WGvsgIDd8g6TJNOVCnqEL3xHHd+do0Qww3ahZOQljuQksIVQWKLZp1HLA37dvYstzc+k/+hmOJ9iz6mm+XDGXbz//oGXC8HieV7ceonNXJyQOvT2f8Zdn5rJz1xb1FEiiyThCJ/IFR7as5VjHck4f2oth6hxeO5+ji2bx1YebW9YdPLCPh375C9qWzieXvcVA39dsWt1G15bVGFoR3zPJiCY5DPWf5/hHuzEqJbX//vQ/2f+31eRyky2FdUNj3553+eST/VQ0XZ2dOneRq9cG1bpFDrKsvDCmaljEU50XoNvufb6XUZ8OUIotrGYGxI2gKLaJw6neoknvDbZJp9BqOVJcH4Sb4Lgpjpc2Z9nzhAirNqUwaCmcEk6ShoI4SYnihg0lLeGNbp8N7/lq7uj2m/uATe8LJguSHBxdmT2WzXNrZFxBr1sIx0QInThysG3prxDThte2ekrh67tC3tgdsvGDkI7uiPbtToMcHLtGFFgEvlCPtpzjyOZO1uHidZdLN1wGrjkMDnv0nndZuM5i6UaLDV0GG7pMhdd3Wrz5Xo28tFC1InYZV5Rx7AppWOHKUJFZTzv86kmPR1/wmL3EY9YzHg8v9nhkscfPH3f49HgevVZgMl8kny9RLFYQQrYijtFsK+QjY2NbNW6NlFm9w+LFDpu2rRYrt9m8stmm/W1bEenyTXVGs5JpPJLIh9QmTQq4oi4fep2qVuW7i4OMjE0wfGec2yOjgK/eiyTxVdDSJCCOfZViMpC5/CQTkyXGshUM+w5191iDbdIkwjRNCqXKPd0CKtIK6d3zg3kiJYqCu5v2qfyTI5vNcvLkSfr6+jh79qyCXPf29nLr9u0ZLfHd/eG/ALgFT50X0lg7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Playtomic app&quot;
        title=&quot;Playtomic app&quot;
        src=&quot;/static/168b742b5d22f851e319624b5419960d/b1bc2/denormalize-mongodb-app.png&quot;
        srcset=&quot;/static/168b742b5d22f851e319624b5419960d/0eb09/denormalize-mongodb-app.png 500w,
/static/168b742b5d22f851e319624b5419960d/1263b/denormalize-mongodb-app.png 1000w,
/static/168b742b5d22f851e319624b5419960d/b1bc2/denormalize-mongodb-app.png 1125w&quot;
        sizes=&quot;(max-width: 1125px) 100vw, 1125px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As you can imagine, there are many access patterns to that collection.
For example, I can search matches that I can join filtered by&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;club&lt;/li&gt;
&lt;li&gt;location (coordinates)&lt;/li&gt;
&lt;li&gt;my levels&lt;/li&gt;
&lt;li&gt;number of players&lt;/li&gt;
&lt;li&gt;court type (indoor vs outdoor, materials of the walls, …)&lt;/li&gt;
&lt;li&gt;gender of the players&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And if I cannot join due to some restrictions, I can request to join and wait for the approval of the rest of the players. I don’t want to bore you, but as I said, there are too many ways to access the same data.&lt;/p&gt;
&lt;p&gt;There is only one way to make those queries efficient: indexes.
But indexes come with a cost:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The more indexes you have, the slower the modifications are (insertion, update, or deletes).&lt;/li&gt;
&lt;li&gt;Those indexes must be in memory to be efficient: more indexes, more memory needed.&lt;/li&gt;
&lt;li&gt;As the collection grows, changing or creating indexes becomes a delicate matter: you must read the whole collection to create it. In a production system, that might entail problems if you are already overloaded.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our product grew fast and in a slightly chaotic way. We had many indexes that covered the most common queries, but we could not have an index for every combination of parameters we allowed in our search. And thus, this happened:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c15ab87da8f5f9c33f254af79f1f40ae/f97d7/denormalize-mongodb-0-tickets.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABj0lEQVQoz5XQS08TURjG8fk+sDAx0VBsOaVz68w5Z64t7UwpVNEERSyWS6BolE/gwvgNLQsMLmRm+ze0GEcTjS5+eZ/3PcmzOMbV1Rdml5d8ns24vv5KWZYURUFZ3FAWRSXfzHPVj/vCt/k0TqZv0d0+Sb5Fkg1I85wo2yTKhkT5kDjLCfMRYTZa3G71796zLcJsmyB/QpDvEGQjjN39MauOj5ARQkU86I1Z2rhgeeMdy733LPUvuJces6pz6qpLQ6U0ZEJddXik+tT0Jg/DZ9xP9lnRQ4y9gwnrjocjAxwVIuLnNIKnc7XoFSvxBFN2cH0Px9e/UTi+xPV9XM/DkhrjxXhC0/GwZTC3Hm0jVEZLdRA6pxE8xpIhlh9U6F93uZimDG8LX1cKQ8xwQFP1aMkUU8YIPaiU/J0pA4yXB4e02hJXhzg6wY6HmLqLrVNsFeOoaP4V/8JWEcbO7h61polw2gjbQ7iKNavNmn3HchH2z/3P2aVutTE+fPzE+OiE47M3HJ1OK87/05TD03O+A1ayidx+vjeCAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;MongoDB 0 Tickets&quot;
        title=&quot;MongoDB 0 Tickets&quot;
        src=&quot;/static/c15ab87da8f5f9c33f254af79f1f40ae/f97d7/denormalize-mongodb-0-tickets.png&quot;
        srcset=&quot;/static/c15ab87da8f5f9c33f254af79f1f40ae/0eb09/denormalize-mongodb-0-tickets.png 500w,
/static/c15ab87da8f5f9c33f254af79f1f40ae/1263b/denormalize-mongodb-0-tickets.png 1000w,
/static/c15ab87da8f5f9c33f254af79f1f40ae/f97d7/denormalize-mongodb-0-tickets.png 2000w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In summary: we were dead. MongoDB had not enough resources to reply.&lt;/p&gt;
&lt;p&gt;When we received many queries at the same that were not fully covered by our indexes… MongoDB suffered. Remind that our collection was 32GB on disk. &lt;a href=&quot;/2022-02-14-mongodb-performance&quot;&gt;If you want to read more about MongoDB Performance, I wrote a post about it in February&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We needed a fast solution that didn’t require completely reworking the API we were exposing. We realized something: the most complex patterns were always to find public matches in the near future.
A public match means that everyone can see and search for those matches. Private matches are not searchable.&lt;/p&gt;
&lt;p&gt;This was the idea: to denormalize that data into a new collection. The source of truth would still be the original collection. We would only have the data that we need to feed the indexes, the rest of the data is not there. We apologized to all the teachers that taught us the &lt;a href=&quot;https://en.wikipedia.org/wiki/Third_normal_form&quot;&gt;3rd normal form at college&lt;/a&gt; and then we got our hands dirty.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1086px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 83.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAABYlAAAWJQFJUiTwAAACXUlEQVQ4y3VUi26jMBDk/7/tdJV6rVKlSZo2TcLDBAx+v+a0pqTkcl1pZbTg8ezOmEKbCG0CnI+g8D7COqpFxJRyLf2wzhFTzDXKgl0sBuEhVcDmXeZnYyMuvQMf/R3A/HziJV6qDVbVGk/n1fV90Q8OMaYM8LwewTqb2RJYP3wDzkkxGoFOcVRjgyM/ox4ZGtHCeouCmH0cFarGoO0dTpXB+1FlwCXQEpDAfAw3jKVVUE5NDK2NNzMJMYFGcR/TZq45wj+AxE47g6K52Fw8nDRWG4Gn9ZhbVjrg4Zlj/6nQdg4Vs6DDH1cjXo8VSQHCmgG10xPDjrtceDsovO4lNnuZ1R2Fz6Mg4KxknFqWKqLkDDGFG+7aGyinUdDcaPMyfEgYpb9Tdl4vqsOgR7w2O/w5rfDWfsAFl0ELYkMsqS0CIdsczhq/n/kN4FIUUtTH6UBaKa8M503D6LMQZGpqr6wNamYhZFgYeAl42zKpTHYqPs8a+4PCy1ZkoG8tcfVn3Vo49+0EJi53KgsrMZgRhZAeUiesdwLHykwfxXRlQ0E3h1Qm8GwbM/mQwOja5Q71iF4PXy0Hg2pXYbeqAKuQ7pz3NRbhUbceFefoVI9WdmCiBdcDyqGeZhithalrJK0ALWAZQzQGKUakEK4ZQ8jeC9rhs6pRizbPspWXzIwOyFcvCAHfdZCbLeR2iyAlbF3DtS0cY1M2zVTrOoiHB5SPv2CRsGN7bNkewkhY7yZRonN588zElmVe8ePFAxp5yT8EYkVJQL3i09XLH3qfQYnpDPa/H8MySeU5ydSzL/8CTh8ribq8O9cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Schema of the solution&quot;
        title=&quot;Schema of the solution&quot;
        src=&quot;/static/7c3f0a53b26902d1339af4eded95e21f/a1954/denormalize-mongodb-schema.png&quot;
        srcset=&quot;/static/7c3f0a53b26902d1339af4eded95e21f/0eb09/denormalize-mongodb-schema.png 500w,
/static/7c3f0a53b26902d1339af4eded95e21f/1263b/denormalize-mongodb-schema.png 1000w,
/static/7c3f0a53b26902d1339af4eded95e21f/a1954/denormalize-mongodb-schema.png 1086w&quot;
        sizes=&quot;(max-width: 1086px) 100vw, 1086px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;
Luckily all our platform was already event-driven, so we could update that denormalized collection via Kafka events. Data would expire &lt;a href=&quot;https://mongodb.com/docs/manual/core/index-ttl/&quot;&gt;using TTL indexes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our clubs generally allow opening public matches within 2-3 weeks. That means that we keep between 14 and 21 days of data. We allow players to upload their public matches, but their number is still below the number of matches that clubs manage.&lt;/p&gt;
&lt;p&gt;To be sure that we are always returning the most recent data, &lt;a href=&quot;https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/&quot;&gt;we use the $lookup command&lt;/a&gt; to read at the final step the last version of the match in the original collection.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1486px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c16f1f962f36d654d14fcda2909de069/4d326/denormalize-mongodb-before.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAiElEQVQI122NiwrCMAxF9/9/OacW9+xjbdN1yR0NKgheEvI4l6TzOWJwIxxFQFoImkQEzIx/+jAW+dnVeqKz3qM3T6zOKthjxLpZlFJ0dj5gs05rTEl7erNaK5Z1+/rb0a4Z+ttdQfva4DjNyEQ4mWFeo2bYI3zYMc0LUs7KiAjDwygnKjiOigsyZ+jatxDDvAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Metrics of matches collection&quot;
        title=&quot;Metrics of matches collection&quot;
        src=&quot;/static/c16f1f962f36d654d14fcda2909de069/4d326/denormalize-mongodb-before.png&quot;
        srcset=&quot;/static/c16f1f962f36d654d14fcda2909de069/0eb09/denormalize-mongodb-before.png 500w,
/static/c16f1f962f36d654d14fcda2909de069/1263b/denormalize-mongodb-before.png 1000w,
/static/c16f1f962f36d654d14fcda2909de069/4d326/denormalize-mongodb-before.png 1486w&quot;
        sizes=&quot;(max-width: 1486px) 100vw, 1486px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1484px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8823f3c43f7284989fc8731ea389b4d3/ed887/denormalize-mongodb-after.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAsUlEQVQY02WPiw6CMAxF+f9PNOILEXlsA/ZiG+Oa1ogxNjlpc9vebsXkLEaroazG5AznEONOjOlNSj/130xKWEJAcVUtzqIBZeLQVfBhAcWGDTlnJqWENWesK7HiE9v2nSHzoiwvkHLkpnceTdNiEJIHidu9RlU/0AsJqUbudYPYDWetcTxdUDdPzNqg6IWAdY6fa6zFbAxfWpbAmhonXqJvkea8Z53qwDsOQir2CCHiBUodNaruFF+rAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Metrics of future matches collection&quot;
        title=&quot;Metrics of future matches collection&quot;
        src=&quot;/static/8823f3c43f7284989fc8731ea389b4d3/ed887/denormalize-mongodb-after.png&quot;
        srcset=&quot;/static/8823f3c43f7284989fc8731ea389b4d3/0eb09/denormalize-mongodb-after.png 500w,
/static/8823f3c43f7284989fc8731ea389b4d3/1263b/denormalize-mongodb-after.png 1000w,
/static/8823f3c43f7284989fc8731ea389b4d3/ed887/denormalize-mongodb-after.png 1484w&quot;
        sizes=&quot;(max-width: 1484px) 100vw, 1484px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;New collection: 7.7k documents, 5.30MB of data (!!), and 2.25MB on indexes.&lt;/p&gt;
&lt;p&gt;I dont’t keep the screenshot of IOPS and Tickets Available after we made the change, or the reduced latency we experimented with in our API, but the improvement was huge.&lt;/p&gt;
&lt;p&gt;Since then, this is how our Tickets Available chart looks:
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/852942ccc722e6063aba12351d28ecbe/0e092/denormalize-mongodb-0-tickets-after.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 48.800000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAABTklEQVQoz52QSU/CQBiGey3lbGI7w8GTYU1oC2UXFzhA05ZlqhCNUUGjMUbijxYL/wB8TaZlsQkaPbx55sm3zGQEbzaH9+Fh+j7FzJvjc7nEcrGdRch/jnD1OEHFYqj3BjjqXqDqnKPmuH7snu82W/v6zL0fOPP7rD6EQtNG5IxBYk+QOiNIzghR+w5R6xbi9RskZ4yI+wqJjSFeTiD17hF1biB1HyAOJxCHL4gMnv1ZdwyhbrmQM1VQvcFDtAaoegKSPYZcZSB6E3LJBsk1oRRtKEYbVD2Fkm9BKXegGCaUksPnlKIJoWa6kOMaaCoHmsyBJLeY0ALXQZK67wmd925cC+p+n1AzGfbjOmi6wENSBmja4CSBh0lTu+pGeKHxbeA/3Cxc3cqT/4W7I1TbjL8uliniQK3w805mwywjFnDlQqXVx96hGvrDv9JY+xeZPm9KnT/BVAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;MongoDB 0 Tickets After the changes&quot;
        title=&quot;MongoDB 0 Tickets After the changes&quot;
        src=&quot;/static/852942ccc722e6063aba12351d28ecbe/f97d7/denormalize-mongodb-0-tickets-after.png&quot;
        srcset=&quot;/static/852942ccc722e6063aba12351d28ecbe/0eb09/denormalize-mongodb-0-tickets-after.png 500w,
/static/852942ccc722e6063aba12351d28ecbe/1263b/denormalize-mongodb-0-tickets-after.png 1000w,
/static/852942ccc722e6063aba12351d28ecbe/f97d7/denormalize-mongodb-0-tickets-after.png 2000w,
/static/852942ccc722e6063aba12351d28ecbe/0e092/denormalize-mongodb-0-tickets-after.png 2530w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
Still not 100% perfect, but much better than we were before!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to peer two VPCs and access it using a VPN]]></title><description><![CDATA[Although I have some experience, I am not a sysadmin. So this might be obvious
for a sysadmin or someone with an AWS certification but not…]]></description><link>https://sgmoratilla.com/2022-06-21-how-to-peer-aws-and-atlas-mongodb-vpcs/</link><guid isPermaLink="false">https://sgmoratilla.com/2022-06-21-how-to-peer-aws-and-atlas-mongodb-vpcs/</guid><pubDate>Tue, 21 Jun 2022 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Although I have some experience, I am not a sysadmin. So this might be obvious
for a sysadmin or someone with an AWS certification but not for a developer O:)&lt;/p&gt;
&lt;p&gt;We had 2 VPCs that are connected through the Internet. We wanted to peer them both
so the traffic goes internally within the AWS region. Reasons?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Efficiency: it is faster to use the internal network.&lt;/li&gt;
&lt;li&gt;Security: the traffic does not leave the data center.&lt;/li&gt;
&lt;li&gt;Price: AWS charges per data transfer.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We access one of the VPCs using a VPN server (specifically an OpenVPN). The other VPC
is managed by a provider.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1636px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7ce714c54f2cc6f2a8bbd8668fc30458/d4880/two-vpc-one-vpn-initial.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABYlAAAWJQFJUiTwAAABmElEQVQoz41Ti26jMBDM//9ge82zDdCQhMTG78cyp3WA0p6u7UqWFq8Yzc6MVzercVICwhm0SqCWN2AY8LWG8W5YzHb7A6y18z2flY2hgP25NDjcz1DBzcNsDFIvkRc/cYUQYKzFerPFXQhorUFEZbZigP2txev9gl3XohId4kAgrQsgV+p7UEqlZ0Z1847D6xua9xPqpsGxqiGEBNGAVcwJnVGQTqOzqjDEAGSlPvYlAjk7A57aM96O1SdJvPfIOTNghokep76FND18SsjOIVmLqBRcXZc+GVN+4PWOxwov6w1SzrMMLEFK6cGw9xbCSdyUhPIO5D0iA6SE5BxyjIhSjoAGVVVju9uX76kM682AmaiszKuyOS7FsmI2emkxstasBJzzhWU/SjIzNCPDT7Hg2ZgK1pBifPTsovdlxBoys8nVfwCniCyzVHqi4m4e3Z7SJ2WP8+WC67WbQbm0MYhxwfB/IR7GuEx3HJGn5xesNztcuw4hxOKwUurh8m9exJJ5iBFK6XJ8CGVNPtP8Z4ZfgL8rfnp/ASmCqfGhLDsUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Initial setup: 2 VPCs connected through the Internet&quot;
        title=&quot;Initial setup: 2 VPCs connected through the Internet&quot;
        src=&quot;/static/7ce714c54f2cc6f2a8bbd8668fc30458/d4880/two-vpc-one-vpn-initial.png&quot;
        srcset=&quot;/static/7ce714c54f2cc6f2a8bbd8668fc30458/0eb09/two-vpc-one-vpn-initial.png 500w,
/static/7ce714c54f2cc6f2a8bbd8668fc30458/1263b/two-vpc-one-vpn-initial.png 1000w,
/static/7ce714c54f2cc6f2a8bbd8668fc30458/d4880/two-vpc-one-vpn-initial.png 1636w&quot;
        sizes=&quot;(max-width: 1636px) 100vw, 1636px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This post applies to any VPCs, but our setup was:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;AWS VPC with our docker cluster.&lt;/li&gt;
&lt;li&gt;Atlas VPC with a MongoDB cluster.&lt;/li&gt;
&lt;li&gt;Traffic goes through the Internet.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As we are connecting an Atlas VPC to an AWS VPC, I am going to use those names (although they both were in AWS of course).&lt;/p&gt;
&lt;p&gt;Steps to connect them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Peer the VPCs&lt;/li&gt;
&lt;li&gt;Route the traffic from one VPC to another&lt;/li&gt;
&lt;li&gt;Route your clients through the VPN&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Peering the VPCs:&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;You have to start the process from one of the VPC. In our case, we started it from Atlas. You have to provide the other vpc id, the region, and VPC CIDR. For example vpc-1aa12345, eu-central-1, 192.16.8.0.0/16&lt;/li&gt;
&lt;li&gt;You have to make sure that you have &lt;em&gt;DNS hostnames&lt;/em&gt; enabled in the target VPC. That makes the DNS of your VPC resolve internally the hostname of your cluster.&lt;/li&gt;
&lt;li&gt;Now in your target VPC under &lt;em&gt;AWS &gt; VPC &gt; Peering connections&lt;/em&gt; you will find the peering to Atlas.&lt;/li&gt;
&lt;li&gt;Now you may secure your Atlas cluster allowing access only to some target groups in your AWS VPC.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With this configuration, every host you have deployed in your AWS VPC will have access to the Atlas VPC (and vice versa). The magic happens at DNS level:
If you resolve one of the hostnames from Atlas locally, you will get a public IP. If you do it from within VPC, you will get an internal IP.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1226px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/71e00bf39a876477972d5238525b97de/98ef1/two-vpc-one-vpn-connected.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 80.60000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB7ElEQVQ4y41TDY+bMAzl//+yadO0tdeWg7a7Fii0fJOEJHz1TfGJqgfcbZYsQmy9PPvZ1v1+x9RHG89Ld8//z3kWFmyaNPW+77Hd2bjF8SzHCqoMP4IjzkWCl+sZq+iMb56LpGaLjIwNw4BXxwXnYhazhvtAB9k2WEUnMC0XS+66jhjZjgvPD4ih4+5xOP7B6exRnBjWrYaThMRwn0Y4Zlf63nj5AG6aBkIIZFmO6y1GWVaoGCNnjEGImmJt28IyzN7yGIUUBHKpcrwVMepG4z68s2/bjpLTLCNGjPNZqZxzetjqhwGqa5DVOXSnKRizEn5wge8HsF9duPsDMTClmt5dwvDRy7Ethi0Btn2PSksckxO8WwAlFb6/rLHe2lhvdtjsXvHz1wq/1xuEYQTP81GU5Wwa2DOgaDQykUG1ioJRkYELQSpKqZCkKdI0g+f7xNCcp4APhk3fIa0ZVNeCaQWuFUJWfOiP1hpKKTDGkeXvzf8U0FwYMCOCaBQ5CfKUbBQ2oEvbMwPEJ/YMaNjFSULC5EXxGOiPPeRozNgsrdb0dSkl8rxAVTEqua7rGQFzR3OIL2wENOVudjY2WxvO/gjHPRCwqGuaSSOgGXaz4/8EHEHjJKU5vIQRbUstJXTT0GPGR6G+BJyC/k/uX1+52bS0hOSJAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Initial setup: 2 VPCs connected through the Internet&quot;
        title=&quot;Initial setup: 2 VPCs connected through the Internet&quot;
        src=&quot;/static/71e00bf39a876477972d5238525b97de/98ef1/two-vpc-one-vpn-connected.png&quot;
        srcset=&quot;/static/71e00bf39a876477972d5238525b97de/0eb09/two-vpc-one-vpn-connected.png 500w,
/static/71e00bf39a876477972d5238525b97de/1263b/two-vpc-one-vpn-connected.png 1000w,
/static/71e00bf39a876477972d5238525b97de/98ef1/two-vpc-one-vpn-connected.png 1226w&quot;
        sizes=&quot;(max-width: 1226px) 100vw, 1226px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Accessing the second VPC from the VPN&lt;/h2&gt;
&lt;p&gt;Our OpenVPN server was configured to access the AWS VPC. Our clients connect to that VPN server
and then they route all the traffic to that VPC through the VPN server. If you are wondering how the OpenVPN client works, you should check your network interfaces :) You will find that the traffic to the internal IPs is routed through that interface.&lt;/p&gt;
&lt;p&gt;For example, in macOS you can find those routes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;❯ &lt;span class=&quot;token function&quot;&gt;netstat&lt;/span&gt; -rn &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; utun16
&lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;     &lt;span class=&quot;token number&quot;&gt;11.7&lt;/span&gt;.0.20          UGSc           utun16&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So… how do we make our VPN route traffic to the new peered VPC?
Just add the rule to &lt;em&gt;/etc/openvpn/server.conf&lt;/em&gt;. You must have a previous rule there O:)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# This is AWS VPC
push &quot;route 192.168.0.0 255.255.0.0&quot;

# This is Atlas VPC
push &quot;route 172.31.0.0 255.255.0.0&quot;`&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now the key. How do we make our clients resolve the hostnames from the peered VPC? &lt;em&gt;Just use the internal DNS from the VPC&lt;/em&gt;. This is where the hostnames are registered.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# Add AWS VPC DNS server so we can use internal mappings
push &quot;dhcp-option DNS 192.168.0.2&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This overwrites our local &lt;em&gt;/etc/resolv.conf&lt;/em&gt; to use that DNS server. If you want to add more DNS servers just in case the client is not able to restore back your configuration, you can add a fallback rule (for example, use the Google DNSs or any other public server)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;push &quot;dhcp-option DNS 192.168.0.2&quot;
push &quot;dhcp-option DNS 8.8.8.8&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Disclaimer:
Those are not our real IPs. You should also check what’s your internal DNS, don’t assume it’s the IP that ends in &lt;em&gt;2&lt;/em&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Dockerized Multi-arch Github Actions Runner]]></title><description><![CDATA[Do you want to run the Github Action runner as a container? Do you want to run it on your Docker or Kubernetes cluster and scale the number…]]></description><link>https://sgmoratilla.com/2022-06-07-docker-multiarch-github-actions-runner/</link><guid isPermaLink="false">https://sgmoratilla.com/2022-06-07-docker-multiarch-github-actions-runner/</guid><pubDate>Tue, 07 Jun 2022 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Do you want to run the Github Action runner as a container? Do you want to run it on your Docker or Kubernetes cluster and scale the number of runners easily? This is your post :)&lt;/p&gt;
&lt;p&gt;We are going to the point here, just a few explanations. If you want to know further, you have a few links at the bottom.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#syntax=docker/dockerfile:1.3-labs&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; ubuntu:20.04&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; TARGETARCH&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENV&lt;/span&gt; RUNNER_VERSION=&lt;span class=&quot;token variable&quot;&gt;${RUNNER_VERSION:-2.291.1}&lt;/span&gt;&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 &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    &amp;amp;&amp;amp; apt-get install -y --no-install-recommends &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    ca-certificates &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    curl &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    libicu-dev &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    netcat &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    jq&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /actions-runner&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; &amp;lt;&amp;lt;EOT &lt;/span&gt;
    export ARCH=${TARGETARCH}
    &lt;span class=&quot;token comment&quot;&gt;# When the arch is amd64, the Github runner binary to use is x64.&lt;/span&gt;
    if [ &quot;amd64&quot; = &quot;$TARGETARCH&quot; ]; then
        export ARCH=&quot;x64&quot;
    fi

    curl -o /actions-runner/actions-runner-linux.tar.gz -L \
        https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${ARCH}-${RUNNER_VERSION}.tar.gz 
EOT

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; tar xzf ./actions-runner-linux.tar.gz&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; rm ./actions-runner-linux.tar.gz&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; entrypoint.sh .&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; chmod -R 7700 entrypoint.sh&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; adduser --no-create-home github -uid 1001&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 github:github /actions-runner&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;USER&lt;/span&gt; github&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENTRYPOINT&lt;/span&gt; [&lt;span class=&quot;token string&quot;&gt;&quot;/actions-runner/entrypoint.sh&quot;&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The keys are the ARG TARGETARCH that the &lt;em&gt;docker build&lt;/em&gt; command will provide and
the checks when the architecture is amd64.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;docker-compose.yml&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;3.5&apos;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;github-action-runner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 
      &lt;span class=&quot;token key atrule&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .
      &lt;span class=&quot;token key atrule&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dockerfile 
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;DOCKER_REPOSITORY&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;nexus.playtomic.io&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/playtomic&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;:&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 punctuation&quot;&gt;-&lt;/span&gt;latest&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; GH_ORGANIZATION=syltek
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; GH_ACCESS_TOKEN=$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;GH_ACCESS_TOKEN&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;max-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10M
    &lt;span class=&quot;token key atrule&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;placement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;node.role == worker&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;REPLICAS&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 key atrule&quot;&gt;update_config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;parallelism&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;failure_action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rollback&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;entrypoint.sh&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; -e

&lt;span class=&quot;token function-name function&quot;&gt;cleanup&lt;/span&gt;&lt;span class=&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 assign-left variable&quot;&gt;GH_RUNNER_TOKEN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$1&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;Removing runner with token &lt;span class=&quot;token variable&quot;&gt;${GH_RUNNER_TOKEN}&lt;/span&gt;...&quot;&lt;/span&gt;
  ./config.sh remove --token &lt;span class=&quot;token variable&quot;&gt;${GH_RUNNER_TOKEN}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# GH_ACCESS_TOKEN must be a token with admin:org permissions https://github.com/settings/tokens/&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; -z &lt;span class=&quot;token variable&quot;&gt;${GH_ACCESS_TOKEN+x}&lt;/span&gt; &lt;span class=&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;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;GH_ACCESS_TOKEN environment variable is not set&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&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; -z &lt;span class=&quot;token variable&quot;&gt;${GH_ORGANIZATION+x}&lt;/span&gt; &lt;span class=&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;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;GH_ORGANIZATION environment variable is not set.&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Generate a runner token from our Github Access Token. Every runner needs a new token.&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;GH_RUNNER_TOKEN&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;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; -sX POST -H &lt;span class=&quot;token string&quot;&gt;&quot;Authorization: token &lt;span class=&quot;token variable&quot;&gt;${GH_ACCESS_TOKEN}&lt;/span&gt;&quot;&lt;/span&gt; https://api.github.com/orgs/$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;GH_ORGANIZATION&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/actions/runners/registration-token &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; jq .token --raw-output&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;Using the following token to register the runner: &lt;span class=&quot;token variable&quot;&gt;${GH_RUNNER_TOKEN}&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Traps are here to unregister the runners on docker stop, but they are not being triggered.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#trap &apos;cleanup ${GH_RUNNER_TOKEN}; exit 143&apos; SIGTERM&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#trap &apos;cleanup ${GH_RUNNER_TOKEN}; exit 2&apos; SIGINT&lt;/span&gt;

cleanup &lt;span class=&quot;token variable&quot;&gt;${GH_RUNNER_TOKEN}&lt;/span&gt;
/actions-runner/config.sh --runnergroup Cluster --url https://github.com/&lt;span class=&quot;token variable&quot;&gt;$GH_ORGANIZATION&lt;/span&gt; --token &lt;span class=&quot;token variable&quot;&gt;$GH_RUNNER_TOKEN&lt;/span&gt; --replace
/actions-runner/run.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; buildx build --platform linux/amd64 &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; buildx build --platform linux/arm64 &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;GH_ACCESS_TOKEN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;your-token &lt;span class=&quot;token function&quot;&gt;docker-compose&lt;/span&gt; up&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Inspiration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://testdriven.io/blog/github-actions-docker/&quot;&gt;https://testdriven.io/blog/github-actions-docker/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.koyeb.com/tutorials/deploying-github-actions-self-hosted-runners-on-koyeb&quot;&gt;https://www.koyeb.com/tutorials/deploying-github-actions-self-hosted-runners-on-koyeb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Modernizing a CI+CD pipeline with Github Actions]]></title><description><![CDATA[Our CI+CD has been working for 5 years long.
You know, if it ain’t broken, don’t fix it. But the company is not the same. It’s time to…]]></description><link>https://sgmoratilla.com/2022-06-06-modernizing-a-ci-cd-pipeline/</link><guid isPermaLink="false">https://sgmoratilla.com/2022-06-06-modernizing-a-ci-cd-pipeline/</guid><pubDate>Mon, 06 Jun 2022 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/2019-04-15-playtomic-pipeline&quot;&gt;Our CI+CD has been working for 5 years long&lt;/a&gt;.
You know, if it ain’t broken, don’t fix it. But the company is not the same. It’s time to update it!&lt;/p&gt;
&lt;p&gt;Let me wrap up a bit our current setup. I am going to be brief, I promise.
We have a Jenkins cluster on-premise. That is, we manage (and maintain) a bunch of hosts
that run Jenkins slaves and a host that runs the master. They are within our on-premise VPN,
a reminiscence of our first hosting provider.&lt;/p&gt;
&lt;p&gt;Our production backend runs a containerized system on top of a Docker Swarm cluster.
Our container registry is Nexus, which allows us to deploy our services in the Swarm.&lt;/p&gt;
&lt;p&gt;Both systems run within their own independent VPNs.&lt;/p&gt;
&lt;p&gt;Our code repositories are in Github. Our Jenkins is listening for changes in Github.
When we merge to develop/production, Jenkins pushes the image to the Nexus, connects
via ssh to the managers of the cluster and runs the deployment command (&lt;em&gt;docker stack deploy&lt;/em&gt;).
We have a common Jenkins pipeline for all our services.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/be32b4d970554afff4772cd68806a51c/6bde6/modern-ci-jenkins.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 45.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABWElEQVQoz32SSW7DMAxFff+rdNtjdNN0GxQtGjtO43iQNVgD+QvSzoAsSoCwKVNPn/qusAX5HhTGawlmxmNYazUfv2vPQ5/UFZcEzgvK+IMyHvRd1rBtcM4hLAF1U6NtW8QY4b2/QyRzBqWkdUVxvp12yxQAJj3c2BnflwMGv6ovpeBy6VHXDXIpsgCKEbQs+qw4eXAckfYvyF+v4GRA0YFzAC0zgh2x7z5howcTK/BQH/G++8BkDJJzChOVxXtRaMHZI7dvKN1uBYUR5DqUFHTk03zG5A1SSgqsmxVojFFVxdpVYQibQspgAZcCpqIj82KQ3QBvZ/R+QOd6EJOOHULANE2rmTGu10S0Am8umgExOL3mq8NEjEt/wnFsMbhR1UnmnFWtmrKBJEWQAsW5pjni93wGbbCrQT4EzHaGdVbdlRSFAr25zHcR1XWzNAkYTw3/xXOf/Id/AtW/VIFycq0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;CI+CD Pipeline with Jenkins&quot;
        title=&quot;CI+CD Pipeline with Jenkins&quot;
        src=&quot;/static/be32b4d970554afff4772cd68806a51c/f97d7/modern-ci-jenkins.png&quot;
        srcset=&quot;/static/be32b4d970554afff4772cd68806a51c/0eb09/modern-ci-jenkins.png 500w,
/static/be32b4d970554afff4772cd68806a51c/1263b/modern-ci-jenkins.png 1000w,
/static/be32b4d970554afff4772cd68806a51c/f97d7/modern-ci-jenkins.png 2000w,
/static/be32b4d970554afff4772cd68806a51c/6bde6/modern-ci-jenkins.png 2100w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple.&lt;/li&gt;
&lt;li&gt;Security on our side (VPN certificates + ssh keys are in our servers).&lt;/li&gt;
&lt;li&gt;Jenkins is commonly known.&lt;/li&gt;
&lt;li&gt;Stable: we haven’t had to change it a lot all this time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We need to maintain and keep update the Jenkins machines.&lt;/li&gt;
&lt;li&gt;As the team grows, the Jenkins cluster has to grow too to be able to run more jobs.&lt;/li&gt;
&lt;li&gt;We have only configured Java 8 and 11.&lt;/li&gt;
&lt;li&gt;Still on our old hosting provider.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have been pretty happy with this setup so far.
Maintenance is something that we always want to simplify at Playtomic. The fewer systems, the better.
Besides, new versions of Java require new versions of the JDK, maven and thus… Jenkins.
We have already been using Github Actions in other projects here, so that we know that it could be
a fine replacement of Jenkins. The workflow defines the environment it is required to run (for example, the java version or the architecture), so it makes complete sense to use Github Actions.&lt;/p&gt;
&lt;h2&gt;Replacing Jenkins by Github Actions&lt;/h2&gt;
&lt;p&gt;We re-wrote the Jenkins pipeline as a Github Actions workflow. Our main concerns are two:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What about security? We are using ssh-keys to access the cluster. We can use Github secrets to store them, but we don’t like having such an important piece of the security stored in a third-party.&lt;/li&gt;
&lt;li&gt;Cost might be a problem in the future. Github Actions are charged by the minute of computation and we have 50 services.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We discovered that Github Actions allow you to run self-hosted nodes… so that’s the solution to both problems.
At this very moment, we can afford the minutes that we are spending, so we are adding a host just for deployments.
We added a t3.nano, which is pretty cheap. ssh-keys are still 100% under our control, as they are installed in the machine.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1912px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e68a62acefed61281d2a3ccc1b01e3ce/9aa73/modern-ci-github-actions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAABv0lEQVQ4y41Ty3LbMAzU/39Cp4f+R6e33nps2kweauxYdlPHFpWh+AAIcDukbcVq3E4xA0kkiAW0Cza4YDln7Pd7iMjF2MlR/I9Yo6o4uOD0zcxo2xaJUz1Y9uQYnwGcCqR0KACgCcGDkyJSQoyEEBmRqPrjrsPjc4fRO8QYEULAZvMTZhim7jUEiPeQcTwASilKBmn5EbL6hOyfkFMAhLAya3TDZvZL7cMCt7d3GMcRygwJYQIunTZJAbUrxK/vqkt/jSwJmRzWpsPW7sHElYbS5c3dPa6uvtUuyVqIc0jGVLAC2ogcefI9NA4TN8VGctjaHcboaneFw/v2Bx4Wy0pJJoKmYz4zNMaDKBUkxTeqGf+C3g9g4WnPOTeJk0Um7so7q6IhIogqUhiRmCCilfCS1PUd1n0HTjxNQBFG5FVtLflHPqsopaIPAe5lB28HhFDU9IjEiNYgDr/ASVAKFy88nkYkX5rD0yJ6C6Y4O6B2AzXtGavz5DeDXjosD+89Foslts87kN1XhdVtQNcfQN/fQ4eb1yE+Sz5fzwALZ8YYWGuhwsgqyMkjPX1BWn9Gjv0M4F/WXDqQ/3K//8d+Awam+Ig9i14SAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;CI+CD Pipeline with Github Actions&quot;
        title=&quot;CI+CD Pipeline with Github Actions&quot;
        src=&quot;/static/e68a62acefed61281d2a3ccc1b01e3ce/9aa73/modern-ci-github-actions.png&quot;
        srcset=&quot;/static/e68a62acefed61281d2a3ccc1b01e3ce/0eb09/modern-ci-github-actions.png 500w,
/static/e68a62acefed61281d2a3ccc1b01e3ce/1263b/modern-ci-github-actions.png 1000w,
/static/e68a62acefed61281d2a3ccc1b01e3ce/9aa73/modern-ci-github-actions.png 1912w&quot;
        sizes=&quot;(max-width: 1912px) 100vw, 1912px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Still a common pipeline (workflow) but allows everyone to use new stuff (new java version, different languages, …) without installing more tools in Jenkins.&lt;/li&gt;
&lt;li&gt;Security is still on our side.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We need to monitor the cost.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What’s next?&lt;/h2&gt;
&lt;h3&gt;Containerized Github Action runner&lt;/h3&gt;
&lt;p&gt;Worried about the cost? If you have a cluster, &lt;a href=&quot;/2022-06-07-docker-multiarch-github-actions-runner/&quot;&gt;you can run as several copies as you want of the Github Action runner!&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;On-demand self-hosted runners&lt;/h3&gt;
&lt;p&gt;This would be a huge improvement to control the cost while still being able to scale the number of runners:
&lt;a href=&quot;https://github.com/machulav/ec2-github-runner&quot;&gt;https://github.com/machulav/ec2-github-runner&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Separate the CI from the CD&lt;/h3&gt;
&lt;p&gt;To separate pipeline for the CI and CD is a good idea, as their lifecycles are pretty different.
In our current setup, the workflow is responsible for running the deployment command. If that fails, then the whole pipeline fails, but the build was successful.&lt;/p&gt;
&lt;p&gt;We have already tested ArgoCD in Kubernetes so that the CD is handled by the cluster itself.
If you are running Kubernetes, you can already &lt;a href=&quot;/2021-10-28-flux-vs-argocd/&quot;&gt;do that with FluxCD or ArgCD&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1732px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0822bb29f3ae77d2209e1191614b759f/3553b/modern-ci-argocd.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAABuklEQVQ4y42STZKbMBCF5/7LnCInyAGyyzKr+YsHpwYPMhiwhH5bal5KyoBxxlMVqlQ0avXHe+q+m+cZtx4pJYQQN3NLzbY2x3nd5Q9mRgihLO89Yow4dSeIRpQ47y35fPYKmhI4hBVcgMYYaK1LgfO+AMS5xa7dQ2qF4AOICOM4ohGi5AuQGUlrJGOQpukamFK6snXSPV7631dqjm2Hh6df2O12CEQgrTHHWHIZyEQXYFaw2DfB4mV4xeHcIFEsyjP07a3Bw+MzqqqCsRZeKUQpQX1fwFnpCozvf8o3pJzEUQqIqYOyE2L6m2u7DvePT6jrGpyb4Fy5w1JHBPb+o8I5aEC3iIkw2jMmr1fLWekwDOu1JOcKqMTZPvMGmBUmQuwrcAyopcB+eIUltwKyi3x2OyalKdaCnbs0hSjAuAB3PsKqocTn8Yjh+AKlJxijYa0tsC1weS8qV2BiBjsJRH9pMzmwPGyn+f8GO89U3/dQ46lYzYVzCojND1D1DayqwloK/oVt94pCpRQOdY1GtAi6xxwM2Hag56/wP78gNd8xz/xB0WdPseycu3T5fbHtkeQeM9mbSj4D/gGYiPepmpxHBwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;CI+CD Pipeline with Github Actions and ArgoCD&quot;
        title=&quot;CI+CD Pipeline with Github Actions and ArgoCD&quot;
        src=&quot;/static/0822bb29f3ae77d2209e1191614b759f/3553b/modern-ci-argocd.png&quot;
        srcset=&quot;/static/0822bb29f3ae77d2209e1191614b759f/0eb09/modern-ci-argocd.png 500w,
/static/0822bb29f3ae77d2209e1191614b759f/1263b/modern-ci-argocd.png 1000w,
/static/0822bb29f3ae77d2209e1191614b759f/3553b/modern-ci-argocd.png 1732w&quot;
        sizes=&quot;(max-width: 1732px) 100vw, 1732px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Replace Nexus by Github Packages&lt;/h3&gt;
&lt;p&gt;Nexus requires a lot of space (as it stores all the containers, libraries, packages, … of your organization).
We don’t want to maintain that space, be responsible of the backups, … so that we are considering to migrate
to Github Packages too.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Performance and MongoDB]]></title><description><![CDATA[If you, a backend developer, had to describe your job, what would you say? We usually talk a lot about servers, clusters, layers, algorithms…]]></description><link>https://sgmoratilla.com/2022-02-14-mongodb-performance/</link><guid isPermaLink="false">https://sgmoratilla.com/2022-02-14-mongodb-performance/</guid><pubDate>Tue, 15 Feb 2022 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you, a backend developer, had to describe your job, what would you say? We usually talk a lot about servers, clusters, layers, algorithms, software stacks, memory consumption, …&lt;/p&gt;
&lt;p&gt;We put data into databases and we get it back as fast as we can.
Databases are our cornerstone. Why don’t we talk more often about them? We are always relying on our ORMs.&lt;/p&gt;
&lt;p&gt;My best advice? Simplify your queries. Simplify your data models. Simplify your access patterns.&lt;/p&gt;
&lt;h1&gt;Performance in MongoDB&lt;/h1&gt;
&lt;h2&gt;Metrics&lt;/h2&gt;
&lt;p&gt;If you are using MongoDB, these two metrics will be your best friends:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scanned Documents/Returned ratio&lt;/li&gt;
&lt;li&gt;IOPS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Scanned docs/returned&lt;/strong&gt;: it means how many documents you are reading from disk vs the number of documents you are actually returning in your &lt;code class=&quot;language-text&quot;&gt;find()&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;aggregate()&lt;/code&gt;. Ideally: this should be 1 (every document read is returned). The only way to get it? All your queries must be covered by indexes. Indexes are in memory (or they are if they fit), so MongoDB doesn´t have to read and filter them from disk.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1926px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/955cabb5b963388ffde95f8973230796/5dd2a/mongodb-scanned-returned.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABKUlEQVQoz43Qy07DMBCF4bwZJM5MBG3iOOM4t6ZcFyzh/Rc/SgEJAVVZjGRZ53y+ZLr3XJxdw39zWVW3lwtb5uKBLeKNrPKGtgnd1ecLYbgM1i3SjWSVTUg6orf78wWb/0B+ggGxmazqF2R8QG9250txQX1EG0ObDvWG+OY3GBeyKq3I9AGWwZ+CZWhOmNRf4IzY8IFuzw8J131+Ue2R/XcwrrjD8QQ6a3Bdw9WhprrxFH1Nud0kTLj7RyQkZLxHQo8LO6TxuGk8rXUfUFvI5Hggf16QtqZYW67vPFePHje05KunmDwyHShe38ifZ/KXB2RM5GtHOSWKp5X8aEjscMtMpr2xjVhAkyHJ0LjtdWj8nD4i44AMhgw9aoamiKQeTT2SIlXsKEPgHZBLVnQVG2lYAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Example of Scanned documents/Returned ratio&quot;
        title=&quot;Example of Scanned documents/Returned ratio&quot;
        src=&quot;/static/955cabb5b963388ffde95f8973230796/5dd2a/mongodb-scanned-returned.png&quot;
        srcset=&quot;/static/955cabb5b963388ffde95f8973230796/0eb09/mongodb-scanned-returned.png 500w,
/static/955cabb5b963388ffde95f8973230796/1263b/mongodb-scanned-returned.png 1000w,
/static/955cabb5b963388ffde95f8973230796/5dd2a/mongodb-scanned-returned.png 1926w&quot;
        sizes=&quot;(max-width: 1926px) 100vw, 1926px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IOPS&lt;/strong&gt;: I/O operations per second. That is, operations on disk.  It is correlated with scanned docs, as they are read from disk. But there are more sources of IOPS, for example writes.
Your disk will give you a limit to your maximum IOPS. Ours is 3000.&lt;/p&gt;
&lt;p&gt;Your goal is to keep IOPS below that threshold, and as low as possible. It is hard to know how many IOPS your query consumes, but it is easy to know the scanned docs/returned ratio.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1916px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5570b676f8aaf459d3c8a7ef39576e06/db5ee/mongodb-iops.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABYElEQVQoz42OW4+aUBSF+WNcBjnb4XIQFMQRcARxtDi281DTtOl//xou6cMkTfqwsi9r7S/bkGjF/yue9e+MESQblsMQzsHPdVSCBBpZ75C0mL3kU2YG+nHKMk5R5Xla6mRSkk/fBCESRCNQ1W942wJ59qd9vJ6yoZ7gYYzhJxnL5gPv9YK3K5E4Q3YNqv+NlBckq5DNC1LfWJzvuF2LHD+Q+orqviOHd0SnqMMNWb8MwA3q9I5zrnH6Dvf+QPW/8C7fcPs7Xv8D78sD1X7FrXLsS8Xi7Y53faCqE4tjh3f7iTpc8eoO43mV4u41ZhNhdjHWdYe73/BU+JgnjV1rnFLztI+x6xDzFGGXPs4+xCkj7DrAbtKxdws9Ae02xmo09usADrCOEVYbj0Cz0VjtrGbW7P3t2+EZjVNpDNErVJ6wzFIkT1FFitoONUFtE6SYetlOvmTJmBnm4W7y50ye8gcvkDV3BFGZAQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Example of IOPS&quot;
        title=&quot;Example of IOPS&quot;
        src=&quot;/static/5570b676f8aaf459d3c8a7ef39576e06/db5ee/mongodb-iops.png&quot;
        srcset=&quot;/static/5570b676f8aaf459d3c8a7ef39576e06/0eb09/mongodb-iops.png 500w,
/static/5570b676f8aaf459d3c8a7ef39576e06/1263b/mongodb-iops.png 1000w,
/static/5570b676f8aaf459d3c8a7ef39576e06/db5ee/mongodb-iops.png 1916w&quot;
        sizes=&quot;(max-width: 1916px) 100vw, 1916px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Tools&lt;/h2&gt;
&lt;p&gt;How can you analyze why your database is behaving as it is?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MongoDB Profiler&lt;/strong&gt;
If you can afford to enable it, do it now. It’s the best source of info. We use the Atlas MongoDB Profiler and it is worth every penny.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1796px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b6bf709c48729f678e9e6389c45ab857/f6a5a/mongodb-profiler.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABN0lEQVQoz41QXU/CQBDsHzFg+WgU0NJrbUvvY68FEvRFMRF8AKLEqNFHExONP8BfPaZ3SIgi8WEytzszu5tzql2Obaj4GfYDgWM1gk+nhlt8aPp/ZUo4XqxRYxJuIOAyYXkD9VCtUQvlL30T5QFmoMt+GJnYGdzlcQ7TPpqRslcyafhfCLf1y4GJRju25zZCbraUQp3x9VZb23cjFKgF3PhNP7B1g9nvcTpZjkRY0YtWm8tAqOBGhCojdBIBP+XY6yq4jKOVKHgxoeJzuCc5KkyjnUocpAWcWCh8Lnu4mw5wMybMzyUWY4HnicLjhON+2sfbQuB91sPyqsD8coCPW42XGeFixPF0neNhWuB1TjgbZnCakUQsBHheIFEaiSSkpMFJgRNZ6G/WyEhDrOrSL7XVJSkcpQpfsF4HXssrdJUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Atlas MongoDB Profiler&quot;
        title=&quot;Atlas MongoDB Profiler&quot;
        src=&quot;/static/b6bf709c48729f678e9e6389c45ab857/f6a5a/mongodb-profiler.png&quot;
        srcset=&quot;/static/b6bf709c48729f678e9e6389c45ab857/0eb09/mongodb-profiler.png 500w,
/static/b6bf709c48729f678e9e6389c45ab857/1263b/mongodb-profiler.png 1000w,
/static/b6bf709c48729f678e9e6389c45ab857/f6a5a/mongodb-profiler.png 1796w&quot;
        sizes=&quot;(max-width: 1796px) 100vw, 1796px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explain planner&lt;/strong&gt;
The planner is the key to understanding your access patterns.
There are several ways of calling it, but you can start with &lt;code class=&quot;language-text&quot;&gt;explain()&lt;/code&gt; after your cursor:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;db.our_collection.find(query).explain()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can use add &lt;code class=&quot;language-text&quot;&gt;executionStats&lt;/code&gt; to get more data about the query.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;db.our_collection.find(query).explain(&quot;executionStats&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/reference/explain-results/&quot;&gt;There are many stages&lt;/a&gt;, but these ones are the most important:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;COLLSCAN: the query is scanning the collection in disk. Pretty bad, as no index covered the search, so MongoDB has to read the whole collection.&lt;/li&gt;
&lt;li&gt;IXSCAN: the query is using an index to filter. It doesn´t mean that all the query is covered by the index, but at least some part.&lt;/li&gt;
&lt;li&gt;FETCH: the planner is reading the documents from the collection. If your query is returning documents, you will get a FETCH stage probably (&lt;a href=&quot;https://docs.mongodb.com/manual/indexes/#covered-queries&quot;&gt;unless your query is covered by the index&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is an example of one of our queries:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;winningPlan: 
      { stage: &apos;COLLSCAN&apos;,
        filter: 
         { &apos;$and&apos;: 
            [ { &apos;$or&apos;: 
                 [ { &apos;invitaed_user_id&apos;: { &apos;$eq&apos;: &apos;1&apos; } },
                   { owner_id: { &apos;$eq&apos;: &apos;1&apos; } },
                   { &apos;player_id&apos;: { &apos;$eq&apos;: &apos;1&apos; } } ] },
              { is_canceled: { &apos;$eq&apos;: false } },
             ] } },&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is not good, as it is scanning the whole collection.&lt;/p&gt;
&lt;p&gt;Another one:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;db.our_collection.find(query).explain(&quot;executionStats&quot;)
executionStats: 
   { executionSuccess: true,
     nReturned: 22,
     executionTimeMillis: 0,
     totalKeysExamined: 24,
     totalDocsExamined: 22,
     executionStages: 
      { stage: &apos;FETCH&apos;,
        nReturned: 22,
        docsExamined: 22,
        inputStage: 
         { stage: &apos;OR&apos;,
           nReturned: 22,
           inputStages: 
            [ { stage: &apos;IXSCAN&apos;,
                nReturned: 0,
                indexName: &apos;example-index-1&apos;,
                indexBounds: 
                 { owner_id: [ &apos;[&quot;1&quot;, &quot;1&quot;]&apos; ],
                   start_date: [ &apos;[MaxKey, MinKey]&apos; ] } },
              { stage: &apos;IXSCAN&apos;,
                nReturned: 22,
                indexName: &apos;example-index-2&apos;
                indexBounds: 
                 { &apos;player_id&apos;: [ &apos;[&quot;1&quot;, &quot;1&quot;]&apos; ],
                   start_date: [ &apos;[MaxKey, MinKey]&apos; ] ] },
                keysExamined: 23,
                dupsDropped: 0 }] } } },&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here you can two see IXSCANs, merged by and OR. After that, the query is fetching the documents. I am reading the query inside-out. &lt;code class=&quot;language-text&quot;&gt;example-index-1&lt;/code&gt; is used to resolve one part of the query, and &lt;code class=&quot;language-text&quot;&gt;example-index-2&lt;/code&gt; for the other part.&lt;/p&gt;
&lt;p&gt;Sometimes you will get a FETCH just after an IXSCAN: it means that the index covers only part of the filter. After that, the planner needs to read the documents from disk to finish the filter.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mongodb/mongo/blob/master/src/mongo/db/query/stage_types.h#L49&quot;&gt;The complete list of stages? You need to check the code.&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Simplify your queries&lt;/h1&gt;
&lt;h2&gt;$ors&lt;/h2&gt;
&lt;p&gt;$ors are the devil. You, as a programmer, are used to thinking in $ors. You add a few $ors, your condition gets much more expressive. But guess what? Your query has gotten exponentially more complex. With every condition you add to the $or, you are adding one more combination of parameters.&lt;/p&gt;
&lt;p&gt;How does the planner resolve all those combinations? It needs an index for each of them.&lt;/p&gt;
&lt;p&gt;Do you remember the &lt;code class=&quot;language-text&quot;&gt;explain()&lt;/code&gt; above? The query was:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{
	$or: [{&quot;owner_id: &quot;1&quot;}, {&quot;player_id: &quot;1&quot;}].
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The indexes used?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;example-index-1: {&quot;owner_id: &quot;1&quot;, &quot;start_date&quot;: 1}
example-index-2: {&quot;player_id: &quot;1&quot;, &quot;start_date&quot;: 1}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And here is another tip: bigger indexes can cover smaller queries as long as the fields are at the beginning of the index.
We are not using start_date to filter.&lt;/p&gt;
&lt;p&gt;What would happen if I add an extra $or?
{
$or: [{“owner_id: “1”}, {“player_id: “1”}].
$or: [{“is_canceled”: true}, “start_date”: {$gt: ISODate(“2022-02-02T00:00:00)}]
}&lt;/p&gt;
&lt;p&gt;Then there would need 4 combinations:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{owner_id, is_canceled}, 
{owner_id, start_date}, 
{player_id, is_canceled], 
{player_id, start_date}, &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Denormalized fields&lt;/h2&gt;
&lt;p&gt;If you find yourself filtering by several fields within an $or, or sorting by several fields, consider adding a denormalized field (based on the others).&lt;/p&gt;
&lt;p&gt;Yeah, my apologies to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Third_normal_form&quot;&gt;third normal form&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Keep reading, we will give an example pretty soon using the so-called &lt;code class=&quot;language-text&quot;&gt;Summary Pattern&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;nulls are always the minimum value&lt;/h2&gt;
&lt;p&gt;This is a minor trick, but still useful. Let’s say you have a nullable field, and you have to filter (or sort descending) by that field.&lt;/p&gt;
&lt;p&gt;You will probably find yourself using a query similar to this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{
 $or: [
        field: {$exists: false}, 
        field: {$lte: value}
      ]
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That is, if the field is null, then it passes the filter. Otherwise, compare. We used this filter to check if a player could join a match given their level and the level restrictions of the match.&lt;/p&gt;
&lt;p&gt;That’s an $or. We don’t like $ors. What would happen if we compare our value to null? Let’s check the &lt;a href=&quot;https://docs.mongodb.com/manual/reference/operator/aggregation/sort/#ascending-descending-sort&quot;&gt;sorting rules in MongoDB&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;MinKey (internal type)
Null
Numbers (ints, longs, doubles, decimals)
Symbol, String
Object
...
MaxKey (internal type)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That is, null is always the lower value when comparing (except for the MinKey object, we will talk about it later).&lt;/p&gt;
&lt;p&gt;Our query can be simplified by this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{
  field: {$lte: value}
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It works for $lt and $lte (lower than and lower or equal).
It works too if you are sorting by descending order of &lt;code class=&quot;language-text&quot;&gt;field&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;{field: -1}&lt;/code&gt; because the object with null will be at the end of the sort.&lt;/p&gt;
&lt;h2&gt;Counts are costly in MongoDB&lt;/h2&gt;
&lt;p&gt;Counting seems an easy operation, but it is not. Even if you have an index, MongoDB needs to traverse the index due to the way &lt;a href=&quot;https://docs.mongodb.com/manual/indexes&quot;&gt;MongoDB builds B-trees&lt;/a&gt;: they don’t store the number of leaves that the sub-tree have. So they need to traverse the index until the end.&lt;/p&gt;
&lt;p&gt;Again, if you are counting using a query with $or, it makes the counting even more complex: the query needs to take into account possible repeated documents.&lt;/p&gt;
&lt;p&gt;For example, we used counts to compute the position of a player in a ranking (the original query was even more complex).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{ranking_id: ?0}, 
{
 $or: [
		{ value: {$gt: ?1}},
    { value: {$eq: ?1}, last_modified: {$gt: ?2} }
 ]
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It required several indexes to count:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{ranking_id: 1, value: -1}
{ranking_id: 1, value: -1, last_modified: -1}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;How can we avoid counting on several indexes? Add a single field that summarize the fields that you are filtering.&lt;/p&gt;
&lt;p&gt;For example: &lt;code class=&quot;language-text&quot;&gt;weight = append(value, last_modified)&lt;/code&gt;
With that new field, we only required one single index:
Indexes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{ranking_id: 1, weight: -1}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is called the Summary Pattern.&lt;/p&gt;
&lt;h1&gt;How to build indexes&lt;/h1&gt;
&lt;p&gt;Ok, indexes are our best tool to keep MongoDB as performant as possible. So the next step, how do we know what indexes we should build?&lt;/p&gt;
&lt;h2&gt;Performance advisor&lt;/h2&gt;
&lt;p&gt;If you are in Atlas, use the &lt;a href=&quot;https://docs.atlas.mongodb.com/performance-advisor/&quot;&gt;Performance advisor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At some point, you will know your system better than the Performance Advisor, but it is a good starting point.&lt;/p&gt;
&lt;h2&gt;Clone your production collections and explain() it in local&lt;/h2&gt;
&lt;p&gt;Test your indexes thoroughly before you put them in production: - it takes a lot of IOPS to build them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;once you built it, the planner is taking it into account too, even if it is not finally used.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember what we said before:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;COLLSCAN: bad.&lt;/li&gt;
&lt;li&gt;IXSCAN: good.&lt;/li&gt;
&lt;li&gt;FETCH: good if it is the final step. Bad in between.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Others that you will see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;COUNT: ok.&lt;/li&gt;
&lt;li&gt;MERGE: ok-ish, you probably could do better.&lt;/li&gt;
&lt;li&gt;MERGE_COUNT: good.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the way, there are blocking and non-blocking stages, meaning that a stage needs to wait for the previous one before it can start computing results.&lt;/p&gt;
&lt;h2&gt;ESR: Equal-Sort-Range&lt;/h2&gt;
&lt;p&gt;Have you ever wondered what fields should go first in an index? You need to follow this rule:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fields filtered by equals ($eq, $in in some cases, …) go first.&lt;/li&gt;
&lt;li&gt;Then fields used in the sort stage. Remember that the index has to be built in the same order as you are sorting.&lt;/li&gt;
&lt;li&gt;Then fields filtered by a range ($lt, $lte, $gt, …).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ESR is the most useful rule you will find to build indexes. You should read as much as you can about it until you understand it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.alexbevi.com/blog/2020/05/16/optimizing-mongodb-compound-indexes-the-equality-sort-range-esr-rule/&quot;&gt;This post by Alex Belilacqua is a gem&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Disambiguate equals&lt;/h2&gt;
&lt;p&gt;If you have two fields that are going to be filtered by $eq, what should go first?&lt;/p&gt;
&lt;p&gt;The answer is that it doesn’t matter. You don’t need to worry about having a more balanced tree.&lt;/p&gt;
&lt;p&gt;Just keep in mind the ESR rule. If one of them goes in a &lt;code class=&quot;language-text&quot;&gt;sort&lt;/code&gt; or a &lt;code class=&quot;language-text&quot;&gt;range&lt;/code&gt;, then it goes the latter.&lt;/p&gt;
&lt;h2&gt;Rollover process&lt;/h2&gt;
&lt;p&gt;Building an index is one of the most costly operations. Your IOPS will go nuts. If you need to do that in your production environment, and your collection is big enough, then we recommend you to use &lt;a href=&quot;https://docs.mongodb.com/manual/tutorial/build-indexes-on-replica-sets/&quot;&gt;a rolling process&lt;/a&gt;. It starts the index build in a secondary, then promotes it to primary once the build is finished. You will be able to build any index even when your database load is high.&lt;/p&gt;
&lt;p&gt;In Atlas, it’s just one click.&lt;/p&gt;
&lt;h2&gt;Remove / Hide indexes&lt;/h2&gt;
&lt;p&gt;The more indexes you have, the worst for the planner. The planner runs the query through the indexes it has and then takes the most promising.&lt;/p&gt;
&lt;p&gt;Again, counting when you have several indexes is pretty bad.&lt;/p&gt;
&lt;p&gt;Sometimes, you cannot just remove an index in production. You can check using your profiler if it can be removed but you might not be 100%. One not-frequent query might launch a COLLSCAN and then you would miss that index.&lt;/p&gt;
&lt;p&gt;Luckily, since MongoDB 4.4 you can &lt;a href=&quot;https://docs.mongodb.com/manual/core/index-hidden/&quot;&gt;hide indexes&lt;/a&gt;. We use them to detect what indexes we can remove safely.&lt;/p&gt;
&lt;h1&gt;Limit your queries&lt;/h1&gt;
&lt;p&gt;Have you set a maximum limit of time to your queries? Why not? Do your clients have a request time out? Then it can be a healthy practice to avoid unexpected uses of your APIs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;db.our_collection.countDocuments(query, {maxTimeMS: 100})
MongoServerError: Error in $cursor stage :: caused by :: operation exceeded time limit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Do you see all these orange dots? No one was waiting for the backend to reply.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1806px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d4d27c7c99d88b0372a4b3bdca00d1ac/3f970/mongodb-profiler-limit-time.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABHUlEQVQoz41RW3KDMBDjJmlwKIEYCqwJAT8g0Dwm97+POmsI/Wja6YfGu/JKo7WDt9LgN2wKjVRf8DE8kLs7MnvDlhy2hca+6Py58bN61QQHfUHSTggrC0HuB6LjgLg5e3D9Tg5EHaQyKKsOETmEy2xIFkFu72BTNtwp5yEW+HoZFGR9OkkGWW2wO/ZIuSbj+edsUJ4fyO3NC6K6/xOi7pErC1lbyNbhUFvfCzXfs2nAZpITEifs15SvwMKELCqlcTha1KpDrOaVWesTSn1F2n2uK4slulAvwOsrh4QMZNkh4nf3vP1eWZrr/Ib/SPhMkbQj9DihaNlk5taEHJcN+Rf3pxH7Zlx/lfu4GWf+9ORHZHqCGQZUZu5X7WnCF9a2/9VHtcbYAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;maxTimeMS effect&quot;
        title=&quot;maxTimeMS effect&quot;
        src=&quot;/static/d4d27c7c99d88b0372a4b3bdca00d1ac/3f970/mongodb-profiler-limit-time.png&quot;
        srcset=&quot;/static/d4d27c7c99d88b0372a4b3bdca00d1ac/0eb09/mongodb-profiler-limit-time.png 500w,
/static/d4d27c7c99d88b0372a4b3bdca00d1ac/1263b/mongodb-profiler-limit-time.png 1000w,
/static/d4d27c7c99d88b0372a4b3bdca00d1ac/3f970/mongodb-profiler-limit-time.png 1806w&quot;
        sizes=&quot;(max-width: 1806px) 100vw, 1806px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/blog/post/building-with-patterns-a-summary&quot;&gt;https://www.mongodb.com/blog/post/building-with-patterns-a-summary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.alexbevi.com/blog/2020/05/16/optimizing-mongodb-compound-indexes-the-equality-sort-range-esr-rule/&quot;&gt;https://www.alexbevi.com/blog/2020/05/16/optimizing-mongodb-compound-indexes-the-equality-sort-range-esr-rule/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Flux vs ArgoCD]]></title><description><![CDATA[I have been trying to find any comparison between FluxCD and ArgoCD that could help me choose between them. I haven’t found any that truly…]]></description><link>https://sgmoratilla.com/2021-10-28-flux-vs-argocd/</link><guid isPermaLink="false">https://sgmoratilla.com/2021-10-28-flux-vs-argocd/</guid><pubDate>Thu, 28 Oct 2021 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been trying to find any comparison between FluxCD and ArgoCD that could help me choose between them. I haven’t found any that truly help us at Playtomic (many were just a list of meaningless comments if you don’t know both systems). So… we tried both and made our own decision.&lt;/p&gt;
&lt;h1&gt;Goals&lt;/h1&gt;
&lt;p&gt;We are adapting the GitOps practices. After digging into the topic, our ideal setup is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The cluster (infrastructure + applications) has to be described as code, not as commands.&lt;/li&gt;
&lt;li&gt;That makes the cluster easy to replicate (eases the process to set up new environments or the disaster recovery of the existing ones).&lt;/li&gt;
&lt;li&gt;As few steps to build a new cluster as possible (how many actions we have to perform manually before the cluster starts running). Spoiler: you can make it with 2 commands: one to build the K8s cluster, another to install all your infra and apps.&lt;/li&gt;
&lt;li&gt;The cluster must monitor changes in our repositories and automatically sync them to the cluster.&lt;/li&gt;
&lt;li&gt;One repository to describe the infrastructure (cluster repo).&lt;/li&gt;
&lt;li&gt;One repository to every service/application we want to deploy in the cluster (applications repo).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two last items are called &lt;a href=&quot;https://github.com/fluxcd/flux2-multi-tenancy&quot;&gt;multi-tenancy&lt;/a&gt;. The goal is that every team can be responsible for the lifecycle of their applications: they can write anything in their repositories, but they cannot alter the cluster infrastructure by themselves. That’s the responsibility of the owners of the cluster repo.&lt;/p&gt;
&lt;p&gt;Long story short: you can do everything in that list with both Flux and ArgoCD but there are differences about how they attain that.&lt;/p&gt;
&lt;h1&gt;Flux vs ArgoCD&lt;/h1&gt;
&lt;h2&gt;Concepts&lt;/h2&gt;
&lt;h3&gt;Flux:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You have to define &lt;a href=&quot;https://fluxcd.io/docs/components/source/gitrepositories/&quot;&gt;GitRepositories&lt;/a&gt;/&lt;a href=&quot;helmrepositories&quot;&gt;HelmRepositories&lt;/a&gt; and make Flux monitor them (if you are using Git+Kustomize you will need a &lt;a href=&quot;https://fluxcd.io/docs/components/kustomize/kustomization/&quot;&gt;Kustomization&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
  name: service-one
  namespace: playtomic
spec:
  ref:
    branch: develop 
  interval: 1m
  url: https://github.com/your-org/your-repo
  secretRef:
    name: git-credentials
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
  name: service-one
  namespace: playtomic
spec:
  interval: 1m
  sourceRef:
    kind: GitRepository
    name: service
  path: deploy/develop
  prune: true&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;ArgoCD&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You have to install &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#applications&quot;&gt;Applications&lt;/a&gt;. An Application is basically a repo to monitor. It supports Kustomize, Helm, …&lt;/li&gt;
&lt;li&gt;An Application can contain references to &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#app-of-apps&quot;&gt;more Applications&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: service-one
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default

  source:
    repoURL: https://github.com/your-org/your-repo
    targetRevision: develop
    path: deploy/develop

  destination:
    server: https://kubernetes.default.svc
    namespace: playtomic

  # Sync policy
  syncPolicy:
    automated: {}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Main differences&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ArgoCD objects have to be installed in the namespace where ArgoCD is installed (argocd in this example). That is the Application object belongs to the ArgoCD namespace. The actual application can be deployed in any target namespace.&lt;/li&gt;
&lt;li&gt;Flux objects belong to the namespace where the application will be actually deployed.&lt;/li&gt;
&lt;li&gt;ArgoCD supports defining several projects with different permissions (what namespaces or Kubernetes objects the application in that project are allowed to use).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to start the cluster&lt;/h2&gt;
&lt;h3&gt;Flux:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You write your cluster using standard k8s objects and Flux objects to indicate what pieces it has to monitor. Dependencies are written as you would write the declarative files in a regular k8s setup.&lt;/li&gt;
&lt;li&gt;You have to run &lt;code class=&quot;language-text&quot;&gt;flux bootstrap&lt;/code&gt; command, and everything is set up and monitored.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;ArgoCD:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You have to install ArgoCD (using the regular k8s descriptors).&lt;/li&gt;
&lt;li&gt;You have to make ArgoCD “self-aware” after that. That is, you have to install ArgoCD as an application in ArgoCD. You will need a secret to let ArgoCD access your repositories.&lt;/li&gt;
&lt;li&gt;Or use &lt;a href=&quot;https://github.com/argoproj-labs/argocd-autopilot&quot;&gt;ArgoCD Autopilot&lt;/a&gt; (which takes care of both steps)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Deploying new versions of your services&lt;/h2&gt;
&lt;p&gt;You probably have a CI already building your images. You can let that CI commit that version to your repository too (be aware of triggered builds afterward) or you can let Flux/ArgoCD check for new images and do that commit for you.&lt;/p&gt;
&lt;p&gt;We wanted to stop our &lt;a href=&quot;https://www.sgmoratilla.com/2019-04-15-playtomic-pipeline/&quot;&gt;Jenkins CI to deploy&lt;/a&gt; actively our services. That’s the CD responsibility and besides, the version deployed was not written anywhere: we had to check our monitor tools or query directly the cluster. So… let’s see what Flux and ArgoCD have to say about this.&lt;/p&gt;
&lt;h3&gt;Flux&lt;/h3&gt;
&lt;p&gt;You have to add two extra components when bootstrapping Flux:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;flux bootstrap github --owner=your-org --repository=your-cluster-repo --token-auth --path=/clusters/develop --components-extra=image-reflector-controller,image-automation-controller&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In every repository where you are storing image versions, you have to add a ImageRepository, a ImagePolicy and a ImageUpdateAutomation.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
  name: service-one
  namespace: playtomic
spec:
  image: your-docker-prefix/one-service
  interval: 1m0s
  secretRef:
    name: nexus-credentials
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
  name: service-one
  namespace: playtomic
spec:
  imageRepositoryRef:
    name: configuration
  filterTags:
    pattern: &apos;^develop-(?P&amp;lt;version&gt;.*)&apos;
    extract: &apos;$version&apos;
  policy:
     numerical:
      order: asc
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: service-one
  namespace: playtomic
spec:
  interval: 1m0s
  sourceRef:
    kind: GitRepository
    name: one-service
  git:
    commit:
      author:
        email: fluxcdbot@users.noreply.github.com
        name: fluxcdbot
      messageTemplate: &apos;{{range .Updated.Images}}{{println .}}{{end}}&apos;
  update:
    path: /deploy/develop/
    strategy: Setters&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;ArgoCD&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: configuration-service
  namespace: argocd
  annotations:
    argocd-image-updater.argoproj.io/image-list: service-one=your-docker-prefix/service-one
    argocd-image-updater.argoproj.io/service-one.allow-tags: regexp:^develop-[0-9a-zA-Z\-]+$
    argocd-image-updater.argoproj.io/service-one.update-strategy: latest
    argocd-image-updater.argoproj.io/write-back-method: git:secret:argocd/github-credentials
    argocd-image-updater.argoproj.io/write-back-target: kustomization
    argocd-image-updater.argoproj.io/git-branch: develop
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default

  source:
    repoURL: https://github.com/your-org/your-service
    targetRevision: develop
    path: deploy/develop

  ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You have several strategies, but here we are setting &lt;code class=&quot;language-text&quot;&gt;write-back-target: kustomization&lt;/code&gt; so it will write the image versions into the &lt;code class=&quot;language-text&quot;&gt;kustomization.yaml&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;deploy/develop&lt;/code&gt; directory.&lt;/p&gt;
&lt;h3&gt;Main differences&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Flux needs two extra components when bootstrapping and then an image-updater per repository whose version is monitoring.&lt;/li&gt;
&lt;li&gt;ArgoCD handles everything in the Application. Flux requires a Kustomization and a GitRepository.&lt;/li&gt;
&lt;li&gt;Flux’s image updater is a component in your tenant repository. ArgoCD’s updater is configured via annotation in their Application object.&lt;/li&gt;
&lt;li&gt;ArgoCD has a web console (with user management). Flux does not. We found that ArgoCD web UI is super useful for not experts to debug the errors in their projects.&lt;/li&gt;
&lt;li&gt;Flux bootstrap command install and make the system monitor itself. ArgoCD does not by default, you will need the &lt;a href=&quot;https://github.com/argoproj-labs/argocd-autopilot&quot;&gt;ArgoCD autopilot&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Dependencies control&lt;/h2&gt;
&lt;p&gt;Sometimes you need to install some components in Kubernetes before others. In ArgoCD they managed that dependency order as &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/&quot;&gt;waves&lt;/a&gt;. In Flux, by default, it retries until everything is ready (at least in our experience)&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;In both systems, it feels like the documentation was written expecting that you have already mastered the topic.&lt;/li&gt;
&lt;li&gt;Flux seems more k8s-ish: everything looks like a k8s object. More raw for my taste.&lt;/li&gt;
&lt;li&gt;ArgoCD seems more refined: Applications seem better defined.&lt;/li&gt;
&lt;li&gt;Flux Auto Update is tricky to configure (you have to configure it in two repositories).&lt;/li&gt;
&lt;li&gt;ArgoCD Auto Update seems smoother to set up (it’s a property of the Application).&lt;/li&gt;
&lt;li&gt;ArgoCD has a web console while Flux does not: it makes errors debugging much simpler.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;How to decide&lt;/h1&gt;
&lt;p&gt;If you have time, try both and check what adapts best to your case. I know, you don’t have the time and that’s why you are here.&lt;/p&gt;
&lt;p&gt;For us, the decisive points are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The image updater configuration is simpler in ArgoCD.&lt;/li&gt;
&lt;li&gt;The image updater in Flux was killing our Nexus with requests: last versions of the updater were unable to query by image name, they have to query everything and then filter the images. As we had several services, one image updater per service, it was too much.&lt;/li&gt;
&lt;li&gt;The ArgoCD console lets our team migrate their services and be able to check their deployments easily.&lt;/li&gt;
&lt;li&gt;Question of taste: ArgoCD descriptors seem more friendly to me.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[My evolution in Playtomic in Github charts]]></title><description><![CDATA[I sometimes take a peek at the Github charts, I find interesting what they speak about. I have never looked thoroughly at the activity…]]></description><link>https://sgmoratilla.com/2021-08-15-my-playtomic-evolution/</link><guid isPermaLink="false">https://sgmoratilla.com/2021-08-15-my-playtomic-evolution/</guid><pubDate>Sun, 15 Aug 2021 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I sometimes take a peek at the Github charts, &lt;a href=&quot;http://localhost:8000/2020-06-17-github-contributions/&quot;&gt;I find interesting what they speak about&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have never looked thoroughly at the activity overview. This is the 2-axis chart that shows how your contributions are split. My version for 2021 is surprisingly biased towards the Issues axis (87%). Omg, I have just turned into a manager.&lt;/p&gt;
&lt;p&gt;Jokes aside, I have taken a quick look at the previous years. It’s fantastic that I see my evolution and the evolution of my team in Playtomic.&lt;/p&gt;
&lt;h2&gt;2017 - the only backend developer&lt;/h2&gt;
&lt;p&gt;This was my first year at Playtomic. The craziest year in my career. I was the only developer dedicated full time to the backend. So most of my work was… writing code.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 798px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a97aa2593b04b14e82bc01d87fa2f4d5/50208/github-contributions-2017.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.20000000000002%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAABYlAAAWJQFJUiTwAAABj0lEQVQ4y6VUy0rDQBTt57hy2aVfIIK4E0HoWhC3gmtdiT8giPtuherGChahoF0plRYFoU2aJplM0jxnciRpTNskzQNPGDJMJufeO+fcqSEHvu/H77+xvJ6FWlmyrHklwuRPnudlrlfO0PMYiEYxGguguh4TV85wQehBmsoQJQkjQYRpWv8rOXgCSHQK7vPyJa8cONKHP1YnsF0ntTdFuE5B5nMwn8HjLCIUU4TJEWe4HG1dZFUnYBF53hHFGbquC1lRw/kPGaM3+UBHeEPz6wGX77e46t5AoSrITINqECgGATV1WJYFXTdi9cMMOecQxAn6gyEMbYbO6BXb7SNs3O2gfr+PzdYeDh9PoRkUxKRz0pkG3TZCKwmiBMdxVksOIjDG4EaRZJfg/PMaW08N1NsHOO5eoAgrJS+DRfYI0Jn2sPtygsbzGcAXDigUJfmBhyrPiVWXotlvFXZJprGTfvT4nCQQxHbtYh+WvRwkIlfrlHVkgXpBLw+G36GalpWfZanLIfCZRnUoKoFt57ffLzutewWYUdKjAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;2017&quot;
        title=&quot;2017 Contributions&quot;
        src=&quot;/static/a97aa2593b04b14e82bc01d87fa2f4d5/50208/github-contributions-2017.png&quot;
        srcset=&quot;/static/a97aa2593b04b14e82bc01d87fa2f4d5/0eb09/github-contributions-2017.png 500w,
/static/a97aa2593b04b14e82bc01d87fa2f4d5/50208/github-contributions-2017.png 798w&quot;
        sizes=&quot;(max-width: 798px) 100vw, 798px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;2018 - we got a team&lt;/h2&gt;
&lt;p&gt;We started to hire and build our backend team. We were still few, so you can see tons of commit yet, but I had to review my teammates’ PRs as well. I started to act as the head of the backend, but I still had to code a lot.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 790px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8371794e65a8ea0224df0ae07b98b9bb/88e66/github-contributions-2018.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 91.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAABYlAAAWJQFJUiTwAAABrklEQVQ4y6WUS0vDQBDH+3EKehG8CZ48FxQVPIkHP4F48O5ZEDz5AC3iQRQVRFAoghfBgyJYaxF89pmmbRKzu3ls8pdsylJj6qOdMGSym/xmduefTeAH831f3uPiOEt0Av0G6wRN/KXCwDjnseP/rtB2HCg1FYViGZpuwPO8/1fYDqSMoaIoqNXrqDea4N0Ao0tzPBeKoXbXFPmBH8YefNiODdVofOt8LDDaQXEFIN8D98PladTAXSkv4mBcvhtxWWF7tmjmWz2Pics5bOT2JfCnLZIVmoSAECpiy7HxpL1jMbuK4fMZJE9SWL/fE3MflglqM+HBVjBmgVD6tUJmWXh5K+Dp+RUGMUEJxdrdLpLHKYxkZjFwPI6Vm21YlKGkVaHoKqpaDRoxROcrVUXqVAADbbmcw3FcqbPAFrLLGLqYxmBmCunHo3BZv6giEdct7oXZGraOyat59J2OYit/KCX0p6ZEJ7gfQq+1HPrPxrCZPxDPrse71GEbdOkhjZXsTgvo9v6nfFgEt4Vcaw97PL5Mk6BUruD9tYhqTYXr8t4OB8YYdMMQ3tR0KY9OwE8/SHOWXcgNiAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;2018&quot;
        title=&quot;2018 Contributions&quot;
        src=&quot;/static/8371794e65a8ea0224df0ae07b98b9bb/88e66/github-contributions-2018.png&quot;
        srcset=&quot;/static/8371794e65a8ea0224df0ae07b98b9bb/0eb09/github-contributions-2018.png 500w,
/static/8371794e65a8ea0224df0ae07b98b9bb/88e66/github-contributions-2018.png 790w&quot;
        sizes=&quot;(max-width: 790px) 100vw, 790px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;2019 - the team grows&lt;/h2&gt;
&lt;p&gt;The exact proportion of commits but the number of reviews went above the number of pull requests opened.&lt;/p&gt;
&lt;p&gt;The same number of commits but fewer PRs means that my contributions were either smaller or required fewer iterations to be merged.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 806px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3688f05277fed26842ea7a37e82e857a/30577/github-contributions-2019.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.39999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAABYlAAAWJQFJUiTwAAABp0lEQVQ4y52US0sCURTH/TZuWgRh1K5a5Ko2tSipRUJg36BWBu2Egh5Qi4jatSooiNIkiaxFEJFCL/L9Gm3Gx/hgxjv3HzOpzJiO5oHDnXvmnh//c++514A2RilVXP2tnrczgx6wObkbaFugOkmsVpFh2T/xroFqJcVyCZFIHNFwAtFYAqIo/k8hVS0mVFLGdJmFK+RtuZ+dgU0wCgrrox32p51GXLfk5pOUTarB5HHZvwnj5SQ2/EcaYLNrFKoDdZhsK/4tDHnmMOi2wOE76KhQjmsUyqcpm0BErL7uwnRjgdm7hIHrGTheakCJtFRY5zT2kEln8BkIQqoQHL9fwHg2gRHnAkZdVvSfT8P+sI0cn0eMSyKRZRDnUuCKOaSYDGKJpBYoTwoFHlwuh1K5DAkU+8ETDHvmMXa3CJN7VlOyXt8a9E7sirnHuNeGPucU1n2HtZKlziW3+kkoURa88SGYb21Ye97rrm307nBV+oV+5aM4/XDVuuCfjd3yYaAAX+J7v8vqJEEQwWazCITC4ItFEEJ6e23qCZVKBd8sp7SV7IIg6AJ/ANIQF7s5HnbjAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;2019&quot;
        title=&quot;2019 Contributions&quot;
        src=&quot;/static/3688f05277fed26842ea7a37e82e857a/30577/github-contributions-2019.png&quot;
        srcset=&quot;/static/3688f05277fed26842ea7a37e82e857a/0eb09/github-contributions-2019.png 500w,
/static/3688f05277fed26842ea7a37e82e857a/30577/github-contributions-2019.png 806w&quot;
        sizes=&quot;(max-width: 806px) 100vw, 806px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;2020 - sharing ownership&lt;/h2&gt;
&lt;p&gt;More people joined the team. We had some huge and complex developments (the new availability and prices engine), so it was too much for me. I had to delegate responsibility. I was involved only in the most profound changes, not every single line of code that went into the system.&lt;/p&gt;
&lt;p&gt;That means fewer reviews, more commits.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 798px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/17c9e7a6dd26030b6995827df03fb121/50208/github-contributions-2020.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAABYlAAAWJQFJUiTwAAABgklEQVQ4y62UT06DQBTGuY2JF/AWxr3xAC68gEs3egJ3blwZly5qqgtjUqMLXZq4sNG20NoChSkDwzDMfKZEkFIo1PiSLzzCzG/ev0HDClNKZc8yv8y0KlAeUDyg6NcCi5tiKRFF4n8i9CiFMfyCboxAZl5p5GsBg4DBdhyMLROzvwKLKUdSwPTsZilXdRK5d8YZxq651PnKCFNYqlhJCBknUlBgUZgBpZJL6/OHaKvCT41HHCax0KQ8GXA8MUGIh4AztHsdnHevcPp2gZPXM+w/HOHu/REhD2FTBw4lcHwCnwVwHBce9X8jnDvU9zEwhsloeD5Fe9DBwfMxtm53sXG9jc3WDlrde8SRSEAk8BIxHmKaAOkiMKsBFtMfhRYujRvsPR3iQX9ZL+X8h1jFSfFT8wIK3R42a0pZl1PNN0sohBHH5KcpRchaNyXdxAXHZGY1m8Oq6PLAedOmxMWqbFJpdTPoEoJeX8fHZx+mZdfOrCaEQLViMBYmPwjqB4nq1n8DpROAGVONfyQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;2020&quot;
        title=&quot;2020 Contributions&quot;
        src=&quot;/static/17c9e7a6dd26030b6995827df03fb121/50208/github-contributions-2020.png&quot;
        srcset=&quot;/static/17c9e7a6dd26030b6995827df03fb121/0eb09/github-contributions-2020.png 500w,
/static/17c9e7a6dd26030b6995827df03fb121/50208/github-contributions-2020.png 798w&quot;
        sizes=&quot;(max-width: 798px) 100vw, 798px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;2021 - the year of the management&lt;/h2&gt;
&lt;p&gt;This is a depressing year hahaha&lt;/p&gt;
&lt;p&gt;87% of my contributions were related to… issues. I am a trouble maker now 😅&lt;/p&gt;
&lt;p&gt;Now seriously. We have evolved into Feature Teams. We gave full ownership of the services to the teams, so my time to thoroughly review code decreased again. My main concern is design and architectural decisions that our teams have to implement by themselves.&lt;/p&gt;
&lt;p&gt;We have been focused &lt;a href=&quot;/2021-05-25-about-observability&quot;&gt;on SRE too&lt;/a&gt;, mandatory to let the teams be fully independent.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 810px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ae414466099cbc2740c05c000798b6f4/249e2/github-contributions-2021.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAABYlAAAWJQFJUiTwAAABJElEQVQ4y61TXWuEMBD0//+rvvThHoTitb0e9I56PdrzK4kmJsLpFLc1qGgUaWAJJu5kZmfXwz8vb+6iaRq7j6N/vxrQlTR3voqhMQZSKQqlyu0Mu4Q8L/B1i3CLYjDGB2XYLLkwkmIJbAA4/rH/zaSgGJs1lee53Kyb+g+QI5O/cu91TedtTDnvLbnWrrLSqKoKa8pjGZKTUtHLncSWVaElgusBjxcfr9k7gviIfXJEEL1B6AKMCfuYZdi2Rvh5xfnjQsC5luBKUChTwg/3eAh3eEoO8KNn+PEL7d8sxukcIk5Th+QJ9aXZINka0zNojSkdAQs45fC4HfhM28y6vDgt2t3YqwDtlBSSih6lCY3h3CwPJDsBpQLnAkmagQsxCdhn+ANltjxrD5gotQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;2021&quot;
        title=&quot;2021 Contributions&quot;
        src=&quot;/static/ae414466099cbc2740c05c000798b6f4/249e2/github-contributions-2021.png&quot;
        srcset=&quot;/static/ae414466099cbc2740c05c000798b6f4/0eb09/github-contributions-2021.png 500w,
/static/ae414466099cbc2740c05c000798b6f4/249e2/github-contributions-2021.png 810w&quot;
        sizes=&quot;(max-width: 810px) 100vw, 810px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The path to observability]]></title><description><![CDATA[o11y = observability = logs, metrics, traces   This post is a summary of the steps we unconsciously followed in Playtomic when digging into…]]></description><link>https://sgmoratilla.com/2021-05-25-about-observability/</link><guid isPermaLink="false">https://sgmoratilla.com/2021-05-25-about-observability/</guid><pubDate>Tue, 25 May 2021 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;o11y = observability = logs, metrics, traces &lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1a35b32cd543b37555370c020e4de391/d43ae/o11y-general.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAACDElEQVQoz31SW1PaYBTkySnmBj6QfPeEMAgRUQyXBIkBAuGigoogKiMFgZm2tErp/59OpONjZ/ZhX3bPOXs2JGEmYcYrSER0xyNEjRB1x/+PkISZiOjheR0aWQnhKFF5BQoQBxaBEYtQNRrYBaZRokUD8ikmqgCQZrcO3V7cahi1vpZ35KTBA8QBykESlkFYQWGZ7cdgWAZ7Mg0rJLITRwjjATNNdGqq2TM9Y6ZTVoWYZaPUvLattpc+drxOtdN10xnHKtr+sHFm2al9EMwP1uYhcy+Y4xCzgI9OSDYXK+bx/Lbaf2w3+o+L+8vniXf7PFw93P2Y96arl8bdkIc0gllIQDSm6kWjkkqY5WOHxY+SyWy7UI0nNYHqItI4GXCQcFDnZCQi8EVhHKC7RIPJEqJES+jxlJ2xSNzQE9kDrO/FkAAwh7CAqADJRwSYB4RXIA8xD/DHzVRLn3vpC99wu4Z57DpKzYWlEvOurmr1duu0Wit7Fb9zeTfsjcb17nXn9v5yMLS9poho6IDFF+u34XQx+rpab97fNvOHSX86H6+3m5/bP8uX5Wy+/L79/TR7HU9n3369T16XejZH0xkRs0DcG437o7Hfu3H8TuGqmevWT1puruVYg2b+xrMGvj3wE8UzXsZRqomIcgr6t7aSOAyKAYkQAAsQi5AEQFSEVEJMQkwA5LNVu/7t/vwXiKlvRShxFZkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;o11y according to Ted Young&quot;
        title=&quot;o11y according to Ted Young&quot;
        src=&quot;/static/1a35b32cd543b37555370c020e4de391/f97d7/o11y-general.png&quot;
        srcset=&quot;/static/1a35b32cd543b37555370c020e4de391/0eb09/o11y-general.png 500w,
/static/1a35b32cd543b37555370c020e4de391/1263b/o11y-general.png 1000w,
/static/1a35b32cd543b37555370c020e4de391/f97d7/o11y-general.png 2000w,
/static/1a35b32cd543b37555370c020e4de391/d43ae/o11y-general.png 2047w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This post is a summary of the steps we unconsciously followed in Playtomic when digging into the world of SRE (Site Reliability Engineering).&lt;/p&gt;
&lt;h1&gt;Logs&lt;/h1&gt;
&lt;p&gt;Most of the people is already comfortable with logs.
I have always been a fan of the 12-factors app design, and they &lt;a href=&quot;https://12factor.net/logs&quot;&gt;have a chapter about logs&lt;/a&gt;. Just throw them to the stdout
and treat them as a stream. As our services run in docker containers, we just read the logs from docker. With logstash+filebeat you can massage and forward them somewhere else (an elasticsearch, logz, datadog, …)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;anemone_configuration.1.ip.eu-central-1.compute.internal    | 2021-05-25 08:44:44.080  INFO [configuration-service,65360f43df5797ee,65360f43df5797ee,3187176603182004136,7845016010104016623,767246] 1 --- [  XNIO-1 task-5] c.p.a.s.IgnorableRequestLoggingFilter    : After request [GET uri=/v2/status/version_control?app_name=playtomic&amp;amp;app_version=3.13.0&amp;amp;device_model=iPhone&amp;amp;os_version=14.5.1&amp;amp;platform=ios;user=one.user@gmail.com;agent=iOS 14.5.1;ms=1]
anemone_configuration.1.ip.eu-central-1.compute.internal    | 2021-05-25 08:44:44.087  INFO [configuration-service,5fe21e342fb8cac9,5fe21e342fb8cac9,4868272858418826990,131548911514375389,767246] 1 --- [  XNIO-1 task-6] c.p.a.s.IgnorableRequestLoggingFilter    : After request [GET uri=/v2/configuration;user=one.user@gmail.com;agent=iOS 14.5.1;ms=8]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1&gt;Metrics&lt;/h1&gt;
&lt;p&gt;Metrics are also quite known in the community, thanks to prometheus and grafana. What is most unknown and super useful is to have them correlated somehow.
Probably via tags/attributes to identify hosts, containers, environments, …&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1624px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/59834a538dde874a08068b66975421b7/fc0cd/o11y-grafana.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 38.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAABVklEQVQoz22RaW7bMBhEfRDb4r5KpKR4iyLXDXL/M72CsrugyI/BPH7LkAB3/qiwe7nJ/edxL7nGwmWYOfcjoVO4g8QfFH5z+Ty3nYOiP0h241i5zZXrVLg2/0etvl7euN9OvJ/eGHOlpETtE3VIjCUx1sxUM0NJFGHYhZDIPpCdJ/pA8oH44iZvHcFaokvcpw/KGAkxEILHO0dwTzfWMXSanbYRKQxKaIQ0SGnpGosnN29nYxLnPOGTR3StZ/7MND5KQ3/U7IyLaGkwLUxZ1DeSUpPzTHAJbd23M0JZ+qNqLwzoVuzUFqi1Qwi93fqbm5xLr1fprb5xp56B0tBJQ26fEvNAij3GOHyI5FwwxuN92rgtN+5f7FykDCPWhk21zhjt/gaebwvr+mBZVuY6sSx3Ho9PpvnC6fzO/f5gXX8w1vrs/fxiqhMxZEqZ6PuCFHoLjHvBL7Wk6rOrxN8hAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Grafana Memory Chart&quot;
        title=&quot;Grafana Memory&quot;
        src=&quot;/static/59834a538dde874a08068b66975421b7/fc0cd/o11y-grafana.png&quot;
        srcset=&quot;/static/59834a538dde874a08068b66975421b7/0eb09/o11y-grafana.png 500w,
/static/59834a538dde874a08068b66975421b7/1263b/o11y-grafana.png 1000w,
/static/59834a538dde874a08068b66975421b7/fc0cd/o11y-grafana.png 1624w&quot;
        sizes=&quot;(max-width: 1624px) 100vw, 1624px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;Traces&lt;/h1&gt;
&lt;p&gt;Traces are probably the newest in the trio.&lt;/p&gt;
&lt;p&gt;In Playtomic, we followed the standard path: we started with in-house ELK (for logs) and prometheus (+ grafana). But we (developers) hate maintaining the infra: we run out of space, indexes get corrupted, … it is quite distracting. Therefore, we got rid of these in-house versions and moved those services to Datadog.&lt;/p&gt;
&lt;p&gt;At that moment, we were already using distributed tracing to inject the trace and spain ids in our logs, so we can keep track of the requests among our microservices. So, with the Tracer already in place and the use of Datadog, the next natural step was using Trace to know more about our platform.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/83a95feb5999e3f95973e75e949fcffa/3a775/o11y-traces.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 68.80000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABYlAAAWJQFJUiTwAAACm0lEQVQ4y3WSTU/UUBSG+8P8BbpDxIUr40Z3stFoNKIIAuoCohGNihjUaGSjLExYuBQTwoDyJQIztJ0pM9OZ6fd02t4+5nbGEURv8vT0vPfc2573XuWHsc1KYZ3l/BpLO9/5Vthgo/iTNX0r0/M1lVxjgy/mMkv1NYygQqVpUm6aVMMaRb/GVqPCjl3F8Kso5UZA3QkJwgQviAmjlFYEzZZoxyhFjWpsBkX2WiZHRtrG8ROCUKBs5b5S3FymupWjtr1C9ccSdn4V8+cyVn410/3CJs72WhZlbmXzObz9Aq7r4gUBlbqFaXso+1PXUO+fpfTwAsXxc6gjpykM9aAOn8zQR06h3enNojrcy+6tHrZv9rBz/QT63FNMN8RqNHBcl4rto6RljYW375i49oDHw9M8vj3F5I1HTFx9wL1L4wz1jzHUP8rgxVHuXrrHq/FnzD2fwVr8THl3nV1jH8e2qTcs6paDIm3w/YTJdyv0DUxz+uZzzgy+4fjlJwy8+MD8gsb8osGbyW/Mf9zFFRCkbfuCpqBULGO7Dpbt4HkeikiSrr+fvr/m6mwv518e48r7PmYWhvjnKaSCVCSkiUAkoiOnpGmKIh9CyKSt66bB2OwUVbt9okmSksiFsq6zSJLtK9/p5J2hdL+bFYps4zjuXIeufpSDcxzQlSiKiKL4gChbEIeK/zcObt79Q8dv0my1ft/PDCH+tCiRLcdxTJIkGdJ3WdPNRdtLiRJbDqFeoqmVaOolAlUnrpjE5SrxfoVExnIVPJ/U9TKEI3EPY7sIP0CJLBtD09EKe2gFFTW/h1E0KGo6akFFV3VKegnHdnAdNyOSHck/izskf6LiuT55o4FWrmM57fskaVh2G9vG833CsNVF+i4t+BvZ/i8APQdWCJ0z3AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Datadog Traces&quot;
        title=&quot;Traces&quot;
        src=&quot;/static/83a95feb5999e3f95973e75e949fcffa/f97d7/o11y-traces.png&quot;
        srcset=&quot;/static/83a95feb5999e3f95973e75e949fcffa/0eb09/o11y-traces.png 500w,
/static/83a95feb5999e3f95973e75e949fcffa/1263b/o11y-traces.png 1000w,
/static/83a95feb5999e3f95973e75e949fcffa/f97d7/o11y-traces.png 2000w,
/static/83a95feb5999e3f95973e75e949fcffa/3a775/o11y-traces.png 2004w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;What’s next?&lt;/h1&gt;
&lt;p&gt;This SRE world is huge. Where are we heading to?&lt;/p&gt;
&lt;h2&gt;SLIs, SLOs, SLAs&lt;/h2&gt;
&lt;p&gt;Once you have your data (specially metrics), you can monitor your system. Welcome to the world of SLIs, SLOs, and SLAs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SLI: Indicator (Service Level): the number (for the tech team). Example: error_rate = % of requests with code &gt;= 500.&lt;/li&gt;
&lt;li&gt;SLO: Objective (Service Level): the target (for the tech team). Example: keep error_rate &amp;#x3C; 99%&lt;/li&gt;
&lt;li&gt;SLA: Agreement (Service Level): the agreement (with the customer). Example: keep error_rate &amp;#x3C; 95%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SLOs are great because it gives you a hint when to spend more time in stability (paying technical debt, investing time researching problems, …).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4711cbd507769eac26d14d4578320db0/8c899/o11y-monitor.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.2%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2ElEQVQY05WKSW7DMBAE9f8H5hQgBgLDsi1TIiluwyHLsJTFCHJIDoXurpnBzAvj5cq8WFSVDvTe/0XrHRHZGDbZ2hP6Y++O1sgx4awjrCtdlaaKVoWuvJ4sb2fPoCLUq0Gnv/H5Wy7fvT32OFEWz9BqgeUE9vxF/6U/O/yZ4/FINiM6j1tWc4JoGYoUXHAUScQccTFQZM+QA7nsXSRh48rk/dYPN8vhtrCmwMtoeJ8t9MogVTFrJmTBJcGETCyVJRSWsPv5I30SJp+w249s94e7+szNRbQ17mq20UPlR4GBAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Datadog Monitor&quot;
        title=&quot;Monitor&quot;
        src=&quot;/static/4711cbd507769eac26d14d4578320db0/f97d7/o11y-monitor.png&quot;
        srcset=&quot;/static/4711cbd507769eac26d14d4578320db0/0eb09/o11y-monitor.png 500w,
/static/4711cbd507769eac26d14d4578320db0/1263b/o11y-monitor.png 1000w,
/static/4711cbd507769eac26d14d4578320db0/f97d7/o11y-monitor.png 2000w,
/static/4711cbd507769eac26d14d4578320db0/8c899/o11y-monitor.png 2240w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;RUM: Real User Monitoring&lt;/h2&gt;
&lt;p&gt;One step further: what if you could connect traces in your clients with your traces in your server? That’s where we are at this moment.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;Image: Ted Young &lt;a href=&quot;https://twitter.com/tedsuo&quot;&gt;https://twitter.com/tedsuo&lt;/a&gt;&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Money operations are not as easy as you think]]></title><description><![CDATA[I see this a lot in Stackoverflow and most of the replies always makes me wonder what
kind of software is running over there. People ask…]]></description><link>https://sgmoratilla.com/2021-05-10-currency-is-not-easy/</link><guid isPermaLink="false">https://sgmoratilla.com/2021-05-10-currency-is-not-easy/</guid><pubDate>Mon, 10 May 2021 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I see this a lot in Stackoverflow and most of the replies always makes me wonder what
kind of software is running over there. People ask about operations with currencies (that is, money) and they usually get replies about using doubles and then rounding.&lt;/p&gt;
&lt;p&gt;Big no! Floats and doubles don’t represent exact numbers. They represent a number at certain precision. 101 in any coding course. If your application handles prices (and its probable if you are charging for some service), you can’t use float/double.
Your language probably have a way to represent exact numbers. In Java we have BigDecimal and… Money!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Money price = Money.of(10.50, &quot;EUR&quot;);
MonetaryOperator roundingOperator = MonetaryOperators.rounding(RoundingMode.HALF_UP);
Money newPrice = price.multiply(61).divide(3).with(roundingOperator);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You will be thinking: why so much trouble to do a simple &lt;code class=&quot;language-text&quot;&gt;ceil(10.50 * 61 / 3)&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;What happens to decimals? What happens to cents? Do you know that there are currencies that don’t have the equivalent to cents (for example, the yen)?&lt;/p&gt;
&lt;p&gt;There are a lot of subtleties you just don’t know when handling money (as happens with &lt;a href=&quot;/2019-12-20-testing-code-based-on-time-clock-provider&quot;&gt;operations with time!&lt;/a&gt;). I don’t know you, but if some people have already taken their time figuring all those things out… I feel safer using their knowledge!&lt;/p&gt;
&lt;p&gt;If your language does not have something similar, then… you’ll have to stick to integers and handle with care how many decimals your currencies need. For example, if you want to represent 15.53€, you can handle it as an integer 1553, but you will have to think what happens with rounding in your use cases.&lt;/p&gt;
&lt;p&gt;PS: Of course, if you are performing simulations (for example, Montecarlo) or other mathematics stuff, although they represent money, you won’t probably need to apply any of the things I said before. You are already dealing with other precision problems! :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Don't use default values in your database]]></title><description><![CDATA[This is a bad practice in my opinion if your database is owned by one application (or service).
It is the usual scenario when using…]]></description><link>https://sgmoratilla.com/2021-04-12-no-default-values-in-database/</link><guid isPermaLink="false">https://sgmoratilla.com/2021-04-12-no-default-values-in-database/</guid><pubDate>Mon, 12 Apr 2021 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a bad practice in my opinion if your database is owned by one application (or service).
It is the usual scenario when using microservices: your service is the only owner of that database.
Of course, it might not be your use case.&lt;/p&gt;
&lt;p&gt;If several services are writing to the same database, then I can see some good examples where you would want default values.
For example, if you don’t have control on the queries that are already writing to it.&lt;/p&gt;
&lt;p&gt;Why shouldn’t you use default values in your schema?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Default values are usually defined in your code too. Now you would have these defaults spread in your code and your database.&lt;/li&gt;
&lt;li&gt;Default values probably depend on your application flow. When the default is in the scheme, you are probably thinking of one single scenario.&lt;/li&gt;
&lt;li&gt;It won’t help you to maintain your code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When schemas evolve, it is super common to add a not null column. The common practice is to add the column with a default value, which will fill that new column with the default value. But the unexpected behaviour is that don’t have to write that value in your INSERTs anymore. And it is not probably what you wanted.&lt;/p&gt;
&lt;p&gt;For example, let’s say we have a table for users and we are writing a functionality for a new type of user. We would probably do something like this
in our database:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ALTER TABLE user ADD user_type ENUM(&apos;PLAYER&apos;, &apos;COACH&apos;) NOT NULL DEFAULT &apos;PLAYER&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It adds the &lt;code class=&quot;language-text&quot;&gt;user_type&lt;/code&gt; column and set all the old values to &lt;code class=&quot;language-text&quot;&gt;&apos;PLAYER&apos;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The robust way to do that is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ALTER TABLE user ADD user_type ENUM(&apos;PLAYER&apos;, &apos;COACH&apos;) NOT NULL DEFAULT &apos;PLAYER&apos;;
UPDATE user SET user_type=&apos;PLAYER&apos; where user_type is null;
ALTER TABLE user MODIFY user_type ENUM(&apos;PLAYER&apos;, &apos;COACH&apos;) NOT NULL;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That is, add the column as nullable, set the value, set the not null.&lt;/p&gt;
&lt;p&gt;In case that you are altering your database while your code is running (for example, in a high availability environment), your code have to support writing the new column but probably reading null values until all the SQL updates are completed.&lt;/p&gt;
&lt;p&gt;That forces you to do two deployments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First deploy to add the nullable column. New code starts writing the default value but supports to read a null.&lt;/li&gt;
&lt;li&gt;Second deploy to update the value in database. After that, the code can assume the value is not null anymore.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;More work, I know, but this is the cost of making your database more manageable in the future!&lt;/p&gt;
&lt;p&gt;PS: sorry for the typos in advance, I wrote this in 10 minutes!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Changing the log format in Spring Boot]]></title><description><![CDATA[Do you know that hours of trial and error can save you minutes of reading documentation? Well, that was not
the case today because I wasn’t…]]></description><link>https://sgmoratilla.com/2021-03-12-changing-the-log-format-in-spring-boot/</link><guid isPermaLink="false">https://sgmoratilla.com/2021-03-12-changing-the-log-format-in-spring-boot/</guid><pubDate>Fri, 12 Mar 2021 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Do you know that hours of trial and error can save you minutes of reading documentation? Well, that was not
the case today because I wasn’t able to find that (although it is).&lt;/p&gt;
&lt;p&gt;The Internet is full of (crappy) pages explaining how to change log format replacing the original files (logback-spring.xml, …) but we
didn’t want to change and write ALL the configuration. We just wanted to add a field to the log.&lt;/p&gt;
&lt;p&gt;You can find that param &lt;a href=&quot;https://docs.spring.io/spring-boot/docs/2.1.13.RELEASE/reference/html/boot-features-logging.html&quot;&gt;on the official documentation&lt;/a&gt;, but it is easily missed.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;application.yml&quot;&gt;&lt;pre class=&quot;language-application.yml&quot;&gt;&lt;code class=&quot;language-application.yml&quot;&gt;logging.pattern.level: &amp;lt;your marvelous pattern&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Do you know how I found that? I had to browse &lt;a href=&quot;https://github.com/spring-cloud/spring-cloud-sleuth/blob/master/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceEnvironmentPostProcessor.java&quot;&gt;Sleuth code to know how they change it&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		Map&amp;lt;String, Object&gt; map = new HashMap&amp;lt;String, Object&gt;();
		// This doesn&apos;t work with all logging systems but it&apos;s a useful default so you see
		// traces in logs without having to configure it.
		if (Boolean.parseBoolean(environment.getProperty(&quot;spring.sleuth.enabled&quot;, &quot;true&quot;))) {
			map.put(&quot;logging.pattern.level&quot;,
					&quot;%5p [${spring.zipkin.service.name:&quot; + &quot;${spring.application.name:}},%X{traceId:-},%X{spanId:-}]&quot;);
		}
		addOrReplace(environment.getPropertySources(), map);
	}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So easy once you know it.&lt;/p&gt;
&lt;p&gt;Ours now is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-},%X{user_id}]&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;user_id is a variable set in the &lt;a href=&quot;http://logback.qos.ch/manual/mdc.html&quot;&gt;MDC&lt;/a&gt; on every request.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Optional dependencies in Rust]]></title><description><![CDATA[Yes, I’m learning a new language. A typed one =D
I love C++. Some of my best code and all my research was done in C++. But I hate its…]]></description><link>https://sgmoratilla.com/2020-11-22-how-to-use-an-optional-dependency-in-rust/</link><guid isPermaLink="false">https://sgmoratilla.com/2020-11-22-how-to-use-an-optional-dependency-in-rust/</guid><pubDate>Sun, 22 Nov 2020 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yes, I’m learning a new language. A &lt;em&gt;typed&lt;/em&gt; one =D
I love C++. Some of my best code and all my research was done in C++. But I hate its dependency system. I don’t want to deal
with them anymore. So here it is Rust, that feels as an appointed heir.&lt;/p&gt;
&lt;p&gt;I don’t want to extend this post more than necessary, so I will get to the point.
Rust can defined &lt;em&gt;dependencies&lt;/em&gt;. Dependencies can be optional.
Besides, we can optionally include dependencies or blocks of code using &lt;em&gt;features&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To define a &lt;em&gt;feature&lt;/em&gt;, you define it in your &lt;em&gt;Cargo.toml&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;t&quot;&gt;&lt;pre class=&quot;language-t&quot;&gt;&lt;code class=&quot;language-t&quot;&gt;[dependencies]
rand = &amp;quot;^0.7.3&amp;quot;
ndarray = &amp;quot;^0.13.1&amp;quot;
log = &amp;quot;^0.4.11&amp;quot;
image = &amp;quot;^0.23.11&amp;quot;
imageproc = &amp;quot;^0.21.0&amp;quot;
sdl2 = {version = &amp;quot;^0.34.3&amp;quot;, optional = true}

[features]
default = [&amp;quot;display-window&amp;quot;]
display-window = [&amp;quot;sdl2&amp;quot;, &amp;quot;imageproc/display-window&amp;quot;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are here declaring a &lt;em&gt;feature&lt;/em&gt; called &lt;em&gt;display-window&lt;/em&gt; that includes the dependency sdl and the crate imageproc/display-window.&lt;/p&gt;
&lt;p&gt;To activate that feature, you have to add a cfg macro in your .rs source.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;rust&quot;&gt;&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg(feature = &lt;span class=&quot;token string&quot;&gt;&quot;display-window&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;imageproc&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;display_image&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;display_image&lt;/span&gt;&lt;span class=&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; canvas &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 keyword&quot;&gt;let&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2048&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; height &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2048&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;imageproc&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;display_image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; width&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; height&lt;span class=&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;/div&gt;
&lt;p&gt;That code makes the feature &lt;em&gt;display-window&lt;/em&gt; active, which enables us to use the &lt;em&gt;imageproc::window&lt;/em&gt; space.
We don’t see that in this code but &lt;em&gt;imageproc::window&lt;/em&gt; requires sdl2 to work, and that’s the reason because we included the sdl2 crate in &lt;em&gt;Cargo.toml&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Profit!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Teaching a computer to draw]]></title><description><![CDATA[There must be hundreds of artists trying to mix art and computing. I have always wanted to try to perform some experiments on this topic.
It…]]></description><link>https://sgmoratilla.com/2020-06-28-teaching-a-computer-to-draw/</link><guid isPermaLink="false">https://sgmoratilla.com/2020-06-28-teaching-a-computer-to-draw/</guid><pubDate>Sun, 28 Jun 2020 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There must be hundreds of artists trying to mix art and computing. I have always wanted to try to perform some experiments on this topic.
It is interesting computationally speaking (there are tons of math and engineering involved), visually (computed generated art is usually impressive) and conceptually (is it actually art? how we define art?).&lt;/p&gt;
&lt;p&gt;This &lt;a href=&quot;https://twitter.com/anastasiaopara&quot;&gt;@anastasiaopara&lt;/a&gt;’s &lt;a href=&quot;https://twitter.com/anastasiaopara/status/1268874763956563971&quot;&gt;tweet&lt;/a&gt; has inspired me to write this post. It is a project to make computer draw images. She has published her code so that we all can play around with it. I wanted to understand how it works and try my own ideas, so that I ended forking and refactoring her code &lt;a href=&quot;https://github.com/sgmoratilla/genetic-drawing&quot;&gt;https://github.com/sgmoratilla/genetic-drawing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is not an essay about genetic algorithms or function optimizations. There is a lot to know about these subjects.
You can find several &lt;em&gt;introductions to genetic algorithms&lt;/em&gt; on the Internet. If you want my advice, read some of them
and jump to scholar papers. For example, &lt;a href=&quot;https://repositorio.uam.es/bitstream/handle/10486/9555/49799_Ruiz%20Ruben.pdf?sequence=1&quot;&gt;this thesis of my former investigation partner Rubén&lt;/a&gt; (&lt;a href=&quot;/b95dd262e7258e6434c1ad40dfc91559/2012_thesis_cardinality_constraints_ruben_torrubiano.pdf&quot;&gt;backup here&lt;/a&gt;).
Part I is about Optimization, Section 2.3.2 about Genetic Algorithms.&lt;/p&gt;
&lt;p&gt;Anyway, if you don’t understand something about what I wrote here, feel free to ask: &lt;a href=&quot;https://twitter.com/sgmoratilla&quot;&gt;@sgmoratilla&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;##What are genetic algorithms?
To be brief, &lt;strong&gt;optimizations are search problems&lt;/strong&gt;. You have a space of possible solutions, a function whose maximum (or minimum) you want to know.
Sometimes you are lucky and your target function has nice properties as being differentiable and smooth, so that you can use some traditional approaches as gradient descent or Newton’s methods. The most interesting problems I’ve worked on weren’t differentiable or their solutions must have to fulfill some restrictions. That’s were genetic and evolutionary (they are not the same) algorithms shine.&lt;/p&gt;
&lt;p&gt;##Can a computer paint?
&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1067px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/fe95ead391cb011daf5a0fd386e7e8c9/40547/genetic-algorithm.sam.final.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 150%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAIAAACjcKk8AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFhElEQVQ4yz2V22vT/hvHc9GapudlaZJPc+5h69ou5yY9JG3Dsimb7eYJp2KpFJHhwCMoA2Vl7EJQUBQGvRBEdiU4ejPUC291f4BXgoKX/hU/2o+/7+sihOR5P8/zeT+ffIIUCoVisVgulxVF0aaoqmoYRqVSsW0bXm3btiyrUqlYlgVjFEUplUrI4uKiMsU0TagxDENVVZ7nU6kUTdPlcrk+5b8shmGYpinLMiLLsqIoqqrqum6aZr1eVxSF47hUKpVMJmOxmKIo8/Pzuq7X6/VarVav160puq4jUKbrumVZ8IUgCKIolkolRVE2NjbG4/Hdu3fn5uY8z3Nd13GcarVaqVQmYk3TqlNqtZppmul0miRJlmUzmYymaUdHR3/+/BmNRoZhOI7TarWazSZswbZtxDRNy7IajYZt2wCAWCw2Ozsry3I+n3/y5Mnfv3+Pj4+fPn2az+dN0/Q8r9lsuq77Twz9dBxH1/VkMhmNRmdmZk6fPr26uvr27dvfv39//Pjx5OSk0+lkMhnf92Fx2DzSmOK6rqZpiUQCRdF4PA6Lj0ajr1+/fvny5fj42PM8QRCWl5c9z2u1WlCC1Go1x3GazWalUolEIoFAAMOwQCAQDAY9zzs6Ojo5Oen1esFgkOM4y7KazSZ0biJut9uwjXK5jKJocAqCTKZw7tw5x3EePXrU6XQCgUAqlRJFUVEUx3Fc1200GhMxvGMYBkXRU6dOoSg6MzPT7/cfPnzYbrfj8TiGYfAhRVGSJNm23Wq1JpXhmi3L4jguEAiEw+FcLnf58uXr16/3er2XL18OBgNZlnEcT0whSdI0zX9tu65br9c1TeN5XhCERqPheV6v1xsOh6PR6PDw8N69e4PBQNd1giBmZ2cxDBMEoVqtOo6DOI4DN939+/dv3boFaw4Gg0+fPn3//v3Dhw83btzo9XorKys8z+M4jqIoRVG6rk9GZRhGsVh8/Pjxt2/fNjc319fX+/3+pUuXxuPxr1+/Dg8PL1y4sL6+7jgOACAUCgWDwXQ6rWmaYRj/xBcvXtzf3/d9f3l5eXNzs9PpvHnz5ufPn+/evTszRdd1nufj8TiKogAAWZZVVZ3sbVmWS6XS0tJSrVZrtVqrq6vdbndnZ+fHjx/v37+HYsuy/hMTBFEsFjVNm3zPCwsLkiQVi0VFUWzbXllZOXv27JUrV8bj8cHBQafT8X1/cXGRoqhIJIKiKI7j8PBAisViqVQSpszPz6uqura2trGxsbS0tLu7u7e35/t+rVZbWFigaToajUYiEQAAPHyQhSnZbDaXy0mSJMuy53lra2uqqm5tbT1//lxV1VKpVCgUUqlUNBoNh8M0TRcKhXw+j8zNzeXzeVEU0+k0RVGlUqnyf65du/bq1aszZ864rguVyWQSwzCGYfL5fDabRXK5XCaTEQQhk8kQBMEwTLlcVlV1MBjcuXPn9evXg8Hg9u3bW1tboVAIwzCSJEVRhPGIJEmCILAs67puu90OhUI4jm9vb+/s7Lx48WI4HO7v7+/t7X3+/Lnf75umCd2FIIIg8FNyudz58+e73W61Wh0Oh7u7u8+ePfN9v9vt3rx588GDBwcHB1evXmUYRhTFbDY7qQyVHMcxDAMA8H1/e3u72+2KoshxHAAgkUhEo1GKogRBSCQSAACe50VRnIgFQZAkCaYgSRLDMAAAjuPhcBjHcZiXIAgcx2OxWCKRgG6JosjzPAInDD0jCCKRSIRCoWg0SpIkNYXneegLAIAkSViZZdl/a4YwDAMFNE3DCEmSaJpmGAb2BXMBABiGgf1ORpXNZnmeZxiGoiiCIAAAgiCk02mWZdNTaJpmWRYAQNM0SZIcx0FHEGg6OwX+YuBxA0PT6TRMCgBIpVLkFJZlof5/mTZ/bnxqsNMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Genetic Algorithm - Final&quot; title=&quot;Genetic Algorithm - Final&quot; src=&quot;/static/fe95ead391cb011daf5a0fd386e7e8c9/40547/genetic-algorithm.sam.final.png&quot; srcset=&quot;/static/fe95ead391cb011daf5a0fd386e7e8c9/0eb09/genetic-algorithm.sam.final.png 500w,
/static/fe95ead391cb011daf5a0fd386e7e8c9/1263b/genetic-algorithm.sam.final.png 1000w,
/static/fe95ead391cb011daf5a0fd386e7e8c9/40547/genetic-algorithm.sam.final.png 1067w&quot; sizes=&quot;(max-width: 1067px) 100vw, 1067px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So.. let’s say we want to solve the “problem of painting”.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is a painting?&lt;/strong&gt;&lt;br&gt;
It is a succession of brush strokes on a canvas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How can we model that into a computer?&lt;/strong&gt;&lt;br&gt;
Let’s say a stroke starts are some point of the canvas (x,y) and goes in some direction.&lt;br&gt;
Painters are able to make a infinite number of strokes depending on how they spin their hands or how much pressure they apply. We could probably model that too, but keep it simple and let’s say that we give to the computer a list of brushes that it can use (brushes as in Photoshop). Painters mix different colors as well. We could model that too, but we are going to simplify assuming only black and white paintings.&lt;/p&gt;
&lt;p&gt;In summary, we can model a stroke as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2 integer numbers x,y that represents where the stroke starts.&lt;/li&gt;
&lt;li&gt;1 integer number that represent which brush to use.&lt;/li&gt;
&lt;li&gt;1 float number that represent the rotation of tha brush.&lt;/li&gt;
&lt;li&gt;1 float number that represent the size of the brush in % of the original brush.&lt;/li&gt;
&lt;li&gt;1 integer number that represent the color (0 = black, 255 = white, any number in between is a scale of grey).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can call that set of 6 numbers a gene. A painting is just a collection of these genes applied in order. For example, let’s say
we can draw a painting with 1000 strokes. A solution is a collection of 1000 genes, that is, a collection of 6000 numbers. This is called the genotype in Genetic Algorithms frameworks.
&lt;strong&gt;Isn’t it magical how can we code the process of painting with these simple numbers?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;How good is this solution of 6000 numbers? How can we measure it? We can apply these strokes on an empty canvas. This is our painting. The fenotype. Then we subtract the original image from this painting (pixel by pixel). The sum of the differences represents how different it is from the original one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our aim is to minimize that number&lt;/strong&gt;. How can we do that using a genetic algorithm?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generate a random population of possible solutions.&lt;/li&gt;
&lt;li&gt;Create a children population by mixing those candidates.&lt;/li&gt;
&lt;li&gt;Alter randomly some of the genes.&lt;/li&gt;
&lt;li&gt;Evaluate how good these solutions are.&lt;/li&gt;
&lt;li&gt;Discard the worse ones and keep the best ones.&lt;/li&gt;
&lt;li&gt;Repeat several times.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At some point, we stop and choose the best solution. That’s our final painting.&lt;/p&gt;
&lt;p&gt;This is how it works on one photo of my own:
&lt;img alt=&quot;Genetic Algorithm - Example&quot; src=&quot;/54829a1159f97f64cf3b30c1b4023299/genetic-algorithm.sam.gif&quot;&gt;
&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 525px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/4c421d6582b22d04e4965782a5d05797/cc15a/genetic-algorithm.sam.original.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 150%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAAeABQDASIAAhEBAxEB/8QAGgAAAgIDAAAAAAAAAAAAAAAAAAUHCAQGCf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeWFl4Sm0g4wwXWgoysNoFwf/8QAHhAAAgICAgMAAAAAAAAAAAAABAUCBgEDBxQSISL/2gAIAQEAAQUCMIiDg2gbENGmNjyLqRT2HK1dKrKxad2wlRJeDL6otD5WFskML2ihdss+oS+f/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwEf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAKBAAAgIBAwMDBAMAAAAAAAAAAQMCBBEABSESEyIUMTIGM2FxI1GB/9oACAEBAAY/Aq6R0h9ufbVKZjGC+REsnKfiMGQ+XiPlLxGtg+qrW4Wbdnfb0Q6qTWbV2+ncDl7fEviYuNtzkd2cFxmhaGw/Ez7a23cG2UVtqk25WgA1ZuNnV7UrhWoCZVGMGQEWvAEp8LhPEpDZNpXvbJU9sr191q7TH+WhW9VFqoOoMlE2lltcBrlFhRJ0ptWtJyNV3s+coeX5MTjP+40rspe5TbK68Yr8h6uyBBMCB9vvkdMWTwszAh1A8aqzjCF65tf0/Sr7hUS2HrIQqtZCHZV9x/RSYizJfDSrvuAK1SwpOBmI5wcjJJJ55z+xx/WosqWrFNh6oFtZhXMwkMSgSPeEs8g55xIeQB1yWmeCA3vN7xBxEibOvqkOTwSRjx9uND9a/8QAHBABAQEBAQEAAwAAAAAAAAAAAREhADFRQWHB/9oACAEBAAE/ISpCaHtAAgddeorFSSx4K4PdS8rH8nbrhpiYhVhheBYLNVNrQbCMrNEittR37+Td3eUQatkpFQbbajm2zXAQACGBk/48qv5SAI9eivcToU1U+nkJhgEAptCHuACBYG5Y6Zf9Pnzv/9oADAMBAAIAAwAAABBDyAz/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAcEAEAAwADAQEAAAAAAAAAAAABABEhMUFREPD/2gAIAQEAAT8QJkMglYSg2Ao1bdpEahHGAGPlwlHZ7+3e4NQ4bp9IM++M7KDkXvBhr9UooWkTAzFKiEPpqNRsz5Lm1QYAzvGir5rATyICh2zkNEoa4XYtK1F5mYiVkR9mtbhVR+l3gxUpgvNcpVLpaFYeHU//2Q==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Genetic Algorithm - Original&quot; title=&quot;Genetic Algorithm - Original&quot; src=&quot;/static/4c421d6582b22d04e4965782a5d05797/cc15a/genetic-algorithm.sam.original.jpg&quot; srcset=&quot;/static/4c421d6582b22d04e4965782a5d05797/953fe/genetic-algorithm.sam.original.jpg 500w,
/static/4c421d6582b22d04e4965782a5d05797/cc15a/genetic-algorithm.sam.original.jpg 525w&quot; sizes=&quot;(max-width: 525px) 100vw, 525px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;##But… is it actually learning?&lt;/p&gt;
&lt;p&gt;Sorry for the catchy headline. We are not actually &lt;em&gt;teaching&lt;/em&gt; a computer to draw. This algorithm does not learn anything O:)
If you think of the algorithm, we have actually programmed how to paint by trial and error given some image as inspiration.&lt;/p&gt;
&lt;p&gt;There are thousands of possible optimizations. For example, use fewer strokes (faster iterations) and resetting the algorithm using the last image
as seed of the next iteration (a greedy approach).&lt;br&gt;
We are not discussing them here, you can see some of them &lt;a href=&quot;https://github.com/sgmoratilla/genetic-drawing&quot;&gt;in my repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I know there are several algorithms to transfer style. You can teach a neural network to copy the style of a painter (given a list of painting with the same style) and &lt;em&gt;transfer&lt;/em&gt; that style to a image or photo. In my opinion and from an &lt;em&gt;artistic/romantic&lt;/em&gt; point of view, I feel that is not painting but applying a filter to the image.&lt;/p&gt;
&lt;p&gt;I am not promissing anything  but I have plans to write more about this subject soon.&lt;/p&gt;
&lt;p&gt;##Links
&lt;a href=&quot;https://github.com/sgmoratilla/genetic-drawing&quot;&gt;Github Project&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/anopara/genetic-drawing&quot;&gt;Anastasia’s Github&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What I see in Github contributions]]></title><description><![CDATA[I have seen several people sharing their contribution graph in Github.
Some of them, bragging about how dark and green their graph is. A few…]]></description><link>https://sgmoratilla.com/2020-06-17-github-contributions/</link><guid isPermaLink="false">https://sgmoratilla.com/2020-06-17-github-contributions/</guid><pubDate>Mon, 15 Jun 2020 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have seen several people sharing their contribution graph in Github.
Some of them, bragging about how dark and green their graph is. A few were very proud of them not having any blank square.
I am not happy with that kind implications: more hours, no rest days, … They don’t mean you are working harder. Just mean that
you are working more hours. Besides, more contributions to a repo don’t mean that they are better. Just that you have
committed more times.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fb15b2536a5e400257ea305c2da444b6/f79ca/github-contributions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21.4%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAABDklEQVQY0yXPWUsCUQCAUf8/FJggRGRokqKVUUapFElYqIVbhkpqWjO4NG7j3Ubii+rpPB+f3gjc9YrN5hshJcbzEELieR5aabRn0MpgjEEphdYazxi08RBSsZYSoRRCC1y5xFefPpJ5uaDx0SLRiHPYjBN6ipJun5B4TdGdDqhPKqR7SYqfBUpWnur4lrf5HcnmEf58kHAlRqQZpeO08BWGOUK1A276GcKNHXZre/hLAbaK2wSfAzz0smTfjznv71O2c5Tta+6tMzpOnqvuKdFqhFQrxmU/xkQO8X3JCV2nj720ma4HLIXDaGkxWLQZuxbWovfnyB0yF2NWcspMOKzEf/G366o5s/UEs1H8APzAHFa9IT1BAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;My Github contributions&quot;
        title=&quot;My Github contributions&quot;
        src=&quot;/static/fb15b2536a5e400257ea305c2da444b6/f97d7/github-contributions.png&quot;
        srcset=&quot;/static/fb15b2536a5e400257ea305c2da444b6/0eb09/github-contributions.png 500w,
/static/fb15b2536a5e400257ea305c2da444b6/1263b/github-contributions.png 1000w,
/static/fb15b2536a5e400257ea305c2da444b6/f97d7/github-contributions.png 2000w,
/static/fb15b2536a5e400257ea305c2da444b6/f79ca/github-contributions.png 2330w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;With that being said, I find this graph quite interesting. I can see my progression during the year: my holidays periods (blank columns), my weekends (blank rows), busier times at work (darker points).
I could probably tell some of the functionality we launched seeing the color of the squares of that month.&lt;/p&gt;
&lt;p&gt;I am not particulary fond of how green it is. I am specially dispised with the green dots on the first and last rows: they are commits on weekends. And that means hotfixes: that is, something that was wrong in production and needed to be fixed right now.
I count 3 this year. It’s not a high number considered the pace of development and how many developers we are in Playtomic. But yikes.&lt;/p&gt;
&lt;p&gt;That number has to be zero.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cloud gaming]]></title><description><![CDATA[This is not a technical entry, but I still think it is worth to be written. I used to be a hardcore gamer. Hundreds of titles, thousands (I…]]></description><link>https://sgmoratilla.com/2020-05-20-cloud-gaming/</link><guid isPermaLink="false">https://sgmoratilla.com/2020-05-20-cloud-gaming/</guid><pubDate>Wed, 20 May 2020 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is not a technical entry, but I still think it is worth to be written.&lt;/p&gt;
&lt;p&gt;I used to be a hardcore gamer. Hundreds of titles, &lt;em&gt;thousands&lt;/em&gt; (I’m not exaggerating) of hours in Team Fortress 2. I used to compete. But life is life, and my time in games was being reduced and I didn’t want to maintain a machine with Windows (for gaming and photography) and Linux (for everything else - work, study, …). So that, I jumped into Mac, said goodbye to video games and hello to Netflix.&lt;/p&gt;
&lt;p&gt;But here we are, spending so many hours at home, getting bored of passive activities as watching TV shows or movies and missing gaming so much. But with neither PC nor video console.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1920px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/5545ed7d14643a8d1c849a6b90a789cf/aaf92/cloud-gaming-batman.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAALABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAYFBwgJ/8QAFwEAAwEAAAAAAAAAAAAAAAAAAQIDBf/aAAwDAQACEAMQAAAB4q3RmdizosJKAT//xAAbEAACAwADAAAAAAAAAAAAAAABBAIDBRESE//aAAgBAQABBQKOrU27TrcLXOaZuWADXpPvFemQ/8QAIREAAgEDAwUAAAAAAAAAAAAAAQISAxEhABMxFDJxktH/2gAIAQMBAT8B6iqtRoMFLC5iqryZcKAPA7QMAWAtu1Dmb+zfdf/EAB8RAAIBAgcAAAAAAAAAAAAAAAECEQADEjFBQlGS0f/aAAgBAgEBPwG9YRyojICJk6DmhgGxeo9r/8QAJBAAAgIBAwIHAAAAAAAAAAAAAQIDERIAITETkQQUIkFygYL/2gAIAQEABj8CkbycUET4dGJXkYQ4RqjUXJLNKwMxzJCsxVaWhpVDkUKK3jx8QN9SVJ4bHNsOpJEHw9stuTzvqQDgdWvy3p7VpBk1Eja9u2sjGtnk/Wv/xAAbEAEBAAMBAQEAAAAAAAAAAAABESExQQBRcf/aAAgBAQABPyHj/vWw0Doa0YYKuCH6v6qUz9y6skaCq8rkyhzrwJRSNy/VdEd+MOgUIRejDvvqRJOQrBWJw9//2gAMAwEAAgADAAAAEK/v/8QAGxEBAQABBQAAAAAAAAAAAAAAAREAMVFxsdH/2gAIAQMBAT8QaEQ4y0aKFRRgAHAATKC8R762n//EABwRAQEAAQUBAAAAAAAAAAAAAAERACExUXGxwf/aAAgBAgEBPxAoQPCjANUtkraoqquIg2wOqfHvLn//xAAZEAEBAQEBAQAAAAAAAAAAAAABEQAhMUH/2gAIAQEAAT8QT7yOcrx8mO3EAQaEAAQFYBvVVrBUFgIZVINfBMAptl7pIIXTtF4qqtjrI+o0YWoUKjLAMCGshC4iLAsqBfN//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Batman - Arkham Knight&quot;
        title=&quot;Batman - Arkham Knight&quot;
        src=&quot;/static/5545ed7d14643a8d1c849a6b90a789cf/aaf92/cloud-gaming-batman.jpg&quot;
        srcset=&quot;/static/5545ed7d14643a8d1c849a6b90a789cf/953fe/cloud-gaming-batman.jpg 500w,
/static/5545ed7d14643a8d1c849a6b90a789cf/0a251/cloud-gaming-batman.jpg 1000w,
/static/5545ed7d14643a8d1c849a6b90a789cf/aaf92/cloud-gaming-batman.jpg 1920w&quot;
        sizes=&quot;(max-width: 1920px) 100vw, 1920px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So… what options do people like me have to enjoy games these days?
These options are still valid for people with a PC but who don’t want to spend in upgrading it.&lt;/p&gt;
&lt;h2&gt;Google Stadia&lt;/h2&gt;
&lt;p&gt;The most known probably. Stadia has a subscription plan which gives early access to the platform and some complimentary games month after month. Apart from that, you have to buy your gamers in Stadia. This is the main handicap, because we usually already have a collection in some other platform (Valve’s Steam, Epic Games, …).&lt;/p&gt;
&lt;p&gt;Current price: 10€/month&lt;/p&gt;
&lt;h2&gt;Geforce Now&lt;/h2&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3b47d9351a73280a2dd9e438d9f9591f/9ad4e/cloud-gaming-gf-now.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAUGAwn/xAAXAQADAQAAAAAAAAAAAAAAAAABAgMF/9oADAMBAAIQAxAAAAHis6SZjAryGIJ//8QAGRABAAMBAQAAAAAAAAAAAAAAAwEEBQIR/9oACAEBAAEFAoRCPgq72B3tTHjR9NqdmxD2u+5b/8QAIREAAgIBAgcAAAAAAAAAAAAAAQIDEQAEEiEiQlFScdL/2gAIAQMBAT8B1G51jdetEcKWI5mF0TTGu54+sWWYAANQA85PrP/EABwRAAICAwEBAAAAAAAAAAAAAAECAyEAEUEicf/aAAgBAgEBPwFpnjLgBS3yjQ1fODYU1hWNiWdFLGz4Q3XSN5//xAAiEAACAgEFAAIDAAAAAAAAAAABAgMRIQAEEhMiBTEyUnH/2gAIAQEABj8CjSJFji3GxEe4Ro5Hb1L2C3MPg+Uo8q4/RyRpNuEkjdxHXXC8ovpVjSozuc3+INfqAKD7P42eaOASF2pEi5SsqhmMcyGQGlVc1YUGhqPgSvX0BcnAMAY/3Oc6ilWV0kHKpIz1uPBXDJxINYsZ0xLEk5JOSSfsknOv/8QAGhABAQEBAAMAAAAAAAAAAAAAAREhADFBYf/aAAgBAQABPyGC0H2fDXy7KdoqUcl9IVq2K1eC3MXzQo6OBbkEBDIBiKlTVIhEh02LsigfNbQKtavahP8A3zQJXVXv/9oADAMBAAIAAwAAABC47//EABgRAQEBAQEAAAAAAAAAAAAAAAERIQAx/9oACAEDAQE/EAiQOHBEO2glhooHRcAB44ZgGQ2FlhU7/8QAGBEBAQEBAQAAAAAAAAAAAAAAAREhQQD/2gAIAQIBAT8QTRLbCgBF5oBSCkXIELuVMpBILqc9/8QAFxABAQEBAAAAAAAAAAAAAAAAAREhAP/aAAgBAQABPxB4a0S8iAZBrRwwDpvXmrVpumMJl97nDtywolNVD+zmwIDIABQVCYyR0KFDqpgbFyho+5QKqrXv/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cloud Gaming in Geforce Now&quot;
        title=&quot;Cloud Gaming in Geforce Now&quot;
        src=&quot;/static/3b47d9351a73280a2dd9e438d9f9591f/451a4/cloud-gaming-gf-now.jpg&quot;
        srcset=&quot;/static/3b47d9351a73280a2dd9e438d9f9591f/953fe/cloud-gaming-gf-now.jpg 500w,
/static/3b47d9351a73280a2dd9e438d9f9591f/0a251/cloud-gaming-gf-now.jpg 1000w,
/static/3b47d9351a73280a2dd9e438d9f9591f/451a4/cloud-gaming-gf-now.jpg 2000w,
/static/3b47d9351a73280a2dd9e438d9f9591f/c7e40/cloud-gaming-gf-now.jpg 3000w,
/static/3b47d9351a73280a2dd9e438d9f9591f/9ad4e/cloud-gaming-gf-now.jpg 3840w&quot;
        sizes=&quot;(max-width: 2000px) 100vw, 2000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
This is my current winner. Geforce Now is partial compatible with Steam, Epic and Uplay. I say partial because no all their catalog is available. They take in and out games from Geforce Now every week without prior notice. So that it might be a bit annoying if you don’t find a game that your expected to be there..&lt;/p&gt;
&lt;p&gt;Current price: ~5€/month&lt;/p&gt;
&lt;h2&gt;Shadow.tech&lt;/h2&gt;
&lt;p&gt;I’m looking forward to try this in Spain, but I think this will be the clear winner. They give you access to a cloud machine with a GPU. They have several levels of computing power being the basic one pretty competitive (they promise full hd gaming) while the advance one seems a bit overkilling (4K and Ray Tracing).&lt;/p&gt;
&lt;p&gt;You can run anything on that machine: Steam, Epic, your own DRM-free games, …&lt;/p&gt;
&lt;p&gt;Current price: 15€/month&lt;/p&gt;
&lt;h2&gt;Your own cloud&lt;/h2&gt;
&lt;p&gt;Shadow.tech gave me this idea. What about setting up your own compute in the cloud? Doable but a bit expensive.&lt;/p&gt;
&lt;p&gt;I tried Paperspace and AWS. In both cases, you have to ask access to the GPU instances.&lt;/p&gt;
&lt;p&gt;In the case of Paperspace, you have to pay a fixed price for the  storage and the fixed public IP to access the computer from the Internet. This is a fixed price of 10€/month. Then you have to pay for the running instance in a hour basis, which is 0.5€/h for the basic GPU one. If you compare it to Shadow.tech, you have 10h/month of game for a similar price.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1512px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/67cf3ce39bd1a6e005b7701ede76627e/2719f/cloud-gaming-paperspace.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.400000000000006%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABH0lEQVQoz52Qy07DMBBFsy6vgtRH0jiJn3nYaUtpKeUfWLDk/z/kILsgQGwQi6PxjK5nrm52fZfzX65uF0yuZ9zOSl5e38iFJbucLriczj9YcHEz/02cT7/6qJvczJnlkqJy5MLQrw8sS0O2LBR5YVnmlmWhKavuG22q+cpQlBZRd6yEJV9pitKgzIhttyg7YtwW0fRkjRzQaodWe6QcCeF0xp/w/okQnlF6RJuRwe9pux2N9Fi3xboNSgeM3RDCkUYFsloOeH+kHw5pUfxcVo6q7hKibqmbPvE5i++oi0vjIVG1DP6RRoezw74/0LYPSSRVIM6ii5/1J1J5jNugzTodcO7+7DDm0nZ7lF6nfGJu4g9EndQh5bgSjkb5lOE76/XAnB2RJNEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Paperspace - Price&quot;
        title=&quot;Paperspace - Price&quot;
        src=&quot;/static/67cf3ce39bd1a6e005b7701ede76627e/2719f/cloud-gaming-paperspace.png&quot;
        srcset=&quot;/static/67cf3ce39bd1a6e005b7701ede76627e/0eb09/cloud-gaming-paperspace.png 500w,
/static/67cf3ce39bd1a6e005b7701ede76627e/1263b/cloud-gaming-paperspace.png 1000w,
/static/67cf3ce39bd1a6e005b7701ede76627e/2719f/cloud-gaming-paperspace.png 1512w&quot;
        sizes=&quot;(max-width: 1512px) 100vw, 1512px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 978px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/8a55848c03ddae6945656d64125de096/9b76f/cloud-gaming-paperspace2.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 43.8%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABU0lEQVQoz31Si5KDIAz0/z+xU0VERUFUFPDdvYFa73pzvczshEDYJAtRWZQQdY2SldD9gG3bMM9z8B6PxwPHcXxjP97jX4isMaCEwhPzsgIvcuQsA0sZOOcX8b7v2PcD3nyRTxYt8wwhGhQlR1VVELWArGrwnEM1CnXh93koqDt9Ef6FQGitDdV/JvrW/+vik/k7UVEU6LW+9HqZJ53cBOccnLWwxsKOJnhnHXwj42hCjj3joOFkHVJC4Ymn6Xk4DiPWZQUlKVjGIIWAkgpt00JJiVY1aFQDXgmMvYbw0ghxjmwskiQBZRlELaGUQl2JUJkxBnGu52mGm1z4Aa94mZcQv42s+x6EEKSEQAp5HW7rhoxS0JQijQmSWxLyWM5wv90D0oQgjmO0qn3q7zvc1hWD1kGfUQ/XA/n/Zk7NPPzaS2GMRdf10MMAY0zQv2vb65W/AKdyta3RzzZ9AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;Markdown Monster icon&quot; title=&quot;Markdown Monster icon&quot; src=&quot;/static/8a55848c03ddae6945656d64125de096/9b76f/cloud-gaming-paperspace2.png&quot; srcset=&quot;/static/8a55848c03ddae6945656d64125de096/0eb09/cloud-gaming-paperspace2.png 500w,
/static/8a55848c03ddae6945656d64125de096/9b76f/cloud-gaming-paperspace2.png 978w&quot; sizes=&quot;(max-width: 978px) 100vw, 978px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;With AWS, it is much more expensive, but you have more options of instances and storage to combine. A similar machine to the Paperspace’s one is 3$/h. Not including the storage and network transfer.&lt;/p&gt;
&lt;h2&gt;Steam, Parsec, …&lt;/h2&gt;
&lt;p&gt;In case that you have remote access to a PC, you can always stream from that computer. Steam has a streaming mode (easily accessed via Big Picture mode), but you can use any other control system, such as Parsec.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://parsecgaming.com/&quot;&gt;Parsec&lt;/a&gt; is such a cool tool, I encourage you to try it.&lt;/p&gt;
&lt;h2&gt;PS Now and Xbox XCloud&lt;/h2&gt;
&lt;p&gt;I’m adding these options just for the sake of mentioning them, but I couldn’t test any of them. PS Now is only available for PC and Xbox XCloud is there only for mobile phones.&lt;/p&gt;
&lt;p&gt;They look promising too. Besides, they give you access to the catalog of Playstation (which have some nice exclusives) and Xbox (fewer exclusives as they end in PC as well).&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;My bet: Parsec &gt; Shadow.tech &gt; Geforce Now &gt; Stadia.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Choosing a documentation system]]></title><description><![CDATA[Documentation is always the victim when developing a system. We will do it tomorrow. And tomorrow… I’m writing this because we had a deep…]]></description><link>https://sgmoratilla.com/2020-05-19-choosing-a-documentation-system/</link><guid isPermaLink="false">https://sgmoratilla.com/2020-05-19-choosing-a-documentation-system/</guid><pubDate>Tue, 19 May 2020 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Documentation is always the victim when developing a system. &lt;em&gt;We will do it tomorrow&lt;/em&gt;. &lt;em&gt;And tomorrow&lt;/em&gt;…&lt;/p&gt;
&lt;p&gt;I’m writing this because we had a deep discussion about this subject in our team, and I would like to share what options we have checked and why we liked or disliked them.&lt;/p&gt;
&lt;p&gt;Our aim is to build a documentation &lt;strong&gt;nice to read&lt;/strong&gt; (e.g. Stripe’s doc) but also &lt;strong&gt;nice to write&lt;/strong&gt; (and this is why we are here).&lt;/p&gt;
&lt;p&gt;From our previous experience, tools that automate the conversion from the code (such as Swagger or Javadoc) are easy to write, but frequently written carelessly. They turn out to be neither easy to read nor useful for the final reader.&lt;/p&gt;
&lt;p&gt;Our requirements are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We have several services, so we need an easy way to join all that documentation in our single site.&lt;/li&gt;
&lt;li&gt;That single site must look like a single piece of documentation, not a glued bunch of several documents.&lt;/li&gt;
&lt;li&gt;Easy to navigate and consume: we want to use it as an introduction to the APIs for the newcomers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Third-Parties systems&lt;/h1&gt;
&lt;p&gt;At the beginning, we tried to use existing solutions.&lt;/p&gt;
&lt;p&gt;Main advantage: you don’t have to invest your own time building the system.
Major drawback: your documentation is locked in that system. Not very customizable.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.postman.com/&quot;&gt;Postman&lt;/a&gt; API / Collections:&lt;/h2&gt;
&lt;p&gt;Adding requests to collections is easy. Writing details in them is not so easy. Besides, it is a bit dark what part of the documentation you have to change after making changes to the service if you are not an expert on that service.&lt;/p&gt;
&lt;p&gt;Postman API can be written in OpenApi format but there is no HTML view for them.&lt;/p&gt;
&lt;p&gt;Static documentation for collections is nice and readable, but there is not a main documentation that acts as entry-point for the documentation.&lt;/p&gt;
&lt;p&gt;No useful version control.
Documentation locked in Postman.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://apiary.io/&quot;&gt;Apiary&lt;/a&gt;:&lt;/h2&gt;
&lt;p&gt;It uses API Blueprint format, which is nice to write.
From our experience, it’s a hell to maintain (no version control, what happens with multiple people
editing the single document).&lt;/p&gt;
&lt;p&gt;A bit pricey.
Documentation locked in Apiary.&lt;/p&gt;
&lt;h1&gt;In-house / custom systems&lt;/h1&gt;
&lt;p&gt;So that, we gave up trying to use someone else’s system.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://slatedocs.github.io/slate&quot;&gt;Slate&lt;/a&gt; (plain):&lt;/h2&gt;
&lt;p&gt;Generates static sites from markdown. Simple, a bit ugly by default, but quite tuneable. We can deploy the static site in Github pages (free!).&lt;/p&gt;
&lt;p&gt;Main drawback: writing schemas in tables are a bit unreadable, because the description of some fields may be a bit long. And I don’t want to write tables/html, I want to write documentation.&lt;/p&gt;
&lt;h2&gt;Slate + Custom tunning (e.g. &lt;a href=&quot;https://docs.monzo.com/&quot;&gt;Monzo’s&lt;/a&gt;):&lt;/h2&gt;
&lt;p&gt;Prettier but it will be difficult to merge changes from the original Slate repo (although I’m not sure whether there will be changes often).&lt;/p&gt;
&lt;p&gt;Main drawback: it is nicer but I feel they are “cheating” in the markdown, as they are including html tags to make it prettier.&lt;/p&gt;
&lt;p&gt;For example, request/response parameters are tables of two columns, but the required keyword is in a span tag written in the first column of the markdown. That’s a bit trickier to write. If we don’t like that format in the future, we have to change all markdown files.&lt;/p&gt;
&lt;h2&gt;Slate + OpenApi converter:&lt;/h2&gt;
&lt;p&gt;What we liked about OpenApi is that it is a standard format and we didn’t have to care how it is displayed. On the contrary, we didn’t have a nice way to convert from openapi files to a static site.&lt;/p&gt;
&lt;p&gt;Given that, our approach has been to write schemas and endpoints in OpenApi format (we already had a lot of this work done in Postman APIs), automatically transform them to markdown, then use slate to generate the documentation.&lt;/p&gt;
&lt;p&gt;OpenApi-Generator and Widdershins allow us to use templates to transform the .yml into .md. You can choose to transform schemas into tables (like Monzo) or nested div (like Stripe). If you dislike that format in the future, we only have to change that template and rebuild the project.&lt;/p&gt;
&lt;p&gt;Service description will be written in plain markdown.&lt;/p&gt;
&lt;p&gt;For example, payments documentation might consist on two files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;payments-section.md: Manually written introduction to payments, their lifecycles, details or highlights, …&lt;/li&gt;
&lt;li&gt;payments-api.yml: OpenAPI file describing schema and endpoints.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to automate the building of this site, every *-api.yml is transformed into a *-api.md after pushing. Then Slate site is built.&lt;/p&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easy to write (markdown for descriptive text, openapi for schemas and requests).&lt;/li&gt;
&lt;li&gt;Easy to change (templates to transform openapi to markdown, css and layout.rb to change Slate)&lt;/li&gt;
&lt;li&gt;Easy to deploy (widdershins + slate + github page)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maybe too many steps that can fail (widdershins + slate)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Conclusions:&lt;/h1&gt;
&lt;p&gt;No free lunch. You have to spend your own time building your documentation site if you want happy API consumers and happy developers writting it.&lt;/p&gt;
&lt;p&gt;You can check a simplified version of &lt;a href=&quot;https://www.playtomic.io&quot;&gt;Playtomic’s&lt;/a&gt; solution at
(&lt;a href=&quot;https://github.com/sgmoratilla/openapi-slate-doc-example&quot;&gt;https://github.com/sgmoratilla/openapi-slate-doc-example&lt;/a&gt;).&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A way of testing code based on time in a Spring Boot Application]]></title><description><![CDATA[Every time I see a  I tremble. How do you expect to test that in a simple / coherent / easy to follow way? When I see that code, I usually…]]></description><link>https://sgmoratilla.com/2019-12-20-testing-code-based-on-time-clock-provider/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-12-20-testing-code-based-on-time-clock-provider/</guid><pubDate>Fri, 20 Dec 2019 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every time I see a &lt;code class=&quot;language-text&quot;&gt;Instant.now() / new Date() / something that creates a date based on the current time&lt;/code&gt; I tremble. How do you expect to test that in a simple / coherent / easy to follow way?&lt;/p&gt;
&lt;p&gt;When I see that code, I usually see tests using now() + duration to check that everything works. So that the test is not the same when run today than when run tomorrow.&lt;/p&gt;
&lt;p&gt;Wouldn’t it better to be able to “fix” the time and test your code with exact values of times/periods as well?&lt;/p&gt;
&lt;p&gt;So that I’ve fighting in my team in order that everyone uses a ClockProvider to get the “current” time. That way I can instance my own ClockProvider in unit tests and I can override the default ClockProvider of my application in integration tests.&lt;/p&gt;
&lt;p&gt;The former is pretty easy to understand, so that I’m not writing an example of that. This is an example of the latter.&lt;/p&gt;
&lt;p&gt;For instance, my application would look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootApplication&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;BookingApplication&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractAnemoneApplication&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@ConditionalOnMissingBean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ClockProvider&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 comment&quot;&gt;// to allow tests to overwrite it&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClockProvider&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getClockProvider&lt;/span&gt;&lt;span class=&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 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;Clock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;systemDefaultZone&lt;/span&gt;&lt;span class=&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;// An example of method using ClockProvider&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isItJanuary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClockProvider&lt;/span&gt; clockProvider&lt;span class=&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; clockProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getClock&lt;/span&gt;&lt;span class=&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;instant&lt;/span&gt;&lt;span class=&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;atZone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;UTC&lt;span class=&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;getMonth&lt;/span&gt;&lt;span class=&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;Month&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;JANUARY&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;/div&gt;
&lt;p&gt;And my IT tests:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@SpringBootTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webEnvironment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpringBootTest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebEnvironment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RANDOM_PORT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; classes &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 class-name&quot;&gt;FixingClockConfiguration&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 class-name&quot;&gt;BookingsApplication&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 punctuation&quot;&gt;)&lt;/span&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;SpringExtension&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 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;BookingsApplicationIT&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@TestConfiguration&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;FixingClockConfiguration&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClockProvider&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getClockProvider&lt;/span&gt;&lt;span class=&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;ZonedDateTime&lt;/span&gt; fixedInstant &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ZonedDateTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2019&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;04&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; UTC&lt;span class=&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 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;Clock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fixedInstant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toInstant&lt;/span&gt;&lt;span class=&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; fixedInstant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getZone&lt;/span&gt;&lt;span class=&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 comment&quot;&gt;// ... your tests based on that date&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Concurrency in Spring's StreamListener and Kafka]]></title><description><![CDATA[Another too fast, too furious post. I have spent a few hours trying to make my event processor multi-threaded, and it’s so damn easy that I…]]></description><link>https://sgmoratilla.com/2019-12-17-concurrency-spring-kafka-stream-listener/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-12-17-concurrency-spring-kafka-stream-listener/</guid><pubDate>Tue, 17 Dec 2019 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Another too fast, too furious post. I have spent a few hours trying to make my event processor multi-threaded, and it’s so damn easy that I don’t want anyone to spend more than a few minutes in this. I couldn’t find it anywhere on Internet, so I share it here.&lt;/p&gt;
&lt;p&gt;We are using the Spring Cloud Stream layer to configure our Kafka consumers.&lt;/p&gt;
&lt;p&gt;For example, a configuration for a processor named ‘reservations-input’ connected to a Kafka topic ‘reservations-topic’ would be similar to this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spring.cloud.stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;bindings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;reservations-input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;content-type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; application/json
      &lt;span class=&quot;token key atrule&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reservations&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;topic
      &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;group&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And your class to start processing those events:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@EnableBinding&lt;/span&gt;&lt;span class=&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;MessagingConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReservationTopic&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 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;MessagingConfiguration&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;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReservationTopic&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; INPUT &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;reservations-channel&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token annotation punctuation&quot;&gt;@Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;INPUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;SubscribableChannel&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;input&lt;/span&gt;&lt;span class=&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;@Service&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;ReservationProcessor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@StreamListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MessagingConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReservationTopic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;INPUT&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;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Message&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;ReservationEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; reservationMessage&lt;span class=&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;// your stuff&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Easy peasy. Only problem here is concurrency.&lt;/p&gt;
&lt;p&gt;If you have used Kafka before, you would know that the number of partitions in your topic limits the concurrency.
Each partition have 1 single consumer.&lt;/p&gt;
&lt;p&gt;I don’t know whether (and where) I read that, but I assumed that my application would generate as many threads/consumers as partitions my topic has. But I was wrong. By default, Spring’s only generates 1-threaded processor.&lt;/p&gt;
&lt;p&gt;Solutions? Get more instances of your application or configure &lt;em&gt;ConcurrentKafkaListenerContainerFactory&lt;/em&gt; to be able to throw more threads (see &lt;a href=&quot;https://docs.spring.io/spring-kafka/docs/2.3.x/reference/html/#container-factory&quot;&gt;https://docs.spring.io/spring-kafka/docs/2.3.x/reference/html/#container-factory&lt;/a&gt;).&lt;/p&gt;
&lt;h1&gt;Option 1: create your own instance of ConcurrentKafkaListenerContainerFactory.&lt;/h1&gt;
&lt;p&gt;The only hint I found in the documentation or stackoverflow was to instance a bean of type ConcurrentKafkaListenerContainerFactory.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConcurrentKafkaListenerContainerFactory&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;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&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;kafkaListenerContainerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConsumerFactory&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;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; consumerFactory&lt;span class=&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;ConcurrentKafkaListenerContainerFactory&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;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; factory &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;ConcurrentKafkaListenerContainerFactory&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 punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        factory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConsumerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;consumerFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        factory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setConcurrency&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 punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; factory&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;/div&gt;
&lt;p&gt;I am not very prone to creating my own beans to configure stuff that seems too obvious. It is easy to overwrite some Spring default values that I am already using, it is more code to maintain…&lt;/p&gt;
&lt;p&gt;There has to be a way through configuration.&lt;/p&gt;
&lt;h1&gt;Option 2: use configuration&lt;/h1&gt;
&lt;p&gt;Getting back to configuration, what we write under &lt;em&gt;spring.cloud.stream.bindings.channel-name.consumer&lt;/em&gt; ends in the configuration of Kafka. Therefore, I tried to configure the property concurrency. That is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spring.cloud.stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;bindings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;reservations-input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;content-type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; application/json
      &lt;span class=&quot;token key atrule&quot;&gt;consumer.concurrency&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 key atrule&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; reservations&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;topic
      &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;group&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Starting our application, we see that we have 3 binders.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;    December 17th &lt;span class=&quot;token number&quot;&gt;2019&lt;/span&gt;, &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;:22:57.274	&lt;span class=&quot;token number&quot;&gt;2019&lt;/span&gt;-12-17 &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;:22:57.274  INFO &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;consumer-service,,,&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;container-1-C-1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; o.s.c.s.b.k.KafkaMessageChannelBinder&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; partitions assigned: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;reservations-topic-1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
	December 17th &lt;span class=&quot;token number&quot;&gt;2019&lt;/span&gt;, &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;:22:57.259	&lt;span class=&quot;token number&quot;&gt;2019&lt;/span&gt;-12-17 &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;:22:57.259  INFO &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;consumer-service,,,&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;container-2-C-1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; o.s.c.s.b.k.KafkaMessageChannelBinder&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; partitions assigned: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;reservations-topic-2&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
	December 17th &lt;span class=&quot;token number&quot;&gt;2019&lt;/span&gt;, &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;:22:57.256	&lt;span class=&quot;token number&quot;&gt;2019&lt;/span&gt;-12-17 &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;:22:57.256  INFO &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;consumer-service,,,&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;container-3-C-1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; o.s.c.s.b.k.KafkaMessageChannelBinder&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; partitions assigned: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;reservations-topic-3&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Profit!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[BlockingQueue and ExecutorService]]></title><description><![CDATA[This is a quick and dirty post, but I promised I would publish everything I research at Playtomic. Last week, we were having a discussion…]]></description><link>https://sgmoratilla.com/2019-12-16-blocking-queue-and-execution-service/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-12-16-blocking-queue-and-execution-service/</guid><pubDate>Mon, 16 Dec 2019 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a quick and dirty post, but I promised I would publish everything I research at Playtomic.&lt;/p&gt;
&lt;p&gt;Last week, we were having a discussion about how to limit how many tasks an ExecutorService can enqueue. We were trying to
control how much memory a service can handle before it throws out of memory exceptions. This service accepts messages from both a Kafka topic
and from an API. Those operations end in the same internal logic, which is threaded.&lt;/p&gt;
&lt;p&gt;There is a kind of Queue, BlockingQueue, that can wait until a spot in the queue is free. It would seem that using an ExecutionService
with a BlockingQueue would wait when submitting a task until that queue is not full. But it is not, the ExecutionService rejects the task.&lt;/p&gt;
&lt;p&gt;You know that hours of trial and error can save you hours of reading the manual 😉. I’m proud to say that I have read the manual first this time.&lt;/p&gt;
&lt;p&gt;This test shows what happens:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&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;BlockingQueueExecutorServiceTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&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;submitTest&lt;/span&gt;&lt;span class=&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;// Worst case scenario: accept only 1 thread in the queue.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; nThreads &lt;span class=&quot;token operator&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 class-name&quot;&gt;ExecutorService&lt;/span&gt; exService &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;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            nThreads&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            nThreads&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token number&quot;&gt;0L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;TimeUnit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MILLISECONDS&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;LinkedBlockingQueue&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;nThreads&lt;span class=&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;// Full this with tasks&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&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 operator&quot;&gt;=&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; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&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;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;WaitingTask&lt;/span&gt; t &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;WaitingTask&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;
            exService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&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;private&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;WaitingTask&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Runnable&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; index&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 class-name&quot;&gt;WaitingTask&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; index&lt;span class=&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;index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; index&lt;span class=&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;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 keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Running task {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&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;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@550dbc7a[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@4dbb42b7[Wrapped task = com.playtomic.anemone.matchmaker.service.BlockingQueueExecutorServiceTest$WaitingTask@66f57048]] rejected from java.util.concurrent.ThreadPoolExecutor@21282ed8[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]

	at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2055)
	at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:825)
	at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1355)
	at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
	at com.playtomic.anemone.matchmaker.service.BlockingQueueExecutorServiceTest.submitTest(BlockingQueueExecutorServiceTest.java:28)
... more boring stacktrace&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you want to wait until queue is not full, you have to provide a RejectedExecutionHandler which does that. For example, Spring’s CallerBlocksPolicy.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&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;BlockingQueueExecutorServiceTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&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;submitTest&lt;/span&gt;&lt;span class=&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;// Worst case scenario: accept only 1 thread in the queue.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; nThreads &lt;span class=&quot;token operator&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 class-name&quot;&gt;CallerBlocksPolicy&lt;/span&gt; policy &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;CallerBlocksPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&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;// 10secs&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ExecutorService&lt;/span&gt; exService &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;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            nThreads&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            nThreads&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token number&quot;&gt;0L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;TimeUnit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MILLISECONDS&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;LinkedBlockingQueue&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;nThreads&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
            policy&lt;span class=&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;// Full this with tasks&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&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 operator&quot;&gt;=&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; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&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;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;WaitingTask&lt;/span&gt; t &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;WaitingTask&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;
            exService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&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;private&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;WaitingTask&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Runnable&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; index&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 class-name&quot;&gt;WaitingTask&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; index&lt;span class=&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;index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; index&lt;span class=&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;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 keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Running task {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&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;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InterruptedException&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And this time we get:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;12:21:32.409 [pool-1-thread-1] INFO com.playtomic.anemone.matchmaker.service.BlockingQueueExecutorServiceTest - Running task 0
12:21:32.422 [main] DEBUG org.springframework.integration.util.CallerBlocksPolicy - Attempting to queue task execution for 10000 milliseconds
12:21:33.420 [pool-1-thread-1] INFO com.playtomic.anemone.matchmaker.service.BlockingQueueExecutorServiceTest - Running task 1
12:21:33.420 [main] DEBUG org.springframework.integration.util.CallerBlocksPolicy - Task execution queued
12:21:33.421 [main] DEBUG org.springframework.integration.util.CallerBlocksPolicy - Attempting to queue task execution for 10000 milliseconds
12:21:34.423 [pool-1-thread-1] INFO com.playtomic.anemone.matchmaker.service.BlockingQueueExecutorServiceTest - Running task 2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Amazed by the Soyuz]]></title><description><![CDATA[I never thought that one of the things that would impress me the most when visiting New York City, would be seeing a Russian Soyuz. It was…]]></description><link>https://sgmoratilla.com/2019-10-09-soyuz/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-10-09-soyuz/</guid><pubDate>Thu, 10 Oct 2019 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I never thought that one of the things that would impress me the most when visiting New York City, would be seeing a Russian Soyuz. It was at the Intrepid Museum. There was a Soyuz and a space shuttle, the &lt;em&gt;Enterprise&lt;/em&gt; (wink wink to Star Trek fans).&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Enterprise&lt;/strong&gt; is huge. Half the size of a Boing or Airbus. First class in spacecrafts. The workhorse of the NASA. I imagine myself working on that and I drool. The dream of every engineer. They built 5 of them, only 3 survive nowadays. But they are discontinued. Too complex, too expensive to keep them on duty.&lt;/p&gt;
&lt;p&gt;Next to the Enterprise there was a tiny metal shell. Something that reminded me of a piece of a submarine that got loose and dropped. A &lt;strong&gt;Soyuz&lt;/strong&gt;. It hardly fits 3 people in it. It can carry a tiny portion of what Enterprise could. But they work. And they are still in service.&lt;/p&gt;
&lt;p&gt;That recalled me my work. Sometimes we engineers are so excited about technology that we get lost in the process and miss the final purpose: solve the problem. It’s cool to solve it with the last shinny piece of tech.&lt;/p&gt;
&lt;p&gt;But what if simpler, &lt;em&gt;rude&lt;/em&gt; solutions are the solution? What if those are the solutions that really last?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A way to unify two authorization methods - Part I]]></title><description><![CDATA[Time to talk about security today. Security is one of the most important aspects of a system, and probably one of the most boring to…]]></description><link>https://sgmoratilla.com/2019-07-15-an-authorization-proposal/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-07-15-an-authorization-proposal/</guid><pubDate>Mon, 15 Jul 2019 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Time to talk about security today. Security is one of the most important aspects of a system, and probably one of the most boring to implement.
It is usually divided in two steps&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; authentication and authorization. In short words, authentication gives access to a system. It is a yes/no question: do I know you? On the other hand, authorization controls permissions (what parts of the system you can access). In this post, I am going to talk about the latter.&lt;/p&gt;
&lt;p&gt;As I said in this very blog, Playtomic evolves so fast (&lt;a href=&quot;https://dev.to/playtomic/how-we-built-our-stack-with-docker-swarm-3md5&quot;&gt;do you remember our microservices architecture?&lt;/a&gt;). So fast that we ended with two authorization systems&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; one based on roles and another one based on authorities.
The main difference is that authorities define fine-grained permissions, while roles usually define sets of permissions based on the type of the user.
For instance, an authority could be “read_users” while a role could be USER&lt;code class=&quot;language-text&quot;&gt;_&lt;/code&gt;ADMINISTRATOR.
We like the authorities because each service is responsible of defining their own authorities. On the other hand, roles are useful because define types of users of the system. They are not defined by the services themselves, but by the whole system.&lt;/p&gt;
&lt;p&gt;We could argue why we got there in the first time. However, I learned from two years in a startup that we don’t discuss guilt but solutions. And &lt;a href=&quot;/2019-05-27-2-years-at-playtomic&quot;&gt;solutions must be
fast to implement and don’t have to be perfect but nearly&lt;/a&gt; :)&lt;/p&gt;
&lt;p&gt;So, this is our problem&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two authorization schemes that we want to merge, or make compatible.&lt;/li&gt;
&lt;li&gt;Authorization based on authorities simplifies the definition of the access rules to your resources, authorization based on roles eases the access configuration of your users.&lt;/li&gt;
&lt;li&gt;There are several microservices already running in production, so that, the fewer the changes, the better. Some use roles, some authorities.&lt;/li&gt;
&lt;li&gt;We use JSON Web Tokens (JWT) to move the authentication/authorization through the services. It can contain roles or authorities. So that, at this point we can access some services but not others depending on how the jwt was generated.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our purpose is to define a “framework” that allows us to write permission rules in a simple way. It is common that your security logic ends scattered all over your code.
By writing permissions using authorities in our controllers and services, we aim to separate the type (role) of the user that access from the actual permission it has.
By contrast, we wanted to be able to assign roles to the users, because it is easier to understand what type of operations that user should be able to do.&lt;/p&gt;
&lt;p&gt;Extra&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; changes have to be as minimal as possible. It would be preferable if services are independent. That is, they don’t have to call to another one to get the transformation from roles to authorities.&lt;/p&gt;
&lt;p&gt;## How authorization was working at this point:
Spring security.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the user is logged, a jwt is generated. All requests will include that jwt as their credentials.&lt;/li&gt;
&lt;li&gt;A security filter intercepts all requests, reads the jwt and gets the roles/authorities.&lt;/li&gt;
&lt;li&gt;Controllers or services are protected via annotation (@PreAuthorize), via config (HttpSecurity in WebSecurityConfigurerAdapter).&lt;/li&gt;
&lt;li&gt;Finer-grained permission (i.e can this user modify this precise resource?) where handled programmatically accessing the Authorization.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example with authorities and PreAuthorize:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;    &lt;span class=&quot;token annotation punctuation&quot;&gt;@PreAuthorize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hasAnyAuthority(&apos;admin_venues&apos;, &apos;read_venues&apos;)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/venue/{venue_id}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; method &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RequestMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GET&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 class-name&quot;&gt;Venue&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getVenue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; venueId&lt;span class=&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; venueService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getVenue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;venueId&lt;span class=&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;/div&gt;
&lt;p&gt;Example with roles and HttpSecurity:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;   &lt;span class=&quot;token annotation punctuation&quot;&gt;@Configuration&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;CustomWebSecurityConfigurerAdapter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebSecurityConfigurerAdapter&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;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpSecurity&lt;/span&gt; http&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;
            http
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;authorizeRequests&lt;/span&gt;&lt;span class=&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;antMatchers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GET&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/venue/**&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;hasAnyRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;VENUE_ADMINISTRATOR&quot;&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 comment&quot;&gt;// see JwtAuthorizationFilter below&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilterBefore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtAuthorizationFilter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnonymousAuthenticationFilter&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 comment&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;## How authorization is going to work from this point:&lt;/p&gt;
&lt;p&gt;Each service defines&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A mapper from roles to authorities. Good&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; independent a easy to implement. Bad&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; if a new role is added, you have to modify this piece. On the other hand, we could add a service that&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;knows the mapping, but then we have to add authorities to a role each time a new service appears. Trade-off. Choose your one.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A checker that verifies your access.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * AbstractJwtAuthorizationFilter reads the jwt and handles the errors when 
 * it cannot be verified or misses some expected data.
 */&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Slf4j&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;JwtAuthorizationFilter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AbstractJwtAuthorizationFilter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RolesToAuthorities&lt;/span&gt; authoritiesProvider&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 class-name&quot;&gt;JwtAuthorizationFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectProvider&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;RolesToAuthorities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; authoritiesProvider&lt;span class=&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;authoritiesProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; authoritiesProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getIfAvailable&lt;/span&gt;&lt;span class=&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 keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DefaultAnemoneRolesToAuthorities&lt;/span&gt;&lt;span class=&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 comment&quot;&gt;/**
     * User contains user data from the jwt (such as, id) and roles/authorities.
     */&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleInternalAuthorization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpServletResponse&lt;/span&gt; httpResponse&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nullable&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; bearer&lt;span class=&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; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userContext &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;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;
            httpResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UNAUTHORIZED&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&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;
            log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userContext == null, unauthorized.&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;return&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;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// CurentUserDetails is our implementation of UserDetails&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// By using authoriesProvider, maps the roles/authorities on User to the final authorities.&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;CurrentUserDetails&lt;/span&gt; currentUser &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;CurrentUserDetails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userContext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; authoritiesProvider&lt;span class=&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;SessionAuthenticationToken&lt;/span&gt; token &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;SessionAuthenticationToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentUser&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bearer&lt;span class=&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;SecurityContextHolder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&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;setAuthentication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;token&lt;span class=&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 boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&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;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&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;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RolesToAuthorities&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Collection&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;GrantedAuthority&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;toAuthorities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; role&lt;span class=&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;/div&gt;
&lt;p&gt;We have abstracted this checker into&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A class that gets the owner of the object being accessed (dependant of the object your are accessing).&lt;/li&gt;
&lt;li&gt;A class that checks that your are the owner of the object (generic).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&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;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PermissionChecker&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;T&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 keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isAllowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; permission&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Authentication&lt;/span&gt; auth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nullable&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; object&lt;span class=&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;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Getter&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@AllArgsConstructor&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;ByResourceOwnershipPermissionChecker&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;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PermissionChecker&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;T&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 annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; resourceType&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetOwner&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;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; getOwner&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isAdmin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Authentication&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 keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GrantedAuthority&lt;/span&gt; adminAuthority &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;SimpleGrantedAuthority&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;admin_&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; resourceType&lt;span class=&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; auth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAuthorities&lt;/span&gt;&lt;span class=&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;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;adminAuthority&lt;span class=&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;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;canOperateOnOwned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; permission&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Authentication&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 keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GrantedAuthority&lt;/span&gt; ownsAuthority &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;SimpleGrantedAuthority&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;permission &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_own_&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; resourceType&lt;span class=&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; auth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAuthorities&lt;/span&gt;&lt;span class=&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;contains&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ownsAuthority&lt;span class=&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 keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isAllowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; permission&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Authentication&lt;/span&gt; auth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nullable&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; object&lt;span class=&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; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isAdmin&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;span class=&quot;token keyword&quot;&gt;return&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;
        &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;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;canOperateOnOwned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;permission&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;span class=&quot;token keyword&quot;&gt;return&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;
        &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;ownedBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&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;span class=&quot;token keyword&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ownedBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nullable&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Authentication&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;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 keyword&quot;&gt;return&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;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; p &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; auth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrincipal&lt;/span&gt;&lt;span class=&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; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&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;p &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserDetails&lt;/span&gt;&lt;span class=&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 boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&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;Object&lt;/span&gt; ownerId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; getOwner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;object&lt;span class=&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; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ownerId &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;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 keyword&quot;&gt;return&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;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; id &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 class-name&quot;&gt;UserDetails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;p&lt;span class=&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;getUsername&lt;/span&gt;&lt;span class=&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;// The anonymous case&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;id &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;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 keyword&quot;&gt;return&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;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ownerId&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;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 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;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GetOwner&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;C&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 annotation punctuation&quot;&gt;@Nullable&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;C&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 punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;PermissionChecker&lt;/span&gt; checker &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;ByResourceOwnershipPermissionChecker&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;TestObject&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 string&quot;&gt;&quot;objects&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;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOwnerId&lt;/span&gt;&lt;span class=&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;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&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;VenueRolesToAuthorities&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DefaultAnemoneRolesToAuthorities&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 annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collection&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;GrantedAuthority&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;toAuthorities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; role&lt;span class=&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; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;VENUE_ADMINISTRATOR&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equalsIgnoreCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;role&lt;span class=&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;Arrays&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;asList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;admin_venues&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;ga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;read_venues&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 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;Arrays&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;asList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;role&lt;span class=&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;@Nonnull&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;GrantedAuthority&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; authority&lt;span class=&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;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SimpleGrantedAuthority&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;authority&lt;span class=&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;/div&gt;
&lt;p&gt;I am going to stop here as this post is already pretty long. We have seen the skeleton of an authorization system that works with roles and authorities (actually, it works on authorities but allows defining permissions in term of roles). Following posts will show a real example with uses of ByResourceOwnershipPermissionChecker.&lt;/p&gt;
&lt;p&gt;Nice to have&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use custom claims in your jwt to specify what authorities/roles apply to what resources.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[What I have learned from building microservices]]></title><description><![CDATA[Continuous Integration + Continuous Deployment. Repeat with me: there is no agile without CI+CD. Continuous deployments get you to the next…]]></description><link>https://sgmoratilla.com/2019-07-11-what-i-learned-from-building-microservices/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-07-11-what-i-learned-from-building-microservices/</guid><pubDate>Thu, 11 Jul 2019 10:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Continuous Integration + Continuous Deployment.&lt;/h2&gt;
&lt;p&gt;Repeat with me: there is no agile without CI+CD.&lt;/p&gt;
&lt;p&gt;Continuous deployments get you to the next level: iterations are shorter, your code is earlier in production, risk is lower.
You will bold enough to push code to production on Fridays!&lt;/p&gt;
&lt;h2&gt;A common framework to build them eases the development&lt;/h2&gt;
&lt;p&gt;I know that microservices theorists encourage no coupling at all among services, either by data or by code.
But a good base library and a good base Dockerfile for those services written in the same language
will accelerate the deployment of new services.&lt;/p&gt;
&lt;p&gt;Playtomic’s base library contains our typical configuration for Spring applications: security (including
configuration and interceptors to call among services), converters, … and some very basic domain classes
(such as users in the security context, location, addresses, …). More specific domain classes
(such as players, venues, …) are not shared.&lt;/p&gt;
&lt;h2&gt;Eases the iteration and the update of libraries&lt;/h2&gt;
&lt;p&gt;Besides, if you release these base components in an ordered way, with a decent versioning, it eases the
upgrading of dependencies of your services. For instance, we started with services
in Spring Boot 1.x and Java 1.8. We experiment and upgrade libraries in the new services.
Older microservices get upgraded when new functionality is added.&lt;/p&gt;
&lt;p&gt;By iteratively upgrading versions in new services, differences are fewer, so that upgrading to Spring 2.0 first,
then to 2.1, finally to Java 11 was piece of cake.&lt;/p&gt;
&lt;h2&gt;Sooner or later, you’ll have to denormalize&lt;/h2&gt;
&lt;p&gt;In a microservices architecture, you don’t have a common database. Efficiency will demand you to denormalize
your data across services. For instance, your user profile may end stored in several databases.
Maybe, they are slightly different versions depending on the needs of your service.&lt;/p&gt;
&lt;h2&gt;Learn to live with outdated data&lt;/h2&gt;
&lt;p&gt;This is a consequence of denormalization and efficiency: you won’t have all data updated in every service.
You have to reconsider if you need the last version of that data to operate or you can survive with an outdated
one while the fresh version arrives.&lt;/p&gt;
&lt;p&gt;This entails eventual consistency, what we will talk about that in some other post.&lt;/p&gt;
&lt;h2&gt;Makes me design better&lt;/h2&gt;
&lt;p&gt;As sharing data is more complicated, it forces you to define cleaner concepts and design better APIs. At first, it seems
so slow and overwhelming. Once you have a handful of services, pieces start to fit together easily, development
speeds up and microservices start to pay off.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[2 years at Playtomic.io]]></title><description><![CDATA[Last February I made 2 years in Playtomic. Everything goes so fast that it feels like 2 decades.
It goes so fast that I started to write…]]></description><link>https://sgmoratilla.com/2019-05-27-2-years-at-playtomic/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-05-27-2-years-at-playtomic/</guid><pubDate>Mon, 27 May 2019 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Last February I made 2 years in Playtomic&lt;/strong&gt;. Everything goes so fast that it feels like 2 decades.
It goes so fast that I started to write this post 2 months ago.&lt;/p&gt;
&lt;p&gt;There have been very stressful moments, most than peaceful. We have iterated versions and versions of our product and
built a huge amount of software in this time. It has been an exhausting learning period.&lt;/p&gt;
&lt;p&gt;So that I am writing this post to share some of the most important stuff I learnt here. Rest assured, I have gained tons of technical knowledge but those are not the most relevant&lt;/p&gt;
&lt;p&gt;What I truly learnt from two years in a startup: &lt;strong&gt;we don’t discuss guilt but solutions&lt;/strong&gt;.
And solutions must be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fast to implement (because we don’t have the resources/money to hold up weeks on development).&lt;/li&gt;
&lt;li&gt;Solve a problem. And user’s problems are more imperative than developer’s problems (don’t go for that fancy-time-consuming solution if a simpler one will solve it as well).&lt;/li&gt;
&lt;li&gt;Good enough to solve those problems (don’t leave trash behind).&lt;/li&gt;
&lt;li&gt;Open enough to be iterated in future releases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will writing several examples in future posts of this blog. The best sample? Our authentication system.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to mix Spring Data queries and MongoDB syntax]]></title><description><![CDATA[I love writing queries using Spring Data’s Criteria. Writing queries directly in SQL (@NativeQuery, @Query) feels a bit dirty.
And I also…]]></description><link>https://sgmoratilla.com/2019-05-23-mixing-spring-data-mongo-queries/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-05-23-mixing-spring-data-mongo-queries/</guid><pubDate>Fri, 24 May 2019 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I love writing queries using Spring Data’s Criteria. Writing queries directly in SQL (@NativeQuery, @Query) feels a bit dirty.
And I also think the official API using MongoCollection and Bson is quirk (at least for a Java developers).&lt;/p&gt;
&lt;p&gt;I started using MongoDB a year ago, so I feel pretty lost at first. I started using Spring’s Criteria because it felt more usable and I felt more skilled.
But as every abstraction, it is not perfect and does not cover all cases. So, what happens when Spring’s DSL is not powerful
enough to describe some part of your query? I had to write all my stages using Bson documents because I didn’t know how to
mix Spring’s and MongoDb’s objects.&lt;/p&gt;
&lt;p&gt;For example, I was unable to write an addField operation using &lt;a href=&quot;https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/core/aggregation/AggregationOperation.html&quot;&gt;AggregationOperations&lt;/a&gt;. Besides, I had to write a pretty complex expression with operators precedence. I search stackoverflow, indeed, what didn’t give me the solution but gave me some hints O:)&lt;/p&gt;
&lt;p&gt;Spring is usually well written and relatively easy to extend/tune to your case. So that… why don’t writing our custom AggregationOperation?
Here you have a snippet of my proposal.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * This example shows how to mix Spring Data Criteria with stages written in MongoDB syntax.
 */&lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Component&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;MongoFilterExample&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MongoTemplate&lt;/span&gt; mongoTemplate&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 class-name&quot;&gt;MongoFilterExample&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MongoTemplate&lt;/span&gt; mongoTemplate&lt;span class=&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;mongoTemplate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mongoTemplate&lt;span class=&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;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AggregationResults&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;MatchDocument&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;example&lt;/span&gt;&lt;span class=&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;// Using Spring syntax&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Criteria&lt;/span&gt; match &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;Criteria&lt;/span&gt;&lt;span class=&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;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;id&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;is&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;document-id&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;Sort&lt;/span&gt; sort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;creationDate&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 comment&quot;&gt;// Using Spring&apos;s AggregationOperation built with MongoDB syntax&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;FieldsExposingAggregationOperation&lt;/span&gt; addFields &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addFieldsOperation&lt;/span&gt;&lt;span class=&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;aggregate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;match&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; addFields&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sort&lt;span class=&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;/**
     * It receives standard Spring&apos;s Criteria and Sort, and a Spring&apos;s AggregationOperation to be use with the Spring&apos;s pipeline operation.
     */&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AggregationResults&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;MatchDocument&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;aggregate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Criteria&lt;/span&gt; match&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AggregationOperation&lt;/span&gt; addFieldsOperation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sort&lt;/span&gt; sort&lt;span class=&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;MatchOperation&lt;/span&gt; matchOperation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Aggregation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;match&lt;span class=&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;SortOperation&lt;/span&gt; sortOperation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Aggregation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sort&lt;span class=&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;aggregate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matchOperation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; addFieldsOperation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sortOperation&lt;span class=&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;/**
     * Aggregation using Spring&apos;s MongoTemplate syntax.
     */&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AggregationResults&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;FancyDocument&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;aggregate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AggregationOperation&lt;/span&gt;&lt;span class=&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; pipeline&lt;span class=&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;TypedAggregation&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;FancyDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; aggregation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Aggregation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newAggregation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FancyDocument&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; pipeline&lt;span class=&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; mongoTemplate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;aggregation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FancyDocument&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 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;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FieldsExposingAggregationOperation&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addFieldsOperation&lt;/span&gt;&lt;span class=&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;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FieldsExposingAggregationOperation&lt;/span&gt;&lt;span class=&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 class-name&quot;&gt;Document&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AggregationOperationContext&lt;/span&gt; context&lt;span class=&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;// Writing MongoDB queries here, so that use the names of the fields as stored in MongoDB&lt;/span&gt;
                        &lt;span class=&quot;token class-name&quot;&gt;Bson&lt;/span&gt; newField &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sqr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;minus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;$anotherField&quot;&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 comment&quot;&gt;// Returning a MongoDB stage here: {&quot;$addFields: {...}}&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;$addFields&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;Document&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;newField&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newField&lt;span class=&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 class-name&quot;&gt;ExposedFields&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getFields&lt;/span&gt;&lt;span class=&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;Field&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Fields&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;newField&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;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExposedFields&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;synthetic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Fields&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&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;boolean&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inheritsFields&lt;/span&gt;&lt;span class=&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 boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&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 comment&quot;&gt;/**
     * Helper methods that write operations in MongoDB syntax.
     */&lt;/span&gt;
    &lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bson&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sqr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bson&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 class-name&quot;&gt;BasicDBList&lt;/span&gt; list &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;BasicDBList&lt;/span&gt;&lt;span class=&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;
        list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&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;
        list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&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 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;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BasicDBObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;$pow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; list&lt;span class=&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;@Nonnull&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bson&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;minus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Nonnull&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; field&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; value&lt;span class=&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;BasicDBList&lt;/span&gt; minusArgs &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;BasicDBList&lt;/span&gt;&lt;span class=&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;

        minusArgs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;field&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        minusArgs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&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;value&lt;span class=&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;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BasicDBObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;$add&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; minusArgs&lt;span class=&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;/div&gt;</content:encoded></item><item><title><![CDATA[Playtomic's Pipeline]]></title><description><![CDATA[Your continuous integration (CI) pipeline is one of the most important pieces of software you can have in your business: it runs every time…]]></description><link>https://sgmoratilla.com/2019-04-15-playtomic-pipeline/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-04-15-playtomic-pipeline/</guid><pubDate>Mon, 15 Apr 2019 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Your continuous integration (CI) pipeline is one of the most important pieces of software you can have in your business: it runs every time a change in your code is made.&lt;/p&gt;
&lt;p&gt;At the backend team of Playtomic, we are quite proud of our CI pipeline. At least, I am :) It not only manages the integration of dozens of branches every day, but the deployments to our development and productions environment. It is currently handling around 35 projects and it is totally unsupervised. Every change to develop branch goes directly to development environment. Every change to master goes directly to production.&lt;/p&gt;
&lt;p&gt;Ruben told us &lt;a href=&quot;https://dev.to/playtomic/how-we-built-our-stack-with-docker-swarm-3md5&quot;&gt;how Playtomic’s stack looks like&lt;/a&gt;. In this post, I want to share some hints about what we were thinking when building our pipeline. It is not very exhaustive, but I think it is a good approximation.&lt;/p&gt;
&lt;p&gt;# How does a pipeline must be?
When building a pipeline there are several points you have to care about. It has to be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fast: Building, testing and deployment must be fast. How much is fast? 1 minute? 2 minutes? It depends on your requirements, but being below 2 minutes is enough for our teams.&lt;/li&gt;
&lt;li&gt;Reliable: several simultaneous builds must not interfiere among each other (i.e.: random ports for testing).&lt;/li&gt;
&lt;li&gt;Replicable: builds on the same commit must produce the same artefact.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pushes to develop and master are always performed after code review (we use github’s pull-requests).
More:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Continuos Deployment: a push to develop goes directly to develop environment. A push to master goes to production. No questions.&lt;/li&gt;
&lt;li&gt;Reduces the drifting among develop and production: several minor changes are safer to deploy to pro than fewer major changes.&lt;/li&gt;
&lt;li&gt;Easy to know which version is deployed where: if the build is green, then it is deployed. build includes commit hash. Important in a microservices architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Software components:&lt;/h1&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 561px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e152ec79f3c1b671ddccd9d2eee9f7c2/66712/playtomic-development-arch.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.199999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAADMklEQVQoz2WQXUyTdxjFT8GFmKg3i9mCH9FEZC7EGBEFZ8BdmEXFZdIE4f3XooAoKIHtwqhBbgxKhH5YYRk48WtaKCKo1UL7f2lfbOWt0NGiolERFRwascSJBQH7TBpjtuwkT87N7zk5OSA6iJOdLKfGw44ZHCzqtOt7FD4axjqHVbn1llSh7pBK0mS7Ib7ZnAQ3gexJeNuyPmXMvSMQcKkH/rYme0fbMwdGb2b0DTesmI8aDyuqu6+i+ocqOtutuqfj+2ZahjqQ73OdMjzzUuVAN5X3/UlKj7NA06hHV23cV6Pt259OevPpQ2cWBb27Qz7ZlU/vpC1WnLutkqucKaSzbBox9jCqlrPirUMNKOyRfy31SVTMG0ZKej1UeUZnpNjZa/4qWL12zCV0v25VUT/fMT4o5U702zLfv2lT09iNND1OuFmB8Z6KTA9CDeUKsWR6x7u7yPc5/zD0d9PxwbtU9sRLhZb6wwREUB8QMMcnvLy2aaJfzKEXUi4N2DLplXlDG53HFwhSBqrdLK3Gwwo0nH15TNqI+0RY6eDrd9mvlpbV/WZKdTsOR0v2+KLqchx4fCacWleDrsQsDrozHga78gIfZMFEBkRQJYCDJgFEmTh0WYjSigx6uzLMfKU4THP6PAiYRnNmLa+7/DtAhKqTe8OMF75ToCQO+/m2mM7aldFDVZFz9d9i2qRpHsabkxTQiQwGic3QiWzzUYsAPRfw840s+M8l4MGhxK/9Vet+obJtCEgrUChmw8DTUSZuneKS8eRiuK8uFqMzAH9DnGK4MQHQcqbQcjblS7ScxVVwYWapqF7Q0ZgUPSlnWIJdefTemV7b25QYdURUzz7OhVlazmK0nC3cY8+G8/qG8DcXY/HatAz+S6sAHWeokZUotzLouLDI0KqOKTJvThySUtv8zl3U25IdGJFz6a1DqU2+mhpZKbLlWi7M19jSUW5Nh05UQcMFfNandqFADWdA8XP0OLZgvPXH7GHXThqU8mikPSc4Yfth6R3xJ0TYdod4jU34/Dt1/wsMQZwpfM0bw+jCN3hhXBo53q5+RN49/jGn0EREEInQ1JKi+NdM/w37qH8AnHzOD73csGsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;Playtomic&amp;#39;s Development Platform&quot;
        src=&quot;/static/e152ec79f3c1b671ddccd9d2eee9f7c2/66712/playtomic-development-arch.png&quot;
        srcset=&quot;/static/e152ec79f3c1b671ddccd9d2eee9f7c2/0eb09/playtomic-development-arch.png 500w,
/static/e152ec79f3c1b671ddccd9d2eee9f7c2/66712/playtomic-development-arch.png 561w&quot;
        sizes=&quot;(max-width: 561px) 100vw, 561px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
These are the components that form our pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A git repo: github&lt;/li&gt;
&lt;li&gt;A CI system: Jenkins&lt;/li&gt;
&lt;li&gt;An artefact store: nexus&lt;/li&gt;
&lt;li&gt;An application container: docker swarm&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Pipeline steps:&lt;/h1&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 829px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3118cdd034f0e91d5c480d06f538e094/c2a54/playtomic-pipeline.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.799999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAmUlEQVQI1z2OSw6CQBBEuf9tPIB7TFy4QE1I+EeQgRkJw/xouttMTKxF5dVbVUJEhLGOAzZtrXF/E8ORmfk3EFFKKYXSWjNz8pHLo7m2Q+HtXryy5p07E8rumbc3o72Q/b2+eBumecyqVClVVsU5PfVDL0aREPLqZQBHyCYsbtdM7GHbgmLiAG71MyHDsa9+igcRxNIRIQB8ASK0qnRVZnZ2AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;alt text&quot;
        title=&quot;Playtomic&amp;#39;s Pipeline Diagram&quot;
        src=&quot;/static/3118cdd034f0e91d5c480d06f538e094/c2a54/playtomic-pipeline.png&quot;
        srcset=&quot;/static/3118cdd034f0e91d5c480d06f538e094/0eb09/playtomic-pipeline.png 500w,
/static/3118cdd034f0e91d5c480d06f538e094/c2a54/playtomic-pipeline.png 829w&quot;
        sizes=&quot;(max-width: 829px) 100vw, 829px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Download code (obviously!)&lt;/li&gt;
&lt;li&gt;Validation (master: does not accept snapshots)&lt;/li&gt;
&lt;li&gt;Build + unit tests&lt;/li&gt;
&lt;li&gt;Integration tests&lt;/li&gt;
&lt;li&gt;Docker build &lt;/li&gt;
&lt;li&gt;Docker push&lt;/li&gt;
&lt;li&gt;Docker deploy&lt;/li&gt;
&lt;li&gt;Clean (be nice)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some snippets. I am skipping the content of the config class and sendNotificationOk(), but I think it is still clear.&lt;/p&gt;
&lt;p&gt;Download code (java node):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Download code&apos;&lt;/span&gt;&lt;span class=&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;deleteDir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    checkout scm
    env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;COMMIT_HASH &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lastLocalCommitHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;POM_VERSION &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mavenVersion&lt;/span&gt;&lt;span class=&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;/div&gt;
&lt;p&gt;Validation (java node):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isMasterBranch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BRANCH_NAME&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 punctuation&quot;&gt;(&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CHANGE_ID &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isMasterBranch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pullRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;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;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Validating project&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        sh &lt;span class=&quot;token string gstring&quot;&gt;&quot;mvn validate -Denforcer.skip=false&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;/div&gt;
&lt;p&gt;Build + unit tests (java node):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Build and test code&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    sh &lt;span class=&quot;token string gstring&quot;&gt;&quot;mvn clean package -DskipITs&quot;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;sendNotificationOk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Build and unit tests: OK&apos;&lt;/span&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;/div&gt;
&lt;p&gt;Integration tests (java node):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Integration tests&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    sh &lt;span class=&quot;token string gstring&quot;&gt;&quot;mvn verify&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sendNotificationOk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Integration tests: OK&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Docker build (docker node):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Docker Build&apos;&lt;/span&gt;&lt;span class=&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;// docker-compose.yml uses DOCKER_REPOSITORY and SERVICE_VERSION to tag the image of the service.&lt;/span&gt;
    sh &lt;span class=&quot;token string gstring&quot;&gt;&quot;DOCKER_REPOSITORY=&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;dockerRepository&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; SERVICE_VERSION=&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&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&gt; docker-compose -f docker-deploy/docker-compose.yml build&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Docker deploy (docker node):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token function&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string gstring&quot;&gt;&quot;Deployment to AWS - Docker Swarm &lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;environment&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&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;
    DOMAIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPublicDomain&lt;/span&gt;&lt;span class=&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;// no suffix for production&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;environment &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token string gstring&quot;&gt;&quot;pro&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;
        DOMAIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; DOMAIN &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string gstring&quot;&gt;&quot;-&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;environment&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// default for develop and features environments&lt;/span&gt;
    REPLICAS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// This is a ElasticIP&lt;/span&gt;
    DOCKER_HOST &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDevelopDockerHost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    DOCKER_CERT_PATH &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDevelopDockerCert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    profile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; environment
    &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;environment&lt;span class=&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;case&lt;/span&gt; &lt;span class=&quot;token string gstring&quot;&gt;&quot;develop&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string gstring&quot;&gt;&quot;pro&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            profile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string gstring&quot;&gt;&quot;amazon-pro&quot;&lt;/span&gt;
            REPLICAS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
            DOCKER_HOST &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getProDockerHost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            DOCKER_CERT_PATH &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getProDockerCert&lt;/span&gt;&lt;span class=&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;break&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    DOCKER_REPOSITORY &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dockerRepository
    SERVICE_VERSION &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; version

    &lt;span class=&quot;token comment&quot;&gt;// Remember that:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 1. We need docker login to access docker repository&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 2. We need --with-auth-registry to pass these docker credentials to the docker host (in order that it can access to docker repository)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 3. If we launch docker deploy in the remote machine, how does it access to the docker repo?&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 4. Conclusion: we are launching a ssh tunnel, and launching the docker deploy in this machine.&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// See: https://unix.stackexchange.com/questions/83806/how-to-kill-ssh-session-that-was-started-with-the-f-option-run-in-background&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// -L opens a ssh tunnel. -f goes to the background.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// This command opens the tunnel for 5 secs, but tunnel is not killed while it is used.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// So that, immediately after that we launch docker deploy which uses&lt;/span&gt;
    sh &lt;span class=&quot;token string gstring&quot;&gt;&quot;ssh -i &lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;DOCKER_CERT_PATH&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/cert.pem -f -L localhost:2374:/var/run/docker.sock docker@&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;DOCKER_HOST&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; sleep 5; DOCKER_REPOSITORY=&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;dockerRepository&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; SERVICE_VERSION=&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&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&gt; SPRING_PROFILES_ACTIVE=&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;profile&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; JAVA_ENABLE_DEBUG=&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;debug&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; REPLICAS=&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;REPLICAS&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; docker -Hlocalhost:2374 stack deploy &lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;DOMAIN&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; --with-registry-auth -c docker-deploy/docker-compose.yml&quot;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;sendNotificationOk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string gstring&quot;&gt;&quot;Deployed to &lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;DOMAIN&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&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;/div&gt;
&lt;p&gt;Clean (docker node):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token function&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;docker-deploy&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    sh &lt;span class=&quot;token string gstring&quot;&gt;&quot;rm -rf target/&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Out of this post:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Different pipelines for libraries and services.&lt;/li&gt;
&lt;li&gt;libraries: develop = SNAPSHOT; master = RELEASE.&lt;/li&gt;
&lt;li&gt;Auth: How to auth jenkins with github, nexus and docker swarm. Auth between jenkins and swarm is outlined in &lt;em&gt;deploy&lt;/em&gt; step.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Tips:&lt;/h2&gt;
&lt;p&gt;Make your commands explicits, that is, specify all environment variables with the command. This way you can replay them in your machine if something goes wrong by looking for the command in the log.&lt;/p&gt;
&lt;h2&gt;Next steps:&lt;/h2&gt;
&lt;p&gt;Some stuff that we want to incorporate or improve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System tests: set up a test environment in docker swarm. &lt;/li&gt;
&lt;li&gt;Quality gates: best implemented as pr-checks than in pipeline. If implemented in pipeline, don’t do in master (build would be broken after merge).&lt;/li&gt;
&lt;li&gt;Multistage docker as pipeline (pros: no specific jenkins nodes required. cons: slower: dependencies have to be downloaded every build.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Yet another static blog]]></title><description><![CDATA[Hello there! Yes, another blog written in a static page generator. Just what the Internet needed. Seriously. Why using a custom template and…]]></description><link>https://sgmoratilla.com/2019-04-10-welcome/</link><guid isPermaLink="false">https://sgmoratilla.com/2019-04-10-welcome/</guid><pubDate>Sun, 07 Apr 2019 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hello there! Yes, another blog written in a static page generator. Just what the Internet needed.&lt;/p&gt;
&lt;p&gt;Seriously. Why using a custom template and framework to do something that could be easily done at medium.com or dev.to?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ownership&lt;/strong&gt;. Being locked in a platform (such as dev.to, medium.com, wordpress.com, squarespace.com) has many drawbacks. Changes in the terms of use are the most annoying to me: price increases or changes in the features of your tier service (i.e squarespace.com), limiting the number of reads (i.e. medium.com), …
Besides, I’m tired of losing content every time I change of platform (or struggling in the migration process).&lt;/p&gt;
&lt;p&gt;Having a repo with your code and posts make that contents truly &lt;strong&gt;yours&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Yes, it is slower to setup and you will have to invest some time in maintenance occasionally. But it pays completely in terms of control of your content.&lt;/p&gt;
&lt;p&gt;So here I am. With a new shiny Gatsby site, a template written in TypeScript, whose code is hosted in GitHub and whose build is constructed and deployed by Netlify.&lt;/p&gt;
&lt;p&gt;PD: This is gonna be full of typos. Embrace mistakes!&lt;/p&gt;</content:encoded></item></channel></rss>