<?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" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[All About Coding]]></title><description><![CDATA[All About Coding]]></description><link>https://antousias.com/</link><image><url>https://antousias.com/favicon.png</url><title>All About Coding</title><link>https://antousias.com/</link></image><generator>Ghost 3.12</generator><lastBuildDate>Thu, 13 Nov 2025 09:49:42 GMT</lastBuildDate><atom:link href="https://antousias.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[How we built a high-performance fully-remote team]]></title><description><![CDATA[Tips on how we managed to build a high-performance fully-remote team in IOV42.]]></description><link>https://antousias.com/how-we-built-a-high-performance-fully-remote-team/</link><guid isPermaLink="false">5fa859c92fe481484c5c49b9</guid><category><![CDATA[IMHO]]></category><category><![CDATA[Best Practices]]></category><dc:creator><![CDATA[Alexandros Ntousias]]></dc:creator><pubDate>Tue, 17 Nov 2020 14:18:46 GMT</pubDate><media:content url="https://antousias.com/content/images/2020/11/header-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://antousias.com/content/images/2020/11/header-1.png" alt="How we built a high-performance fully-remote team"><p>Two years ago, when we were setting up our development team in <a href="https://iov42.com/">IOV42</a>, we made a conscious decision to enable remote work as much as possible. Of course, at that time, we were not really considering a fully remote team, but we wanted to have the flexibility to work remotely whenever we needed to.</p><p>During the first year we refined our team processes to such a degree that co-location was no longer necessary. This gradually led to all of us working from home more and more frequently.</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/11/wfh-2.jpg" class="kg-image" alt="How we built a high-performance fully-remote team"></figure><p>So, when the COVID-19 global pandemic broke out and we had to transition to a fully-remote team, we were ready! At some point we had people in our team working from Greece, France, Turkey, Austria and of course the UK...and nothing felt any different!</p><p>In the rest of this post I will try to present some the things that worked really well for us and hopefully, they will prove useful to more of you out there. Of course, every team is different, so don't expect them to work right out of the box. See what works for you, change it, own it!</p><p>Here we go....</p><!--kg-card-begin: markdown--><h2 id="asynchronouscommunicationwithslack">Asynchronous communication with <a href="https://slack.com/">Slack</a></h2>
<!--kg-card-end: markdown--><p>Naturally, when people are not co-located, they tend to create their own work schedule, which in most cases is different than everyone else's. Because of this, the need for asynchronous communication is imperative.</p><p>There are many ways and tools to achieve that. We decided to use <a href="https://slack.com/">Slack</a> which worked very well for us. But in order to get the most out of it, we had to improve and refine the ways we were using it.</p><p>Here are a few takeaways/tips from our experience:</p><!--kg-card-begin: markdown--><h4 id="1focusedchannels">1. Focused channels</h4>
<!--kg-card-end: markdown--><p>Each channel should have a very specific purpose and defined scope and only the required audience should be included. The last thing you want is people ignoring messages in a channel because a big percentage of these messages do not concern them.</p><!--kg-card-begin: markdown--><h4 id="2groupconversationsusingthreads">2. Group conversations using threads</h4>
<!--kg-card-end: markdown--><p>By using the <a href="https://slack.com/intl/en-gr/help/articles/115000769927-Use-threads-to-organize-discussions-">thread feature in Slack</a> to reply to a message, allows you to keep all the related replies grouped together. This in turn helps people to follow the discussion much easier, easily find the discussion in the future, and also reduce the noise for everyone else that might not be interested in that particular discussion.</p><!--kg-card-begin: markdown--><h4 id="3usereactions">3. Use reactions</h4>
<!--kg-card-end: markdown--><p>Remember, your colleagues cannot see you, so they don't know whether you saw something they posted or not. Reactions is a very handy way to acknowledge that you have seen a message even though you might not have anything to say about it.</p><!--kg-card-begin: markdown--><h4 id="4avoidlongdiscussionsspammingandflamewars">4. Avoid long discussions, spamming and flame-wars</h4>
<!--kg-card-end: markdown--><p>When you see that a discussion is derailing and spans over multiple messages, always prefer to jump on a direct call to resolve the issue and come to an agreement. The important thing is, once the issue is resolved with the direct communication, <em>don't forget to notify the rest of the team</em>!</p><!--kg-card-begin: markdown--><h2 id="synchronouscommunicationwithgooglemeet">Synchronous communication with <a href="https://meet.google.com/">GoogleMeet</a></h2>
<!--kg-card-end: markdown--><p>Of course, there are times when you need to have synchronous communication with one or more of your colleagues. The important thing here is to establish some best practices.</p><!--kg-card-begin: markdown--><ul>
<li>Invest in good gear. Get a decent microphone, preferrably a noise-cancelling one, and always wear headphones...it makes a huge differene in audio quality.</li>
<li>When you don't speak, please mute. Otherwise someone else might mute you!</li>
<li>Don't interrupt others when they talk, wait for your turn.</li>
<li>Try to have your camera enabled. This way, the communication feels more personal.</li>
<li>Finally, if your internet connection is not good, disable your camera. It's more important for people to hear you than to see you.</li>
</ul>
<!--kg-card-end: markdown--><p>If you don't establish some ground rules and practices, you might end up in situations like this:</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/11/video-calls.jpg" class="kg-image" alt="How we built a high-performance fully-remote team"></figure><p>As for the tool, again, many solutions...</p><p>We decided to go with <a href="https://meet.google.com/">GoogleMeet</a>. We were already using <a href="https://workspace.google.com/">Google Workspace</a> so it made sense to go with that. It's extremely easy to setup an instant meeting, it supports a sufficiently big number of participants, by enabling the grid view you can see all of them, it has great screen sharing features, and it integrates perfectly with the rest of the Google services, e.g. <a href="https://support.google.com/meet/answer/9302870?co=GENIE.Platform%3DDesktop&amp;hl=en">GoogleCalendar</a>.</p><p>Easy choice. Next!</p><!--kg-card-begin: markdown--><h2 id="virtualofficeusingspatialchat">Virtual office using <a href="https://spatial.chat/">SpatialChat</a></h2>
<!--kg-card-end: markdown--><p>One of the biggest issues we had was the loss of ad-hoc communication with colleagues. Co-location makes random conversations so much easier, which in turn very often leads to interesting ideas and innovations. It also strengthens the feel of togetherness that you have when you are all in the same office.</p><p>We've tried many different things to address that. We tried "<em>coffee breaks</em>", which were dedicated time slots every other day at which people could login into a GoogleMeet and socialise. We've tried "<em>virtual</em> <em>town hall</em>" rooms which would run the whole day and people could login whenever they wanted to socialise with the rest of the team. Unfortunately, none of that worked!</p><p>And then we discovered <a href="https://spatial.chat/">SpatialChat</a>!</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/11/mind-blown.gif" class="kg-image" alt="How we built a high-performance fully-remote team"></figure><p>SpatialChat allows you to mimic to a certain degree the interactions you might have in a physical space. When you login to a SpatialChat space, you can go to any of the rooms in that space you want. You are represented by an avatar and you can move yourself around with your mouse. The really cool thing is that the closer you get to someone, the louder you hear him. And of course you can share your screen with others, you can use the megaphone to talk to everyone in the room without having to go next to them, you can enable/disable video, etc.</p><p>From the very first few days of usage in IOV42, you would login and you could see groups of colleagues gathered around at different corners in a room, discussing. You could approach them, listen to what they were talking about, move away if the discussion wasn't relevant, start new conversations easily, etc.</p><p>The only thorny bit about it is probably the price. But still, it definitely worths a try!</p><!--kg-card-begin: markdown--><h2 id="collaborativediagramswithmiro">Collaborative diagrams with <a href="https://miro.com/">Miro</a></h2>
<!--kg-card-end: markdown--><p>When we were all in the same office, it was very easy to just go to a whiteboard and start sharing ideas by drawing architectural and design diagrams, algorithms, etc.</p><p>On our search to find something similar to this experience we ended up with <a href="https://miro.com/">Miro</a>. It's a great collaboration tool that provides whiteboards, diagrams and many more. You can use it to organise event storming sessions, retrospectives (we'll talk more about that in the next section), strategy meetings, share design ideas and many more!</p><p>And even though it offers so many capabilities, it's still extremely easy to use.</p><!--kg-card-begin: markdown--><h2 id="retrospectives">Retrospectives</h2>
<!--kg-card-end: markdown--><p>Something that really helps with the sustainability and efficiency of a team is retrospectives. But for fully-remote teams, it's a <em>must</em>! It's very easy for pressure to build up when you are not physically next to each other and you need to find ways to release it somehow.</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/11/retro-2.png" class="kg-image" alt="How we built a high-performance fully-remote team"></figure><p>Throughout my career, I've seen many different retrospective formats. Lately, and this is purely a personal opinion, I find that the easiest formats are also the most effective ones.</p><!--kg-card-begin: markdown--><p>For example, my personal favourite is:</p>
<ol>
<li>Create two columns, <em><strong>Liked</strong></em> and <em><strong>To improve</strong></em></li>
<li>Let people write in both columns for 10 minutes</li>
<li>Read through the <em>Liked</em> column outloud so that, as a team, we can celebrate our successes and boost our morale</li>
<li>Read through the <em>To improve</em> column outloud to have an idea of what is on the board</li>
<li>Group the tickets of the <em>To improve</em> column into more general (but not too broad) categories</li>
<li>Vote on what you want to talk about (each person gets three votes)</li>
<li>Start with the highest voted item and walk your way through them while time-boxing this exercise</li>
</ol>
<!--kg-card-end: markdown--><p><strong>Remember!</strong> The whole point of this exercise is to capture actions that will improve your processes. Also, what I find extremely useful is to always assign each of these actions to a particular person who will be responsible for carrying it out.</p><p>With a simple format like this, you don't get distracted by the framework itself and you can solely focus on the content.</p><p>There are many tools out there to perform remote retrospectives. But, in the spirit of <a href="https://en.wikipedia.org/wiki/KISS_principle">KISS</a>, we found that a simple GoogleDoc was all we needed! Again, don't distract the team with fancy tools, allow them to focus on the task in hand.</p><!--kg-card-begin: markdown--><h2 id="trustyourteam">Trust your team</h2>
<!--kg-card-end: markdown--><p>Tools and processes can only get you half way. The rest of it is constantly developing the proper mentality as a team.</p><p>Trust plays a really big role in that. As long as we acknowledge that no one is perfect and everyone is trying their best for the sake of the team, we can keep on improving. Be open about things that are not working out but also be mindful of other people's limitations. When you see issues, try to find solutions. When you see limitations, try to overcome them as a team. When you see positive improvements, try to encourage them.</p><p>Don't forget, you're all in this together as a team.</p><!--kg-card-begin: markdown--><h2 id="conclusion">Conclusion</h2>
<!--kg-card-end: markdown--><p>Having a fully-remote environment gives you a lot of benefits! People don't have to waste time commuting, it's much easier to focus in the comfort of your home, as a company you don't have to pay for big offices, employees tend to be more satisfied and you get higher retention, and in general it's a great incentive to attract talented people.</p><p>So, how do you start building a successful remote team?</p><p>It is all about finding the proper tools and processes to compensate to a certain degree what you will miss from not sharing the same physical location. Of course, it will never be exactly the same, but that's ok.</p><p>Once you have that, it's a constant refinement process. You see what works and what doesn't and you keep improving from there. Don't be afraid to experiment, try out new things and get rid of anything that doesn't work.</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/11/keep-calm-and-carry-on-working-remotely.png" class="kg-image" alt="How we built a high-performance fully-remote team"></figure>]]></content:encoded></item><item><title><![CDATA[Effective Code Reviews]]></title><description><![CDATA[Tips on how you could make your code review processes more effective.]]></description><link>https://antousias.com/effective-code-reviews/</link><guid isPermaLink="false">5eb830c82fe481484c5c4559</guid><category><![CDATA[IMHO]]></category><category><![CDATA[Best Practices]]></category><dc:creator><![CDATA[Alexandros Ntousias]]></dc:creator><pubDate>Wed, 13 May 2020 21:43:36 GMT</pubDate><media:content url="https://antousias.com/content/images/2020/05/source_code.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://antousias.com/content/images/2020/05/source_code.jpg" alt="Effective Code Reviews"><p>This is an edited and slightly expanded transcript of a lightning talk I gave for <a href="https://www.jhug.gr/">JHUG</a>'s virtual meetup.</p><!--kg-card-begin: html--><iframe src="//www.slideshare.net/slideshow/embed_code/key/53nj4eYgWzUF4P" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe><!--kg-card-end: html--><p></p><p>Throughout my years as a software engineer, I was fortunate enough to join several different teams in various companies. Each of these had different development processes and approaches when it comes to building software, but they had one thing in common: <em>Code Reviews</em>.</p><p>Each team used code reviews for different reasons. In some cases, a code review was a way to find defects before pushing something to production. In other cases, they acted as a knowledge sharing tool. There were teams that used code reviews as historical reference.</p><p>Regardless of how you use it though, there is one thing that is true in every project I've seen so far:</p><p><strong>Nobody likes code reviews!</strong></p><p>Code<em> authors</em> hate code reviews. They spend time writing all this code. And after all their hard work, they need to submit a code review and ask their teammates to spot all the errors and mention all the negative things they might have done.</p><p>On the other side, <em>code reviewers</em> also hate code reviews. They are working on some piece of code themselves when someone asks them for a review. They hate all this context switching, they want to focus on what they're currently working and not spend time looking at other people's code and have arguments about the best way to implement something.</p><p>So, a code review is a process that by nature results in conflict.</p><p>However, there are certain things that in my experience can make a huge difference and make the whole process less painful.</p><!--kg-card-begin: markdown--><h1 id="codeauthors">Code authors</h1>
<!--kg-card-end: markdown--><p>Let's start with what the code authors can do to ease the process for the reviewers.</p><!--kg-card-begin: markdown--><h2 id="keepitshortsweet">Keep it short &amp; sweet</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/05/Screenshot-2020-05-10-at-18.14.31.png" class="kg-image" alt="Effective Code Reviews"><figcaption>Keep it short &amp; sweet</figcaption></figure><p>Imagine yourself having to choose a new book to read.</p><p>Would you choose a book that has an enormous amount of pages or a book with reasonable size? Well, the reasonable size of course, it's much easier to read.</p><p>Many times, in order to develop a feature, we might have to change quite a lot of the existing code. We might have to add modules, implement new services, alter schema tables, etc. </p><p>However, the more changes we do, the harder it is for a reviewer to spot problems we might have missed. <strong><em>Always favour splitting a feature into smaller code reviews</em></strong> rather than include all the changes into a single one. A good rule to have is that a PR (pull request) shouldn't have more than 500 lines of code changes. </p><p></p><p>Back to the book analogy...let's say you have a choice between a book that talks about philosophy, politics, epic battles, music, art, mathematics, etc. And then you have another book that focuses only on one of these subjects. Which one is easier to read?</p><p>As code authors, we tend to massively expand the scope of our changes...it's very easy to get carried away when we work. How many times did you have to work on a feature and you ended up also fixing 2-3 bugs, along with a major refactoring?</p><p>This can be very confusing for a reviewer as he has to context switch between all the different things that your code have changed. <strong><em>Try to create focused reviews</em></strong> instead. As with code, high cohesion is preferable.</p><p></p><p>Finally, imagine a book that has all its chapters mixed and another one that has a logical continuation. Which one is easier to read?</p><p>In code reviews, these chapters are our commits. And <strong><em>your commit history should tell a comprehensive story</em></strong>. Of course, this is quite difficult to do, as we don't necessarily know from the beginning where some code changes might lead us.</p><p><a href="https://thoughtbot.com/blog/git-interactive-rebase-squash-amend-rewriting-history">Interactive rebase is your friend</a>. Once you're satisfied with your changes, try to formulate your commit history in a way that would make the reader understand the journey you went through. Squash and edit commits, reword your commit messages, etc.</p><p></p><!--kg-card-begin: markdown--><h2 id="givecontext">Give context</h2>
<!--kg-card-end: markdown--><p>Every code review needs to have a descriptive title that when a reviewer reads it, he/she immediately understands what these code changes are all about. If you are also using some issue tracking tool, it's worth including the issue number in the title as well.</p><p>In addition to the title, a code review should include a detailed description. Some things that are worth including in this description are:</p><!--kg-card-begin: markdown--><ul>
<li>Overview (as bullet-points) of the changes</li>
<li>Reasoning behind certain design choices</li>
<li>Potential problems that you had to go through that might help reviewers better understand your approach</li>
<li>Things that are missing or that you intentionally left out</li>
<li>Links to resources that could help the reviewers, e.g. link to the issue ticket that this code changes try to address</li>
</ul>
<!--kg-card-end: markdown--><p>Finally, it's very useful to annotate with comments all the places in your code review that might require clarification. This really helps the reviewers to avoid going through the same thought-process that the author went through and avoid asking the same questions.</p><p></p><!--kg-card-begin: markdown--><h2 id="chooseyouraudience">Choose your audience</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/05/choose_your_audience-1.png" class="kg-image" alt="Effective Code Reviews"></figure><p>Usually, when it comes to code reviews, the author must choose certain people from the team to review his code.</p><p>It's a good practice to include someone who is familiar with the area of code that you changed, for example the person who was the last one to modify it.</p><p>In addition to that, it's also useful to include someone unfamiliar with that particular code. The benefit is that he/she will manage to look at it with completely fresh eyes and might uncover problems or enhancements that you might not have thought about.</p><p>Finally, always make sure to include more people than the required number of reviews. For example, if your team's policy is to have 2 reviews, include at least 3-4 people. This allows the whole process to be more efficient as it takes into account that some of your team-members might be too busy or too distracted and others can take their place.</p><!--kg-card-begin: markdown--><h1 id="codereviewers">Code reviewers</h1>
<!--kg-card-end: markdown--><p>Now, let's have a look at what the reviewers should do when they receive a code review request.</p><!--kg-card-begin: markdown--><h2 id="testsasimportantasproductioncode">Tests as important as production code</h2>
<!--kg-card-end: markdown--><p>What's the best way to ensure that a piece of code is working as you expect? Well, testing of course!</p><p>A code review should always contain the necessary tests to back up the actual logic changes. This means that not only there should be tests, but they should be reviewed as well.</p><p>Some things you should be looking in the tests:</p><!--kg-card-begin: markdown--><ol>
<li>Are the tests readable?</li>
<li>Do they cover all edge cases?</li>
<li>Are the tests overly complicated?</li>
<li>Are the tests testing exactly what we need?</li>
<li>Is there any room for DRYing them up?</li>
</ol>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h2 id="toolingisimportant">Tooling is important</h2>
<!--kg-card-end: markdown--><p>Code reviews contain code. So, as you would use tools (e.g. IDEs) to write code easier, you should do the same with reviews. </p><p>I've seen so many people trying to use GitHub's browser-UI to review code changes. So they have to go up and down, trying to piece all the information together, trying to find usages using the browser's search functionality, etc. This is so difficult and inefficient. </p><p>It's also very easy to miss problems like that. For example, GitHub shows you only the code that has changed. But many times, you might want to also see the code that hasn't changed...for example, there might be duplication or you might have made a method obsolete, etc.</p><p>Here are a few tools that might help you with reviewing code:</p><!--kg-card-begin: markdown--><ul>
<li><strong><a href="https://www.codestream.com/">CodeStream</a></strong>: Plugin that allows you to do in-IDE code reviews. It is available for all major IDEs out there, i.e. IntelliJ, VSCode, Atom, Visual Studio. The downside of this one is that it requires read access to your GitHub repository, and your company might have strict rules about that.</li>
<li><strong><a href="https://www.jetbrains.com/upsource/">UpSource</a></strong>: This is a JetBrains product, so only available for IntelliJ. Also, it requires a JetBrains license (although at the moment it is free for up to 10 people). However, if your company is already using JetBrains products, you might want to go with that.</li>
<li><strong><a href="https://www.octotree.io/">Octotree</a></strong>: If you don't want an IDE-based solution, you could try a browser plugin. It doesn't give you all the benefits of an IDE of course, but at least it makes the review process a bit easier.</li>
<li><strong><a href="https://github.com/features/codespaces">Codespaces</a></strong>: GitHub recently released a browser-based IDE. Unfortunately, at the moment, there is limited access to this feature, so I personally haven't tried it. However, I can see how something like that might help with code reviews as well.</li>
</ul>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h2 id="focusondesignlogic">Focus on design &amp; logic</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/05/automate_everything-2.jpg" class="kg-image" alt="Effective Code Reviews"></figure><p>One of the things I hate in code reviews are comments like The<em> indentation is not correct here</em>, or <em>This variable is not used</em>.</p><p>Human reviewers should focus on everything that cannot be automated. This basically means logic and design. Making sure that the logic is sound, the design makes sense and is using the correct abstractions, the code is reusable and maintainable.</p><p>Everything else should be automated and should be checked in your CI pipeline. Some of the things you should be automating are:</p><!--kg-card-begin: markdown--><ol>
<li>Formatting</li>
<li>Static code analysis with predefined styling rules</li>
<li>Static code analysis for common bugs</li>
<li>Code coverage</li>
<li>Checking project external dependencies</li>
</ol>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h2 id="pickyourbattles">Pick your battles</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/05/bugs_me.jpg" class="kg-image" alt="Effective Code Reviews"></figure><p>There are so many things to look in a code review. Is the logic correct? Is the design abiding to the <a href="https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design">SOLID principles</a>? Design patterns? Performance? How about security? Is the solution over-engineered? Is it following the team's and company's agreed practices?</p><p>In addition to all the above, you need to keep in mind that different team members might have different interpretations or trade-offs of these. You might want to sacrifice some of your performance in favour of readability. You might not want to use a design pattern at this point to avoid premature optimisations.</p><p>So, to avoid making a code review drag for a long time and also maintain the good dynamics of the team, you should pick your battles. Nobody likes someone who leaves comments for every single problem he finds in a code review. Make sure you <em>only</em> mention problems that are very important to you and leave the rest.</p><p></p><!--kg-card-begin: markdown--><h2 id="wearthenobuthat">Wear the &quot;No, but...&quot; hat</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/05/no-but.png" class="kg-image" alt="Effective Code Reviews"></figure><p>Once you find a problem that you want fixed, avoid just saying <em>No, this won't work</em>. Chances are that the author had something else in mind, so just shooting down his/her ideas is a recipe for disappointment and .</p><p>Instead, try to explain why something might not work or it can be written in a better way. Give concrete reasons. </p><p>Also, try to give solutions not only focusing on the problems. Be constructive. In general, it's good to have the "<em>Yes, and ....</em>" and "<em>No, but ....</em>" mentality.</p><p></p><!--kg-card-begin: markdown--><h2 id="avoidlongthreadsflamewars">Avoid long threads &amp; flame wars</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/05/avoid-long-threads.png" class="kg-image" alt="Effective Code Reviews"><figcaption>Prefer direct communication over long threads</figcaption></figure><p>There are times where both sides, the author and the reviewer, have conflicting thoughts about a certain approach. This can lead to long threads of comments, and heated arguments, each side arguing why their approach is better. This promotes long-lasting code reviews and negativity in general. </p><p>Instead, what I've seen working much better is, in the first sign of a long conflict, to pick up the phone and talk to the other person directly. This makes the whole process much faster as it makes the communication synchronous.</p><p>In addition to that, it makes the conflict resolution much easier. When you speak to the other person directly, you have more empathy and there are less chances you will be as judgemental and hurtful as you might have been over asynchronous comments.</p><p>One important step in this process, is to update the code review with a summary of your discussion. This way, other people are aware of what happened and what was agreed.</p><p></p><!--kg-card-begin: markdown--><h2 id="praisegoesalongway">Praise goes a long way</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/05/praise.png" class="kg-image" alt="Effective Code Reviews"></figure><p>Finally, be positive! Always try to find something positive to say, celebrate your successes as a team. You cannot imagine how important this is.</p><p>As developers, we have big egos...we are very proud of the code we're writing and we like to share it with our peers. So, congratulating a code author for something he/she wrote or a design decision he/she took can go a long way!</p>]]></content:encoded></item><item><title><![CDATA[Deep dive into Cassandra]]></title><description><![CDATA[<p>Let's start with a disclaimer....I love <a href="http://cassandra.apache.org/">Cassandra</a>! I have used it in a couple of projects and I was always impressed by what is offering.</p><p>But I also believe that Cassandra is one of those technologies that should only be applied to specific use cases. And I assume that's</p>]]></description><link>https://antousias.com/deep-dive-into-cassandra/</link><guid isPermaLink="false">5e8ddbe02fe481484c5c3bc8</guid><category><![CDATA[Software Architecture]]></category><category><![CDATA[Deep Dive]]></category><dc:creator><![CDATA[Alexandros Ntousias]]></dc:creator><pubDate>Wed, 15 Apr 2020 18:44:12 GMT</pubDate><media:content url="https://antousias.com/content/images/2020/04/eye-bg-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://antousias.com/content/images/2020/04/eye-bg-1.jpg" alt="Deep dive into Cassandra"><p>Let's start with a disclaimer....I love <a href="http://cassandra.apache.org/">Cassandra</a>! I have used it in a couple of projects and I was always impressed by what is offering.</p><p>But I also believe that Cassandra is one of those technologies that should only be applied to specific use cases. And I assume that's where most of the hatred is coming from, teams trying to use it even though it doesn't really fit  their use case.</p><p>But before I get carried away, let's first have a look at what is Cassandra and how it's working internally.</p><!--kg-card-begin: markdown--><h2 id="whatiscassandra">What is Cassandra</h2>
<!--kg-card-end: markdown--><p>Cassandra is a highly-scalable, highly-available distributed <a href="https://martinfowler.com/bliki/NosqlDefinition.html">NoSQL database</a>. It was created at Facebook and later was open-sourced under the Apache umbrella.</p><p>It's a column-oriented database and its data model is based on <a href="https://cloud.google.com/bigtable/docs/overview">Google's BigTable</a>, while its data distribution design is based on <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html">Amazon's DynamoDB</a>.</p><p>It's very well suited for handling massive amount of load with very good performance while offering <strong>linear scalability</strong> (the more nodes you add, the more requests can handle) and <strong>no single point of failure</strong>.</p><p>These are some of the reasons why a lot of big companies such as Facebook, Twitter, Netflix, eBay etc. are using it.</p><!--kg-card-begin: markdown--><h2 id="clusterarchitecture">Cluster architecture</h2>
<!--kg-card-end: markdown--><p>When Facebook created Cassandra, they had a specific use case in mind. It was supposed to be used in their messaging app, so their main focus was availability and not strong consistency. And it makes sense in the context of a messaging app, you care more about being able to send a message and not so much about getting every message the exact moment they are sent.</p><!--kg-card-begin: markdown--><h3 id="datadistribution">Data distribution</h3>
<!--kg-card-end: markdown--><p>With that in mind, Cassandra's clusters don't have a special master/coordinator node. <em>Nodes in the cluster are peers</em>, they are exactly the same and clients can use any node to connect to the cluster and do reads and writes.</p><p>The nodes are <em>logically</em> organised into a ring. That's why we often hear the term <strong>Cassandra ring</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/04/cassandra_ring.png" class="kg-image" alt="Deep dive into Cassandra"><figcaption>Cassandra ring</figcaption></figure><p>Each node is owning a part of the overall data that is stored in the cluster. For data distribution, Cassandra uses <a href="https://antousias.com/consistent-hash-rings/">consistent hashing</a> to decide the owner node of a piece of data. For hashing it uses the <strong>primary key</strong> of the data (we will discuss more about this later, when we talk about Cassandra's data model) and the <a href="https://www.sderosiaux.com/articles/2017/08/26/the-murmur3-hash-function--hashtables-bloom-filters-hyperloglog/"><strong>Murmur3</strong> algorithm</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/04/cassandra_data_stored.png" class="kg-image" alt="Deep dive into Cassandra"><figcaption>Data distribution in token ring using murmur3 and partition key</figcaption></figure><p>To achieve the desired availability, we need to replicate data. Cassandra's <strong>replication factor </strong>basically tells the cluster how many copies of the data it should keep. A typical value of this is 3. So, when a client sends a new piece of data to be written, Cassandra will first write it to the node that owns this data (as we showed above) and then it will send it to the appropriate nodes to replicated according to the replication factor.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/04/cassandra_replication-1.png" class="kg-image" alt="Deep dive into Cassandra"><figcaption>Data distribution with replication factor</figcaption></figure><p>In addition to the local data distribution, Cassandra also offers <strong>global distribution</strong>. In other words, it allows you to setup clusters in different datacenters, and Cassandra will take care of the distribution. This is a very popular feature of Cassandra as it allows geographic distribution of data which leads to lower latencies.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/04/cassandra_global_distribution.png" class="kg-image" alt="Deep dive into Cassandra"><figcaption>Data distribution across datacenters</figcaption></figure><!--kg-card-begin: markdown--><h3 id="gossipprotocol">Gossip protocol</h3>
<!--kg-card-end: markdown--><p>Based on what we've said so far, the following question should arise naturally:</p><!--kg-card-begin: markdown--><blockquote>
<p>If there is no master or special node in the cluster, and no configuration server is used, how does each node know about the other nodes in the cluster?</p>
</blockquote>
<!--kg-card-end: markdown--><p>And the answer to this question is all the nodes in the network are using the <strong>gossip protocol</strong>. </p><p>Gossip protocol is a peer-to-peer communication protocol that is based on the actual gossip that occurs in social networks.</p><!--kg-card-begin: markdown--><p>Basically, each node in the cluster randomly communicates with other nodes and sends information that it currently knows, for example the cluster topology. This helps Cassandra nodes achieve three things:</p>
<ol>
<li>It's a great way to propagate knowledge regarding the status of the cluster, which nodes are currently participating and how to reach them</li>
<li>It makes node membership (new nodes joining the cluster) easy</li>
<li>Provides easy healthcheck as nodes can easily detect whether another node is down or not</li>
</ol>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="consistency">Consistency</h3>
<!--kg-card-end: markdown--><p>Cassandra offers tunable consistency. It allows you to sacrifice performance and availability to achieve greatest consistency. Besides, as the <a href="https://en.wikipedia.org/wiki/CAP_theorem">CAP theorem</a> states, we cannot have both strong-consistency and high-availability in a distributed system.</p><p>In Cassandra, we have a <strong>per-request consistency level</strong>. For each request, either a write or a read, the client can define how strong of consistency he/she wants. Basically, the client specifies how many nodes he/she wants to acknowledge the request before Cassandra replies back. Some of the most popular options are:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th style="text-align:center">Consistency Level</th>
<th style="text-align:center">Write Request</th>
<th style="text-align:center">Read Request</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">ONE</td>
<td style="text-align:center">Written by at least one replica</td>
<td style="text-align:center">Result of the closest replica</td>
</tr>
<tr>
<td style="text-align:center">TWO</td>
<td style="text-align:center">Written by at least two replicas</td>
<td style="text-align:center">Most recent data from the two closest replicas</td>
</tr>
<tr>
<td style="text-align:center">THREE</td>
<td style="text-align:center">Written by at least three replicas</td>
<td style="text-align:center">Most recent data from the three closest replicas</td>
</tr>
<tr>
<td style="text-align:center">LOCAL_QUORUM</td>
<td style="text-align:center">Written by the quorum of replicas in the datacenter</td>
<td style="text-align:center">Result after a quorum of the replicas in the same datacenter</td>
</tr>
<tr>
<td style="text-align:center">QUORUM</td>
<td style="text-align:center">Written by the quorum of replicas <em>across all datacenters</em></td>
<td style="text-align:center">Result after a quorum of the replicas <em>across all datacenters</em></td>
</tr>
<tr>
<td style="text-align:center">EACH_QUORUM</td>
<td style="text-align:center">Written by the quorum of replicas in <em>each datacenter</em></td>
<td style="text-align:center">N/A</td>
</tr>
<tr>
<td style="text-align:center">ALL</td>
<td style="text-align:center">Written by all the replicas</td>
<td style="text-align:center">Result from all the replicas</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>The further you go down in the above list, the lower the availability gets while the consistency increases. For example, in the <code>ALL</code> case, you get a very strong consistency, but if a datacenter or even a single replica is down, the request fails.</p><p>However, in most of the projects I've worked with Cassandra, given that it was the tool of choice because of its performance and availability and also due to the fact we could tolerate eventual consistency, we wouldn't go above <code>QUORUM</code> which actually strikes a nice balance between availability and consistency. In some cases, we were even using <code>ONE</code>.</p><p>Now, let's say we have a read request that uses consistency level of <code>TWO</code>. And let's assume that for some reason, the two replica nodes return different results. How does Cassandra decide which data should be returned?</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/04/cassandra_conflict_resolution-1.png" class="kg-image" alt="Deep dive into Cassandra"><figcaption>Conflict resolution</figcaption></figure><p>In situations like that, Cassandra uses the <code>last write wins</code> scheme, and picks the most recent data to return. So, in the above example, if <code>Result B</code> was written after <code>Result A</code> (i.e. <em>node 2 </em>wasn't up-to-date), the cluster would reply with <code>Result B</code>.</p><p>In addition to the above conflict resolution, Cassandra will also perform a <strong>read repair</strong> in situations like that. Basically, upon a read request, when it detects that some replicas are not up-to-date, it will try to fix them. So it will send the correct value to all these out-of-date replicas which will perform this operation like a normal write (with backdated timestamp).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/04/cassandra_read_repair.png" class="kg-image" alt="Deep dive into Cassandra"><figcaption>Read repair</figcaption></figure><!--kg-card-begin: markdown--><p>Read repair is one of the <strong>anti-entropy mechanisms</strong> that Cassandra is using to make sure data is consistent. Some others that we won't see in this post are:</p>
<ol>
<li>Hinted-handoff: When a node goes down for a relatively small period of time, when it comes back up, the other replicas will stream to it all the additional data that were stored while the node was away.</li>
<li>Repair: While read repair fixes specific data on specific replicas, repair will fix consistency issues across your cluster. It's a good practice to run this operation frequently (for example weekly) to ensure the data integrity in your cluster.</li>
</ol>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="nodearchitecture">Node architecture</h2>
<!--kg-card-end: markdown--><p>So far, we've seen how different Cassandra nodes in a cluster work together to serve read and write requests. But what exactly happens in each of these nodes when such a request comes in?</p><p>Let's start with the write requests. </p><!--kg-card-begin: markdown--><h3 id="writepath">Write path</h3>
<!--kg-card-end: markdown--><p>When a write comes in, it will first be persisted to disk into a <strong>commit log</strong>. This commit log is an append-only file and contains every write request that the node receives. These entries survive even if the node goes down and they are basically used for recovery in case of failures.</p><p>The node also writes the request into an in-memory cache called <strong>memtable</strong>. This cache accumulates writes that have the same key (i.e. the same partition key) and is being used to serve reads for data that haven't been persisted to disk yet. A node has one memtable for each of its tables.</p><p>Once both of these operations happen, the node will send an acknowledgement back to the caller.</p><p>The memtable will keep accumulating write requests per key in a sorted fashion until it reaches a specific configurable limit. Once it reaches that limit, then the whole memtable is flushed to disk, in a sorted strings table called <strong>SSTable</strong>. This is a concept that comes from <a href="https://cloud.google.com/bigtable/docs/overview">Google's BigTable</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/04/cassandra_write_node.png" class="kg-image" alt="Deep dive into Cassandra"><figcaption>Write path in a Cassandra node</figcaption></figure><p>Periodically, each node will run a background process called <strong>compaction</strong> to merge the SSTables into a single one. Having a single SSTable helps with the performance of read requests. There are many different types of <a href="http://cassandra.apache.org/doc/latest/operating/compaction.html">compaction techniques</a>, but we won't be covering them in this post.</p><p>One thing to keep in mind is that all the above structures are append-only structures. When we update existing rows, we do not overwrite data. Instead we create new entries and we leave it to the compaction and read paths to decide which entry to use. Basically, as we saw earlier, Cassandra uses the <code>last write wins</code> scheme.</p><p>The same is true for delete operations. Deletes do not actually delete data, but they mark them as deleted by creating a new entry for this data called a <strong>tombstone</strong>.</p><p>Ok, let's see now how the read path works.</p><!--kg-card-begin: markdown--><h3 id="readpath">Read path</h3>
<!--kg-card-end: markdown--><p>The read path is quite straightforward. When a read request comes in, the node will check the SSTables and the Memtables to find the data requested. Since both SSTables and Memtables are sorted by key, these operations are quite fast.</p><p>In addition to that, to avoid going to disk for every single SSTable to check if it contains data for the given key, it uses <a href="https://en.wikipedia.org/wiki/Bloom_filter">bloom filters</a> to quickly verify whether it should hit the disk or not. A bloom filter is a probabilistic data structure that tells you whether an element is definitely not in the set or it maybe is. So, if the filter tells you that a key is not in a given SSTable, there is no need to go and check it. On the other hand, if it tells you maybe, then we need to check it since we don't know whether it is or it isn't.</p><p>Once it retrieves the data, it will aggregate them by removing duplicates and discarding older entries (keeping only the latest update) and tombstones and it will return them back to the caller.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://antousias.com/content/images/2020/04/cassandra_read_path.png" class="kg-image" alt="Deep dive into Cassandra"><figcaption>Read path in a Cassandra node</figcaption></figure><p>One thing to keep in mind at this point is that since both memtable and sstables are sorted by partition key, if we need to query data with something else other than the key, Cassandra will struggle. It will need to go over the entirety of the node's SSTables as well as memtables to find what you're looking for. Moreover, given that Cassandra determines the nodes that hold specific data based on the data's primary key, when we search with something else we actually have to search the entire cluster! That's why we <strong>DO NOT</strong> use Cassandra for relational modelling.</p><!--kg-card-begin: markdown--><h2 id="datamodelling">Data modelling</h2>
<!--kg-card-end: markdown--><p>The way to create, insert and query data in Cassandra is by using its query language called CQL (Cassandra Query Language), which is similar to a subset of SQL.</p><p>The top-level construct of CQL is a <strong>keyspace</strong>. If we want to parallelise it to the relational world, a keyspace would be something like a database. A keyspace has a single attribute that you can define which is the replication factor. All the tables inside that keyspace will have this replication factor. And we can be as specific as we want for this, for example defining different replication for the different datacenters that we have in the cluster.</p><pre><code class="language-cql">CREATE KEYSPACE my_keyspace WITH REPLICATION = {
	'class': 'NetworkTopologyStrategy',
    'dc1': 2,
    'dc2': 3
};</code></pre><p>A keyspace can have one or more tables. Tables are consisted of columns which can be any of the <a href="http://cassandra.apache.org/doc/latest/cql/types.html">supported types</a>. Each table must have a <strong>primary key</strong> which should be unique for each row in that table.</p><p>One very important thing is that the primary key is different than the <strong>partition key</strong>. The partition key is formed from the first column that participates in the primary key of the table. This partition key is the one that determines in which node the data will be stored and also will be used for lookups within the node's Memtable and SSTables.</p><pre><code class="language-cql">CREATE TABLE events(
    event_address    text,
    event_timestamp  timestamp,
    username         text,
    PRIMARY KEY(event_address, event_timestamp)
);</code></pre><p>So, in the above example, the table <code>events</code> has a primary key which consists of <code>event_address</code> and <code>event_timestamp</code>. It also has a partition key which is the <code>event_address</code>.</p><p>Inserting and querying data is equivalent to the respective procedures in the SQL-world. The only thing to be careful though, as we've already mentioned, is that data queries must include the partition key for the reasons we explained earlier in the post.</p><pre><code>INSERT INTO events(event_address, event_timestamp, username)
VALUES ('acquisition_1', '2020-04-13', 'antousias');

SELECT * FROM events WHERE event_address = 'acquisition_1';
</code></pre><!--kg-card-begin: markdown--><h2 id="personalthoughtsoncassandra">Personal thoughts on Cassandra</h2>
<!--kg-card-end: markdown--><p>As I've mentioned in the beginning, because of the way Cassandra is architectured, it's extremely good at certain use cases and extremely bad at others!</p><p>I have used it in the past in various projects with great success. In particular, we had a project in King where our system had to support around 1,000,000 requests per second from all around the world. Cassandra could handle that without breaking a sweat. Good luck doing that with another database.</p><p>Having said that, there are a few things you should take into account before deciding to go down the Cassandra route.</p><!--kg-card-begin: markdown--><h3 id="knowyourqueries">Know your queries</h3>
<!--kg-card-end: markdown--><p>You should know the queries before creating your tables. Or at least have an idea of what is going to be the partition key of each table. </p><p>As we discussed earlier, the partition key needs to be part of the read requests, so that Cassandra can know which nodes to contact and also in each node to easily find the required entries. Changing from one partition key to another is an expensive and painful operation...so make sure you nail that from the beginning.</p><!--kg-card-begin: markdown--><h3 id="choosepartitionkeyswisely">Choose partition keys wisely</h3>
<!--kg-card-end: markdown--><p>Choosing the correct partition key can have tremendous impact on the performance of your Cassandra cluster. </p><p>But what constitutes a good partition key?</p><p>Ideally, partition keys should be uniformly distributed but not extremely scattered. We want to avoid hot-spots that will lead to extremely huge replica nodes, but we also want to avoid partition keys that lead to groups of just a handful of entries.</p><!--kg-card-begin: markdown--><h3 id="availabilityorconsistency">Availability or consistency?</h3>
<!--kg-card-end: markdown--><p>A big factor of success with Cassandra is understanding the requirements of your use case and choosing the availability and consistency level you want carefully. </p><p>As we saw earlier, availability comes mainly from setting the appropriate replication factor in your keyspace. Consistency on the other hand comes from choosing the consistency level for each read and write queries. Some applications will need availability over consistency, while others the opposite. Remember, <a href="https://en.wikipedia.org/wiki/CAP_theorem">you cannot have both</a>!</p><p>If you want to strike a balance between these two, the following equation is a good rule of thumb:</p><pre><code>CL.READ + CL.WRITE &gt; RF

where:
- CL.READ:  Consistency level used for reads
- CL.WRITE: Consistency level used for writes
- RF:       Replication Factor</code></pre><!--kg-card-begin: markdown--><h3 id="devopsarepeopletoo">Devops are people too</h3>
<!--kg-card-end: markdown--><p>One of the pain points of Cassandra is the fact that it needs careful maintenance. Although you get high availability and any failures that might happen are completely invisible to the client, this comes at a cost.</p><p>First you need to maintain your data integrity and consistency. This requires analysing your cluster topology and running repair operations frequently.</p><p>You also need to have a good understanding of how Cassandra works internally. In most cases, you won't have to deal with any internal problems. In most cases you will get the awesome speed and availability that Cassandra is known for. In most cases the cluster will recover on its own from any failures that might happen.</p><p>But, there are these rare moments....</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/04/why_god_why.gif" class="kg-image" alt="Deep dive into Cassandra"></figure><!--kg-card-begin: markdown--><h3 id="areyourichenough">Are you rich enough?</h3>
<!--kg-card-end: markdown--><p>Last but not least, Cassandra can be really expensive!</p><p>As we saw earlier, Cassandra is using the gossip protocol for inter-node communication. This can generate a lot of messages and if you're running your cluster in the cloud can increase your monthly bills by a big factor.</p><p>Also, if you run a cluster on multiple datacenters, you should be careful on the replication factor you're using as well as the consistency level you choose for your queries. This again can generate a lot of data traffic and this time is across different availability zones, which is even more expensive.</p><!--kg-card-begin: markdown--><h2 id="conclusion">Conclusion</h2>
<!--kg-card-end: markdown--><p>Regardless of these issues, I think Cassandra is an amazing piece of technology. When a database gives you so impressive features, it is expected to have some drawbacks as well. </p><p>So, if it fits your use case, definitely give it a try. You won't regret it a bit.</p><p>But remember...</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/04/remember-with-great-1.jpg" class="kg-image" alt="Deep dive into Cassandra"></figure>]]></content:encoded></item><item><title><![CDATA[Consistent hash rings]]></title><description><![CDATA[<p>Systems nowadays are expected to be scalable and highly-available. They should be able to handle any load given to them (always in the boundaries of the agreed SLA) and since they usually run on cheap machines they should be fault-tolerant and always serve requests even if some of the machines</p>]]></description><link>https://antousias.com/consistent-hash-rings/</link><guid isPermaLink="false">5e8ecbc32fe481484c5c3caa</guid><category><![CDATA[Software Architecture]]></category><category><![CDATA[Distributed Systems]]></category><dc:creator><![CDATA[Alexandros Ntousias]]></dc:creator><pubDate>Thu, 09 Apr 2020 13:46:12 GMT</pubDate><media:content url="https://antousias.com/content/images/2020/04/architecture_tag-1.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://antousias.com/content/images/2020/04/architecture_tag-1.jpeg" alt="Consistent hash rings"><p>Systems nowadays are expected to be scalable and highly-available. They should be able to handle any load given to them (always in the boundaries of the agreed SLA) and since they usually run on cheap machines they should be fault-tolerant and always serve requests even if some of the machines die.</p><p>How do we achieve that? Well, that's easy...</p><p>Make a service stateless, startup a bunch of instances of it, and put the instances behind a load balancer.</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/04/job_done-1.jpg" class="kg-image" alt="Consistent hash rings"></figure><p><strong>But wait a minute....!</strong></p><p>There are times that we actually want to make sure a certain request is routed to the same instance.</p><p>Some common use cases of this is when we use in-memory caching and we want to minimise cache misses or when the instances of the service are stateful.</p><!--kg-card-begin: markdown--><h2 id="hashingthenaiveway">Hashing...the naive way</h2>
<!--kg-card-end: markdown--><p>A way to redirect a request into the same node is by using <em>hashing</em>. Basically, apply a hash function to a request to get a number and then somehow map this number to a specific node.</p><p>The naive and easy solution to this would be to do a modulo on the hash result with the total number of nodes we have, thus getting a number that corresponds to one of our nodes.</p><pre><code>node_number = hash(request) mod total_number_of_nodes</code></pre><p>So, as an example, let's assume we have a total of 3 nodes and the following 4 requests:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th style="text-align:center">Request</th>
<th style="text-align:center">Hash</th>
<th style="text-align:center">Node</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">A</td>
<td style="text-align:center">1337</td>
<td style="text-align:center">2</td>
</tr>
<tr>
<td style="text-align:center">B</td>
<td style="text-align:center">1338</td>
<td style="text-align:center">0</td>
</tr>
<tr>
<td style="text-align:center">C</td>
<td style="text-align:center">1339</td>
<td style="text-align:center">1</td>
</tr>
<tr>
<td style="text-align:center">D</td>
<td style="text-align:center">1330</td>
<td style="text-align:center">2</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>Let's assume now that suddenly the load increases so the autoscaler spins up one more node. Now we have 4 nodes. Let's see what happens to the above example:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th style="text-align:center">Request</th>
<th style="text-align:center">Hash</th>
<th style="text-align:center">Node (Before)</th>
<th style="text-align:center">Node (Now)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">A</td>
<td style="text-align:center">1337</td>
<td style="text-align:center">2</td>
<td style="text-align:center">1</td>
</tr>
<tr>
<td style="text-align:center">B</td>
<td style="text-align:center">1338</td>
<td style="text-align:center">0</td>
<td style="text-align:center">2</td>
</tr>
<tr>
<td style="text-align:center">C</td>
<td style="text-align:center">1339</td>
<td style="text-align:center">1</td>
<td style="text-align:center">3</td>
</tr>
<tr>
<td style="text-align:center">D</td>
<td style="text-align:center">1340</td>
<td style="text-align:center">2</td>
<td style="text-align:center">0</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>As we can see, all the requests were affected by the addition of a new node. Basically, the distribution is changing whenever we change the number of nodes.</p><p>Obviously, this is something we want to avoid. And this is exactly what <em>consistent hashing</em> is solving.</p><!--kg-card-begin: markdown--><h2 id="consistenthashing">Consistent hashing</h2>
<!--kg-card-end: markdown--><p>In consistent hashing, we think of the nodes as points on a ring (thus the name <em>consistent hashing ring</em>).</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/04/hashing_1.png" class="kg-image" alt="Consistent hash rings"></figure><p>These points can be determined in various ways. For example, we could use something like the following:</p><pre><code>node_point = hash(node_ip_address) mod 360°</code></pre><p>Then, the incoming requests are also mapped as points on this ring. In other words, instead of doing a modulo with the total number of nodes as we did before, we do a modulo with 360°.</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/04/hashing_2.png" class="kg-image" alt="Consistent hash rings"></figure><p>Finally, once we have a request as a point in our ring, the node that should be used for this request is defined as the next node on the ring in a clockwise order.</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th style="text-align:center">Request</th>
<th style="text-align:center">Node</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">A</td>
<td style="text-align:center">2</td>
</tr>
<tr>
<td style="text-align:center">B</td>
<td style="text-align:center">3</td>
</tr>
<tr>
<td style="text-align:center">C</td>
<td style="text-align:center">1</td>
</tr>
<tr>
<td style="text-align:center">D</td>
<td style="text-align:center">1</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>Let's see now what happens when we add a new node in the ring:</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/04/hashing_3.png" class="kg-image" alt="Consistent hash rings"></figure><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th style="text-align:center">Request</th>
<th style="text-align:center">Node (Before)</th>
<th style="text-align:center">Node (Now)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">A</td>
<td style="text-align:center">2</td>
<td style="text-align:center">2</td>
</tr>
<tr>
<td style="text-align:center">B</td>
<td style="text-align:center">3</td>
<td style="text-align:center">3</td>
</tr>
<tr>
<td style="text-align:center">C</td>
<td style="text-align:center">1</td>
<td style="text-align:center">4</td>
</tr>
<tr>
<td style="text-align:center">D</td>
<td style="text-align:center">1</td>
<td style="text-align:center">1</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>We notice that, unlike before, only a single request was affected by this change, request C.</p><!--kg-card-begin: markdown--><h2 id="conclusion">Conclusion</h2>
<!--kg-card-end: markdown--><p>Consistent hashing is a great way to achieve hashing in a distributed system which is independent of the number of servers or objects in the system. It's definitely a very useful tool that any engineer dealing with distributed systems should have in his toolbox.</p><p>In fact, a lot of the modern distributed highly-scalable databases such as <a href="https://antousias.com/deep-dive-into-cassandra/">Cassandra</a>, <a href="https://www.dynamodbguide.com/the-dynamo-paper/#infinitely-scalable">DynamoDB</a>, <a href="https://www.project-voldemort.com/voldemort/design.html">VoldemortDB</a>, and many more are using consistent hashing internally to determine the nodes that likely contain the information the user is trying to retrieve.</p>]]></content:encoded></item><item><title><![CDATA[Deep dive into Kafka (pt 2)]]></title><description><![CDATA[<p>In the <a href="https://antousias.com/deep-dive-into-kafka-part-1/">previous post</a> we saw how Kafka topics and partitions work. Let's now have a closer look at how producers and consumers publish and consume messages respectively. Finally, we'll talk about the different delivery guarantees that Kafka provides and how we can configure each one of them.</p><!--kg-card-begin: markdown--><h2 id="producers">Producers</h2>
<!--kg-card-end: markdown--><p>A</p>]]></description><link>https://antousias.com/deep-dive-into-kafka-part-2/</link><guid isPermaLink="false">5e8c6449f8ca150df0f97041</guid><category><![CDATA[Software Architecture]]></category><category><![CDATA[Distributed Systems]]></category><category><![CDATA[Deep Dive]]></category><dc:creator><![CDATA[Alexandros Ntousias]]></dc:creator><pubDate>Wed, 25 Mar 2020 17:50:00 GMT</pubDate><media:content url="https://antousias.com/content/images/2020/04/kafka_bg_2-4.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://antousias.com/content/images/2020/04/kafka_bg_2-4.jpg" alt="Deep dive into Kafka (pt 2)"><p>In the <a href="https://antousias.com/deep-dive-into-kafka-part-1/">previous post</a> we saw how Kafka topics and partitions work. Let's now have a closer look at how producers and consumers publish and consume messages respectively. Finally, we'll talk about the different delivery guarantees that Kafka provides and how we can configure each one of them.</p><!--kg-card-begin: markdown--><h2 id="producers">Producers</h2>
<!--kg-card-end: markdown--><p>A producer is an application that publishes messages to a Kafka topic. A message is essentially a key-value pair, where the <em><strong>key</strong></em> is used to identify the partition of the topic that the event will be published to and the <em><strong>value</strong></em> is the actual message.</p><p>Pretty simple, huh? Although Kafka is a very complicated system in its core, all this complexity is hidden away and its producer and consumer abstractions are very easy to grasp and simple to use.</p><!--kg-card-begin: markdown--><h3 id="producerinternals">Producer Internals</h3>
<!--kg-card-end: markdown--><p>In order to create a producer, you need to supply the following configurations:</p><ul><li><strong><em>bootstrap.servers</em></strong>: Connection details for at least one of the Kafka brokers in the cluster. By connecting to one broker, you essentially connect to the whole cluster. It's actually a good practice to supply more than one broker, in case one of them is down.</li><li><strong><em>key.serializer</em></strong>: The serialiser class which converts the key of a record to a format understood by Kafka.</li><li><strong><em>value.serializer</em></strong>: The serialiser class which converts the value of a record to a format understood by Kafka.</li></ul><p>There are more configuration that you could supply upon creation of the producer, but these are the most common ones. You can find a detailed list of all producer configuration <a href="https://kafka.apache.org/documentation/#producerconfigs">here</a>.</p><p>Internally, a Kafka producer looks something like the following:</p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/producer_internals.png?w=1024" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>The application (i.e. application thread) creates a <strong>ProducerRecord</strong> which sends to the producer by calling <code>producer.send(record)</code>. The producer record contains information like the following:</p><ul><li><em>topic</em>: The Kafka topic that the message will be sent to</li><li><em>key</em>: The key is used to determine to which partition of the topic the message will be sent to</li><li><em>value</em>: The actual message we want to send to Kafka</li><li><em>partition</em>: Optional property in case we want to explicitly specify the partition</li><li><em>timestamp</em>: The timestamp of the record as milliseconds since epoch</li><li><em>headers</em>: Any headers to be included along with the record</li></ul><p>There might be more or less properties, depending on the Kafka client's implementation. The only mandatory properties though are the <em><strong>topic</strong></em> and the <em><strong>value</strong></em>. But it's good practice to always use a <em><strong>key</strong></em> as well, to control the partitions that each record will end up.</p><!--kg-card-begin: markdown--><h3 id="serializer">Serializer</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/producer_internals_serializer-1.png?w=1024" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>Once the producer receives a record to be published, it uses the <strong>Serializer</strong> to convert it to Kafka's message format. The message format that Kafka is using is configurable upon creation with a variety of options to choose from. Some of the most popular options are:</p><ul><li><a href="https://www.json.org/">JSON</a>: Textual format, common format used by browsers/API endpoints</li><li><a href="https://avro.apache.org/">Avro</a>: Binary format, thus non-human readable but also more space-efficient. Allows easier schema evolution via <a href="https://docs.confluent.io/current/schema-registry/index.html">Schema Registry</a></li></ul><p>The type of serialiser that the producer is using is defined upon the creation of the producer using the following configurations: <code>key.serializer</code> and <code>value.serializer</code>.</p><!--kg-card-begin: markdown--><h3 id="partitioner">Partitioner</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/producer_internals_partitioner.png?w=1024" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>The next step after serialisation is the <strong>Partitioner</strong>. At this stage, the producer determines to which partition of the topic, the record should be sent to. By default, the <strong>DefaultPartitioner</strong> will be used, which follows these rules:</p><ul><li>If there is a partition number defined in the incoming record, then that partition is used.</li><li>If there is a key in the incoming record, then use <code>hash(record.key) % num_partitions</code>.</li><li>If there is no partition number and no key, choose a partition in a round-robin fashion.</li></ul><p>In most cases, the default partitioner works just fine. But there might be cases where you want to control the partitioning logic, for example we might want to split certain keys to several partitions just to avoid hotspots. The producer's configuration value that allows us to override the partitioner is <code>partitioner.class</code>.</p><!--kg-card-begin: markdown--><h3 id="buffersandbatches">Buffers and Batches</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/producer_internals_batches-1.png?w=1024" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>Once the partition of the record is determined, the record is placed into the partition's buffer. Basically, the producer will try to batch records for efficiency, both IO and compression. These partition buffers also help with back-pressure, since the records will be sent as fast as the broker can keep up, which is configured by <code>max.in.flight.requests.per.connection</code>.</p><p>To increase the producer's throughput, it's a good practice to set <code>linger.ms</code> to a value greater than zero. This basically instructs the producer to wait up to this number of milliseconds before sending the contents of the partition buffer, or until the batch fills up (whichever of the two comes first). The batch size is configured by <code>batch.size</code>.</p><p>So under heavy load, the batches fill up quickly so the linger time is not met...under lighter load, the producer uses this lingering time to increase IO throughput.</p><!--kg-card-begin: markdown--><h3 id="reliabilityandretries">Reliability and Retries</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/producer_internals_acks-2.png?w=1024" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>One important aspect that we need to consider when configuring a Kafka producer is its <strong>reliability</strong>. In other words, what degree of confidence we want to have so that when we publish a message to the cluster, the message is not lost.</p><p>The reliability of a producer is achieved via the acknowledgement (<strong>ACK</strong>) configuration <code>request.required.acks</code>.</p><p>When we set this value to <code>0</code> , i.e. <strong>ACK-0</strong>, is equivalent to fire and forget. Basically the producer will place the record in the partition buffer as we saw above and it will never check whether it was successfully persisted in the Kafka cluster.</p><p>You might ask, why would anyone choose this option?</p><p>And the answer is <strong>low-latency</strong>.<br>Since the producer doesn't wait for Kafka's acknowledgement, it's very fast.<br>This is particularly useful for applications where we have high-volume and we don't really care whether some of the data is lost. Such examples are IoT applications where we don't really care if we lose readings from some of the sensors.</p><p>Another option is to set the required acknowledgements to <code>1</code> (<strong>ACK-1</strong>). This is also called  <em>leader acknowledgement</em> and it means that the Kafka broker will acknowledge as soon as the partition leader writes the record in its local log but without confirming that the partition followers got the record. As you can imagine, if the partition leader fails immediately after sending back the ack but before replicating the record to its followers, we might have data loss. That's why we usually use this option if a rarely missed record is not very significant, for example log aggregation, data for machine learning or dashboards, etc.</p><p>The final option is <code>all</code> (<strong>ACK-ALL</strong>). In this case, the leader gets write confirmations from all in-sync replicas (ISRs) before sending an acknowledgement back to the producer. This ensures that as long as there is one ISR alive, the data is not lost.</p><p>Finally, producers can be configured to <strong>retry</strong> sending failed messages by setting the configuration value of <code>retries</code> to the maximum number of retries for each failed message. By default the retries is set to <code>0</code>. One thing you should note though is that if you set the retries to a value greater than zero, then you should also set the <code>max.in.flight.requests.per.connection</code> to <code>1</code>, otherwise there is a chance that a retried message could be delivered out of order.</p><!--kg-card-begin: markdown--><h2 id="consumers">Consumers</h2>
<!--kg-card-end: markdown--><p>The last piece of the puzzle to complete our end-to-end flow is the consumers.</p><p>As someone would expect, consumers read data from a Kafka topic. In order for a consumer to start reading from a topic, it needs the name of the topic and at least one of the cluster's brokers to connect to. As with producers, once it connects to one broker, it is connected to the whole cluster. Also, as with producers, it doesn't need to connect to the broker that holds the data, Kafka will take care of that.</p><p>As we mentioned many times already, a topic is just a logical grouping of partitions. So, consumers actually read data from partitions. But again, Kafka will take care of that, the developer doesn't need to worry about it.<br><br></p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/consumers_read_order.png?w=678" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>One thing to note though, is that the consumer will read <strong>data from the same partition in the order they appear</strong> on the partition, but different partitions will be read in parallel. For example, in the picture above, consumers will <em>always</em> read <code>0A</code> before <code>0B</code> and the same for <code>1A</code> and <code>1B</code>. But the order in which it reads <code>0A</code> and <code>1A</code> might differ each time.</p><!--kg-card-begin: markdown--><h3 id="consumergroups">Consumer Groups</h3>
<!--kg-card-end: markdown--><p>Consumers are organised into consumer groups. This way we can parallelise the data consumption.</p><p>A very important thing to understand is that each partition of a topic is read by <strong>one and only one consumer in a consumer group</strong>! Let me repeat that...in a consumer group, a partition is read by a single consumer.<br></p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/consumers_groups_all.png?w=631" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>As we can see in the above example, each partition is read by one consumer in each partition group, but each consumer can read multiple partitions.</p><p>The reason of having a single consumer responsible for a partition in each consumer group is that messages in a partition are read in order.</p><p>So what happens if we have more consumers than partitions in a consumer group?</p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/consumers_extra.png?w=471" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>The answer is that the extra consumer is not being used and is on standby mode, ready to be utilised if one of the other consumers fail.</p><p>This basically means that the <strong>level of parallelism</strong> and the scaling of Kafka is defined by the number of partitions we have. The more partitions we have in a topic, the more consumers we can use for that topic.</p><!--kg-card-begin: markdown--><h3 id="rebalancing">Rebalancing</h3>
<!--kg-card-end: markdown--><p>But how do consumers enter and exit a consumer group? How do they know which partition to read when they join or how are partitions assigned to other consumers if a consumer exits the group?</p><p>When a consumer group is created, one of the brokers in the Kafka cluster is elected to be the <strong>group coordinator</strong>. In addition to that, the first consumer that joins the consumer group, becomes the <strong>group leader</strong>. Through the coordination of these two, it's possible to detect changes in the group and re-assign partitions to consumers.</p><p>This action of reassigning partitions to the consumers in a consumer group is called <strong>rebalancing</strong>. The rebalancing process can be described as follows:</p><ol><li>A new consumer joins the group and wants to read from the topic.</li><li>The group coordinator (elected Kafka broker) detects the new consumer that tries to read and sends a list of all the consumers trying to read, to the group leader (first consumer that joined the consumer group).</li><li>The group leader decides the partition assignments and sends them back to the group coordinator.</li><li>The group coordinator sends this information to all the consumers.</li></ol><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/consumers_rebalancing_1-3.png?w=462" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p><br></p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/consumers_rebalancing_2-4.png?w=462" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p><br></p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/consumers_rebalancing_3-2.png?w=462" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p><br></p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/consumers_rebalancing_4-2.png?w=462" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>One thing to keep in mind is that while rebalancing is happening, all the consumers stop reading from their partitions. Frequent rebalancing is a common cause for performance degradation in Kafka consumer applications and it should be monitored and avoided if possible.</p><!--kg-card-begin: markdown--><h3 id="consumeroffsets">Consumer Offsets</h3>
<!--kg-card-end: markdown--><p>Now we know what happens when a new consumer joins a consumer group or when an existing one leaves. But wait, does this mean that we'll have a bunch of duplicate events because the new consumer will re-read the whole partition?</p><p>The answer of course is no. The consumer will start reading from the point of the partition that the previous consumer stopped. And this is achieved with <strong>consumer offsets</strong>.</p><p>Basically, consumers will commit the offsets they have read back to Kafka. Kafka keeps track which offsets of a partition have been read by a particular consumer group. This information is kept in a separate topic called <code>__consumer_offsets</code>. So, once a consumer of a particular consumer group  is assigned a partition, Kafka will check which was the latest committed offset of that partition for this consumer group, and will send the next offset.</p><p>The main question is, when should a consumer commit an offset?</p><p>It could commit it once it receives the message from Kafka and before does the actual processing of the message. The problem is that in this case, if for some reason the consumer dies after it commits the offset and before the processing, then we lose the data. The next consumer will not see this message again.</p><p>Alright, then it could commit it after it processes the message. But what happens if the processing is complete (for example, the message is transformed and some local database is updated) but the consumer fails right before it commits the offset? In this case, the new consumer that will take over will re-receive the message and it will process it again. So, we're getting duplicate messages.</p><p>Which way to go depends on your application use-case, but in most cases the correct thing to do is the latter. You just need to make sure in your application logic that your message processing is idempotent.</p><!--kg-card-begin: markdown--><h2 id="deliveryguarantees">Delivery Guarantees</h2>
<!--kg-card-end: markdown--><p>Now that we know how messages are published and consumed from Kafka as well as how they are stored internally, it's time to talk about the delivery guarantees that Kafka provides. As we have seen already, Kafka is a highly configurable system. As such, you can configure it in a different way to achieve different delivery semantics depending on your use case.</p><!--kg-card-begin: markdown--><h3 id="atmostonce">At most once</h3>
<!--kg-card-end: markdown--><p>This setup basically ensures that a message published by a producer into the Kafka cluster will arrive zero or one times into the consumer, but not more than that! This guarantees that there will not be any duplicate messages, but there might be some data loss.</p><p>It's quite useful for applications where we have high volume of data and some data loss is acceptable. An example application space is IoT applications.</p><p>To achieve at-most-once deliveries, first we need to configure the producers so that they don't retry sending messages to the cluster. The best way achieve at-most-once semantics in a producer is to set <code>request.required.acks</code> to <code>0</code>.</p><p>In the consumer side, we're in luck as at-most-once delivery is the default behaviour. In general, to achieve this, we just need to set the <code>enable.auto.commit</code> to <code>true</code>. This basically informs the cluster that a message has been read (i.e. commits the offset) as soon as the consumer receives the message and before processing it.</p><p>By default, to avoid performance impact, consumers will try to batch as many offset commits as they can and commit them all at once. The amount of time the consumer will wait before committing the offset is configurable by <code>auto.commit.interval.ms</code>. So, in the case of at-most-once, we need to set this to the lowest timeframe possible, to make sure that the offset is committed before the processing of the message.</p><!--kg-card-begin: markdown--><h3 id="atleastonce">At least once</h3>
<!--kg-card-end: markdown--><p>This ensures that a message published by a producer will definitely arrive to the consumer, but it might arrive multiple times. It is probably the most common setup, as it guarantees no data loss and it's not as hard to achieve as the exactly-once case that we'll see next.</p><p>First, the producer needs to make sure that a message is safely persisted in the cluster. This is achieved by setting the <code>request.required.acks</code> to <code>all</code>, telling the leader partition to only notify us for success if the message is stored in the leader's and the followers' logs.</p><p>In addition, the producer needs to retry sending a message if it doesn't hear back from the Kafka cluster, or if the cluster replies with an error. So, we need to set the <code>retries</code> to a number greater than zero.</p><p>Finally, in the consumer side, we need to disable the auto-commit by setting the <code>enable.auto.commit</code> to <code>false</code>, as this could lead to data loss in the case where a consumer commits its offset and then fails before processing the message. Since auto-commit is disabled, the consumer needs to do the offset management by explicitly calling the <code>consumer.commitSync()</code> after the processing of the message is complete.</p><p>Consumers with at-least-once semantics, need to handle duplicate messages that may arrive. This can be done by implementing the consumer to have idempotent behaviour.</p><!--kg-card-begin: markdown--><h3 id="exactlyonce">Exactly once</h3>
<!--kg-card-end: markdown--><p>At this point, those of you who have worked with Kafka or have experience with distributed systems might be thinking that exactly-once delivery in such an environment is impossible or it comes with such a high price that is not practical.</p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2020/03/exactly_once.png?w=800" class="kg-image" alt="Deep dive into Kafka (pt 2)"></figure><p>In all the years I've been working with Kafka, I've never seen any real-life producer-consumer systems that uses exactly-once delivery. Most projects tend to stay with at-least-once semantics and implement an idempotent behaviour in the application.</p><p>Having all that in mind, let's see how we could achieve exactly-once delivery.</p><p>On the producer side, we need to ensure that the messages we send to the cluster are persisted. As we saw on the at-least-once case, this can be achieved by setting the <code>request.required.acks</code> to <code>all</code> and the <code>retries</code> to a value greater than <code>0</code>.</p><p>Of course, this might lead to duplicate messages when a message is successfully persisted in the cluster but the broker dies right before it sends back the acknowledgement, or there is some network failure and the acknowledgement is lost. In that case, the producer won't know that the message was persisted successfully, so it will re-send it, creating this way duplicate message. To avoid that, we need to set <code>enable.idempotence</code> to <code>true</code>.</p><p>On the consumer side, things are a bit different depending on whether you're using the consumer API that we saw in this post, or you're using Kafka stream processors (which we'll look in a later post).</p><p>Good news first....<strong>if you're using stream processors</strong> to consume and process your messages, then exactly-once is a simple configuration change. More specifically, we just need to set <code>processing.guarantee</code> to <code>exactly_once</code> in our stream configuration.</p><p>If, on the other hand, you're using the <strong>Consumer API</strong>, things are a bit trickier. You will need to use the transactions API. First, on the producer side, you need to set a unique <code>transactional.id</code> and publish messages as follows:</p><p>[code language="java"] producer.initTransactions(); try { producer.send(record); producer.commitTransaction(); } catch (KafkaException e) { producer.abortTransaction(); } [/code]</p><p>On the consumer side, you will need to set the <code>isolation.level</code> to <code>read_committed</code>.</p><p>In general, Kafka allows exactly-once delivery but it comes with cost in throughput, latency and also complexity. So my personal advice would be, go with at-least-once semantics and handle duplicate messages on the application level.</p><!--kg-card-begin: markdown--><h2 id="conclusion">Conclusion</h2>
<!--kg-card-end: markdown--><p>Phew, that was long! Kudos to all of you who stayed until the end.</p><p>This concludes our deep dive to Kafka. I hope you learned something from all of this.</p><p>I hope your takeaway is that although Kafka is a very complicated system and there are a lot of corner cases you need to think about, its abstractions are clear and quite easy to use.</p>]]></content:encoded></item><item><title><![CDATA[Deep dive into Kafka (pt 1)]]></title><description><![CDATA[<p>The last couple of years there is a shift towards event-driven microservice architectures. More and more companies have already switched or are in the process of switching to such an architecture with Kafka being the central piece that enables communication between all the different services.</p><p>So, apparently Kafka is a</p>]]></description><link>https://antousias.com/deep-dive-into-kafka-part-1/</link><guid isPermaLink="false">5e8c6292f8ca150df0f97001</guid><category><![CDATA[Software Architecture]]></category><category><![CDATA[Distributed Systems]]></category><category><![CDATA[Deep Dive]]></category><dc:creator><![CDATA[Alexandros Ntousias]]></dc:creator><pubDate>Sun, 09 Feb 2020 19:25:00 GMT</pubDate><media:content url="https://antousias.com/content/images/2020/04/kafka_bg_3-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://antousias.com/content/images/2020/04/kafka_bg_3-2.png" alt="Deep dive into Kafka (pt 1)"><p>The last couple of years there is a shift towards event-driven microservice architectures. More and more companies have already switched or are in the process of switching to such an architecture with Kafka being the central piece that enables communication between all the different services.</p><p>So, apparently Kafka is a thing now!</p><figure class="kg-card kg-image-card"><img src="http://memes.ucoz.com/_nw/38/98024176.png" class="kg-image" alt="Deep dive into Kafka (pt 1)"></figure><p>Given that I have been using Kafka extensively for the past few years, I thought it would be a good idea to provide a deeper look on what Kafka is and how it works internally.</p><!--kg-card-begin: markdown--><h2 id="sowhatiskafka">So...what is Kafka?</h2>
<!--kg-card-end: markdown--><p>Oh, if I had a dime for every different definition I've heard over the years...! <em>Messaging system</em>, <em>Distributed replicated log</em>, <em>Streaming platform</em> and many many more.</p><p>Well, all of them are correct, but personally I don't think they give an exact definition of what Kafka actually provides. When I'm asked this question, I find it easier to answer as:</p><blockquote>Kafka is a distributed, persistent, highly-available event streaming platform with publish-subscribe messaging semantics.</blockquote><p>Long one, isn't it? Well, Kafka is complicated and I feel this definition gives it justice. Let's try to break it apart, shall we?</p><!--kg-card-begin: markdown--><h3 id="eventstreamingplatform">Event Streaming Platform</h3>
<!--kg-card-end: markdown--><p>Kafka is a platform that provides a never-ending series of events. Usually you will see it in a system architecture as the central piece where all the different events that occur in the system are published. These events could really be anything, from user clicks, sensor readings, payment reports, you name it.</p><!--kg-card-begin: markdown--><h3 id="distributed">Distributed</h3>
<!--kg-card-end: markdown--><p>Kafka is a distributed system, it doesn't run on one machine (although it can, but why would you do that to yourself?). In fact, Kafka is a cluster of machines. Each machine in a Kafka cluster is called a <strong>broker</strong>.</p><figure class="kg-card kg-image-card"><img src="https://i.kym-cdn.com/photos/images/original/000/604/701/e74.png" class="kg-image" alt="Deep dive into Kafka (pt 1)"></figure><p>Brokers are basically responsible for handling requests of new events, storing events and serving them to services that request them. They also have additional functionalities, e.g. leader partition election, but more on this later.</p><p>So, if Kafka is a cluster of machines, how does each broker know which other brokers are in the cluster? The answer is <strong><a href="https://zookeeper.apache.org/">Zookeeper</a></strong>. Zookeeper acts as a centralised service that is used to maintain naming and configuration data. So, at this point, Kafka clusters rely on Zookeeper for member discovery.</p><!--kg-card-begin: markdown--><h3 id="persistent">Persistent</h3>
<!--kg-card-end: markdown--><p>Events that are published to Kafka are persisted in disk. This means, that even if a broker goes down for any reason, when it starts up again, it will still have the events that received before shutting down. More details on how brokers store events on disk will follow on a subsequent post.</p><!--kg-card-begin: markdown--><h3 id="highlyavailable">Highly Available</h3>
<!--kg-card-end: markdown--><p>Data in Kafka are replicated across multiple brokers. This basically means that even if a broker that holds some data goes down, the data is still available to be consumed by its replicas οn the other brokers.</p><!--kg-card-begin: markdown--><h3 id="publishsubscribemessagingsemantics">Publish-Subscribe Messaging Semantics</h3>
<!--kg-card-end: markdown--><p>One very smart thing that the architects of Kafka did, was to design it with publish-subscribe messaging semantics. On one side we have producers publishing messages to Kafka and on the other side consumers consuming these messages.</p><p>This was one of the reasons that really helped the adoption of Kafka, because despite its internal complexity, it provides a well-understood and widely-adopted pattern that almost all developers are familiar with.</p><!--kg-card-begin: markdown--><h2 id="topicsandpartitions">Topics and partitions</h2>
<!--kg-card-end: markdown--><p><em>Disclaimer: From now on, I will be using the terms messages and events interchangeably to describe data that are flowing through Kafka.</em></p><p>So, how are events stored in Kafka?</p><p>Events are stored in what we call <strong>partitions</strong>. You could think of partitions like log files (actually that's exactly how the data is stored as we will see later). Each partition is an ordered, immutable sequence of messages that is continually appended to. The messages on each partition are assigned an <strong>offset</strong>, a sequential id that uniquely identifies the message within a partition. This offset is used by the consumers, but more on that in a later post when we look at producers and consumers in mode detail.</p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/screenshot-2019-12-05-at-10.04.30.png?w=884" class="kg-image" alt="Deep dive into Kafka (pt 1)"></figure><p>The partitions are further organised into logical groups that are called <strong>topics</strong>.</p><p>Basically, when a producer publishes a message to Kafka, he publishes it to a certain topic. Kafka will decide on which partition of that topic this message will be stored.</p><p>But wait, how does it decide which partition to be used?</p><p>This is the role of the <strong>partitioner</strong>. The partitioner will receive a message and it will decide on which partition of a topic it should be sent to. The published message is actually a key-value pair, where the value is the actual data of the message and the key can be anything that makes sense for this event. So, the partitioner will use the key part of the message to make the partition decision. Kafka producers provide default partitioners, but as with everything in Kafka, this is configurable.</p><p>The default partitioner guarantees that all messages with the same key published to the same topic, will end up in the same partition. There is however an exception to this rule and this has to do with messages with <em>null</em> key. If you don't supply a key, then the default partitioner will use a round-robin algorithm and will send each message to a different partition.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/deep-dive-in-kafka-11.png?w=598" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>Partitioner decides on which partition a message will be sent to</figcaption></figure><p>As we mentioned above, messages in Kafka are replicated to provide high-availability. In reality, the partitions are the ones that are replicated across different brokers. So, if for example you have 1 topic with 3 partitions and a replication factor of 3 (i.e. 3 replicas for each partition), you end up with 9 partitions scattered around the different brokers in a Kafka cluster. As you would expect, replicas of the same partition cannot be stored in the same broker, in other words the replication factor can only be as high as the number of brokers you have in the cluster.</p><p>Let's take an example. Let's suppose we have the following setup:</p><ul><li>4 brokers</li><li>two topics, A and B</li><li>topic A has 2 partitions (partition 0 and 1) with replication factor 3</li><li>topic B has 1 partition (partition 0) with replication factor 4</li></ul><p>This could look like that:</p><figure class="kg-card kg-image-card"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/untitled-diagram.png?w=672" class="kg-image" alt="Deep dive into Kafka (pt 1)"></figure><p>At this point you might be wondering, what happens if we have two producers that are trying to publish to the same partition but in different brokers? In other words, what happens if we have the following scenario, how would Kafka resolve such a conflict and keep the replicas in-sync?</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/untitled-diagram-2.png?w=502" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>How would Kafka reconcile the data in the replicas of Topic A-Partition 0?</figcaption></figure><p>And the answer to this is: <strong>leader partition</strong>. You see, for every partition there is one replica that acts as the leader and the rest of the replicas of the same partition are the followers. When a producer wants to publish a message to a partition, it will need to send it to the leader replica for that partition. If it sends it to a follower, then it will get an error back. Once a leader replica receives a new message, it will forward it to the followers.</p><p>So, the above scenario is not valid. What happens in reality is something like the following.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/untitled-diagram-3.png?w=502" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>Propagate messages from leader to followers</figcaption></figure><p>Kafka's scalability comes from the fact that it tries to assign leaders for the different partitions as evenly as possible in the available brokers. This way, producers that are trying to write to different partitions will send their requests to different brokers.</p><p>But what happens if the broker that has a leader partition dies? In that case, there will be a new leader elected from the available replicas. One of the brokers in the cluster has the additional role of <strong>Controller</strong>. This broker-controller is constantly monitoring the rest of the brokers and if it spots a dead one, then it will re-assign any available follower replica as leader for every leader replica that was on the dead broker.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/deep-dive-in-kafka-6.png?w=502" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>Initial setup</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/deep-dive-in-kafka-8.png?w=502" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>Broker 2 dies</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/deep-dive-in-kafka-9.png?w=332" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>Controller re-assigns leaders</figcaption></figure><p>Then, the next obvious question is what happens if the broker that died is the controller? Then there is an election on the remaining brokers to decide which one will be the next controller. This election is being done via Zookeeper. More specifically, the first of the available brokers that will call the <code>/controller</code> path in Zookeeper will be the next controller. This approach provides some minimal guarantees that the next controller will be the broker with the least load, since it was the one that managed to contact Zookeeper first, while the others might have been serving requests.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/deep-dive-in-kafka-2.png?w=509" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>1. Broker 1 dies</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/deep-dive-in-kafka-3.png?w=323" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>2. Brokers 2 and 3 try to become controllers</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/deep-dive-in-kafka-4.png?w=323" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>3. Broker 2 is faster and becomes the new controller</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://allaboutcodinghome.files.wordpress.com/2019/12/deep-dive-in-kafka-5.png?w=323" class="kg-image" alt="Deep dive into Kafka (pt 1)"><figcaption>4. New controller re-assigns missing leaders</figcaption></figure><p>With Kafka being a distributed platform, you can expect lots of corner cases. One corner case worth mentioning here is what happens if followers are not in-sync with the leader when the leader dies. There are cases where a broker might be too busy or too slow and thus it cannot keep up with the rate that the leader is accepting messages. In that case we say that the replica is out-of-sync.</p><p>In fact, Kafka maintains an <strong>in-sync replica set</strong> (ISR) for every partition. If a replica gets out-of-sync with the leader of the partition, then it is removed from the ISR of that partition.</p><p>When a leader dies, the controller will try to choose one of the replicas in the ISR of that partition to be the new leader. If the only replicas that are available are not part of the partition's ISR, then we call it an <strong>unclean leader election</strong>. In that case, there is a chance that there might be some data loss. Of course, Kafka will try its best to reconcile the data once the broker with the additional messages comes up online, but this doesn't always work. As we will see when we talk about configuration, you can decide to disable unclean leader election and in that case, if the leader dies and the only available replicas are not in the partition's ISR, Kafka will reject any new messages going to that partition.</p><!--kg-card-begin: markdown--><h2 id="tryitout">Try it out</h2>
<!--kg-card-end: markdown--><p>Now that you have a better understanding on what Kafka is and how messages are stored, it would be a good idea to try it out.</p><p>First, you will need to <a href="https://kafka.apache.org/downloads">download the latest version of Kafka</a>. At the time of this post, the latest version of Kafka is 2.3.1.</p><p>Next, unzip the tar file and move into the newly created directory.</p><pre><code>&gt; tar -xzf kafka_2.12-2.3.1.tgz &gt; cd kafka_2.12-2.3.1</code></pre><p>As we saw earlier, Kafka is using Zookeeper for cluster membership. So the next step is to start Zookeeper. For now, we'll use the default properties that come along with the Kafka distribution you downloaded.</p><pre><code>&gt; bin/zookeeper-server-start.sh config/zookeeper.properties</code></pre><p>Finally, we can start our Kafka broker. As with Zookeeper, we will be using the default properties for the broker. You could start multiple of these if you want to try out a cluster with multiple brokers, just make sure to give to each one a different broker id, a different port and a different path for its data.</p><pre><code>&gt; bin/kafka-server-start.sh config/server.properties --override broker.id=1 --override listeners=PLAINTEXT://:9092 --override log.dirs=/tmp/kafka-logs-1</code></pre><p>Now that we have our Kafka cluster up and running, we can create a topic with name <em>all_about_coding</em> that has 2 partition with replication factor 1. Remember, the replication factor can only be as high as the number of brokers you have in the cluster.</p><pre><code>&gt; bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 2 --topic all_about_coding &gt; bin/kafka-topics.sh --list --bootstrap-server localhost:9092 &gt;&gt; all_about_coding</code></pre><p>The Kafka distribution comes with a sample producer and consumer to play around with. The producer is a command line client that receives input from standard input and send it to Kafka. So let's try startup the producer and send two messages.</p><pre><code>&gt; bin/kafka-console-producer.sh --broker-list localhost:9092 --topic all_about_coding &gt;&gt; Deep dive into Kafka (part 1) &gt;&gt; Deep dive into Kafka (part 2)</code></pre><p>Finally, we can start the consumer and consume the messages that already exist in the <em>all_about_coding</em> topic. The consumer that comes with the Kafka distribution just reads all messages from a given topic and dumps them on the console.</p><pre><code>&gt; bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic all_about_coding --from-beginning &gt;&gt; Deep dive into Kafka (part 1) &gt;&gt; Deep dive into Kafka (part 2)</code></pre><!--kg-card-begin: markdown--><h2 id="conclusion">Conclusion</h2>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>I think this is a good point to stop as the post grew quite longer than I anticipated.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>I hope you now have a clearer picture of what Kafka provides and how topics and partitions work internally.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>In the next post we'll take a closer look at the producers and consumers, as well as the deliver guarantees that Kafka provides.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Monad - Breaking down this dreadful word...]]></title><description><![CDATA[<p>If you were ever exposed to functional programming or went through a programming language course, you surely must have heard this dreadful word: <strong>Monad</strong>. It's usually followed by other words like complicated, confusing, difficult to understand, lots of theory, etc.</p><p>I was more than once in that position, trying to</p>]]></description><link>https://antousias.com/monad-breaking-down-this-dreadful-word/</link><guid isPermaLink="false">5e8c5d61f8ca150df0f96f81</guid><category><![CDATA[Functional Programming]]></category><category><![CDATA[Scala]]></category><dc:creator><![CDATA[Alexandros Ntousias]]></dc:creator><pubDate>Sat, 07 Dec 2019 19:00:00 GMT</pubDate><media:content url="https://antousias.com/content/images/2020/04/monad.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://antousias.com/content/images/2020/04/monad.jpg" alt="Monad - Breaking down this dreadful word..."><p>If you were ever exposed to functional programming or went through a programming language course, you surely must have heard this dreadful word: <strong>Monad</strong>. It's usually followed by other words like complicated, confusing, difficult to understand, lots of theory, etc.</p><p>I was more than once in that position, trying to understand what a monad is by reading lengthy articles only to find myself more confused than before.</p><p>But in all fairness, <em>monad</em> is a pretty easy concept to grasp, at least if you are looking for a practical explanation and don't care about the theory behind it so much.</p><p>After all...</p><figure class="kg-card kg-image-card"><img src="https://antousias.com/content/images/2020/11/44b0bd758f8ee5c81362923f0d5c8e017c9ddf623925e60c29a4c015b89fbb45.jpg" class="kg-image" alt="Monad - Breaking down this dreadful word..."></figure><!--kg-card-begin: markdown--><h2 id="sowhatisamonad">So, what is a Monad?</h2>
<!--kg-card-end: markdown--><p>Well, a monad is a concept that comes from <em>category theory</em>. It does have a lot of theory behind it, which I have to admit, it can be quite overwhelming and complicated. If you are interested in that, there are multiple resources online to learn more about it (a good one is <a href="https://www.youtube.com/watch?v=I8LbkfSSR58&amp;list=PLbgaMIhjbmEnaH_LTkxLI7FMa2HsnawM_" rel="noopener noreferrer">this series of lectures on category theory</a>).</p><p>But in practice, a <em>monad</em> is basically a <strong>type wrapper</strong> which represents <strong>a specific form of computation</strong>.<br> In other words, you use it to <em>wrap other types</em> in order to give them some <em>additional context</em>.</p><p>Let's write some code and try to expand on it as we go along. <br>Let's try and create a new <em>monad</em> called <code>Container</code> which can represent the result of an operation that can either return a value (in this case it will be <code>NonEmpty</code>) or return nothing (<code>Empty</code>).</p><pre><code class="language-scala">sealed trait Container[+A] {
  def isEmpty: Boolean
}

case class NonEmpty[+A](value: A) extends Container[A] {
  override val isEmpty: Boolean = false
}

case object Empty extends Container[Nothing] {
  override val isEmpty: Boolean = true
}</code></pre><p>Is that all? Wow, that was simple.</p><p>Err, not exactly…in order for a type wrapper to be considered a <em>monad</em>, it needs to provide two operations.</p><!--kg-card-begin: markdown--><h3 id="pure">Pure</h3>
<!--kg-card-end: markdown--><p><em>Monads</em> need to provide a way to wrap a pure value of a type into the monad itself (in other words, to yield a <strong>monadic value</strong>).</p><p>This function can be found with different names, depending on the language/library you might be using, but the most common ones are: <strong>identity</strong>, <strong>pure</strong> or <strong>unit</strong> (in Scala), <strong>return</strong> (in Haskel).</p><p>So, we can extend our example above to include this method.</p><pre><code class="language-scala">trait Container[+A] {
  def isEmpty: Boolean
}

object Container {
  def pure[A](value: A): Container[A] =
    if (value == null) Empty
    else NonEmpty(value)
}

case class NonEmpty[+A](value: A) extends Container[A] {
  override val isEmpty: Boolean = false
}</code></pre><p>It is sort of a <em>monad constructor</em> if you will. This is the reason why in the above example I defined it in the companion object instead of the trait itself.</p><!--kg-card-begin: markdown--><h3 id="flatmap">FlatMap</h3>
<!--kg-card-end: markdown--><p>The second thing that a <em>monad</em> needs to provide is a way to compose functions that output monadic values (called <strong>monadic functions</strong>).</p><figure class="kg-card kg-image-card"><img src="https://i.imgflip.com/13iqrw.jpg" class="kg-image" alt="Monad - Breaking down this dreadful word..."></figure><p>Well, this is what I'm talking about:</p><pre><code class="language-scala">def flatMap[B](f: A =&gt; M[B]): M[B]</code></pre><p>Basically, it needs to provide a function that receives another function <code>f</code> as a parameter which is applied in the wrapped type <code>A</code> and returns a monad of another type <code>B</code>.</p><p>This function is commonly named <strong>flatMap</strong>, but again, you will find it with different names, depending on the language and/or library you're using, e.g. <strong>bind</strong>, <strong>&gt;&gt;=</strong> (in Haskel).</p><pre><code class="language-scala">trait Container[+A] {
  def isEmpty: Boolean
  def flatMap[B](f: A =&gt; Container[B]): Container[B] = this match {
    case NonEmpty(value) =&gt; f(value)
    case Empty           =&gt; Empty
  }
}

object Container {
  def pure[A](value: A): Container[A] =
    if (value == null) Empty
    else NonEmpty(value)
}</code></pre><p>Hmmm, so as long as I have a type wrapper that provides two functions with the above signatures, I have a <em>monad</em>?</p><p>Not exactly. We talked about naming and signatures…but we never talked about the laws these functions should obey.</p><!--kg-card-begin: markdown--><h2 id="monadlaws">Monad Laws</h2>
<!--kg-card-end: markdown--><p>In order for a type wrapper to be considered a proper <em>monad</em>, in addition to providing the <strong>pure</strong> and <strong>flatMap</strong> functions, it needs to obey certain laws as well, called <strong>monad laws</strong>.</p><p>In particular, there are 3 laws:</p><ul><li><strong>Left Identity</strong>: If we create a <em>monad</em> out of a value and then <em>flatMap</em> the monad using a function <code>f</code>, it should give us the same result as applying the function <code>f</code> in the initial value.</li></ul><pre><code class="language-scala">def onlyPositives(value: Int): Container[Int] = 
  if (value &gt;= 0) NonEmpty(value) else Empty

val value: Int = 1337

Container.pure(value).flatMap(onlyPositives) == onlyPositives(value)</code></pre><ul><li><strong>Right Identity</strong>: If we have a m<em>onadic value</em> and we <em>flatMap</em> using the <em>pure</em> operation, we should get back the initial <em>monadic value</em>.</li></ul><pre><code class="language-scala">val monadicValue: Container[Int] = Container.pure(42)

monadicValue.flatMap(Container.pure) == monadicValue</code></pre><ul><li><strong>Associativity</strong>: When we have a chain of <strong>monadic function</strong> applications, it shouldn't matter how they are nested.</li></ul><pre><code class="language-scala">val monadicValue: Container[String] = NonEmpty("monads rule")
def size(value: String): Container[Int] = Container.pure(value.length)
def isEven(value: Int): Container[Boolean] = NonEmpty(value % 2 == 0)

monadicValue.flatMap(size).flatMap(isEven) == monadicValue.flatMap(str =&gt; size(str).flatMap(isEven))</code></pre><!--kg-card-begin: markdown--><h2 id="conclusion">Conclusion</h2>
<!--kg-card-end: markdown--><p>All the above could be summarised in the following:</p><ul><li>A <em>monad</em> is a type wrapper that provides some computation context to the wrapped values</li><li>It needs to provide a way of wrapping values of any basic type within the <em>monad</em> (i.e. create <strong>monadic values</strong>)</li><li>It needs to provide a way to compose functions that output monadic values (i.e. <strong>monadic functions</strong>)</li><li>It needs to obey the three monad laws: <strong>left identity</strong>, <strong>right identity</strong> and <strong>associativity</strong></li></ul><p>Of course, in reality, monads will most likely have much more methods than the two I described above. But all these methods, can be composed in one way or another from the two listed here.</p><p>I hope, at this point, monad is a less scaring concept for you.</p><p>But, why do we even care about monads? Why all these laws and rules? And what are these <em>free monads</em>, <em>comonads</em> and <em>additive monads</em> you've been hearing about?</p><p>Well, let's talk about this and also give some concrete examples of monads that you can find in most functional programming languages in another post.</p>]]></content:encoded></item><item><title><![CDATA[A better way to constrain  case class construction in Scala]]></title><description><![CDATA[<p>A lot of times in Scala, we want to constraint the creation of certain case classes. Unfortunately, as I found out recently, most of us (myself included) used to do it in a wrong way.</p><p>One of the most <em>common</em> ways of doing this is by declaring the <code>case</code> class</p>]]></description><link>https://antousias.com/a-better-way-to-constraint-case-class-construction-in-scala/</link><guid isPermaLink="false">5e8c5a99f8ca150df0f96f56</guid><category><![CDATA[Functional Programming]]></category><category><![CDATA[Best Practices]]></category><category><![CDATA[Scala]]></category><dc:creator><![CDATA[Alexandros Ntousias]]></dc:creator><pubDate>Sun, 10 Nov 2019 18:00:00 GMT</pubDate><media:content url="https://antousias.com/content/images/2020/04/scala-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://antousias.com/content/images/2020/04/scala-1.jpg" alt="A better way to constrain  case class construction in Scala"><p>A lot of times in Scala, we want to constraint the creation of certain case classes. Unfortunately, as I found out recently, most of us (myself included) used to do it in a wrong way.</p><p>One of the most <em>common</em> ways of doing this is by declaring the <code>case</code> class as <code>private</code> and provide some factory method in the companion object to instantiate it.</p><pre><code class="language-scala">private case class Positive(value: Int)

object Positive {
  def fromInt(num: Int): Option[Positive] =
    if (num &gt;= 0) Some(Positive(num))
    else None
}</code></pre><p><br>Although you might find this pattern in a lot of codebases, it has two problems: the companion object's <strong>apply</strong> and <strong>copy</strong> methods are still generated. So, the following are still allowed:</p><pre><code class="language-scala">// apply from companion object
val wrong1 = Positive(-3)

// copy from companion object
val wrong2 = Positive.fromInt(2).get.copy(-3)</code></pre><p><br>A trick that I found out recently was to use a <code>sealed abstract case class</code> to <em>properly</em> constraint the construction of my case classes.</p><p>So, the above code would look something like the following:</p><pre><code class="language-scala">sealed abstract case class Positive(value: Int)

object Positive {
  def fromInt(num: Int): Option[Positive] =
    if (num &gt;= 0) Some(new Positive(num) {})
    else None
}</code></pre><p><br>The above syntax gives us the following benefits:</p><ul><li>The default <code>apply</code> and <code>copy</code> methods in the companion object are not generated (because of <strong>abstract</strong>)</li><li>We cannot instantiate our case class, using <code>new</code>, outside the file (because of <strong>sealed</strong>)</li><li>We can still use pattern-matching as normal <code>case</code> classes</li></ul><p>Maybe there are other better or more interesting ways of achieving the same results. If you do know of any, please let me know in the comments below.</p>]]></content:encoded></item></channel></rss>