<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Tugberk Ugurlu @ the Heart of Software</title>
    <link>https://www.tugberkugurlu.com</link>
    <description>Software Engineer and Tech Product enthusiast Tugberk Ugurlu&#39;s home on the interwebs! Here, you can find out about Tugberk&#39;s conference talks, books and blog posts on software development techniques and practices</description>
    <managingEditor> (Tugberk Ugurlu)</managingEditor>
    <item>
      <title>My Mental Model on Choosing a Database for a Particular Problem</title>
      <link>https://www.tugberkugurlu.com/archive/my-mental-model-on-choosing-a-database-for-a-particular-problem</link>
      <description>&lt;nav&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_0&#34;&gt;Dimensions to Think About&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_1&#34;&gt;Access and Write Patterns&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_2&#34;&gt;Consistency&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_3&#34;&gt;Fault Tolerance&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_4&#34;&gt;Scalability&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_5&#34;&gt;Read Scalability&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_6&#34;&gt;Write Scalability&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_7&#34;&gt;Elastic Scalability&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_8&#34;&gt;Maintenance and Observability&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_9&#34;&gt;Self-healing&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_10&#34;&gt;Cost&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_11&#34;&gt;Regulations and Compliance&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_12&#34;&gt;Small Details That Could be Deal Breakers&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_13&#34;&gt;Practicality of This Mental Model&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;/nav&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu-blog.s3.us-east-2.amazonaws.com/post-images/01F2Q319P5PZHDDM99S4F9YZDA-burst-aoN3HWLbhdI-unsplash-40.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&#34;https://unsplash.com/@burst?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&#34;&gt;Burst&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/choice?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Choosing a database technology for the problem at hand is a multifaceted problem. There is usually no single correct choice, but there are surely multiple wrong choices. Depending on the problem you are trying to solve within your unique set of constraints, getting this choice wrong could have significant negative implications on your users and business.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Over the years, I have developed a mental model on how I go about to judge whether a certain database technology is the correct one for the problem at hand. This worked well for me when I am designing systems as well as peer reviewing other technical designs. I wanted to share this here in case it helps others, while also serving my selfish need to document this somewhere.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I have put this together quickly. So, some ideas may not be solidified as effective as they could have been. Please drop a comment if you have any questions, or suggestions.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_0&#34;&gt;Dimensions to Think About&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;There are various dimensions that I consider to be able to make an informed decision about whether a certain database technology fits into the problem which I am trying to solve. These set of dimensions likely to be unique according to the environment and constraints you are working within. However, I have observed some common ones which always came up no matter what type of the environment I was trying to make the choice. I will list them here and explain what I mean by those. However, just keep in mind that this is by no means an exhaustive list. So, if you can think of any others based on your own experience, I would like to hear and learn about them 🙂&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_1&#34;&gt;Access and Write Patterns&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Before you make a choice about a database technology, it&#39;s immensely valuable think about your access and write patterns, how the data is written and how you want to query that data. This is to be able to increase your chances to make a correct choice, and it is likely the biggest deciding factor that will let you narrow down your choices as database technologies often differentiate themselves around access patterns, and how the writes are processed.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Write pattern is usually the easy part since the writes usually come in a defined shape, and unlikely to change drastically throughout the lifetime of the application. Therefore, writes usually dictate in what shape you want to keep the source of truth. I will talk more about the write pattern within the scalability section below, which is where the real challenge with writes come into the picture. However, there are still a few other considerations you need to keep in mind with writes. For example, what happens if you happen to receive the same write concurrently? You need to have a strategy for dealing with this situation through techniques like &lt;a href=&#34;https://en.wikipedia.org/wiki/Optimistic_concurrency_control&#34;&gt;optimistic concurrency control&lt;/a&gt;, and it&#39;s critical to know how your data storage technology can help here. The other important but often overlooked aspect is the idempotent writes. If you want to ensure that the writes are processed exactly once, you might want to employ techniques to mitigate these. For example, &lt;a href=&#34;https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html#API_TransactWriteItems_RequestSyntax&#34;&gt;DynamoDB&#39;s &lt;code&gt;ClientRequestToken&lt;/code&gt; parameter&lt;/a&gt; acts as an idempotency token and a great example for an out of the box support for this type of problems.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As for the access pattern, it is all about how you want to be querying the data. The reason why this matters, and differs from the write pattern is that it&#39;s almost always the case that we need to query the data with a different need in mind compared to how it&#39;s written, and more importantly the need for your read pattern often changes (while usually needing multiple patterns per data source). The fact that data access patterns evolve in a much greater velocity than the writes is a too complicated topic to get into here. I will leave &lt;a href=&#34;https://martinfowler.com/bliki/CQRS.html&#34;&gt;CQRS&lt;/a&gt; here as a topic for your to explore.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Take the example of a coffee shop which receives orders from each customer, and the loyalty program which needs to give customers points based on their orders and value of those orders. Here, writes are simple: an &amp;quot;order entity&amp;quot; is written into a data storage system and grouped within the orders &amp;quot;bucket&amp;quot; per each customer order. However, the read here is more complex. We need to be able to calculate a loyalty point score per each customer based on their previous orders, and we also need a way to perform this efficiently (i.e. we don&#39;t want to scan the entire &amp;quot;bucket&amp;quot; for this).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This was a sort of an easy example, and I am sure you can emphasize here that a lot of data storage systems already have a solution for this problem. So, it may not make much of a difference if your needs are this simple. However, as your access pattern needs get more complex, more differentiation this aspect is going to be putting its weight on your decision making. Take the example of a data access need where you need to find out conflicting events within a given location. The write pattern here is likely to be simple, but the access pattern is a unique one. Not many data storage systems can index for this type of query. So, you need a specific data storage tech to be able to pull this off, or you need to get creative with how you store the data with the potential cost of lower throughput and higher latency on writes.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Another example here is when you need to perform a full-text search over a piece of text across all the rows. Depending on how you want to perform your search (e.g. contains, starts-with, exact word match, etc.), how you want to normalize the search experience (e.g. case-sensitivity, ignoring some stop words such as &amp;quot;and&amp;quot;, &amp;quot;or&amp;quot;, etc.) and which languages you want to support, this can get pretty complex quite easily. Therefore, the need here needs to be understood before you can make a choice about the data storage technology.&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_2&#34;&gt;Consistency&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The consistency of the data is another pretty important aspect to worry about. For the cases where you attempt to write the data into the database and get a successful ACK back, the databases out there have set different expectations when you happen to read the data again immediately. For example, your data might be written and stored in a durable way. However, you may not see the data you have written immediately, but you are likely given a promise to see the data eventually. This characteristics is commonly refereed as &lt;a href=&#34;https://en.wikipedia.org/wiki/Eventual_consistency&#34;&gt;eventual consistency&lt;/a&gt; and can have a big negative impact on the system we are designing unless we understand the implications beforehand. There are several reasons why a data storage technology is providing eventual consistency. However, fundamental trade-off here is that you gain either higher availability and/or throughput at the cost of consistency.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In case where you are working against a single node data storage system, the reason you might be facing eventual consistency could be related to the fact that the indexes might be updated asynchronously, meaning at the time when you received an ACK from the database about your write, the index which your query is hitting may not be updated. This is employed to increase throughput as waiting on an index update will take time, and decrease the number of writes you can handle per second.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, in the case where you are working against a multi-node database structure, where you have replicas, the reason why you might be facing with eventual consistency is likely to be related to the fact that the data hasn&#39;t be replicated to the replica node which is being used for the query. In this case, you are actually gaining three benefits:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;further availability (by having the replica which you can failover to),&lt;/li&gt;&#xA;&lt;li&gt;higher read throughput (by having the replica which you can use to read data from),&lt;/li&gt;&#xA;&lt;li&gt;and higher write throughput (by not replicating the data to each replica synchronously).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Each database is designed work differently with this structure, and it&#39;s common to have systems where the replication is performed asynchronously by default to increase write throughput at the cost of eventual consistency. Understanding how each database works and identifying your needs are critical here. In many database systems, there is a way to influence the default replication behavior. For instance, &lt;a href=&#34;https://www.tugberkugurlu.com/tags/redis&#34;&gt;Redis&lt;/a&gt; provides a &lt;a href=&#34;https://redis.io/commands/wait&#34;&gt;WAIT&lt;/a&gt; command which can be used to block the current client until all the previous write commands are successfully transferred and acknowledged by at least the specified number of replicas. While this does not make Redis a strongly consistent store, it increase the changes of data availability to a much higher state, and also increases the changes of consistency as well. Note that this comes with a few costs:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The writes will take more time to be executed due to the nature of synchronous replication.&lt;/li&gt;&#xA;&lt;li&gt;You are performing this at the cost of &#39;P&#39; from the &lt;a href=&#34;https://en.wikipedia.org/wiki/CAP_theorem&#34;&gt;CAP theorem&lt;/a&gt;, which means that your system is no longer Partition tolerant.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;MongoDB also offers a similar configuration which is called &lt;a href=&#34;https://docs.mongodb.com/manual/reference/write-concern/&#34;&gt;write concern&lt;/a&gt;, and can be set per each write.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;There is no simple answer here to indicate one is better than the other. It all depends on your needs, and what type of trade-offs you are happy to make.&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_3&#34;&gt;Fault Tolerance&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;What I mean by fault tolerance here is whether the database can gracefully handle one or multiple nodes going down. When a replica is down, this is usually not a big issue within the setup. A new replica can be spawn up, and can catch up with others through the log of the data storage system (which can be unique to each database technology). However, when the primary node, which handles the writes, is down, it&#39;s a bit more serious as there is only one node that handles the writes. In this case, what usually happens is that a new primary is elected from the available replicas through the consensus protocol employed by the database technology. This is what we call &lt;a href=&#34;https://en.wikipedia.org/wiki/Failover&#34;&gt;failover&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As nearly every data storage system comes with a replication setup and a failover approach, this is less of an issue today, but can still be a deciding factor. The differences usually come down the following points:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;What type of protocol the database system employs to handle consensus, and failover. &lt;a href=&#34;https://medium.com/the-sixt-india-blog/raft-and-paxos-a-brief-introduction-to-the-basic-consensus-protocols-powering-distributed-systems-1a0ef7ca3acb&#34;&gt;Raft and Paxos&lt;/a&gt; are a few of them. You often don&#39;t care about the actual protocol used but you care about the implications such as how hitting common issues like &lt;a href=&#34;https://www.percona.com/blog/2020/03/26/split-brain-101-what-you-should-know/&#34;&gt;split-brain&lt;/a&gt; is handled, how fast the failover detection can happen and whether you have any configuration point to influence this, etc.&lt;/li&gt;&#xA;&lt;li&gt;How the clients are handling the failover, e.g. how they perform DNS caching, what is the default timeout to detect a node which is down, etc.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_4&#34;&gt;Scalability&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;One of the most critical parts that can be a differentiator for a database choice. What I mean by scalability here is in terms of both for reads and writes.&lt;/p&gt;&#xA;&#xA;&lt;h4 id=&#34;toc_5&#34;&gt;Read Scalability&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;I believe read scalability is largely a commonly solved problem across databases, and easy to reason about by users with some trade-offs such as eventual consistency. So, read scaling will likely not going to be the differentiator. That said, there could be some constraints put by a particular database around read scaling which might be enough to put you off. For example, AWS PostgreSQL Aurora has a hard limit of 15 nodes to be had at max per cluster. That usually doesn&#39;t become an issue but I can see how it can be deciding factor.&lt;/p&gt;&#xA;&#xA;&lt;h4 id=&#34;toc_6&#34;&gt;Write Scalability&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;On the other hand, things get really interesting when the topic comes to write scaling. Scaling writes have always been much more challenging than scaling the reads, and one of the reasons for this is the shape of the data we have historically stored (i.e. relational data), how we relied on some database features which made this super hard (i.e. &lt;a href=&#34;https://medium.com/ingeniouslysimple/why-did-we-shift-away-from-database-generated-ids-7e0e54a49bb3&#34;&gt;database-generated sequential IDs&lt;/a&gt;), and how much you as the user of a particular database technology needs to do to scale your writes. However, with the rise of &lt;a href=&#34;https://www.youtube.com/watch?v=qI_g07C_Q5I&#34;&gt;NoSQL&lt;/a&gt; databases, the write scaling became much more easier. This waw not because they came with a magical, breakthrough technique or anything. This type of databases just restricted how we were allowed to write the data at the first place, and educated every user about eventual consistency.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If we turn back to our today&#39;s World, the common way to solve the problem of write scaling is through &lt;a href=&#34;https://www.tugberkugurlu.com/archive/redis-cluster-benefits-of-sharding-and-how-it-works&#34;&gt;sharding&lt;/a&gt;, which is the processing of horizontally partitioning the data across multiple different nodes where each of these nodes hold only the subset of the data. The process of how to determine which node holds which data is likely to come down to the user&#39;s choice based on the chosen sharding strategy. However, default is likely to perform the data distribution based on the following calculation with simple systems: generating the the hash of the identifier of the data, and taking the modulo of it against the number of shards. Which hashing algorithm to use here is also database technology specific. For example, Redis uses &lt;a href=&#34;https://redis.io/topics/cluster-spec#appendix&#34;&gt;CRC16&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you identify that you need to perform data partitioning as one node won&#39;t be enough to be able to handle the write load, you will be narrowing down your choice significantly as some databases will not get your this functionality out of the box.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, the real issue starts when it comes to identifying the details of how each database technology makes the sharding work. One interesting aspect here is the resharding part. When you reach to a certain scale with your initial setup (e.g. 5 shards, each having 3 replicas), you might be in need for scaling further. In that scenario, you will be looking into options to add a new shard node to your setup. This will mean that you need to perform an action called resharding, meaning that your data set will be redistributed across nodes, and at least some of the data if not all within the existing shards will move to the newly created shard. This creates a problem with the sharding approach I described above as the clients know that there are only 5 shards, and which node the data needs to be directed to is calculated based on this value. Adding a new shard will start changing things drastically, and will likely introduce downtime to our system unless we are happy to serve the wrong outcome to our users (which we almost never are). More importantly, more data we have across all the nodes, more the downtime we will have which is not a very good scenario to be in.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is where more clever distribution techniques are employed to allow for zero-downtime resharding to be performed. For example, Redis uses a slot based &lt;a href=&#34;https://www.tugberkugurlu.com/archive/redis-cluster-benefits-of-sharding-and-how-it-works#key-distribution&#34;&gt;key distribution model&lt;/a&gt; which works really well under &lt;a href=&#34;https://redis.io/topics/cluster-spec#cluster-live-reconfiguration&#34;&gt;resharding&lt;/a&gt; scenarios. On the other hand, &lt;a href=&#34;https://cassandra.apache.org/&#34;&gt;Cassandra&lt;/a&gt; uses a technique called &lt;a href=&#34;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/architecture/archDataDistributeHashing.html&#34;&gt;consistent hashing&lt;/a&gt; which allows distribution of data across a cluster to minimize reorganization when nodes are added or removed. I don&#39;t have much experience with a structure like this but it seems like &lt;a href=&#34;https://twitter.com/MarcSelwan/status/1320736675882938373?s=20&#34;&gt;writes continue to be accepted successfully during the resharding phase&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;h4 id=&#34;toc_7&#34;&gt;Elastic Scalability&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;Also, you should not just be thinking about the today&#39;s load demand when it comes to scalability but also project the next 6, 12, 24, 36 months. This will let you see whether your system is likely to meet the demand of the future with your current solution or likely to require a redesign. A good example why this is important could be related to a case when you decide to work with a database from a cloud provider, for instance, &lt;a href=&#34;https://aws.amazon.com/rds/aurora/postgresql-features/&#34;&gt;PostgreSQL Aurora&lt;/a&gt; from &lt;a href=&#34;https://aws.amazon.com/&#34;&gt;AWS&lt;/a&gt;. Let&#39;s imagine that based on the demand on your read-heavy system today, you can get away with having 5 largest aurora instances where you have one master, and 4 replicas. As Aurora doesn&#39;t support data partitioning at the time of this writing, your only choice is to increase the replica size to be able to scale your reads further. This sounds great, as our system is read-heavy (for now, let&#39;s assume writes are not an issue, e.g. we have a way to throttle them, etc.), and we can keep adding replicas. However, if we were to project our load against to expected company growth, we may find out that in 12 months, we will be needing 20 replicas instead of 4. That&#39;s a problem as Aurora doesn&#39;t allow more than 15 nodes per cluster. So, this will likely require a redesign of your system one way or another, and most importantly, you may find out about this limit when you happen to hit the ceiling which could be too late to save the day.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Sometimes though, it’s acceptable to commit to a redesign if we were to need a larger scale, for several reasons, e.g. development velocity. However, this needs to be an explicit call and your team needs to have a shared understanding on this.&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_8&#34;&gt;Maintenance and Observability&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Regular backups, and database engine upgrades are big part of your maintenance. Backups are usually handled pretty well with nearly all databases out there today. However, version upgrades can be a pain and often the issues associated with it are not detected till the first upgrade needs to happen. Finding out the procedure for these tasks before you actually need them are critical, and can be a deciding factor. For instance, if you have no choice but to introduce downtime during version upgrades, it could mean a significant impact to your business. Therefore, that risk might need to be assessed prior to your choice.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Observability is another part which you need to be absolutely on top of from day one. You should be understanding what&#39;s happening within your database servers, and have your monitors lined up to detect the potential issues ideally proactively. For example, you need to be able to reason about when you start seeing that your database query latency started to increase for certain queries. Is this happening due to hitting the IOPS limit? Are we saturating other resources of the server such as CPU due to intense indexing caused by elevated writes? Without proper observability, you will be in hell to figure these out. Luckily, many data storage systems are offering near-perfect metrics to gain as good observability as possible.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Related to point above, it&#39;s also worth thinking about the operational aspect. For example, when you are on-call, and you are getting paged related to your data storage system, you want to have some actions that you can take with minimum effort and negative implications. This may not be a problem for you today, but it will surely become a problem at some point (remember &lt;a href=&#34;https://en.wikipedia.org/wiki/Murphy%27s_law&#34;&gt;Murphy&#39;s law&lt;/a&gt;: Anything that can go wrong will go wrong!).&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_9&#34;&gt;Self-healing&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;As mentioned above, anything that can go wrong will go wrong. Paging the on-call engineer to fix an issue related to a database is not the ideal scenario. If you can make your system work within a self-healing mode, you should absolutely invest in doing so. What I mean by self-healing is related to scaling to the elevated need to prevent potential resource saturation issues, healing from failures by performing failovers, resharding when writes start to become a problem, and doing all without needing any intervention.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Some systems offer some of these self-healing operations out of the box. For example, &lt;a href=&#34;https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Integrating.AutoScaling.html&#34;&gt;with AWS Aurora, you can configure replication auto-scaling based on several metrics&lt;/a&gt; (e.g. average CPU time of all replicas, etc.). &lt;a href=&#34;https://aws.amazon.com/blogs/database/how-amazon-dynamodb-adaptive-capacity-accommodates-uneven-data-access-patterns-or-why-what-you-know-about-dynamodb-might-be-outdated/&#34;&gt;With DynamoDB, resharding is performed for you without you knowing nothing about it&lt;/a&gt;, which is pretty epic.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Judging the self-healing aspect might bring a pretty big weight for a certain database choice, and could be a differentiating factor. Also, by investing your effort into investigating this area will pay off quite quickly, and you will thank yourself when you go on-call 🙂&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_10&#34;&gt;Cost&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;This can be an important distinction especially when you are working with a cloud provider. Depending on your needs, sometimes the cost will make you shy away from a perfect data storage system. For example, consider a write-heavy workload with a predictable, steady amount of writes for the foreseeable future. Considering this requirement, DynamoDB sounds like a near-perfect choice if you are heavily using AWS. However, &lt;a href=&#34;https://aws.amazon.com/dynamodb/pricing/&#34;&gt;DynamoDB charges are heavily influenced by write capacity units&lt;/a&gt; (and a few other aspects) which is based on the number of writes and how large the data which you are writing into it. The best thing with DynamoDB is the fact that it scales to your needs elasticity but in this case, we already know our load and it&#39;s steady. Therefore, a PostgreSQL Aurora instance with a known capacity to handle the predictable load will likely perform much better in terms of cost as the price is set based on the instance size, and amount of data you will be storing.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I would suggest to pay attention to this area, especially if your scale requires it and you have alternative choices. If you happen to narrow down your choice to a single storage system, you might need to still calculate the expected cost but just for FYI purposes rather than being an input to your decision making.&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_11&#34;&gt;Regulations and Compliance&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;I am no expert in this area, but I have managed to find my way around it so far. Depending on different regulatory needs which your organization may need to comply with, your database choice may get impacted. This is especially the case when you are going to be storing &lt;a href=&#34;https://en.wikipedia.org/wiki/Personal_data&#34;&gt;personally identifiable information (PII)&lt;/a&gt;. There are several relations to how your data storage system might be concerned with these compliance issues:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Nearly all regulations I have had to deal with so far required encrypting PII data at rest, meaning that you should store the data on disk as encrypted. Luckily, many databases offer this support out of the box, e.g. &lt;a href=&#34;https://docs.mongodb.com/manual/core/security-encryption-at-rest/&#34;&gt;MongoDB&lt;/a&gt;, &lt;a href=&#34;https://docs.amazonaws.cn/en_us/amazondynamodb/latest/developerguide/encryption.usagenotes.html&#34;&gt;DynamoDB&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Transport level encryption is also something you need to have when connecting to the database from the clients. This is also something which many databases have support for. So, unlikely to be an issue.&lt;/li&gt;&#xA;&lt;li&gt;One interesting part with regulations such as &lt;a href=&#34;https://ico.org.uk/for-organisations/dp-at-the-end-of-the-transition-period/data-protection-now-the-transition-period-has-ended/the-gdpr/&#34;&gt;GDPR&lt;/a&gt; is that it gives users &lt;a href=&#34;https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/individual-rights/right-to-erasure/&#34;&gt;the right to be forgotten&lt;/a&gt;, meaning that you should have a way to delete relevant user data from your system upon request from the user within 28 days. For many databases, this is not much of an issue, but some architectural patterns such as event sourcing, and for the database which support this such as &lt;a href=&#34;https://www.eventstore.com/&#34;&gt;EventStoreDB&lt;/a&gt; you may need to plan ahead how you can do this, and what would be the implications.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_12&#34;&gt;Small Details That Could be Deal Breakers&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Besides these, there are some small details which could turn out to be deal breakers depending on your circumstances.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;One of them is &lt;strong&gt;database client support for a particular programming language&lt;/strong&gt;. You found the near-perfect database for your needs. However, it turns out that there is no client for the programming language of your choice. Unless you are happy to stop what you are doing and write the client by yourself, this could be a deal breaker. However, there could be some steps you can take to overcome this deficit depending on how many alternative choices you have:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;You might be able to change the programming language especially if you are considering this database to be used within a fresh codebase. This may or may not be applicable to you depending on your appetite and constraints you are working within.&lt;/li&gt;&#xA;&lt;li&gt;In case you are thinking about using this database within an existing codebase, you can still take the approach described above but encapsulate the data access logic within a separate service that can be written in a different programming language, and exposed through common transport protocols (e.g. direct use of &lt;a href=&#34;https://en.wikipedia.org/wiki/HTTP/2&#34;&gt;HTTP/2&lt;/a&gt;, or &lt;a href=&#34;https://grpc.io/&#34;&gt;gRPC&lt;/a&gt;, etc.).&lt;/li&gt;&#xA;&lt;li&gt;In case none of these are options and the database seems to have a C/C++ client, you might be able to wrap this client within the programming language of your choice, if there is a support for this (e.g. Go has this through &lt;a href=&#34;https://golang.org/pkg/cmd/cgo/&#34;&gt;cgo&lt;/a&gt;). This still requires extra initial effort, and potential maintenance effort. So, you should evaluate the trade-offs of going down this road vs. choosing an alternative.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The other one is around &lt;strong&gt;licensing&lt;/strong&gt;. This is usually a concern when you pick an an open source database technology, and that technology turns out not to be open sourced with &lt;a href=&#34;https://en.wikipedia.org/wiki/Permissive_software_license&#34;&gt;a permissive license&lt;/a&gt;. There could be differences between each license but this might concern you if you are especially distributing the software to your users directly. It&#39;s worth double checking the license and getting a clearance before moving forward with a choice.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_13&#34;&gt;Practicality of This Mental Model&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;This mental model worked for me so many times, and most of the choices I have made turned out to be sustainable options. However, you will see that this or similar approaches usually helps the most on narrowing down your choices. Therefore, it&#39;s likely that you will end up with multiple options that you can work with, and sometimes the trade-offs will also be balanced, and it might become harder to make a call. When that happens, the best tool in your disposal is going to be benchmarking. This approach will likely get you the most accurate quantitative data you will have in your hand to be able to compare choices, and allow you to gain a more solid perspective.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Also, when we get into the hunt of choosing a database, we may end up forgetting that the best database is sometimes no database. This is especially the case if you are working with ephemeral data which is easily repopulated, and can fit into memory. Not suggesting that this is something you should go with, but it&#39;s worth considering it for sure 🙂&lt;/p&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/my-mental-model-on-choosing-a-database-for-a-particular-problem</guid>
      <pubDate>Thu, 08 Apr 2021 02:37:00 +0000</pubDate>
    </item>
    <item>
      <title>Whiteboard-style Coding Interviews Might Not Be as Bad as You Think</title>
      <link>https://www.tugberkugurlu.com/archive/whiteboard-style-coding-interviews-might-not-be-as-bad-as-you-think</link>
      <description>&lt;nav&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_0&#34;&gt;There are Various Hiring Strategies&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_1&#34;&gt;Today&#39;s Problems vs. Tomorrow&#39;s&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_2&#34;&gt;Today&#39;s Problems&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_3&#34;&gt;Tomorrow&#39;s Problems&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_4&#34;&gt;Mixed Hiring Strategies&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_5&#34;&gt;Hiring Into a Team vs. Company&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_6&#34;&gt;Specialist vs. Generalist&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_7&#34;&gt;Hiring Strategy Relationship with Whiteboard-style Coding Interviews&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_8&#34;&gt;Whiteboard-style Coding Interviews&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_9&#34;&gt;These Interviews are Intentionally Structured&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_10&#34;&gt;Hiring Strategy and Coding Interview Structure&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_11&#34;&gt;Whiteboard-style Coding Interviews are not Perfect&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_12&#34;&gt;Untold Truth?&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_13&#34;&gt;Common Misconceptions&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_14&#34;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_15&#34;&gt;Further Resources&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;/nav&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu-blog.s3.us-east-2.amazonaws.com/post-images/01F2D2V8WP27YAPWZ9NR8XCVEZ-thisisengineering-raeng-GzDrm7SYQ0g-unsplash-8.jpg&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&#34;https://unsplash.com/@thisisengineering?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&#34;&gt;ThisisEngineering RAEng&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/s/photos/whiteboard-coding?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText&#34;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Whiteboard-style coding interviews has a bad reputation, and they are being perceived as &amp;quot;bad interview practice&amp;quot; in general within the software industry (e.g. &amp;quot;&lt;a href=&#34;https://www.youtube.com/watch?v=CkmiRCjnl-k&#34;&gt;Why whiteboard interviews are BROKEN&lt;/a&gt;&amp;quot;, &amp;quot;&lt;a href=&#34;https://www.youtube.com/watch?v=bx3--22D4E4&#34;&gt;Coding Interviews are Broken&lt;/a&gt;&amp;quot;, and there are many others like these). Despite this perception, many companies (especially the top-tier tech companies, a.k.a. &lt;a href=&#34;https://medium.com/dev-genius/whats-faang-a9561bb713e2&#34;&gt;FAANG&lt;/a&gt; and the likes) still hire software engineers through this interviewing process.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;What I mean by &amp;quot;whiteboard-style coding interviews&amp;quot; is an assessment through algorithms and data structure based coding challenges which are performed within a coding environment that doesn&#39;t reflect the day-to-day experience of an engineer, e.g. whiteboard, rudimentary online coding editor, etc. This is also known as &amp;quot;leetcode-style&amp;quot;, interviewing through CS trivia questions, etc. So, a physical whiteboard doesn&#39;t necessarily need to be present, but it&#39;s a name that&#39;s often associated with this type of interviews.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;As someone who has conducted 100+ tech interviews within the last 4 years and also as someone who has failed this type of interviews nearly dozen times while being successful with a few of them in the last 10+ years, I would like to express my thoughts on why this perception might not be the most accurate one, why organizations still stick with this type of interviewing style despite the negative perception, and also why this type of interviews can and should even be perceived positively by candidates.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I am a person who believes that there is no cookie-cutter approach to the problem of hiring software engineers effectively. Each organization should essentially do what&#39;s working best for them, and most importantly it should be encouraged to experiment with different approaches to find the near-perfect balance based on what the organization is trying to optimize for. Therefore, my intention with this post is not to try to reject the hypothesis of whiteboard-style interviews being &amp;quot;bad interview practices&amp;quot;. That said, in general, I do believe that gaining a different perspective on this type of interviewing processes will be helpful to all of us, mainly to understand the problem which is aimed to be solved through this type of interview processes. This is not only for us to eventually accept the process as is, and move on with our lives. However, if we understand why the process exist today in the way it&#39;s, we will then be able to form a better judgment of the process from the perspective of both sides, and we can then challenge the assumptions, and influence a change if necessary.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;⚠️ Disclaimer 1: the content of this blog post is based on my personal opinions, and by no means represent the view of my current or former employers. So, just keep that in mind while reading the post, and be nice 🙂&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;div&gt;&lt;/div&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;⚠️ Disclaimer 2: I am acknowledging that interviewing is an area where quantifying the success of the certain interviewing processes is really challenging. Also, I am aware that all the information I will unleash here are not backed by any scientific evidence. However, they come from an experience. So, I still see it valuable to share. Nevertheless, take all this info in this post with a grain of salt.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;div&gt;&lt;/div&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;⚠️ Disclaimer 3: I am aware that there are organizations in many shapes and sizes. It would be naive to think that there would be one interviewing style that would work for all of them. In this post, I am mainly focusing on organizations which work on &lt;a href=&#34;https://svpg.com/customer-inspired-technology-enabled/&#34;&gt;tech-centric products&lt;/a&gt;, where the tech side of the business is not there to just implement features and fix bugs, but instead it&#39;s at the center of your organization to drive product decisions and innovations (in these organizations, tech becomes the organization&#39;s competitive advantage in the market).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In some parts, I might be implicit about this fact. So, keep this in mind. When I am referring to an &amp;quot;organization&amp;quot;, you should now know what I mean by that.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_0&#34;&gt;There are Various Hiring Strategies&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Let&#39;s start seeing the problem of hiring software engineers from the organization&#39;s view point. I personally believe that defining what type of software engineer you want to hire into the organization is a multifaceted problem, and plays a crucial role on understanding the reasons behind the interview process of an organization. I want to touch on three of these dimensions to narrow down the focus of this post. I am super aware that this is not an exhaustive list. However, based on my experience, they play a significant role on shaping up the interview process that software engineers go through. My hope here is that the information in this section will set foundational knowledge for us to understand why a certain interview process might be set in the way it is today, which will/should eventually make it more clear what the problem of &amp;quot;whiteboard&amp;quot;-style interviews are trying solve (more on that later).&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_1&#34;&gt;Today&#39;s Problems vs. Tomorrow&#39;s&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;The hiring criteria usually differs a lot depending on whether you are hiring Software Engineers to solve today&#39;s problems vs. tomorrow&#39;s. This one indeed plays the biggest role on what type of interview process and what type of coding assessment Software Engineers may need to go through.&lt;/p&gt;&#xA;&#xA;&lt;h4 id=&#34;toc_2&#34;&gt;Today&#39;s Problems&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;Today&#39;s problems for an organization are well-known, and we are already aware of the challenges of those problems even if they may have not been solved yet. If you are hiring engineers on solving today&#39;s problems, meaning that you know what technologies, technical challenges, and architectural structure you will be working in the long run based on the today&#39;s view, you will have a pretty good idea on what you want from a Software Engineer.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For instance, if you as the organization are on k8s and you intend to stay on k8s for the rest of your existence as an organization, it&#39;s highly logical to assess the candidate&#39;s knowledge and experience on k8s as part of your interview process. Another example could be given here for a specific domain. Let&#39;s assume that organization&#39;s products are centered around a search functionality. So, it could be acceptable to think that organization can aim for hiring Software Engineers who have prior experience on search domain, and the assessment criteria could be centered around search domain during the interview.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This type of interview processes are relatively easy compared to the next topic we will discuss, because the organization has a high chance to be able to solidify the assessment criteria while also leaving less room for personal judgment. On the other hand, this type of hiring approach comes with a great deal of risk, and the reason is quite simple: tech-centric product design and development is highly volatile. It&#39;s not just that the problems, which we have to deal with, keep evolving, but also the technologies we use to solve these problems. These sometimes even change in the direction which haven&#39;t been imagined yet. Therefore, if your assessment criteria during an interview process is centered around a specific area, regardless of this being a specific technology, domain, or problem solving technique, you are betting on the successful candidates to adopt to these changes, be able to solve the unique problems which come with those, while also being able to innovate. Or, maybe as an organization, you are more naive and thinking that things will stay still for the forseeable future, and you will just be fine. However, I am hoping that no organization is betting on the latter.&lt;/p&gt;&#xA;&#xA;&lt;h4 id=&#34;toc_3&#34;&gt;Tomorrow&#39;s Problems&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;What I mean by tomorrow&#39;s problems is the notion of uncertainty from the user, business and technology centric challenges perspective. It&#39;s critical to being able to deal with this uncertainty potentially within an ambiguous environment (uncertainty often brings out ambiguity regardless of whether the organization is well-structured or not by default). Based on my experience, this is much more close to the reality within the current tech industry where things are changing fast, and you are required to adopt and move fast.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As an organization that wants to hire Software Engineers who will be able to cope with the challenges of both today&#39;s and tomorrow&#39;s problems, &lt;strong&gt;you want to hire engineers that fit into the creative thinking process through their sharp problem solving skills with critical and analytical thinking&lt;/strong&gt;. This may sound great, but assessing engineers against these criteria is much, much harder compared to the process to hire engineers for today&#39;s problems. There are a few reasons for this, again, based on my own experience:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The criteria described above itself doesn&#39;t reveal a specific domain or technology for you to be able to pick for the assessment. This eventually guides the organization to boil down the evaluation to be performed on the basis of &lt;strong&gt;fundamental knowledge and problem solving skills&lt;/strong&gt; (more on that later).&lt;/li&gt;&#xA;&lt;li&gt;As there is no specific technology or domain here to assess, &lt;strong&gt;individual interviewer judgment might be more significant&lt;/strong&gt;. This is especially the case when the interview process is set up correctly and effectively, meaning that the decision is not binary i.e. it&#39;s not &amp;quot;solved the coding problem. Therefore, it&#39;s a pass!&amp;quot;, or vice-versa.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;It&#39;s a challenge to make this type of interview process work in scale, both for the organization and the candidates&lt;/strong&gt;. This is especially the case for big tech companies, which have thousands of engineers working for them.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h4 id=&#34;toc_4&#34;&gt;Mixed Hiring Strategies&lt;/h4&gt;&#xA;&#xA;&lt;p&gt;There is also a room for a hiring strategy which is mixed with both of the criteria mentioned above. At the end of the day, majority of the work for any tech-centric organization is going to be around problem solving through a creative thinking process. However, there is probably still small amount of (I would like to unscientifically say around ~10-15%) work which needs to be completed in the short-medium term, and requires specific skills or knowledge and experience on a specific technology. When you have this need, it&#39;s common to see organizations adopting a different hiring process for that to hire employees for the short term (while also still hiring full time employees to solve tomorrow&#39;s problems), i.e. as contractors. This is completely valid, and works well for both sides as long as this is kept in a minimum, and doesn&#39;t become your default and only hiring strategy. The reason is that:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The organization knows what they want in terms of hiring criteria, which happens to be a specific and easy to assess one.&lt;/li&gt;&#xA;&lt;li&gt;Expectations are set correctly for both sides, i.e. short-term employees know they are hired to execute on the work based on their existing skills and organization knows that there are folks who are hired purely for execution.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_5&#34;&gt;Hiring Into a Team vs. Company&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Depending on where you want to land a prospective employee after the successful outcome from the interview process, your hiring strategy can also differ, and can even be multiplied. If you are an organization where your teams have longevity, and work on specific domains, it&#39;s valid to hire into a specific team. This gives autonomy to each team to be creative about their own hiring strategy while also allowing the team to be much more specific about the assessment criteria, which will potentially lead to multiple hiring strategies to exist within an organization for the same role.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The other option is to hire software engineers into the organization based on a generic criteria, and defer the team selection to a later point. This often works better based on my experience, as it makes it much easier for engineers to move between teams within an organization which further helps for an organization to retain the talent under circumstances where the employee wants to change their team for one reason or another.&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_6&#34;&gt;Specialist vs. Generalist&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;It&#39;s common that software engineers sometimes end up specializing within an area, e.g. Backend, Frontend, Test, QA, iOS, Security, and so on or and so forth. The list can go on, but the truth is that each of these roles requires different skill sets even if all these will require the person to write code and implement software to a certain extent.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;On the other hand, some organizations purposely hire &amp;quot;Software Engineers&amp;quot;. This can vary from organization to organization what this mean but in general this refers to the generalist software engineer, where they are well-versed on solving problems through designing, and implementing software without necessarily constraining themselves within a workflow. In my experience, even these hires end up specializing in one area of software engineering. However, these engineers can still contribute to pretty much throughout the whole lifecycle of the software delivery process.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In general, an organization&#39;s chosen interview process can be very different when it comes to hiring a specialist vs. generalist, but they can still have some fundamental common characteristics. I will touch on this later in this post.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_7&#34;&gt;Hiring Strategy Relationship with Whiteboard-style Coding Interviews&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;What does this information have to do with coding interviews, more specifically the whiteboard-style coding interviews? Having an understanding on what type of hiring strategies are out there, and which one is being used by the organization I am interviewing with helps me gain a wider perspective about their interview process, and makes me emphasize with the strategy. If I am convinced that hiring strategy and interview process aligns, this motivates me more. I am hoping this will at least be similar for you.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So, for the rest of the post, I will be making the assumption that the organization&#39;s hiring strategy has the following criteria:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Hiring engineers to solve tomorrow&#39;s problems, not just today&#39;s problems.&lt;/li&gt;&#xA;&lt;li&gt;Hiring engineers into the organization, not particularly into a team. Even if the intention is to land them into a specific team, you are making the assumption that they can move within the organization.&lt;/li&gt;&#xA;&lt;li&gt;Hiring generalist engineers, not specialist.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;I made these choices here not because the alternatives are somehow bad. The main reason is that these are the criteria that the many organizations generally choose as part of their software engineer hiring strategy, and these contribute heavily to why whiteboard-style coding interviews become the core part of the assessment throughout the interviews.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_8&#34;&gt;Whiteboard-style Coding Interviews&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;There seems to be notable amount of speculation about the negative side of whiteboard-style coding interviews. I say speculation here on purpose, not because it&#39;s people&#39;s intention to speculate, and all of the information out there is completely speculative. I say that because genuinely there are a lot of misconceptions and lack of rationale out there when judging these interviews. As humans we sometimes forget that most things are not black and white, and trade-offs matter. I believe that&#39;s what really is happening here. We end up judging these processes from one side: the candidate&#39;s.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;While acknowledging that every organization&#39;s intentions are different with this type of interviews, I personally believe, with the experience I have both as an interviewer and interviewee, I have a pretty solid idea on why these interviews are shaped up in the way they are today in general. Most importantly, it&#39;s actually largely positive for the candidates that these interviews are set up the way they are today.&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_9&#34;&gt;These Interviews are Intentionally Structured&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;This type of interviews are assessing the candidate&#39;s core problem solving ability through coding while also assessing the candidate&#39;s fundamental computer science knowledge around algorithms, data structures, and complexity analysis&lt;/strong&gt;. Throughout the interview, interviewers will look for signals to give them a higher confidence on these fronts, and this is the key to understand! The evaluation here is not binary, and it will likely be linked to how you communicate your ideas as well as how you execute on them, but let&#39;s ignore that for now to purely look at why these matter. If we go back to the tomorrow&#39;s problems point we touched on above, the reasoning will make much more sense. Even if the organization accepts the fact that tomorrow&#39;s problems are unpredictable and full of uncertainty, they need to be able to form some assumptions around some commonalities of these problems. One of those assumptions happen to be that tech-centric problems will eventually require understanding of the core algorithms, data structures as well as being able to solve problems effectively through coding.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;They also assess the candidate&#39;s critical thinking ability within context of a problem that needs to be solved through coding&lt;/strong&gt;. This gives a pretty solid idea on what type of thought process you have, and how you reason about problems as a Software Engineer. This extends from thinking about edge cases to seeing opportunities within your solution to optimize it proactively in terms of various aspects (e.g. modularizing your solution, improving the runtime performance, choosing your test cases, etc.).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;These coding challenges also set up a pretty good environment to be able to assess your analytical ability, to a certain extent&lt;/strong&gt;. Can you reason about how your solution would perform with different input sizes? Can you ask the correct questions upfront to gather an accurate enough analytical reasoning, and proactively determine the optimum solution according to the info collected? All of this can give pretty accurate signals.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;One of the things that these questions don&#39;t asses is the rote knowledge of the candidate on a particular technology such as a programming language&lt;/strong&gt;. Unless you are being hired as a specialist on a specific programming language, you will likely be given the freedom to choose the language that you are most familiar with, even if that language is not among the languages that the organization is using. This is great, as it gives you the choice for the language which you are the most comfortable with. However, this doesn&#39;t mean that you don&#39;t even need to be fluent in that programming language. It&#39;s quite the opposite. As you are given the choice to choose the best language which you feel comfortable with, you are expected to be fluent with the basics of the language, and it&#39;s very likely that you will only need the basics throughout the interview.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;As a contrary to point above, one other thing that&#39;s not being evaluated is your rote knowledge on the syntax of the programming language you have chosen&lt;/strong&gt;. As you will likely be performing for these interviews within an unfamiliar environment such as an online code editor that won&#39;t have autocompletion, or any other IDE features that you might already be familiar with day to day, it&#39;s understandable that you may make some syntax mistakes here and there, and your code may not actually compile. This is totally OK, and you should not worry about this too much. That&#39;s one other reason why you will likely not be given any option to be able to run your code. The core reason for this is to prevent to noise around retrieving the accurate signals that the interviewer is looking for, and get you spend as much time as possible on the code logic, implementation, and testing. There is also a fundamental assumption here: if you are an engineer who is able to perform well within these interviews by solving the given problems effectively, you will almost always be able to find syntax problems and solve them. So, the potential time that you might have spent fixing these issues would have actually lowered your chances because you would not be giving any useful signals to the interviewer during that time.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_10&#34;&gt;Hiring Strategy and Coding Interview Structure&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;If we relate the structure of the coding interviews with the hiring strategy I mentioned in the previous section, we should now be able to see how things start to make more sense (🤞🏼):&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;The organization, which we are basing our assumptions on in this post, wants to hire  generalist software engineers into the organization (not into a specific team) to solve tomorrow&#39;s problems, not just today&#39;s problems&lt;/strong&gt;. So, the assessment is not restricted according to the work the organization is taking on today. The software engineering candidates are evaluated according to their core problem solving and computer science knowledge, based on several different coding challenges in varying degree of difficulty to maximize the chance of accurate, and high quality signals to be retrieved to reduce the chance of a false-positive, or false-negative. Coding interviewers are only one part of the entire interview process. However, they on its own give pretty accurate signals for the minimum bar around he candidate&#39;s:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;skills to use a programming language to solve a particular problem through coding, while being able to form an algorithm&lt;/li&gt;&#xA;&lt;li&gt;ability to be able to critically think about the problem, while being able to ask the right questions to widen their understanding on the problem, proactively finding out the edge cases, and potential optimizations&lt;/li&gt;&#xA;&lt;li&gt;analytical reasoning for a given problem, according to requirements which may or may not be ambiguous to start with.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_11&#34;&gt;Whiteboard-style Coding Interviews are not Perfect&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Whiteboard-style coding interviews are no silver bullet. The hiring is a complicated problem to solve in general, and this becomes much harder when it comes to hiring talented people to fit into an environment where creative thinking, and core problem solving skills matter the most, if not as much as the core technical skills. It would have been very naive to think that one solution would work perfectly to solve this complicated, multifaceted problem. It&#39;s again worth emphasizing that every organization is different. However, we should have a shared understanding by know what type of organization I am referring to.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As applicable to nearly everything else in our precious World, this type of interviews comes with their own trade-offs, and it&#39;s worth acknowledging them so that we can do as much as possible to mitigate them. The following list of negatives about these interviews are based on my own experience, and not meant to be a definitive and exhaustive list.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;They require significant amount of preparation for candidates&lt;/strong&gt;. This is a legitimate one, and I would like to believe that it&#39;s one of the biggest negatives that&#39;s felt commonly among all the candidates, including myself. As it&#39;s likely that you are not working directly with the all data structures day to day, and spending all your time around algorithmic problems, you need to at least refresh your memory around these concepts, while also making sure that you can give the accurate signals to the interviewers within 45-60 minutes. So, you gotta have the knowledge and skills, and prove that you have it fast. This requires preparation, and not everyone has the privilege to dedicate this amount of effort.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;The structure can be copied by some organizations without actually forming an hiring strategy at the first place&lt;/strong&gt;. This is the worst negative about these interviews, by far! As this interviewing style is adopted by top tech companies, they must be working effectively and efficiently, correct? Great, let&#39;s adopt the same process, and call it a day. Well, not so much. I hope that we now have a shared understanding by now that hiring strategy plays a huge role on why these interviews exist in the way they are today. So, without understanding the why, it&#39;s going to be a miserable experience for your organization and the candidates if you end up copying the process. Don&#39;t do that, understand your organization&#39;s unique problems first, at least then see if this type of coding interviews are a good fit for the problems you are trying to solve as part of your hiring strategy.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;They are occasionally executed poorly, and they lead to either false-negatives or false-positives&lt;/strong&gt;. Sometimes the execution doesn&#39;t meet with the intentions or the expectations, and this type of interviews may occasionally be performed poorly. This could be a one-off occurrence due to the interviewer&#39;s own performance, or it might be a systematic issue (it could actually be related to the point above). For instance, the candidate might be rejected because they didn&#39;t get the syntax of the programming language correctly. It could also be that the candidate has performed really well on a coding interview because they already knew the questions, and executed flawlessly on paper without actually giving much high quality accurate signals around the problem solving ability. Highly calibrated interviewers have the ability to retrieve the correct signals under these circumstances to understand when this is happening, and can often mitigate this type of situations really well. However, less-calibrated interviewers may sometimes overlook this, and potentially lead to a false-positive outcome.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;💡 &lt;strong&gt;A word on false-negatives&lt;/strong&gt;: The topic of false-negatives is outside the purpose of this post, but comes up a lot when coding interview topic is brought up. False-negative interview outcomes are the ones which concludes the candidate&#39;s rejection when the same candidate would have actually performed well on the job (speculatively) even if the candidate&#39;s performance was less than strong during the interview itself. These are super common in software engineering interviews, and more common especially in coding interviews. These outcomes might potentially cause an organization to miss out on incredible talent. However, when you think deeply about this, &lt;strong&gt;false-negatives are actually much more favorable compared to false-positives for the organization&#39;s, their employees&#39; success and happiness in the long run&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The reason is simple: false-positives will lead to a potential decline of the talent within the organization, and this can grow exponentially. Once this starts happening, the talent you retained in your organization might also start being unhappy about this because &lt;a href=&#34;https://youtu.be/rQKis2Cfpeo?t=44&#34;&gt;talented employees tend to want to work with talented colleagues&lt;/a&gt;, and this will eventually lead to attrition of the talent and high turnover rate.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Let&#39;s assume that within the last month you had a hundred borderline outcomes through your interview process, and you let half of them slip while taking the risk on these employees, as you didn&#39;t want to miss out on a potentially great talent. Let&#39;s again assume half of these hires turn out to be false-positives, and they start interviewing candidates within 6 months or so. They will likely end up hiring engineers under the bar, and good portion of those engineers will likely also turn out to be false-positives. Those hires will also end up interviewing engineers within the next 6 months or so, and this cycle will keep continuing, and it won&#39;t be much longer to see the impact of this exponential cycle.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You should also remember that once you hire the wrong person into your organization, letting go of that person is super painful and a long process, unless you are adopting &lt;a href=&#34;https://www.slideshare.net/reed2001/culture-1798664/23-Unlike_many_companieswe_practiceadequate_performance&#34;&gt;a structure similar to what Netflix operates under&lt;/a&gt; (which I personally respect and relate to).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;These interviews favour new graduate software engineers more than the seniors&lt;/strong&gt;. The assumption seems to be that the new graduate software engineer candidates can perform much better in this type of interviews as they have the fresh knowledge from the college around all the core algorithms and data structures subject, whereas the senior engineers won&#39;t have the fresh knowledge around these. This might reflect the truth depending on what type of job a senior engineer is performing day-to-day. If you are not making use of these concepts during your daily job, you might need more preparation. However, going back to the core evaluation criteria, it is also likely the case that senior engineers potentially have the edge when it comes to critical thinking ability, and core problem solving. So, this one is a bit up in the air for me even if there is some truth to it.&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;&lt;strong&gt;These interviews not at all in favour of software engineers who don&#39;t have a formal computer science education&lt;/strong&gt;. This is largely correct, but also largely depends on what type of learning you have accumulated. If we were to speak commonly though, it&#39;s going to be very rough until these candidate can feel comfortable around this type of coding challenges, let alone feeling confident to perform well on the interviews. I am one of these folks without a formal CS education background. I still remember the time where I bombed a coding interview long time ago when the interviewer asked me to implement a stack data structure without using any collection types. I didn&#39;t really know how to it, and wasn&#39;t also able to reason about the problem. However, I took this as a challenge, and over the years, I have accumulated tons of knowledge around algorithms, data structures, and complexity analysis thanks to this interview style. More importantly, this knowledge proved to be immensely useful many times while I was designing, implementing and reviewing systems, and code changes. Long story short, this is not really a negative. It&#39;s either an excuse or opportunity for you to learn and grow yourself. In an era where the entire lectures of formal CS courses are published online (e.g. see &lt;a href=&#34;https://www.youtube.com/playlist?list=PLUl4u3cNGP61Oq3tWYp6V_F-5jb5L2iHb&#34;&gt;Introduction to Algorithms&lt;/a&gt; and &lt;a href=&#34;https://www.youtube.com/playlist?list=PLUl4u3cNGP61hsJNdULdudlRL493b-XZf&#34;&gt;Advanced Data Structures&lt;/a&gt; from &lt;a href=&#34;https://www.youtube.com/user/MIT&#34;&gt;MIT OpenCourseWare&lt;/a&gt;), this knowledge is much more easily acquirable than you might think.&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_12&#34;&gt;Untold Truth?&lt;/h2&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;⚠️ OK, this section is purely based on assumptions, and totally speculative. If you are type of person who likes to focus on facts, or at least the opinions formed based on experience, you may wanna skip this. If not, buckle up!&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;We should be open about the fact that this style of coding interviews require us to prepare, and that takes significant amount of time commitment from the candidate&#39;s point of view. It&#39;s a privilege to have that time, but let&#39;s assume you have that, it also still takes significant amount of diligence and perseverance to be able to perform strongly in these interviews. The outcome is also not left to luck. It&#39;s common that the candidate goes through 4-6 coding challenges throughout the hiring process, and one less than strong performance could be enough for them to get rejected.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I personally believe that the preparation characteristics of these interviews on its own puts off great deal of candidates. These candidates could actually perform well on the job if they were to get it, but they never try because they don&#39;t have the enough enthusiasm and perseverance to go through this process (even if they might have the time). I also speculatively believe that this is a good thing for the organization, especially the ones which can already attack significant amount of candidates, because they are implicitly evaluating the candidate&#39;s enthusiasm for the role, and general attitude on perseverance through this characteristic of the interview style.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I don&#39;t know whether this is the correct assumption or not, but wanted to put it out there here. Considering that software engineering hiring game favouring false-negatives over false-positives (for the right reasons in my opinion), this assumption might not be too far off.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_13&#34;&gt;Common Misconceptions&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Based on the common rationale and understanding we have laid down, I believe it&#39;s now fair to be able to judge some of the common misconceptions about whiteboard-style coding interviews. Here are some of them that I am aware of which are worth specifically highlighting, this is not meant to be an exhaustive list:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;You are being asked about questions which don&#39;t reflect the day to day experience of the engineers working in the organization, and there needs to be a system that should close the gap between the interview and the actual job that is being interviewed for.&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;If you are hiring software engineers only for today&#39;s problems, by all means, you should absolutely do this. If, however, you want to hire software engineers to solve not just today&#39;s problems, but also the tomorrow&#39;s ones, this advice introduces a lot of risks for the business.&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;li&gt;Interviewing environment doesn&#39;t reflect the reality, e.g. you are left with a whiteboard or code editor which doesn&#39;t have autocompletion and even a compiler.&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;This ties back to assessment criteria and the signals which the interviewer is trying to retrieve from the candidate throughout the interview. Being able to get the syntax of the programming language, and how to use the compiler is not one of those criteria. The potential time that you might have spent fixing the potential issues related to these areas would have actually lowered your chances because you are not giving any useful signals to the interviewer during that time. So, it is actually in your favour that you are forced to use a rudimentary coding environment for the test.&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;li&gt;The candidates with interviewing anxiety is being passed on unfairly.&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Who doesn&#39;t have interviewing anxiety? Interviews are stressful as hell, but being able to control the stress and anxiety is also part of our lives. I also have to put this here: &lt;a href=&#34;https://youtu.be/oMkmjg-qZRk?t=2199&#34;&gt;if actors with millions of dollars net worth are willing to take auditions&lt;/a&gt;, I am sure we, as Software Engineers, should be able to justify to ourselves that it might actually be reasonable to take the job interviews 🙂&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_14&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;TL;DR; is that &lt;strong&gt;whiteboard-style coding interviews exist for certain specific reasons, mainly to be able to assess the candidate&#39;s core knowledge around algorithms and data structures, and how well they will likely fit into the creative thinking process through their sharp problem solving skills with critical and analytical thinking to solve not just today&#39;s problems, but also tomorrow&#39;s unique problems within a potentially ambiguous environment.&lt;/strong&gt; I believe this is largely a good thing when these interviews are setup and executed effectively by the organizations and interviewers. They often give a strong indication on what type of organization you are going to be jumping into, which usually tends to be the one who sees software engineers &lt;strong&gt;not just execution machines, but also as part of their product development and innovation process, and values their abilities much more strongly&lt;/strong&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;They surely have some downsides, biggest one of which is that these interviews require significant upfront preparation, and not everyone has the privilege to commit a significant amount of time for this. The process itself also favours false-negatives over false-positives, which is a very good thing for the organization and their teams, but can be super demoralizing for the candidates.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;They are not perfect for the problem that they are trying to solve, for sure. However, I am yet to see an alternative solution to this problem that works as effectively and efficiently as this one when hiring at scale according to the criteria we have just laid down (yes, take-home assessments are not an alternative!).&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_15&#34;&gt;Further Resources&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=bx3--22D4E4&#34;&gt;Coding Interviews are Broken&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=CkmiRCjnl-k&#34;&gt;Why whiteboard interviews are BROKEN&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://twitter.com/rakyll/status/1372747015260540936&#34;&gt;Thread based on Jaana Dogan&#39;s recent Tweet on this topic&lt;/a&gt; Very informative thread, it can be useful if you want to gain further perspective on how different people think about this.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=9TTN-vtWIKw&#34;&gt;Coding Interviews are NOT Broken&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/poteto/hiring-without-whiteboards&#34;&gt;Hiring Without Whiteboards&lt;/a&gt;, it seems to be curated list for companies (or teams) that don&#39;t do &amp;quot;whiteboard&amp;quot; interviews&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://steve-yegge.blogspot.com/2008/03/get-that-job-at-google.html&#34;&gt;Get that job at Google&lt;/a&gt; Steve Yegge&#39;s very informative post on hiring at Google. Very old now, but surprising still a lot of relevant bits. It&#39;s worth checking out.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=r8RxkpUvxK0&#34;&gt;Moishe Lettvin - What I Learned Doing 250 Interviews at Google&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=1wMBw38rAlw&#34;&gt;Coding Interview Problem: Least Disruptive Subrange&lt;/a&gt; Very helpful example coding challenge walk-through with full of rationale from &lt;a href=&#34;https://www.youtube.com/channel/UCcdCkJKXlRoXVD03eo-q8mQ&#34;&gt;Jackson Gabbard&lt;/a&gt;, an ex-Facebook Software Engineer.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/whiteboard-style-coding-interviews-might-not-be-as-bad-as-you-think</guid>
      <pubDate>Sat, 03 Apr 2021 22:07:00 +0000</pubDate>
    </item>
    <item>
      <title>C++, Getting Started with the Basics: Working with Dependencies and Linker</title>
      <link>https://www.tugberkugurlu.com/archive/cpp-getting-started-with-the-basics-working-with-dependencies-and-linker</link>
      <description>&lt;nav&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_0&#34;&gt;Intro&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_1&#34;&gt;Taking a Library Dependency inside Our Own C++ Code&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_2&#34;&gt;Setting up the Build Pipeline&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_3&#34;&gt;Back to Basics: Compilation of a C++ Program&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_4&#34;&gt;Preprocessing the Headers&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_5&#34;&gt;Linking&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_6&#34;&gt;Resources&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;/nav&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_0&#34;&gt;Intro&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;As I mentioned in &lt;a href=&#34;https://www.tugberkugurlu.com/archive/cpp-getting-started-with-the-basics-hello-world-and-build-pipeline&#34;&gt;my previous post about C++&lt;/a&gt;, I am learning &lt;a href=&#34;https://www.tugberkugurlu.com/tags/cpp&#34;&gt;C++&lt;/a&gt;. It has been a bumpy ride so far, and C++ is certainly not an easy to pick up programming language! So, I thought what better way to make the learning stronger than blogging about my journey and pinning down my experience. You now know that the reason this post exists is a bit selfish, but I am hoping it will be helpful to some other folks who are going through the same while also acknowledging that everyone&#39;s mental model is different. So, &lt;a href=&#34;https://www.urbandictionary.com/define.php?term=ymmv&#34;&gt;YMMV&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this post, I want to share my experience of incorporating a 3rd party dependency into my own program, and understanding what goes under the hood during the compilation and linking phase of the build process. For the purposes of this, I will be aiming to incoorporate &lt;a href=&#34;https://github.com/gflags/gflags&#34;&gt;&lt;code&gt;gflags&lt;/code&gt;&lt;/a&gt; C++ library into my own program. This library is providing support to be able to define and parse commandline flags.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_1&#34;&gt;Taking a Library Dependency inside Our Own C++ Code&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The way you should be stating a library dependency in your own C++ code is through the &lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/preprocessor/hash-include-directive-c-cpp?view=msvc-160&#34;&gt;&lt;code&gt;#include&lt;/code&gt; directive&lt;/a&gt; by specifying the header file that you want to take a dependency on. As we probably know by now that the header file doesn&#39;t actually contain the implementation, but only declares the contract between the library and the consumer. We will shortly touch on how we will be able to tie the header file with its implementation.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As we can see inside the &lt;a href=&#34;https://gflags.github.io/gflags/&#34;&gt;&lt;code&gt;gflags&lt;/code&gt; documentation&lt;/a&gt;, the header file we want to work with is called &lt;code&gt;gflags/gflags.h&lt;/code&gt;. That immediately raised some questions for me. I am sure it will for you if you happen to be a newbie in C++ World like me. The biggest one of all is where &lt;code&gt;gflgas/&lt;/code&gt; folder is relative to. That will become more clear when it comes to the building part. So, for now, let&#39;s assume it&#39;s magic™️.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As we learned about how to take a dependency on this library within the code, here is how our sample program looks like:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;iostream&amp;gt;&#xA;#include &amp;lt;gflags/gflags.h&amp;gt;&#xA;&#xA;DEFINE_string(name, &amp;quot;Tugberk&amp;quot;, &amp;quot;Name of the person to greet&amp;quot;);&#xA;&#xA;int main(int argc, char *argv[]) {&#xA;    gflags::ParseCommandLineFlags(&amp;amp;argc, &amp;amp;argv, true);&#xA;    std::cout &amp;lt;&amp;lt; &amp;quot;Hello &amp;quot; &amp;lt;&amp;lt; FLAGS_name &amp;lt;&amp;lt; std::endl;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Nothing fancy, and you can see the &lt;a href=&#34;https://gflags.github.io/gflags/&#34;&gt;&lt;code&gt;gflags&lt;/code&gt; documentation&lt;/a&gt; about the specifics of our usage here. The purpose of this post is not to explain that. The only reason that we are using &lt;code&gt;gflags&lt;/code&gt; here to demonstrate how to take a dependency on an external library, and it is an easy to use one that won&#39;t be hard to explain.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, one thing that&#39;s worth noting is the usage of &lt;code&gt;gflags::&lt;/code&gt; before the &lt;code&gt;ParseCommandLineFlags&lt;/code&gt; function call. &lt;code&gt;gflags&lt;/code&gt; that&#39;s being referred here is the namespace, which we are betting that it will be declared within the &lt;code&gt;gflags.h&lt;/code&gt; header file. &lt;code&gt;gflags::ParseCommandLineFlags&lt;/code&gt; is the fully-qualified reference to the function we want to invoke.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Alternatively, we could have imported the entire &lt;code&gt;gflags&lt;/code&gt; namespace, and be able to call &lt;code&gt;ParseCommandLineFlags&lt;/code&gt; directly without a namespace declaration like the following, which would mean that you can use anything under that namespace directly:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;iostream&amp;gt;&#xA;#include &amp;lt;gflags/gflags.h&amp;gt;&#xA;&#xA;using namespace gflags;&#xA;&#xA;DEFINE_string(name, &amp;quot;Tugberk&amp;quot;, &amp;quot;Name of the person to greet&amp;quot;);&#xA;&#xA;int main(int argc, char *argv[]) {&#xA;    ParseCommandLineFlags(&amp;amp;argc, &amp;amp;argv, true);&#xA;    std::cout &amp;lt;&amp;lt; &amp;quot;Hello &amp;quot; &amp;lt;&amp;lt; FLAGS_name &amp;lt;&amp;lt; std::endl;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Based on my understanding, there is nothing wrong with this in terms of performance of the program or the compiler (I could be wrong, don&#39;t quote me on this). However, this will likely increase your changes of having a name collisions, and also it will make it a bit hard to read the code (i.e. it&#39;s not immediately clear where &lt;code&gt;ParseCommandLineFlags&lt;/code&gt; is coming from).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;One other alternative is to just declare a using for the type you want to use:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;#include &amp;lt;iostream&amp;gt;&#xA;#include &amp;lt;gflags/gflags.h&amp;gt;&#xA;&#xA;using gflags::ParseCommandLineFlags;&#xA;&#xA;DEFINE_string(name, &amp;quot;Tugberk&amp;quot;, &amp;quot;Name of the person to greet&amp;quot;);&#xA;&#xA;int main(int argc, char *argv[]) {&#xA;    ParseCommandLineFlags(&amp;amp;argc, &amp;amp;argv, true);&#xA;    std::cout &amp;lt;&amp;lt; &amp;quot;Hello &amp;quot; &amp;lt;&amp;lt; FLAGS_name &amp;lt;&amp;lt; std::endl;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Although this still suffers from the same problems I listed above to a certain extent, this is a bit better especially when you are planning to use the defined type a few times within the same file.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Final thing I want to note within this code is the use of &lt;code&gt;DEFINE_string&lt;/code&gt;. It&#39;s also defined within the same header file. However, that&#39;s a &lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/preprocessor/macros-c-cpp?view=msvc-160&#34;&gt;Macro&lt;/a&gt; and &lt;a href=&#34;https://github.com/gflags/gflags/blob/827c769e5fc98e0f2a34c47cef953cc6328abced/src/gflags.h.in#L595-L620&#34;&gt;it doesn&#39;t seem to be tied to a namespace&lt;/a&gt;. I don&#39;t have much info about Macros at this stage, but wanted to touch on the rationale of why it&#39;s being used in this way.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_2&#34;&gt;Setting up the Build Pipeline&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;We have our implementation which should give us a command like program where we can call &lt;code&gt;hello-world --name Bob&lt;/code&gt; and that would print out &lt;code&gt;Hello Bob&lt;/code&gt; for us. To be able to demonstrate different build variations, I am going to run the build within a &lt;a href=&#34;https://www.tugberkugurlu.com/tags/docker&#34;&gt;Docker&lt;/a&gt; container. Configuration for this is going to be very simple. The code we have seen above will be inside the &lt;code&gt;main.cpp&lt;/code&gt; file. Also to start with, we will also have a &lt;code&gt;build.sh&lt;/code&gt; file with the following content:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash&#xA;&#xA;g++ -v ./main.cpp -o hello-world&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;-v&lt;/code&gt; is here to give verbose output from the compiler which will be handy when it comes to understanding what goes under the hood. The &lt;code&gt;Dockerfile&lt;/code&gt; content will be as following:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM ubuntu&#xA;&#xA;RUN apt-get update &amp;amp;&amp;amp; apt-get -y install build-essential&#xA;&#xA;WORKDIR /opt/&#xA;RUN mkdir app&#xA;WORKDIR /opt/app&#xA;&#xA;COPY ./ ./&#xA;&#xA;RUN ./build.sh&#xA;CMD [&amp;quot;./hello-world&amp;quot;, &amp;quot;--name=Bob&amp;quot;]&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;When I run &lt;code&gt;docker build .&lt;/code&gt; with this setup, I&#39;m getting an error:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;...&#xA;...&#xA;Step 7/7 : RUN ./build.sh&#xA; ---&amp;gt; Running in 39ce491a452e&#xA;Using built-in specs.&#xA;COLLECT_GCC=g++&#xA;COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper&#xA;OFFLOAD_TARGET_NAMES=nvptx-none:hsa&#xA;OFFLOAD_TARGET_DEFAULT=1&#xA;Target: x86_64-linux-gnu&#xA;Configured with: ../src/configure -v --with-pkgversion=&#39;Ubuntu 9.3.0-17ubuntu1~20.04&#39; --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-HskZEa/gcc-9-9.3.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu&#xA;Thread model: posix&#xA;gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) &#xA;COLLECT_GCC_OPTIONS=&#39;-v&#39; &#39;-o&#39; &#39;hello-world&#39; &#39;-shared-libgcc&#39; &#39;-mtune=generic&#39; &#39;-march=x86-64&#39;&#xA; /usr/lib/gcc/x86_64-linux-gnu/9/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE ./main.cpp -quiet -dumpbase main.cpp -mtune=generic -march=x86-64 -auxbase main -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccebxWeM.s&#xA;GNU C++14 (Ubuntu 9.3.0-17ubuntu1~20.04) version 9.3.0 (x86_64-linux-gnu)&#xA;&#x9;compiled by GNU C version 9.3.0, GMP version 6.2.0, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.22.1-GMP&#xA;&#xA;GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072&#xA;ignoring duplicate directory &amp;quot;/usr/include/x86_64-linux-gnu/c++/9&amp;quot;&#xA;ignoring nonexistent directory &amp;quot;/usr/local/include/x86_64-linux-gnu&amp;quot;&#xA;ignoring nonexistent directory &amp;quot;/usr/lib/gcc/x86_64-linux-gnu/9/include-fixed&amp;quot;&#xA;ignoring nonexistent directory &amp;quot;/usr/lib/gcc/x86_64-linux-gnu/9/../../../../x86_64-linux-gnu/include&amp;quot;&#xA;#include &amp;quot;...&amp;quot; search starts here:&#xA;#include &amp;lt;...&amp;gt; search starts here:&#xA; /usr/include/c++/9&#xA; /usr/include/x86_64-linux-gnu/c++/9&#xA; /usr/include/c++/9/backward&#xA; /usr/lib/gcc/x86_64-linux-gnu/9/include&#xA; /usr/local/include&#xA; /usr/include/x86_64-linux-gnu&#xA; /usr/include&#xA;End of search list.&#xA;GNU C++14 (Ubuntu 9.3.0-17ubuntu1~20.04) version 9.3.0 (x86_64-linux-gnu)&#xA;&#x9;compiled by GNU C version 9.3.0, GMP version 6.2.0, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.22.1-GMP&#xA;&#xA;GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072&#xA;Compiler executable checksum: 466f818abe2f30ba03783f22bd12d815&#xA;./main.cpp:2:10: fatal error: gflags/gflags.h: No such file or directory&#xA;    2 | #include &amp;lt;gflags/gflags.h&amp;gt;&#xA;      |          ^~~~~~~~~~~~~~~~~&#xA;compilation terminated.&#xA;The command &#39;/bin/sh -c ./build.sh&#39; returned a non-zero code: 1&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;There are a few important things to call out here:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;As you may remember from &lt;a href=&#34;https://www.tugberkugurlu.com/archive/cpp-getting-started-with-the-basics-hello-world-and-build-pipeline&#34;&gt;the previous C++ post&lt;/a&gt;, the compiler is looking under several directories which includes &lt;code&gt;/usr/local/include&lt;/code&gt; and a few others right after hitting the &lt;code&gt;#include&lt;/code&gt; directives during its preprocessing stage.&lt;/li&gt;&#xA;&lt;li&gt;We can see that compilation is failing with the following error: &lt;code&gt;gflags/gflags.h: No such file or directory&lt;/code&gt;. That&#39;s giving us an indication that the header file with the path of &lt;code&gt;gflags/gflags.h&lt;/code&gt; wasn&#39;t found in any of the include directories which the compiler was searching under.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;This is an expected error at this stage, because gflags is a 3rd party library, this is a fresh box and we didn&#39;t install that library.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_3&#34;&gt;Back to Basics: Compilation of a C++ Program&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Let&#39;s pause a bit and learn some fundamentals. I kept mentioning compilation, like it&#39;s a black box where you give it some input and get an output, compiled object back. Most of the time, this type of thinking will get us where we want to be. However, my aim here is to understand what&#39;s going on under the hood a bit more. When I went a bit deeper to understand the build process for C++, I have found out that the build step is broken down into three independent steps:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Preprocessing&lt;/strong&gt;: This stage handles the preprocessor directives, like &lt;code&gt;#include&lt;/code&gt; and &lt;code&gt;#define&lt;/code&gt;. After the processing of these directives, the preprocessor produces a single output.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Compilation&lt;/strong&gt;: The compilation step is performed on each output of the preprocessor, and this is the step where the C++ code is converted into assembly code. This step also involves the assembler to turn the assembly code into machine code, then producing an actual binary file (a.k.a. object file). The bit that&#39;s super interesting at this stage is that &lt;strong&gt;these object files can refer to symbols that are not defined&lt;/strong&gt;, and this is how the header files are being compiled at this stage without any specific implementation.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Linking&lt;/strong&gt;: This is the final step within our build process, and this step is handled through the linker which produces the final output for our program from the object files that the compiler produced. This output can be either a library or an executable. It links all the object files by replacing the references to undefined symbols with the correct addresses, and &lt;strong&gt;if the definitions exist in libraries other than the standard one, the linker needs to be informed about these specificity&lt;/strong&gt;, which is relevant to what I am trying to achieve in this post (more on this to come later).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;You can check out &lt;a href=&#34;https://stackoverflow.com/a/6264256/463785&#34;&gt;this incredible Stackoverflow answer&lt;/a&gt; on this topic, which explains compilation steps of a C++ program more in-depth, and I copied most of what I mentioned in this section from there.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_4&#34;&gt;Preprocessing the Headers&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Let&#39;s install &lt;code&gt;gflags&lt;/code&gt; according to &lt;a href=&#34;https://github.com/gflags/gflags/blob/827c769e5fc98e0f2a34c47cef953cc6328abced/INSTALL.md#installing-a-binary-distribution-package&#34;&gt;the installation guidelines of this library&lt;/a&gt;, and rerun the compilation:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-patch&#34;&gt;diff --git a/1-dependency/Dockerfile b/1-dependency/Dockerfile&#xA;index fbaeba8..58215ea 100644&#xA;--- a/1-dependency/Dockerfile&#xA;+++ b/1-dependency/Dockerfile&#xA;@@ -1,6 +1,7 @@&#xA; FROM ubuntu&#xA; &#xA; RUN apt-get update &amp;amp;&amp;amp; apt-get -y install build-essential&#xA;+RUN apt-get -y install libgflags-dev&#xA; &#xA; WORKDIR /opt/&#xA; RUN mkdir app&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;If I run &lt;code&gt;docker build .&lt;/code&gt; command again, it still gives me an error but this time error is different:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;COLLECT_GCC_OPTIONS=&#39;-v&#39; &#39;-o&#39; &#39;hello-world&#39; &#39;-shared-libgcc&#39; &#39;-mtune=generic&#39; &#39;-march=x86-64&#39;&#xA; /usr/lib/gcc/x86_64-linux-gnu/9/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/9/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper -plugin-opt=-fresolution=/tmp/ccjLVDaH.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello-world /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/9/../../.. /tmp/ccg11F4K.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o&#xA;/usr/bin/ld: /tmp/ccg11F4K.o: in function `main&#39;:&#xA;main.cpp:(.text+0x27): undefined reference to `google::ParseCommandLineFlags(int*, char***, bool)&#39;&#xA;/usr/bin/ld: /tmp/ccg11F4K.o: in function `__static_initialization_and_destruction_0(int, int)&#39;:&#xA;main.cpp:(.text+0x12e): undefined reference to `google::FlagRegisterer::FlagRegisterer&amp;lt;std::__cxx11::basic_string&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt; &amp;gt;(char const*, char const*, char const*, std::__cxx11::basic_string&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;*, std::__cxx11::basic_string&amp;lt;char, std::char_traits&amp;lt;char&amp;gt;, std::allocator&amp;lt;char&amp;gt; &amp;gt;*)&#39;&#xA;collect2: error: ld returned 1 exit status&#xA;The command &#39;/bin/sh -c ./build.sh&#39; returned a non-zero code: 1&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;We are still not quite there yet. However, as a software engineer, you know that this is a great feeling! You made some progress, and the changes that you have just made had some impact to move you forward 🙂&lt;/p&gt;&#xA;&#xA;&lt;p&gt;What has happened here is that the compiler was able to find the header file to be able to preprocess the &lt;code&gt;#include&lt;/code&gt; directives. However, where did it find it? We can try to look for &lt;code&gt;gflags.h&lt;/code&gt; file inside the container and see where it&#39;s located:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;# find / -iname gflags.h&#xA;/usr/include/gflags/gflags.h&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;This makes more sense now as &lt;code&gt;/usr/include&lt;/code&gt; is one of the directories where the compiler is looking for to find the header files.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_5&#34;&gt;Linking&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The error we have received this time seems to be coming from &lt;a href=&#34;https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_toc.html&#34;&gt;&lt;code&gt;ld&lt;/code&gt;, the linker&lt;/a&gt;, and it seems to be indicating that there are undefined references to several objects and functions under &lt;code&gt;google&lt;/code&gt; namespace.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;/usr/bin/ld: /tmp/ccg11F4K.o: in function `main&#39;:&#xA;main.cpp:(.text+0x27): undefined reference to `google::ParseCommandLineFlags(int*, char***, bool)&#39;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;It&#39;s worth noting where this &lt;code&gt;google::&lt;/code&gt; namespace comes from. This library seems to be exposed under two namespaces: &lt;code&gt;gflags&lt;/code&gt; and &lt;code&gt;google&lt;/code&gt;. All the documentation is referring to &lt;code&gt;gflags&lt;/code&gt;. However, it seems like any usage under that namespace eventually seems to be redirected to &lt;code&gt;google&lt;/code&gt; namespace. It took a while for me to understand why and how, but I documented the investigation in &lt;a href=&#34;https://stackoverflow.com/questions/66739009&#34;&gt;this Stackoverflow question&lt;/a&gt;. I would suggest for you to check that out first before basing any assumptions on the namespace usage.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;This error is also expected, as we haven&#39;t told the compiler yet what library dependency we want to link to, a.k.a archive, or static library. For static library files, the filenames always start with &lt;code&gt;lib&lt;/code&gt;, and end with &lt;code&gt;.a&lt;/code&gt; (archive, static library) on Unix/Linux (see &lt;a href=&#34;https://www.bogotobogo.com/cplusplus/libraries.php&#34;&gt;this post&lt;/a&gt; for reference). We can use the &lt;code&gt;-l&lt;/code&gt; command line option of the &lt;code&gt;g++&lt;/code&gt; compiler, which would eventually pass this to &lt;code&gt;ld&lt;/code&gt; to &lt;a href=&#34;https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_3.html&#34;&gt;add the archive file to the list of files to link&lt;/a&gt;. This option may be used any number of times. &lt;code&gt;ld&lt;/code&gt; will search its path-list for occurrences of &lt;code&gt;lib{archive}.a&lt;/code&gt; for every &lt;code&gt;{archive}&lt;/code&gt; specified.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With this in mind, we should be able to complete our compilation journey by passing &lt;code&gt;-lgflags&lt;/code&gt; option to &lt;code&gt;g++&lt;/code&gt; compiler:&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The error output above might be confusing you since it seems like &lt;code&gt;/usr/lib/gcc/x86_64-linux-gnu/9/collect2&lt;/code&gt; is invoked directly, not &lt;code&gt;ld&lt;/code&gt;. Quick search suggests to me that &lt;a href=&#34;https://gcc.gnu.org/onlinedocs/gccint/Collect2.html&#34;&gt;&lt;code&gt;collect2&lt;/code&gt;&lt;/a&gt; eventually calls &lt;code&gt;ld&lt;/code&gt; but I am not sure at this stage why and how the compiler located &lt;code&gt;collect2&lt;/code&gt; at the first place, and decided to call it instead of calling &lt;code&gt;ld&lt;/code&gt; directly. For simplicity, I will ignore &lt;code&gt;collect2&lt;/code&gt; for the rest of the post, and only mention &lt;code&gt;ld&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash&#xA;&#xA;g++ -v ./main.cpp -lgflags -o hello-world&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Now, let&#39;s run &lt;code&gt;docker build .&lt;/code&gt; with this setup:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;...&#xA;...&#xA;GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072&#xA;ignoring duplicate directory &amp;quot;/usr/include/x86_64-linux-gnu/c++/9&amp;quot;&#xA;ignoring nonexistent directory &amp;quot;/usr/local/include/x86_64-linux-gnu&amp;quot;&#xA;ignoring nonexistent directory &amp;quot;/usr/lib/gcc/x86_64-linux-gnu/9/include-fixed&amp;quot;&#xA;ignoring nonexistent directory &amp;quot;/usr/lib/gcc/x86_64-linux-gnu/9/../../../../x86_64-linux-gnu/include&amp;quot;&#xA;#include &amp;quot;...&amp;quot; search starts here:&#xA;#include &amp;lt;...&amp;gt; search starts here:&#xA; /usr/include/c++/9&#xA; /usr/include/x86_64-linux-gnu/c++/9&#xA; /usr/include/c++/9/backward&#xA; /usr/lib/gcc/x86_64-linux-gnu/9/include&#xA; /usr/local/include&#xA; /usr/include/x86_64-linux-gnu&#xA; /usr/include&#xA;End of search list.&#xA;GNU C++14 (Ubuntu 9.3.0-17ubuntu1~20.04) version 9.3.0 (x86_64-linux-gnu)&#xA;&#x9;compiled by GNU C version 9.3.0, GMP version 6.2.0, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.22.1-GMP&#xA;&#xA;GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072&#xA;Compiler executable checksum: 466f818abe2f30ba03783f22bd12d815&#xA;COLLECT_GCC_OPTIONS=&#39;-v&#39; &#39;-o&#39; &#39;hello-world&#39; &#39;-shared-libgcc&#39; &#39;-mtune=generic&#39; &#39;-march=x86-64&#39;&#xA; as -v --64 -o /tmp/ccZZScyH.o /tmp/ccvjxfiH.s&#xA;GNU assembler version 2.34 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.34&#xA;COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/&#xA;LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../:/lib/:/usr/lib/&#xA;COLLECT_GCC_OPTIONS=&#39;-v&#39; &#39;-o&#39; &#39;hello-world&#39; &#39;-shared-libgcc&#39; &#39;-mtune=generic&#39; &#39;-march=x86-64&#39;&#xA; /usr/lib/gcc/x86_64-linux-gnu/9/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/9/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper -plugin-opt=-fresolution=/tmp/cc10fZHH.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o hello-world /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/9/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/9/../../.. /tmp/ccZZScyH.o -lgflags -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/9/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/crtn.o&#xA;COLLECT_GCC_OPTIONS=&#39;-v&#39; &#39;-o&#39; &#39;hello-world&#39; &#39;-shared-libgcc&#39; &#39;-mtune=generic&#39; &#39;-march=x86-64&#39;&#xA;Removing intermediate container ce5a3c257fe2&#xA; ---&amp;gt; 455abaa9d2d9&#xA;Step 9/9 : CMD [&amp;quot;./hello-world&amp;quot;, &amp;quot;--name=Bob&amp;quot;]&#xA; ---&amp;gt; Running in 2b17e00b3210&#xA;Removing intermediate container 2b17e00b3210&#xA; ---&amp;gt; cc8ae20c8aa8&#xA;Successfully built cc8ae20c8aa8&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Build passed! If we look at the compiler output from this, we should be able to see that &lt;code&gt;-lgflags&lt;/code&gt; option is passed to the linker:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu-blog.s3.us-east-2.amazonaws.com/post-images/01F1G3FVJPT67X1TW1MWXBGWPJ-Screenshot-2021-03-21-at-22.51.31.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Based on the information we have about the linker and with the &lt;code&gt;-lgflags&lt;/code&gt; option being passed to it now, we know that the linker is looking for &lt;code&gt;libgflags.a&lt;/code&gt; static library file to use as part of the linking process. Where did it find it though, and how did it knew to look there at the first place? Let&#39;s look for that file within the container:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;➜ docker run -it cc8ae20c8aa8 /bin/sh&#xA;# find / -iname libgflags.a&#xA;/usr/lib/x86_64-linux-gnu/libgflags.a&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;That seems to be existing under &lt;code&gt;/usr/lib/x86_64-linux-gnu&lt;/code&gt; folder. This is &lt;a href=&#34;https://unix.stackexchange.com/a/43214&#34;&gt;the folder where architecture specific libraries live&lt;/a&gt; under Ubuntu. If we also look at what&#39;s being passed to the linker through the &lt;code&gt;-L&lt;/code&gt; command line option, which adds a path to the list of paths that &lt;code&gt;ld&lt;/code&gt; will search for archive libraries and &lt;code&gt;ld&lt;/code&gt; control scripts, we will see that &lt;code&gt;/usr/lib/x86_64-linux-gnu&lt;/code&gt; is already bing passed.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu-blog.s3.us-east-2.amazonaws.com/post-images/01F1G46H1V0G9KJCGZN2NVDB0W-Screenshot-2021-03-21-at-22.50.58.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Nice, the C++ build process is now making more sense for me 🙂&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Just to make sure things are working as expected, I will run the container I have just built.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;➜ docker run cc8ae20c8aa8                  &#xA;Hello Bob&#xA;➜ docker run cc8ae20c8aa8 ./hello-world --name=Alice&#xA;Hello Alice&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;It works as expected 🎉&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_6&#34;&gt;Resources&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;These are the resources I benefited from while writing this post. It&#39;s only fair I give these some credit. They might not entirely beneficial to you though:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art026&#34;&gt;Where Does GCC Look to Find its Header Files?&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.toptal.com/c-plus-plus/c-plus-plus-understanding-compilation&#34;&gt;How C++ Works: Understanding Compilation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/46456498/what-is-the-purpose-of-a-cmake-file&#34;&gt;What is the purpose of a .cmake file?&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://unix.stackexchange.com/questions/138188/easily-unpack-deb-edit-postinst-and-repack-deb&#34;&gt;Easily unpack DEB, edit postinst, and repack DEB&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://askubuntu.com/questions/30482/is-there-an-apt-command-to-download-a-deb-file-from-the-repositories-to-the-curr&#34;&gt;Is there an apt command to download a deb file from the repositories to the current directory?&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://en.cppreference.com/w/cpp/language/lookup&#34;&gt;Name lookup&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://en.cppreference.com/w/cpp/language/translation_phases&#34;&gt;Phases of translation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/6264249&#34;&gt;How does the compilation/linking process work?&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/17281214/using-fully-qualified-names-in-c&#34;&gt;Using fully qualified names in C++&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.learncpp.com/cpp-tutorial/introduction-to-the-compiler-linker-and-libraries/&#34;&gt;0.5 — Introduction to the compiler, linker, and libraries&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_toc.html&#34;&gt;The GNU linker&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_3.html&#34;&gt;ld: Command Line Options&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/cpp-getting-started-with-the-basics-working-with-dependencies-and-linker</guid>
      <pubDate>Tue, 23 Mar 2021 18:46:00 +0000</pubDate>
    </item>
    <item>
      <title>C++, Getting Started with the Basics: Hello World and the Build Pipeline</title>
      <link>https://www.tugberkugurlu.com/archive/cpp-getting-started-with-the-basics-hello-world-and-build-pipeline</link>
      <description>&lt;nav&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_0&#34;&gt;Intro&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_1&#34;&gt;Hello World&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_2&#34;&gt;The Build Pipeline&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_3&#34;&gt;What&#39;s Next?&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_4&#34;&gt;Resources&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;/nav&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_0&#34;&gt;Intro&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I am probably the least qualified person to be writing a blog post about &lt;a href=&#34;https://en.wikipedia.org/wiki/C%2B%2B&#34;&gt;C++&lt;/a&gt;. So, please approach this post with some caution.&#xA;But, why am I writing it then? Well, I am currently learning C++, and it has been an unusual experience compared to my other programming-language-learning journeys.&#xA;At this stage, my assumption is that the main difference that causes me to struggle comes from the fact that:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;C++ doesn&#39;t have a universally agreed way to define how projects should be structured and built (well, sort of)&lt;/li&gt;&#xA;&lt;li&gt;C++ also doesn&#39;t have one defined way to manage your dependencies (well, sort of)&lt;/li&gt;&#xA;&lt;li&gt;Finally, C++ is not a programming language that will spit out an executable which will perform garbage collection for you out of the box (well, it really doesn&#39;t have this)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;All of these are quite unusual characteristics for me when learning a programming language. I admit, I have been spoiled! The other thing to admit is that I have used C++ before in projects, but on all those occasions, the projects and build structures were already set up, and I only needed to maintain the codebase, and do occasionally changes in them. Also, these were projects which didn&#39;t run under a significant scale to generate those usual high-scale issues. So, nothing really required me to deeply understand how the C++ and its toolchain worked.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, I knew that a few obstacle wouldn&#39;t wear me down, and I had to find a way to regain my perseverance! So, I thought what better way to make the learning stronger than blogging about my journey and pinning down my experience. And, here we are! You now know that the reason this post exists is a bit selfish, but I am hoping it will be helpful to some other folks who are going through the same while also acknowledging that everyone&#39;s mental model is different. So, &lt;a href=&#34;https://www.urbandictionary.com/define.php?term=ymmv&#34;&gt;YMMV&lt;/a&gt;!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If you are still here, let me tell you what this post is all about! I will be going through the &amp;quot;Hello World&amp;quot; experience for C++, while also taking the explanation a bit beyond and understanding how the build pipeline works by attempting to dive deep into the bowels of the compiler (but I also now I am probably only scratching the surface)!&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_1&#34;&gt;Hello World&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;I am learning a programming language. So, I should be concerned about the syntax, right? Well, usually but I really don&#39;t care about that at this stage, at least too much. At the moment, I am making an assumption that I will be able to get a handle of the syntax gradually as I start solving actual problems. What I really wanted to focus is the toolchain experience, and how I can glue things together.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So, I focused on getting a bare minimum program written and understanding what goes in that as much as possible. As you can guess, it&#39;s the good-old &amp;quot;Hello World&amp;quot; program. Here is the code for that which is saved within the &lt;code&gt;main.cpp&lt;/code&gt; file.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;#include &amp;lt;iostream&amp;gt;&#xA;&#xA;int main() {&#xA;    std::cout &amp;lt;&amp;lt; &amp;quot;Hello World&amp;quot; &amp;lt;&amp;lt; std::endl;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Nothing fancy here. However, I was able to learn so many things just from this small program!&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;First line is the &lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/preprocessor/hash-include-directive-c-cpp&#34;&gt;#include directive&lt;/a&gt;, which allows us to define the dependency between our source file and the &amp;quot;include file&amp;quot; (a.k.a. header file), which are the files that contains the constant and macro definitions, declarations of external variables and complex data types. These files don&#39;t contain the actual implementation. I know that you have more questions now, but hopefully I will be able to touch more into this in the upcoming posts.&lt;/li&gt;&#xA;&lt;li&gt;C++ has a &lt;a href=&#34;https://en.wikipedia.org/wiki/C%2B%2B_Standard_Library&#34;&gt;Standard Library&lt;/a&gt;, which contains a collection of classes and functions, which are written in the core language and part of the C++ ISO Standard itself.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Input/output_(C%2B%2B&#34;&gt;&lt;code&gt;iostream&lt;/code&gt;&lt;/a&gt;#Input/output_streams) is part of the standard library, and provides C++ input and output fundamentals. &lt;code&gt;std::&lt;/code&gt; prefix represents the standard library.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;cout&lt;/code&gt; is coming from the standard library, and represents the standard output stream.&lt;/li&gt;&#xA;&lt;li&gt;You can write into an output stream using the &amp;quot;&lt;a href=&#34;https://docs.microsoft.com/en-us/cpp/standard-library/using-insertion-operators-and-controlling-format&#34;&gt;insertion operator&lt;/a&gt;&amp;quot; (i.e. &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;), which sends bytes to that output stream object.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_2&#34;&gt;The Build Pipeline&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;The question now is how we can run this. C++ is a static typed language which requires a compilation step (well, it&#39;s more involved than compilation but hopefully we will get there, hang in there!). So, we need to compile the source code into an executable which we can run. This is where things got a bit more interesting for me because there isn&#39;t one compiler that you can use for C++. There are at least three of them (possibly more?): &lt;code&gt;g++&lt;/code&gt;, &lt;code&gt;gcc&lt;/code&gt;, &lt;code&gt;clang++&lt;/code&gt; (well, &lt;em&gt;I guess&lt;/em&gt; you can also count &lt;a href=&#34;https://stackoverflow.com/a/1712803/463785&#34;&gt;&lt;code&gt;c++&lt;/code&gt; which is actually a symbolic link&lt;/a&gt;). I honestly don&#39;t know at this stage enough about these to be able to tell you about the difference between them. So, for the purposes of not getting stuck, I am going to go with &lt;code&gt;g++&lt;/code&gt; for now, and there is not a particular reason to why I chose it other than the fact that most examples I have come across so far have been using &lt;code&gt;g++&lt;/code&gt; 🤷🏻‍♂️.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu-blog.s3.us-east-2.amazonaws.com/post-images/01F140WSJCR5KE63NNXD48R488-Screenshot-2021-03-19-at-01.13.44.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Here is something more fun! &lt;a href=&#34;https://stackoverflow.com/a/19535525/463785&#34;&gt;&lt;code&gt;g++&lt;/code&gt; on my Mac actually ends up calling &lt;code&gt;clang&lt;/code&gt;&lt;/a&gt;. Go figure 🤷🏻‍♂️. If you are someone who understands the diff between C++ compilers, please direct me to a resource which would make me understand what is really going on here. I have given up on this for now 😕&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;OK, compiler choice is sorted out, kind of. Let&#39;s compile this now. Here is the simplest compilation we can run which will spit out an executable called &lt;code&gt;hello-world&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;g++ ./main.cpp -o hello-world&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;If we add &lt;code&gt;-v&lt;/code&gt; flag, we will actually be able to see in more details what&#39;s going on underneath:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;➜ g++ -v ./main.cpp -o hello-world&#xA;Apple LLVM version 10.0.1 (clang-1001.0.46.4)&#xA;Target: x86_64-apple-darwin18.7.0&#xA;Thread model: posix&#xA;InstalledDir: /Library/Developer/CommandLineTools/usr/bin&#xA; &amp;quot;/Library/Developer/CommandLineTools/usr/bin/clang&amp;quot; -cc1 -triple x86_64-apple-macosx10.14.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cpp -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fno-strict-return -masm-verbose -munwind-tables -target-sdk-version=10.14 -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 450.3 -v -resource-dir /Library/Developer/CommandLineTools/usr/lib/clang/10.0.1 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk -I/usr/local/include -stdlib=libc++ -Wno-atomic-implicit-seq-cst -Wno-framework-include-private-from-public -Wno-atimport-in-framework-header -Wno-quoted-include-in-framework-header -fdeprecated-macro -fdebug-compilation-dir /Users/tugberkugurlu/go/src/github.com/tugberkugurlu/cmake-getting-started/0-hello-world -ferror-limit 19 -fmessage-length 95 -stack-protector 1 -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fobjc-runtime=macosx-10.14.0 -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o /var/folders/l4/2c_f_d8973z3g7lmkb9xcw9h0000gn/T/main-d3ea6f.o -x c++ ./main.cpp&#xA;clang -cc1 version 10.0.1 (clang-1001.0.46.4) default target x86_64-apple-darwin18.7.0&#xA;ignoring nonexistent directory &amp;quot;/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/v1&amp;quot;&#xA;ignoring nonexistent directory &amp;quot;/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/local/include&amp;quot;&#xA;ignoring nonexistent directory &amp;quot;/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/Library/Frameworks&amp;quot;&#xA;#include &amp;quot;...&amp;quot; search starts here:&#xA;#include &amp;lt;...&amp;gt; search starts here:&#xA; /usr/local/include&#xA; /Library/Developer/CommandLineTools/usr/include/c++/v1&#xA; /Library/Developer/CommandLineTools/usr/lib/clang/10.0.1/include&#xA; /Library/Developer/CommandLineTools/usr/include&#xA; /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include&#xA; /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/System/Library/Frameworks (framework directory)&#xA;End of search list.&#xA; &amp;quot;/Library/Developer/CommandLineTools/usr/bin/ld&amp;quot; -demangle -lto_library /Library/Developer/CommandLineTools/usr/lib/libLTO.dylib -no_deduplicate -dynamic -arch x86_64 -macosx_version_min 10.14.0 -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk -o hello-world /var/folders/l4/2c_f_d8973z3g7lmkb9xcw9h0000gn/T/main-d3ea6f.o -L. -L/Users/tugberkugurlu/.tensorflow-1.11.0/lib -L/usr/local/lib -lc++ -lSystem /Library/Developer/CommandLineTools/usr/lib/clang/10.0.1/lib/darwin/libclang_rt.osx.a&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Lots of useful things to unpack from this output, which really helped me understand how the compiler is behaving! I cannot say that I currently understand all of it, but let me try to explain what I have been able to extract from this so far:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;clang&lt;/code&gt; compiler is called to compile the &lt;code&gt;main.cpp&lt;/code&gt; file, and spit out the file called &lt;code&gt;main-d3ea6f.o&lt;/code&gt;, &lt;a href=&#34;https://stackoverflow.com/a/2186252/463785&#34;&gt;which contains the compiled object code&lt;/a&gt;. That file is being put under the temporary &lt;code&gt;/var/folders/l4/2c_f_d8973z3g7lmkb9xcw9h0000gn/T&lt;/code&gt; folder, so that the compiler can refer back to it later.&lt;/li&gt;&#xA;&lt;li&gt;The compilation will happen for the target &lt;code&gt;x86_64-apple-darwin18.7.0&lt;/code&gt;. I am assuming this is used as the default because I am performing this compilation on my Mac, and I haven&#39;t specified a target for the compiler.&lt;/li&gt;&#xA;&lt;li&gt;While compilation is happening, the compiler is looking under several directories which includes &lt;code&gt;/usr/local/include&lt;/code&gt; and a few others right after hitting the &lt;code&gt;#include&lt;/code&gt; directive. These directories are known as include directories, and these are where the header files are being looked for. In my case, &lt;code&gt;iostream&lt;/code&gt; header file is located under &lt;code&gt;/Library/Developer/CommandLineTools/usr/include/c++/v1&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Once the compilation is performed, &lt;a href=&#34;https://linux.die.net/man/1/ld&#34;&gt;&lt;code&gt;ld&lt;/code&gt;&lt;/a&gt; is invoked. &lt;code&gt;ld&lt;/code&gt; is the linker (see &lt;code&gt;man ld&lt;/code&gt;), which combines several object files and libraries, resolves references and produces an output file. If you look at the output, you can see that &lt;code&gt;main-d3ea6f.o&lt;/code&gt; file which contains our compiled object code is passed into &lt;code&gt;ld&lt;/code&gt; as one of its arguments. There are also a few folders passed in as an argument here, and one of them is &lt;code&gt;/Users/tugberkugurlu/.tensorflow-1.11.0/lib&lt;/code&gt;, which is a bit strange. The reason that&#39;s there is because it&#39;s set as one of the paths through the &lt;code&gt;LIBRARY_PATH&lt;/code&gt; environment variable for me. &lt;a href=&#34;https://gcc.gnu.org/onlinedocs/gcc/Environment-Variables.html&#34;&gt;Colon-separated list of directories through this environment variable is used by the linker when searching for special linker files&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;You can also see the output of the &lt;code&gt;ld&lt;/code&gt; is specified as &lt;code&gt;-o hello-world&lt;/code&gt;, which is the name that we have given to &lt;code&gt;g++&lt;/code&gt; compiler.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;I am most likely glossing over a lot of details here. I have found &lt;a href=&#34;https://www.toptal.com/c-plus-plus/c-plus-plus-understanding-compilation&#34;&gt;this article&lt;/a&gt; to be a very informative when it comes to explaining the C++ build pipeline, by breaking it down to three steps called preprocessing, compilation and linking. So, please check it out for more thorough explanation.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;After the compilation and linking, we end up with an executable file called &lt;code&gt;hello-world&lt;/code&gt;, and we can execute it to see our super complex output:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;➜ ./hello-world&#xA;Hello World&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_3&#34;&gt;What&#39;s Next?&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;This was the very basic example. I am sure you can relate to the fact that almost none of the real world problems will be solved with this simple implementation. The next part for me will be to look at how we can work with a multi-file project as well as being able to take an external open source library as a dependency.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I am new on my C++ journey. So, if you see anything wrong and things that I can benefit from, please do leave a comment on this post 🙂&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_4&#34;&gt;Resources&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.toptal.com/c-plus-plus/c-plus-plus-understanding-compilation&#34;&gt;How C++ Works: Understanding Compilation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://stackoverflow.com/questions/172587/what-is-the-difference-between-g-and-gcc&#34;&gt;What is the difference between g++ and gcc?&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://alibabatech.medium.com/gcc-vs-clang-llvm-an-in-depth-comparison-of-c-c-compilers-899ede2be378&#34;&gt;GCC vs. Clang/LLVM: An In-Depth Comparison of C/C++ Compilers&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art026&#34;&gt;Where Does GCC Look to Find its Header Files&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/cpp-getting-started-with-the-basics-hello-world-and-build-pipeline</guid>
      <pubDate>Fri, 19 Mar 2021 01:09:00 +0000</pubDate>
    </item>
    <item>
      <title>Configure Free Wildcard SSL Certificate on AWS Application Load Balancer (ALB) Through Terraform</title>
      <link>https://www.tugberkugurlu.com/archive/configure-free-wildcard-ssl-certificate-on-aws-application-load-balancer-through-terraform</link>
      <description>&lt;nav&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_0&#34;&gt;Request Certificate Creation With AWS Certificate Manager&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_1&#34;&gt;Domain Name Validation Through Route53 DNS Configuration&lt;/a&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_2&#34;&gt;Wiring It up with Application Load Balancer&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_3&#34;&gt;Redirecting HTTP Traffic Through an ALB Rule&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;a href=&#34;#toc_4&#34;&gt;Resources&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;/nav&gt;&#xA;&lt;p&gt;Last week, I have moved all my personal compute and storage from &lt;a href=&#34;https://azure.microsoft.com/&#34;&gt;Azure&lt;/a&gt; to &lt;a href=&#34;https://aws.amazon.com/&#34;&gt;AWS&lt;/a&gt;. I took this opportunity as an excuse to also start to manage all that infrastructure through &lt;a href=&#34;https://www.terraform.io/&#34;&gt;Terraform&lt;/a&gt;. Why AWS though? I had the chance to use AWS before on and off, but since &lt;a href=&#34;https://twitter.com/tourismgeek/status/1091003681003237376&#34;&gt;I joined Deliveroo 2 years ago&lt;/a&gt;, I have been using AWS exclusively and extensively. So, it&#39;s the least friction for me when it comes to working with a cloud provider. That said, this migration has still been a really great learning experience, and it also emphasized it more for me that AWS is million miles ahead in their journey when it comes to developer experience. Things just work ™️, especially when it comes to gluing things together (we will see in an example of that in this post). When they don&#39;t, it&#39;s also very obvious the reasons, which makes it easy to diagnose what&#39;s going wrong (although, &lt;a href=&#34;https://nodramadevops.com/2019/11/why-is-aws-iam-so-hard/&#34;&gt;it&#39;s probably because of IAM&lt;/a&gt; for like 99.9% of the cases).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;During this migration, I have also discovered that you can actually configure SSL on your own domain for free, without any additional charges through &lt;a href=&#34;https://aws.amazon.com/certificate-manager/&#34;&gt;AWS Certificate Manager&lt;/a&gt; (ACM) if you are already using &lt;a href=&#34;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html&#34;&gt;AWS Application Load Balancer&lt;/a&gt; (ALB). This was a valuable find for me, as I needed to enable HTTPS for this blog which I have been procrastinating to get it done, like forever. However, when I think about it, the additional payment for the SSL certificate wasn&#39;t the only reason that was making me delay getting one. It was more to do with the cost of maintenance that I didn&#39;t really want to get into (e.g. certificate renewals and all that).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;ALB and ACM integration addresses both of these issues, by providing a way to configure SSL as well as keeping it automatically renewed without any additional charges. To be fair, there is probably also a way to automate this all on Azure, but I have been also away from that world for over 2 years now, and I didn&#39;t have the mental capacity to sort it out. Anyway, enough with the excuses, and let&#39;s see how to make this all sorted through Terraform.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_0&#34;&gt;Request Certificate Creation With AWS Certificate Manager&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;For the purpose of serving the content of this blog through HTTPS, I wanted to create an SSL certificate for &lt;code&gt;www.tugberkugurlu.com&lt;/code&gt;. However, I also wanted to have the option to serve other content under subdomains. That led me to look into whether I can actually create a wildcard certificate, and this turned out to be possible. As started in &lt;a href=&#34;https://docs.aws.amazon.com/acm/latest/userguide/acm-certificate.html&#34;&gt;the ACM characteristics docs&lt;/a&gt;, ACM allows you to use an asterisk (&lt;code&gt;*&lt;/code&gt;) in the domain name to create an ACM certificate containing a wildcard name that can protect several subdomains.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With that information, the next step was to see how Terraform would allow me to create a wilcard certificate. &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs&#34;&gt;Terraform AWS provider&lt;/a&gt; already has a resource to create the certificate, which is called &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate&#34;&gt;&lt;code&gt;aws_acm_certificate&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;resource &amp;quot;aws_acm_certificate&amp;quot; &amp;quot;tugberkugurlu_com&amp;quot; {&#xA;  domain_name               = &amp;quot;tugberkugurlu.com&amp;quot;&#xA;  subject_alternative_names = [&amp;quot;*.tugberkugurlu.com&amp;quot;]&#xA;  validation_method         = &amp;quot;DNS&amp;quot;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Let&#39;s take a look what each of these things mean:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;domain_name&lt;/code&gt;: Fully qualified domain name (FQDN), that you want to secure with an ACM certificate.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;subject_alternative_names&lt;/code&gt;: Additional FQDNs to be included in the Subject Alternative Name extension of the ACM certificate. Here, we can use an asterisk (&lt;code&gt;*&lt;/code&gt;) to create a wildcard certificate that protects several sites in the same domain. However, note that the asterisk (&lt;code&gt;*&lt;/code&gt;) can protect only one subdomain level when you request a wildcard certificate. For example, &lt;code&gt;*.tugberkugurlu.com&lt;/code&gt; can protect &lt;code&gt;foo.tugberkugurlu.com&lt;/code&gt; and &lt;code&gt;bar.tugberkugurlu.com&lt;/code&gt;, but it cannot protect &lt;code&gt;foo.bar.tugberkugurlu.com&lt;/code&gt;. Another thing to note here is that &lt;code&gt;*.tugberkugurlu.com&lt;/code&gt; protects only the subdomains of &lt;code&gt;tugberkugurlu.com&lt;/code&gt;, it does not protect &lt;a href=&#34;https://help.easyredir.com/en/articles/453072-what-is-a-domain-apex&#34;&gt;the domain apex&lt;/a&gt; (i.e. &lt;code&gt;tugberkugurlu.com&lt;/code&gt; in our case here). That&#39;s why I am providing that through the &lt;code&gt;domain_name&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;validation_method&lt;/code&gt;: ACM needs to validate that you actually own this domain before it can issue a public certificate. This validation can be performed through either &lt;code&gt;EMAIL&lt;/code&gt; or &lt;code&gt;DNS&lt;/code&gt;. I am going with &lt;code&gt;DNS&lt;/code&gt; here for several reasons:&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;DNS validation is required to be able to renew your certificates automatically through &lt;a href=&#34;https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html&#34;&gt;the managed certificate renewal&lt;/a&gt; process.&lt;/li&gt;&#xA;&lt;li&gt;DNS validation also allows us to complete the whole process through Terraform if you use &lt;a href=&#34;https://aws.amazon.com/route53/&#34;&gt;Route53&lt;/a&gt; as your domain&#39;s DNS host, and managing its state through Terraform as well.&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;This is all we have to do to request a creation of the certificate, and like I mentioned, the certificate renewal is going to be performed automatically for us since we are creating the initial certificate with DNS validation. See &lt;a href=&#34;https://docs.aws.amazon.com/acm/latest/userguide/dns-renewal-validation.html&#34;&gt;this documentation around ACM certificate renewal&lt;/a&gt; to find more about how the automatic renewal works. It&#39;s also worth mentioning that there is further configuration you can provide. So, I suggest you to check out the Terraform documentation for &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate&#34;&gt;the ACM certificate resource&lt;/a&gt; to learn more about those options in case you end up needing them.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_1&#34;&gt;Domain Name Validation Through Route53 DNS Configuration&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Before I applied the changes, I also wanted to make sure that the domain validation side of the story is also sorted out. I use &lt;a href=&#34;https://aws.amazon.com/route53/&#34;&gt;Route53&lt;/a&gt; as my DNS service, and this made it so much easier to perform the validation.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;To be fair, even without this, it shouldn&#39;t be too much of a hassle as DNS validation is just going to be a one-off process regardless of the approach. So, it&#39;s still low friction even you need to perform this manually.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;The main data point I needed to hook into for this was &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate#domain_validation_options&#34;&gt;&lt;code&gt;domain_validation_options&lt;/code&gt;&lt;/a&gt; attribute which is exported through &lt;code&gt;aws_acm_certificate&lt;/code&gt; resource for my certificate. Quoting from the documentation directly, this attribute gives you the domain validation objects which can be used to complete certificate validation. Note that this can have more than one value. So, wee need to keep that in mind when we are using this.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is great as we can use this value to create a Route53 record through the &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record&#34;&gt;&lt;code&gt;aws_route53_record&lt;/code&gt;&lt;/a&gt; resource. This object exports a few attributes for us:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;domain_name&lt;/code&gt;: The domain name to be validated.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;resource_record_name&lt;/code&gt;: The name of the DNS record to create to validate the certificate.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;resource_record_type&lt;/code&gt;: The type of DNS record to create, e.g. &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;CNAME&lt;/code&gt;, etc.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;resource_record_value&lt;/code&gt;: The value the DNS record needs to have.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;The only issue was to figure out how to iterate over &lt;code&gt;domain_validation_options&lt;/code&gt; array, and create a &lt;code&gt;aws_route53_record&lt;/code&gt; resource for each. Luckly, Terraform has a way to make this work through &lt;a href=&#34;https://www.terraform.io/docs/configuration/meta-arguments/for_each.html&#34;&gt;&lt;code&gt;for_each&lt;/code&gt;&lt;/a&gt; meta-argument, which allows us to create an instance for each item in that map or set.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Here is how my &lt;code&gt;aws_route53_record&lt;/code&gt; resource declaration looked like:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;resource &amp;quot;aws_route53_record&amp;quot; &amp;quot;tugberkugurlu_com_acm_validation&amp;quot; {&#xA;  for_each = {&#xA;    for dvo in aws_acm_certificate.tugberkugurlu_com.domain_validation_options : dvo.domain_name =&amp;gt; {&#xA;      name   = dvo.resource_record_name&#xA;      record = dvo.resource_record_value&#xA;      type   = dvo.resource_record_type&#xA;    }&#xA;  }&#xA;&#xA;  zone_id = aws_route53_zone.tugberkugurlu_com.zone_id&#xA;  name    = each.value.name&#xA;  type    = each.value.type&#xA;  ttl     = 60&#xA;  records = [&#xA;    each.value.record,&#xA;  ]&#xA;&#xA;  allow_overwrite = true&#xA;} &#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;zone_id&lt;/code&gt; here refers to the &lt;code&gt;zone_id&lt;/code&gt; attribute from an &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone&#34;&gt;&lt;code&gt;aws_route53_zone&lt;/code&gt;&lt;/a&gt; resource which I already had declared for this domain.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Another interesting bit here is the &lt;code&gt;allow_overwrite&lt;/code&gt; argument which is used to allow creation of this record in Terraform to overwrite an existing record, if any. It turns out that &lt;a href=&#34;https://stackoverflow.com/a/59745029/463785&#34;&gt;&lt;code&gt;domain_validation_options&lt;/code&gt; can result in duplicate DNS records&lt;/a&gt;, and &lt;a href=&#34;https://github.com/hashicorp/terraform-provider-aws/issues/13653#issuecomment-640237762&#34;&gt;this argument seems to have been added just for this purpose&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This should on its own be enough to get the validation performed. However, one caveat here is that the validation will happen asynchronously. Therefore, your certificate might be usable right away (a.k.a. the good old eventual consistency). Terraform already has a solution for this, too through the &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation&#34;&gt;&lt;code&gt;aws_acm_certificate_validation&lt;/code&gt;&lt;/a&gt; resource. This resource implements a part of the validation workflow and represents a successful validation of an ACM certificate by waiting for validation to complete. Note that this doesn&#39;t represent a real-world entity in AWS. So, changing or deleting this resource on its own has no immediate effect.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;As we already have the &lt;code&gt;aws_acm_certificate&lt;/code&gt; and &lt;code&gt;aws_route53_record&lt;/code&gt;(s) for the validation, we can easily declare an &lt;code&gt;aws_acm_certificate_validation&lt;/code&gt; resource:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;resource &amp;quot;aws_acm_certificate_validation&amp;quot; &amp;quot;tugberkugurlu_com&amp;quot; {&#xA;  certificate_arn         = aws_acm_certificate.tugberkugurlu_com.arn&#xA;  validation_record_fqdns = [for record in aws_route53_record.tugberkugurlu_com_acm_validation : record.fqdn]&#xA;} &#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;As there can be multiple &lt;code&gt;aws_route53_record.tugberkugurlu_com_acm_validation&lt;/code&gt; resources, we make use of the Terraform &lt;a href=&#34;https://www.terraform.io/docs/configuration/expressions/for.html&#34;&gt;&lt;code&gt;for&lt;/code&gt; expression&lt;/a&gt; to assign &lt;code&gt;validation_record_fqdns&lt;/code&gt; argument.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;These are all for what&#39;s needed for the certificate creation and its validation. Once I executed the &lt;a href=&#34;https://www.terraform.io/docs/commands/apply.html&#34;&gt;&lt;code&gt;terraform apply&lt;/code&gt;&lt;/a&gt; command, the certificate was created and it was all ready to use.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu-blog.s3.us-east-2.amazonaws.com/post-images/01ETDHHY9QJ765MSWM7MTENMF6-Screenshot-2020-12-25-at-18.00.50-resized-3.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_2&#34;&gt;Wiring It up with Application Load Balancer&lt;/h2&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;I am going to skip what AWS ALB is, how it works, and how to configure it to start directing traffic to your resources (e.g. ECS services, Lambda, EC2 instances, etc.). However, it&#39;s worth checking out &lt;a href=&#34;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html&#34;&gt;the ALB documentation&lt;/a&gt; before this post if you don&#39;t have a good grasp of its concepts.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;I already had created an Application Load Balancer under my account through Terraform, with a &lt;a href=&#34;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html&#34;&gt;Target Group&lt;/a&gt; wired to my &lt;a href=&#34;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html&#34;&gt;ECS Service&lt;/a&gt;. When you create an ALB, AWS assigns a domain name for you so that you can access the ALB publicly. HTTPS is enabled on this domain name, but it&#39;s highly likely that you want to hide this away by allowing access to your site through your own domain. When that&#39;s the case, the HTTPS certificate will stop working properly since the the ALB server could not prove that it is the domain that&#39;s being accessed through.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Therefore, we need a way to wire up our own certificate issued to our own domain with the ALB resource. AWS makes this super easy when the certificate is issued through ACM. What you need to do is to attach a listener to your load balancer through &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener&#34;&gt;&lt;code&gt;aws_lb_listener&lt;/code&gt;&lt;/a&gt; Terraform resource to listen on port &lt;code&gt;443&lt;/code&gt;. Then, you can also attach the SSL certificate we have on ACM. Here is how my configuration looked like for this:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;resource &amp;quot;aws_lb_listener&amp;quot; &amp;quot;tugberkugurlu_com_https_forward&amp;quot; {&#xA;  load_balancer_arn = aws_lb.tugberkugurlu_com.arn&#xA;  port              = 443&#xA;  protocol          = &amp;quot;HTTPS&amp;quot;&#xA;  ssl_policy        = &amp;quot;ELBSecurityPolicy-2016-08&amp;quot;&#xA;  certificate_arn   = aws_acm_certificate.tugberkugurlu_com.arn&#xA;&#xA;  default_action {&#xA;    type             = &amp;quot;forward&amp;quot;&#xA;    target_group_arn = aws_lb_target_group.tugberkugurlu_com.arn&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;It&#39;s pretty self explanatory, but there a few things that are worth touching on:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;load_balancer_arn&lt;/code&gt;: This points to the ALB resource ARN (&lt;a href=&#34;https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html&#34;&gt;Amazon Resource Name&lt;/a&gt;) which I had previously created through &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb&#34;&gt;&lt;code&gt;aws_lb&lt;/code&gt; resource&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;ssl_policy&lt;/code&gt;: You can see the &lt;a href=&#34;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html#describe-ssl-policies&#34;&gt;Security Policies section of the ALB documentation&lt;/a&gt;, which gives more information about this. To be frankly honest here, I didn&#39;t fully understand the full extend of this configuration, and just used what&#39;s recommended for compatibility. For what&#39;s worth, it doesn&#39;t seem to have a notable implication when I used the recommended value for what I was trying to achieve.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;certificate_arn&lt;/code&gt;: This points to the ACM certificate ARN which we have created previously with one of the steps above.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;default_action&lt;/code&gt;: Default action for the listener. In my case here, I want it to direct traffic to resources I configured with the target group.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;I&#39;m intentionally skipping the explanation around target groups here (e.g. how it&#39;s defined, and how it works, etc.) in this post since it would easily be in the size of a single blog post on its own post. Besides that, it&#39;s worth noting that the action type here doesn&#39;t have to be &lt;code&gt;forward&lt;/code&gt;, it can be any of the allowed &lt;a href=&#34;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html#rule-action-types&#34;&gt;rule action types&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;One other thing that I want to mention around target groups is that a target&#39;s protocol doesn&#39;t have to be the same as the listener protocol. Therefore, you can you use ALB for SSL termination here by configuring your HTTP endpoints within the target group with &lt;code&gt;HTTP&lt;/code&gt; protocol.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;⚠️ Don&#39;t forget to allow ingress traffic for TCP port &lt;code&gt;443&lt;/code&gt; through your security group for the ALB&#xA;once you add the HTTPS listener. Otherwise, the requests won&#39;t hit your ALB listener:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&#xA;&lt;code&gt;resource &#34;aws_security_group&#34; &#34;tugberkugurlu_com_lb&#34; {&#xA;  name        = &#34;lb-sg&#34;&#xA;  description = &#34;controls access to the Application Load Balancer (ALB)&#34;&#xA;&#xA;  # ...&#xA;&#xA;  ingress {&#xA;    protocol    = &#34;tcp&#34;&#xA;    from_port   = 443&#xA;    to_port     = 443&#xA;    cidr_blocks = [&#34;0.0.0.0/0&#34;]&#xA;  }&#xA;&#xA;  # ...&#xA;}&lt;/code&gt;&#xA;&lt;/pre&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;Once I applied this, I had the &lt;code&gt;HTTPS&lt;/code&gt; working for &lt;code&gt;tugberkugurlu.com&lt;/code&gt; 🎉&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;➜ curl -v https://www.tugberkugurlu.com&#xA;* Rebuilt URL to: https://www.tugberkugurlu.com/&#xA;*   Trying 3.139.131.63...&#xA;* TCP_NODELAY set&#xA;* Connected to www.tugberkugurlu.com (3.139.131.63) port 443 (#0)&#xA;* ALPN, offering h2&#xA;* ALPN, offering http/1.1&#xA;* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH&#xA;* successfully set certificate verify locations:&#xA;*   CAfile: /etc/ssl/cert.pem&#xA;  CApath: none&#xA;* TLSv1.2 (OUT), TLS handshake, Client hello (1):&#xA;* TLSv1.2 (IN), TLS handshake, Server hello (2):&#xA;* TLSv1.2 (IN), TLS handshake, Certificate (11):&#xA;* TLSv1.2 (IN), TLS handshake, Server key exchange (12):&#xA;* TLSv1.2 (IN), TLS handshake, Server finished (14):&#xA;* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):&#xA;* TLSv1.2 (OUT), TLS change cipher, Client hello (1):&#xA;* TLSv1.2 (OUT), TLS handshake, Finished (20):&#xA;* TLSv1.2 (IN), TLS change cipher, Client hello (1):&#xA;* TLSv1.2 (IN), TLS handshake, Finished (20):&#xA;* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256&#xA;* ALPN, server accepted to use h2&#xA;* Server certificate:&#xA;*  subject: CN=tugberkugurlu.com&#xA;*  start date: Dec 16 00:00:00 2020 GMT&#xA;*  expire date: Jan 14 23:59:59 2022 GMT&#xA;*  subjectAltName: host &amp;quot;www.tugberkugurlu.com&amp;quot; matched cert&#39;s &amp;quot;*.tugberkugurlu.com&amp;quot;&#xA;*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon&#xA;*  SSL certificate verify ok.&#xA;...&#xA;...&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3 id=&#34;toc_3&#34;&gt;Redirecting HTTP Traffic Through an ALB Rule&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;As I didn&#39;t previously have HTTPS on this web site, all the existing links out there (e.g. all of the pages were indexed on search engines) were pointing to &lt;code&gt;HTTP&lt;/code&gt; on port &lt;code&gt;80&lt;/code&gt;. So, I wanted to still be able to listen on port &lt;code&gt;80&lt;/code&gt;, and redirect traffic back to port &lt;code&gt;443&lt;/code&gt; to serve it over &lt;code&gt;HTTPS&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;ALB also makes this super easy, as you can wire up more than one listener onto your load balancer (&lt;a href=&#34;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-limits.html&#34;&gt;maximum 50 listeners are allowed per load balancer&lt;/a&gt;). So, we can do that for port &lt;code&gt;80&lt;/code&gt; on &lt;code&gt;HTTP&lt;/code&gt; protocol, and use the &lt;a href=&#34;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html#redirect-actions&#34;&gt;redirect action&lt;/a&gt; to direct traffic to port &lt;code&gt;443&lt;/code&gt; on &lt;code&gt;HTTPS&lt;/code&gt; protocol.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Here is how this looks like in Terraform:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;resource &amp;quot;aws_lb_listener&amp;quot; &amp;quot;tugberkugurlu_com_https_redirect&amp;quot; {&#xA;  load_balancer_arn = aws_lb.tugberkugurlu_com.arn&#xA;  port              = 80&#xA;  protocol          = &amp;quot;HTTP&amp;quot;&#xA;&#xA;  default_action {&#xA;    type = &amp;quot;redirect&amp;quot;&#xA;&#xA;    redirect {&#xA;      port        = &amp;quot;443&amp;quot;&#xA;      protocol    = &amp;quot;HTTPS&amp;quot;&#xA;      status_code = &amp;quot;HTTP_301&amp;quot;&#xA;    }&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;After I applied this change through &lt;code&gt;terraform apply&lt;/code&gt;, it all worked as expected:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;➜ curl -v http://www.tugberkugurlu.com &#xA;* Rebuilt URL to: http://www.tugberkugurlu.com/&#xA;*   Trying 3.14.215.140...&#xA;* TCP_NODELAY set&#xA;* Connected to www.tugberkugurlu.com (3.14.215.140) port 80 (#0)&#xA;&amp;gt; GET / HTTP/1.1&#xA;&amp;gt; Host: www.tugberkugurlu.com&#xA;&amp;gt; User-Agent: curl/7.54.0&#xA;&amp;gt; Accept: */*&#xA;&amp;gt; &#xA;&amp;lt; HTTP/1.1 301 Moved Permanently&#xA;&amp;lt; Server: awselb/2.0&#xA;&amp;lt; Date: Fri, 25 Dec 2020 22:17:03 GMT&#xA;&amp;lt; Content-Type: text/html&#xA;&amp;lt; Content-Length: 134&#xA;&amp;lt; Connection: keep-alive&#xA;&amp;lt; Location: https://www.tugberkugurlu.com:443/&#xA;&amp;lt; &#xA;&amp;lt;html&amp;gt;&#xA;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;301 Moved Permanently&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&#xA;&amp;lt;body&amp;gt;&#xA;&amp;lt;center&amp;gt;&amp;lt;h1&amp;gt;301 Moved Permanently&amp;lt;/h1&amp;gt;&amp;lt;/center&amp;gt;&#xA;&amp;lt;/body&amp;gt;&#xA;&amp;lt;/html&amp;gt;&#xA;* Connection #0 to host www.tugberkugurlu.com left intact&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;toc_4&#34;&gt;Resources&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://aws.amazon.com/certificate-manager/faqs&#34;&gt;ACM FAQ&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/acm/latest/userguide/acm-certificate.html&#34;&gt;ACM Certificate Characteristics&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html&#34;&gt;Requesting a Public Certificate&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-validate-dns.html&#34;&gt;Using DNS to Validate Domain Ownership&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html&#34;&gt;Managed Renewal for ACM Certificates&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/acm/latest/APIReference/API_RequestCertificate.html&#34;&gt;AWS ACM RequestCertificate API Reference&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://manicminer.io/posts/terraform-aws-acm-certificates-for-multiple-domains/&#34;&gt;Terraform: AWS ACM Certificates for Multiple Domains&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://hackernoon.com/getting-a-free-ssl-certificate-on-aws-a-how-to-guide-6ef29e576d22&#34;&gt;Getting a Free SSL Certificate on AWS a How-To Guide&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/configure-free-wildcard-ssl-certificate-on-aws-application-load-balancer-through-terraform</guid>
      <pubDate>Fri, 25 Dec 2020 22:10:00 +0000</pubDate>
    </item>
    <item>
      <title>Redis Cluster - Benefits of Sharding and How It Works</title>
      <link>https://www.tugberkugurlu.com/archive/redis-cluster-benefits-of-sharding-and-how-it-works</link>
      <description>&#xA;&lt;blockquote&gt;&#xA;&lt;h3&gt;Content&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#the-problem&#34;&gt;The Problem&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#redis-cluster-enter&#34;&gt;Redis Cluster: Enter&lt;/a&gt;&lt;/li&gt;&#xA;&lt;ul&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;#key-distribution&#34;&gt;Key Distribution&lt;/a&gt;&lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;#hash-tags&#34;&gt;Hash Tags: Getting back into control of your sharding strategy&lt;/a&gt;&lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;#redirection&#34;&gt;Redirection&lt;/a&gt;&lt;/li&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;#distributing-reads&#34;&gt;Distributing Reads&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#conclusion&#34;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;a href=&#34;https://redis.io/&#34;&gt;Redis&lt;/a&gt; is by far one of the most frequently used data stores. It&#39;s fascinating how much our our software-developer-minds go to Redis when we are faced with a data storage problem that requires some level of scale. Even if this might make us feel guilty, I have a somewhat confident assumption that this&#39;s the case, and there is probably a relation here to its simplicity: e.g. Redis is &#39;just&#39; a data structure server, a hash table in the &#39;cloud&#39;, etc. (I know I am a bit exaggerating here, but hopefully you get the idea). Redis also makes digestible and reasonable trade-offs, and it allows us to solve many problems which require certain degree of scale.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;For a long time, Redis has come with &lt;a href=&#34;https://redis.io/topics/replication&#34;&gt;an out-of-the-box replication functionality&lt;/a&gt;, which allows for a high availability (HA) setup as well as allowing us to scale the reads by distributing the load across replicas with the cost of eventual consistency. However, it was only in April, 2015 that Redis added support for a built-in &lt;a href=&#34;https://en.wikipedia.org/wiki/Shard_(database_architecture)&#34;&gt;sharding functionality&lt;/a&gt; with &lt;a href=&#34;https://raw.githubusercontent.com/antirez/redis/3.0/00-RELEASENOTES&#34;&gt;its version 3 release&lt;/a&gt;. I have been working with several Redis Cluster setups for a while, and have probably read the Redis Cluster spec at least couple of times. In this post, my aim is to give you more understanding on what problem Redis Cluster actually solves, why you need such a setup, and most important details you need to know about its configuration and implementation details based on my own experience.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;the-problem&#34;&gt;The Problem&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;When designing a software system, we have somewhat of an idea what the scale of usage is going to be on that system. This could be based off of previous usage patterns on the same or similar functionality, based on the data you collected over an experiment that has been run with a rudimentary functionality in a smaller scale, or based on just a pure guess. If you are mature enough as a business, you should also be able to project how much the expected growth is going to be for the forseeable future (e.g. next 12, 24 months). All of this data should help on determining a baseline number, where you can then be able to extrapolate to understand the load estimations for the system that you are designing.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Being a software engineer, I bet you also have the urge to boil these estimates down to peakiest number of writes/reads per second so that you can reason about these numbers in a relatable way, and can test your system accordingly before going to production. The ideal scenario is also that you want to be on the comfortable side, and will likely want to have 20% over scaling here in case your estimation turns out to be wrong.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;So far so good, and this is exactly what I would expect from a software engineer who knows what they are doing and have proper critical thinking skills. The reason is that these numbers will help you choose the shape and size of the resources you want to set up (e.g. the node size of your Elasticache Redis instance, etc.), which will help you optimize your resources. That said, we still have problems with this:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;These estimations are just estimations, and they will almost certainly turn out to be wrong. When they are higher than you expected, you will struggle with the load. When lower, you will likely burn money unnecessarily and will be overscaled more than you really like it to be.&lt;/li&gt;&#xA;&lt;li&gt;There will always (I actually mean &#39;always&#39; here) be unforeseen business activities or external events which will impact the load on your system (e.g. marketing campaigns, etc.). These activities may actually have dramatic impact on the per-second based load. In those circumstances, you need to find a way to accommodate the needs of the new load without actually having any downtime.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Why am I talking about these? These problems are actually what makes Redis Cluster as the suitable candidate for your needs when those problems are especially centered around the writes. For reads, you might still be able to get away with a single master setup by wiring up as many replicas as you need. This should allow you to distribute the read load across replicas at the cost of data consistency gap depending on the replication lag, which would take the pressure off from the master. When the load is lower and you don&#39;t need all the replica, you can tear those down to save some £££. All of these operations shouldn&#39;t really require too much logic on the clients, and you should really be able to get away with by only employing a logic to figure out a new Redis replica addition, and start directing requests to it.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, the matter is not that simple for writes. One option we have here is scaling up the nodes (i.e. adding more resources). However, that is going to be a complex operation to perform without introducing a downtime. There is also a limit to how much you can scale up to (although for the majority of use cases out there, you may never need to go close to that limit). This could still be an option when the issue is with memory. However, not so much for CPU. When it comes to Redis, your CPU is rarely the issue. It&#39;s throughput that ends up becoming the bottleneck.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;If we want to approach this problem the same way we have approached the read scaling issue, there are some questions that really deserve an upfront answer:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How the clients are going to know which node to write data into, and read data from?&lt;/li&gt;&#xA;&lt;li&gt;What will happen when we add a new node to scale the writes?&lt;/li&gt;&#xA;&lt;li&gt;What will happen when we remove a new node to scale down?&lt;/li&gt;&#xA;&lt;li&gt;How can we distribute the load evenly across the nodes?&lt;/li&gt;&#xA;&lt;li&gt;If we are making multi-command operations (e.g. pipeline requests, &lt;a href=&#34;https://redis.io/commands/mget&#34;&gt;&lt;code&gt;MGET&lt;/code&gt;&lt;/a&gt;, etc.), how are those going to work with this model?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Don&#39;t get me wrong here: these are not unique Redis problems. Any data storage system that needs to scale the writes face the same challenges, and there are some common techniques such as &lt;a href=&#34;https://en.wikipedia.org/wiki/Shard_(database_architecture)&#34;&gt;data sharding&lt;/a&gt;, and we are now about to see how Redis tackles these problems through the same technique, with some spice added on top to cater for its unique needs.&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;redis-cluster-enter&#34;&gt;Redis Cluster: Enter&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;Since v3.0, Redis has included an out of the box support for a data sharding solution, which is called &lt;a href=&#34;https://redis.io/topics/cluster-tutorial&#34;&gt;Redis Cluster&lt;/a&gt;. It provides a way to run a Redis installation where data is sharded across multiple Redis nodes as well as providing tools to manage the setup. These Redis nodes still have the same capabilities as a normal Redis node, and they can have their own replica sets. The only difference is that each node will be only holding the subset of your data, which will depend on the shape of the data and Redis&#39; key distribution model (don&#39;t worry about this now, we will get to this concept shortly).&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I have configured a local Redis cluster setup to use throughout this blog post, and with the help of &lt;a href=&#34;https://redis.io/commands/cluster-nodes&#34;&gt;&lt;code&gt;CLUSTER NODES&lt;/code&gt;&lt;/a&gt; command, I can see its high level structure:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; CLUSTER NODES&#xA;b7366bdbb09dbb20dcf0d4f8b7281c98f7e3b78e 172.19.197.7:6379@16379 master - 0 1608418117542 10 connected 10923-16383&#xA;164dc6aaf77aa0530490f0c9fbf5c8eb9f653a53 172.19.197.5:6379@16379 slave fdf56116c8b8f322561c7189574e6092101fa718 0 1608418118557 12 connected&#xA;f75939944d18ee12995c60d4cc9fcc1e53458d32 172.19.197.3:6379@16379 slave 88875e065f5ecf24b5adde973223a7799aee4521 0 1608418117949 11 connected&#xA;fdf56116c8b8f322561c7189574e6092101fa718 172.19.197.2:6379@16379 myself,master - 0 1608418118000 12 connected 0-5460&#xA;1c822510aa0f349a9b12cba1c68bc98feab5433e 172.19.197.4:6379@16379 slave b7366bdbb09dbb20dcf0d4f8b7281c98f7e3b78e 0 1608418118000 10 connected&#xA;88875e065f5ecf24b5adde973223a7799aee4521 172.19.197.6:6379@16379 master - 0 1608418118963 11 connected 5461-10922&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;You can learn more about the serialization format of this output from &lt;a href=&#34;https://redis.io/commands/cluster-nodes#serialization-format&#34;&gt;the doc&lt;/a&gt;, but let me take a stab at summarizing it:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;We have setup of 3 master nodes with each having one replica.&lt;/li&gt;&#xA;&lt;li&gt;We are currently connected to the node at &lt;code&gt;172.19.197.2:6379&lt;/code&gt;, and its node ID is &lt;code&gt;fdf56116c8b8f322561c7189574e6092101fa718&lt;/code&gt;. We know this is the node we are connected as the &lt;code&gt;myself&lt;/code&gt; flag indicates the the node you are contacted. This node is also one of the master nodes.&lt;/li&gt;&#xA;&lt;li&gt;The node that we are connected is shown to be responsible for &lt;code&gt;0&lt;/code&gt;-&lt;code&gt;5460&lt;/code&gt; slot range (don&#39;t worry about what exactly this is now, we will shortly get to this).&lt;/li&gt;&#xA;&lt;li&gt;The node at &lt;code&gt;172.19.197.5:6379&lt;/code&gt; is the replica of the current node which we are connected to. We know this as the node ID of &lt;code&gt;fdf56116c8b8f322561c7189574e6092101fa718&lt;/code&gt; is shown under the &lt;code&gt;master&lt;/code&gt; column and we know that this the ID of the node that we are connected to.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;At this point, you should have more questions in your head compared to when you have started reading this post, which is not good :) So, I am hoping to guess what those questions are and try answer at least some of them proactively.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;However, note that &lt;a href=&#34;https://redis.io/topics/cluster-spec&#34;&gt;Redis Cluster Specification&lt;/a&gt; already does a pretty good job on the details. With that in mind, my aim is not to duplicate that documentation here. That said, I want to still highlight the most impactful parts that are valuable to focus based on my own experience working with Redis cluster.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;key-distribution&#34;&gt;Key Distribution&lt;/h3&gt;&#xA;&lt;p&gt;&#xA;This section is all about essentially answering our first question above regarding which node holds which data. Redis has an interesting way of making this work which seemed to have worked for the use cases I have experienced with. Here is the very high level summary of how it works:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Redis assigns &#34;slot&#34; ranges for each master node within the cluster. These slots are also referred as &#34;hash slots&#34;&lt;/li&gt;&#xA;&lt;li&gt;These slots are between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;16384&lt;/code&gt;, which means each master node in a cluster handles a subset of the &lt;code&gt;16384&lt;/code&gt; hash slots.&lt;/li&gt;&#xA;&lt;li&gt;Redis clients can query which node is assigned to which slot range by using the &lt;a href=&#34;https://redis.io/commands/cluster-slots&#34;&gt;&lt;code&gt;CLUSTER SLOTS&lt;/code&gt;&lt;/a&gt; command. This gives clients a way to be able to directly talk to the correct node for the majority of cases.&lt;/li&gt;&#xA;&lt;li&gt;For a given Redis key, the hash slot for that key is the result of &lt;code&gt;CRC16(key)&lt;/code&gt; modulo &lt;code&gt;16384&lt;/code&gt;, where &lt;code&gt;CRC16&lt;/code&gt; here is the implementation of the CRC16 hash function. I am no expect when it comes to cryptography and hashing, but &lt;a href=&#34;https://play.golang.org/p/mEmbtCibk_o&#34;&gt;here&lt;/a&gt; is how this can be done in Go by using the &lt;a href=&#34;https://github.com/snksoft/crc&#34;&gt;snksoft/crc&lt;/a&gt; library. Note that Redis also has a handy command called &lt;a href=&#34;https://redis.io/commands/cluster-keyslot&#34;&gt;&lt;code&gt;CLUSTER KEYSLOT&lt;/code&gt;&lt;/a&gt; which performs this operation for you per given Redis key. &lt;a href=&#34;https://redis.io/topics/cluster-spec#clients-first-connection-and-handling-of-redirections&#34;&gt;The clients are expected to embed this logic&lt;/a&gt; so that they can directly communicate with the correct node with the help of &lt;code&gt;CLUSTER SLOTS&lt;/code&gt; command mentioned above.&lt;/li&gt;&#xA;&lt;li&gt;Same as the single node Redis setup, Redis Cluster uses asynchronous replication between nodes. So, each shard can have its own set of replicas which would be responsible for the same subset of the hash slots as its master. These replicas can be used for failover scenarios as well as distributing the read load (which we will touch on later).&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;For example, if you have a setup of 3 master nodes with each having 3 replicas, it would look something like the following:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;img src=&#34;https://tugberkugurlu-blog.s3.us-east-2.amazonaws.com/post-images/01ESSD5TSBTEAK416T46X6MHF0-redis-cluster-1.jpg&#34; alt=&#34;redis cluster slot assignment&#34; /&gt;&#xA;&#xA;&lt;p&gt;&#xA;The specific ranges of the hash slots doesn&#39;t matter here too much, even the fact that they might be balanced fairly (as we will touch later, we can have influence over slot allocation if we need to). What matters is that it&#39;s clear which master node owns.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;As an example, I have a local Redis cluster setup which has 3 master nodes, and I am connected to one of them (&lt;code&gt;172.19.197.2&lt;/code&gt;) through redis-cli. When I run the &lt;code&gt;CLUSTER SLOTS&lt;/code&gt; command, I can see that the node I am connected to handles hash slot range between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;5460&lt;/code&gt;:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; CLUSTER SLOTS&#xA;...&#xA;...&#xA;2) 1) (integer) 0&#xA;   2) (integer) 5460&#xA;   3) 1) &#34;172.19.197.2&#34;&#xA;      2) (integer) 6379&#xA;      3) &#34;fdf56116c8b8f322561c7189574e6092101fa718&#34;&#xA;   4) 1) &#34;172.19.197.5&#34;&#xA;      2) (integer) 6379&#xA;      3) &#34;164dc6aaf77aa0530490f0c9fbf5c8eb9f653a53&#34;&#xA;...&#xA;...&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;I want to set 4 keys, which I already know that falls into the slot range of this node:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.7&#xA;(integer) 717&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.6&#xA;(integer) 4844&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.2&#xA;(integer) 4712&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.3&#xA;(integer) 585&#xA;172.19.197.2:6379&gt; SET coffee_shop_branch.status.7 PERMANENTLY-CLOSED&#xA;OK&#xA;172.19.197.2:6379&gt; SET coffee_shop_branch.status.6 PERMANENTLY-CLOSED&#xA;OK&#xA;172.19.197.2:6379&gt; SET coffee_shop_branch.status.2 OPEN&#xA;OK&#xA;172.19.197.2:6379&gt; SET coffee_shop_branch.status.3 CLOSED&#xA;OK&#xA;172.19.197.2:6379&gt; KEYS *&#xA;1) &#34;coffee_shop_branch.status.7&#34;&#xA;2) &#34;coffee_shop_branch.status.6&#34;&#xA;3) &#34;coffee_shop_branch.status.2&#34;&#xA;4) &#34;coffee_shop_branch.status.3&#34;&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;I can also successfully read these the same way I would have done with a single node Redis setup: &#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; GET coffee_shop_branch.status.7&#xA;&#34;PERMANENTLY-CLOSED&#34;&#xA;172.19.197.2:6379&gt; GET coffee_shop_branch.status.6&#xA;&#34;PERMANENTLY-CLOSED&#34;&#xA;172.19.197.2:6379&gt; GET coffee_shop_branch.status.2&#xA;&#34;OPEN&#34;&#xA;172.19.197.2:6379&gt; GET coffee_shop_branch.status.3&#xA;&#34;CLOSED&#34;&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;hash-tags&#34;&gt;Hash Tags: Getting back into control of your sharding strategy&lt;/h3&gt;&#xA;&lt;p&gt;&#xA;In certain cases, we would like to influence which node our data is stored at. This is to be able to group certain keys together so that we can later access them together through a multi-key operation, or through &lt;a href=&#34;https://redis.io/topics/pipelining&#34;&gt;pipelining&lt;/a&gt;.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;One use case here would be to satisfy the access pattern of retrieving the status of multiple coffee shops within the same city, where we don&#39;t have a way to group these together during write time. Therefore, it makes sense to write the status of each coffee shop under their individual keys, and access the ones that we care about through pipelining, or &lt;code&gt;MGET&lt;/code&gt;.&lt;/p&gt; &#xA;&#xA;&lt;blockquote&gt;&#xA;⚠️ I am mentioning &lt;code&gt;MGET&lt;/code&gt; as an option here as it is technically a viable option. However, keep in mind that &lt;a href=&#34;https://stackoverflow.com/a/61532233/463785&#34;&gt;&lt;code&gt;MGET&lt;/code&gt; blocks other clients&lt;/a&gt; till the whole read operation completes, whereas pipelining doesn&#39;t since it&#39;s just a way of batching commands. Although you may not see the difference with just a few keys, it&#39;s not a good idea to use &lt;code&gt;MGET&lt;/code&gt; for too many keys. I suggest for you to perform your own benchmarks for your own use case to see what the threshold might be here.&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;Idea is solid but there is still a question: how can we make sure that coffee shops under the same city are co-located within the same node? For example, if we also have the coffee shops with ID &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;4&lt;/code&gt;, they are not going to be stored within the same node as coffee shops with ID &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;3&lt;/code&gt;, &lt;code&gt;6&lt;/code&gt; and &lt;code&gt;7&lt;/code&gt; based on our current setup (remember: the node at &lt;code&gt;172.19.197.2&lt;/code&gt; is responsible for hash slot range of &lt;code&gt;0&lt;/code&gt;-&lt;code&gt;5460&lt;/code&gt;):&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.1&#xA;(integer) 8715&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.4&#xA;(integer) 12974&#xA;&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.2&#xA;(integer) 4712&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.3&#xA;(integer) 585&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.6&#xA;(integer) 4844&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.status.7&#xA;(integer) 717&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;You can also see that Redis will also complain when we try to &lt;code&gt;MGET&lt;/code&gt; all of these keys:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; MGET coffee_shop_branch.status.1 coffee_shop_branch.status.2 coffee_shop_branch.status.3 coffee_shop_branch.status.4 coffee_shop_branch.status.6 coffee_shop_branch.status.7&#xA;(error) CROSSSLOT Keys in request don&#39;t hash to the same slot&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;We can also see the same behavior even if we remove &lt;code&gt;coffee_shop_branch.status.1&lt;/code&gt; and &lt;code&gt;coffee_shop_branch.status.4&lt;/code&gt; from the list of keys. This is because the fact that &lt;code&gt;MGET&lt;/code&gt; can only succeed if all of the keys belong to same slot as the error message suggests.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; MGET coffee_shop_branch.status.2 coffee_shop_branch.status.3 coffee_shop_branch.status.6 coffee_shop_branch.status.7&#xA;(error) CROSSSLOT Keys in request don&#39;t hash to the same slot&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;This is where the concept of &lt;a href=&#34;https://redis.io/topics/cluster-spec#keys-hash-tags&#34;&gt;hash tags&lt;/a&gt; comes in. Hash tags allow us to force certain keys to be stored in the same hash slot. I encourage you the read the linked section of the spec to understand better how hash tags work as I am going to skip some corner cases here, but in a nutshell, the concept is really simple from the usage point of view: when the Redis key contains &lt;code&gt;&#34;{...}&#34;&lt;/code&gt; pattern only the substring between &lt;code&gt;{&lt;/code&gt; and &lt;code&gt;}&lt;/code&gt; is hashed in order to obtain the hash slot.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;For our use case, this means that we can change our key structure from &lt;code&gt;coffee_shop_branch.status.COFFEE-SHOP-ID&lt;/code&gt; to something like &lt;code&gt;coffee_shop_branch.{city_CITY-ID}.status.COFFEE-SHOP-ID&lt;/code&gt;. The exact shape of the key is not important here. What&#39;s important is that the value between curly braces which is the city ID prefixed with &lt;code&gt;city_&lt;/code&gt; for readability purposes.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;For the example that we have been working with, and with the assumption that the coffee shops with ID &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;4&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;3&lt;/code&gt;, &lt;code&gt;6&lt;/code&gt; and &lt;code&gt;7&lt;/code&gt; are all with the same city, let&#39;s say that it&#39;s the city with ID &lt;code&gt;4&lt;/code&gt;, the keys will shape up as following, and we can see from the &lt;code&gt;CLUSTER KEYSLOT&lt;/code&gt; command outcome that all of these keys are hashed to the same slot:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.{city_4}.status.1&#xA;(integer) 1555&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.{city_4}.status.4&#xA;(integer) 1555&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.{city_4}.status.2&#xA;(integer) 1555&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.{city_4}.status.3&#xA;(integer) 1555&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.{city_4}.status.6&#xA;(integer) 1555&#xA;172.19.197.2:6379&gt; CLUSTER KEYSLOT coffee_shop_branch.{city_4}.status.7&#xA;(integer) 1555&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;We can also see that &lt;code&gt;MGET&lt;/code&gt; will start working as expected with these keys:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; MGET coffee_shop_branch.{city_4}.status.1 coffee_shop_branch.{city_4}.status.4 coffee_shop_branch.{city_4}.status.2 coffee_shop_branch.{city_4}.status.3 coffee_shop_branch.{city_4}.status.6 coffee_shop_branch.{city_4}.status.7&#xA;1) &#34;OPEN&#34;&#xA;2) &#34;CLOSED&#34;&#xA;3) &#34;OPEN&#34;&#xA;4) &#34;CLOSED&#34;&#xA;5) &#34;PERMANENTLY-CLOSED&#34;&#xA;6) &#34;PERMANENTLY-CLOSED&#34;&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;So, hash tags are great, and we should use them all the time, right? Not so fast! This approach can make a notable positive impact on the latency of your application, and resource utilization of your redis nodes. However, there is a drawback here which might be a big worry for you depending on your load and data distribution: the Hot Shard problem (a.k.a. Hot Key problem). In our use case for instance, this can be a significant problem when certain cities hold way more coffee shops than the others, or the access for certain cities are significantly higher even if the data sizes are the same. I will leave &lt;a href=&#34;http://highscalability.com/blog/2010/10/15/troubles-with-sharding-what-can-we-learn-from-the-foursquare.html&#34;&gt;this super informative post&lt;/a&gt; from 2010 here, which is about one of the Foursquare outages. You will quickly realise after reading &lt;a href=&#34;https://web.archive.org/web/20131114075609/http://blog.foursquare.com/2010/10/05/so-that-was-a-bummer/&#34;&gt;the post-mortem&lt;/a&gt; that it was caused by the exact same problem.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Hash tags is a tool that can help you, but there is unfortunately no magic bullet here. You need to understand your use case, data distribution, and test different setups to understand what might work for you the best.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;redirection&#34;&gt;Redirection&lt;/h3&gt;&#xA;&lt;p&gt;Apart from the &lt;code&gt;MGET&lt;/code&gt; example above, we have been playing it by the rules so far: knowingly issuing commands against the nodes that actually hold the data for the given keys. We were able to do this through the couple of cluster commands that Redis provides such as &lt;code&gt;CLUSTER SLOTS&lt;/code&gt; and &lt;code&gt;CLUSTER KEYSLOT&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;What would happen if we do the opposite though: issuing a command against a Redis node which doesn&#39;t actually own the hash slot for the given key? Here is the answer:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.2:6379&gt; get coffee_shop_branch.status.1&#xA;(error) MOVED 8715 172.19.197.6:6379&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Redis is erroring, but erroring in a more clever way than you probably have guessed. The error itself includes the hash slot of the key, and the ip:port of the instance that owns that hash slot and can serve the query. This is called &lt;a href=&#34;https://redis.io/topics/cluster-spec#moved-redirection&#34;&gt;MOVED redirection&lt;/a&gt; in Redis spec, and all the Redis Cluster clients are expected to handle this error appropriately so that they can eventually succeed the request by connecting to the correct node and issuing the command there.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;https://redis.io/topics/rediscli&#34;&gt;redis-cli&lt;/a&gt;, as being one of the Redis clients, also knows how to handle &lt;code&gt;MOVED&lt;/code&gt; redirection. The CLI utility implements basic cluster support when started with the &lt;code&gt;-c&lt;/code&gt; switch.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;➜ docker run -it --rm \&#xA;    --net redis-cluster_redis_cluster_network \&#xA;    redis \&#xA;    redis-cli -h redis_1&#xA;redis_1:6379&gt; get coffee_shop_branch.status.1&#xA;(error) MOVED 8715 172.19.197.6:6379&#xA;redis_1:6379&gt; exit&#xA;&#xA;➜ docker run -it --rm \&#xA;    --net redis-cluster_redis_cluster_network \&#xA;    redis \&#xA;    redis-cli -c -h redis_1&#xA;redis_1:6379&gt; get coffee_shop_branch.status.1&#xA;-&gt; Redirected to slot [8715] located at 172.19.197.6:6379&#xA;&#34;OPEN&#34;&#xA;172.19.197.6:6379&gt; &#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;You can see that on the first case when we connected to a Redis node through redis-cli without the &lt;code&gt;-c&lt;/code&gt; switch, we got the &lt;code&gt;MOVED&lt;/code&gt; redirection. However, in the case where we used the &lt;code&gt;-c&lt;/code&gt; switch, the client handled the redirection transparently by connecting to the given Redis node, and issuing the command there.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;However, Redis already gives a way to identify which master node is responsible for which hash slot range, and Redis cluster clients should also be able to generate the hash of a given key to figure out which node to connect to. So, why is this feature useful? There are two main key reasons that I am aware of:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;First one is that Redis cluster specification doesn&#39;t require Redis Clsuter clients to be clever about routing, meaning that clients don&#39;t need to keep track of which master nodes serve for which hash slot range. Instead, they can just have the logic to be able to handle the redirection to be considered a complete Redis Cluster client. I don&#39;t exactly know what the reason was for this, but I presume this made it easier for existing Redis clients to adopt to be a Redis Cluster client at the time. That said, these clients have a major drawback that they are so much inefficient compared to their clever counterparts since these clients have a high change of making at least twice the number of requests than they need to for the majority of the operations they perform.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Another reason why we have the &lt;code&gt;MOVED&lt;/code&gt; redirection in place (probably the most important one) is related to &lt;a href=&#34;https://redis.io/topics/cluster-spec#cluster-live-reconfiguration&#34;&gt;resharding&lt;/a&gt;. For instance, when a new master node is added to the Redis Cluster to offload some of the pressure from the existing nodes, it&#39;s expected to perform some of the cluster reconfiguration operations to move certain hash slot ranges from the existing nodes to the new node. This would trigger a what-is-commonly-known-as resharding operation, and Redis aims to handle this without causing a disruption. However, when this happens and certain hash slot ranges are being moved from one node to another, there is a chance that the client can have the stale information about the cluster during this phase. This might cause the client to connect to the old node which used to be responsible for a given hash slot, instead of the correct node which took charge of that slot after the client retrieved the latest state of the cluster. This is where the &lt;code&gt;MOVED&lt;/code&gt; redirection is handy, and it also hints to the client to reload its cluster configuration.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I am aware that we haven&#39;t touched on the resharding point in depth yet (and we won&#39;t be in this post), but redirection is such a fundamental concept of the Redis Cluster specification that I wanted briefly to go over at a high level. Also note that there is another type of redirection which is known as &lt;a href=&#34;https://redis.io/topics/cluster-spec#ask-redirection&#34;&gt;ASK redirection&lt;/a&gt;, and we won&#39;t be covering that here at all since it&#39;s fundamentally related to resharding and that one really deserves its own post.&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;distributing-reads&#34;&gt;Distributing Reads&lt;/h3&gt;&#xA;&lt;p&gt;&#xA;The last point I want to touch on is around scaling reads, where we can make use of the replicas to distribute the load. For example, with the setup that we have been working with in this post, we have a replica per each master node. Considering we have 3 master nodes, by default, 3 nodes are serving reads and writes. However, we can utilize the replicas to serve the read commands which would essentially double the number of nodes that can serve reads.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;This is great but it&#39;s at the cost of data consistency since Redis uses by default asynchronous replication unless you are using the &lt;a href=&#34;https://redis.io/commands/wait&#34;&gt;WAIT&lt;/a&gt; command to enforce a synchronous replication during write time.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Let&#39;s assume that we are OK with the data inconsistency, and we are monitoring the replication lag. How can we utilize these replicas for reads? We can start by exploring this through redis-cli. From &lt;a href=&#34;#redis-cluster-enter&#34;&gt;our previous exploration&lt;/a&gt;, we know that the node at &lt;code&gt;172.19.197.5:6379&lt;/code&gt; is the replica of the node at &lt;code&gt;172.19.197.2:6379&lt;/code&gt;. So, let&#39;s connect to that node directly, and issue a &lt;code&gt;GET&lt;/code&gt; command there:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;➜ docker run -it --rm \&#xA;    --net redis-cluster_redis_cluster_network \&#xA;    redis \&#xA;    redis-cli -c -h 172.19.197.5&#xA;172.19.197.5:6379&gt; get coffee_shop_branch.{city_4}.status.4&#xA;-&gt; Redirected to slot [1555] located at 172.19.197.2:6379&#xA;&#34;CLOSED&#34;&#xA;172.19.197.2:6379&gt; &#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;That&#39;s a surprising outcome as we were being redirected to the node at &lt;code&gt;172.19.197.2:6379&lt;/code&gt; which is the master node of the replica that we were connected to. From this, it seems like the replica either doesn&#39;t hold the data that we need, or it doesn&#39;t allow any read operations.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Let&#39;s first check whether it actually holds the data. Looking at the &lt;a href=&#34;https://redis.io/commands/keys&#34;&gt;KEYS&lt;/a&gt; stored at that node, it seems like it has the data that we need:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.5:6379&gt; KEYS *&#xA; 1) &#34;coffee_shop_branch.status.3&#34;&#xA; 2) &#34;coffee_shop_branch.status.6&#34;&#xA; 3) &#34;coffee_shop_branch.status.7&#34;&#xA; 4) &#34;coffee_shop_branch.{city_4}.status.2&#34;&#xA; 5) &#34;coffee_shop_branch.{city_4}.status.4&#34;&#xA; 6) &#34;coffee_shop_branch.status.2&#34;&#xA; 7) &#34;coffee_shop_branch.{city_4}.status.7&#34;&#xA; 8) &#34;coffee_shop_branch.{city_4}.status.3&#34;&#xA; 9) &#34;coffee_shop_branch.{city_4}.status.6&#34;&#xA;10) &#34;coffee_shop_branch.{city_4}.status.1&#34;&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When we check the replica status, we can also see that the replica is up-to-date:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;172.19.197.5:6379&gt; INFO replication&#xA;# Replication&#xA;role:slave&#xA;master_host:172.19.197.2&#xA;master_port:6379&#xA;master_link_status:up&#xA;master_last_io_seconds_ago:8&#xA;master_sync_in_progress:0&#xA;...&#xA;...&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It seems like the replica doesn&#39;t allow us to perform any read operations, and this is expected which is also documented inside the &lt;a href=&#34;https://redis.io/topics/cluster-spec#scaling-reads-using-slave-nodes&#34;&gt;Redis Cluster spec&lt;/a&gt;:&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;Normally slave nodes will redirect clients to the authoritative master for the hash slot involved in a given command, however clients can use slaves in order to scale reads using the &lt;code&gt;READONLY&lt;/code&gt; command.&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;a href=&#34;https://redis.io/commands/readonly&#34;&gt;&lt;code&gt;READONLY&lt;/code&gt;&lt;/a&gt; command enables read queries for a connection to a Redis Cluster replica node. This command hints to the server that the client is OK with the potential data inconsistency. This command needs to be sent per each connection to the replica nodes and ideally should be sent right after the connection is established. &#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;➜ docker run -it --rm \&#xA;    --net redis-cluster_redis_cluster_network \&#xA;    redis \&#xA;    redis-cli -c -h 172.19.197.5&#xA;172.19.197.5:6379&gt; READONLY&#xA;OK&#xA;172.19.197.5:6379&gt; get coffee_shop_branch.{city_4}.status.4&#xA;&#34;CLOSED&#34;&#xA;172.19.197.5:6379&gt; &#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;To be honest, I remember that this threw me off when I first realized this behavior. However, it makes sort of a sense to be explicit when it comes to reading stale data. My only gripe about it is the name of the command which is sort of confusing. That said, you get used to it after a while, and it&#39;s well supported by the clients (e.g. &lt;a href=&#34;https://github.com/go-redis&#34;&gt;go-redis&lt;/a&gt; client has a way for you to &lt;a href=&#34;https://github.com/go-redis/redis/blob/143859e34596a8e80ee858b5842d503d86572249/cluster.go#L38-L45&#34;&gt;configure this as well as being able to configure the replica routing behavior&lt;/a&gt;).&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&#xA;Redis cluster gives us the ability to scale our Redis setup horizontally not just for reads but also for writes, and you should consider it especially if you have a write heavy workload where you cannot easily predict the demand ahead of time. The sharding model Redis is offering us is also very interesting where it has the mix of both client and server level logic on where your data is, and how to find it. This gives us an easy way to get started with a rudimentary sharding setup as well as allowing us to optimize our system further by making our clients a bit more clever.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;I am aware that there are still further unknowns in terms of how to actually initialize a Redis cluster setup from scratch, details of how clients interact with a Redis cluster setup, how maintenance/operational side of the cluster setup actually works (e.g. resharding), etc. However, this post is already too long (there you go, my excuse!), and I hope to cover those in the upcoming posts one by one. If you have any specific areas that you are wondering about Redis Cluster, drop a comment below and I will try to cover them if I have any experience around those areas. &#xA;&lt;/p&gt;&#xA;&#xA;&lt;h2 href=&#34;#resources&#34;&gt;Resources&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://redis.io/topics/cluster-spec&#34;&gt;Redis Cluster Specification&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://redis.io/topics/cluster-tutorial&#34;&gt;Redis cluster tutorial&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://lmgtfy.app/?q=site%3Aredis.io%2Fcommands%2Fcluster-&#34;&gt;Redis cluster commands&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/scaling-redis-cluster-mode-enabled.html&#34;&gt;Elasticache: Scaling Clusters in Redis (Cluster Mode Enabled)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://redis.io/topics/pipelining&#34;&gt;Using pipelining to speedup Redis queries&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/Replication.Redis-RedisCluster.html&#34;&gt;Elasticashe: Replication: Redis (Cluster Mode Disabled) vs. Redis (Cluster Mode Enabled)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://highscalability.com/blog/2010/10/15/troubles-with-sharding-what-can-we-learn-from-the-foursquare.html&#34;&gt;Troubles With Sharding - What Can We Learn From The Foursquare Incident?&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/redis-cluster-benefits-of-sharding-and-how-it-works</guid>
      <pubDate>Sun, 20 Dec 2020 01:34:00 +0000</pubDate>
    </item>
    <item>
      <title>Working with Slices in Go (Golang) - Understanding How append, copy and Slice Expressions Work</title>
      <link>https://www.tugberkugurlu.com/archive/working-with-slices-in-go-golang-understanding-how-append-copy-and-slicing-syntax-work</link>
      <description>&#xA;&lt;blockquote&gt;&#xA;&lt;h3&gt;Content&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#introduction&#34;&gt;Introduction&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#how-slices-work&#34;&gt;How Slices Work&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#how-append-and-copy-works&#34;&gt;How append and copy Works&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#how-slice-expressions-work&#34;&gt;How Slice Expressions Work&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#modifying-a-sliced-slice-modifies-the-original-slice&#34;&gt;⚠️ Modifying a Sliced-slice Modifies the Original Slice&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;#calling-append-on-a-sliced-slice-may-modify-the-original-slice&#34;&gt;⚠️ Calling append on a Sliced-slice May Modify the Original Slice&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;h3 href=&#34;#introduction&#34;&gt;Introduction&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;Go programming language has two fundamental types at the language level to enable working with numbered sequence of elements: &lt;a href=&#34;https://golang.org/ref/spec#Array_types&#34;&gt;array&lt;/a&gt; and &lt;a href=&#34;https://golang.org/ref/spec#Slice_types&#34;&gt;slice&lt;/a&gt;. At the syntax level, they may look like the same but they are fundamentally very different in terms of their behavior. The most critical fundamental differences are:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Size of the array is fixed and determined at the construction (as you may expect). However, slices can dynamically grow in size (you may wonder how? We will touch on this soon, be patient!)&lt;/li&gt;&#xA;&lt;li&gt;An array with a specific length is a distinct type based on its length (check &lt;a href=&#34;https://play.golang.org/p/gmF99PSNhiX&#34;&gt;this&lt;/a&gt; out). Whereas the slice can be represented as one type (e.g. &lt;code&gt;[]int&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;The in-memory representation of an array type is values laid out sequentially. A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (we will shortly see what this actually means).&lt;/li&gt;&#xA;&lt;li&gt;Go&#39;s arrays are values, which means that the entire content of the array will be copied when you start passing it around. Slices, on the other hand, a pointer to the underlying array along with the length of the segment. So,  when we started passing around a slice, it creates a new slice value that points to the original array, which will be much cheaper to pass around.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;Above points highlight some characteristics of slices and how they differ from arrays, but these are mostly differences in terms of how they are structured. More interesting and unobvious differences of slices are around their behaviors around manipulations.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;how-slices-work&#34;&gt;How Slices Work&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;To be able to understand how slices works, we first need to have a good understanding of how arrays work in Go, and you can check out &lt;a href=&#34;https://blog.golang.org/slices#TOC_2.&#34;&gt;this&lt;/a&gt; informative description to gain more understanding on arrays than the above summary.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;a href=&#34;https://blog.golang.org/slices#TOC_3.&#34;&gt;Slices&lt;/a&gt; are the constructs in Go which give us the flexibility to work with dynamically sized collections. A slice is an abstraction of an array, and it points to a contiguous section of an array stored separately from the slice variable itself. Internally, a slice is a descriptor which holds the following values:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;pointer to the backing array (actually, pointer to the array value which indicates 0th index of the slice, which we will cover later)&lt;/li&gt;&#xA;&lt;li&gt;the length of the segment it&#39;s referring to&lt;/li&gt;&#xA;&lt;li&gt;its capacity (the maximum length of the segment)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;There are various ways how you can define a slice in Go, and all of the following ways leads to the same outcome: a slice with a zero length and capacity&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;var a []int &#xA;&#x9;b := []int{}&#xA;&#x9;c := make([]int, 0)&#xA;&#x9;fmt.Printf(&#34;a: %v, len %d, cap: %d\n&#34;, a, len(a), cap(a))&#xA;&#x9;fmt.Printf(&#34;b: %v, len %d, cap: %d\n&#34;, b, len(b), cap(b))&#xA;&#x9;fmt.Printf(&#34;c: %v, len %d, cap: %d\n&#34;, c, len(c), cap(c))&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;a: [], len 0, cap: 0&#xA;b: [], len 0, cap: 0&#xA;c: [], len 0, cap: 0&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;You can also initialize a slice with seed values, and the length of the values here will also be the capacity of the backing array:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;a := []int{1,2,3}&#xA;&#x9;fmt.Printf(&#34;a: %v, len %d, cap: %d\n&#34;, a, len(a), cap(a))&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;a: [1 2 3], len 3, cap: 3&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;In case you know the maximum capacity that a slice can grow to, it&#39;s best to initialize the slice by hinting the capacity so that you don&#39;t have to grow the backing array as you add new values to slice (which we will see how to in the next section). You can do so by passing it to the &lt;a href=&#34;https://golang.org/pkg/builtin/#make&#34;&gt;&lt;code&gt;make&lt;/code&gt; builtin function&lt;/a&gt;.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;a := make([]int, 0, 10)&#xA;&#x9;fmt.Printf(&#34;a: %v, len %d, cap: %d\n&#34;, a, len(a), cap(a))&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;a: [], len 0, cap: 10&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;This still doesn&#39;t mean that you can access the backing array freely by index, as the length is still &lt;code&gt;0&lt;/code&gt;. If you attempt to do so, you will get &#34;index out of range&#34; runtime error.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;how-append-and-copy-works&#34;&gt;How append and copy Works&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;When we want to add a new value to an existing slice which will mean growing its length, we can use &lt;a href=&#34;https://golang.org/pkg/builtin/#append&#34;&gt;append&lt;/a&gt;, which is a built-in and &lt;a href=&#34;https://golang.org/ref/spec#Function_types&#34;&gt;variadic&lt;/a&gt; function. This function appends elements to the end of a slice, and returns the updated slice.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/YvzmKIIfY0U --&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;var result []int&#xA;&#x9;for i := 0; i &lt; 10; i++ {&#xA;&#x9;&#x9;if i % 2 == 0 {&#xA;&#x9;&#x9;&#x9;result = append(result, i)&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#x9;fmt.Println(result)&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;As you may expect, this prints &lt;code&gt;[0 2 4 6 8]&lt;/code&gt; to the console as a result. However, it&#39;s not clear here what exactly is happening underneath as a result of invocation of the &lt;code&gt;append&lt;/code&gt; function, and what the time complexity of the call is. When we run the below code, things will be a bit more clear to us:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/uQQFBbJWhAS --&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;reflect&#34;&#xA;&#x9;&#34;unsafe&#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;var result []int&#xA;&#x9;for i := 0; i &lt; 10; i++ {&#xA;&#x9;&#x9;if i % 2 == 0 {&#xA;&#x9;&#x9;&#x9;fmt.Printf(&#34;appending &#39;%d&#39;: %s\n&#34;, i, getSliceHeader(&amp;result))&#xA;&#x9;&#x9;&#x9;result = append(result, i)&#xA;&#x9;&#x9;&#x9;fmt.Printf(&#34;appended &#39;%d&#39;:  %s\n&#34;, i, getSliceHeader(&amp;result))&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#x9;fmt.Println(result)&#xA;}&#xA;&#xA;// https://stackoverflow.com/a/54196005/463785&#xA;func getSliceHeader(slice *[]int) string {&#xA;&#x9;sh := (*reflect.SliceHeader)(unsafe.Pointer(slice))&#xA;&#x9;return fmt.Sprintf(&#34;%+v&#34;, sh)&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;appending &#39;0&#39;: &amp;{Data:0 Len:0 Cap:0}&#xA;appended &#39;0&#39;:  &amp;{Data:824633901184 Len:1 Cap:1}&#xA;appending &#39;2&#39;: &amp;{Data:824633901184 Len:1 Cap:1}&#xA;appended &#39;2&#39;:  &amp;{Data:824633901296 Len:2 Cap:2}&#xA;appending &#39;4&#39;: &amp;{Data:824633901296 Len:2 Cap:2}&#xA;appended &#39;4&#39;:  &amp;{Data:824633803136 Len:3 Cap:4}&#xA;appending &#39;6&#39;: &amp;{Data:824633803136 Len:3 Cap:4}&#xA;appended &#39;6&#39;:  &amp;{Data:824633803136 Len:4 Cap:4}&#xA;appending &#39;8&#39;: &amp;{Data:824633803136 Len:4 Cap:4}&#xA;appended &#39;8&#39;:  &amp;{Data:824634228800 Len:5 Cap:8}&#xA;[0 2 4 6 8]&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;We can extract the following facts from this result:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;nil&lt;/code&gt; slice starts off with empty capacity, nothing surprising with that&lt;/li&gt;&#xA;&lt;li&gt;The capacity of the slice doubles while attempting to append a new item when its capacity and length are equal&lt;/li&gt;&#xA;&lt;li&gt;When the capacity is doubled, we can also observe that the pointer to the backing array (i.e. the &lt;code&gt;Data&lt;/code&gt; field value of &lt;code&gt;reflect.SliceHeader&lt;/code&gt; struct) changes&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;In summary, it&#39;s a fair to assume from these facts that the content of the backing array of the slice is copied into a new array which has double capacity than the itself when it&#39;s being attempted to append a new item to it while its capacity is full. It should go without saying that the implementation is a bit more complicated than this as you may expect, and &lt;a href=&#34;https://medium.com/vendasta/golang-the-time-complexity-of-append-2177dcfb6bad&#34;&gt;this post&lt;/a&gt; from &lt;a href=&#34;https://medium.com/@glucn&#34;&gt;Gary Lu&lt;/a&gt; does a good job on explaining the implementation details in more details. You can also check out the &lt;a href=&#34;https://github.com/golang/go/blob/b3ef90ec7304a28b89f616ced20b09f56be30cc4/src/runtime/slice.go#L125-L240&#34;&gt;growSlice&lt;/a&gt; function which is used by the compiler generated code to grow the capacity of the slice when needed.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;In a nutshell, this is not a good news to us since we are doing too much more work than it&#39;s worth. In these cases, initializing the array with the make built-in function is a far better option with a capacity hint based on the max capacity that the slice can grow to:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/rAE7KWP9yfk --&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;reflect&#34;&#xA;&#x9;&#34;unsafe&#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;maxValue := 10&#xA;&#x9;result := make([]int, 0, maxValue)&#xA;&#x9;for i := 0; i &lt; maxValue; i++ {&#xA;&#x9;&#x9;if i % 2 == 0 {&#xA;&#x9;&#x9;&#x9;fmt.Printf(&#34;appending &#39;%d&#39;: %s\n&#34;, i, getSliceHeader(&amp;result))&#xA;&#x9;&#x9;&#x9;result = append(result, i)&#xA;&#x9;&#x9;&#x9;fmt.Printf(&#34;appended &#39;%d&#39;:  %s\n&#34;, i, getSliceHeader(&amp;result))&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#x9;fmt.Println(result)&#xA;}&#xA;&#xA;// https://stackoverflow.com/a/54196005/463785&#xA;func getSliceHeader(slice *[]int) string {&#xA;&#x9;sh := (*reflect.SliceHeader)(unsafe.Pointer(slice))&#xA;&#x9;return fmt.Sprintf(&#34;%+v&#34;, sh)&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;appending &#39;0&#39;: &amp;{Data:824633794640 Len:0 Cap:10}&#xA;appended &#39;0&#39;:  &amp;{Data:824633794640 Len:1 Cap:10}&#xA;appending &#39;2&#39;: &amp;{Data:824633794640 Len:1 Cap:10}&#xA;appended &#39;2&#39;:  &amp;{Data:824633794640 Len:2 Cap:10}&#xA;appending &#39;4&#39;: &amp;{Data:824633794640 Len:2 Cap:10}&#xA;appended &#39;4&#39;:  &amp;{Data:824633794640 Len:3 Cap:10}&#xA;appending &#39;6&#39;: &amp;{Data:824633794640 Len:3 Cap:10}&#xA;appended &#39;6&#39;:  &amp;{Data:824633794640 Len:4 Cap:10}&#xA;appending &#39;8&#39;: &amp;{Data:824633794640 Len:4 Cap:10}&#xA;appended &#39;8&#39;:  &amp;{Data:824633794640 Len:5 Cap:10}&#xA;[0 2 4 6 8]&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;We can observe from the result here that we have been operating over the same backing array with the size of 10, which means that all the append operations have run in &lt;code&gt;O(1)&lt;/code&gt; time.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;There is also another built-in function which makes it easy to transfer values from one slice to another: &lt;a href=&#34;https://golang.org/pkg/builtin/#copy&#34;&gt;&lt;code&gt;copy&lt;/code&gt;&lt;/a&gt;. I will quote the definition of &lt;code&gt;copy&lt;/code&gt; straight from the &lt;a href=&#34;https://golang.org/ref/spec#Appending_and_copying_slices&#34;&gt;Go spec&lt;/a&gt;:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&#34;The function copy copies slice elements from a source &lt;code&gt;src&lt;/code&gt; to a destination &lt;code&gt;dst&lt;/code&gt; and returns the number of elements copied. Both arguments must have identical element type &lt;code&gt;T&lt;/code&gt; and must be assignable to a slice of type &lt;code&gt;[]T&lt;/code&gt;. The number of elements copied is the minimum of &lt;code&gt;len(src)&lt;/code&gt; and &lt;code&gt;len(dst)&lt;/code&gt;.&#34;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&#xA;It&#39;s probably obvious, but still worth mentioning that &lt;code&gt;copy&lt;/code&gt; runs in &lt;code&gt;O(N)&lt;/code&gt; time, where &lt;code&gt;N&lt;/code&gt; is the number of elements it can copy. The following example demonstrates &lt;code&gt;copy&lt;/code&gt; function in action:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;a := make([]int, 5, 6)&#xA;&#x9;b := []int{1, 2, 3, 4, 5}&#xA;&#x9;fmt.Println(copy(a, b))&#xA;&#x9;fmt.Printf(&#34;a: %v, cap: %d\n&#34;, a, cap(a))&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;5&#xA;a: [1 2 3 4 5], cap: 6&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;how-slice-expressions-work&#34;&gt;How Slice Expressions Work&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;a href=&#34;https://golang.org/ref/spec#Slice_expressions&#34;&gt;Slice expressions&lt;/a&gt; construct a substring or slice from a string, array, pointer to array, or slice (e.g. &lt;code&gt;a[1:5]&lt;/code&gt;). The result has indices starting at &lt;code&gt;0&lt;/code&gt; and length equal to &lt;code&gt;high&lt;/code&gt; - &lt;code&gt;low&lt;/code&gt;. This is great, as it gives us an easy way to perform slicing operations on the original value, and this is being performed super efficiently. The reason for this is that slicing does not result in copying the slice&#39;s data. It creates &lt;a href=&#34;https://blog.golang.org/slices-intro#TOC_4.&#34;&gt;a new slice value (i.e. &lt;code&gt;reflect.SliceHeader&lt;/code&gt;) that points to the original array (it&#39;s actually the pointer to the first element of the new slice)&lt;/a&gt;:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/sliced-slice.png&#34; alt=&#34;Go slice header&#34; /&gt;&#xA;&#xA;&lt;p&gt;&#xA;The following example should be able to demonstrate this behavior for us:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;reflect&#34;&#xA;&#x9;&#34;unsafe&#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;maxValue := 10&#xA;&#x9;result := make([]int, 0, maxValue)&#xA;&#x9;for i := 0; i &lt; maxValue; i++ {&#xA;&#x9;&#x9;if i % 2 == 0 {&#xA;&#x9;&#x9;&#x9;result = append(result, i)&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#x9;for i := range result {&#xA;&#x9;&#x9;fmt.Printf(&#34;%d: %v\n&#34;, i, &amp;result[i])&#xA;&#x9;}&#xA;&#x9;newSlice := result[1:3]&#xA;&#x9;newSlice2 := result[2:4]&#xA;&#x9;fmt.Printf(&#34;[:]: %s\n&#34;, getSliceHeader(&amp;result))&#xA;&#x9;fmt.Printf(&#34;[1:3]: %s\n&#34;, getSliceHeader(&amp;newSlice))&#xA;&#x9;fmt.Printf(&#34;[2:4]: %s\n&#34;, getSliceHeader(&amp;newSlice2))&#xA;}&#xA;&#xA;func getSliceHeader(slice *[]int) string {&#xA;&#x9;sh := (*reflect.SliceHeader)(unsafe.Pointer(slice))&#xA;&#x9;return fmt.Sprintf(&#34;%+v&#34;, sh)&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Let&#39;s unpack what we are doing here:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;we are creating a slice&lt;/li&gt;&#xA;&lt;li&gt;filling it with data and printing the hexadecimal representation of a memory address of each value in the slice (so that we can compare these later)&lt;/li&gt;&#xA;&lt;li&gt;slicing it twice, and assigning the new slices to separate variables&lt;/li&gt;&#xA;&lt;li&gt;inspecting the header values of each slice&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;The outcome is as below (you can also run it &lt;a href=&#34;https://play.golang.org/p/QiL326lJTgF&#34;&gt;here&lt;/a&gt;):&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;0: 0xc000012050&#xA;1: 0xc000012058&#xA;2: 0xc000012060&#xA;3: 0xc000012068&#xA;4: 0xc000012070&#xA;[:]: &amp;{Data:824633794640 Len:5 Cap:10}&#xA;[1:3]: &amp;{Data:824633794648 Len:2 Cap:9}&#xA;[2:4]: &amp;{Data:824633794656 Len:2 Cap:8}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;As a result, we are seeing that &lt;code&gt;[1:3]&lt;/code&gt; slice has the length 2 (which is expected). What&#39;s interesting is the capacity which is &lt;code&gt;9&lt;/code&gt;. The reason for that is that the capacity assigned to the sliced-slice is influenced by the starting point of the new slice (i.e. &lt;code&gt;low&lt;/code&gt;) and the capacity of the original slice &lt;code&gt;cap&lt;/code&gt;, and calculated as &lt;code&gt;cap - low&lt;/code&gt;, and the rest of the capacity is referring to the same sequential dedicated memory addresses of the backing array. We will see in the next sections what the implications of this behavior can be.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The other interesting thing we are seeing here is that the pointer to the backing array has changed. This is the result of the memory representation of the array. An array is stored as a sequence of n blocks of the type specified. So, the pointer here is actually pointing to the 1st index value of the original array, which we can confirm by comparing the hexadecimal representation of a memory address of each value in the original slice: &lt;code&gt;[1:3]&lt;/code&gt; slice is pointing to &lt;code&gt;824633794648&lt;/code&gt; and 1st indexed value in the original slice is pointing to &lt;code&gt;0xc000012058&lt;/code&gt; which is the hexadecimal value of &lt;code&gt;824633794648&lt;/code&gt;.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The similar story is there for the &lt;code&gt;[2:4]&lt;/code&gt; sliced-slice, too. What we can confirm from this is that slicing is super efficient with the cost of sharing the backing array with the original slice.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;modifying-a-sliced-slice-modifies-the-original-slice&#34;&gt;⚠️ Modifying a Sliced-slice Modifies the Original Slice&lt;/h3&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/aCoF1zJf18y --&gt;&#xA;&#xA;&lt;p&gt;&#xA;By looking at the internals of how slicing works, we have seen that the new slice, which is returned by slicing an existing slice, is still referring to the same backing array as the original slice. This introduces a very interesting implication that modifying data on the indices of the newly sliced-slice also causes the same modification on the original slice, which can actually cause very hard to track down bugs, and the following code snippet is showing how this can happen:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;a := []int{1, 2, 3, 4, 5}&#xA;&#x9;b := a[2:4]&#xA;&#x9;b[0] = 10&#xA;&#x9;fmt.Println(b)&#xA;&#x9;fmt.Println(a)&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;[10 4]&#xA;[1 2 10 4 5]&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/AZR2Qbucmpy --&gt;&#xA;&#xA;&lt;p&gt;&#xA;In this given example, the issue may already be apparent to us. However, this unobvious behavior of how slicing works underneath (to be fair, for the right performance reasons) can make some issues more obfuscated when the slicing and modification is done in different places. For instance, with the following example (which you can also see &lt;a href=&#34;https://play.golang.org/p/2OUIE7s69mi&#34;&gt;here&lt;/a&gt;), we can see that &lt;code&gt;Result&lt;/code&gt; method on the &lt;code&gt;race&lt;/code&gt; instance is not returning the expected result anymore due to the modifications done to the slice returned by the &lt;code&gt;Top10Finishers&lt;/code&gt; method, because &lt;code&gt;sort.Strings&lt;/code&gt; call modified the array which is actually backing the both slices.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;play.ground/race&#34;&#xA;&#x9;&#34;sort&#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;belgium2020Race := race.New(&#34;Belgian&#34;, []string{&#xA;&#x9;&#x9;&#34;Hamilton&#34;, &#34;Bottas&#34;, &#34;Verstappen&#34;, &#34;Ricciardo&#34;, &#34;Ocon&#34;,&#xA;&#x9;&#x9;&#34;Albon&#34;, &#34;Norris&#34;, &#34;Gasly&#34;, &#34;Stroll&#34;, &#34;Perez&#34;,&#xA;&#x9;&#x9;&#34;Kvyat&#34;, &#34;Räikkönen&#34;, &#34;Vettel&#34;, &#34;Leclerc&#34;, &#34;Grosjean&#34;,&#xA;&#x9;&#x9;&#34;Latifi&#34;, &#34;Magnussen&#34;, &#34;Giovinazzi&#34;, &#34;Russell&#34;, &#34;Sainz&#34;,&#xA;&#x9;})&#xA;&#x9;top10Finishers := belgium2020Race.Top10Finishers()&#xA;&#x9;sort.Strings(top10Finishers)&#xA;&#x9;fmt.Printf(&#34;%s GP top 10 finishers, in alphabetical order: %v\n&#34;, belgium2020Race.Name(), top10Finishers)&#xA;&#x9;fmt.Printf(&#34;%s GP result: %v\n&#34;, belgium2020Race.Name(), belgium2020Race.Result())&#xA;}&#xA;&#xA;-- go.mod --&#xA;module play.ground&#xA;&#xA;-- race/race.go --&#xA;package race&#xA;&#xA;type race struct {&#xA;&#x9;name   string&#xA;&#x9;result []string&#xA;}&#xA;&#xA;func (r race) Name() string {&#xA;&#x9;return r.name&#xA;}&#xA;&#xA;func (r race) Result() []string {&#xA;&#x9;return r.result&#xA;}&#xA;&#xA;func (r race) Top10Finishers() []string {&#xA;&#x9;return r.result[:10]&#xA;}&#xA;&#xA;func New(name string, result []string) race {&#xA;&#x9;return race{&#xA;&#x9;&#x9;name:   name,&#xA;&#x9;&#x9;result: result,&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;Belgian GP top 10 finishers: [Hamilton Bottas Verstappen Ricciardo Ocon Albon Norris Gasly Stroll Perez]&#xA;Belgian GP top 10 finishers, in alphabetical order: [Albon Bottas Gasly Hamilton Norris Ocon Perez Ricciardo Stroll Verstappen]&#xA;Belgian GP result: [Albon Bottas Gasly Hamilton Norris Ocon Perez Ricciardo Stroll Verstappen Kvyat Räikkönen Vettel Leclerc Grosjean Latifi Magnussen Giovinazzi Russell Sainz]&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;There is no one-size-fits-all solution to the the problem here. It will really depend on your usage, and what type of contract you are exposing from your defined type. If you are after creating a domain model encapsulation where you don&#39;t want to allow unmodified access to the state of that model, you can instead make a copy of the slice that you want to return, with the cost of extra time and space complexity you are introducing. The following code shows the only modification we would have done to the above example to make this work:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;func (r race) Top10Finishers() []string {&#xA;&#x9;top10 := r.result[:10]&#xA;&#x9;result := make([]string, len(top10))&#xA;&#x9;copy(result, top10)&#xA;&#x9;return result&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;a href=&#34;https://play.golang.org/p/mwWdeuvx20n&#34;&gt;When we execute this version of the implementation&lt;/a&gt;, we can now see that the &lt;code&gt;sort.Strings&lt;/code&gt; call is not implicitly modifying the original slice:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;Belgian GP top 10 finishers: [Hamilton Bottas Verstappen Ricciardo Ocon Albon Norris Gasly Stroll Perez]&#xA;Belgian GP top 10 finishers, in alphabetical order: [Albon Bottas Gasly Hamilton Norris Ocon Perez Ricciardo Stroll Verstappen]&#xA;Belgian GP result: [Hamilton Bottas Verstappen Ricciardo Ocon Albon Norris Gasly Stroll Perez Kvyat Räikkönen Vettel Leclerc Grosjean Latifi Magnussen Giovinazzi Russell Sainz]&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Another option here is to expose a read-only version of the data, which you can achieve by encapsulating the slice behind an interface. This would only allow certain read-only operations, and make it more obvious to the consumer of the package what&#39;s the cost of the operation is:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;type ReadOnlyStringCollection interface {&#xA;&#x9;Each(f func(i int, value string))&#xA;&#x9;Len() int&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;This forces your consumer to iterate over the data first before attempting to manipulate it, which is positive from the point of establishing a much more clear contract from your package. The following is showing how you can implement this inside the &lt;code&gt;race&lt;/code&gt; package:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;-- race/race.go --&#xA;package race&#xA;&#xA;type race struct {&#xA;&#x9;name   string&#xA;&#x9;result []string&#xA;}&#xA;&#xA;func (r race) Name() string {&#xA;&#x9;return r.name&#xA;}&#xA;&#xA;func (r race) Result() []string {&#xA;&#x9;return r.result&#xA;}&#xA;&#xA;func (r race) Top10Finishers() ReadOnlyStringCollection {&#xA;&#x9;return readOnlyStringCollection{r.result[:10]}&#xA;}&#xA;&#xA;func New(name string, result []string) race {&#xA;&#x9;return race{&#xA;&#x9;&#x9;name:   name,&#xA;&#x9;&#x9;result: result,&#xA;&#x9;}&#xA;}&#xA;&#xA;type readOnlyStringCollection struct {&#xA;&#x9;value []string&#xA;}&#xA;&#xA;func (r readOnlyStringCollection) Each(f func(i int, value string)) {&#xA;&#x9;for i, v := range r.value {&#xA;&#x9;&#x9;f(i, v)&#xA;&#x9;}&#xA;}&#xA;&#xA;func (r readOnlyStringCollection) Len() int {&#xA;&#x9;return len(r.value)&#xA;}&#xA;&#xA;type ReadOnlyStringCollection interface {&#xA;&#x9;Each(f func(i int, value string))&#xA;&#x9;Len() int&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The following is how you can now make use of it:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;belgium2020Race := race.New(&#34;Belgian&#34;, []string{&#xA;&#x9;&#x9;&#34;Hamilton&#34;, &#34;Bottas&#34;, &#34;Verstappen&#34;, &#34;Ricciardo&#34;, &#34;Ocon&#34;,&#xA;&#x9;&#x9;&#34;Albon&#34;, &#34;Norris&#34;, &#34;Gasly&#34;, &#34;Stroll&#34;, &#34;Perez&#34;,&#xA;&#x9;&#x9;&#34;Kvyat&#34;, &#34;Räikkönen&#34;, &#34;Vettel&#34;, &#34;Leclerc&#34;, &#34;Grosjean&#34;,&#xA;&#x9;&#x9;&#34;Latifi&#34;, &#34;Magnussen&#34;, &#34;Giovinazzi&#34;, &#34;Russell&#34;, &#34;Sainz&#34;,&#xA;&#x9;})&#xA;&#x9;top10Finishers := func() []string {&#xA;&#x9;&#x9;result := make([]string, 10)&#xA;&#x9;&#x9;top10 := belgium2020Race.Top10Finishers()&#xA;&#x9;&#x9;top10.Each(func(i int, val string) {&#xA;&#x9;&#x9;&#x9;result[i] = val&#xA;&#x9;&#x9;})&#xA;&#x9;&#x9;return result&#xA;&#x9;}()&#xA;&#x9;fmt.Printf(&#34;%s GP top 10 finishers: %v\n&#34;, belgium2020Race.Name(), top10Finishers)&#x9;&#xA;&#x9;sort.Strings(top10Finishers)&#xA;&#x9;fmt.Printf(&#34;%s GP top 10 finishers, in alphabetical order: %v\n&#34;, belgium2020Race.Name(), top10Finishers)&#xA;&#x9;fmt.Printf(&#34;%s GP result: %v\n&#34;, belgium2020Race.Name(), belgium2020Race.Result())&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;a href=&#34;https://play.golang.org/p/B_FAa42_vur&#34;&gt;When we execute this version of the implementation&lt;/a&gt;, we can now see that the &lt;code&gt;sort.Strings&lt;/code&gt; call is not implicitly modifying the original slice in this case, too:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;Belgian GP top 10 finishers: [Hamilton Bottas Verstappen Ricciardo Ocon Albon Norris Gasly Stroll Perez]&#xA;Belgian GP top 10 finishers, in alphabetical order: [Albon Bottas Gasly Hamilton Norris Ocon Perez Ricciardo Stroll Verstappen]&#xA;Belgian GP result: [Hamilton Bottas Verstappen Ricciardo Ocon Albon Norris Gasly Stroll Perez Kvyat Räikkönen Vettel Leclerc Grosjean Latifi Magnussen Giovinazzi Russell Sainz]&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;calling-append-on-a-sliced-slice-may-modify-the-original-slice&#34;&gt;⚠️ Calling append on a Sliced Slice May Modify the Original Slice&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;We have previously went over &lt;a href=&#34;#how-append-and-copy-works&#34;&gt;how &lt;code&gt;append&lt;/code&gt; function in Go works&lt;/a&gt; by appending elements to the end of a slice, and returning the updated slice. This can sort of give you the impression that the append function is a pure function, and doesn&#39;t modify your state. However, we have seen from how append works that it may not be the case. If we combine this with the fact that the new slice, which is returned by slicing an existing slice, is still referring to the same backing array as the original slice, following example demonstrates a behavior which is a bit more unobvious than &lt;a href=&#34;#modifying-a-sliced-slice-modifies-the-original-slice&#34;&gt;the above one&lt;/a&gt;:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/iDnB1ZrG4lj --&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;a := []int{1, 2, 3, 4, 5}&#xA;&#x9;b := a[2:4]&#xA;&#x9;fmt.Printf(&#34;a: %v, cap: %d\n&#34;, a, cap(a))&#xA;&#x9;fmt.Printf(&#34;b: %v, cap: %d\n&#34;, b, cap(b))&#xA;&#x9;&#xA;&#x9;b = append(b, 20)&#xA;&#x9;fmt.Printf(&#34;b: %v, cap: %d\n&#34;, b, cap(b))&#xA;&#x9;fmt.Printf(&#34;a: %v, cap: %d\n&#34;, a, cap(a))&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;a: [1 2 3 4 5], cap: 5&#xA;b: [3 4], cap: 3&#xA;b: [3 4 20], cap: 3&#xA;a: [1 2 3 4 20], cap: 5&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;In this example, we have a slice assigned to variable &lt;code&gt;a&lt;/code&gt;, and we slice this array to assign a new slice to variable &lt;code&gt;b&lt;/code&gt;. We print the results. We see that our sliced-slice has a length of &lt;code&gt;2&lt;/code&gt;, and capacity of &lt;code&gt;3&lt;/code&gt;. All good, expected. However, interesting behavior kicks in when we attempt to append a value to slice &lt;code&gt;b&lt;/code&gt;. The append works as expected, and the slice &lt;code&gt;b&lt;/code&gt; has the new appended value. Besides that, we can see from the result of our code that slice &lt;code&gt;a&lt;/code&gt; has also been modified, and the original value on the 4th index (i.e. &lt;code&gt;5&lt;/code&gt;) is now replaced with &lt;code&gt;20&lt;/code&gt; which was the value appended to slice &lt;code&gt;b&lt;/code&gt;.&#xA;&lt;/p&gt; &#xA;&#xA;&lt;p&gt;&#xA;The reason for this behavior is exactly reasons we have talked about before. The remaining capacity of the sliced-slice is still also being used by the original slice. To be frankly honest, it would be unfair to call this an unexpected behavior since it&#39;s documented in a detailed fashion. Nevertheless, this would be fair to classify this as an entirely unobvious behavior especially if you are new to the language, and expect the code to highlight a bit more of its behavior.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/g3nfRo8kXll --&gt;&#xA;&#xA;&lt;p&gt;The only time you wouldn&#39;t have the same behavior here is where the sliced-slice&#39;s capacity was already full right after slicing:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;a := []int{1, 2, 3, 4, 5}&#xA;&#x9;b := a[2:]&#xA;&#x9;fmt.Printf(&#34;a: %v, cap: %d\n&#34;, a, cap(a))&#xA;&#x9;fmt.Printf(&#34;b: %v, cap: %d\n&#34;, b, cap(b))&#xA;&#x9;&#xA;&#x9;b = append(b, 20)&#xA;&#x9;fmt.Printf(&#34;b: %v, cap: %d\n&#34;, b, cap(b))&#xA;&#x9;fmt.Printf(&#34;a: %v, cap: %d\n&#34;, a, cap(a))&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;a: [1 2 3 4 5], cap: 5&#xA;b: [3 4 5], cap: 3&#xA;b: [3 4 5 20], cap: 6&#xA;a: [1 2 3 4 5], cap: 5&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;In this example, we can see that the capacity and length of slice &lt;code&gt;b&lt;/code&gt; was &lt;code&gt;3&lt;/code&gt;, and calling &lt;code&gt;append&lt;/code&gt; on slice &lt;code&gt;b&lt;/code&gt; triggered the grow logic which meant that the values had to be copied to a new array with capacity &lt;code&gt;6&lt;/code&gt; before attempting to append the new value. This eventually meant that original slice was not impacted by the modification.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;This behavior is also the case other way around. For instance, take the following example:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/sRh6RpAek65 --&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;)&#xA;&#xA;func main() {&#xA;&#x9;a := make([]int, 5, 6)&#xA;&#x9;copy(a, []int{1, 2, 3, 4, 5})&#xA;&#x9;fmt.Printf(&#34;a: %v, cap: %d\n&#34;, a, cap(a))&#xA;&#xA;&#x9;b := a[3:]&#xA;&#x9;fmt.Printf(&#34;b: %v, cap: %d\n&#34;, b, cap(b))&#xA;&#x9;b = append(b, 10)&#xA;&#x9;fmt.Printf(&#34;b: %v, cap: %d\n&#34;, b, cap(b))&#xA;&#xA;&#x9;a = append(a, 20)&#xA;&#x9;fmt.Printf(&#34;a: %v, cap: %d\n&#34;, a, cap(a))&#xA;&#x9;fmt.Printf(&#34;b: %v, cap: %d\n&#34;, b, cap(b))&#xA;}&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;pre&gt;&#xA;a: [1 2 3 4 5], cap: 6&#xA;b: [4 5], cap: 3&#xA;b: [4 5 10], cap: 3&#xA;a: [1 2 3 4 5 20], cap: 6&#xA;b: [4 5 20], cap: 3&#xA;&lt;/pre&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;In this case, appending the value &lt;code&gt;20&lt;/code&gt; to slice &lt;code&gt;a&lt;/code&gt; causes the 2nd-indexed value of slice &lt;code&gt;b&lt;/code&gt;.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;!-- https://play.golang.org/p/5RaMtP0u-wF --&gt;&#xA;&#xA;&lt;h3&gt;Conclusion&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;Slice type in Go is a powerful construct, giving us flexibility over Go&#39;s array type with as minimum performance hit as possible. This flexibility introduced with least performance impact comes with some additional cost of being implicit on the implications of modifications performed on slices, and these implications can be significant depending on the use case while also being very hard to track down. This post sheds some light on some of these, but I encourage you to spend time on understand how slices really works in depth before making use of them in anger. Besides this post, following resources should also help you&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://blog.golang.org/slices-intro&#34;&gt;Go Slices: usage and internals&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://blog.golang.org/slices&#34;&gt;Arrays, slices (and strings): The mechanics of &#39;append&#39;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://tour.golang.org/moretypes/7&#34;&gt;A Tour of Go: Slices&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://medium.com/@marty.stepien/arrays-vs-slices-bonanza-in-golang-fa8d32cd2b7c&#34;&gt;Arrays vs. slices bonanza&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.sohamkamani.com/golang/arrays-vs-slices/&#34;&gt;A Comprehensive Guide of Arrays and Slices in Golang (and their differences)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.callicoder.com/golang-pointers/&#34;&gt;Playing with Pointers in Golang&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/working-with-slices-in-go-golang-understanding-how-append-copy-and-slicing-syntax-work</guid>
      <pubDate>Sat, 12 Sep 2020 16:55:00 +0000</pubDate>
    </item>
    <item>
      <title>Implementing OrderedMap in Go 2.0 by Using Generics with Delete Operation in O(1) Time Complexity</title>
      <link>https://www.tugberkugurlu.com/archive/implementing-ordered-map-in-go-2-0-by-using-generics-with-delete-operation-in-o-1-time-complexity</link>
      <description>&#xA;&lt;p&gt;&#xA;Probably the most sought after feature of Go programming language, Generics, is on its way and is expected to land with v2. You can check out the proposal &lt;a href=&#34;https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md&#34;&gt;here&lt;/a&gt;, and have a play with it in &lt;a href=&#34;https://go2goplay.golang.org/&#34;&gt;Go playground for v2&lt;/a&gt;. I stumbled upon &lt;a href=&#34;https://medium.com/@rocketlaunchr.cloud&#34;&gt;rocketlaunchr.cloud&lt;/a&gt;&#39;s great &lt;a href=&#34;https://medium.com/swlh/ordered-maps-for-go-using-generics-875ef3816c71&#34;&gt;post on using generics in Go 2&lt;/a&gt;, and the post shows how you can implement ordered maps. The post is very informative, and shows you how powerful Generics will be for Go.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;However, I noticed a performance issues with the implementation of &lt;code&gt;Delete&lt;/code&gt; operation on the &lt;code&gt;OrderedMap&lt;/code&gt; struct, and in this post, I want to show a much better implementation in terms of time complexity with a Doubly Linked List data structure, and show the impact of the change.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;The Problem&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;To summarize the current approach in rocketlaunchr.cloud&#39;s post, it essentially exposes the below signature: &#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;type OrderedMap[type K comparable, V any] struct {&#xA;&#x9;store map[K]V&#xA;&#x9;keys  []K&#xA;}&#xA;&#xA;func (o *OrderedMap[K, V]) Get(key K) (V, bool) {&#xA;    // ...&#xA;}&#xA;&#xA;func (o *OrderedMap[K, V]) Set(key K, val V) {&#xA;    // ...&#xA;}&#xA;&#xA;func (o *OrderedMap[K, V]) Delete(key K) {&#xA;    // ...&#xA;}&#xA;&#xA;func (o *OrderedMap[K, V]) Iterator() func() (*int, *K, V) {&#xA;    // ...&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The implementation also gives the FIFO guarantee through the iterator and maintaining the order of the list even after the delete operation (e.g. if the map has &lt;code&gt;1,2,3,4&lt;/code&gt;, and &lt;code&gt;3&lt;/code&gt; is then deleted, the iterator will output the data with the following order &lt;code&gt;1,2,4&lt;/code&gt;.).&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The performance problem with the implementation is with the &lt;code&gt;Delete&lt;/code&gt; method, which has the below implementation:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;func (o *OrderedMap[K, V]) Delete(key K) {&#xA;&#x9;delete(o.store, key)&#xA;&#xA;&#x9;// Find key in slice&#xA;&#x9;var idx *int&#xA;&#xA;&#x9;for i, val := range o.keys {&#xA;&#x9;&#x9;if val == key {&#xA;&#x9;&#x9;&#x9;idx = &amp;[]int{i}[0]&#xA;&#x9;&#x9;&#x9;break&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#x9;if idx != nil {&#xA;&#x9;&#x9;o.keys = append(o.keys[:*idx], o.keys[*idx+1:]...)&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The implementation here is iterating over the entire keys slice to perform the delete operation, which has &lt;code&gt;O(N)&lt;/code&gt; time complexity and this can be a significant performance hit where there is a need to store large collections while making use of the Delete operation frequently. We can also observe that we are performing a shift in the keys slice as well, with the below operation&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;if idx != nil {&#xA;    o.keys = append(o.keys[:*idx], o.keys[*idx+1:]...)&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;code&gt;o.keys[:*idx]&lt;/code&gt;, &lt;code&gt;o.keys[*idx+1:]&lt;/code&gt; and &lt;code&gt;append&lt;/code&gt; all here have its own time complexity, and &lt;a href=&#34;https://stackoverflow.com/questions/15702419/append-complexity&#34;&gt;depending on how much the backing array needs to grow&lt;/a&gt;, the complexity of this operation can grow. That said, I have to admit that the language features here make it hard to reason about the exact time complexity of each operation here.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;In the post, I realized that rocketlaunchr.cloud actually calls for an action for readers to address this known issue with the below quote for this :)&#xA;&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&#xA;Currently, when you delete a key-value pair, you need to iterate over the keys slice to find the index of the key you want to delete. You can use another map that associates the key to the index in the keys slice. I’ll leave it to the reader to implement.&#xA;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&#xA;Thinking about this now and it won&#39;t actually be enough for us to store the index as it will require us to perform either shift the slice where the keys are stored, or keep the storage for deleted keys and skip them during iteration. Both has its own disadvantages where the first option will have time complexity hit per each delete operation, whereas the second one will both increase the time complexity of iterator as well as increasing the space complexity.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Doubly Linked List Data Structure to Rescue&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;It&#39;s actually possible to reduce the time complexity of the &lt;code&gt;Delete&lt;/code&gt; operation to &lt;code&gt;O(1)&lt;/code&gt; by changing the way how we store the data within the implementation of &lt;code&gt;OrderedMap&lt;/code&gt; struct, without increasing the time complexity of other operations, and needing to change any of its public signature. We can do this by storing the ordered data in a &lt;a href=&#34;https://en.wikipedia.org/wiki/Doubly_linked_list&#34;&gt;Doubly Linked List&lt;/a&gt; data structure, and storing each node in the map as the value, instead of the raw value.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/doubly%20linked%20list%20and%20map.jpg&#34; alt=&#34;doubly linked list and map storage&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Implementation of a Doubly Linked List data structure should be fairly straight forward to implement. However, Go already has one under &lt;a href=&#34;https://golang.org/pkg/container/list/&#34;&gt;&lt;code&gt;container/list&lt;/code&gt; package&lt;/a&gt;. The only caveat for us with this one is that there is no generic version of this in Go 2 at the moment, as far as I am aware. That said, we actually don&#39;t need a generic version of this since we will use this internally within the scope of &lt;code&gt;OrderedMap&lt;/code&gt;, and we can store the value as &lt;code&gt;interface{}&lt;/code&gt; instead.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Implementation&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;Let&#39;s look at the implementation, going through it step by step. Below we can see how we are changing the internal state storage constructs of the &lt;code&gt;OrderedMap&lt;/code&gt; struct as well as its construction:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;type OrderedMap[type K comparable, V any] struct {&#xA;&#x9;store map[K]*list.Element&#xA;&#x9;keys  *list.List&#xA;}&#xA;&#xA;func NewOrderedMap[type K comparable, V any]() *OrderedMap[K, V] {&#xA;&#x9;return &amp;OrderedMap[K, V]{&#xA;&#x9;&#x9;store: map[K]*list.Element{},&#xA;&#x9;&#x9;keys:  list.New(),&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The biggest changes to notice here are:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;keys&lt;/code&gt; type has changed from &lt;code&gt;[]K&lt;/code&gt; to &lt;code&gt;*list.List&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;map type has changed from &lt;code&gt;map[K]V&lt;/code&gt; to &lt;code&gt;map[K]*list.Element&lt;/code&gt;, where we are now storing the doubly linked list node, instead of the raw value&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;These changes to the internal storage will impact the implementation of all methods, but without increasing the time complexity, and needing to change the public signature of each method. Below, for example, is how the &lt;code&gt;Set&lt;/code&gt; and &lt;code&gt;Get&lt;/code&gt; method implementations have changed:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;type keyValueHolder[type K comparable, V any] struct {&#xA;&#x9;key K&#xA;&#x9;value V&#xA;}&#xA;&#xA;func (o *OrderedMap[K, V]) Set(key K, val V) {&#xA;&#x9;var e *list.Element&#xA;&#x9;if _, exists := o.store[key]; !exists {&#xA;&#x9;&#x9;e = o.keys.PushBack(keyValueHolder[K, V]{&#xA;&#x9;&#x9;&#x9;key: key,&#xA;&#x9;&#x9;&#x9;value: val,&#xA;&#x9;&#x9;})&#xA;&#x9;} else {&#xA;&#x9;&#x9;e = o.store[key]&#xA;&#x9;&#x9;e.Value = keyValueHolder[K, V]{&#xA;&#x9;&#x9;&#x9;key: key,&#xA;&#x9;&#x9;&#x9;value: val,&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#x9;o.store[key] = e&#xA;}&#xA;&#xA;func (o *OrderedMap[K, V]) Get(key K) (V, bool) {&#xA;&#x9;val, exists := o.store[key]&#xA;&#x9;if !exists {&#xA;&#x9;&#x9;return *new(V), false&#xA;&#x9;}&#xA;&#x9;return val.Value.(keyValueHolder[K, V]).value, true&#xA;}&#xA;&#xA;func (o *OrderedMap[K, V]) Iterator() func() (*int, *K, V) {&#xA;&#x9;e := o.keys.Front()&#xA;&#x9;j := 0&#xA;&#x9;return func() (_ *int, _ *K, _ V) {&#xA;&#x9;&#x9;if e == nil {&#xA;&#x9;&#x9;&#x9;return&#xA;&#x9;&#x9;}&#xA;&#xA;&#x9;&#x9;keyVal := e.Value.(keyValueHolder[K, V])&#xA;&#x9;&#x9;j++&#xA;&#x9;&#x9;e = e.Next()&#xA;&#xA;&#x9;&#x9;return func() *int { v := j-1; return &amp;v }(), &amp;keyVal.key, keyVal.value&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;In a nutshell, the difference here is that we are storing the doubly linked list node as the value in the map, and we store both the key and value as the value of the doubly linked list node through &lt;code&gt;keyValueHolder&lt;/code&gt; struct. This obviously impacts how we set and get the data, but the time complexity of both the methods stay as &lt;code&gt;O(1)&lt;/code&gt;. We will actually observe the biggest change with the &lt;code&gt;Delete&lt;/code&gt; method here:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;func (o *OrderedMap[K, V]) Delete(key K) {&#xA;&#x9;e, exists := o.store[key]&#xA;&#x9;if !exists {&#xA;&#x9;&#x9;return&#xA;&#x9;}&#xA;&#xA;&#x9;o.keys.Remove(e)&#xA;&#xA;&#x9;delete(o.store, key)&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;If we were to break this down, here is what we are doing here:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Accessing the doubly linked list node from the map first, based on the given key. This is &lt;code&gt;O(1)&lt;/code&gt; in terms of time complexity.&lt;/li&gt;&#xA;&lt;li&gt;Calling &lt;code&gt;Remove&lt;/code&gt; on the doubly linked list by passing the found element. This is &lt;code&gt;O(1)&lt;/code&gt; in terms of time complexity.&lt;/li&gt;&#xA;&lt;li&gt;Deleting node from the map, based on the given key. This is also &lt;code&gt;O(1)&lt;/code&gt; in terms of time complexity.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;One more thing to unwrap here is how come calling &lt;code&gt;Remove&lt;/code&gt; on the doubly linked list is &lt;code&gt;O(1)&lt;/code&gt; in terms of time complexity, and showing its implementation might shed some light on the rationale:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;// Remove removes e from l if e is an element of list l.&#xA;// It returns the element value e.Value.&#xA;// The element must not be nil.&#xA;func (l *List) Remove(e *Element) interface{} {&#xA;&#x9;if e.list == l {&#xA;&#x9;&#x9;// if e.list == l, l must have been initialized when e was inserted&#xA;&#x9;&#x9;// in l or l == nil (e is a zero Element) and l.remove will crash&#xA;&#x9;&#x9;l.remove(e)&#xA;&#x9;}&#xA;&#x9;return e.Value&#xA;}&#xA;&#xA;// remove removes e from its list, decrements l.len, and returns e.&#xA;func (l *List) remove(e *Element) *Element {&#xA;&#x9;e.prev.next = e.next&#xA;&#x9;e.next.prev = e.prev&#xA;&#x9;e.next = nil // avoid memory leaks&#xA;&#x9;e.prev = nil // avoid memory leaks&#xA;&#x9;e.list = nil&#xA;&#x9;l.len--&#xA;&#x9;return e&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;All that this implementation does is to appropriately remove the links from the node that we want to remove, and wiring up its next node with its previous node (if they exist). You can also see this implementation &lt;a href=&#34;https://github.com/golang/go/blob/4fc3896e7933e31822caa50e024d4e139befc75f/src/container/list/list.go#L107-L144&#34;&gt;here&lt;/a&gt; in Go source code.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;You can also see the whole implementation working &lt;a href=&#34;https://go2goplay.golang.org/p/UJZsQnPRmRh &#34;&gt;here in Go 2 Playground&lt;/a&gt;:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;0 1 string1 is a string&#xA;1 2 string2 is a string&#xA;2 4 string4 is a string&#xA;&#xA;Program exited.&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Benchmarking to Show the Impact&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;To be able to understand the improvement we have made here, we can run a benchmark between the two &lt;code&gt;Delete&lt;/code&gt; implementations with &lt;a href=&#34;https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go&#34;&gt;Go&#39;s built-in benchmark tooling&lt;/a&gt;. For this, we will go with the below benchmark setup:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Sequential input of an array with 1M items&lt;/li&gt;&#xA;&lt;li&gt;Seeding the &lt;code&gt;OrderedMap&lt;/code&gt; with these 1M items&lt;/li&gt;&#xA;&lt;li&gt;Deleting a random key for 100K times&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;One caveat with the benchmark here is that we will run it with non-generic implementations since I wasn&#39;t unable to find a way to install Go 2 on my machine to be able to run a benchmark, and I don&#39;t believe it&#39;s possible to run one with Go playground. That said, this shouldn&#39;t change anything for us to be able to understand the difference between two &lt;code&gt;Delete&lt;/code&gt; implementations as the logic will stay the same. The only change will be that the usage of the type will not be obvious due to lack of strongly typed signature. I have put the &lt;code&gt;OrderedMap&lt;/code&gt; with original &lt;code&gt;Delete&lt;/code&gt; implementation &lt;a href=&#34;https://github.com/tugberkugurlu/algos-go/blob/a8c519e7c60e5fb838a3e5bd8a97893f96ad834b/ordered-map-generics/ordered_map.go#L27-L42&#34;&gt;here&lt;/a&gt;, and with the improved one &lt;a href=&#34;https://github.com/tugberkugurlu/algos-go/blob/a8c519e7c60e5fb838a3e5bd8a97893f96ad834b/ordered-map-generics/ordered_map_linkedlist_based_delete.go#L47-L56&#34;&gt;here&lt;/a&gt;.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The code for the benchmark itself is as below (which you can also find &lt;a href=&#34;https://github.com/tugberkugurlu/algos-go/blob/a8c519e7c60e5fb838a3e5bd8a97893f96ad834b/ordered-map-generics/main_test.go#L136-L160&#34;&gt;here&lt;/a&gt;):&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;math/rand&#34;&#xA;&#x9;&#34;testing&#34;&#xA;)&#xA;&#xA;func BenchmarkOrderedMapLinkedListBasedDelete(b *testing.B) {&#xA;&#x9;for n := 0; n &lt; b.N; n++ {&#xA;&#x9;&#x9;seedCount := 1000000&#xA;&#x9;&#x9;m := NewLinkedListBasedOrderedMap()&#xA;&#x9;&#x9;for i := 1; i &lt;= seedCount; i++ {&#xA;&#x9;&#x9;&#x9;m.Set(i, fmt.Sprintf(&#34;string%d&#34;, i))&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;for i := 0; i &lt; 100000; i++ {&#xA;&#x9;&#x9;&#x9;m.Delete(rand.Intn(seedCount-1) + 1)&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;}&#xA;&#xA;func BenchmarkOrderedMapSliceBasedDelete(b *testing.B) {&#xA;&#x9;for n := 0; n &lt; b.N; n++ {&#xA;&#x9;&#x9;seedCount := 1000000&#xA;&#x9;&#x9;m := NewOrderedMap()&#xA;&#x9;&#x9;for i := 1; i &lt;= seedCount; i++ {&#xA;&#x9;&#x9;&#x9;m.Set(i, fmt.Sprintf(&#34;string%d&#34;, i))&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;for i := 0; i &lt; 100000; i++ {&#xA;&#x9;&#x9;&#x9;m.Delete(rand.Intn(seedCount-1) + 1)&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;We can run this benchmark with &lt;a href=&#34;https://golang.org/pkg/cmd/go/internal/test/&#34;&gt;&lt;code&gt;go test&lt;/code&gt; command&lt;/a&gt;. However, note that the exact time that takes to run each function is not significant here, since it will depend on the machine spec, etc. Also, in the test, we are seeding the map implementations for each run, which means extra time we are adding to the exact time to run the function. That said, what&#39;s important to observe here is the difference in time between two functions:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;➜  ordered-map-generics git:(master) ✗ go test -bench=BenchmarkOrderedMap -benchtime=30s&#xA;goos: darwin&#xA;goarch: amd64&#xA;pkg: github.com/tugberkugurlu/algos-go/ordered-map-generics&#xA;BenchmarkOrderedMapLinkedListBasedDelete-4   &#x9;      42&#x9; 724271767 ns/op&#xA;BenchmarkOrderedMapSliceBasedDelete-4        &#x9;       1&#x9;54509739278 ns/op&#xA;PASS&#xA;ok  &#x9;github.com/tugberkugurlu/algos-go/ordered-map-generics&#x9;86.361s&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Wow, we can see the orders of magnitude improvement here, and it&#39;s very rewarding to see the impact.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Conclusion&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;The biggest take away from this post I believe is how Generics will change the way we can implement more complex and powerful data structures, and allow them to be reused more effectively by truly taking advantage of strongly-typed nature of Go, as well as how important it&#39;s to use the correct data structures for the expected usage of own types. As a side note, I thoroughly enjoyed writing this post, as I was able to feel the power of having Generics available to use in Go!&#xA;&lt;/p&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/implementing-ordered-map-in-go-2-0-by-using-generics-with-delete-operation-in-o-1-time-complexity</guid>
      <pubDate>Sat, 29 Aug 2020 11:33:00 +0000</pubDate>
    </item>
    <item>
      <title>Usage of the Heap Data Structure in Go (Golang), with Examples</title>
      <link>https://www.tugberkugurlu.com/archive/usage-of-the-heap-data-structure-in-go-golang-with-examples</link>
      <description>&#xA;&lt;h3&gt;So, Tell Me More About This Heap Data Structure&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Heap_(data_structure)&#34;&gt;Heap&lt;/a&gt; is one of the most powerful data structures that is in our disposal to solve various real world problems more efficiently. Heap data structure usually comes with two shapes: Min Heap or Max Heap, and depending on which one it is, heap will give you efficient (i.e. &lt;code&gt;O(1)&lt;/code&gt;) access to min/max value within the given collection.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;Here is the characteristics of the heap data structure, which separate it from other data structure when all of these are combined together:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;a tree-based data structure, which is a complete binary tree&lt;/li&gt;&#xA;&lt;li&gt;In case of max heap, root node of the tree must represent the greatest value within the tree&lt;/li&gt;&#xA;&lt;li&gt;In case of min heap, root node of the tree must represent the smallest value within the tree&lt;/li&gt;&#xA;&lt;li&gt;Building a heap over an array of values has the cost of &lt;code&gt;O(n log n)&lt;/code&gt; in terms of time complexity (worst case), where &lt;code&gt;n&lt;/code&gt; is the length of the original array&lt;/li&gt;&#xA;&lt;li&gt;Adding/removing a value from an existing heap has the cost of &lt;code&gt;O(log n)&lt;/code&gt; in terms of time complexity, where &lt;code&gt;n&lt;/code&gt; is the length of the heap&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/01EGE38K4Z9FNA8R0WD5V76XA3.png&#34; alt=&#34;min heap&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;This information should be enough for us to get going for the purposes of this post, but if you want to understand a bit more on how to build a heap data structure, you can check &lt;a href=&#34;https://www.geeksforgeeks.org/binary-heap/&#34;&gt;this&lt;/a&gt; post out which shows some clever ways of building heap even with an array, instead of a tree.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;heap Package in Go&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;Go is infamous for its lack of generics (which is &lt;a href=&#34;https://blog.golang.org/generics-next-step&#34;&gt;hopefully changing soon&lt;/a&gt;), which makes it hard to implement this type of collection types very hard. That said, Go provides a package called &lt;a href=&#34;https://golang.org/pkg/container/heap/&#34;&gt;&lt;code&gt;container/heap&lt;/code&gt;&lt;/a&gt; which has heap operations for any type that implements &lt;a href=&#34;https://golang.org/pkg/container/heap/#Interface&#34;&gt;&lt;code&gt;heap.Interface&lt;/code&gt;&lt;/a&gt;.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;code&gt;heap.Interface&lt;/code&gt; has the below signature:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;type Interface interface {&#xA;&#x9;sort.Interface&#xA;&#x9;Push(x interface{}) // add x as element Len()&#xA;&#x9;Pop() interface{}   // remove and return element Len() - 1.&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;As we can see, it embeds the &lt;code&gt;sort.Interface&lt;/code&gt; into its signature. So, let&#39;s also see what that interface signature looks like:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;type Interface interface {&#xA;&#x9;// Len is the number of elements in the collection.&#xA;&#x9;Len() int&#xA;&#x9;// Less reports whether the element with&#xA;&#x9;// index i should sort before the element with index j.&#xA;&#x9;Less(i, j int) bool&#xA;&#x9;// Swap swaps the elements with indexes i and j.&#xA;&#x9;Swap(i, j int)&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;That&#39;s pretty much it. In a nutshell, Go asks us to implement some very basic operations on our own collection such as adding and removing a value, as well as requiring us to implement the sort interface which needs us to check which one of the given two values are less than the other, and doing a swap between two indices within the array. It also &#34;kindly&#34; asks us to perform some casting on behalf of it (ahem, covariance and contravariance, ahem, cough!).&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;There is still a catch here, since you can&#39;t add new methods to types outside package. For instance, the below code where we add methods to &lt;code&gt;[]int&lt;/code&gt; doesn&#39;t really work, with the error message of &#34;&lt;code&gt;Invalid receiver type &#39;[]int&#39; (&#39;[]int&#39; is an unnamed type)&lt;/code&gt;&#34;:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;func (h []int) Push(x interface{}) {&#xA;&#x9;*h = append(*h, x.(int))&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;We can get around this with a type declaration and attaching the method on that type:&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;type IntHeap []int&#xA;&#xA;func (h *IntHeap) Push(x interface{}) {&#xA;&#x9;*h = append(*h, x.(int))&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;You can swap the type &lt;code&gt;int&lt;/code&gt; here with your own type, and build the heap structure for that type. For the purposes of this post though, we will continue with the &lt;code&gt;IntHeap&lt;/code&gt; type which we have declared above. With that in mind, let&#39;s see how the final implementation looks like:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&#xA;Tip 💡: if you are using &lt;a href=&#34;https://www.jetbrains.com/go/&#34;&gt;Goland IDE&lt;/a&gt;, you can hit CMD + ENTER while your cursor is on the type, choose &#34;Implement interface...&#34; option, and then select the interface which you want to implement from the list, it will scaffold the structure of the interface for you:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/01EGE2X681RKA6S5ESQKFRGZK2.png&#34; alt=&#34;implement interface&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;br/&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/01EGE2TE6FMKSEV4MVRJVN6C51.png&#34; alt=&#34;choose interface to implement&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;type IntHeap []int&#xA;&#xA;func (h IntHeap) Len() int {&#xA;&#x9;return len(h)&#xA;}&#xA;&#xA;func (h IntHeap) Less(i, j int) bool {&#xA;&#x9;return h[i] &lt; h[j]&#xA;}&#xA;&#xA;func (h IntHeap) Swap(i, j int) {&#xA;&#x9;h[i], h[j] = h[j], h[i]&#xA;}&#xA;&#xA;func (h *IntHeap) Push(x interface{}) {&#xA;&#x9;*h = append(*h, x.(int))&#xA;}&#xA;&#xA;func (h *IntHeap) Pop() interface{} {&#xA;&#x9;old := *h&#xA;&#x9;n := len(old)&#xA;&#x9;x := old[n-1]&#xA;&#x9;*h = old[0:n-1]&#xA;&#x9;return x&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;That&#39;s pretty much it. As you can see, all the implementation we had to do is for rudimentary operations, nothing fancy. We can now make use of this by initializing a variable with the given type: &lt;code&gt;h := &amp;IntHeap{}&lt;/code&gt;, and then start making use of the &lt;code&gt;heap.&lt;/code&gt; functions. Below, you can see a very basic example where we build the heap from a set of values inside an array, and then start printing them (which is essentially the same as performing &lt;a href=&#34;https://en.wikipedia.org/wiki/Heapsort&#34;&gt;heap sort&lt;/a&gt;):&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;func main() {&#xA;&#x9;nums := []int{3,2,20,5,3,1,2,5,6,9,10,4}&#xA;&#xA;&#x9;// initialize the heap data structure&#xA;&#x9;h := &amp;IntHeap{}&#xA;&#xA;&#x9;// add all the values to heap, O(n log n)&#xA;&#x9;for _, val := range nums { // O(n)&#xA;&#x9;&#x9;heap.Push(h, val) // O(log n)&#xA;&#x9;}&#xA;&#xA;&#x9;// print all the values from the heap&#xA;&#x9;// which should be in ascending order&#xA;&#x9;for i := 0; i &lt; len(nums); i++ {&#xA;&#x9;&#x9;fmt.Printf(&#34;%d,&#34;, heap.Pop(h).(int))&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;The output is the values printed in ascending order, as you expect:&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;➜  git:(master) ✗ go run main.go&#xA;1,2,2,3,3,4,5,5,6,9,10,20,%&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;A Practical Application of Heap Data Structure&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;That&#39;s great, but what are the real world applications of this data structure? &lt;a href=&#34;https://en.wikipedia.org/wiki/Heap_(data_structure)#Applications&#34;&gt;There are a few&lt;/a&gt;, and I want to focus on one real world example of this today: finding the &lt;code&gt;k&lt;/code&gt; best elements within an unsorted array with the length of &lt;code&gt;n&lt;/code&gt;, where the definition of &#34;the best&#34; is the largest value in the array. Seems straight forward! If we are after solving this problem by spending minimum effort, the easiest way is by basically sorting the array and returning the first &lt;code&gt;k&lt;/code&gt; elements from it. The code for this would look like as below:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;func FindBestKElementsWithSort(nums []int, k int) []int {&#xA;&#x9;sort.Slice(nums, func(i, j int) bool { // O (n log n)&#xA;&#x9;&#x9;return nums[i] &gt; nums[j]&#xA;&#x9;})&#xA;&#xA;&#x9;return func() []int { // O (k)&#xA;&#x9;&#x9;result := make([]int, k)&#xA;&#x9;&#x9;for i := 0; i &lt; k; i++ {&#xA;&#x9;&#x9;&#x9;result[i] = nums[i]&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;return result&#xA;&#x9;}()&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;We can also test it with the below code, to ensure that the logic works as expected:&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;testing&#34;&#xA;)&#xA;&#xA;var bestElementsTestdata = []struct {&#xA;&#x9;in  []int&#xA;&#x9;k   int&#xA;&#x9;f   func(nums []int, k int) []int&#xA;&#x9;out []int&#xA;}{&#xA;&#x9;{[]int{3, 2, 1, 5, 6, 4}, 2, FindBestKElementsWithSort, []int{6,5}},&#xA;&#x9;{[]int{3, 2, 3, 1, 2, 4, 5, 5, 6}, 4, FindBestKElementsWithSort, []int{6,5,5,4}},&#xA;}&#xA;&#xA;func TestBestElementsLogic(t *testing.T) {&#xA;&#x9;for _, tt := range kthElementTestdata {&#xA;&#x9;&#x9;t.Run(fmt.Sprintf(&#34;%v&#34;, tt.in), func(t *testing.T) {&#xA;&#x9;&#x9;&#x9;out := tt.f(tt.in, tt.k)&#xA;&#x9;&#x9;&#x9;if out != tt.out {&#xA;&#x9;&#x9;&#x9;&#x9;t.Errorf(&#34;got %q, want %q&#34;, out, tt.out)&#xA;&#x9;&#x9;&#x9;}&#xA;&#x9;&#x9;})&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The time complexity of this is going to be &lt;code&gt;O (n log n + k)&lt;/code&gt;, which is not bad. However, we can do better with the assumption that &lt;code&gt;k&lt;/code&gt; will be smaller than &lt;code&gt;n&lt;/code&gt; here. In a real world case, where we want to, for example, find the best top 100 results within a result set of millions, this assumption will be the key part to our optimization.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;With that in mind, what we can do instead of directly sorting the array is to maintain a heap with the max length of &lt;code&gt;k&lt;/code&gt;, and once we iterate over the entire given list, we can then reverse the result from our min heap. The code for this will look like as below:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;func FindBestKElements(nums []int, k int) []int {&#xA;&#x9;h := &amp;IntHeap{}&#xA;&#x9;for _, val := range nums { // O(N)&#xA;&#x9;&#x9;heap.Push(h, val) // O(log K)&#xA;&#x9;&#x9;if h.Len() &gt; k {&#xA;&#x9;&#x9;&#x9;heap.Pop(h) // O(log K)&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#xA;&#x9;return func() []int { // O (k log k)&#xA;&#x9;&#x9;result := make([]int, h.Len())&#xA;&#x9;&#x9;initialLen := h.Len()&#xA;&#x9;&#x9;for i := initialLen; i &gt; 0; i-- {&#xA;&#x9;&#x9;&#x9;result[i-1] = heap.Pop(h).(int)&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;return result&#xA;&#x9;}()&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;We can now extend the original test cases to make sure that our logic works as expected:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;fmt&#34;&#xA;&#x9;&#34;testing&#34;&#xA;)&#xA;&#xA;var bestElementsTestdata = []struct {&#xA;&#x9;in  []int&#xA;&#x9;k   int&#xA;&#x9;f   func(nums []int, k int) []int&#xA;&#x9;out []int&#xA;}{&#xA;&#x9;{[]int{3, 2, 1, 5, 6, 4}, 2, FindBestKElements, []int{6,5}},&#xA;&#x9;{[]int{3, 2, 3, 1, 2, 4, 5, 5, 6}, 4, FindBestKElements, []int{6,5,5,4}},&#xA;&#x9;{[]int{3, 2, 1, 5, 6, 4}, 2, FindBestKElementsWithSort, []int{6,5}},&#xA;&#x9;{[]int{3, 2, 3, 1, 2, 4, 5, 5, 6}, 4, FindBestKElementsWithSort, []int{6,5,5,4}},&#xA;}&#xA;&#xA;func TestBestElementsLogic(t *testing.T) {&#xA;&#x9;for _, tt := range kthElementTestdata {&#xA;&#x9;&#x9;t.Run(fmt.Sprintf(&#34;%v&#34;, tt.in), func(t *testing.T) {&#xA;&#x9;&#x9;&#x9;out := tt.f(tt.in, tt.k)&#xA;&#x9;&#x9;&#x9;if out != tt.out {&#xA;&#x9;&#x9;&#x9;&#x9;t.Errorf(&#34;got %q, want %q&#34;, out, tt.out)&#xA;&#x9;&#x9;&#x9;}&#xA;&#x9;&#x9;})&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;Result:&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;➜  git:(master) ✗ go test --run=TestBestElementsLogic -v&#xA;=== RUN   TestBestElementsLogic&#xA;=== RUN   TestBestElementsLogic/[3_2_1_5_6_4]&#xA;=== RUN   TestBestElementsLogic/[3_2_3_1_2_4_5_5_6]&#xA;=== RUN   TestBestElementsLogic/[3_2_1_5_6_4]#01&#xA;=== RUN   TestBestElementsLogic/[3_2_3_1_2_4_5_5_6]#01&#xA;--- PASS: TestBestElementsLogic (0.00s)&#xA;    --- PASS: TestBestElementsLogic/[3_2_1_5_6_4] (0.00s)&#xA;    --- PASS: TestBestElementsLogic/[3_2_3_1_2_4_5_5_6] (0.00s)&#xA;    --- PASS: TestBestElementsLogic/[3_2_1_5_6_4]#01 (0.00s)&#xA;    --- PASS: TestBestElementsLogic/[3_2_3_1_2_4_5_5_6]#01 (0.00s)&#xA;PASS&#xA;ok  &#x9;_/Users/tugberkugurlu/go/src/github.com/tugberkugurlu/algos-go/kth-largest&#x9;0.835s&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;The time complexity of this is &lt;code&gt;O(n log k + k log k)&lt;/code&gt;, which is much better.&#xA;&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Benchmarking&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;But, how much better? To be able to understand the improvement we have made here, we can run a benchmark between the two implementations with &lt;a href=&#34;https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go&#34;&gt;Go&#39;s built-in benchmark tooling&lt;/a&gt;. For this, we will go with the below benchmark setup:&#xA;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Random input of an array with 10M items&lt;/li&gt;&#xA;&lt;li&gt;We will use the same set of input across all the runs to be able to make the comparison fair. We will achieve this by using the &lt;a href=&#34;https://golang.org/pkg/testing/#hdr-Main&#34;&gt;&lt;code&gt;TestMain&lt;/code&gt; hook in Go&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Value of &lt;code&gt;k&lt;/code&gt; as 500&lt;/li&gt;&#xA;&lt;li&gt;For each run, we will make a copy of the array so that we can run the benchmark deterministically since the sort based solution mutates the given array, and we will also do this for the heap based solution to make the comparison fair&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;The code for this benchmark is as below:&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;package main&#xA;&#xA;import (&#xA;&#x9;&#34;math/rand&#34;&#xA;&#x9;&#34;reflect&#34;&#xA;&#x9;&#34;testing&#34;&#xA;)&#xA;&#xA;var nums []int&#xA;func TestMain(m *testing.M) {&#xA;&#x9;maxVal := 10000000&#xA;&#x9;nums = make([]int, maxVal)&#xA;&#x9;for i := 0; i &lt; len(nums); i++ {&#xA;&#x9;&#x9;nums[i] = rand.Intn(maxVal)&#xA;&#x9;}&#xA;&#x9;m.Run()&#xA;}&#xA;&#xA;func BenchmarkFindBestKElementsK500(b *testing.B) {&#xA;&#x9;k := 500&#xA;&#x9;for n := 0; n &lt; b.N; n++ {&#xA;&#x9;&#x9;nums2 := make([]int, len(nums))&#xA;&#x9;&#x9;for i, v := range nums {&#xA;&#x9;&#x9;&#x9;nums2[i] = v&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;FindBestKElements(nums2, k)&#xA;&#x9;}&#xA;}&#xA;&#xA;func BenchmarkFindBestKElementsWithSortK500(b *testing.B) {&#xA;&#x9;k := 500&#xA;&#x9;for n := 0; n &lt; b.N; n++ {&#xA;&#x9;&#x9;nums2 := make([]int, len(nums))&#xA;&#x9;&#x9;for i, v := range nums {&#xA;&#x9;&#x9;&#x9;nums2[i] = v&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;FindBestKElementsWithSort(nums2, k)&#xA;&#x9;}&#xA;}&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;We can run this benchmark with &lt;code&gt;go test&lt;/code&gt; command. However, note that the exact time that takes to run each function is not significant here, since it will depend on the machine spec, etc. Also, in the test, we are copying the input array for each run, which means extra time we are adding to the exact time to run the function. That said, what&#39;s important to observe here is the difference in time between two functions:&#xA;&#xA;&lt;p&gt;&#xA;&lt;pre&gt;&#xA;➜  git:(master) ✗ go test -bench=BenchmarkFindBestKElements -benchtime=30s &#xA;goos: darwin&#xA;goarch: amd64&#xA;BenchmarkFindBestKElementsK500-4           &#x9;      15&#x9;2200446151 ns/op&#xA;BenchmarkFindBestKElementsWithSortK500-4   &#x9;      13&#x9;2618637516 ns/op&#xA;PASS&#xA;ok  &#x9;_/Users/tugberkugurlu/go/src/github.com/tugberkugurlu/algos-go/kth-largest&#x9;72.740s&#xA;&lt;/pre&gt;&#xA;&lt;/p&gt;&#xA;&#xA;The heap based implementation is about 20% faster than the sort based implementation, which is a significant difference of performance. More importantly, this diff will get worst as the length of the array increases.&#xA;&#xA;&lt;h3&gt;Conclusion&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;&#xA;Heap is a powerful data structure, which is a perfect to solve some real world problems most efficiently. However, it&#39;s often overlooked. Hopefully, this post sheds some light on where this data structure can be useful for us in terms of efficiency, and how Go programming language helps us by providing the necessary ground work to work with data structure even if it&#39;s still not at the desirable level in terms of reusability due to lack of generics in the platform (which means that I used all my daily allowance for ranting about lack of generics in Go).&#xA;&lt;/p&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/usage-of-the-heap-data-structure-in-go-golang-with-examples</guid>
      <pubDate>Sun, 23 Aug 2020 13:39:00 +0000</pubDate>
    </item>
    <item>
      <title>Kafka Core Concepts and Producer Semantics</title>
      <link>https://www.tugberkugurlu.com/archive/kafka-core-concepts-and-producer-semantics</link>
      <description>&#xA;&lt;p id=&#34;91d84e99-35a9-4e5b-9914-280efbee84a6&#34; class=&#34;&#34;&gt;Being able to pass data around within a distributed&#xA;    system is the one of the the most crucial aspects of the success for your business, especially when you&#xA;    are dealing with large number of users, reads and writes. It&amp;#x27;s usual that for a given data write&#xA;    for an entity, you will have N number of read patterns, not just one. &lt;a&#xA;        href=&#34;https://kafka.apache.org/&#34;&gt;Apache Kafka&lt;/a&gt; is one of the most effective ways to enable that&#xA;    data distribution within a complex system. I have had the chance to use Kafka at work for more than a&#xA;    year now. However, it has always been implicit and I never needed to understand its intrinsic semantics&#xA;    (standing on the shoulders of giants). I have spent this extended weekend reading the Kafka&#xA;    documentation and running some local examples with Kafka to understand it in details, not just at a high&#xA;    level.&lt;/p&gt;&#xA;&lt;p id=&#34;831c386d-05a1-46bc-909a-14ed649663be&#34; class=&#34;&#34;&gt;Kafka already has &lt;a&#xA;        href=&#34;https://kafka.apache.org/documentation&#34;&gt;a great documentation&lt;/a&gt;, which is very detailed and&#xA;    clear. The intention with this post is not to replicate that document. Instead, it&amp;#x27;s to pull out&#xA;    bits and pieces which helped me understand Kafka better, and increased by trust. As it has been said in&#xA;    Batman Begins movie (which is one of my all-time favourites): &amp;quot;&lt;a&#xA;        href=&#34;https://www.magicalquote.com/moviequotes/you-always-fear-what-you-dont-understand/&#34;&gt;You always&#xA;        fear what you don&amp;#x27;t understand&lt;/a&gt;&amp;quot;, and the main outcome here is to remove that fear :)&#xA;    The post is written by a someone, which is me, who has previous experience with messaging systems such&#xA;    as RabbitMQ, Amazon SQS, and Azure Service Bus. So, I might be overlooking some important aspects which&#xA;    you may also need if you don&amp;#x27;t have this background. If that&amp;#x27;s the case, it might be useful to&#xA;    first understand &lt;a href=&#34;https://kafka.apache.org/documentation/#uses&#34;&gt;some use cases where Kafka might&#xA;        fit in&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ee274949-d1ca-447e-aa21-db03ad51d1af&#34; class=&#34;&#34;&gt;Concepts&lt;/h2&gt;&#xA;&lt;p id=&#34;2e13d642-9e0a-46a7-81f7-36f504b75e0f&#34; class=&#34;&#34;&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/01E972A8Z95PB98E3082YJ1P2M-log_anatomy.png&#34; alt=&#34;Kafka Topic&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&lt;p id=&#34;91f725c1-83dc-47f0-90e2-55c0ba86db27&#34; class=&#34;&#34;&gt;Let&amp;#x27;s first understand some of the high level&#xA;    concepts of Kafka, which will allow us to get started and work on a sample later on. This is by all&#xA;    means not an exhaustive list of concepts in Kafka but will be enough to get us going by allowing us to&#xA;    extract some facts as well as allowing us to make some assumptions with high confidence.&lt;/p&gt;&#xA;&lt;p id=&#34;e53c9b17-6500-4ca6-9cee-bc21f99dd908&#34; class=&#34;&#34;&gt;The most important concept of Kafka is a&#xA;    &lt;strong&gt;Topic&lt;/strong&gt;. Topics in Kafka is a place where you can logically group your messages into.&#xA;    When I say logically, I don&amp;#x27;t mean a schema or anything. You can think of it as just a bucket where&#xA;    your data will end up in the order they appear, and can also be retrieved in the same order (i.e.&#xA;    continually appended to a structured commit log). Topics can be subscribed to by one or more consumers,&#xA;    which we will touch on that a few points later, but this means that Kafka doesn&amp;#x27;t have exact&#xA;    message queue semantics, which ensures that the data is gone as soon as one consumer processes the data.&#xA;&lt;/p&gt;&#xA;&lt;p id=&#34;94138dad-c82c-41e8-89ef-fc56d65c9609&#34; class=&#34;&#34;&gt;These message are called &lt;strong&gt;Records&lt;/strong&gt;,&#xA;    which are durably persisted in the Kafka cluster regardless of the fact that they have been consumed or&#xA;    not. This differentiates Kafka from queuing systems such as RabbitMQ or SQS, where messages vanish after&#xA;    they are being consumed and processed. Using Kafka for storing records permanently is a perfectly valid&#xA;    choice. However, if this is not desired, Kafka also give you a retention configuration options to&#xA;    specific how long you want to hold onto records per topic basis. &lt;/p&gt;&#xA;&lt;p id=&#34;d9dba195-478a-497e-9d03-3a04e571265b&#34; class=&#34;&#34;&gt;The records gets into (i.e. written) a topic through a&#xA;    &lt;a href=&#34;https://kafka.apache.org/documentation/#intro_producers&#34;&gt;&lt;strong&gt;producer&lt;/strong&gt;&lt;/a&gt;, who are&#xA;    also responsible for choosing which record to assign to which partition within the topic. In other&#xA;    words, data sharding is handled by the clients which publish data to a particular topic. Depending on&#xA;    what client you use, you may have different options on how to distribute data across the partitions,&#xA;    e.g. round robin, your custom sharding strategy, etc.&lt;/p&gt;&#xA;&lt;p id=&#34;cbeac82e-a422-4047-aeab-f29ceaee0a73&#34; class=&#34;&#34;&gt;The records within a specific topic are consumed (i.e.&#xA;    read) by a &lt;a&#xA;        href=&#34;https://kafka.apache.org/documentation/#intro_consumers&#34;&gt;&lt;strong&gt;consumer&lt;/strong&gt;&lt;/a&gt;, which&#xA;    is part of a &lt;strong&gt;consumer group&lt;/strong&gt;. Consumer groups allow records to be processed in parallel&#xA;    by the consumer instances (which are associated to that group, and can live in separate processes or&#xA;    machines) with a guarantee that a record is only delivered to one consumer instance. A consumer instance&#xA;    within a consumer group will own one or more partitions exclusively, which means that you can have at&#xA;    max N number of consumer if you have N partitions.&lt;/p&gt;&#xA;&lt;p id=&#34;c879ef20-895f-43c1-a44f-835dea800f6a&#34; class=&#34;&#34;&gt;So, based on these, here are some take aways which I&#xA;    was able to further unpack by following up:&lt;/p&gt;&#xA;&lt;ul id=&#34;a636bdfa-af19-42f1-8f94-d31454f22312&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;Data stored in Kafka is immutable, meaning that it cannot be updated. In other words, Kafka is&#xA;        working with an append-only data structure and all you can do with it is to ask for the next record&#xA;        and reset to current pointer.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;dd72b22e-8079-4871-aef2-eeea83300e13&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;Kafka has a distributed nature to cater your scalability and high availability needs.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;0543d407-ec23-4f2c-a8f0-451701cbd62c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;Kafka guarantees ordering for the records but this is only per partition basis and how you retry&#xA;        messages can also have an impact on this order. Therefore, it&amp;#x27;s safest to assume at the&#xA;        consumption level that Kafka won&amp;#x27;t give you a message ordering guarantees, and you may need to&#xA;        understanding the details of this further depending on how your messages are distributed across the&#xA;        partitions, and how you plan to process that data. &lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;44915cbf-9b6c-4123-aaad-7a24aba376e3&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;Kafka is consumer driven, which means that consumer is in charge of determining reading the data&#xA;        from which position they like. In practical terms, this means that the consumer can reset the offset&#xA;        and start from wherever it wants to. Check out the &lt;a&#xA;            href=&#34;https://kafka.apache.org/documentation/#impl_offsettracking&#34;&gt;Offset Tracking&lt;/a&gt; and &lt;a&#xA;            href=&#34;https://kafka.apache.org/documentation/#basic_ops_consumer_group&#34;&gt;Consumer Group&#xA;            Management&lt;/a&gt; sections for more info on this.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;205e5f65-a123-42c3-8f19-81ace05b8e98&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://kafka.apache.org/documentation/#basic_ops_cluster_expansion&#34;&gt;It&amp;#x27;s possible to&#xA;            add new nodes to your cluster&lt;/a&gt;. The data distribution to this node though &lt;a&#xA;            href=&#34;https://kafka.apache.org/documentation/#basic_ops_automigrate&#34;&gt;needs to be triggered&#xA;            manually&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;311e25fe-5b27-4a0a-b2e6-2165a19d7987&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;Related to above, &lt;a href=&#34;https://kafka.apache.org/documentation.html#basic_ops_modify_topic&#34;&gt;you&#xA;            can increase the number of partitions for a given topic&lt;/a&gt;. However, this is an operation you&#xA;        do not want to perform without proactively thinking through the consequences since the way you&#xA;        publish data to Kafka might be impacted by this, if, for example, your sharding strategy is rely on&#xA;        knowing the partition count (i.e. &lt;code&gt;hash(key) % number_of_partitions&lt;/code&gt;). It&amp;#x27;s also&#xA;        important to know that Kafka will not attempt to automatically redistribute data in any way. So,&#xA;        this onus is also on you, too.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;724bf185-22ce-4ce5-a796-986961796e9e&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;There is currently no support for reducing the number of partitions for a topic.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;cc2239f7-0c89-4dd1-bdcb-d9bde4f1cf3a&#34; class=&#34;&#34;&gt;Semantics of Data Producing&lt;/h2&gt;&#xA;&lt;p id=&#34;b45d7cab-9342-48be-b927-c6ea20b9508c&#34; class=&#34;&#34;&gt;On the data producing side, we need to know the topic&#xA;    name and the approach we need to use to distribute data across partitions (which is likely that your&#xA;    client will help on this with some out-of-the-box strategies, such as round-robin &lt;a&#xA;        href=&#34;https://docs.confluent.io/4.0.0/clients/producer.html&#34;&gt;as guaranteed by Confluent&#xA;        clients&lt;/a&gt;). Apart from this, we have quite a few &lt;a&#xA;        href=&#34;https://kafka.apache.org/documentation.html#producerconfigs&#34;&gt;producer level configuration&lt;/a&gt;&#xA;    we can apply to influence the semantics of data publishing.&lt;/p&gt;&#xA;&lt;p id=&#34;751e8cc8-4b69-4b51-8df3-f4b2d8dc6ed7&#34; class=&#34;&#34;&gt;When I am working with messaging systems, the first&#xA;    thing I want to understand is how the message delivery and durability guarantees are influenced, and&#xA;    what the default behaviour is for these. In Kafka, I found that this story a bit more confusing that it&#xA;    should probably be, which is due to a few configuration settings to be aligned to make it work in favour&#xA;    of durability to prevent message loss. Here are some important configuration for this:&lt;/p&gt;&#xA;&lt;ul id=&#34;b9d8ed44-c468-4981-9b0b-13c56f09f36b&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;code&gt;&lt;a href=&#34;https://kafka.apache.org/documentation.html#acks&#34;&gt;acks&lt;/a&gt;&lt;/code&gt;: This setting&#xA;        indicates the number of acknowledgments the producer requires for a message publishing to be deemed&#xA;        as successful. It can be set to &lt;code&gt;0&lt;/code&gt;, meaning that the producer won&amp;#x27;t require an ack&#xA;        from any of the servers and this won&amp;#x27;t give us any guarantees that the message is received by&#xA;        the server. This option could be preferable for cases where we need high throughput at the producing&#xA;        side and the data loss is not critical (e.g. sensor data, where losing a few seconds of data from a&#xA;        source won&amp;#x27;t spoil our world). For cases where record durability is important, this can be set&#xA;        to &lt;code&gt;all&lt;/code&gt;. This means the leader will wait for the full set of in-sync replicas to&#xA;        acknowledge the record, where the minimum number of required in-sync replicas is configured&#xA;        separately.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;d4459335-5203-457b-a408-266ebd92f5c1&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;code&gt;&lt;a href=&#34;https://kafka.apache.org/documentation.html#min.insync.replicas&#34;&gt;min.insync.replicas&lt;/a&gt;&lt;/code&gt;:&#xA;        Quoting from the doc directly: &amp;quot;When a producer sets &lt;code&gt;acks&lt;/code&gt; to&#xA;        &amp;quot;&lt;code&gt;all&lt;/code&gt;&amp;quot; (or &amp;quot;&lt;code&gt;-1&lt;/code&gt;&amp;quot;), min.insync.replicas specifies the&#xA;        minimum number of replicas that must acknowledge a write for the write to be considered&#xA;        successful&amp;quot;. This setting is topic level but can also be specified at the broker level. Setting&#xA;        this to the correct amount is really important and it&amp;#x27;s set to &lt;code&gt;1&lt;/code&gt; by default, which&#xA;        is probably not what you want if you care about durability of your messages and you have replication&#xA;        factor of &lt;code&gt;&amp;gt;3&lt;/code&gt; for the topic.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;a6dc5996-8065-494b-a542-a00fc6b34f2c&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;code&gt;&lt;a href=&#34;https://kafka.apache.org/documentation.html#flush.messages&#34;&gt;flush.messages&lt;/a&gt;&lt;/code&gt;:&#xA;        In Kafka, messages are immediately written to the filesystem but by default we only fsync() to sync&#xA;        the OS cache lazily. This means that even if we have set our acks and min.insync.replicas to&#xA;        optimise for durability, there is still a theoretical chance that we can lose data with this&#xA;        behaviour. I explicitly said &amp;quot;theoretical&amp;quot; here as it&amp;#x27;s quite unlikely to lose data&#xA;        with appropriate settings to rely on replication for data durability. For instance, with&#xA;        &lt;code&gt;acks=all&lt;/code&gt; and &lt;code&gt;min.insync.replicas=2&lt;/code&gt; settings for a topic which has&#xA;        replication factor of 3, we would be losing data after seeing a data write as successfull in cases&#xA;        of 3 machines (1 leader and 2 replicas) to fail at the same time before having a chance to flush&#xA;        that particular record to the disk, which is pretty unlikely, and this is why Kafka doesn&amp;#x27;t&#xA;        recommend setting this value as well as &lt;a&#xA;            href=&#34;https://kafka.apache.org/documentation.html#flush.ms&#34;&gt;&lt;code&gt;flush.ms&lt;/code&gt;&lt;/a&gt; value. So,&#xA;        we need to think a bit harder before setting these configuration values as this has some trade-offs&#xA;        to be thought about:&lt;ul id=&#34;72970d7c-7179-41c6-8b0f-f510c324ebb2&#34; class=&#34;bulleted-list&#34;&gt;&#xA;            &lt;li&gt;&lt;strong&gt;Durability&lt;/strong&gt;: Unflushed data may be lost if you are not using replication.&#xA;            &lt;/li&gt;&#xA;        &lt;/ul&gt;&#xA;        &lt;ul id=&#34;b6835546-07bf-4311-a2da-824c6f980acf&#34; class=&#34;bulleted-list&#34;&gt;&#xA;            &lt;li&gt;&lt;strong&gt;Latency&lt;/strong&gt;: Very large flush intervals may lead to latency spikes when the&#xA;                flush does occur as there will be a lot of data to flush.&lt;/li&gt;&#xA;        &lt;/ul&gt;&#xA;        &lt;ul id=&#34;7e7571ab-c4d6-4a13-94f7-35e20b5c9cef&#34; class=&#34;bulleted-list&#34;&gt;&#xA;            &lt;li&gt;&lt;strong&gt;Throughput&lt;/strong&gt;: The flush is generally the most expensive operation, and a&#xA;                small flush interval may lead to exceessive seeks.&lt;/li&gt;&#xA;        &lt;/ul&gt;&#xA;    &lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p id=&#34;6ab2286f-f5db-494b-9cb4-51680e9122cf&#34; class=&#34;&#34;&gt;So, a lot to think about here just to get message&#xA;    durability right. The good side of this complexity here is that Kafka is not trying to provide one way&#xA;    to solve all problems, which is not really possible especially when you want to optimise against&#xA;    different aspects (e.g. durability, throughput, etc.) depending the problem at hand. There is some&#xA;    further information on &lt;a href=&#34;https://kafka.apache.org/documentation.html#semantics&#34;&gt;message delivery&#xA;        guarantees in Kafka documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p id=&#34;945a6992-f1cf-4c81-8e36-d49532ced8ad&#34; class=&#34;&#34;&gt;There are some other producer semantics that requires&#xA;    understanding since the consequences of not understanding these might be costly depending on your needs.&#xA;    For example, &lt;a href=&#34;https://kafka.apache.org/documentation.html#semantics&#34;&gt;producer retries&lt;/a&gt; is&#xA;    really important to understand correctly as this will have impact on message ordering even within a&#xA;    single partition. Another one is the &lt;a&#xA;        href=&#34;https://kafka.apache.org/documentation.html#batch.size&#34;&gt;batch size configuration&lt;/a&gt;, which&#xA;    influences how many records to batch into one request whenever multiple records are being sent to the&#xA;    same partition. This might mean that &lt;a&#xA;        href=&#34;https://kafka.apache.org/documentation.html#design_asyncsend&#34;&gt;the sends will be performed&#xA;        asynchronously&lt;/a&gt; and it may not be suitable for your needs. Finally, the &lt;a&#xA;        href=&#34;https://kafka.apache.org/documentation.html#compaction&#34;&gt;log compaction&lt;/a&gt; is another concept&#xA;    which can be really useful to have a prior knowledge on, especially for cases where you publish the&#xA;    current state of an entity to a topic instead of publishing fine-grained events.&lt;/p&gt;&#xA;&lt;h2 id=&#34;8a618d84-a3bf-4e35-98e3-03698a63a312&#34; class=&#34;&#34;&gt;Resources&lt;/h2&gt;&#xA;&lt;ul id=&#34;f875d8a2-fd2d-46b6-899f-787842adafda&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://medium.com/better-programming/kafka-acks-explained-c0515b3b707e&#34;&gt;Kafka Acks&#xA;            Explained&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;14c2c4c5-4fff-4a15-97ac-3c72ae20663f&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a href=&#34;https://link.medium.com/sfm4jiKbL6&#34;&gt;Why fsync is bad for Kafka&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;75cfc68b-ddad-4541-8970-f45ac94c9071&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a&#xA;            href=&#34;https://users.kafka.apache.narkive.com/UUQx7UcG/does-kafka-send-the-acks-response-to-the-producer-after-flush-the-messages-to-the-disk-or-just-keep-&#34;&gt;Does&#xA;            kafka send the acks response to the producer after flush the messages to the disk or just keep&#xA;            them in the memory&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ul id=&#34;50279304-d760-4bdf-8607-6012628be5a2&#34; class=&#34;bulleted-list&#34;&gt;&#xA;    &lt;li&gt;&lt;a&#xA;            href=&#34;https://stackoverflow.com/questions/57987591/can-a-message-loss-occur-in-kafka-even-if-producer-gets-acknowledgement-for-it&#34;&gt;Can&#xA;            a message loss occur in Kafka even if producer gets acknowledgement for it?&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/kafka-core-concepts-and-producer-semantics</guid>
      <pubDate>Tue, 26 May 2020 00:30:00 +0000</pubDate>
    </item>
    <item>
      <title>Distributed Caching in .NET Core with PostSharp and Redis</title>
      <link>https://www.tugberkugurlu.com/archive/distributed-caching-in--net-core-with-postsharp-and-redis</link>
      <description>&#xA;&lt;p&gt;&lt;a href=&#34;https://www.tugberkugurlu.com/archive/declarative-coding-approach-to-caching-in--net-core-with-postsharp&#34; target=&#34;_blank&#34;&gt;On my previous post&lt;/a&gt;, I walked through the benefits of using &lt;a href=&#34;https://www.postsharp.net/?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=06_2019&#34;&gt;PostSharp&lt;/a&gt; for caching in a .NET Core server application. However, the example I have showed there would work on a single node application but as we know, probably no application today works on a single node. The benefits of deploying into multiple nodes are multiple such as providing further fault tolerance, and load distribution.&lt;/p&gt;&lt;p&gt;Luckily for us, &lt;a href=&#34;https://doc.postsharp.net/caching?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=06_2019&#34; target=&#34;_blank&#34;&gt;PostSharp caching&lt;/a&gt; backend is modular and the default in-memory one I have used in my previous post can be swapped. One of the out of the box implementations is based on &lt;a href=&#34;https://doc.postsharp.net/caching-redis/?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=06_2019&#34; target=&#34;_blank&#34;&gt;Redis&lt;/a&gt;, which is a highly scalable, distributed data structure server solution. One of the widely use cases of Redis is to be used as a ephemeral key/value store to power the caching needs of the apps.&lt;/p&gt;&lt;h3&gt;Run Redis Locally&lt;/h3&gt;&lt;p&gt;The best way to &lt;a href=&#34;https://hub.docker.com/_/redis/&#34; target=&#34;_blank&#34;&gt;run Redis locally is through Docker&lt;/a&gt;. Let’s run the below code to do this:&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;docker run --name postsharp-redis -p 6379:6379 -d redis a30f1c1e991e0159fb5f96dfb053f50c50726101907c7f76d319d5e987a6cf3a&#xA;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&#xA;&lt;p&gt;We have just got a redis instance up and running on our local environment and exposed it through TCP port mapping to the host machine to be available at port 6379. The final thing we need to do to get this ready for PostSharp usage is to set up the key-space notification to include the AKE events. You can see &lt;a href=&#34;https://redis.io/topics/notifications#configuration&#34; target=&#34;_blank&#34;&gt;the Redis notifications document&lt;/a&gt; for details on this.&lt;/p&gt;&lt;h3&gt;Configure for Redis Cache&lt;/h3&gt;&lt;p&gt;First thing to do is to install the NuGet package which contains the Redis&amp;nbsp; caching backend implementation for PostSharp.&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;dotnet add package PostSharp.Patterns.Caching.Redis --version 6.2.8&#xA;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Then, all we need to do is to change the caching backend to be the Redis implementation, which we have configured inside our Program.Main method in the previous post:&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;string connectionConfiguration = &#34;127.0.0.1&#34;;&#xA;var connection = ConnectionMultiplexer.Connect(connectionConfiguration);&#xA;var redisCachingConfiguration = new RedisCachingBackendConfiguration();&#xA;CachingServices.DefaultBackend = RedisCachingBackend.Create(connection, redisCachingConfiguration);&#xA;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Notice the server address we have entered, that points to the Redis instance we have got up and running through Docker and exposed to the host through port mapping. As we used the default Redis port, we didn’t need to state it explicitly.&lt;/p&gt;&lt;p&gt;From this point forward, our app is all ready to run with Redis caching enabled, without a single line of code change on the app components. Only change we had to do was on the configuration side.&lt;/p&gt;&lt;p&gt;For production, it’s worth getting a hold of the Redis server address through &lt;a href=&#34;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.2&#34; target=&#34;_blank&#34;&gt;a configuration system such as the one provided with ASP.NET Core&lt;/a&gt; so that you can swap it based on your environment.&lt;/p&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/distributed-caching-in--net-core-with-postsharp-and-redis</guid>
      <pubDate>Wed, 03 Jul 2019 21:48:34 +0000</pubDate>
    </item>
    <item>
      <title>Declarative Coding Approach to Caching in .NET Core with PostSharp</title>
      <link>https://www.tugberkugurlu.com/archive/declarative-coding-approach-to-caching-in--net-core-with-postsharp</link>
      <description>&#xA;&lt;p&gt;One of the first criteria of effective code is that it does its job with as few lines of code as possible. Effective code does not repeat itself. Less code in our codebases increases our chances of having less bugs. So, how do we avoid repeating ourselves? We apply our intelligence and abstraction skills to generalize behaviors into methods and classes, the constructs offered by C# to implement abstraction which we call encapsulation. However, some features such as logging or caching cannot be properly encapsulated into a class or method. That’s why you end up having code repetition. C# alone is simply not able to properly encapsulate features like logging, caching, security, INotifyPropertyChanged, undo/redo, etc.&lt;/p&gt;&lt;p&gt;I have been meaning to look into &lt;a href=&#34;https://en.wikipedia.org/wiki/Aspect-oriented_programming&#34; target=&#34;_blank&#34;&gt;Aspect-oriented programming&lt;/a&gt; for a while to help my code to be less noisy without sacrificing the application&#39;s acceptable performance and observability. This would help cut right to the business logic, allowing me to care about what&#39;s more important. When the topic is Aspect-oriented programming, first software comes to my mind is obviously &lt;a href=&#34;https://www.postsharp.net/?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;PostSharp&lt;/a&gt; in .NET world and in this post, I will be looking at how PostSharp can help us cut the noise out of our code and showcase this with a sample on data caching.&lt;/p&gt;&lt;h3&gt;Getting Started with PostSharp&lt;/h3&gt;&lt;p&gt;First of all, let&#39;s create our project structure and install PostSharp. I have .NET Core SDK 2.2.202 installed and ran the below commands to create the empty project structure.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;dotnet new web --no-https&#xA;dotnet new sln&#xA;dotnet sln 1-sample-web.sln add 1-sample-web.csproj&#xA;dotnet new globaljson&#xA;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In order to give you an idea about the value proposition of PostSharp, I created this little ASP.NET Core sample which exposes HTTP APIs to read, write and modify the Cars in our system. Some of the code here is contrived such as sleeping for half a second, etc. but we will see why this will be useful for us to see the PostSharp in action.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;using System;&#xA;using System.Collections.Concurrent;&#xA;using System.Collections.Generic;&#xA;using System.ComponentModel.DataAnnotations;&#xA;using System.Linq;&#xA;using System.Threading;&#xA;using System.Threading.Tasks;&#xA;using Microsoft.AspNetCore.Builder;&#xA;using Microsoft.AspNetCore.Hosting;&#xA;using Microsoft.AspNetCore.Http;&#xA;using Microsoft.AspNetCore.Mvc;&#xA;using Microsoft.Extensions.DependencyInjection;&#xA;&#xA;namespace _1_sample_web&#xA;{&#xA;    public class Startup&#xA;    {&#xA;        public void ConfigureServices(IServiceCollection services)&#xA;        {&#xA;            services.AddMvc();&#xA;        }&#xA;&#xA;        public void Configure(IApplicationBuilder app, IHostingEnvironment env)&#xA;        {&#xA;            app.UseMvcWithDefaultRoute();&#xA;        }&#xA;    }&#xA;&#xA;    public class CarsController : Controller&#xA;    {&#xA;        private static readonly CarsContext _carsCtx = new CarsContext();&#xA;&#xA;        [HttpGet(&#34;cars&#34;)]&#xA;        public IEnumerable&lt;car&gt; Get()&#xA;        {&#xA;            return _carsCtx.GetAll();&#xA;        }&#xA;&#xA;        [HttpGet(&#34;cars/{id}&#34;)]&#xA;        public IActionResult GetCar(int id) &#xA;        {&#xA;            var carTuple = _carsCtx.GetSingle(id);&#xA;            if (!carTuple.Item1) &#xA;            {&#xA;                return NotFound();&#xA;            }&#xA;&#xA;            return Ok(carTuple.Item2);&#xA;        }&#xA;&#xA;        [HttpPost(&#34;cars/{id}&#34;)]&#xA;        public IActionResult PostCar(Car car) &#xA;        {&#xA;            var createdCar = _carsCtx.Add(car);&#xA;            return CreatedAtAction(nameof(GetCar), &#xA;                new { id = createdCar.Id }, &#xA;                createdCar);&#xA;        }&#xA;&#xA;        [HttpPut(&#34;cars/{id}&#34;)]&#xA;        public IActionResult PutCar(int id, Car car) &#xA;        {&#xA;            car.Id = id;&#xA;            if (!_carsCtx.TryUpdate(car)) &#xA;            {&#xA;                return NotFound();&#xA;            }&#xA;&#xA;            return Ok(car);&#xA;        }&#xA;&#xA;        [HttpDelete(&#34;cars/{id}&#34;)]&#xA;        public IActionResult DeleteCar(int id) &#xA;        {&#xA;            if (!_carsCtx.TryRemove(id)) &#xA;            {&#xA;                return NotFound();&#xA;            }&#xA;&#xA;            return NoContent();&#xA;        }&#xA;    }&#xA;&#xA;    public class Car &#xA;    {&#xA;        public int Id { get; set; }&#xA;&#xA;        [Required]&#xA;        [StringLength(20)]&#xA;        public string Make { get; set; }&#xA;&#xA;        [Required]&#xA;        [StringLength(20)]&#xA;        public string Model { get; set; }&#xA;&#xA;        public int Year { get; set; }&#xA;&#xA;        [Range(0, 500000)]&#xA;        public float Price { get; set; }&#xA;    }&#xA;&#xA;    public class CarsContext&#xA;    {&#xA;        private int _nextId = 9;&#xA;        private object _idLock = new object();&#xA;&#xA;        private readonly ConcurrentDictionary&lt;int, car=&#34;&#34;&gt; _database = new ConcurrentDictionary&lt;int, car=&#34;&#34;&gt;(new HashSet&lt;keyvaluepair&lt;int, car=&#34;&#34;&gt;&amp;gt; &#xA;        { &#xA;            new KeyValuePair&lt;int, car=&#34;&#34;&gt;(1, new Car { Id = 1, Make = &#34;Make1&#34;, Model = &#34;Model1&#34;, Year = 2010, Price = 10732.2F }),&#xA;            new KeyValuePair&lt;int, car=&#34;&#34;&gt;(2, new Car { Id = 2, Make = &#34;Make2&#34;, Model = &#34;Model2&#34;, Year = 2008, Price = 27233.1F }),&#xA;            new KeyValuePair&lt;int, car=&#34;&#34;&gt;(3, new Car { Id = 3, Make = &#34;Make3&#34;, Model = &#34;Model1&#34;, Year = 2009, Price = 67437.0F }),&#xA;            new KeyValuePair&lt;int, car=&#34;&#34;&gt;(4, new Car { Id = 4, Make = &#34;Make4&#34;, Model = &#34;Model3&#34;, Year = 2007, Price = 78984.2F }),&#xA;            new KeyValuePair&lt;int, car=&#34;&#34;&gt;(5, new Car { Id = 5, Make = &#34;Make5&#34;, Model = &#34;Model1&#34;, Year = 1987, Price = 56200.89F }),&#xA;            new KeyValuePair&lt;int, car=&#34;&#34;&gt;(6, new Car { Id = 6, Make = &#34;Make6&#34;, Model = &#34;Model4&#34;, Year = 1997, Price = 46003.2F }),&#xA;            new KeyValuePair&lt;int, car=&#34;&#34;&gt;(7, new Car { Id = 7, Make = &#34;Make7&#34;, Model = &#34;Model5&#34;, Year = 2001, Price = 78355.92F }),&#xA;            new KeyValuePair&lt;int, car=&#34;&#34;&gt;(8, new Car { Id = 8, Make = &#34;Make8&#34;, Model = &#34;Model1&#34;, Year = 2011, Price = 1823223.23F })&#xA;        });&#xA;        &#xA;        public IEnumerable&lt;car&gt; GetAll()&#xA;        {&#xA;            Thread.Sleep(500);&#xA;            return _database.Values;&#xA;        }&#xA;&#xA;        public IEnumerable&lt;car&gt; Get(Func&lt;car, bool=&#34;&#34;&gt; predicate) &#xA;        {&#xA;            Thread.Sleep(500);&#xA;            return _database.Values.Where(predicate);&#xA;        }&#xA;&#xA;        public Tuple&lt;bool, car=&#34;&#34;&gt; GetSingle(int id) &#xA;        {&#xA;            Thread.Sleep(500);&#xA;&#xA;            Car car;&#xA;            var doesExist = _database.TryGetValue(id, out car);&#xA;            return new Tuple&lt;bool, car=&#34;&#34;&gt;(doesExist, car);&#xA;        }&#xA;&#xA;        public Car GetSingle(Func&lt;car, bool=&#34;&#34;&gt; predicate) &#xA;        {&#xA;            Thread.Sleep(500);&#xA;            return _database.Values.FirstOrDefault(predicate);&#xA;        }&#xA;&#xA;        public Car Add(Car car) &#xA;        {&#xA;            Thread.Sleep(500);&#xA;            lock(_idLock) &#xA;            {&#xA;                car.Id = _nextId;&#xA;                _database.TryAdd(car.Id, car);&#xA;                _nextId++;&#xA;            }&#xA;&#xA;            return car;&#xA;        }&#xA;&#xA;        public bool TryRemove(int id) &#xA;        {&#xA;            Thread.Sleep(500);&#xA;&#xA;            Car removedCar;&#xA;            return _database.TryRemove(id, out removedCar);&#xA;        }&#xA;&#xA;        public bool TryUpdate(Car car) &#xA;        {&#xA;            Thread.Sleep(500);&#xA;&#xA;            Car oldCar;&#xA;            if (_database.TryGetValue(car.Id, out oldCar)) {&#xA;&#xA;                return _database.TryUpdate(car.Id, car, oldCar);&#xA;            }&#xA;&#xA;            return false;&#xA;        }&#xA;    }&#xA;}&lt;font color=&#34;#000000&#34; face=&#34;-apple-system, system-ui, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji&#34;&gt;&lt;span style=&#34;font-size: 16px; white-space: normal;&#34;&gt;&#xA;&lt;/span&gt;&lt;/font&gt;&lt;/car,&gt;&lt;/bool,&gt;&lt;/bool,&gt;&lt;/car,&gt;&lt;/car&gt;&lt;/car&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/keyvaluepair&lt;int,&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/car&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Before going further, let&#39;s install PostSharp through NuGet. The first thing you want to install is &lt;a href=&#34;https://www.nuget.org/packages/PostSharp&#34; target=&#34;_blank&#34;&gt;PostSharp NuGet package&lt;/a&gt; which magically hooks into the compilation step thanks to its custom MSBuild scripts. The other package here will be &lt;a href=&#34;https://www.nuget.org/packages/PostSharp.Patterns.Diagnostics&#34; target=&#34;_blank&#34;&gt;PostSharp.Patterns.Diagnostics&lt;/a&gt; as I want to show you a logging example first.&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;dotnet add package PostSharp&#xA;dotnet add package PostSharp.Patterns.Diagnostics&#xA;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Let&#39;s get the sample code from the &lt;a href=&#34;https://doc.postsharp.net/add-logging?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;logging documentation&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;using PostSharp.Patterns.Diagnostics;&#xA;using PostSharp.Extensibility;&#xA;&#xA;[assembly: Log(AttributePriority = 1, AttributeTargetMemberAttributes = MulticastAttributes.Protected | MulticastAttributes.Internal | MulticastAttributes.Public)]&#xA;[assembly: Log(AttributePriority = 2, AttributeExclude = true, AttributeTargetMembers = &#34;get_*&#34; )]&lt;font color=&#34;#000000&#34; face=&#34;-apple-system, system-ui, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji&#34;&gt;&lt;span style=&#34;font-size: 16px; white-space: normal;&#34;&gt;&#xA;&lt;/span&gt;&lt;/font&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;When you run the application now, you will be impressed and probably also be blown away by how much value and observability you get with a very little work!&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/20190504104919-266c1533-a4c2-4e39-bb68-b03d7a67c788-image.png&#34;&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/20190504105012-f2342e91-3912-41af-8491-e8576b9c7b6c-image.png&#34;&gt;&lt;br&gt;&lt;/p&gt;&lt;h3&gt;PostSharp Caching Example&lt;/h3&gt;&lt;p&gt;The main reason for me to explore PostSharp is for caching and this is where &lt;a href=&#34;https://www.postsharp.net/caching?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;PostSharp Caching&lt;/a&gt; shines really. Let&#39;s run our sample application again and perform a mini load test on it.&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;1..10 | foreach {write-host &#34;$([Math]::Round((Measure-Command -Expression { Invoke-WebRequest -Uri http://localhost:5000/cars }).TotalMilliseconds, 1))&#34;}&lt;font color=&#34;#000000&#34; face=&#34;-apple-system, system-ui, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji&#34;&gt;&lt;span style=&#34;font-size: 16px; white-space: normal;&#34;&gt;&#xA;&lt;/span&gt;&lt;/font&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;You will notice that each call to the &#34;/cars&#34; endpoint takes more than 500ms, which is fair due to us sleeping that amount of time on purpose. However, this could well be the case when you connect to a data store in a real world example. Even if your data store is performant and gets the result instantly, we are still wasting resources here because the data hasn&#39;t changed and we would be doing an unnecessary trip to the database to get the data which we have already retrieved previously.&lt;/p&gt;&lt;p&gt;Caching is the solution to this problem. However, it&#39;s not really easy to get right on your own in a web application which is multithreaded in its nature. You can use built-in APIs such as &lt;a href=&#34;https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-2.2&#34; target=&#34;_blank&#34;&gt;the ones come from ASP.NET Core&lt;/a&gt; but you then need to express your caching requirements in code, in a verbose way which will make it hard to understand the business logic behind a cluttered codebase and suddenly, you will be struggling to add or modify functionality in an existing software.&lt;/p&gt;&lt;p&gt;Let&#39;s see how PostSharp can help us here. First, we need to add the caching support by installing &lt;a href=&#34;https://www.nuget.org/packages/PostSharp.Patterns.Caching&#34; target=&#34;_blank&#34;&gt;PostSharp.Patterns.Caching&lt;/a&gt; NuGet package.&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;dotnet add package PostSharp.Patterns.Caching&lt;font color=&#34;#000000&#34; face=&#34;-apple-system, system-ui, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji&#34;&gt;&lt;span style=&#34;font-size: 16px; white-space: normal;&#34;&gt;&#xA;&lt;/span&gt;&lt;/font&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Then, we need to make some changes to our code to enable caching. Here is the git patch which shows you what exactly I have changed:&lt;br&gt;&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;From a20fc8e95ffd9bf5d424467e0e1283ae5891454a Mon Sep 17 00:00:00 2001&#xA;From: Tugberk Ugurlu&#xA;Date: Tue, 9 Apr 2019 23:38:32 +0100&#xA;Subject: [PATCH] add caching&#xA;&#xA; postsharp/0-caching/1-sample-web/1-sample-web.csproj | 1 +&#xA; postsharp/0-caching/1-sample-web/Program.cs          | 3 +++&#xA; postsharp/0-caching/1-sample-web/Startup.cs          | 4 +++-&#xA; 3 files changed, 7 insertions(+), 1 deletion(-)&#xA;&#xA;diff --git a/postsharp/0-caching/1-sample-web/1-sample-web.csproj b/postsharp/0-caching/1-sample-web/1-sample-web.csproj&#xA;index bd55b6c..008c486 100644&#xA;--- a/postsharp/0-caching/1-sample-web/1-sample-web.csproj&#xA;+++ b/postsharp/0-caching/1-sample-web/1-sample-web.csproj&#xA;@@ -10,6 +10,7 @@&#xA;     &lt;packagereference include=&#34;Microsoft.AspNetCore.App&#34;&gt;&lt;/packagereference&gt;&#xA;     &lt;packagereference include=&#34;Microsoft.AspNetCore.Razor.Design&#34; version=&#34;2.2.0&#34; privateassets=&#34;All&#34;&gt;&lt;/packagereference&gt;&#xA;     &lt;packagereference include=&#34;PostSharp&#34; version=&#34;6.1.17&#34;&gt;&lt;/packagereference&gt;&#xA;+    &lt;packagereference include=&#34;PostSharp.Patterns.Caching&#34; version=&#34;6.1.17&#34;&gt;&lt;/packagereference&gt;&#xA;     &lt;packagereference include=&#34;PostSharp.Patterns.Diagnostics&#34; version=&#34;6.1.17&#34;&gt;&lt;/packagereference&gt;&#xA;   &#xA; &#xA;diff --git a/postsharp/0-caching/1-sample-web/Program.cs b/postsharp/0-caching/1-sample-web/Program.cs&#xA;index 3dcae2c..9d241eb 100644&#xA;--- a/postsharp/0-caching/1-sample-web/Program.cs&#xA;+++ b/postsharp/0-caching/1-sample-web/Program.cs&#xA;@@ -7,6 +7,8 @@ using Microsoft.AspNetCore;&#xA; using Microsoft.AspNetCore.Hosting;&#xA; using Microsoft.Extensions.Configuration;&#xA; using Microsoft.Extensions.Logging;&#xA;+using PostSharp.Patterns.Caching;&#xA;+using PostSharp.Patterns.Caching.Backends;&#xA; using PostSharp.Patterns.Diagnostics;&#xA; using PostSharp.Patterns.Diagnostics.Backends.Console;&#xA; &#xA;@@ -18,6 +20,7 @@ namespace _1_sample_web&#xA;         public static void Main(string[] args)&#xA;         {&#xA;             LoggingServices.DefaultBackend = new ConsoleLoggingBackend();&#xA;+            CachingServices.DefaultBackend = new MemoryCachingBackend();&#xA;             CreateWebHostBuilder(args).Build().Run();&#xA;         }&#xA; &#xA;diff --git a/postsharp/0-caching/1-sample-web/Startup.cs b/postsharp/0-caching/1-sample-web/Startup.cs&#xA;index 18b3dbc..bed37ca 100644&#xA;--- a/postsharp/0-caching/1-sample-web/Startup.cs&#xA;+++ b/postsharp/0-caching/1-sample-web/Startup.cs&#xA;@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Hosting;&#xA; using Microsoft.AspNetCore.Http;&#xA; using Microsoft.AspNetCore.Mvc;&#xA; using Microsoft.Extensions.DependencyInjection;&#xA;+using PostSharp.Patterns.Caching;&#xA; &#xA; namespace _1_sample_web&#xA; {&#xA;@@ -115,7 +116,8 @@ namespace _1_sample_web&#xA;             new KeyValuePair&lt;int, car=&#34;&#34;&gt;(7, new Car { Id = 7, Make = &#34;Make7&#34;, Model = &#34;Model5&#34;, Year = 2001, Price = 78355.92F }),&#xA;             new KeyValuePair&lt;int, car=&#34;&#34;&gt;(8, new Car { Id = 8, Make = &#34;Make8&#34;, Model = &#34;Model1&#34;, Year = 2011, Price = 1823223.23F })&#xA;         });&#xA;&#xA;+        [Cache]&#xA;         public IEnumerable&lt;car&gt; GetAll()&#xA;         {&#xA;             Thread.Sleep(500);&#xA;-- &#xA;2.15.2 (Apple Git-101.1)&lt;font color=&#34;#000000&#34; face=&#34;-apple-system, system-ui, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji&#34;&gt;&lt;span style=&#34;font-size: 16px; white-space: normal;&#34;&gt;&#xA;&lt;/span&gt;&lt;/font&gt;&lt;/car&gt;&lt;/int,&gt;&lt;/int,&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Couple of things we have done here:&lt;br&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;In our entry point, we configured the cache backend we wanted to use which in our case is the &lt;a href=&#34;https://doc.postsharp.net/caching-memory?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;MemoryCache&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;We marked the CarContext.GetAll method with the &lt;a href=&#34;https://doc.postsharp.net/t_postsharp_patterns_caching_cacheattribute?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;CacheAttribute&lt;/a&gt;.&lt;br&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Believe it or not, this is pretty much it! When we run the sample mini load test, you will see the dramatic difference even if we are seeing a higher response time on the first load.&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/20190504110040-34404b0c-2b63-430f-921c-6eae974388d5-image.png&#34;&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Again, very little work but tremendous gain in terms of value!&lt;/p&gt;&lt;p&gt;We have improved our performance drastically but introduced a very nasty problem now: serving stale data. Thankfully, &lt;a href=&#34;https://doc.postsharp.net/caching-invalidation?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;PostSharp has a solution to cache invalidation&lt;/a&gt; out of the box without losing our declarative nature for simple cases. For this, we need to use &lt;a href=&#34;https://doc.postsharp.net/t_postsharp_patterns_caching_invalidatecacheattribute?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;InvalidateCacheAttribute&lt;/a&gt; aspect. When this attribute is applied to a method, it causes any call to this method to remove from the cache the value of one or more other methods. It’s worth noting that the cached methods are matched, by type and name, against the parameters of the invalidating method. PostSharp compilation takes care of the rest during the build step to set up all the invalidation logic.&lt;/p&gt;&lt;p&gt;For example, the below changes makes it possible for us to invalidate the cache of a single car entity for example when it’s updated.&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;From f0889e68e55298e43360e01dd3b0e8b1cf6468e3 Mon Sep 17 00:00:00 2001&#xA;From: Tugberk Ugurlu&#xA;Date: Tue, 30 Apr 2019 09:40:21 +0100&#xA;Subject: [PATCH] cache invalidation, declarative&#xA;&#xA; postsharp/0-caching/1-sample-web/Startup.cs | 6 ++++--&#xA; 1 file changed, 4 insertions(+), 2 deletions(-)&#xA;&#xA;diff --git a/postsharp/0-caching/1-sample-web/Startup.cs b/postsharp/0-caching/1-sample-web/Startup.cs&#xA;index bed37ca..ec95d1e 100644&#xA;--- a/postsharp/0-caching/1-sample-web/Startup.cs&#xA;+++ b/postsharp/0-caching/1-sample-web/Startup.cs&#xA;@@ -62,7 +62,7 @@ namespace _1_sample_web&#xA;         public IActionResult PutCar(int id, Car car) &#xA;         {&#xA;             car.Id = id;&#xA;-            if (!_carsCtx.TryUpdate(car)) &#xA;+            if (!_carsCtx.TryUpdate(id, car)) &#xA;             {&#xA;                 return NotFound();&#xA;             }&#xA;@@ -130,6 +130,7 @@ namespace _1_sample_web&#xA;             return _database.Values.Where(predicate);&#xA;         }&#xA; &#xA;+        [Cache]&#xA;         public Tuple&lt;bool, car=&#34;&#34;&gt; GetSingle(int id) &#xA;         {&#xA;             Thread.Sleep(500);&#xA;@@ -166,7 +167,8 @@ namespace _1_sample_web&#xA;             return _database.TryRemove(id, out removedCar);&#xA;         }&#xA; &#xA;-        public bool TryUpdate(Car car) &#xA;+        [InvalidateCache(nameof(GetSingle))]&#xA;+        public bool TryUpdate(int id, Car car) &#xA;         {&#xA;             Thread.Sleep(500);&#xA; &#xA;-- &#xA;2.20.1 (Apple Git-117)&lt;font color=&#34;#000000&#34; face=&#34;-apple-system, system-ui, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji&#34;&gt;&lt;span style=&#34;font-size: 16px; white-space: normal;&#34;&gt;&#xA;&lt;/span&gt;&lt;/font&gt;&lt;/bool,&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;However, this only invalidates the GetSingle method and we still have problem of serving stale data from GetAll method. There is also an ability out of the box to to &lt;a href=&#34;https://doc.postsharp.net/caching-invalidation#ID2RBSection?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;imperatively invalidate an item from the cache&lt;/a&gt; which is very handy for cases where we cannot simply invalidate the cache purely based on method signature. You can see below an example of how this looks like.&amp;nbsp;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&#xA;&lt;/p&gt;&lt;pre&gt;From f629b295fc8f9bbd44904284cb0ec832d51185be Mon Sep 17 00:00:00 2001&#xA;From: Tugberk Ugurlu&#xA;Date: Tue, 30 Apr 2019 09:55:44 +0100&#xA;Subject: [PATCH] cache invalidation, imperatively&#xA;&#xA; postsharp/0-caching/1-sample-web/Startup.cs | 4 ++++&#xA; 1 file changed, 4 insertions(+)&#xA;&#xA;diff --git a/postsharp/0-caching/1-sample-web/Startup.cs b/postsharp/0-caching/1-sample-web/Startup.cs&#xA;index ec95d1e..8ee6652 100644&#xA;--- a/postsharp/0-caching/1-sample-web/Startup.cs&#xA;+++ b/postsharp/0-caching/1-sample-web/Startup.cs&#xA;@@ -67,6 +67,10 @@ namespace _1_sample_web&#xA;                 return NotFound();&#xA;             }&#xA; &#xA;+            CachingServices.Invalidation.Invalidate(&#xA;+                typeof(CarsContext).GetMethod(nameof(CarsContext.GetAll)), &#xA;+                _carsCtx);&#xA;+                &#xA;             return Ok(car);&#xA;         }&#xA; &#xA;-- &#xA;2.20.1 (Apple Git-117)&lt;font color=&#34;#000000&#34; face=&#34;-apple-system, system-ui, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji&#34;&gt;&lt;span style=&#34;font-size: 16px; white-space: normal;&#34;&gt;&#xA;&lt;/span&gt;&lt;/font&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;/p&gt;&lt;p&gt;We Invalidate the GetAll method cache on the given CarsContext instance when we have an update on any of the items.&lt;br&gt;&lt;/p&gt;&lt;p&gt;This is all I want to cover on this post in terms of the API surface area of PostSharp and I hope this gives you taste of how simple it’s to get going with PostSharp. &lt;a href=&#34;https://doc.postsharp.net/caching?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;PostSharp Caching documentation&lt;/a&gt; is also very comprehensive and I recommend you to check that out for further details.&lt;br&gt;&lt;/p&gt;&lt;h3&gt;Limitations&lt;/h3&gt;&lt;p&gt;The biggest limitation I have seen with PostSharp is its lack of .NET Core compilation support outside of Windows at the time of writing (you may check the current status &lt;a href=&#34;https://support.postsharp.net/request/20871-support-for-coreclr-as-a-build&#34; target=&#34;_blank&#34;&gt;here&lt;/a&gt;). You can run PostSharp on .NET Core, even outside of Windows. However, you first need a Windows machine to be able to compile your code.&lt;/p&gt;&lt;p&gt;Apart from this, there is also a trade off for you to make with PostSharp which is the increased build time. However, with incremental builds, this additional increase can become noticeable. Besides this, compared to the value you got from the tool, I think this is trade-off which is well worth to be made.&lt;/p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;This post just touches the surface on what you can achieve with PostSharp. In terms of caching for example, there is even a support for &lt;a href=&#34;https://doc.postsharp.net/caching-redis?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;Redis&lt;/a&gt; which is very suitable for horizontally scaled web applications where multiple nodes serve HTTP requests.&lt;/p&gt;&lt;p&gt;PostSharp provides help on many other various patterns such as &lt;a href=&#34;https://doc.postsharp.net/threading-library?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;mutithreading&lt;/a&gt;. You can get started with PostSharp with &lt;a href=&#34;https://www.postsharp.net/essentials?utm_source=blog&amp;amp;utm_medium=tugberk&amp;amp;utm_campaign=3_2019&#34; target=&#34;_blank&#34;&gt;PostSharp Essentials, the free but project-size-limited edition&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/declarative-coding-approach-to-caching-in--net-core-with-postsharp</guid>
      <pubDate>Sat, 04 May 2019 11:14:56 +0000</pubDate>
    </item>
    <item>
      <title>Software Architecture and System Design - Getting Your Grip and Some Related Resources</title>
      <link>https://www.tugberkugurlu.com/archive/software-architecture-and-system-design---getting-your-grip-and-some-related-resources</link>
      <description>&#xA;&lt;p&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;/p&gt;&lt;div&gt;If you have never been exposed to software software system design challenges, you might be totally lost on even where to begin. I believe in finding the limits to a certain extend first and then start getting your hands dirty. The way you can start this is by finding some interesting product or services (ideally you are a fan of), and learning about their implementations. You will be surprised that how simple they may look, they most probably involve great deal of complexity. Don’t forget:&amp;nbsp;&lt;a href=&#34;https://www.youtube.com/watch?v=k9Zbuuo51go&#34;&gt;simple is usually complex&lt;/a&gt;&amp;nbsp;and that’s OK™.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/20190223142605-178ec251-75c5-4fcc-a9de-ad09e6551073-isaac-smith-1182056-unsplash.jpg&#34;&gt;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Photo by&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://unsplash.com/photos/6EnTPvPPL6I?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Isaac Smith&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;on&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://unsplash.com/search/photos/system-design?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Unsplash&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;I believe the biggest suggestion I can give you while approaching to system design challenges is this: not to assume anything! You should pin down the facts and expectations from this system first. Some good questions to ask here are which will help you start this process:&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;ul&gt;&lt;li&gt;What is the problem you are trying to solve?&amp;nbsp;&lt;/li&gt;&lt;li&gt;What is the the peak&amp;nbsp;volume of users that will interact with your system?&lt;/li&gt;&lt;li&gt;What are&amp;nbsp;the data write and read patterns going to be?&lt;/li&gt;&lt;li&gt;What are the expected failure cases, how do you plan to mitigate them?&lt;/li&gt;&lt;li&gt;What are the availability and consistency expectations?&lt;/li&gt;&lt;li&gt;Do you need to worry about any auditing, regulation aspects?&lt;/li&gt;&lt;li&gt;What type of sensitive data are you going to be storing?&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;These are just a questions few that have worked for me and the teams that I worked with over the years. Once you have answers to these questions (or any other which are relevant to the context you are in), then you should be starting to dive into the technical side of the problem.&lt;/div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;Setting Your Baseline&lt;/h3&gt;&lt;div&gt;&lt;div&gt;What do I mean by the baseline here? Well, in this era of software development, most of the problems &#34;can&#34; be solved by already existing techniques and technologies. Knowing these to a certain extend will give you a head start when you are faced with similar problems. Remember, we are writing software to solve business&#39; and our users&#39; problems and the desire is to do that in a most straight-forward and simple way from a user experience point of view. Why do you need to remember this? It could well be your reality that you should solve problems in unique ways as you might be thinking &#34;what&#39;s the point of me writing software then if I am here to follow a pattern?&#34;. &lt;b&gt;The craft here is in the decision making process to define where to do what&lt;/b&gt;. Surely, we may have challenging, unique problems which we can face at certain times. However, if we have our baseline solid, we will surely know whether we should direct our efforts into finding out ways to solve the problems or further understand the depth of it.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;I believe I have convinced you at this point now that having a solid knowledge on how some of the exciting systems are architecturally shaped is quite critical for you to progress on having some appreciation on the craft and a solid baseline.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;OK, but where to start?&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Donne Martin&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;has a GitHub repo called&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;system-design-primer&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;which helps you learn how to design large-scale systems and also prep for the system design interviews. Inside this, there is a section dedicated to&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/tree/1144b8ef4c441af73f7cbfd51f56b8eb62350a66#real-world-architectures&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;real world architectures&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;which also involves some&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/tree/1144b8ef4c441af73f7cbfd51f56b8eb62350a66#company-architectures&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;system designs of well-known companies&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;such as Twitter, Uber, etc.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;However, before jumping into this, you might want to have some insights on what matters the most in the architectural challenges. This is important because there are A LOT of aspects involved in disambiguating a gnarly, ambiguous problem and solving it within the guidelines of a defined system.&amp;nbsp;&lt;a href=&#34;https://jg.gg/&#34;&gt;Jackson Gabbard&lt;/a&gt;, an ex-Facebook employee, has&amp;nbsp;&lt;a href=&#34;https://www.youtube.com/watch?v=ZgdS0EUmn70&#34;&gt;a 50 mins video on system design interviews&lt;/a&gt;&amp;nbsp;based on his experience on interviewing hundreds of candidates at Facebook. Even if this is focused on the system design interview objective and what success looks like for that, it&#39;s still a very comprehensive resource on what matters the most when it comes to system design. There is also&amp;nbsp;&lt;a href=&#34;https://jg.gg/2016/07/31/architecture-and-systems-design-interview/&#34;&gt;a write-up of this video&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;h3&gt;Start Building up Your Data Storage and Retrieval Knowledge&lt;/h3&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Most of the time, the choice of how you decide to persist and serve data will play a crucial role on the performance of your system. Therefore, you should be able to understand the expectations around data writes and reads about your system first. Then, you should be able to assess these and convert that assessment into a choice. However, you can only do this effectively if you know the existing storage patterns. This essentially means having a good knowledge around&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/#database&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;database choices&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;Databases are really scalable and durable data structures. So, all your knowledge around data structures should be really beneficial around understanding the various database choices. For example,&amp;nbsp;&lt;a href=&#34;https://redis.io/topics/data-types-intro&#34;&gt;Redis&lt;/a&gt;&amp;nbsp;is a&amp;nbsp;data structures server, supporting different kinds of values. It allows you to work with the concept of data strictures such as sets and lists, and provides you to read data through commonly-known algorithms such as&amp;nbsp;&lt;a href=&#34;https://redis.io/topics/lru-cache&#34;&gt;LRU&lt;/a&gt;&amp;nbsp;in a durable and highly available fashion.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/20190223143617-0f50297f-b8e1-481c-b069-dba268ba6b2c-samuel-zeller-118195-unsplash.jpg&#34;&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Photo by&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://unsplash.com/photos/JuFcQxgCXwA?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Samuel Zeller&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;on&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://unsplash.com/search/photos/database?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Unsplash&lt;/a&gt;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Once you get enough grip around the various data storage patterns, it&#39;s now time for you to get into data consistency and&amp;nbsp;availability land.&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/#cap-theorem&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;CAP theorem&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;is the first thing you should try to have a good grip of, which you can polish it off by looking deeper into established&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/#consistency-patterns&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;consistency&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;and&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/#availability-patterns&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;availability&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;patterns. These will allow you to have a wide spectrum when it comes to understanding data writes and reads are really very separate concerns and have separate challenges associated to them. By embracing several consistency and availability patterns, you can gain a lot of performance while serving the data to your applications.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;Finally around data storage needs, you should also be aware of caching. Should it be both on the client and server?&amp;nbsp;&amp;nbsp;What data will you cache?&amp;nbsp;&amp;nbsp;And why?&amp;nbsp;&amp;nbsp;How will you invalidate the cache?&amp;nbsp;&amp;nbsp;(will it be based on time?&amp;nbsp;&amp;nbsp;If so, how long?).&amp;nbsp;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/#cache&#34;&gt;This section of system-design-primer&lt;/a&gt;&amp;nbsp;should be a good starting point on this topic.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;h3&gt;Communication Patterns&lt;/h3&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Systems are composed of various components, which can be different processes living inside the same physical node or different machines sitting at the separate parts in your network. Some of these resources might be private within your network but some needs to be accessed publicly by your consumers.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;These resources needs to be able to communicate between them and to the outside world. In context of system design, this again introduces another set of unique challenges. Understanding how&amp;nbsp;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/#asynchronism&#34;&gt;asynchronous workflows&lt;/a&gt;&amp;nbsp;can help you and what are the&amp;nbsp;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/#communication&#34;&gt;various communication patterns available&lt;/a&gt;&amp;nbsp;such as TCP, UDP, HTTP (which sits on top of TCP),&amp;nbsp;etc. will help you understand the breadth of the problem space and solutions currently available.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/20190223143955-f34828a1-6336-4f9e-8822-8c742b4e046a-tony-stoddard-454601-unsplash.jpg&#34;&gt;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Photo by&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://unsplash.com/photos/DgZxBnZeQo8?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Tony Stoddard&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;on&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://unsplash.com/search/photos/communication?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Unsplash&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;When dealing with communication to the outside world,&amp;nbsp;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer/#security&#34;&gt;security&lt;/a&gt;&amp;nbsp;is always another side-effect that you need to be aware of and actively deal with.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;Connection Distribution&lt;/h3&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;blockquote class=&#34;blockquote&#34;&gt;I am not sure if this logical grouping makes sense here. I will go with it anyway since it’s the closest term that reflects what I want to cover here. &lt;/blockquote&gt;&lt;div&gt;Systems are formed by gluing multiple components together,&amp;nbsp;and how they communicate with each other often is designed through well-established protocols such as TCP and UDP. However, these protocols are often not enough on their own to cover the needs of today’s systems which can have high load and demands from our consumers. We often need ways to be able to distribute connections in order to handle the high load of our system.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Domain_Name_System&#34;&gt;Domain Name System (DNS)&lt;/a&gt;&amp;nbsp;sits at the core of this distribution. A DNS&amp;nbsp;translates a domain name such as &lt;a href=&#34;http://www.example.com&#34;&gt;www.example.com&lt;/a&gt; to an IP address. Besides this, some DNS services can route traffic through various methods such as weighted round robin and latency-based to help distribute the load.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Load_balancing_(computing)&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Load balancing&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;is very vital and nearly every major system on the Web we interact with today sits behind one or multiple load balancers.&amp;nbsp;Load balancers help us distribute incoming client requests to multiple instances of resources. There both hardware and software forms of load balancers but it’s often that you see software based ones used such as&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;http://www.haproxy.org/&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;HAProxy&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;and&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://aws.amazon.com/elasticloadbalancing/&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;ELB&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;.&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer#reverse-proxy-web-server&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Reverse proxies&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;are also very smilar to the concept of load balancing&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://github.com/donnemartin/system-design-primer#load-balancer-vs-reverse-proxy&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;with some distinctive differences&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;though.&amp;nbsp;These differences will have an effect on your choice based the needs.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Content_delivery_network&#34;&gt;Content Delivery Networks (CDN)&lt;/a&gt;&amp;nbsp;are also something which you should be aware of. A CDN&amp;nbsp;is a globally distributed network of proxy servers, serving content from locations closer to the user. CDNs are usually preferred when you are serving static files such as JavaScript, CSS and HTML. It’s also common that you see cloud services offer traffic managers (such as&amp;nbsp;&lt;a href=&#34;https://docs.microsoft.com/en-us/azure/traffic-manager/traffic-manager-overview&#34;&gt;Azure Traffic Manager&lt;/a&gt;) which gives you global distribution and reduced latency benefits for your dynamic content. However, these services are mostly beneficial if you have stateless web services.&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;What About My Business Logic? Structuring Business Logic, Workflows and Components&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Thus far, we talked about all the infrastructure related aspects of a system. These are the parts of your system which your users probably have no idea about and to be frank, they don&#39;t give a damn about them. What they care about is how they interact with your system, what they can achieve by doing so and how the system acts on behalf of them to make certain decisions and process their data.&lt;/div&gt;&lt;/div&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;As you might guess from this post’s title, I intended this blog post to be about software architecture and system design. Therefore, I wasn’t going to cover the software design patterns which are&amp;nbsp;concerned with how the components are built. However, thinking about this more and more, it’s clear to me that the line between them are very blurred and usually both sides are interconnected. Take&amp;nbsp;&lt;a href=&#34;https://martinfowler.com/eaaDev/EventSourcing.html&#34;&gt;Event Sourcing&lt;/a&gt;&amp;nbsp;for example. Once you adopt this software architecture pattern, it pretty much effects most parts of your system; how you persist data, what level consistency you choose for your system’s clients to deal with, how you shape the components within your system, so on and so forth. Therefore, I decided to touch on some of the design and architectural patterns related which directly concerns your business logic. Even if it’s going to be just touching the surface, it should be useful for you have some ideas. Here is a few of them:&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/SOLID&#34;&gt;SOLID principles&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Domain-driven_design&#34;&gt;Domain-driven Design&lt;/a&gt; concepts such as &lt;a href=&#34;https://medium.com/ingeniouslysimple/aggregates-in-domain-driven-design-5aab3ef9901d&#34;&gt;Aggregates&lt;/a&gt;, &lt;a href=&#34;https://medium.com/ingeniouslysimple/command-vs-event-in-domain-driven-design-be6c45be52a9&#34;&gt;Commands and Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://fideloper.com/hexagonal-architecture%0A&#34;&gt;Hexagonal Architecture&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://martinfowler.com/bliki/CQRS.html&#34;&gt;Command Query Responsibility Segregation (CQRS)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://martinfowler.com/eaaDev/EventSourcing.html&#34;&gt;Event Sourcing&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://doc.akka.io/docs/akka/2.5/guide/actors-intro.html&#34;&gt;Actor model&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://medium.com/@ankit.sinhal/mvc-mvp-and-mvvm-design-pattern-6e169567bbad%0A&#34;&gt;MVC, MVVM and MVP design patterns&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;Collaboration Approaches&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;It&#39;s highly unlikely that you are going to be the only one involved in a project where you need to be part of a system design process. Therefore, you need to be able to collaborate with other folks in your team, both inside and outside of your job function. There is also a breadth and depth of this surface area and as the technical leader, you should be able to address the concerns on each level by going into it with a required depth. The activities here may involve evaluating technology choices together or pinning down the business needs and understanding how the work needs to be parallelised.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/20190223145034-84d05466-4058-4683-998d-de2eb886b5ef-kaleidico-754525-unsplash.jpg&#34;&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Photo by&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://unsplash.com/photos/3V8xo5Gbusk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Kaleidico&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;on&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://unsplash.com/search/photos/collaboration?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Unsplash&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;div&gt;First and foremost, you need to have an accurate and shared understanding of what you are trying to achieve as a business goal and what moving parts involved in this aspect.&amp;nbsp;Group modeling techniques such as&amp;nbsp;&lt;a href=&#34;https://techbeacon.com/devops/introduction-event-storming-easy-way-achieve-domain-driven-design&#34;&gt;event storming&lt;/a&gt;&amp;nbsp;are powerful methods to accelerate this process and increases your changes of success. You may get into this process before or after you define your&amp;nbsp;&lt;a href=&#34;https://www.infoq.com/news/2015/02/service-boundaries-healthcare&#34;&gt;service boundaries&lt;/a&gt;, deepening on your product/service maturity stage. Based on the level of alignment you see here, you may want to facilitate a separate activity to define the&amp;nbsp;&lt;a href=&#34;https://martinfowler.com/bliki/UbiquitousLanguage.html&#34;&gt;Ubiquitous Language&lt;/a&gt;&amp;nbsp;for the bounded context you are operating on. When it comes to communicating the architecture of your system, you may find&amp;nbsp;&lt;a href=&#34;https://c4model.com/&#34;&gt;the C4 model for software architecture&lt;/a&gt;&amp;nbsp;from&amp;nbsp;&lt;a href=&#34;https://twitter.com/simonbrown&#34;&gt;Simon Brown&lt;/a&gt;&amp;nbsp;useful, especially when it comes to understanding what level of depth you should go into while visualising what you are trying to convey.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;There are most probably other mature techniques available in this space. However, all will tie back to your domain understanding and your experience and knowledge around&amp;nbsp;&lt;/span&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Domain-driven_design&#34; style=&#34;background-color: rgb(255, 255, 255); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;Domain-driven Design&lt;/a&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&amp;nbsp;will prove to be handy.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&#34;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;; font-size: 1rem;&#34;&gt;&lt;br&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;h3 style=&#34;&#34;&gt;Some Other Resources&lt;/h3&gt;&lt;div style=&#34;&#34;&gt;&lt;div&gt;Here are some resources which may help you. These are not in any particular oder.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=KmAyPUv9gOY&#34;&gt;System Design: How to design Twitter?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=J3DY3Te3A_A&#34;&gt;System Design: Uber Lyft ride sharing services&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://gist.github.com/vasanthk/485d1c25737e8e72759f&#34;&gt;System Design Cheatsheet&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;http://blog.gainlo.co/index.php/2016/03/29/design-news-feed-system-part-1-system-design-interview-questions/&#34;&gt;Design News Feed System (Part 1)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;http://blog.gainlo.co/index.php/2016/04/05/design-news-feed-system-part-2/&#34;&gt;Design News Feed System (Part 2)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://gist.github.com/jboner/2841832&#34;&gt;Latency Numbers Every Programmer Should Know&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=-W9F__D3oY4&#34;&gt;Scalability Harvard Web Development by David Malan&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/heynickc/awesome-ddd&#34; style=&#34;text-decoration-line: underline;&#34;&gt;A curated list of Domain-Driven Design (DDD), Command Query Responsibility Segregation (CQRS), Event Sourcing, and Event Storming resources&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/jetbasrawi/go.cqrs&#34; style=&#34;text-decoration-line: underline;&#34;&gt;A Golang reference implementation of the CQRS pattern&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://github.com/FallibleInc/security-guide-for-developers&#34;&gt;A practical security guide for web developers&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.infoq.com/news/2015/02/service-boundaries-healthcare&#34;&gt;Udi Dahan on Defining Service Boundaries&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://docs.microsoft.com/en-us/azure/architecture/microservices/microservice-boundaries&#34;&gt;Designing microservices: Identifying microservice boundaries&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=0UTOLRTwOX0&#34;&gt;Distributed Sagas: A Protocol for Coordinating Microservices - Caitie McCaffrey&lt;/a&gt;&amp;nbsp;(&lt;a href=&#34;https://speakerdeck.com/caitiem20/distributed-sagas-a-protocol-for-coordinating-microservices&#34;&gt;Slides&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://skillsmatter.com/skillscasts/13062-keynote-deriving-meaning-in-a-time-of-chaos-the-intersection-between-chaos-engineering-and-observability-for-distributed-systems&#34;&gt;Deriving Meaning in a Time of Chaos: The Intersection Between Chaos Engineering and Observability for Distributed Systems&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&#34;https://www.nginx.com/resources/glossary/reverse-proxy-vs-load-balancer/&#34;&gt;What is a Reverse Proxy vs. Load Balancer?&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;/div&gt;&lt;!--?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?--&gt;&#xA;&#xA;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/software-architecture-and-system-design---getting-your-grip-and-some-related-resources</guid>
      <pubDate>Sat, 23 Feb 2019 14:52:48 +0000</pubDate>
    </item>
    <item>
      <title>Pulling an Old Article From the Coffin: SignalR with Redis Running on a Windows Azure Virtual Machine</title>
      <link>https://www.tugberkugurlu.com/archive/pulling-an-old-article-from-the-coffin-signalr-with-redis-running-on-a-windows-azure-virtual-machine</link>
      <description>&#xA;&lt;p&gt;Long time ago (about 5 years, at least), I contributed an article to SignalR wiki about scaling a SignalR application with Redis. You can still find the article &lt;a href=&#34;https://github.com/SignalR/SignalR/wiki/SignalR-with-Redis-Running-on-a-Windows-Azure-Virtual-Machine&#34;&gt;here&lt;/a&gt;. I also blogged about it &lt;a href=&#34;https://www.tugberkugurlu.com/archive/signalr-with-redis-running-on-a-windows-azure-virtual-machine&#34;&gt;here&lt;/a&gt;. However, over time, pictures got lost there. I got a few requests from my readers to refresh those images and I was lucky enough to be able to find them :) I decided to publish that article here so that I would have a much better control over the content. So, here is the post :)&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Please keep in mind that this is a really old post and lots of things have evolved since then. However, I do believe the concepts still resonate and it’s valuable to show the ways of how to achieve this within a cloud provider’s context. &lt;/p&gt;&lt;/blockquote&gt;&#xA;&#xA;&lt;hr&gt;&#xA;&#xA;&lt;h2&gt;SignalR with Redis Running on a Windows Azure Virtual Machine&lt;/h2&gt;&lt;p&gt;This wiki article will walk your through on how you can run your SignalR application in multiple machines with Redis as your backplane using Windows Azure Virtual Machines for scale out scenarios.&lt;h3&gt;Creating the Windows Azure Virtual Machines&lt;/h3&gt;&lt;p&gt;First of all, we will spin up our virtual machines. What we want here is to have two &lt;strong&gt;Windows Server 2008 R2&lt;/strong&gt; virtual machines for our SignalR application and we will name them as &lt;strong&gt;Web1-08R2&lt;/strong&gt; and &lt;strong&gt;Web2-08R2&lt;/strong&gt;. We will have the IIS installed on both of these servers and at the end, we will load balance the request on port 80.&lt;p&gt;Our third virtual machine will be another Windows Server 2008 R2 only for our Redis server. We will call this server &lt;strong&gt;Redis-08R2&lt;/strong&gt;.&lt;p&gt;To spin up the VMs, go to &lt;a href=&#34;https://web.archive.org/web/20140827164959/https://manage.windowsazure.com/&#34;&gt;new Windows Azure Management Portal&lt;/a&gt; and hit New icon at the bottom-right corner.&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/cdc760e3-3290-4c92-8de2-58f7b93c4bcc.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;63&#34; title=&#34;0-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;0-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/94945a68-44ee-4c65-b4d5-966083844c97.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;Creating a virtual machine running Windows Server 2008 R2 is explained &lt;a href=&#34;https://web.archive.org/web/20140827164959/https://www.windowsazure.com/en-us/manage/windows/tutorials/virtual-machine-from-gallery/&#34;&gt;here&lt;/a&gt; in details. We followed the same steps to create our first VM named &lt;strong&gt;Web1-08R2&lt;/strong&gt;.&lt;p&gt;The second VM we will be creating has a slightly different approach than the first one. Under the hood, every virtual machine is a cloud service instance and we want to put our second VM (Web2-08R2) under the same cloud service that our first web VM is running under. To do that, we need to follow the same steps as explained inside the previously mentioned article but when we come to 3rd step in the creation wizard, we should chose &lt;strong&gt;Connect to existing Virtual Machine&lt;/strong&gt; option this time and we should choose our first VM we have just created.&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/70193b30-29e9-46cd-b2de-64248a66e07a.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;467&#34; title=&#34;1-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732d417a7572652d5&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;1-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732d417a7572652d5&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/c6dd5dd9-3f7d-474d-948e-fff2fab6ba2b.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;As the last step, we now need to create our redis VM which will be named Redis-08R2. We will follow the same steps as we did when we were creating our second web VM (Web2-08R2).&lt;h3&gt;Setting Up Redis as a Windows Service&lt;/h3&gt;&lt;p&gt;To use Redis on a Windows machine, we went to &lt;a href=&#34;https://web.archive.org/web/20140827164959/https://github.com/MSOpenTech/redis&#34;&gt;Redis on Windows prototype&lt;/a&gt; GitHub page and cloned the repository and followed the steps explained under &lt;a href=&#34;https://web.archive.org/web/20140827164959/https://github.com/MSOpenTech/redis#how-to-build-redis-using-visual-studio&#34;&gt;How to build Redis using Visual Studio&lt;/a&gt; section.&lt;p&gt;After you build the project, you will have all the files you need under &lt;em&gt;msvs\bin\release&lt;/em&gt; path as zip files. &lt;strong&gt;redisbin.zip&lt;/strong&gt; file will contain the redis server, redis command line interface and some other stuff. &lt;strong&gt;rediswatcherbin.zip&lt;/strong&gt; file will contain the msi file to install redis as a windows service. You can just copy those zip files to your Redis VM and extract redisbin.zip under &lt;em&gt;c:\redis\bin&lt;/em&gt;. Then follow the steps:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Currently, &lt;a href=&#34;https://web.archive.org/web/20140827164959/https://github.com/MSOpenTech/redis/issues/1#issuecomment-6722716&#34;&gt;there is a bug in the RedisWatcher installer&lt;/a&gt; and if you don&#39;t have &lt;a href=&#34;https://web.archive.org/web/20140827164959/http://www.microsoft.com/en-us/download/details.aspx?id=5555&#34;&gt;Microsoft Visual C++ 2010 Redistributable Package&lt;/a&gt; installed on your machine, the service won&#39;t start. So, I installed it first.&lt;/p&gt;&lt;li&gt;&lt;p&gt;Copy this &lt;strong&gt;&lt;a href=&#34;https://web.archive.org/web/20140827164959/https://github.com/MSOpenTech/redis/blob/bksavecow/redis.conf&#34;&gt;redis.conf&lt;/a&gt;&lt;/strong&gt; file and put it under &lt;strong&gt;c:\redis\bin&lt;/strong&gt; directory. Open it up and add a password by adding the following line of code:&lt;p&gt;requirepass 1234567&lt;blockquote&gt;&lt;p&gt;Take this note into considiration when you are setting up your redis password:&lt;p&gt;&lt;em&gt;Warning: since Redis is pretty fast an outside user can try up to 150k passwords per second against a good box. This means that you should use a very strong password otherwise it will be very easy to break.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;li&gt;&lt;p&gt;Then, extract the &lt;strong&gt;rediswatcherbin.zip&lt;/strong&gt; somewhere and run the &lt;strong&gt;InstallWatcher.msi&lt;/strong&gt;to install the service.&lt;/p&gt;&lt;li&gt;&lt;p&gt;Navigate to &lt;em&gt;C:\Program Files (x86)\RedisWatcher&lt;/em&gt; directory. You will see a file named &lt;em&gt;watcher.conf&lt;/em&gt; inside this directory. Open this file up and replace the entire file with the following text. Only difference here is that we are supplying the &lt;strong&gt;redis.conf&lt;/strong&gt; file directory for the server to use:&lt;pre&gt;&lt;code&gt;exepath c:\redis\bin&#xA;exename redis-server.exe&#xA;&#xA;{&#xA; workingdir c:\redis\inst1&#xA; runmode hidden&#xA; saveout 1&#xA; cmdparms c:\redis\bin\redis.conf&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;li&gt;&lt;p&gt;Create a folder named &lt;strong&gt;inst1&lt;/strong&gt; under &lt;em&gt;c:\redis&lt;/em&gt; because we have specified this folder as working directory for our redis instance.&lt;/p&gt;&lt;li&gt;&lt;p&gt;When you do a search against windows services in PowerShell, you will see &lt;strong&gt;RedisWatcherSvc&lt;/strong&gt; service is installed.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/35330ca8-3686-42a0-be49-3dc10ee6f875.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;152&#34; title=&#34;2-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;2-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/f9576b81-b03e-42ca-a8b1-d1e2cf3ae2bb.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Run the following PowerShell command to start the service for the first time.&lt;pre&gt;&lt;code&gt;(Get-Service -Name RedisWatcherSvc).Start()&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Now we have a Redis server running on our VM. To test if it is actually running, open up a windows command window under c:\redis\bin and run the following command (assuming you set your password 1234567):&lt;pre&gt;&lt;code&gt;redis-cli -h localhost -p 6379 -a 1234567&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, you have a redis client running.&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/919a77db-d4c7-4592-9fb4-e00fe7fbffd7.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;181&#34; title=&#34;3-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;3-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/1880730f-ea20-4afa-895f-5448713a852c.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;Ping the redis to see if you are really authenticated:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/0bbe5e34-4901-474c-9e2d-239a0ae55f82.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;170&#34; title=&#34;4-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;4-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/ef5bd337-b5c5-4bb0-9ef0-ef1fc2b88694.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;Now, we are nearly set. As a last step in our redis server, we need to open up TCP port 6379 for external communication. You can do this under &lt;strong&gt;Windows Firewall with Advanced Security&lt;/strong&gt; window as explained &lt;a href=&#34;https://web.archive.org/web/20140827164959/http://www.2x.com/blog/2010/04/tech/configuring-the-windows-server-2008-r2-firewall-to-open-ports-for-2x-solutions/&#34;&gt;here&lt;/a&gt;.&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/434c762d-aa78-4b86-a683-b502e5a562bb.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;185&#34; title=&#34;5-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;5-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/4606a80e-d23c-4ca5-873c-fa563bf51c71.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;h3&gt;Communicating Through Internal Endpoints Between Windows Azure Virtual Machines Under Same Cloud Service&lt;/h3&gt;&lt;p&gt;When you are inside one of your web VMs, you can simply look up the redis VM by hostname.&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/4a0693a0-185d-42be-92e4-2bfe9857a280.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;168&#34; title=&#34;6-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;6-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/b166b196-aaf4-4488-b018-112c8b3dcbf6.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;The hostname will resolve to DIP (Dynamic IP Address) which Windows Azure will use internally. We can configure public endpoints through Windows Azure Management Portal easily but in that case, we would be opening redis to the whole world. Also, if we communicate to our redis server through VIP (Virtual IP Address), we would always go through the load balancer which has its own additional cost.&lt;p&gt;So, we can easily connect to our redis server from any other connected VM by hostname.&lt;h3&gt;The SignalR Application with Redis&lt;/h3&gt;&lt;p&gt;Our SignalR application will not be that much different from a normal SignalR application thanks to &lt;a href=&#34;https://web.archive.org/web/20140827164959/https://github.com/SignalR/SignalR.Redis&#34;&gt;SignalR.Redis&lt;/a&gt; project. All you need to do is to add the &lt;a href=&#34;https://web.archive.org/web/20140827164959/http://nuget.com/packages/SignalR.Redis&#34;&gt;SignalR.Redis&lt;/a&gt; nuget package into your application and configure SignalR to use Redis as the message bus inside the &lt;code&gt;Application_Start&lt;/code&gt; method in &lt;em&gt;Global.asax.cs&lt;/em&gt; file:&lt;pre&gt;protected void Application_Start(object sender, EventArgs e)&#xA;{&#xA;    // Hook up redis&#xA;    string server = ConfigurationManager.AppSettings[&#34;redis.server&#34;];&#xA;    string port = ConfigurationManager.AppSettings[&#34;redis.port&#34;];&#xA;    string password = ConfigurationManager.AppSettings[&#34;redis.password&#34;];&#xA;&#xA;    GlobalHost.DependencyResolver.UseRedis(server, Int32.Parse(port), password, &#34;SignalR.Redis.Sample&#34;);&#xA;}&#xA;&lt;/pre&gt;&lt;p&gt;For our demo, the AppSettings should look like as below:&lt;pre&gt;&amp;lt;appSettings&amp;gt;&#xA;    &amp;lt;add key=&#34;redis.server&#34; value=&#34;Redis-08R2&#34; /&amp;gt;&#xA;    &amp;lt;add key=&#34;redis.port&#34; value=&#34;6379&#34; /&amp;gt;&#xA;    &amp;lt;add key=&#34;redis.password&#34; value=&#34;1234567&#34; /&amp;gt;&#xA;&amp;lt;/appSettings&amp;gt;&#xA;&lt;/pre&gt;&lt;p&gt;I put the application under IIS on our both web servers (Web1-08R2 and Web2-08R2) and configured them to run under .NET Framework 4.0 integrated application pool.&lt;blockquote&gt;&lt;p&gt;For this demo, I am using the &lt;a href=&#34;https://web.archive.org/web/20140827164959/https://github.com/SignalR/SignalR.Redis/tree/master/Redis.Sample&#34;&gt;Redis.Sample&lt;/a&gt; chat application included inside the SignalR.Redis project.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Let&#39;s test them quickly before going public. I fired the both web applications inside the servers and here is the result:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/ab1c221c-7f0f-4859-90d9-465dbf13ee36.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;336&#34; title=&#34;7-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;7-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/050d8fff-caf5-4984-81d5-28266470a11e.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;Perfectly running! Let&#39;s open them up to the world.&lt;h3&gt;Opening up the Port 80 and Load Balancing the Requets&lt;/h3&gt;&lt;p&gt;Our requirement here is to make our application reachable over HTTP and at the same time, we want to load balance the request between our two web servers.&lt;p&gt;To do that, we need to go to Windows Azure Management portal and set up the TCP endpoints for port 80.&lt;p&gt;First, we navigate to dashboard of our Web1-08R2 VM and hit Endpoints from the dashboard menu:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/359ea1a5-e759-4279-9dc2-c8026141705e.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;265&#34; title=&#34;8-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;8-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/b3721b44-caa8-482f-8d7f-f96eaf0bfcc9.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;From there, hit the End Endpoint icon at the bottom of the page:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/7c3a397c-78ae-45b9-8b84-116d3f55a5ec.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;67&#34; title=&#34;9-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;9-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/5b8f10ba-210b-4ad8-bb64-960d360329c3.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;A wizard is going to appear on the screen:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/65375f01-a36f-4015-b6b0-2305421452b7.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;467&#34; title=&#34;10-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;10-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/f2591e3d-ff31-4b04-9504-5d90516b0bd1.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;Click the right-arrow icon and go to next step which is the last one and we will enter the port details there:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/59731054-1f16-4c9f-a46e-4abc8ac48499.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;462&#34; title=&#34;11-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;11-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/f159832a-692f-48f5-966a-c2b40369d2e2.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;After that, our endpoint will be created:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/5ff157f9-ef32-41d6-a795-f079b43018ee.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;165&#34; title=&#34;12-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;12-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/0e64df4f-ae98-4000-899b-6dfcb69db4cd.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;Follow the same steps of Web2-08R2 VM as well and open the &lt;em&gt;Add Endpoint&lt;/em&gt; wizard. This time, we will be able to select Load-balance traffic on an existing port. Chose the previously created port and continue:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/2bcadc2e-d3b6-4073-9305-ca218b4e5e66.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;467&#34; title=&#34;13-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;13-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/0224e8a1-52e6-4e13-9ca5-c7c381230b9a.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;At the last step, enter the proper details and hit save:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/7b11591a-2d68-46e3-ace1-a4736d42884c.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;462&#34; title=&#34;14-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;14-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/fc528db4-5255-4082-b527-c3bc360aaf43.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;We will see our new endpoint is being crated but this time &lt;strong&gt;Load Balanced&lt;/strong&gt; column indicates &lt;strong&gt;Yes&lt;/strong&gt;.&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/fb98d3c1-442a-46e5-ab71-0e2d166b477c.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;178&#34; title=&#34;15-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;15-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/4cdb64d4-2dd1-46a5-a690-9a5fa3b6c177.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;As we configured our web applications without a host name and they are exposed through port 80, we can directly run reach our application through the URL or Public Virtual IP Address (VIP) which is provided to us. When we run our application, we should see it running as below:&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/ce2aa6ef-2021-41f7-b23c-dbe48994e649.png&#34;&gt;&lt;img width=&#34;644&#34; height=&#34;346&#34; title=&#34;16-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; style=&#34;display: inline; background-image: none;&#34; alt=&#34;16-687474703a2f2f7475676265726b2e626c6f622e636f72652e77696e646f77732e6e65742f7475676265726b756775726c752d626c6f672f53696e67616c522d776974682d52656469732d52756e6e696e672d6f6e2d612d57696e646f77732&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/befe42d5-9931-46f3-b499-7bbeb78a78ce.png&#34; border=&#34;0&#34;&gt;&lt;/a&gt;&lt;p&gt;No matter which server it goes, the message will be broadcasted to every client because we will be using Redis as a message bus.&lt;h3&gt;&lt;a name=&#34;user-content-references&#34; href=&#34;https://web.archive.org/web/20140827164959/https://github.com/SignalR/SignalR/wiki/SignalR-with-Redis-Running-on-a-Windows-Azure-Virtual-Machine#references&#34;&gt;&lt;/a&gt;References&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20140827164959/https://github.com/MSOpenTech/redis&#34;&gt;Redis on Windows prototype&lt;/a&gt;&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20140827164959/https://www.windowsazure.com/en-us/manage/windows/how-to-guides/custom-create-a-vm/&#34;&gt;How to Create a Custom Virtual Machine&lt;/a&gt;&lt;li&gt;&lt;a href=&#34;https://web.archive.org/web/20140827164959/https://www.windowsazure.com/en-us/manage/windows/how-to-guides/setup-endpoints/&#34;&gt;How to Set Up Communication with a Virtual Machine&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;  &#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/pulling-an-old-article-from-the-coffin-signalr-with-redis-running-on-a-windows-azure-virtual-machine</guid>
      <pubDate>Wed, 08 Aug 2018 14:32:00 +0000</pubDate>
    </item>
    <item>
      <title>Graph Depth-First Search (DFS)</title>
      <link>https://www.tugberkugurlu.com/archive/graph-depth-first-search-dfs</link>
      <description>&#xA;&lt;p&gt;A while ago, I have written up on &lt;a href=&#34;https://www.tugberkugurlu.com/archive/understanding-graphs-and-their-application-on-software-systems&#34;&gt;Graphs&lt;/a&gt; and gave a few examples about their application for real world problems. I absolutely love graphs as they are so powerful to model the data for several key computer science problems. In this post, I want to talk about one of the most common graph algorithms, &lt;a href=&#34;https://en.wikipedia.org/wiki/Depth-first_search&#34;&gt;Depth-first search&lt;/a&gt; (DFS) and how and where it could be useful.&lt;/p&gt; &lt;h2&gt;What is Depth-First Search (DFS)?&lt;/h2&gt; &lt;p&gt;DFS is a specific algorithm for traversing and searching a graph data structure. Depending on the type of graph, the algorithm might differ. However, the idea is actually quite simple for a &lt;a href=&#34;https://en.wikipedia.org/wiki/Directed_acyclic_graph&#34;&gt;Directed Acyclic Graph (DAG)&lt;/a&gt;: &lt;ol&gt; &lt;li&gt;You start with a source vertex (let&#39;s call it &#34;S&#34;) &lt;li&gt;You visit the first neighbour vertex of that node (let&#39;s call this &#34;N&#34;) &lt;li&gt;You do the same for &#34;N&#34; and you keep going till you end up at a leaf vertex (L) (which is a vertex that has no edges to another vertex) &lt;li&gt;Then you visit the second neighbour of L&#39;s parent vertex. &lt;li&gt;You would be once you exhaust all the vertices.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;I must admit that this is a bit simplified version of the algorithm even for a DAG. For instance, we didn&#39;t touch on the fact that we might end up actually visiting the same vertex multiple times if we don&#39;t take this into account in our algorithm. There is a really good visualization of this algorithm &lt;a href=&#34;https://www.cs.usfca.edu/~galles/visualization/DFS.html&#34;&gt;here&lt;/a&gt; where you can observe how the algorithm works in a visual way through a logical graph representation. &lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/0e5556cc-36ba-4ef8-abb8-2961a843348c.jpg&#34;&gt;&lt;img title=&#34;Picture2&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Picture2&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/bd09c753-df98-4276-bfbd-2f29007011c5.jpg&#34; width=&#34;644&#34; height=&#34;458&#34;&gt;&lt;/a&gt; &lt;h2&gt;Application of Depth-First Search&lt;/h2&gt; &lt;p&gt;There are various applications of DFS which are used to solve particular problems such as &lt;a href=&#34;https://en.wikipedia.org/wiki/Topological_sorting&#34;&gt;Topological Sorting&lt;/a&gt; and &lt;a href=&#34;https://www.geeksforgeeks.org/detect-cycle-in-a-graph/&#34;&gt;detecting cycle in a graph&lt;/a&gt;. There are also occasions where DFS is used as part of another known algorithm to solve a real world problem. One example to that is the &lt;a href=&#34;https://www.geeksforgeeks.org/tarjan-algorithm-find-strongly-connected-components/&#34;&gt;Tarjan’s Algorithm to find Strongly Connected Components&lt;/a&gt;. &lt;p&gt;&lt;a href=&#34;https://www.geeksforgeeks.org/applications-of-depth-first-search/&#34;&gt;This&lt;/a&gt; is also a good resource which lists out different real world applications of DFS. &lt;h2&gt;Other Graph Traversal Algorithms&lt;/h2&gt; &lt;p&gt;As you might guess, DFS is not the only known algorithm in order to traverse a graph data structure. &lt;a href=&#34;https://en.wikipedia.org/wiki/Breadth-first_search&#34;&gt;Breadth&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Breadth-first_search&#34;&gt;-First Search (BFS)&lt;/a&gt; is a another most known graph traversal algorithm which has the similar semantics to DFS but instead of going in depth on a vertex, it prefers visit the all the neighbors of the current vertex. &lt;a href=&#34;https://en.wikipedia.org/wiki/Bidirectional_search&#34;&gt;Bidirectional search&lt;/a&gt; is another one of the traversal algorithms which is mainly used to find a shortest path from an initial vertex to a goal vertex in a directed graph.    &#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/graph-depth-first-search-dfs</guid>
      <pubDate>Sat, 28 Jul 2018 12:32:00 +0000</pubDate>
    </item>
    <item>
      <title>Setting up a MongoDB Replica Set with Docker and Connecting to It With a .NET Core App</title>
      <link>https://www.tugberkugurlu.com/archive/setting-up-a-mongodb-replica-set-with-docker-and-connecting-to-it-with-a--net-core-app</link>
      <description>&#xA;&lt;p&gt;Easily setting up realistic non-production (e.g. dev, test, QA, etc.) environments is really critical in order to reduce the feedback loop. In this blog post, I want to talk about how you can achieve this if your application relies on MongoDB Replica Set by showing you how to set it up with Docker for non-production environments.  &lt;blockquote&gt; &lt;h3&gt;Hold on! I want to watch, not read!&lt;/h3&gt; &lt;p&gt;I got you covered there! I have also recorded a ~5m covering the content of this blog post, where I also walks you through the steps visually. If you find this option useful, let me know through the comments below and I can aim harder to repeat that :)  &lt;p&gt;&lt;iframe height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/1lAjmJ1ht1o&#34; frameborder=&#34;0&#34; width=&#34;560&#34; allowfullscreen allow=&#34;autoplay; encrypted-media&#34;&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;What are we trying to do here and why?&lt;/h3&gt; &lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/67b0afbb-825b-4cd3-b1b0-522d2f6869f8.png&#34;&gt;&lt;img title=&#34;Picture2&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Picture2&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/b5111dd7-6e7e-4246-a2ec-76421a7dfd98.png&#34; width=&#34;640&#34; height=&#34;222&#34;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;If you have an application which works against a MongoDB database, it’s very common to have a replica set in production. This approach ensures the high availability of the data, especially for read scenarios. However, applications mostly end up working against a single MongoDB instance, because setting up a Replica Set in isolation is a tedious process. As mentioned at the beginning of the post, we want to reflect the production environment to the process of developing or testing the software applications as much as possible. The reason for that is to catch unexpected behaviour which may only occur under a production environment. This approach is valuable because it would allow us to reduce the feedback loop on those exceptional cases.&lt;/p&gt; &lt;h3&gt;Docker makes this all easy!&lt;/h3&gt; &lt;p&gt;This is where Docker enters into the picture! Docker is containerization technology and it allows us to have repeatable process to provision environments in a declarative way. It also gives us a try and tear down model where we can experiment and easily start again from the initial state. Docker can also help us with easily setting up a MongoDB Replica Set. Within our Docker Host, we can create Docker Network which would give us the isolated DNS resolution across containers. Then we can start creating the MongoDB docker containers. They would initially be unaware of each other. However, we can initialise the replication by connecting to one of the containers and running the replica set initialisation command. Finally,&amp;nbsp; we can deploy our application container under the same docker network. &lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/34165076-d303-419d-a7c1-505c5fc277ec.png&#34;&gt;&lt;img title=&#34;Picture1&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Picture1&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/ec36359f-82aa-4d8e-9fc7-a025e8ea4986.png&#34; width=&#34;644&#34; height=&#34;358&#34;&gt;&lt;/a&gt; &lt;p&gt;There are a handful of advantages to setting up this with Docker and I want to specifically touch on some of them: &lt;ul&gt; &lt;li&gt;&amp;nbsp;&lt;strong&gt;It can be automated easily&lt;/strong&gt;. This is especially crucial for test environments which are provisioned on demand.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;It’s repeatable!&lt;/strong&gt; The declarative nature of the Dockerfile makes it possible to end up with the same environment setup even if you run the scripts months later after your initial setup.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Familiarity!&lt;/strong&gt; Docker is a widely known and used tool for lots of other purposes and familiarity to the tool is high. Of course, this may depend on your development environment&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;Let’s make it work!&lt;/h3&gt; &lt;p&gt;First of all, I need to create a docker network. I can achieve this by running the &#34;docker network create” command and giving it a unique name. &lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt; &lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;docker network create my&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;mongo&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;cluster&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;The next step is to create the MongoDB docker containers and start them. I can use “docker run” command for this. Also, MongoDB has an official image on Docker Hub. So, I can reuse that to simplify the acqusition of MongoDB. For convenience, I will name the container with a number suffix. The container also needs to be tied to the network we have previously created. Finally, I need to specify the name of the replica set for each container.&lt;/p&gt;&#xA;&lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;docker run &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;name mongo&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;node1 &lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;d &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;net my&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;mongo&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;cluster mongo &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;replSet “rs0&#34;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;First container is created and I need to run the same command to create two more MongoDB containers. The only difference is with the container names.&#xA;&lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;docker run &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;name mongo&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;node2 &lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;d &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;net my&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;mongo&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;cluster mongo &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;replSet &lt;span style=&#34;color: #a31515&#34;&gt;&#34;rs0&#34;&lt;/span&gt;&#xA;docker run &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;name mongo&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;node3 &lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;d &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;net my&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;mongo&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;cluster mongo &lt;span style=&#34;color: gray&#34;&gt;--&lt;/span&gt;replSet “rs0&#34;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;I can see that all of my MongoDB containers are at the running state by executing the “docker ps” command.&#xA;&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/0ccafe12-d243-48c3-93ea-0930fbaa2b99.png&#34;&gt;&lt;img title=&#34;Image&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Image&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/c328fad0-9fe9-42f4-923d-aafb9d39ccc9.png&#34; width=&#34;644&#34; height=&#34;74&#34;&gt;&lt;/a&gt;&#xA;&lt;p&gt;In order to form a replica set, I need to initialise the replication. I will do that by connecting to one of the containers through the “docker exec” command and starting the mongo shell client.&#xA;&lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;docker exec &lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;it mongo&lt;span style=&#34;color: gray&#34;&gt;-&lt;/span&gt;node1 mongo&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/a2b23cf7-54c7-42d9-9e8e-67f79f1fdb44.png&#34;&gt;&lt;img title=&#34;Image&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Image&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/ae3ae9c5-9098-4885-9e47-ea88e245811f.png&#34; width=&#34;644&#34; height=&#34;206&#34;&gt;&lt;/a&gt;&#xA;&lt;p&gt;As I now have a connection to the server, I can initialise the replication. This requires me to declare a config object which will include connection details of all the servers.&#xA;&lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;config = {&#xA;      &lt;span style=&#34;color: #a31515&#34;&gt;&#34;_id&#34;&lt;/span&gt; : &lt;span style=&#34;color: #a31515&#34;&gt;&#34;rs0&#34;&lt;/span&gt;,&#xA;      &lt;span style=&#34;color: #a31515&#34;&gt;&#34;members&#34;&lt;/span&gt; : [&#xA;          {&#xA;              &lt;span style=&#34;color: #a31515&#34;&gt;&#34;_id&#34;&lt;/span&gt; : 0,&#xA;              &lt;span style=&#34;color: #a31515&#34;&gt;&#34;host&#34;&lt;/span&gt; : &lt;span style=&#34;color: #a31515&#34;&gt;&#34;mongo-node1:27017&#34;&lt;/span&gt;&#xA;          },&#xA;          {&#xA;              &lt;span style=&#34;color: #a31515&#34;&gt;&#34;_id&#34;&lt;/span&gt; : 1,&#xA;              &lt;span style=&#34;color: #a31515&#34;&gt;&#34;host&#34;&lt;/span&gt; : &lt;span style=&#34;color: #a31515&#34;&gt;&#34;mongo-node2:27017&#34;&lt;/span&gt;&#xA;          },&#xA;          {&#xA;              &lt;span style=&#34;color: #a31515&#34;&gt;&#34;_id&#34;&lt;/span&gt; : 2,&#xA;              &lt;span style=&#34;color: #a31515&#34;&gt;&#34;host&#34;&lt;/span&gt; : &lt;span style=&#34;color: #a31515&#34;&gt;&#34;mongo-node3:27017&#34;&lt;/span&gt;&#xA;          }&#xA;      ]&#xA;  }&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Finally, we can run “rs.initialize&#34; command to complete the set up.&#xA;&lt;p&gt;You will notice that the server I am connected to will be elected as the primary in the replica set shortly. By running “rs.status()”, I can view the status of other MongoDB servers within the replica set. We can see that there are two secondaries and one primary in the replica set.&#xA;&lt;h3&gt;.NET Core Application&lt;/h3&gt;&#xA;&lt;p&gt;As a scenario, I want to run &lt;a href=&#34;https://github.com/tugberkugurlu/mongodb-replica-set&#34;&gt;my .NET Core application&lt;/a&gt; which writes data to a MongoDB database and start reading it in a loop. This application will be connecting to the MongoDB replica set which we have just created.&amp;nbsp; This is a standard .NET Core console application which you can create by running the following script:&#xA;&lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;dotnet new console&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;The csproj file for this application looks like below.&#xA;&lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;Project&lt;/span&gt; &lt;span style=&#34;color: red&#34;&gt;Sdk&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;Microsoft.NET.Sdk&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;  &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;PropertyGroup&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;    &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;OutputType&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;Exe&lt;span style=&#34;color: blue&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;OutputType&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;    &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;TargetFramework&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;netcoreapp2.0&lt;span style=&#34;color: blue&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;TargetFramework&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;  &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;PropertyGroup&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;  &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;ItemGroup&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;    &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;PackageReference&lt;/span&gt; &lt;span style=&#34;color: red&#34;&gt;Include&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;Bogus&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt; &lt;span style=&#34;color: red&#34;&gt;Version&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;18.0.2&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;    &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;PackageReference&lt;/span&gt; &lt;span style=&#34;color: red&#34;&gt;Include&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;MongoDB.Driver&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt; &lt;span style=&#34;color: red&#34;&gt;Version&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;2.4.4&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;    &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;PackageReference&lt;/span&gt; &lt;span style=&#34;color: red&#34;&gt;Include&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;Polly&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt; &lt;span style=&#34;color: red&#34;&gt;Version&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;5.3.1&lt;/span&gt;&lt;span style=&#34;color: black&#34;&gt;&#34;&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;/&amp;gt;&lt;/span&gt;&#xA;  &lt;span style=&#34;color: blue&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;ItemGroup&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;span style=&#34;color: blue&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&#34;color: #a31515&#34;&gt;Project&lt;/span&gt;&lt;span style=&#34;color: blue&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Notice that I have two interesting dependencies there. Polly is used to retry the read calls to MongoDB based on defined policies. This bit is interesting as I would expect the MongoDB client to handle that for read calls. However, it might be also a good way of explicitly stating which calls can be retried inside your application. Bogus, on the other hand, is just here to be able to create fake names to make the application a bit more realistic :)&#xA;&lt;p&gt; Finally, this is the code to make this application work:&#xA;&lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;&lt;span style=&#34;color: blue&#34;&gt;partial&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;class&lt;/span&gt; Program&#xA;{&#xA;    &lt;span style=&#34;color: blue&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;void&lt;/span&gt; Main(&lt;span style=&#34;color: blue&#34;&gt;string&lt;/span&gt;[] args)&#xA;    {&#xA;        &lt;span style=&#34;color: blue&#34;&gt;var&lt;/span&gt; settings = &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt; MongoClientSettings&#xA;        {&#xA;            Servers = &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt;[]&#xA;            {&#xA;                &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt; MongoServerAddress(&lt;span style=&#34;color: #a31515&#34;&gt;&#34;mongo-node1&#34;&lt;/span&gt;, 27017),&#xA;                &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt; MongoServerAddress(&lt;span style=&#34;color: #a31515&#34;&gt;&#34;mongo-node2&#34;&lt;/span&gt;, 27017),&#xA;                &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt; MongoServerAddress(&lt;span style=&#34;color: #a31515&#34;&gt;&#34;mongo-node3&#34;&lt;/span&gt;, 27017)&#xA;            },&#xA;            ConnectionMode = ConnectionMode.ReplicaSet,&#xA;            ReplicaSetName = &lt;span style=&#34;color: #a31515&#34;&gt;&#34;rs0&#34;&lt;/span&gt;&#xA;        };&#xA;&#xA;        &lt;span style=&#34;color: blue&#34;&gt;var&lt;/span&gt; client = &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt; MongoClient(settings);&#xA;        &lt;span style=&#34;color: blue&#34;&gt;var&lt;/span&gt; database = client.GetDatabase(&lt;span style=&#34;color: #a31515&#34;&gt;&#34;mydatabase&#34;&lt;/span&gt;);&#xA;        &lt;span style=&#34;color: blue&#34;&gt;var&lt;/span&gt; collection = database.GetCollection&amp;lt;User&amp;gt;(&lt;span style=&#34;color: #a31515&#34;&gt;&#34;users&#34;&lt;/span&gt;);&#xA;&#xA;        System.Console.WriteLine(&lt;span style=&#34;color: #a31515&#34;&gt;&#34;Cluster Id: {0}&#34;&lt;/span&gt;, client.Cluster.ClusterId);&#xA;        client.Cluster.DescriptionChanged += (&lt;span style=&#34;color: blue&#34;&gt;object&lt;/span&gt; sender, ClusterDescriptionChangedEventArgs foo) =&amp;gt; &#xA;        {&#xA;            System.Console.WriteLine(&lt;span style=&#34;color: #a31515&#34;&gt;&#34;New Cluster Id: {0}&#34;&lt;/span&gt;, foo.NewClusterDescription.ClusterId);&#xA;        };&#xA;&#xA;        &lt;span style=&#34;color: blue&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color: blue&#34;&gt;int&lt;/span&gt; i = 0; i &amp;lt; 100; i++)&#xA;        {&#xA;            &lt;span style=&#34;color: blue&#34;&gt;var&lt;/span&gt; user = &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt; User { Id = ObjectId.GenerateNewId(), Name = &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt; Bogus.Faker().Name.FullName() };&#xA;            collection.InsertOne(user);&#xA;        }&#xA;&#xA;        &lt;span style=&#34;color: blue&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color: blue&#34;&gt;true&lt;/span&gt;)&#xA;        {&#xA;            &lt;span style=&#34;color: blue&#34;&gt;var&lt;/span&gt; randomUser = collection.GetRandom();&#xA;            Console.WriteLine(randomUser.Name);&#xA;&#xA;            Thread.Sleep(500);&#xA;        }&#xA;    }&#xA;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;This is not the most beautiful and optimized code ever but should demonstrate what we are trying to achieve by having a replica set. It&#39;s actually the GetRandom method on the MongoDB collection object which handles the retry:&#xA;&lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;&lt;span style=&#34;color: blue&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;class&lt;/span&gt; CollectionExtensions &#xA;{&#xA;    &lt;span style=&#34;color: blue&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;readonly&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;static&lt;/span&gt; Random random = &lt;span style=&#34;color: blue&#34;&gt;new&lt;/span&gt; Random();&#xA;&#xA;    &lt;span style=&#34;color: blue&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;static&lt;/span&gt; T GetRandom&amp;lt;T&amp;gt;(&lt;span style=&#34;color: blue&#34;&gt;this&lt;/span&gt; IMongoCollection&amp;lt;T&amp;gt; collection) &#xA;    {&#xA;        &lt;span style=&#34;color: blue&#34;&gt;var&lt;/span&gt; retryPolicy = Policy&#xA;            .Handle&amp;lt;MongoCommandException&amp;gt;()&#xA;            .Or&amp;lt;MongoConnectionException&amp;gt;()&#xA;            .WaitAndRetry(2, retryAttempt =&amp;gt; &#xA;                TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) &#xA;            );&#xA;&#xA;        &lt;span style=&#34;color: blue&#34;&gt;return&lt;/span&gt; retryPolicy.Execute(() =&amp;gt; GetRandomImpl(collection));&#xA;    }&#xA;&#xA;    &lt;span style=&#34;color: blue&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color: blue&#34;&gt;static&lt;/span&gt; T GetRandomImpl&amp;lt;T&amp;gt;(&lt;span style=&#34;color: blue&#34;&gt;this&lt;/span&gt; IMongoCollection&amp;lt;T&amp;gt; collection)&#xA;&#xA;    {&#xA;        &lt;span style=&#34;color: blue&#34;&gt;return&lt;/span&gt; collection.Find(FilterDefinition&amp;lt;T&amp;gt;.Empty)&#xA;            .Limit(-1)&#xA;            .Skip(random.Next(99))&#xA;            .First();&#xA;    }&#xA;}&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;I will run this through docker as well and here is the dockerfile for this:&amp;nbsp; &lt;div class=&#34;code-wrapper border-shadow-1&#34;&gt;&#xA;&lt;div style=&#34;color: black; background-color: white&#34;&gt;&lt;pre&gt;FROM microsoft/dotnet:2-sdk&#xA;&#xA;COPY ./mongodb-replica-set.csproj /app/&#xA;WORKDIR /app/&#xA;RUN dotnet --info&#xA;RUN dotnet restore&#xA;ADD ./ /app/&#xA;RUN dotnet publish -c DEBUG -o out&#xA;ENTRYPOINT [&#34;dotnet&#34;, &#34;out/mongodb-replica-set.dll&#34;]&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;When it starts, we can see that it will output the result to the console:&#xA;&lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/404373db-0d84-4fa2-96f5-5f50a5bc8370.png&#34;&gt;&lt;img title=&#34;Image&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Image&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/c453cae9-dc86-41bf-9ecf-bf1c7449c5bc.png&#34; width=&#34;606&#34; height=&#34;484&#34;&gt;&lt;/a&gt;&#xA;&lt;h3&gt;Prove that It Works!&lt;/h3&gt;&#xA;&lt;p&gt;In order to demonstrate the effect of the replica set, I want to take down the primary node. First of all, we need to have look at the output of rs.status command we have previously ran in order to identify the primary node. We can see that it’s node1!&amp;nbsp; &lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/ea0da0c1-4c20-4242-a766-5f4ff0598be4.png&#34;&gt;&lt;img title=&#34;Image&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Image&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/2a7ea5bd-c60c-41fb-9554-e8271a849492.png&#34; width=&#34;644&#34; height=&#34;268&#34;&gt;&lt;/a&gt;&#xA;&lt;p&gt;Secondly, we need to get the container id for that node.&amp;nbsp; &lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/ce3a7018-9e43-48fd-be87-488a0e71e784.png&#34;&gt;&lt;img title=&#34;Image&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Image&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/b814732a-dcc3-4b8b-9ea9-dadefc375a01.png&#34; width=&#34;644&#34; height=&#34;79&#34;&gt;&lt;/a&gt;&#xA;&lt;p&gt;Finally, we can kill the container by running the “docker stop command”. Once the container is stopped, you will notice that application will gracefully recover and continue reading the data.&amp;nbsp; &lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/d583a8d9-fbd8-4c85-8c79-668f3ef50f3a.png&#34;&gt;&lt;img title=&#34;Image&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Image&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/6243d043-0d47-4488-9a89-a5747a58a4dc.png&#34; width=&#34;644&#34; height=&#34;270&#34;&gt;&lt;/a&gt;  &#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/setting-up-a-mongodb-replica-set-with-docker-and-connecting-to-it-with-a--net-core-app</guid>
      <pubDate>Wed, 31 Jan 2018 10:10:00 +0000</pubDate>
    </item>
    <item>
      <title>Speaking at SQL in the City 2017, Register Now!</title>
      <link>https://www.tugberkugurlu.com/archive/speaking-at-sql-in-the-city-2017-register-now</link>
      <description>&#xA;&lt;p&gt;I&#39;m quite happy to tell you that I&#39;ll be speaking at &lt;a href=&#34;https://www.red-gate.com/hub/events/sqlinthecity/&#34; target=&#34;_blank&#34;&gt;SQL in the City 2017&lt;/a&gt; on the 13th of December about l&lt;strong&gt;atest &lt;/strong&gt;&lt;a href=&#34;https://www.red-gate.com/products/sql-development/sql-compare/&#34; target=&#34;_blank&#34;&gt;&lt;strong&gt;SQL Compare&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; features and support for SQL Server 2017 &lt;/strong&gt;with my colleague and fellow MVP, &lt;a href=&#34;https://voiceofthedba.com/&#34; target=&#34;_blank&#34;&gt;Steve Jones&lt;/a&gt;. &lt;/p&gt; &lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/6411302e-d9b2-4b95-9c58-3804d294b488.png&#34;&gt;&lt;img title=&#34;MVPs in SITCs&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;MVPs in SITCs&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/72312e88-0444-4971-94eb-0eae024cd25f.png&#34; width=&#34;644&#34; height=&#34;324&#34;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;SQL in the City &lt;a href=&#34;https://www.red-gate.com&#34; target=&#34;_blank&#34;&gt;Redgate&lt;/a&gt;&#39;s annual virtual event and this year&#39;s livestream event focuses on enabling you to be more productive. Technical sessions will dive into the latest Microsoft SQL Server releases, and cover topical issues such as data compliance, protection &amp;amp; privacy.&lt;/p&gt; &lt;p&gt;&lt;a href=&#34;https://www.red-gate.com/hub/events/sqlinthecity/#schedule&#34; target=&#34;_blank&#34;&gt;This year&#39;s agenda&lt;/a&gt; is full of really great sessions from enabling &lt;a href=&#34;https://www.red-gate.com/hub/product-learning/sql-source-control/moving-from-application-automation-to-true-devops-by-including-the-database&#34; target=&#34;_blank&#34;&gt;DevOps for databases by automating your deployments&lt;/a&gt; to rapid (and magic!) &lt;a href=&#34;https://www.red-gate.com/hub/product-learning/sql-clone/sql-clone-concept&#34; target=&#34;_blank&#34;&gt;database provisioning with SQL Clone&lt;/a&gt;. This year Data Platform MVPs Steve Jones and &lt;a href=&#34;https://www.scarydba.com/&#34; target=&#34;_blank&#34;&gt;Grant Fritchey&lt;/a&gt; will be joined by Kathi Kellenberger, Editor of Simple Talk and &lt;a href=&#34;https://www.red-gate.com/hub/events/sqlinthecity/#speakers&#34; target=&#34;_blank&#34;&gt;many more&lt;/a&gt; who are behind the great tools we build!&lt;/p&gt; &lt;h2&gt;Register Now!&lt;/h2&gt; &lt;p&gt;&lt;a href=&#34;https://www.red-gate.com/hub/events/sqlinthecity/&#34; target=&#34;_blank&#34;&gt;Register now&lt;/a&gt; to confirm your attendance, and be the first to get access to Grant Fritchey’s new eBook, &lt;b&gt;SQL Server Execution Plans&lt;/b&gt;, when it&#39;s released in 2018.&lt;/p&gt;  &#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/speaking-at-sql-in-the-city-2017-register-now</guid>
      <pubDate>Tue, 05 Dec 2017 18:27:00 +0000</pubDate>
    </item>
    <item>
      <title>Understanding Graphs and Their Application on Software Systems</title>
      <link>https://www.tugberkugurlu.com/archive/understanding-graphs-and-their-application-on-software-systems</link>
      <description>&#xA;&lt;p&gt;Lately, I wanted to spend a little bit time on going back to fundamental computer science concepts. Hopefully, I will be able to write about these while I am looking into them in order to offload the knowledge from my brain to the magic hands of the Web :) I am going to start with Graphs, specifically Depth First Traversal (a.k.a. Depth First Search or DFS) and Breadth First Traversal (a.k.a Breadth First Search or BFS). However, this post is only about the definition of Graph and its application in software systems.&lt;/p&gt; &lt;h3&gt;What is a Graph?&lt;/h3&gt; &lt;p&gt;I am sure you are capable of Googling what a Graph is and ironically maybe that’s why you are reading this sentence now. However, I am not going to put the fancy explanation of a Graph here. &lt;a href=&#34;https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)&#34;&gt;Wikipedia already has a great definition on a Graph&lt;/a&gt; which can be useful to start with.&lt;/p&gt; &lt;p&gt;Let’s start with a picture:&lt;/p&gt; &lt;p&gt;&lt;a href=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/03a29b1b-1ed6-4c45-b2a4-3f33babd14be.jpg&#34;&gt;&lt;img title=&#34;Picture1&#34; style=&#34;border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px&#34; border=&#34;0&#34; alt=&#34;Picture1&#34; src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/0abb0dc1-1c50-4698-9c68-22703a1654c6.jpg&#34; width=&#34;644&#34; height=&#34;456&#34;&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;This is a graph and there are some unique characteristics of this which makes it a graph. &lt;ul&gt; &lt;li&gt;Vertices (a.k.a. Nodes): Each circle with a label inside the above picture is called a vertex or node. They are fundamental building blocks of a graph. &lt;li&gt;Edges (a.k.a. Arc, Line, Link, Branch): A line that joins two vertices together is called as edge. An edge could be in three forms: undirected and directed. We will get to what these actually mean.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;At this point you might be asking what is the difference between a graph and a tree? A tree is actually a graph with some special constraints applied to. A few of these that I know: &lt;ul&gt; &lt;li&gt;A tree cannot contain a cycle but a graph can (see the A, B and E nodes and their edges inside the above picture). &lt;li&gt;A tree always has a specific root node, whereas you don’t have this concept with a graph. &lt;li&gt;A tree can only has one edge between its two nodes whereas we can have unidirectional and bidirectional edges between nodes within a graph&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;I am sure there are more but I believe these are the ones that matter the most. &lt;p&gt;As we can see with the tree example, graphs comes in many forms. There are many &lt;a href=&#34;https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)#Types_of_graphs&#34;&gt;types of graphs&lt;/a&gt; and each type has its own unique characteristics and real world use cases. Undirected and directed graphs are two of these types as I briefly mentioned while explaining the edges. I believe the best example to describe the difference between them is to have a look at the fundamental concept of Facebook and Twitter. &lt;h3&gt;Application of Graphs&lt;/h3&gt; &lt;p&gt;Graphs are amazing, I absolutely love the concept of a graph! Everyone interacts with a system everyday which somehow makes use of graphs. Facebook, Google Maps, Foursquare, the fraud check system that your bank applies are all making use of a graph and there are many, many more. One application of graph concept which I love is a recommendation engine. There are many forms of this but a very basis one is called &lt;a href=&#34;https://en.wikipedia.org/wiki/Collaborative_filtering&#34;&gt;Collaborative Filtering&lt;/a&gt;. At its basis, it works under a notion of “Steve and Mark liked BMW, Mercedes and Toyota, you like BMW and Toyota, and you may like Mercedes, too?”. &lt;p&gt;There are some really good graph databases with their own query languages as well. One that I love about is &lt;a href=&#34;https://www.tugberkugurlu.com/archive/getting-started-with-neo4j-in--net-with-neo4jclient-library&#34;&gt;Neo4j&lt;/a&gt; which uses &lt;a href=&#34;https://neo4j.com/developer/cypher-query-language/&#34;&gt;Cypher&lt;/a&gt; query language to make its data available to be consumed. &lt;a href=&#34;https://neo4j.com/use-cases/&#34;&gt;On their web site, there are a few key applications of Neo4j listed&lt;/a&gt; and they are fundamentally real world applications of the graph concept. &lt;p&gt;You can also come across some interesting problems in the space of mathematics which has solutions based on a type of graph like &lt;a href=&#34;https://en.wikipedia.org/wiki/Seven_Bridges_of_K%C3%B6nigsberg&#34;&gt;Seven Bridges of Königsberg problem&lt;/a&gt; (and I think this problem is &lt;a href=&#34;https://en.wikipedia.org/wiki/Graph_theory#History&#34;&gt;the cornerstone in the history of graph theory&lt;/a&gt;).    &#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/understanding-graphs-and-their-application-on-software-systems</guid>
      <pubDate>Tue, 19 Sep 2017 17:21:00 +0000</pubDate>
    </item>
    <item>
      <title>Here is My Proudest Achievement, What is Yours?</title>
      <link>https://www.tugberkugurlu.com/archive/here-is-my-proudest-achievement-what-is-yours</link>
      <description>&#xA;&lt;p&gt;It&#39;s very common that you get asked about your proudest achievement. I wanted to put mine here publicly so that I would have a place to direct people to. So, here it is :)&lt;/p&gt; &lt;p&gt;My proudest achievement to this day dates back to 2010. I was working at a local Travel Agency in Turkey while still studying Travel Management at the university and we had a Web site for our customers to book their airport transfers from/to their hotels by paying online. However, the application didn&#39;t allow our customers to book additional services with extra cost such as baby booster seat. In addition to this, we were unable to reflect our pricing accurately for particular conditions due to the limitations on the system. At the time, I was working at the reservations and booking department but I had a huge interest on software development, especially on web applications. &lt;p&gt;When our web developer left the company, I prototyped the algorithm to calculate the airport transfer pricing on SQL Server based on the number of passengers, arrival and departure dates. I presented this to my manager who was also in charge of the company&#39;s online sales, and asked for a budget and time for developing a new airport transfer booking system for the company. I explained that this system would have the same features as the old system along with the additional features we have always wanted. This was going to allow us to provide better service to our customers and reflect cheaper prices by having a maintainable system to build upon. My manger believed in me, and gave me time and budget to invest on this. I spent a month to develop the system and its content management system by coding the business logic in C# (while learning it at the same time), developing the user interface as a web application using HTML, CSS and JavaScript, and integrating it with an online payment system. I had to deal with lots of things I hadn&#39;t known about but having a good support from my manager made me always trust myself and keep pushing to come through all the obstacles. We rolled out the system under a different domain name first and advertised it through Google AdWords‎ (you could see that version see &lt;a href=&#34;https://web.archive.org/web/20100906032705/http://www.fethiyeairport.com:80/&#34;&gt;here&lt;/a&gt; even if the styles and functionality don&#39;t quite work on web.archive.org). Within the first 5 hours, we sold a Private Minibus Transfer through the system  Over the weeks, we directed all our transfer booking channels to this new system and kept evolving it. After two years, the system sold 32% more transfers than the old system and yielded 26% more revenue. The final look of the system is still running &lt;a href=&#34;http://www.dalamanairport.net/&#34;&gt;here&lt;/a&gt; and maintained by the company (I should point out that I am not entirely responsible for the new look of the site , especially for those red primary action buttons!). Proving myself with this achievement also gave me a chance to take more responsibility on software engineering at the company and I was able to get a budget for an accommodation booking system (&lt;a href=&#34;https://web.archive.org/web/20121023114201/http://www.turkeybeds.com:80/&#34;&gt;it can been found on web.archive.org&lt;/a&gt;) which yielded extra revenue for the company for a few years. &lt;p&gt;Lots of things happened after this and I achieved so much more such as being part of several successful teams to create valuable software products, being published, having the Microsoft MVP award for 5 years in a row, speaking at lots of international conferences, maintaining a successful blog for 7+ years and many more. However, nothing was able to beat that because it was a unique opportunity to be able to fight for something I truly believed in. Besides that, having a true leader as your manager is a unique opportunity. He trusted me and my skills, and when looking back at this now, it&#39;s very clear to see that I would never have become a good software developer without this trust and my confident in myself. &lt;h2&gt;What&#39;s Your Proudest Achievement?&lt;/h2&gt; &lt;p&gt;Well, it&#39;s your turn. Hopefully I encouraged you to share yours publicly as well. Please share yours as a comment here, preferably by linking to your blog post which you are about to write :)&lt;/p&gt;  &#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/here-is-my-proudest-achievement-what-is-yours</guid>
      <pubDate>Mon, 18 Sep 2017 16:59:00 +0000</pubDate>
    </item>
    <item>
      <title>Defining What Good Looks Like for a Software Engineer</title>
      <link>https://www.tugberkugurlu.com/archive/defining-what-good-looks-like-for-a-software-engineer</link>
      <description>&#xA;&lt;p&gt;If you are a software engineer, this is a very common question you will get to ask yourself a lot. This is going to be especially very frequent if you are being part of the recruitment process in your company. As you may know, I work at &lt;a href=&#34;https://www.red-gate.com/&#34;&gt;Redgate&lt;/a&gt;, and we have &lt;a href=&#34;https://www.red-gate.com/blog/working/software-development-culture&#34;&gt;a good culture for development teams&lt;/a&gt;. Besides that, common characteristics of a good engineer with examples and counter examples for each engineering role are defined, too. This is a really good guidance for the employer to reflect their culture for a particular role. It’s also good for the employees to understand where they are on being an effective employee.  &lt;p&gt;&lt;img src=&#34;https://tugberkugurlu.blob.core.windows.net/bloggyimages/640px-Coding_Shots_Annual_Plan_high_res-5.jpg&#34;&gt; &lt;p&gt;&lt;em&gt;&lt;font size=&#34;1&#34;&gt;(Image is from &lt;/font&gt;&lt;/em&gt;&lt;a title=&#34;https://commons.wikimedia.org/wiki/File:Coding_Shots_Annual_Plan_high_res-5.jpg&#34; href=&#34;https://commons.wikimedia.org/wiki/File:Coding_Shots_Annual_Plan_high_res-5.jpg&#34; target=&#34;_blank&#34;&gt;&lt;em&gt;&lt;font size=&#34;1&#34;&gt;https://commons.wikimedia.org/wiki/File:Coding_Shots_Annual_Plan_high_res-5.jpg&lt;/font&gt;&lt;/em&gt;&lt;/a&gt;)&lt;/p&gt; &lt;p&gt;I got inspired by this and I wanted to share the list of principals I value and look for within a software engineer. Obvious disclaimer: this is not the list of principals that my employer values even if the most of them are pretty similar. As we got the disclaimer out of our way, let&#39;s see these principals:  &lt;ul&gt; &lt;li&gt;&lt;b&gt;Knows the fundamental concepts, data structures and common algorithms&lt;/b&gt; rather than only being too good with a programming language or a specific framework w/o understanding the basics. In other words, know the basics and be polyglot.  &lt;li&gt;&lt;b&gt;Has good communication skills&lt;/b&gt; - both verbal and written. Without this, it&#39;s impossible to be a good software engineer.  &lt;li&gt;&lt;b&gt;Being pragmatic&lt;/b&gt; - Works incrementally and balances delivering value frequently with delivering high quality.  &lt;li&gt;&lt;b&gt;Iterates fast&lt;/b&gt; - Values Continuous Integration (CI) and Continuous Delivery (CD), makes their code fail fast, enforce consistency and keep master branch releasable. Your release process should as easy as &lt;a href=&#34;https://www.tugberkugurlu.com/archive/versioning-software-builds-based-on-git-tags-and-semantic-versioning-semver&#34;&gt;adding a git tag as a valid semantic version&lt;/a&gt;.  &lt;li&gt;&lt;b&gt;Cares for sustainability&lt;/b&gt; - Strives for producing code which will sustain for years, even decades. Not one-off, works-now-who-knows-when-it-will-stop-working ones.  &lt;li&gt;&lt;b&gt;Knows the business&lt;/b&gt; - Cares to understand the business domain and strives for establishing an ubiquitous language between the software product team and stakeholders.  &lt;li&gt;&lt;b&gt;Strives for THE BEST UX&lt;/b&gt; - Makes user experience the part of the product completeness.  &lt;li&gt;&lt;b&gt;Being a team player&lt;/b&gt; - Works with their peers, gets/gives code review from/to them. Develops their skills while they are developing their own. Should strive for being transparent to the team all the time.  &lt;li&gt;&lt;b&gt;Knows the metrics but also has a vision&lt;/b&gt; - Should know the metrics and how to get them to make decisions. However, they should have a vision at the same time, too. They should not have the &#34;Let’s ask the users” mindset as the default approach for product feature decisions. Remember, &lt;a href=&#34;http://quoteinvestigator.com/2013/03/06/artists-steal/&#34;&gt;good artists copy; great artists steal!&lt;/a&gt; The problem you are trying to solve has been probably solved within the same or a different context. Find that, bend it and apply differently.  &lt;li&gt;&lt;b&gt;Disagrees and commits when needed&lt;/b&gt; - Should not be shy about getting their opinions out and pursue them. However, they should also know that a decision has to be made, and when that’s the case, they should commit fully and try to get the best out of it even if it’s not the decision they wanted to see.  &lt;li&gt;&lt;b&gt;Values open source and contributing back to software community&lt;/b&gt; - Has a blog, gives talks at conferences or user groups, contributes back to open source projects. Simply shares what they are proud of with others openly.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;There are probably more but these are the most important ones that I care about and value at a very high level. However, I wonder what yours are, too. Therefore, please share them with me here by send me a comment.&lt;/p&gt;  &#xA;</description>
      <author>Tugberk Ugurlu</author>
      <guid>https://www.tugberkugurlu.com/archive/defining-what-good-looks-like-for-a-software-engineer</guid>
      <pubDate>Sat, 18 Mar 2017 15:00:00 +0000</pubDate>
    </item>
  </channel>
</rss>