<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Daniel Lee</title>
    <description>The latest articles on Forem by Daniel Lee (@dleedev365).</description>
    <link>https://forem.com/dleedev365</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1283778%2Fa7f08490-139f-4b6f-9b0e-81ce2bfafbd4.png</url>
      <title>Forem: Daniel Lee</title>
      <link>https://forem.com/dleedev365</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dleedev365"/>
    <language>en</language>
    <item>
      <title>Design a short url for system design interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Tue, 03 Dec 2024 19:13:16 +0000</pubDate>
      <link>https://forem.com/dleedev365/design-a-short-url-for-system-design-interview-1bk2</link>
      <guid>https://forem.com/dleedev365/design-a-short-url-for-system-design-interview-1bk2</guid>
      <description>&lt;p&gt;This article is a personal note from "System Design Interview - An Insider's Guide by Alex Xu". It's intended as a memory refresher for system design interview in hurry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;hash funciton&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to generate a shorter URL, use a hash function to map between short URLs and long URLs. For example, hash functions like &lt;strong&gt;CRC&lt;/strong&gt;, &lt;strong&gt;MD5&lt;/strong&gt; and &lt;strong&gt;SHA-1&lt;/strong&gt; are used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;hash value&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to generate short URLs of length "n", find the value that meets "back-of-the-envelope" estimation. For example, a system is identified to support 365 million unique URLS, then given the system generates using [0-9,a-z,A-Z], "n" has to be 62^n &amp;gt;= 365 M (roughly 7).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;hash collision&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to generate unique short URLs, two approaches can be taken:

&lt;ol&gt;
&lt;li&gt;*&lt;em&gt;Base 62 conversion *&lt;/em&gt;- converts the same number between its different number presentations&lt;/li&gt;
&lt;li&gt;Hash the first "n" number of characters and append it to the rest to generate a short URL. If the length of the result still exceeds "n", repeat the process. 

&lt;ul&gt;
&lt;li&gt;As this can be time-consuming, "bloom filter" can be used to improve performance. &lt;strong&gt;Bloom Filter&lt;/strong&gt; is a space-efficient probablistic technique to test if an element in a number of a set.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>interview</category>
      <category>design</category>
    </item>
    <item>
      <title>Security strategy: zero trust model with mTLS</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Tue, 26 Nov 2024 05:17:23 +0000</pubDate>
      <link>https://forem.com/dleedev365/zero-trust-security-mtls-cfa</link>
      <guid>https://forem.com/dleedev365/zero-trust-security-mtls-cfa</guid>
      <description>&lt;h3&gt;
  
  
  Prologue
&lt;/h3&gt;

&lt;p&gt;When I worked at Mastercard, I had an opportunity to contribute to an organization's effort to upgrade backend services to support optional mTLS for a new client from the middle east. Due to the country policy, all the data handled within our application had to be on-premise and this was the first time I heard about "Zero Trust Architecture" which sounded pretty cool!&lt;/p&gt;

&lt;p&gt;Terminologies explained:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"On-premise" means that software, systems, data, and infrastructure are installed and operated within an organization's own facilities, such as an office building or data centre in a certain region, etc.&lt;/li&gt;
&lt;li&gt;"Zero trust" is a cybersecurity framework that assumes no subject in an information system is trusted by default.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  What is mTLS?
&lt;/h3&gt;

&lt;p&gt;A mutual transport layer security is an encryption protocol often used in a zero trust security framework. To better understand, we first need to know about the following three important building blocks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Public and Private keys&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anything encrypted with a public key can be decrypted only with the private key&lt;/li&gt;
&lt;li&gt;Anyone can view the public key by looking at the domain's or server's TLS certificate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TLS Certificate&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A data file containing information about server's or domain's identity, public key, and statement of certificates (issuer, expiry date, etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TLS handshake&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A process for verifying the TLS certificate and the server's possession of the private key&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  How does mTLS work?
&lt;/h3&gt;

&lt;p&gt;In TLS, normally, the server has a TLS certificate and a public/private key pair while the client doesn't. TLS is established in the following manner:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client connects to the server&lt;/li&gt;
&lt;li&gt;Server presents its TLS certificate&lt;/li&gt;
&lt;li&gt;Client verifies the server's certificate&lt;/li&gt;
&lt;li&gt;Client and server exchange information over encrypted TLS connection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the other hand, in mTLS, both client and server have a certificate, and both sides authenticate using their public/private key pairs. mTLS is established in the following manner (additional steps in bold):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client connects to the server&lt;/li&gt;
&lt;li&gt;Server presents its TLS certificate&lt;/li&gt;
&lt;li&gt;Client verifies the server's certificate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client presents its TLS certificate&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server verifies the client's certificate&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server grants access&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Client and server exchange information over encrypted TLS connection&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  What's unique about mTLS?
&lt;/h3&gt;

&lt;p&gt;As both clients and servers need to verify certificates, there has to be a central authority, so called, "Root" TLS certificate. This enables an organization to be their own certificate authority and it is self-signed, meaning organizations create it themselves (if they have their own private network or internet service provider). Thus, authorized clients and servers have to correspond to this root certificate.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;To learn more about &lt;em&gt;mTLS&lt;/em&gt;, please refer to &lt;a href="https://www.cloudflare.com/learning/access-management/what-is-mutual-tls" rel="noopener noreferrer"&gt;CloudFlare blog post&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;To learn more about &lt;em&gt;Zero Trust&lt;/em&gt;, please refer to &lt;a href="https://www.youtube.com/watch?v=yn6CPQ9RioA" rel="noopener noreferrer"&gt;IBM Technology youtube video&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
    </item>
    <item>
      <title>Typescript OOD concept for technical interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Tue, 26 Nov 2024 04:23:40 +0000</pubDate>
      <link>https://forem.com/dleedev365/typescript-ood-concept-review-3jj3</link>
      <guid>https://forem.com/dleedev365/typescript-ood-concept-review-3jj3</guid>
      <description>&lt;h3&gt;
  
  
  Basics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;class can implement one or more interfaces. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ex)

&lt;code&gt;class C implements A,B {...}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;class can extend from one ore more base classes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ex)

&lt;code&gt;class C extends A,B {...}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Member visibility
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;public&lt;/strong&gt;: can be accessed anywhere&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;protected&lt;/strong&gt;: only visible to subclasses of the class they're declared in. For example,&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpecialGreeter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// good&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SpecialGreeter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// subclass&lt;/span&gt;
&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// good, public method of the parent class&lt;/span&gt;
&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// bad! cannot call a protected method of the parent class&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;private&lt;/strong&gt;: cannot be accessed even from subclasses, but via getter method&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;static&lt;/strong&gt;: define properties or methods on the class itself, rather than on instances of the class. It can be accessed without needing to create an instance of the class. For example&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MathUtils&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;calculateCircleArea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;MathUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="c1"&gt;// 3.14159&lt;/span&gt;
&lt;span class="nx"&gt;MathUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateCircleArea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//78.53975&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When to use it?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the functionality is tied to the class rather than an object&lt;/li&gt;
&lt;li&gt;For constant or utility function that should be shared&lt;/li&gt;
&lt;li&gt;For singleton-like pattern where one central instance is sufficient&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;abstract&lt;/strong&gt;: cannot be instantiated (class, field, method). It is a means to serve as a base class for subclasses which do implement all the abstract members. For example,&lt;br&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// bad&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Derived&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Derived&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// good&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>typescript</category>
      <category>web</category>
      <category>ood</category>
      <category>interview</category>
    </item>
    <item>
      <title>Design a key-value store for system design interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Wed, 20 Nov 2024 06:25:49 +0000</pubDate>
      <link>https://forem.com/dleedev365/design-a-key-value-store-for-system-design-interview-3i4j</link>
      <guid>https://forem.com/dleedev365/design-a-key-value-store-for-system-design-interview-3i4j</guid>
      <description>&lt;p&gt;When talking about a key-value store in a backend system, the very first thing that comes to my mind is a cache storage like Redis. &lt;/p&gt;

&lt;p&gt;Caching is a technique used to avoid hitting database multiple times for the same retrieval of data, thereby serves Read requests more efficiently. However, fitting everything in a hash map or in memory is limited when the size of data grows over time. Although there are ways to optimize how data is stored (via data compression or separately storing frequently accessed data in memory and the rest in disk), a system will eventually require a distributed key-value store to handle more traffic.&lt;/p&gt;




&lt;p&gt;When designing a distributed key-value store system, &lt;strong&gt;CAP theorem&lt;/strong&gt; has to be considered: Consistency, Availability, and Partition tolerance. In fact, partition tolerance is almost inevitable in modern systems as network failures cannot be avoided all the time. So, we have to make tradeoffs between consistency and availability in reality. Here're 3 possible combinations in theory: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CA: choose consistency and availability over partition tolerance (unrealistic)&lt;/li&gt;
&lt;li&gt;CP: choose consistency and partition tolerance over availability&lt;/li&gt;
&lt;li&gt;AP: choose availability and partition tolerance over consistency&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Given these tradeoffs, one should further consider the followings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Data partition and repetition&lt;/li&gt;
&lt;li&gt;Consistency (strong, weak, eventual)&lt;/li&gt;
&lt;li&gt;Inconsistency resolution&lt;/li&gt;
&lt;li&gt;Handling failures (temporary, permanent)&lt;/li&gt;
&lt;li&gt;Write and read paths&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;When we partition or replicate data, "&lt;strong&gt;consistency hashing&lt;/strong&gt;" can be used to ensure minimum data movement and even distribution of data amongst key-val stores. Checked!&lt;/p&gt;




&lt;p&gt;To ensure consistency, "&lt;strong&gt;Quorum consensus&lt;/strong&gt;" can be used to guarantee consistency for both read and write operations. Quorum consensus basically states that&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Given N number of replicas, W as a write quorum size, R as a read quorum size,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For a write operation to be considered successful, write operation must be acknowledged from W replicas&lt;/li&gt;
&lt;li&gt;For a read operation to be considered successful, read operation must wait for responses from at least R replicas&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, if a data is replicated to server0, server1, server2, and W = 1, a coordinator must wait for 1 acknowledge from one of the servers. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If W + R &amp;gt; N, strong consistency is guaranteed.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;There're 3 consistency models that can be considered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Strong consistency&lt;/strong&gt;: all replicas or nodes will see up-to-date data all the time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak consistency&lt;/strong&gt;: somewhere in the middle of strong and eventual consistency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eventual consistency&lt;/strong&gt;: given enough time, all updates will be propagated and all replicas are consistent&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;To resolve inconsistency amongst replicas, &lt;strong&gt;versioning&lt;/strong&gt; and &lt;strong&gt;vector clocks&lt;/strong&gt; can be used to detect and reconcile conflicts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Versioning&lt;/strong&gt; technique basically treats each data modification as a new immutable version of data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector clock&lt;/strong&gt; is a [server, version] pair associated with a data item. For example, D([s1,v1], [s2,v2], ..., [sn,vn]), where D is data item, sn is nth server, and vn is nth version. Vector clocks has to choose one of the following operations:

&lt;ol&gt;
&lt;li&gt;Increment vi if [si,vi] exists&lt;/li&gt;
&lt;li&gt;Otherwise, create a new entry [si, v1]&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;To handle failures (of replicas/servers/nodes), a system first needs to detect a failure. "&lt;strong&gt;Gossip protocol&lt;/strong&gt;" can be used for that purpose. The following is how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;each node maintains a node membership list (memberID and heartbeats)&lt;/li&gt;
&lt;li&gt;each node periodically increments its heartbeat counter and sends heartbeats to a set of random nodes (which in turn propagate to another set of nodes)&lt;/li&gt;
&lt;li&gt;once nodes receive heartbeats, membership list is updated. If the heartbeat hasn't increased for more than predefined periods, the member is considered as offline&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After detecting a failure, "&lt;strong&gt;Sloppy Quorum&lt;/strong&gt;" can be used to recover from &lt;u&gt;temporary&lt;/u&gt; failures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Quorum" means the minimum number of members of an assembly to make proceedings of that meeting valid.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It works as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A system chooses the first W healthy servers for writes and first R healthy servers for reads on the hash ring. Then, use "&lt;strong&gt;hinted handoff&lt;/strong&gt;" to achieve data consistency. For instance, when the down server is up, changes will be pushed back to the server to achieve data consistency. If a server is unavailable due to network for server failures, another server will process requests temporarily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, "&lt;strong&gt;Anti-Entropy protocol&lt;/strong&gt;" can be used to recover from &lt;u&gt;permanent&lt;/u&gt; failures. &lt;/p&gt;

&lt;p&gt;It keeps replicas in sync by comparing each piece of data on replicas and updating each replica to the newest version. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Merkle Tree is used for inconsistency detection and efficiently minimizing the amount of data transferred. 

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Merkle Tree&lt;/strong&gt; has every non-leaf node labeled with the hash of the labels or values of its child nodes. To learn more about it, check out &lt;a href="https://www.youtube.com/watch?v=qHMLy5JjbjQ" rel="noopener noreferrer"&gt;Gaurav's Video&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;Write Path&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The write request is persisted on a commit log file&lt;/li&gt;
&lt;li&gt;Data is saved in the memory caches&lt;/li&gt;
&lt;li&gt;When the memory cache is full or reaches a predefined threshold, data is flushed to &lt;strong&gt;SSTable&lt;/strong&gt; in disk (sorted-string  pairs)&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Read Path&lt;br&gt;
After a read is directed to a specific node, a system first checks if data is in the memory cache, else return data from the disk. In the disk, to find which SSTable contains the key, "&lt;strong&gt;Bloom Filter&lt;/strong&gt;" is commonly used.&lt;/p&gt;




&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;p&gt;System Design Interview – An insider's guide by Alex Xu&lt;/p&gt;

</description>
      <category>interview</category>
      <category>design</category>
      <category>architecture</category>
      <category>redis</category>
    </item>
    <item>
      <title>Design a consistent hashing for system design interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Fri, 15 Nov 2024 22:44:29 +0000</pubDate>
      <link>https://forem.com/dleedev365/design-a-consistent-hashing-for-system-design-interview-50f</link>
      <guid>https://forem.com/dleedev365/design-a-consistent-hashing-for-system-design-interview-50f</guid>
      <description>&lt;p&gt;Imagine that you launch a new product and it attracts a huge volume of traffic ($1M order per second). Your product was designed with a single server and the server couldn't keep up with the volume, causing you to lose $1M order which you could've earned. &lt;/p&gt;

&lt;p&gt;To support a serge of a demand, what kind of mechanism could you implement in the server-side to not only distribute the traffic evenly, buy also never miss incoming orders? You also want to ensure that other working servers are protected from being overloaded? This is where consistent hashing algorithm comes into play.&lt;/p&gt;

&lt;p&gt;Traditionally, a common way to distribute traffic could be done by following a simple formula:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;serverIndex = hash(key) % N, where N is the number of servers&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But, when a server is added or removed, serverIndex changes and that can possibly lead to redistribution of all hash keys. Think about caching servers as an example (the backend system would result in many cache misses because N value is different)!&lt;/p&gt;

&lt;p&gt;One of solutions you can consider is a consistent hashing algorithm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistent hashing&lt;/strong&gt; is a hash ring formed by connecting the head and tail of a hash space with k number of virtual nodes added between each server. It ensures (uniform) minimal number of redistribution of hash keys or data, thereby prevents overloading a server (aka, "hotspot" key problem)&lt;/p&gt;

&lt;p&gt;On the hashing ring, servers are mapped based on server IP or name. k number of virtual nodes are placed for each server. When a server goes down or new servers are consistently added to the system, it impacts hash spaces and that can easily be detected by going the hash ring anti-clockwise (from the server impacted to the server prior to the impacted server). Once broken spots are identified, only keys in that impacted space need to be remapped (not all!).&lt;/p&gt;

&lt;p&gt;In summary, employing consistent hashing in balancing the server load has 3 benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;reduces the number of keys to be distributed when a server's added or removed&lt;/li&gt;
&lt;li&gt;makes it easy to scale horizontally as data are more evenly distributed&lt;/li&gt;
&lt;li&gt;mitigates "hotspot" problem&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;p&gt;System Design Interview – An insider's guide by Alex Xu&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Design a rate limiter for system design interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Thu, 31 Oct 2024 19:03:03 +0000</pubDate>
      <link>https://forem.com/dleedev365/design-a-rate-limiter-for-system-design-interview-165i</link>
      <guid>https://forem.com/dleedev365/design-a-rate-limiter-for-system-design-interview-165i</guid>
      <description>&lt;h2&gt;
  
  
  What is it?
&lt;/h2&gt;

&lt;p&gt;A rate limiter controls the rate of traffic from a client or a server. In HTTP, it limits the number of client requests allowed to be sent over a specific time period.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-side vs Server-side
&lt;/h2&gt;

&lt;p&gt;API requests can be limited on the client-side or server-side. Having a rate limiter implemented on the server-side allows full control of implementation. However, implementing it on the client-side can be manipulated by malicious users and limits the control of the implementation.&lt;/p&gt;

&lt;p&gt;Furthermore, there's API gateway, a cloud microservice that we can use as a middleware when implementing the rate limiter on the server-side. It is a fully managed service that supports not only rate limiting, but also, SSL termination, authentication, IP whitelisting, static content servicing, etc. No reinventing the wheel!&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Prevent resource starvation (ex. DoS Attack).&lt;/li&gt;
&lt;li&gt;In case your backend system relies on external APIs (payment, health records, etc), it can reduce per-call basis costs.&lt;/li&gt;
&lt;li&gt;Prevent server overloading.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Algorithms
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Token Bucket&lt;/li&gt;
&lt;li&gt;Leaking Bucket&lt;/li&gt;
&lt;li&gt;Fixed Window Counter&lt;/li&gt;
&lt;li&gt;Sliding Window Log&lt;/li&gt;
&lt;li&gt;Sliding Window Counter (hybrid: combination of 3 and 4)&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Token Bucket
&lt;/h4&gt;

&lt;p&gt;This algorithm basically adds a user-defined number of tokens to the bucket periodically, and each request consumes one token. If no token is available, drop subsequent requests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verdict: easy to implement; space-efficient; tuning is challenging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Leaking Bucket
&lt;/h4&gt;

&lt;p&gt;This algorithm processes requests at a fixed rate, which is ideal for a stable outflow rate environment. Similar to token bucket algorithm, but each request is queued instead. If a queue is full, a request is rejected.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verdict: easy to implement; space-efficient; not suitable to handle burst of request spike. &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Fixed Window Counter
&lt;/h4&gt;

&lt;p&gt;This algorithm divides timeline into fix-sized time windows and assigns a counter to each window. Each request increments the counter and once a window reaches its threshold, the request is dropped until a new time window starts. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verdict: spikes at the edges of a window could cause more requests than allowed quota. For example, 5 requests/minute is the threshold, and there can be 5 requests between 2:00:00 and 2:01:00 (at the last 50% of the window) and another 5 requests between 2:01:00 and 2:02:00 (at the first 50% of the window).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Sliding Window Log
&lt;/h4&gt;

&lt;p&gt;This algorithm stores the timestamp of each request in a log. When a new request comes in, older timestamps are removed from the log (relative to the start of the current window). If a log size is larger than a pre-defined size, a request is rejected.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verdict: it needs a lot of memory to store timestamps even if a request is rejected, some timestamp may still be in a log.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Sliding Window Counter
&lt;/h4&gt;

&lt;p&gt;This algorithm computes the number of requests in the rolling window to decide whether to accept or reject a request based on the following formula:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;requests in the current window + (requests in the previous window * overlap % of the rolling window and previous window)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, a minute window threshold is set to 7 requests/minute. 5 requests were made in the last 70% of the previous minute window, and 3 requests were made in the first 30% of the current minute window. Then, within a minute, there were 3 + 5 * 0.7 = 6.5 requests made. So, the current request can go through.&lt;br&gt;
-verdict: it can smooth out spikes in traffic, but it only works for not-so strict look back window (because this algorithm assumes that requests in the previous window are evenly distributed)&lt;/p&gt;
&lt;h2&gt;
  
  
  Design Decision
&lt;/h2&gt;

&lt;p&gt;Because multiple rate limiters may be required in a system, A configuration that defines a set of rules needs to be stored in a cache (for faster access than db). A configuration rule can look something like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;domain:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;messaging&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;descriptors:&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;key:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;message_type&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;marketing&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="err"&gt;rate_limit:&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="err"&gt;unit:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;day&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="err"&gt;requests_per_unit:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rate Limiter in Distributed Systems
&lt;/h2&gt;

&lt;p&gt;To handle concurrent requests, locking mechanism can be used. However, there're still 2 major issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Race condition - which can be solved via Lua Script and sorted_sets (data structure) in Redis&lt;/li&gt;
&lt;li&gt;Synchronization - which can be solved with a centralized data store. Sticky session is not efficient and still introduces the same problem.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Graceful Rejection of Request
&lt;/h2&gt;

&lt;p&gt;In the HTTP header, you can define a set of rate limiter properties to handle a rejection gracefully on the client-side. &lt;/p&gt;




&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;p&gt;System Design Interview – An insider's guide by Alex Xu&lt;/p&gt;

</description>
      <category>interview</category>
      <category>ratelimiter</category>
      <category>design</category>
      <category>redis</category>
    </item>
    <item>
      <title>How in the world can a nested for/while loop(s) have time complexity of O(N)?! for coding interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Wed, 23 Oct 2024 01:31:45 +0000</pubDate>
      <link>https://forem.com/dleedev365/til-nested-forwhile-loops-can-have-on-time-complexity-in-some-cases-dsa-for-technical-interviews-2ido</link>
      <guid>https://forem.com/dleedev365/til-nested-forwhile-loops-can-have-on-time-complexity-in-some-cases-dsa-for-technical-interviews-2ido</guid>
      <description>&lt;p&gt;In Python (and in general algorithm analysis), the time complexity of a nested loop depends on how the loops are structured. If you have a while loop inside a for loop, the overall complexity can still be O(N) in certain cases due to how the loops interact.&lt;/p&gt;

&lt;p&gt;Here are a few examples that explain why a while loop inside a for loop can still result in O(N) complexity:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1: Constant-Time Inner While Loop&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;some_condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Do constant-time work (O(1))
&lt;/span&gt;        &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The for loop runs N times.&lt;/li&gt;
&lt;li&gt;The while loop executes only once per iteration of the for loop because of the break statement.&lt;/li&gt;
&lt;li&gt;The work done inside the while loop is constant time, so the complexity for each for loop iteration is O(1).&lt;/li&gt;
&lt;li&gt;Therefore, the overall complexity is O(N)×O(1)=O(N).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 2: Inner While Loop's Work Depends on the Outer Loop&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Do constant-time work (O(1))
&lt;/span&gt;        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The for loop runs N times.&lt;/li&gt;
&lt;li&gt;The while loop runs only a constant number of times (at most 5) for each iteration of the for loop.&lt;/li&gt;
&lt;li&gt;Again, the complexity remains O(N).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 3: While Loop Consumes the Range in Total&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Do constant-time work (O(1))
&lt;/span&gt;        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The for loop runs N times.&lt;/li&gt;
&lt;li&gt;The while loop's condition (i &amp;lt; N) means that i will only increase up to N in total, across all iterations.&lt;/li&gt;
&lt;li&gt;Even though it's nested, the while loop will increment i a total of N times overall, not N×N.&lt;/li&gt;
&lt;li&gt;Therefore, the complexity is still O(N).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaway:&lt;/strong&gt;&lt;br&gt;
If the number of iterations of the inner while loop is independent of the for loop (or if the total work done by the while loop across the for iterations is bounded by N), then the combined complexity can remain O(N).&lt;/p&gt;

&lt;p&gt;The complexity would only be O(N^2) if the inner while loop ran N times for each iteration of the outer for loop. For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Do constant-time work (O(1))
&lt;/span&gt;        &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The for loop runs N times.&lt;/li&gt;
&lt;li&gt;The while loop runs N times for each iteration of the for loop.
Therefore, the complexity is O(N^2).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>coding</category>
      <category>interview</category>
    </item>
    <item>
      <title>Search optimized database (full-text search) for system design interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Wed, 16 Oct 2024 01:16:58 +0000</pubDate>
      <link>https://forem.com/dleedev365/til-search-optimized-database-full-text-search-for-system-design-5db1</link>
      <guid>https://forem.com/dleedev365/til-search-optimized-database-full-text-search-for-system-design-5db1</guid>
      <description>&lt;p&gt;Traditional databases run a table scan to find a search term in the database. This is slow and efficient if a table stores a large dataset (1000+ rows). To improve, "search optimized database" can be used.&lt;/p&gt;

&lt;p&gt;It uses indexing, tokenization and stemming to make search queries fast and efficient (by building "inverted indexes"):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tokenization is a process of reducing words to their root form. For example, "running" and "runs" can be reduced to "run".&lt;/li&gt;
&lt;li&gt;Stemming is a process of breaking a piece of task into individual words. It helps mapping words to documents containing those words in the inverted indexes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Something to note is the underlying data structure of search mechanisms of search optimized database - Inverted Indexes.&lt;/p&gt;

&lt;p&gt;It is a data structure that maps words to the documents that contain them. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"word1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;doc&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;doc&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;doc&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"word2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;doc&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;doc&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;doc&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most search optimized database also support "Fuzzy Search" out of box as a configuration. Fuzzy Search works by leveraging "edit distance calculation" technique, which measures how many letters to be changed/added/removed to transform one word into another. Thus, results with minor misspellings or discrepancy relative to the search term can be returned efficiently in case of human errors.&lt;/p&gt;

&lt;p&gt;One of the popular search optimized database is "ElasticSearch".&lt;/p&gt;




&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.hellointerview.com/learn/system-design/in-a-hurry/key-technologies" rel="noopener noreferrer"&gt;https://www.hellointerview.com/learn/system-design/in-a-hurry/key-technologies&lt;/a&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>elasticsearch</category>
      <category>database</category>
      <category>interview</category>
    </item>
    <item>
      <title>TIL: HTTP methods are case-sensitive</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Thu, 19 Sep 2024 01:04:56 +0000</pubDate>
      <link>https://forem.com/dleedev365/til-http-methods-are-case-sensitive-5edh</link>
      <guid>https://forem.com/dleedev365/til-http-methods-are-case-sensitive-5edh</guid>
      <description>&lt;p&gt;When making different API calls, for example, using fetch() api, request methods are case-sensitive (all UPPER-CASE) according to &lt;a href="https://www.rfc-editor.org/rfc/rfc7231#section-4.1" rel="noopener noreferrer"&gt;RFCs 7230 and 7231&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The method token is case-sensitive because it might be used as a gateway to object-based systems with case-sensitive method names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;fetch(&amp;lt;url&amp;gt;, { method: [GET, PATCH, PUT, DELETE, etc.], ...}&lt;/code&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>web</category>
      <category>http</category>
      <category>rest</category>
    </item>
    <item>
      <title>TIL: api route paths can be designed with the hypen (-) and the dot (.)</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Mon, 16 Sep 2024 17:16:03 +0000</pubDate>
      <link>https://forem.com/dleedev365/til-design-api-route-paths-with-the-hypen-and-the-dot-for-useful-purposes-4kmi</link>
      <guid>https://forem.com/dleedev365/til-design-api-route-paths-with-the-hypen-and-the-dot-for-useful-purposes-4kmi</guid>
      <description>&lt;p&gt;API routes can be designed to use the hypen and dot for useful purposes. For instance, to pass/parse multiple pieces of information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example usage of the hypen
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;Route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;path:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/flights/:from-:to&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;Request&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;URL:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;http://localhost:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="err"&gt;/flights/LAX-SFO&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;req.params:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LAX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SFO"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example usage of the dot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;Route&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;path:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/plantae/:genus.:species&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;Request&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;URL:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;http://localhost:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="err"&gt;/plantae/Prunus.persica&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;req.params:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Prunus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"species"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"persica"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;There're other ways to pass multiple values to a route path such that uses an array or the same parameter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Route path: /test
  Request URL1: https://localhost:3000/test?array=a&amp;amp;array=b&amp;amp;array=c
  Request URL2: https://localhost:3000/test?array[]=a&amp;amp;array[]=b&amp;amp;array[]=c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  In the code
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;app.get('/test',&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;function(req,res)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;console.log(req.query.array);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;array=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;res.send(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that query string exposes sensitive data to clients, so sensitive data shouldn't be put into a query string.&lt;/p&gt;

</description>
      <category>study</category>
      <category>web</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Key components to know for system design interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Fri, 13 Sep 2024 04:25:55 +0000</pubDate>
      <link>https://forem.com/dleedev365/12-system-design-fundamentals-notes-1g2c</link>
      <guid>https://forem.com/dleedev365/12-system-design-fundamentals-notes-1g2c</guid>
      <description>&lt;p&gt;This article is intended for software engineers with prior experience in development.&lt;/p&gt;

&lt;p&gt;How to Approach System Design Interviews?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Think like a tech lead guiding junior engineers how to implement your  design.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What interviewers want to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;base-level understanding of system design fundamentals&lt;/li&gt;
&lt;li&gt;back-and-forth about problem constraints and parameters of your service&lt;/li&gt;
&lt;li&gt;well-reasoned, qualified decisions based on engineering trade-offs&lt;/li&gt;
&lt;li&gt;unique direction your experience and decisions take them&lt;/li&gt;
&lt;li&gt;holistic view of a system and its users&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1) API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  REST
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;APIs must be modelled based on the resources in the system. For instance, a single URL with HTTP verbs (GET, POST, PATCH, PUT, DELETE)&lt;/li&gt;
&lt;li&gt;Good: versioning, structured &lt;/li&gt;
&lt;li&gt;Bad: unneeded data also get fetched&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  RPC
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Write code that executes on another remote machine internally&lt;/li&gt;
&lt;li&gt;APIS are thought of as an action/command (ex. &lt;code&gt;/postAnOrder(OrderDetails order)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Good: no special syntax to be learned, space-efficient&lt;/li&gt;
&lt;li&gt;Bad: only to be used for internal communication because of timing issues (it becomes challenging to distinguish concurrent multiple communications between machines)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GraphQL
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Data are structured in a graph relationships. Vertices (entities) and Edges (relationships)&lt;/li&gt;
&lt;li&gt;Good: ideal for customer-facing apps; you get what you ask; no more routing in backend to get and modify information&lt;/li&gt;
&lt;li&gt;Bad: less friendly to generate documentations like REST; not suitable for aggregate data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2) Databases (SQL vs NoSQL)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SQL
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;composed of rows and tables&lt;/li&gt;
&lt;li&gt;strong ACID (emphasis: strong consistency)&lt;/li&gt;
&lt;li&gt;support powerful queries&lt;/li&gt;
&lt;li&gt;bad: writes are slow due to B-Trees splitting/merging pages/blocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  NoSQL
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;nested key-val store&lt;/li&gt;
&lt;li&gt;multiple writes can be easily handled&lt;/li&gt;
&lt;li&gt;emphasis: eventual consistency&lt;/li&gt;
&lt;li&gt;bad: reads might be stale for a couple of seconds (due to log-structured merge-tree)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other types
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;document-type (JSON)&lt;/li&gt;
&lt;li&gt;columnar-type (good for queries involving computing the same value types across multiple values)&lt;/li&gt;
&lt;li&gt;graph-type&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3) Scaling (horizontal vs vertical)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Database scaling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;utilize replicas, then shard into separate databases. Sharding uses a hash function for even distribution and retrieval of entries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Compute Scaling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;divide a processing into pieces and designate each piece as a job in a queue so that multiple computers can work together in parallel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;both approaches may introduce some latency between calls/requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;replicas ensures the reliability of a system by avoiding a single point of failure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4) CAP Theorem
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In real world, it's impossible to achieve all three&lt;/li&gt;
&lt;li&gt;one of key fundamentals of distributed system design&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consistency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;every node in a network will have access toe the same data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Availability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;even if one or more nodes are down, any client making a data request receives a response&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Partition Tolerance (necessary for modern systems)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In case of a fault in a network or communication, the system will continue to work&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5) Web Authentication and Basic Security
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's all about the trade-offs between total safety and total convenience&lt;/li&gt;
&lt;li&gt;Authentication (JWT, session tokens/cookies) is about verifying identity, whereas authorization is allowing actions.&lt;/li&gt;
&lt;li&gt;For instance, user password can be secured with hashing and salting.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6) Load Balancers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's used to distribute traffic across machines (adding or removing servers in case of a failure).&lt;/li&gt;
&lt;li&gt;3 common techniques: round-robin, least connections/response time, consistent hashing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Round-Robin
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;sends request to servers one by one&lt;/li&gt;
&lt;li&gt;can overload a server&lt;/li&gt;
&lt;li&gt;ideal when servers are stable and loads are random&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Least Connections/Response Time
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ideal when servers with similar compute power and requests have varying connection time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Consistent Hashing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;install N number of virtual nodes for each server, so that loads are distributed as evenly as possible and only partial of the hash ring is affected when a server is added or removed.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7) Caching
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;To reduce latency of an expensive network computation/network calls/database queries/asset fetching.&lt;/li&gt;
&lt;li&gt;Popular caching patterns: cache-aside, and write-through/write-back.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cache-aside
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;fetch from cache first, if not found, fetch from database, then cache it.&lt;/li&gt;
&lt;li&gt;data can become stale in cache if there's frequent write to the database. "Time-to-Live" can resolve it.&lt;/li&gt;
&lt;li&gt;Checking cache first might introduce extra latency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Write-through and write-back
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Application writes data directly to the cache: asynchronously (write-back) or synchronously (write-through)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Write-back
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;data goes into a queue and writes the data back to database.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Write-through
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;opposite of write-back. Hence synchronous workflow, it can slow down whole streaming process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;cache invalidation strategy: Least Recently Used (LRU)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8) Message Queues (Pub/Sub)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;beneficial if there can be a spike of traffic that potentially brings a server or a database down.&lt;/li&gt;
&lt;li&gt;queues can send requests to multiple servers/systems instead of clients sending the same request to multiple servers/systems.&lt;/li&gt;
&lt;li&gt;queues decouple the client from the server by eliminating the need to know the server address.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common properties (based on implementations)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;guaranteed delivery&lt;/li&gt;
&lt;li&gt;no duplicate messages are delivered&lt;/li&gt;
&lt;li&gt;ensure that the order of messages is maintained&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  9) Indexing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;great for fetching a block of data from the hard disk to primary memory &lt;/li&gt;
&lt;li&gt;can be multi-levelled&lt;/li&gt;
&lt;li&gt;B-tree (self-adjusting; sorted order of pages)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10) Failover (active-passive or leader-follower)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;replications are used to avoid a single point of failure. It also helps a system serve global users across geographical locations/regions, and increases throughput.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  leaders
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;machine that handles &lt;strong&gt;write&lt;/strong&gt; requests to the data-store&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  followers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;replicas of the leader that handles &lt;strong&gt;read&lt;/strong&gt; requests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  synchronous replication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;a write request to the followers must be acknowledged (by the leader machine). It slows down streaming, but ensures guaranteed delivery.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  asynchronous replication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;opposite of synchronous replication.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;less-time consuming, but no guarantee on delivery.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;most common types of replication systems: single-leader, multi-leader (multiple machines can handle writes, but each needs to catch up with writes on other machines for consistency)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;to resolve concurrent write conflicts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep the update with the largest client timestamp&lt;/li&gt;
&lt;li&gt;sticky routing: writes from the same client go to the same leader&lt;/li&gt;
&lt;li&gt;keep all the updates and return all the updates from each other&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>systemdesign</category>
      <category>web</category>
      <category>interview</category>
    </item>
    <item>
      <title>Storage and retrieval for system design interview</title>
      <dc:creator>Daniel Lee</dc:creator>
      <pubDate>Wed, 11 Sep 2024 06:21:30 +0000</pubDate>
      <link>https://forem.com/dleedev365/designing-data-intensive-applications-storage-and-retrieval-5e89</link>
      <guid>https://forem.com/dleedev365/designing-data-intensive-applications-storage-and-retrieval-5e89</guid>
      <description>&lt;p&gt;The following content is my own note from the book, "Designing Data-Intensive Applications". The writing is intended for people who want to dash through the book quickly.&lt;/p&gt;




&lt;p&gt;There're different ways to store data (store engines) for transactional workloads and analytics. They're called &lt;strong&gt;OLTP&lt;/strong&gt; (optimized for transaction processing) and &lt;strong&gt;OLAP&lt;/strong&gt; (optimized for analytics). &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OLTP is user-facing, meaning request volume is high, so some strategies are required to improve performance on queries such as using index. &lt;/li&gt;
&lt;li&gt;On the other hand, OLAP is computational heavy where each query demands scanning over millions of records because of aggregate functions (SUM, COUNT, AVG, etc). Therefore, column-oriented storage is generally desirable.&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;Where does it start from?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order for humans to understanding what's happening in applications or machines, we need some kind of records, we call it, &lt;strong&gt;log&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Log is simply an output text describing states of a machine or an application. As long as a machine operates, logs will be created endlessly and storing these log is constrained by disk or RAM capacity. So, some strategies to avoid running out of disk space such as compaction is performed. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compaction&lt;/strong&gt; breaks a large log file down into smaller segments and merge them to keep the storage efficient. It's generally ideal for writes because it throws away duplicate keys in the log, keeping only the most recent update for each key. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Segments&lt;/strong&gt; are never modified after they have been written and reads can be served fast as there's no need of frequent updates on segment files. It continues to write requests to the latest segment file, and after a while, it then merges old segments and switch read requests to using new merged segment, then remove old segment files to keep storage efficient.&lt;/p&gt;




&lt;p&gt;There're also different ways to store data such as &lt;strong&gt;column-based&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In a relational database schema, each row can consist of 1 to many columns and not every request needs those column values and indexing strategy is often used to optimize performance to some degree. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index&lt;/strong&gt; is a data structure to efficiently find the value for a particular key into the database. The general idea is to keep some additional metadata on the side (to help you locate the data you want). It's derived from the primary data, affecting performance of queries. Especially, when a write happens, indexes also need to be updated and it slows down writes. So, developers need to be mindful when creating indexes. &lt;/p&gt;

&lt;p&gt;Most-widely used indexing data structure is &lt;strong&gt;B-trees&lt;/strong&gt; which stores key-val pairs sorted by key. &lt;/p&gt;

&lt;p&gt;There're also many other indexing strategies such as hash indexing, etc. However, in a column-based schema, values of each field are stored in a single row, separated by a comma, so to access nth record, developers simply accesses nth value of each row (representing fields/attributes). Duplications can be improved by bitmap encoding, etc.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>interview</category>
      <category>database</category>
    </item>
  </channel>
</rss>
