<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[System Design Tutorial]]></title><description><![CDATA[Learn System Design]]></description><link>https://systemdesigntutorial.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!ki0G!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsystemdesigntutorial.substack.com%2Fimg%2Fsubstack.png</url><title>System Design Tutorial</title><link>https://systemdesigntutorial.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 29 May 2026 14:39:54 GMT</lastBuildDate><atom:link href="https://systemdesigntutorial.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Kapil Gupta]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[systemdesigntutorial@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[systemdesigntutorial@substack.com]]></itunes:email><itunes:name><![CDATA[Kapil Gupta]]></itunes:name></itunes:owner><itunes:author><![CDATA[Kapil Gupta]]></itunes:author><googleplay:owner><![CDATA[systemdesigntutorial@substack.com]]></googleplay:owner><googleplay:email><![CDATA[systemdesigntutorial@substack.com]]></googleplay:email><googleplay:author><![CDATA[Kapil Gupta]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Coming soon]]></title><description><![CDATA[This is System Design Tutorial, a newsletter about Learn System Design.]]></description><link>https://systemdesigntutorial.substack.com/p/coming-soon</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/coming-soon</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Sun, 16 Oct 2022 16:09:44 GMT</pubDate><content:encoded><![CDATA[<p><strong>This is System Design Tutorial</strong>, a newsletter about Learn System Design.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://systemdesigntutorial.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://systemdesigntutorial.substack.com/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[Design Dropbox or Google Drive]]></title><description><![CDATA[In this Article, we will learn "How to Design a Cloud Storage Service".]]></description><link>https://systemdesigntutorial.substack.com/p/design-dropbox-google-drive</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/design-dropbox-google-drive</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 12:01:19 GMT</pubDate><content:encoded><![CDATA[<p>In this Article, we will learn "How to Design a Cloud Storage Service". This will help you understand how such services work behind the scenes and help you answer system design questions like "How to Design Google Drive", "How to Design Dropbox" or "How to Design One Drive"</p><p>Cloud Storage Services have become very popular as they allows you to access your data anywhere and anytime you want it, on multiple devices without manually transfering data or maintaining disks.</p><h2>Challenges</h2><ol><li><p>Heavy Write Volume - Read to Write ration roughly 1: 1.</p></li><li><p>ACIDity requirement - Strong ACID compliance is required, If we delete a file in a folder and then and then share with other people, it should not be the case that the folder is shared first and people are able to see the deleted file for some time, before it is vanished.</p></li><li><p>Data Deduplication - Files should be deduplicated, to prevent multiple copies of the same file on the server. To prevent data deduplication, dropbox like services breaks the files in chunks of 4MB and then maintain unique identifier for each chunk.</p></li><li><p>Delta Upload - Only the part of files that have changed should be synched on remote servers, For this client application should be intelligent enough to identify the chunks that have changed.</p></li></ol><h2>Requirement Gathering</h2><h3>Features of Dropbox, Google Drive and One Drive</h3><ul><li><p>Upload, Download, Update and Delete of all files and folders in the workspace.</p></li><li><p>Automatic Versioning and Syncronize data among different clients - Desktop, Mobile, Web etc.</p></li><li><p>Ability to share files and folders with other people.</p></li><li><p>Support Offline operation such as adding/deleting/updating files and folders in workspace, these changes should be synced to server and other clients once you are online.</p></li><li><p>Security and Permissions - Data should be secured using encryption and should be visible to only those people who have the permission to view the files.</p></li></ul><p>Above are the functional Requirements of the Dropbox service, Here are some non functional requirements</p><ul><li><p>High Availibility - Service should be always up to allow users to read their data any time.</p></li></ul><h2>Estimations</h2><p>According to the stats Dropbox has around 600M users, out of which we can assume that 100M users are active daily</p><p>On average if user adds/modifies close to 100 files, we will have a total of 10B uploads daily.</p><p>Storage Required: 10B * 1MB = 10PB</p><h2>High Level Design</h2><p>For uploading and synchronizing your files to Dropbox, you provide a folder that acts as the workspace, any files added, modified or deleted in workspace will be synchronized to Dropbox server and other Desktop and mobile devices.</p><h2>Detailed Design</h2><h3><strong>Client Application</strong></h3><p>Client Application is responsible to monitor the changes in the workspace, It interacts with the synchronization service to process metadata updates like change in file name, or contents. It is also responsible to index the file, and send the updated chunks to the cloud storage and retreiving the same in case other clients have updated the file.</p><p>Major Components of Client Application :</p><ul><li><p><strong>Watcher</strong> : responsible to monitor and synchronize the files and folders for any Create, Update or Delete operation and then inform the <strong>Indexer</strong> component to handle the changes.</p></li><li><p><strong>Chunker</strong> : job of a chunker is to split the files and the incremental changes into chunks of a suitable size ~4MB. These chunks can be later joined in the same order to reconstruct the original file. This component can intelligently sense the changes that are done on the file and transmit only those parts to the storage server.</p></li><li><p><strong>Indexer</strong> : responsible to listen to the <strong>Watcher</strong> and maintains the information about the chunks of files. Indexer also syncs this data with the Metadata storage server using syncronization service on successful storage of chunks in cloud storage.</p></li><li><p><strong>Internal Database</strong> - keeps maintains a record of the chunks and associated metadata. It allows for the offline operation when the client is not connected to the Dropbox server.</p></li></ul><h3><strong>Metadata Storage</strong></h3><p>Responsible for maintaining the metadata of the files stored in the Cloud storage. It includes information like workspace, versioning, information regarding chunks, users and workspaces. This information can be stored in a SQL database owing to the strong ACID requirements for this data. Two users who are a part of same workspace should see a consistent view of the files and folders. The Synchronization service will use this metadata to syncronized data among different workspaces across multiple clients.</p><h3><strong>Synchornization Service</strong></h3><p>One of the critical component of Cloud Storage design. It process all the updates made by the client on the file and syncronizes those updates across all the subscribers. It updates the client local database to be in sync with the Metadata storage on server. All Dropbox clients including Desktop, Mobile and Web clients talk to syncronization service to get updates from the server or push updates to the server. This way, all clients are in sync with the master copy that is stored in the Dropbox cloud. When the client is offline, all updates are stored locally and when the client becomes online, the syncronization service syncs the data to Metadata storage and the same is subsequently pushed to other clients or shared workspace users. It is also possible that two clients have made changes to the same file offline, so it should handle such conflicts. Dropbox handles such scenarios by creating a <strong>Conflicted copy</strong> and saving it with the editor&#8217;s username, and the save date. Users will be required to manually resolve that conflict.</p><h3><strong>Cloud Chunk Storage</strong></h3><p>All the chunks of the files are uploaded to the cloud Storage, and their location is maintained in the metadata. To reduce load on the dropbox servers the client directly talks to the cloud storage to retreive users data. A good option for the cloud storage is the the Amazon Simple Storage Service (S3).</p><p>Take a file, divide into 4MB blocks, based upon hash they are mapped to the same object in the storage.<br>block level deduplication.</p><p>server_file_journal - logs of changes happended on the file.</p><p><strong>PreProcessing</strong> :<br>Since we are dealing with large files, It is always a good idea to divide the data into chunks to allow upload and download in parallel, this also allows partial uploads/downloads in case of loss of network connectivity and provides efficient utilization of Bandwidht and storage space as we will see later.<br>Before upload, a large file is broken into multiple chunks. Each chunk has a metadata that will allow us to recreate the file later, Hence we need to store the name (hash of the chunk content), ordering information, size, last modification date etc.</p><p><strong>Synchronization</strong> : Our client application will be installed on Desktop and mobile devices. Client will monitor the folders within the workspace and synchronize the data by interacting with the Synchronization service using file metadata.</p><p>Lets assume we have file upload client installed on computer/mobile<br>Desktop Client Application monitors the folders that are identified as workspace or sync folders and synchronizes them with the remote Cloud Storage. T<br>&#8226; Watcher monitors the sync folders and notifies the Indexer of any action performed by the user for example when user create, delete, or update files or folders.<br>&#8226; Chunker splits the files into smaller pieces called chunks. To reconstruct a file, chunks will be joined back together in the correct order. A chunking algorithm can detect the parts of the files that have been modified by user and only transfer those parts to the Cloud Storage, saving on cloud storage space, bandwidth usage, and synchronization time.<br>&#8226; Indexer processes the events received from the Watcher and updates the internal database with information about the chunks of the modified files. Once the chunks are successfully submitted to the Cloud Storage, the Indexer will communicate with the Synchronization Service using the Message Queuing Service to update the Metadata Database with the changes.<br>&#8226; Internal Database keeps track of the chunks, files, their versions, and their location in the file system</p><p><strong>Metadata Service</strong></p><p>Metadata service will be responsible for maintaining the metadata regarding the chunks of files included<br>Here we need to maintain strong data consistency to reliably maintain the files for the user.</p>]]></content:encoded></item><item><title><![CDATA[Design Twitter]]></title><description><![CDATA[Twitter is one of the largest social networks where users can post and read tweets ( short text messages with 140 character limit) along with photos and video support.]]></description><link>https://systemdesigntutorial.substack.com/p/design-twitter</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/design-twitter</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:59:42 GMT</pubDate><content:encoded><![CDATA[<p><strong>Twitter</strong> is one of the largest social networks where users can post and read tweets ( short text messages with 140 character limit) along with photos and video support.<br>Users of the Twitter service can also follow their family, friends, or celebrities to get the latest updates about them.<br>In Twitter, there are two types of timeline features,<br>User Timeline is a list of all tweets that the user has tweeted.</p><p>Home Timeline or News Feed -&gt; is the temporal merge of the user timelines that you follow with certain business rules around it. (complex logic and multiple signals used for this and settings like don&#8217;t see the retweets from a particular user etc.)</p><h2>Requirement Gathering</h2><ul><li><p>Publish Tweets: The user should be able to tweet and see the news feed on his/her home page</p></li><li><p>News Feed: The user should be able to see tweets/posts from the users that she follows.</p></li><li><p>Relevance: The home timeline/newsfeed should be relevant and sorted in reverse chronological order.</p></li><li><p>Follow/Unfollow: The user can follow/unfollow other users</p></li><li><p>Notifications: The user should be notified about the latest events that are relevant to him.</p></li><li><p>Search: The user can search for tweets, hashtags, people.</p></li></ul><p>Apart from the basic features above, we can support the following requirements.</p><ul><li><p>Trending Topics</p></li><li><p>Retweet</p></li><li><p>Comments</p></li><li><p>HashTag support for posting and searching for tweets.</p></li><li><p>Recommendations.</p></li></ul><p>Above are the functional requirements of the Twitter Design, Here are some non-functional requirements</p><ul><li><p>High Availability - Service should be always up to allow users to post and read the tweets.</p></li><li><p>Latency - Users of a service like Twitter rely on it for quick updates on what is happening in the world, hence the goal is to minimize the latency. It is unacceptable for Twitter for messages to reach the followers in more than 5 sec time frame.</p></li><li><p>Eventual Consistency - We are ok if a particular tweet is available to one user before another.</p></li></ul><h2>Challenges</h2><p><strong>Read Heavy System</strong>: In the case of Twitter, we have a read-heavy system, since the number of reads(timeline requests) is much more than the number of writes(post tweets). We need to keep the latency as minimum as possible so that the updates reach the followers in time.</p><p><strong>Tip</strong>: Whenever we are designing a read-heavy system, we need to make efficient use of caching and precomputing, which will help us reduce the latency to a minimum.</p><h2>Estimations</h2><p>Total Number of Users on platform -&gt; 1 Billion<br>Total Number of Daily Active Users -&gt; 100 Million<br>Total Number of New Tweets / Day -&gt; 150 Million<br>Total Number of Follows per user -&gt; 200<br>Total Number of Timelines generated per day -&gt; 100 Million * 5<br>Total Number of Tweets viewed per day -&gt; 100 Million * 5 * 20 tweets = 10 Billion</p><p>Storage:</p><p>Storage required to store 1 tweet -&gt; 140 char * 2 Bytes / char + 20 Bytes for Metadata -&gt; 300 Bytes<br>Storage required for storing tweets per day -&gt; 300*150 Million = 45000 Million Bytes = 45 GB<br>Avg Storage required for one photo -&gt; 1MB<br>Avg Storage required for one video -&gt; 15MB</p><p>Assuming 1/10 of tweets contains one photo and video<br>Storage required for photo and video -&gt; (1 + 15 ) MB * 150 Million /10 =&gt; 240 Million MB -&gt; 240 TB/day</p><p>Bandwidth Estimates</p><p>Total ingress -&gt; 240 TB/day -&gt; 2.9GB/sec<br>Total egress -&gt; 10 Billion * 300 Byte + 10 Billion /10 * 1 MB + 10 Billion / 10 * 15 MB ~= 185 GB/sec</p><h2>API's</h2><p>Our service can expose the following REST API for posting a tweet and getting the news feed</p><pre><code>POST /users/{userId}/tweet
{
&#9;&#8220;api_key&#8221; : "test_key",
&#9;
&#9;&#8220;user_id&#8221; : &#8216;2244994945&#8217;&#8221;,
&#8220;tweet_text&#8221;: &#8220;My First Tweet&#8221;,
&#9;"entities": {
       "media": [
          {
          "id_str": "1494379920126095365",
          "media_url": "https://pbs.twimg.com/media/FL0anqqXsAU-KDH.jpg",
          "type": "photo"
        }
      ]
    }
}
</code></pre><p>api_key(String): The API key for the client making the API request, API Key is used for checking the access, rate-limiting the requests, and for analytics.<br>tweet_text(String) : 140 character tweet.<br>user_id(String) : unique identifier of the user.<br>media (Object): media related to the tweet.</p><p>Response: API will provide the URL of the created tweet if a JSON response with appropriate status</p><pre><code>HTTP OK 200
{
      "user_id": "2244994945",
      "created_at": "2020-02-14T19:00:55.000Z",
      "id": "1494379925427662851",
      "tweet_text": "My First Tweet",
     "entities": {
       "media": [
          {
          "id_str": "1494379920126095365",
          "media_url": "https://pbs.twimg.com/media/FL0anqqXsAU-KDH.jpg",
          "type": "photo"
        }
      ]
    }
}

</code></pre><p>In case of error is appropriate, an HTTP status code will be returned.</p><pre><code>GET /users/{userId}/timeline
</code></pre><p>Response: API will return the timeline of the user.</p><pre><code>HTTP OK 200

{
&#9;&#8220;tweets&#8221; : [
&#9;  "1494379925427662851": {
&#9;    "id_str": "1494379925427662851",
&#9;    "tweet_text": "This counts as an open source contribution, right?? &#128514;&#128514;&#128541; https://t.co/5alFRLFYvR",
&#9;    "entities": {
&#9;      "media": [
&#9;        {
&#9;          "id_str": "1494379920126095365",
&#9;          "media_url": "https://pbs.twimg.com/media/FL0anqqXsAU-KDH.jpg",
&#9;          "url": "https://t.co/5alFRLFYvR",
&#9;          "display_url": "pic.twitter.com/5alFRLFYvR",
&#9;          "expanded_url": "https://twitter.com/EddyVinckk/status/1494379925427662851/photo/1",
&#9;          "type": "photo"
&#9;        }
&#9;      ]
&#9;    },
&#9;    "source": "&lt;a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\"&gt;Twitter for iPhone&lt;/a&gt;",
&#9;    "user_id_str": "633958151",
&#9;    "retweet_count": 17,
&#9;    "favorite_count": 236,
&#9;    "reply_count": 15,
&#9;    "quote_count": 5,
&#9;    "conversation_id_str": "1494379925427662851",
&#9;    "lang": "en"
&#9;  }
&#9;, .....
&#9;]
}
</code></pre><h2>Database Schema</h2><p>We need to store the user data, tweet data, user_follower, etc, Here is the representation of the data.</p><h2>High-level design</h2><p>As It is evident from the estimations that we need to be able to store around<br>1700 tweets per second and read around 115k tweets per second. This is a very read-heavy system. Also in case of any major events, popular celebrity tweets, there can be very spiky traffic, hence we need to scale our system accordingly.</p><p>The figure shows the basic high-level design for the Twitter service, Requests land on the load balancers which distribute the traffic to the application servers.<br>Media files like photos and videos are uploaded to a separate media server and its metadata will be stored with the tweet.</p><p>At a high level, we will need the following components in our Newsfeed service:</p><ul><li><p>Web servers: receive the request for publishing the post via the REST API. The web servers check for valid authentication and authorization before allowing users to post. Besides this, there can be additional validations for example character limit, rate limiting, and anti-abuse check.</p></li><li><p>Tweet service: Tweets coming in are processed by the Tweet service. Tweets are written in a NoSQL DB like Cassandra.</p></li><li><p>Media Service: It is responsible for storing the media like photos and videos associated with the tweets. Media files are uploaded separately and then linked to the tweet.</p></li><li><p>Newsfeed generation service: It is responsible for creating the relevant news feed for all the active users. The feed will constantly keep on updating and new feed items will be reflected in the news feed of the user.</p></li><li><p>Notification service: It is responsible to notify the users about the updates from the people the user follows.</p></li><li><p>Analytics service: It is used to analyze tweets and generate trending items.</p></li></ul><h2>Home Timeline/ Newsfeed Generation:</h2><p>Home Timeline or the newsfeed is generated from the tweets of people that the user follows.<br>To generate the timeline we need to follow the below steps.<br>Query the social graph to get the list of users (followee), the current user follows.<br>Get User timelines for every followee.<br>Rank the tweets from this timeline based on various ranking parameters/signals in reverse chronological order.<br>Store the feed-in cache and return the first page of tweets.</p><p>There are two approaches to generate the news feed:<br>Online Generation: We generate the news feed when the user comes online and loads the home page.</p><p>Advantages: Simpler to implement</p><p>Disadvantages: This approach is not scalable as news feed generation is an expensive process. Also as new tweets constantly come in, we need a mechanism to rank and add new posts to the feed.</p><p>We will need to constantly check for updates of all the followers even though they might not have posted any tweets.</p><p>Offline Generation: instead of generating the news feed online when the user logs in, we can generate it beforehand offline, cache it and serve it to the user as soon as he logs in.<br>We can choose to keep the 200 latest tweets for each user, allowing the user to scroll through 10 pages of tweets on his homepage.</p><p>Advantages : Fast: Users need not wait for news feed generation.<br>Scalable: Since the system is not loaded when the user comes online, this approach is more scalable. Also, we need not store the news feed for all the users in the cache. We can store the timelines of the users that were active in the last 15 or 30 days. As we scale our product, we can add intelligent systems that can generate the news feed by predicting the login patterns of the user.</p><p>Disadvantage : Think about the scenario where a celebrity with millions of followers tweets, this can lead to the regeneration of millions of timelines and can put a heavy load on the system.</p><h2>Fanout</h2><p>If someone tweets, the timeline of all the followers is affected. Only if the users are active users.</p><p>Fanout is the process of delivering the tweet of a user to all of his followers. Two types of fanout models are:<br>fanout on writing (also called push model) and fanout on reading (also called pull model). Both<br>models have pros and cons. We explain their workflows and explore the best approach to<br>support our system.</p><p>Push Based (Fanout on write) - When one of the followers has published a post, we can push the post to all of his followers. There is a persistent connection(Use sockets for push, at any given point there are millions of such sockets getting data from the push cluster at twitter.) between the server and the client over which feed is pushed. There is a lot more processing on the Write ingest to figure out where to route a particular tweet.</p><p>Issues with this approach: As discussed in the feed generation part, for a celebrity with millions of followers tweets, it can put a heavy load on the system.</p><p>Pull Based (Fanout on reading) - When the user comes online, the client (web or mobile app) will pull the data after certain intervals or when the user refreshes the page.<br>Issues with this approach: New data will be shown only after a pull request is issued. and some pull might result in no data.</p><p>Hybrid Approach - Use a combination of the Pull and Push based approach. Wherein we push data for the users with a limited number of followers, we can limit that too only for the currently online users. For the celebrity user, the client will pull the updates.</p><p>The above image shows a detailed overview of the feed publishing mechanism.</p><p>References<br><a href="https://www.infoq.com/presentations/Twitter-Timeline-Scalability/">https://www.infoq.com/presentations/Twitter-Timeline-Scalability/</a></p>]]></content:encoded></item><item><title><![CDATA[Design Notification System]]></title><description><![CDATA[A notification system is a system to send notification alerts to the users.]]></description><link>https://systemdesigntutorial.substack.com/p/notification-system</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/notification-system</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:58:13 GMT</pubDate><content:encoded><![CDATA[<p>A notification system is a system to send notification alerts to the users. Notification Alert is a piece of information used to provide some updates to the user for example - updates about the ongoing order, new offering, meeting reminder, bill due alert, OTP notification, etc.</p><p>Notification systems are an integral part of any mobile or web application and act as a channel to communicate with users. Notification can be of multiple types, Mobile push notification, Email Notification, SMS, Whatsapp chat. Let us call them different notification channels</p><h2>Functional Requirements</h2><p>Notification Types: Push(Desktop, IOS, and Android), SMS, and email.</p><p>Notification Preferences: The system should respect User preferences or settings. For example, a user who has opted out of a particular type of notification should no longer receive the notifications.</p><p>Notification Scale: Our notification system should be able to handle a scale of close to 10 Million notifications per day.</p><p>Notification Prioritization - certain messages like OTP are higher priority, whereas promotional messages are lower priority</p><h2>Non Functional Requirements</h2><p>Always Available: Since we are using the Notification System to send critical information to the customer, the system should be always available.</p><p>Latency: Notifications should be delivered in almost real-time. Slight delays are acceptable when the system is under peak workload.</p><p>Pluggable: Our notification system should allow additional channels without much effort and redesign.</p><p>Scalable: the system should be scalable to cater to increased loads without much increase in latency.</p><p>Client Services: The services on the left represent different services that want to send notification messages via our Notification service. It can be an auth service, trying to send OTP over SMS or email channel, or it could be an order service trying to send order updates.<br>Application Servers: expose the API&#8216;s which client services can use to initiate notifications<br>Validate incoming messages for valid emails, phone numbers, etc.<br>Verify user settings if the user has opted out of such notifications.<br>Fetch other data required to send the notification for example notification templates, device info, etc.<br>Push message to the appropriate message queue for processing.<br>Cache: used for caching the user settings, device info, and notification templates to prevent frequent DB trips.<br>DB: to get info in case of a cache miss.</p><p>Message queues: Asynchronous way of handling the messages, messages are buffered in the queue till they are picked by the workers for processing. Different queues are used for different channels like email, SMS, etc.</p><p>Workers: the set of servers that pull messages from the messaging queue and process them one by one by sending them to corresponding services. Here are few examples</p><ul><li><p>SMS</p></li><li><p>Email</p></li><li><p>Mobile Push Notifications<br>a) iOS b) Android</p></li></ul><h3>API</h3><p>POST <a href="https://notification-api.example.com/api/v1/sms">https://notification-api.example.com/api/v1/sms</a></p><p>Request body</p><pre><code>{
&#9;"user_ids" : [
&#9;&#9;&#9;"ee0ad2aa-2f71-11ec-8d3d-0242ac130003", 
&#9;&#9;&#9;"980ea932-76ee-4816-b46e-4c26d99f0f9a",
&#9;&#9;&#9;"5cea9e39-f2f1-4277-aeaf-5eb344f4a7bd"
&#9;&#9;    ],
&#9;"sub": "The Big Festival sale starts in 10 minutes!",
&#9;"body": {
&#9;&#9;"type": "text/plain",
&#9;&#9;"value": "Don't miss the latest deals, visit abcd.com now!"
&#9;}

}
</code></pre><h3>Reliability</h3><p>Our notification system should be reliable so that the customer does not miss any update meant for him/her. Even in the case of peak traffic, we should avoid any data loss. Some amount of data delay is acceptable though. For this, we can employ acknowledgment and delivery semantics by acknowledging the message only in case the processing of the message was successful at the worker end. We can also keep an append-only log of all notifications for auditing purposes at the worker level.</p><h3>Analytics</h3><p>Analytics is an important component for notification systems as it will help us determine how effective our system is by collecting engagement statistics like open rate and click rate.<br>This will provide great feedback about the customer needs and help us understand the customer behavior effectively.</p>]]></content:encoded></item><item><title><![CDATA[Design URL Shortener]]></title><description><![CDATA[URL shortening Service creates a compact version of a long URL called tinyURL or shortURL.]]></description><link>https://systemdesigntutorial.substack.com/p/url-shortening</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/url-shortening</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:57:17 GMT</pubDate><content:encoded><![CDATA[<p>URL shortening Service creates a compact version of a long URL called tinyURL or shortURL. These URL's can be shared as an alias to the long URL and when someone hits this shortURL they are redirected to the original long URL. Benefits of short URL includes more readablity, convenience to the user and save space. They are much more user friendly when displayed on screen, shared, or tweeted.</p><p>URL shortening is also used for tracking and analytics purposes for performance measurements of different campaigns and affiliates. Some URL shortening services also allow yout to provide custom domains to promote your own brand.</p><p>Example:</p><pre><code>https://www.google.com/imgres?imgurl=https%3A%2F%2Fnationalzoo.si.edu%2Fsites%2Fdefault%2Ffiles%2Fnewsroom%2F649a1243-cropped.jpg&amp;imgrefurl=https%3A%2F%2Fnationalzoo.si.edu%2Fanimals%2Fnews%2Fcelebrating-national-panda-day&amp;tbnid=ExJDq27RAbY-3M&amp;vet=12ahUKEwiMnrW6pqbqAhUyFrcAHeVmDxsQMygDegUIARDYAQ..i&amp;docid=WNx1NKHAfQ_wJM&amp;w=4819&amp;h=2410&amp;q=panda%20image&amp;ved=2ahUKEwiMnrW6pqbqAhUyFrcAHeVmDxsQMygDegUIARDYAQ
</code></pre><p>can be shortened to this</p><pre><code>https://bit.ly/3i8Yitn
</code></pre><h2>Requirements and Goals of the System</h2><p>Requirements for a URL Shortening service.</p><h3>Functional Requirements:</h3><ul><li><p>Create a unique short URL for any given URL.</p></li><li><p>When user hits the shortened URL, he should be redirected to the original long URL.</p></li><li><p>Shortened URL must exprire after a configurable expriation time.</p></li><li><p>Based on the subscription user should be able to pick up a custom domain for the links to promote their own brand.</p></li></ul><h3>Non-Functional Requirements:</h3><ul><li><p>Our redirect service which redirects any short URL to the original URL must be highly available. Otherwise users won't be able to access the required URL.</p></li><li><p>Redirection should have minimum latency otherwise it will be a bad user experience and users might switch back to long URL.</p></li></ul><h3>Good to have features</h3><ul><li><p>Our service should provide analytics on the usage of shortened URL, like the total number of times it was hit, countries where it was hit, etc.</p></li><li><p>We can also expose our services throught REST endpoint to allow programmatic access to developers, supporting bulk requests.</p></li></ul><h3>Estimations</h3><p><strong>Traffic estimates</strong> :</p><p>We can assume that we will have around 10M new URL shortenings per day. There will more number of redirections to the original URL than the creation of new shortened URLs. Hence let's assume a 1:100 write to read ratio.</p><pre><code>Total number of new URL shortened per day = 10M
URL Shortening Queries per second = 10M/(24 * 3600) ~= 40M/(100 *3600) = 120QPS

Total number of redirection requests per day = 100 * 10M = 1B request/day
URL Redirection Queries per second = 100 * 120 QPS ~= 12K QPS

</code></pre><p><strong>Storage estimates</strong>:<br>Assuming that we 10M new URL shortened per day,</p><pre><code>Total number of records stored per day = 10M * 365 * 5 ~= 10M*400*5 = 20 Billion

Assuming 1 KB storage space per record,
Total memory required to store the data = 20B*1KB = 20TB

Assuming, 20% of the total read requests are cached
Total cache memory required = .2 * 12K *3600 *24 *1KB = 200GB

</code></pre><h2>API's</h2><p>Our service can expose the following REST API for creating and deleting shor URL's.</p><pre><code>generateShortURL(String longURL, String customDomain, String apiKey):
</code></pre><ol><li><p><strong>longURL(String)</strong> : The URL which needs to be shortened.</p></li><li><p><strong>Custom Domain (String)</strong> : Custom domain to be used for shortened URL. This could be a limited feature based upon the subscription of the client.</p></li><li><p><strong>apiKey(String)</strong> : The API key for the client making the API request, used for access check, rate limiting, and analytics purpose.</p></li></ol><p>API will provide a JSON response with the generated short URL.</p><pre><code>deleteURL(String shortURL, String apiKey):
</code></pre><ol><li><p><strong>shortURL(String)</strong> : The URL which needs to be deleted.</p></li><li><p><strong>apiKey(String)</strong> : The API key for the client making the API request, used for access check, rate limiting, and analytics purpose.</p></li></ol><p>API will provide a JSON response with appropriate status if url was deleted.</p><h2>Database</h2><p>We need to store billions of records over the time. Also our service is read heavy.</p><p>What about the consitency requirements? this is a very simple use case where we only create a new record for the shortened URL and then never update the same record. Hence we don't have strict consitency requirements.</p><p>What about the availability requirements ? Our service should be highly available otherwise users will not be able to access the original URL and will result into loss of business. Hence we want strong availability.</p><p>The trivial choice will be to use a NoSQL database like Cassandra, MongoDB or DynamoDB because we need a database that can easily scale. We can store the URL's in the following collection. To retreive the URL fast we can add index no the hash column.</p><pre><code>
   //URL Collection Sample Document:

    {
        "_id": "507f191e810c19729de860ea",
        "hash": "3i8Yitn",
        "original_url": "https://www.google.com/imgres?imgurl"
        "creation_date": "2020 Jul 11 16:54:20 UTC+5:30",
        "expiration_date": "2021 Jul 11 16:54:20 UTC+5:30",
        "user_id": ""
    }

    // User Collection Sample Document:
    {
        "_id": "507f1f77bcf86cd799439011",
        "name":"John Doe",
        "subscription_tier": "",
        "creation_date": "2019 Jul 11 16:54:20 UTC+5:30"
        "last_login_date": "2020 June 11 16:54:20 UTC+5:30"

    }

</code></pre><h2>High Level System Design</h2><h3>Approach</h3><p>Since we want to convert a long URL to a short one, we can choose to use 6-8 characters to represent our tinyURL. Let say we have a character set of 64 (0-9, a-z, A-Z , -, .), and<br>We can have a total of:</p><pre><code>    64^6 ~= 68 billion
    64^7 ~= 4.39 trillion
    64^8 ~= 281 trillion
</code></pre><p>Based upon our estimations, we are expecting around 20 billion URL's over the 5 years, we can go with 6 characters to represent our tinyURL.</p><p>The Naive approach to convert the URL will be to maintain a counter, increment that counter every time we have a new request for tinyURL. However this scheme is not suitable for a scalable service. Several servers will compete to get the new value of the counter and it will become a bottleneck.</p><p>Another approach could be to generate a random number, this approach could scale as different servers could generate random numbers in parallel. In Practical scenarios there is no possibility of collisions if we use the correct random number generator. We can use a UUID generator. However it will have 36 characters, if we take only first six characters, we can increase the chance of collisions. In such scenario we can check whether the first six characters are already used. In that case we can use the next 6 characters and so on until we get a unique tinyURL.<br>The problem with this approach is that if you make several requests for converting the same long URL, it will result into a different tinyURLs.</p><p>To overcome above problem, we could use any popular Hash Algorithm like (SHA-2 or MD5) to generate a hash of the original URL. We can encode the hash value to the base 64 and use the first six characters of the hash value. This way we would always generate the same hash value for different requests for the same long URL. But this approach could also generate duplicates, we can use the same approach descibed above by checking if the tinyURL is already used and belongs to the same long URL. If so, we can use the next six characters and repeat the process.</p><h3>Scaling the Key Generation.</h3><p>The problem with the above approach will be that the key generation generation can become a bottleneck as we scale our service. It would be time consuming to check whether the required key is already present in the database.</p><p>We can overcome this limitation by generating the keys in advance using. We can have a dedicated key generation service that will create a range of keys in advance. This range will be distributed across multiple nodes and will be loaded in memory, hence no processing time will be spent in hashing or encoding the URL.</p><p>Since each server has its own unique set of keys, we don't need to worry about any duplicates or coordination between the servers. When the request comes in to shorten a particular URL, the server will pick one of the keys and assign it to the URL, by making an entry into the database. It will then mark the key as used, or remove it from the memory.</p><p><strong>What happens if a server dies ?</strong> If the server dies, the range of keys will be lost. However that is absolutely fine, since we have huge number of keys.</p><p><strong>What happens if the Key Generation service fails</strong> ? Since we are now depending upon on the key generation service, it should have replicas to make it highly unlikely available in event of node failure.</p><h2>URL Redirection</h2><p>When the User enters the tinyURL in the browser, the request will hit our API, API will fetch the data from the database based upon the tinyURL ke. If the key is not present user will be shown a "404 Not Found" HTTP error, other wise, user will be redirected to the original URL that was shortened.</p><h3>URL Expiration</h3><p>Since we need to store data for the shortened URL's, we can choose to clean up URL's which were created long back based upon a certain expiration date and the frequency of usage. This expiration date could depend on subscription of the user who created the URL. This will clean up the data</p><p>For the Free tier users, we can have an expiration time of 6 month or 1 year from the last used time, before the data is purged. Similarly we can have a longer expiration time for paid users according to their subscription plan.</p><p>We can run a scheduled job every 24 hours that will cleanup the expired URL's from the database. The service should run at time when user traffic is low to prevent load on the database. For example, we can choose to run the job at midnight in each region. We can also reuse the key from the expired link.</p><h2>Data Partitioning and Replication</h2><p>In order to scale our DB, we need to introduce some partitioning scheme to store URL's. This will distribute our load to different database servers.</p><p>We can do one of the following:</p><ul><li><p>Partitioning based upon Range: We can do a range based partitioning by distributing URL's across partitions based upon the first letter of the URL. Although this is a simple approach, but it can lead to unbalanced partitions. If we have a lot of URL's that start from 'G', the server that stores these URL's can becom a hotspot.</p></li><li><p>Partitioning based upon Hash: Under this scheme, we can distribute the URL's among different DB partitions based upon a hash value. If our hash function can ensure uniform distribution the partitions will be balanced. We can use consistent hashing to avoid any problems related to uneven loads.</p></li></ul><h2>Caching</h2><p>Since our application is read heavy, we must use some caching strategies to store the hot URL's.<br>We can assume that almost 20% of the URL's will contribute to 80% of the traffic. We can store a mapping of the shortened URL and the original URL.</p><p>To store 20% of the hot URLs, we need to (200GB) of memory. We can use any popular caching system like Redis, or Memcached. We can create a distribute caching system to scale our caching layer using more servers.</p><p>Caching the server will reduce the load on the DB servers and improve the overall performance of the system.<br>As discussed in <a href="caching">Caching</a> tutorial. We can use a suitable cache eviction policy like Least Recently Used (LRU).</p><p>Similarly we can use a suitable cache write policy to update the cache in case of a cache miss. We can use the write around cache policy, initally data will be written directly tothe database while bypassing the cache. While reading recent data there will be a cache miss and will result in data being read from the disk. At this point data will put into the cache.</p>]]></content:encoded></item><item><title><![CDATA[Yelp System Design]]></title><description><![CDATA[Yelp is a platform for crowd-sourced local business reviews and listings.]]></description><link>https://systemdesigntutorial.substack.com/p/yelp</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/yelp</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:56:03 GMT</pubDate><enclosure url="https://www.systemdesigntutorial.com/img/geohash.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Yelp</strong> is a platform for crowd-sourced local business reviews and listings. Locations like restaurants, bars, and other businesses have dedicated pages, where users can read or submit reviews and ratings. Businesses can provide information about themselves on their pages so that people can discover their services and timings. In addition to reviews, Yelp also provides the ability to upload images and videos related to a location.</p><p>Users can search for any place or nearby attractions like restaurants, theaters as well as events. Other similar services include TripAdvisor (for travel), Zomato (for Restaurants)</p><h2>Functional Requirements</h2><ol><li><p>Business Profile - Yelp provides dedicated pages for each business, business owners or customers should be able to create a page for Businesses.</p></li><li><p>Search by Location or GPS coordinates - Users should be able to search for attractions by providing location and also able to search nearby places.</p></li><li><p>Reviews and Rating System - Users should be able to provide reviews for the places they have visited, services they have availed. Users should be able to provide ratings in terms of stars out of 5.</p></li><li><p>Photo and Video upload - Users should be able to add photos and videos related to the place.</p></li></ol><h2>Non Functional Requirement</h2><ol><li><p>High Availability: Our search service should be highly available.</p></li><li><p>Scalable: There will be peak demand on the Holiday season or certain tourist attractions.</p></li><li><p>Low Latency: Users should be able to access information as fast as possible.</p></li></ol><h2>Additional Requirements</h2><ol><li><p>Fake Reviews Detection: The system should be able to detect fake reviews by business owners that try to increase their ratings unethically.</p></li><li><p>Image and Video Processing: These services process the images and videos uploaded to provide the best photos to its users.</p></li></ol><h2>Estimations</h2><p>We need to store location entries, Assuming that we will be storing around 500M places. Let&#8217;s also assume a 20% growth in the number of places each year, we can safely assume that we don't need more than 1MB(excluding photos and videos) to store the metadata related to a location.</p><p>Our system will be read-heavy, we can assume a 1:1000 write to read ratio with around a peak of 100k concurrent users.</p><h2>Database Schema</h2><p>We can store the following data in a relational database. We can have a place/location table to store the place information</p><p><strong>Location Table</strong> :</p><pre><code>id : integer
name : varchar(256)
geohash : varchar(12)
description: varchar (512 bytes)
address : varchar (1024)
category_id: integer
</code></pre><p>Similarly, we will have tables for users, reviews, ratings, photos, and videos. We can store the photos and videos on the cloud blob storage like AWS S3 and maintain a reference in the photo and video table respectively.</p><h2>API's</h2><p>Our service can expose the following REST API for searching nearby places.</p><pre><code>search(String searchTerm, Map&lt;String, Object&gt; search params, String apiKey):
</code></pre><ol><li><p><strong>searchTerm(String)</strong> : The term which the user is searching for, it could be the name or location of the business.</p></li><li><p><strong>search params(Map)</strong> : search criteria, includes the location for which search needs to be performed, the search radius, additional filter params like categories, number of results to return, sorting order, etc.</p></li></ol><pre><code>    sample searchParams :

    {
        "location": [34.3, -118.243],
        "categories" : ["restuarent", "bar", "dine-out"],
        "searchRadius": 5,
        "maxResults": 100,
        "sortCritera": 1,
        "sortAscending" true,
    }
</code></pre><ol start="3"><li><p><strong>apiKey(String)</strong> : The API key for the client making the API request, used for access check, rate limiting, and analytics purpose.</p></li></ol><p>API will provide a JSON response for the list of places matching the search criteria.</p><p>We can expose additional API to Add/Modify new listing, add reviews, add ratings, upload photo/video to the listing, or a particular review.</p><h2>High-Level Design</h2><p>At a high level, The key feature of the Yelp system design is to find the nearby places/businesses with minimum latency and sort them by distance, because everyone wants to do business with the nearest store.</p><p>How to store the geographical context in the database.</p><p><strong>Naive solution</strong> - A naive solution will be to store latitude and longitude data for every POI, same for the user, and then compare the information of the user's location with the database. Given a certain range query, the database where latitude is between xi and xj and longitude is between yi and yj.</p><p>This approach will not be scalable given the huge number of places, that users can query for.</p><p><strong>GeoHash</strong>: Sequence of characters that identify a particular region to design a scalable system that provides geolocation data, we can use the concept of Geohash.</p><p>GeoHash essentially encodes the latitude and longitude information about a place to a String. The world map is divided into a rectangular grid system. Each rectangle is identified by a hash string and is further divided into 32 rectangles following a hierarchical structure, each level having an extra character than the hash of the parent rectangle. The "Geohash alphabet" (32ghs) uses all digits 0-9 and almost all lower case letters except "a", "i", "l" and "o".</p><a class="image-link image2" target="_blank" href="https://www.systemdesigntutorial.com/img/geohash.jpg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://www.systemdesigntutorial.com/img/geohash.jpg 424w, https://www.systemdesigntutorial.com/img/geohash.jpg 848w, https://www.systemdesigntutorial.com/img/geohash.jpg 1272w, https://www.systemdesigntutorial.com/img/geohash.jpg 1456w" sizes="100vw"><img src="https://www.systemdesigntutorial.com/img/geohash.jpg" data-attrs="{&quot;src&quot;:&quot;https://www.systemdesigntutorial.com/img/geohash.jpg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Geohash&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Geohash" title="Geohash" srcset="https://www.systemdesigntutorial.com/img/geohash.jpg 424w, https://www.systemdesigntutorial.com/img/geohash.jpg 848w, https://www.systemdesigntutorial.com/img/geohash.jpg 1272w, https://www.systemdesigntutorial.com/img/geohash.jpg 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Geohash represents a boundary and not a point. Since our mission is to calculate the nearby places, it is well suited for the application. Also, there will be some corner scenarios that we need to consider while determining the neighbors as shown in the below picture.</p><a class="image-link image2" target="_blank" href="https://www.systemdesigntutorial.com/img/geohash-corner-case.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://www.systemdesigntutorial.com/img/geohash-corner-case.png 424w, https://www.systemdesigntutorial.com/img/geohash-corner-case.png 848w, https://www.systemdesigntutorial.com/img/geohash-corner-case.png 1272w, https://www.systemdesigntutorial.com/img/geohash-corner-case.png 1456w" sizes="100vw"><img src="https://www.systemdesigntutorial.com/img/geohash-corner-case.png" data-attrs="{&quot;src&quot;:&quot;https://www.systemdesigntutorial.com/img/geohash-corner-case.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Corner Cases in Geohash&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Corner Cases in Geohash" title="Corner Cases in Geohash" srcset="https://www.systemdesigntutorial.com/img/geohash-corner-case.png 424w, https://www.systemdesigntutorial.com/img/geohash-corner-case.png 848w, https://www.systemdesigntutorial.com/img/geohash-corner-case.png 1272w, https://www.systemdesigntutorial.com/img/geohash-corner-case.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Geohashes are popular for spatial indexing and search applications thought initially were used only as part of URL-shortening service. If you have never heard the concept behind <strong>Geohash</strong>. I highly recommend viewing the following video.</p><p><a href="https://www.youtube.com/watch?v=UaMzra18TD8">https://www.youtube.com/watch?v=UaMzra18TD8</a></p><p>Let's return to our Yelp System Design scenario for finding nearby places and restaurants.</p><p>The naive algorithm to find out the nearest restaurants and businesses would be to find out the points with the same geohash as the location of the user. As discussed above due to corner cases, some of the nearby points of interest, but depending on the distance we want to search, likely leave out nearby points of interest that are in neighboring geohash bin.</p><p>To get correct and a more comprehensive set of points of interest, we have to get all the points in the current bin as well as all the points in the surrounding geohash bins i.e 8 immediate neighboring bins. Then we calculate the distance between all the points around the center.</p><p>Using Geohash based approach to find a point on the map, we can handle a very large dataset since on entering a particular geohash bin, we are essentially discarding the remaining bins, reducing the size of the problem exponentially. Users of a Yelp like service needs to see the results in real-time, hence we need to store and index the data about geohash of the places and associated reviews and ratings. Since the location data will not change, we need to implement the indexing for reading efficiency.</p><p>We can use a reasonable Geohash size of 12 characters to represent any place on earth with good precision. We can store the Geohash for locations in the database.</p><a class="image-link image2" target="_blank" href="https://www.systemdesigntutorial.com/img/geohash-vs-area.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://www.systemdesigntutorial.com/img/geohash-vs-area.png 424w, https://www.systemdesigntutorial.com/img/geohash-vs-area.png 848w, https://www.systemdesigntutorial.com/img/geohash-vs-area.png 1272w, https://www.systemdesigntutorial.com/img/geohash-vs-area.png 1456w" sizes="100vw"><img src="https://www.systemdesigntutorial.com/img/geohash-vs-area.png" data-attrs="{&quot;src&quot;:&quot;https://www.systemdesigntutorial.com/img/geohash-vs-area.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Geohash Precision&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Geohash Precision" title="Geohash Precision" srcset="https://www.systemdesigntutorial.com/img/geohash-vs-area.png 424w, https://www.systemdesigntutorial.com/img/geohash-vs-area.png 848w, https://www.systemdesigntutorial.com/img/geohash-vs-area.png 1272w, https://www.systemdesigntutorial.com/img/geohash-vs-area.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><h3>Storing GeoHash</h3><p>Let&#8217;s see what are different ways to store this data and find out which method will suit best for our use cases.</p><h4>Custom Approach</h4><p>Since this is hierarchical data, we can store the data in the form of a tree structure. Each node will represent a <strong>geohash</strong> and will 32 children for each of the nodes to model the geohash like structure.<br>We will have additional tables to store the metadata for any place corresponding to each geohash.</p><p><strong>Building the tree structure</strong></p><p>We will start with the root node that would represent the whole map. The root node will have 32 children. We will insert take the 12 character geohash of the place we want to insert into the tree and will create a node for each character. For example to insert a place represented by the geohash "9q8zhuyj1ccb1" we would create a node under root node to represent the grid "9", under that node we will create a child node to represent "9q" and so on.</p><p><strong>Querying the grid for a place</strong></p><p>We can start from the root node and search downward for the required node. At each node, we will move towards the child node with the next character in the geohash, Once we reach the leaf node, that is the required node representing the point of interest. If at any point we don't find the required node, then the point of interest does not exist in the grid.</p><p><strong>Finding out the neighbors</strong></p><p>To find out the neighbors of any point, we can go to the parent node, or grandparent node depending upon the radius for which we want to search for neighbors, find out all the points under them, Calculate their distance from the Point of Interest and then sort according to the given criteria.</p><p>We can optimize the data structure for finding the neighbors, by connecting the leaf nodes with a double linked list to allow quick forward and backward iterations among the neighboring places.</p><p>After getting the nearby geohashes, we can query our metadata table to find the details corresponding to those places.</p><p><strong>Memory Requirements</strong><br>Since each leaf node represents a place using a Geohash of 12 characters, Total memory needed to store the Tree Structure for 500M places will be :</p><pre><code>12 * ~1Byte * 500M = 6GB
</code></pre><p>Since we also have internal nodes in the tree, assuming around 1/3 internal nodes with 32 children, they will occupy:</p><pre><code>500M * 1/3 * 32 * ~1 Byte = ~5GB
</code></pre><h3>Use NoSQL offerings like Redis, ElasticSearch, MongoDB to store the Geospatial data</h3><p>Instead of writing custom logic to implement Geospatial search, we can use some popular NoSQL databases like Redis, Elasticsearch to achieve the same functionality.</p><p>For example, We can use the Geospatial indexing feature of the Redis database. to quickly implement this feature offloading the indexing, searching, and sorting work to Redis using very few lines of code.<br>We can use the Geo Set to work with spatial data in Redis, Redis provides commands like GEOADD, GEODIST, GEORADIUS, and GEORADIUSBYMEMBER.</p><p>More information can be found using the below linked<br><a href="https://www.infoworld.com/article/3128306/build-geospatial-apps-with-redis.html">Working with Geospatial Data in Redis</a></p><p>Similar support is provided by ElasticSearch and MongoDB databases. Going with elastic search gives full-text search as well as we need to search by the geographic tuple.</p><p><a href="https://medium.com/@yatinadd/going-geospatial-with-elasticsearch-using-geo-points-plus-its-application-b013c638064e">Working with Geospatial Data in ElasticSearch</a></p><p><a href="https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/">Working with Geospatial Data in MongoDB</a></p><h2>Data Partitioning</h2><p>If we are using NoSQL offerings like Redis, MongoDB, Elasticsearch, etc. We can easily scale horizontally as we scale and add new places.</p><p>If we are building a custom application using the Tree-based structure. We can use the following schemes for Partitioning our data.</p><ul><li><p><strong>Partitioning based on regions</strong>: We can divide the complete tree into multiple parts based upon the region. Places in the same region will be stored on the same node. Before storing a new location, we need to first find out which nodes store the data for a particular region, and then we can store a new location on those nodes, Similarly to querying the data.</p><p>The problem with this approach is that there will be certain regions like in San Francisco which will be densely populated and will have more restaurants and other Points of Interest. This will result in an uneven distribution of data and would cause more loads on the server than others. To prevent this problem, we might partition these hot regions further or use consistent hashing.</p></li><li><p><strong>Partitioning based on Hash</strong>: We can use consistent hashing to decide which server will hold the data of a region, we can use a hash function to hash the geohash of a place and then distribute the data uniformly across multiple servers.<br>to fetch all the nearby places, we will get the data from all servers and aggregate the data.</p></li></ul><p><strong>Processing the photos and videos</strong> Yelp stores the photos and videos and applies deep learning models to enhance them on the go.<br>More information can be found on the Yelp Engineering Blog.</p><p><a href="https://engineeringblog.yelp.com/2016/11/finding-beautiful-yelp-photos-using-deep-learning.html">Yelp Engineering Blog</a></p><p><strong>Spam detection</strong> : Similarly Yelp like services uses machine learning models to prevent false reviews and ratings.</p><h2>Caching</h2><p>As our service is read-heavy, there will be around 20% of places which are searched frequently. We can use a caching solution like Memcache or Redis to store this data. This will improve the performance of 80% search queries.<br>To deal with hot Places, we can introduce a cache in front of our database. We can use an off-the-shelf solution like Memcache, which can store all data about hot places. Application servers before hitting the backend database can quickly check if the cache has that Place. Based on clients&#8217; usage patterns, we can adjust how many cache servers we need. For cache eviction policy, Least Recently Used (LRU) seems suitable for our system.</p>]]></content:encoded></item><item><title><![CDATA[WebSockets]]></title><description><![CDATA[WebSockets is essentially a transport layer built on the top of the TCP/IP Protocol.]]></description><link>https://systemdesigntutorial.substack.com/p/websockets</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/websockets</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:51:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>WebSockets is essentially a transport layer built on the top of the TCP/IP Protocol. and provides a persistent bidirectional communication channel between a client and a server.<br>The initial connection is established similar to a normal HTTP request and then upgraded to a lightweight and real-time bidirectional channel which allows the server to send downstream messages to the connected clients.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg 424w, https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg 848w, https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg 1272w, https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638831/websocket_nqzpem.jpg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;WebSockets Connection&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="WebSockets Connection" title="WebSockets Connection" srcset="https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg 424w, https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg 848w, https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg 1272w, https://substackcdn.com/image/upload/v1648638831/websocket_nqzpem.jpg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>WebSockets allow exchanging messages using any protocol as long as both client and server agree on the same for example JSON, XML, and any more.</p><p>Before the WebSockets, achieving the same functionalities would require constant polling the server which is generally high latency and overloads the backend servers.</p><p>Starting from around 2010, WebSockets are supported on all platforms including web and mobile devices. The standard for the WebSocket Protocol (RFC 6455 &#8211; The WebSocket Protocol) was published by IETF in 2011.<br>Backend servers can go for any client authentication mechanism for example cookie-based, HTTP, or TLS authentication.</p><h2>WebSocets Usecases</h2><p>Websockets are used in a myraid of applications which demands low latency, realtime connection for exchanging data between client and server.</p><ul><li><p>Notification/ Chat messages</p></li><li><p>Multiplayer online games</p></li><li><p>Live Sports commentary / ticker</p></li><li><p>Realtime Social Network updates.</p></li><li><p>Realtime monitoring</p></li></ul><h2>Considerations while using WebSockets</h2><ul><li><p>Security Issues: WebSockets are prone to common security vulnerabilities linked to the HTTP Protocol such as DDos, Authentication and Authorization issues, Sniffing, Cross-Site Websocket Hijacking (Similar to CSRF (Cross-Site Request Forgery))</p></li><li><p>Connection Issues : WebSocket connection, once terminated don't recover automatically and need to be handled. However, Reconnection logic is generally handled by the available client side libraries.</p></li></ul><h2>WebSockets Implementations</h2><p>There are a plenty of popular WebSocket implemenations/libs available in the most popular languages, which makes it easy to get up and running easily.</p><ul><li><p>Javascript : WS, Sockets.io, SockJS</p></li><li><p>Java : javax.websocket-api, java.net.Socket, Jetty</p></li><li><p>Ruby : EventMachine, websocket-client-simple, em-websocket</p></li><li><p>Python : pywebsocket, Tornado</p></li></ul>]]></content:encoded></item><item><title><![CDATA[SQL vs NoSQL Database]]></title><description><![CDATA[A key aspect of any Large scale system is the ability to handle a large amount of Data.]]></description><link>https://systemdesigntutorial.substack.com/p/sql-vs-nosql</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/sql-vs-nosql</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:50:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A key aspect of any Large scale system is the ability to handle a large amount of Data.</p><p>A Database is used to persist information that will be useful later on. Broadly there are two types of database categories available to store the data and those are SQL or NoSQL. Which one to use depends on the needs of the system. For many years in the past SQL or Relational databases were the standard to store information. As the systems became more and more complex and the data grew many-fold NoSQL databases have gained their ground over the last decade and emerged as the primary data store for many applications. To understand which database is better for your needs to let's understand the difference between these two.</p><h2>SQL or Relational Databases</h2><p>A SQL or Relational database stores data in an organized set of tables with rows and columns. All the data related to a particular entity is stored in one table or more tables. A row stores all the related values for a particular instance of entity or object and each column stores one attribute of that object. Queries data using SQL syntax and JOINS. CRUD uses SCHEMA and transactions.</p><h2>NoSQL Databases</h2><p>NoSQL Database refers to Non Relational database, In contrast to a relational database where data is stored in well-defined tabular relations and Structured Query Language is used to access data, a NoSQL database uses different mechanisms for storage and retrieval. Different flavors of NoSQL use proprietary storage methods. Based on the problem they are solving NoSQL Databases store data in a wide variety of forms like key-value pairs, documents, columnar forms, graphs, or special time-sequenced events.</p><h2>SQL v/s NoSQL</h2><h3>Schema</h3><p><strong>SQL:</strong> Data is nicely organized in appropriate tables according to the predefined schema, which reduces redundant information. Data conforms to the applied constraints. Requires a lot of initial thought to minimize changes later which might require migration of existing data and related downtime. Structured data prevents developers from sloppily adding data, and constraints prevent the corruption of data due to software bugs. This translates to more effort for developers initially.</p><p><strong>NoSQL:</strong> schema is flexible, depending upon the type of NoSQL database, we might add or skip different columns and data in each document. It provides the flexibility of the data model to the developers allowing for easy iterations through the development process, without any downtime. On the flip side, it can lead to data corruption, if constraints and checks are not implemented correctly at the application layer.</p><h3>Storage</h3><p><strong>SQL:</strong> Data is typically stored across multiple tables in normalized forms to prevent duplication of data.</p><p><strong>NoSQL:</strong> Data is stored in a nested form with everything related to an entity stored in a single document. Though it introduces data redundancy but provides very fast access.</p><h3>Data Retrieval:</h3><p><strong>SQL:</strong> Due to structured data, the Relational database provides a powerful SQL(Structured Query Language) interface for Data definition, Data Control, and Data Manipulation. The split structure allows us to join data in any way. Numerous join types are available and can be done with any number of tables in any way.</p><p><strong>NoSQL:</strong> Since data for a particular entity is stored together, data access is simple and does not require any complex queries.</p><h3>Performance :</h3><p><strong>SQL:</strong> With proper use of indexes and query tuning, it is possible to query very high volumes of data with reasonable performance.</p><p><strong>NoSQL:</strong> Depending upon the type of NoSQL certain queries and access patterns are extremely optimized and fast. for example, the Key-Value database provides fast lookup, Column based database provides fast aggregation capabilities.</p><h3>Scalability:</h3><p><strong>SQL:</strong> SQL Database is powerful and flexible but constrained in terms of scaling. Scaling SQL Database systems require expensive hardware to scale vertically. If we distribute SQL over multiple servers using data partitioning, however, performance suffers for certain queries and joins. This can also be attributed to the fact that relational databases came into existence in the early 1970s, in those times the scale and traffic that we witness today were unheard of.</p><p><strong>NoSQL:</strong> These are built for web-scale applications and are easily scalable horizontally. queries. NoSQL does not use custom expensive hardware that allows for faster retrieval of data at a high scale. Since most of the NoSQL databases were created in recent times, they were developed cloud-native and with a distributed approach in mind. Almost all popular cloud vendors provide managed solutions that scale very easily according to growing traffic.</p><h3>Data Integrity:</h3><p><strong>SQL:</strong> Majority of SQL databases offer strong ACID compliance and ensure consistency and reliability of data that is stored, thus making them relevant for transactional data even today.</p><p><strong>NoSQL:</strong>, Unlike SQL counterparts, most NoSQL databases offer vague interpretations of ACID compliance and guarantees. These are majorly designed for scale and hence focus on Availability and Partition Tolerance by compromising on the consistency of the data. Always dig deep about the guarantees offered by the database that you are using before making any assumptions.</p><p>After discussing all the differences between SQL vs NoSQL, it is essential to note that in recent times, the lines between the SQL vs NoSQL has been blurring.</p><h4>SQL providing NoSQL like Features</h4><p>SQL databases like PostgreSQL and MySQL now provide support for storing, manipulating, and querying JSON Data. Similarly we can use PostgreSQL <a href="https://arctype.com/blog/postgresql-key-value-store/">HStore</a> to store key-value pairs.</p><h3>NoSQL providing SQL like Features</h3><p>MongoDB provides support for Joins and Transactions, although the underlying implementation and guarantees can be very different.</p><h2>Choosing between SQL vs NoSQL</h2><p>While designing a system or during a system design interview, it is of utmost importance to decide on the right database for storing data as it will allow you to get good performance out of your system. We need to choose the database that is the best fit for a particular use case.</p><p>For example: If you are designing a Banking application or other financial services-related application, It is a no-brainer to choose a Relational Database because of the data guarantees and security it has to offer which is a prime concern for this use case.</p><p>On the other hand, If you are designing a web crawler or search engine application, it is not advisable to use a Relational database due to the unstructured nature of web data and the scale at which you need to operate.</p><p>It is a common source of confusion for the new and in-experienced developers on whether to choose SQL or NoSQL while doing the required system design. So how to approach this?</p><ol><li><p>Always think in terms of the access patterns or operations that you will need, does the database supports that as a core feature ?.</p></li><li><p>What kind of Guarantees and Isolation level (for transactions) do you need for your application?</p></li></ol><p>Here is a list of Popular Use cases along with the most suitable Database types for each use case.</p><p>Sno Use case Choice of Database 1 Cache NoSQL inmemory datastore : Redis, Memcached 2 Banking Application SQL Database 3 Social Network NoSQL Graph Database: Neo4j 4 Facebook Ad Platform NoSQL columnar Databse: Cassandra, BigTable</p><p>Below is Genius Cheatsheet created by <a href="https://www.linkedin.com/in/scgupta/">Satish Chandra Gupta</a>, that can help you in picking the correct DB for your use case.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png 424w, https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png 848w, https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png 1272w, https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Choosing the SQL vs NoSQL DB&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Choosing the SQL vs NoSQL DB" title="Choosing the SQL vs NoSQL DB" srcset="https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png 424w, https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png 848w, https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png 1272w, https://substackcdn.com/image/upload/v1650127832/SQL-vs-NoSQL/choosing_the_db_isajwh.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Here is a list of all the Popular Database and their rankings - <a href="https://db-engines.com/en/ranking">DB Ranking</a></p>]]></content:encoded></item><item><title><![CDATA[Scaling]]></title><description><![CDATA[Scaling means increasing the system capacity to cope up with increased load for example number of requests or handling more data.]]></description><link>https://systemdesigntutorial.substack.com/p/scaling</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/scaling</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:48:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Scaling means increasing the system capacity to cope up with increased load for example number of requests or handling more data.</p><h2>Need for Scaling</h2><p>If our system is currently handling 100 requests/min and suddenly the traffic increases to 1000 requests/min, even though our system might be able to handle the same the traffic but due to increased load, the performance might become unacceptable, hence we will need to scale our system to be able to support that additional load with acceptable performance.</p><p>The performance of a system can be measured using <a href="https://www.systemdesigntutorial.com/glossary#throughput">throughput</a><br>and <a href="https://www.systemdesigntutorial.com/glossary##response-time">response time</a>.</p><h2>Type of Scaling</h2><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png 424w, https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png 848w, https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png 1272w, https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648640898/scaling_ffczq5.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Scaling&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Scaling" title="Scaling" srcset="https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png 424w, https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png 848w, https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png 1272w, https://substackcdn.com/image/upload/v1648640898/scaling_ffczq5.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><h3>Vertical Scaling (Scaling Up)</h3><p>Increasing the capacity to the current machine by improving its specifications like attaching more RAM, adding better CPU, upgrading HDD to SSD or adding more storage capacity, increasing network bandwidth etc.</p><h4>Advantages:</h4><ul><li><p>Architecture - Relatively simple, Less issues to worry about like inter service dependencies, network partitions etc.</p></li><li><p>Maintenance - Easy to maintain due to less moving parts.</p></li></ul><h4>Disadvantages:</h4><ul><li><p>Cost - Adding more resources leads to exponential increase in cost.</p></li><li><p>Scope - Limited scope of Scaling due to physical limitation of size.</p></li><li><p>Downtime - Risk of fault or complete downtime due to hardware failure.</p></li></ul><h3>Horizontal Scaling (Scaling Out)</h3><p>Adding more machines and distributing the load among them.</p><h4>Advantages:</h4><ul><li><p>Cost - Cost efficient due to the use of commodity hardware.</p></li><li><p>Scope - Theoretically unlimited scope of scaling.</p></li><li><p>Resilience and Fault Tolerant - Due to redundancy, if few nodes go down, other nodes take up the requests.</p></li></ul><h4>Disadvantages:</h4><ul><li><p>Architecture - Complex to design, due to fallacies of distributed systems.</p></li><li><p>Maintenance - Difficult to observe and maintain due to moving parts.</p></li></ul><h3>Which approach is the best ?</h3><p>There is no single correct answer to this, the choice of how to scale a particular system depends specifically on our system. The bottleneck for the system may be the number of requests, the volume of data , the access patterns or something else.<br>Depending upon whether you are targeting sub 200ms response time on your API&#8217;S or trying to manage petabytes of data, The System design and scaling strategies will be entirely different.<br>It is not possible to find an approach that fits all.</p><p>In general, it is easy to distribute a stateless system across multiple machines. Managing and synchronizing state within shared systems can introduce a lot of additional complexity and becomes hard to design and maintain.</p><p>It is a good practice to use a hybrid approach. To keep it simple, We can first scale vertically by using relatively powerful machines till we reach a point where it makes sense to use a large number of smaller machines.</p><p>Also, it is an ongoing process, a system designed for a particular load may not be able to handle 5x or 10x the load. At each milestone, we need to optimize, monitor and constantly re-architect on every magnitude of increased load.</p>]]></content:encoded></item><item><title><![CDATA[Rate Limiting]]></title><description><![CDATA[Rate Limiting means to throttle the number requests to your service from a particular source (user, device, IP, location, etc) to some maximum limit.]]></description><link>https://systemdesigntutorial.substack.com/p/rate-limiting</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/rate-limiting</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:42:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vqWx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vqWx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vqWx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vqWx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vqWx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vqWx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vqWx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rate Limiting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rate Limiting" title="Rate Limiting" srcset="https://substackcdn.com/image/fetch/$s_!vqWx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vqWx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vqWx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vqWx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8a511dab-c205-44c4-8a0b-3c7ccb655e23_2000x1333.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>Rate Limiting means to throttle the number requests to your service from a particular source (user, device, IP, location, etc) to some maximum limit. The requests submitted over the limit are either immediately rejected or they are delayed. Rate limiting allows us to create resilient services, that can handle various scenarios discussed below.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png 424w, https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png 848w, https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png 1272w, https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638815/rate-limiter-working_ljxlgf.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rate Limiting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rate Limiting" title="Rate Limiting" srcset="https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png 424w, https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png 848w, https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png 1272w, https://substackcdn.com/image/upload/v1648638815/rate-limiter-working_ljxlgf.png 1456w" sizes="100vw"></picture><div></div></div></a><p>The simplest example of rate limiting is that you are allowed to enter 3 incorrect passwords before your bank account is locked for online transactions for a day. Similarly, the Github API allows around 5000 requests from a user account per hour, after that, you will get an error to wait for some time before sending another request.<br>Services typically send 503 (service unavailable) or 429(Too many requests) Http status code when the limit is exceeded.</p><h2>Advantages of Rate Limiting</h2><ul><li><p>Security: Rate Limiting is implemented to prevent DDoS attacks which overwhelms the servers which are a costly affair for any company both in terms of money and customer experience. Rate limiting your APIs will also limit the amount of data that gets exposed when security is somehow compromised.</p></li><li><p>Protection against faulty Clients: Rate Limiting is also useful to make your system foolproof against any faulty or malicious client software, which might be sending a lot of requests due to a software bug.</p></li><li><p>Cost Optimization: Rate Limiting Computationally intensive API will help optimize the cost of infrastructure.</p></li><li><p>Maintain Quality: If you have a public API, implementing rate limit offers a better experience for all the users by distributing the number of requests that can be served. It prevents the Noisy Neighbour problem when one user utilizes too much-shared resources, such that it causes higher latency or higher failure rates.</p></li><li><p>Maintain Priority: Several API's have paid version and free version, by assigning lower rate limits to the free users, you can make sure that Paid users get a better experience for their money.</p></li></ul><h3>Types of Rate Limiting</h3><p>Due to ever-increasing loads on the servers for popular services, almost all companies use rate-limiting in their services, If you are also creating such services, it is very important that you add some form of rate limiting to prevent your infrastructure and customer base.</p><p>We can implement rate limiting based upon various methods and parameters that can be defined when setting rate limits. Based on the security and business requirements, we can choose one of the following criteria.</p><ul><li><p><strong>User rate limiting</strong>: This is the most popular criteria used for rate limiting. Based on the number of requests from a particular User's IP or API Key, requests will be throttled once the limit is reached. Users will be shown the appropriate status code to reflect that their requests are throttled.</p></li><li><p><strong>Geographic rate limiting</strong>: Based upon the security or business requirements, We can set rate limits based on the geographic regions, This could reduce the likelihood of DDOS attacks and other suspicious activity.</p></li><li><p><strong>Resource Based Rate limiting</strong>: We can also employ flexible Rate Limits based upon the amount of resources available like CPU, network bandwidth, etc. When resources are constrained, we can reduce the rate limits, Once the resources are available, we can increase the rate limits.</p></li><li><p><strong>Hybrid rate limiting</strong>: We can combine certain characteristics of the above rate limiting algorithm to achieve optimum results, like a user can send 100 requests per second from a particular IP address in a geographic region.</p></li></ul><h2>Requirement Gathering.</h2><h3>Function Requirements</h3><ul><li><p>For a given request, return a boolean value of whether the request is throttled or not.</p></li></ul><h3>NonFunction Requirements</h3><ul><li><p><strong>Low Latency</strong> - We want the rate limiting module to be as fast as possible, otherwise it adds latency to the processing time of the request.</p></li><li><p><strong>Accurate</strong> - Our rate limiting module should be as accurate as possible. It should not be throttling requests which should have actually gone through.</p></li><li><p><strong>Scalable</strong> - Our service should be highly scalable to support more number of requests.</p></li></ul><p>In case the rate limiting service is not available, the system should not block the processing of the request.</p><h3>Rate Limiting Algorithms</h3><ul><li><p><strong>Token Bucket Algorithm</strong> - In this algorithm, we place n tokens in a bucket, every bucket has a maximum capacity and is filled at a constant rate, ex 10 tokens pers second. When a request for our API is received, tokens are withdrawn from the bucket if available. If required number of tokens are not available then the request is rejected or delayed. Eventually, the bucket is refilled with tokens and the client can make more requests to our API. Please note that depending upon the operations in the API request, one request might need more than one token.</p></li></ul><ul><li><p><strong>Leaky Bucket Algorithm</strong> - It is one of the simplest rate limiting algorithms, It implements rate limiting using a bucket concept per user (depending upon the factor on which rate limiting is applied) which holds all the incoming requests of that user. The bucket is of the fixed size corresponding to the number of requests we want to allow. When a request is received, it is put into the bucket. Requests are picked up from the other end of the bucket (more like a queue than a bucket) for processing. If the number of requests at any point in time exceeds the size of the bucket, those will leak and will not be considered. It can lead to queueing up of old requests if they take long time to process and prevent recent requests from processing.</p></li></ul><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rate Limiting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rate Limiting" title="Rate Limiting" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-leaky-bucket_y1xcff.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><ul><li><p><strong>Fixed Window Algorithm</strong> - As the name suggests, in this algorithm, we fix a time window over which the rate limiting is imposed. Irrespective of the actual time the request came in, the count is maintained for fixed time windows of x seconds.</p></li></ul><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rate Limiting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rate Limiting" title="Rate Limiting" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-fixed-window_xsdjuc.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Consider the example in the above image where the rate limit is 3 requests per second, the first request came in after 500 ms. It is clearly visible from the previous example, that from 500ms to 1500 ms, the server actually serves 4 requests this might lead to a rate limiter allowing extra of requests if more such requests come near the boundary of the time window. The next algorithm will overcome this issue with Fixed Window Algorithm.</p><ul><li><p><strong>Rolling Window Algorithm</strong> - In the Fixed Window Algorithm, the reference time window was fixed, however in case of a rolling window, the time window starts when the first request is received. The rate limit is imposed based upon the number of requests that came in from the start of the window to the end of the window. This will improve the performance around the boundary of the time zone.</p></li></ul><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rate Limiting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rate Limiting" title="Rate Limiting" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-sliding-window_umwezi.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>As shown in the image above, now the time window starts at 500 ms when the first request comes in, and it allows us to serve only 3 requests R1, R2, R3 for that second. Thus request R4 is throttled, after 1 second is complete, the new time window starts once a new request comes in.</p><h2>High Level Design for Rate Limiter.</h2><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rate Limiting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rate Limiting" title="Rate Limiting" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-high-level-design_mwfwwh.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>The above diagram shows the High level design for a rate limiting system. When the Webserver receives the request it would ask the Rate Limiter service, whether the request should be served or throttled. If the request is not throttled, it will be forwarded to one of the API servers.</p><h2>Distributed Rate Limiter Design</h2><p>The true benefits of rate limiters are predominant in a distributed system, where we are operating on a large scale, and we want to have efficient use of our resources. In a distributed system, there will be multiple API servers serving requests from users.</p><p>Due to the number of requests, we would need to scale our rate limiter service by distributing over multiple nodes. In each of the algorithms, described above we store some kind of count that constraints the number of requests that will be served. How can we achieve this if our counters/buckets are distributed over multiple nodes?</p><p><strong>Communication between the hosts is the key here</strong>. We want to implement the rate limit at a global level, hence we need a way to synchronize among different cluster nodes.</p><p>We can use a global data store to maintain the counters for each window and user. But having a global data store is a bottleneck as all the nodes will flock to the data store to get the count. The worst cases could lead to race conditions. If one of the nodes has read the value of the counter before it could update the counter, another node could read the same value and the final value will be incremented by only one instead of two.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rate Limiting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rate Limiting" title="Rate Limiting" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-race-condition_swcpt4.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>We could avoid race conditions by introducing locks. However, that could degrade the performance of the system and it will not scale well. Querying the central data store will add the additional overhead of several milliseconds for each request.</p><p><strong>Can we do better ?</strong><br>Instead of querying the centralized data store for each request, we can maintain the counters locally. Let's take an example: If our rate limit is 4 requests per second per user. We can have a limit of 4 requests per second per user on each server as shown in examples below.</p><p>Global Limit : 4 req/sec/user</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rate Limiting&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rate Limiting" title="Rate Limiting" srcset="https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png 424w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png 848w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png 1272w, https://substackcdn.com/image/upload/v1648638814/rate-limiter-distributed_n5gwct.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>Let's say Server1 received 2 requests in a particular second, Server2 received 1 request and Server3 received 1 request. Since each server is maintaining the counters locally, If 2 more requests come to Server1, it would allow the API call. Thus our rate limiter has served 6 requests per second for the user.</p><p>This scheme will lead to relaxed rate limits. However, each node can synchronize the counters with a central data store eventually as a part of the synchronization cycle. If a particular node had served more requests in a time window it would receive a negative counter value for the future time window and hence the average number of requests served per second per user will still be the same as the original limit. We can configure the interval between synchronization cycles to achieve optimal results.</p><h2>References and interesting reads.</h2><p><a href="https://cloud.google.com/solutions/rate-limiting-strategies-techniques">Rate Limiting Techniques</a></p><p><a href="https://blog.figma.com/an-alternative-approach-to-rate-limiting-f8a06cf7c94c">Alternative Approach to Rate Limiting</a></p>]]></content:encoded></item><item><title><![CDATA[NoSQL]]></title><description><![CDATA[NoSQL Database refers to Non Relational database, In contrast to a relational database where data is stored in well defined tabular relations and Structured Query Language is used to access data, NoSQL database uses different mechanisms for storage and retrieval and generally does not provide SQL based data access and hence the name "Non-SQL", Although some of NoSQL implementations may provide SQL kind of interface, hence NoSQL is also sometimes called as "Not Only SQL".]]></description><link>https://systemdesigntutorial.substack.com/p/nosql</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/nosql</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:33:45 GMT</pubDate><content:encoded><![CDATA[<p>NoSQL Database refers to Non Relational database, In contrast to a relational database where data is stored in well defined tabular relations and Structured Query Language is used to access data, NoSQL database uses different mechanisms for storage and retrieval and generally does not provide SQL based data access and hence the name "Non-SQL", Although some of NoSQL implementations may provide SQL kind of interface, hence NoSQL is also sometimes called as "Not Only SQL".</p><p>NoSQL Database doesn't use relational tables or schema. Different flavors of NoSQL use proprietary storage methods. Based on the problem they are solving NoSQL Databases store data in a wide variety of forms like key-value pairs, like documents, in columnar form, in the form of graphs or special time-sequenced events.</p><p>NoSQL Database is inherently designed for Availability and Partition tolerance over Consistency, however, different vendors do provide the support to increase the level of consistency by trading off other attributes.</p><p>NoSQL database has gained a lot of popularity in the past decade as it is designed for overcoming limits of scale and providing the ability to scale horizontally. These are created by large web companies and a lot of popular ones open-sourced. NoSQL database is highly prevalent in the Cloud Data storage model. Major cloud vendors like AWS, GCP, and Azure provide managed NoSQL solutions with enterprise-level support and tooling.</p><h2>Characteristics of NoSQL</h2><ul><li><p><strong>Scale</strong>: NoSQL databases can store data on very large scales. These are designed and optimized for specific uses cases at a super large scale, something that cannot be achieved by the relational database. This helps in scaling your data storage to such extent and allowing fast retrieval for specific queries. NoSQL database is used for solving problems like indexing the web for search engines like Google and Bing, predicting customer behavior for analytics and ads on platforms like Google and Facebook or backing recommendation systems for Netflix.</p></li></ul><ul><li><p><strong>Flexibility and Ease of Use</strong>: Another reason for widespread popularity is the flexibility of use, Due to flexible schema, these are best to get started without putting much thought about the structure initially.</p></li><li><p><strong>OpenSource and Community Driven</strong>: Most of the popular NoSQL database are open-sourced, and provide community editions of these products.</p></li></ul><h2>Types of NoSQL Database</h2><p>Following are the popular types of NoSQL database:</p><ul><li><p><strong>Key-Value NoSQL</strong>: These databases store huge lists of key-value pairs, where the key represents the <strong>field or attribute</strong> name, and the value represents the <strong>value</strong> of that field. These store hot datasets mostly for caching or lookup purposes. They provide extremely fast access through in-memory storage options. Most popular key-value stores include DynamoDB, Redis, Memcached, and Voldemort.</p></li><li><p><strong>document-oriented NoSQL</strong>: Data is stored in the form of documents, the documents are further grouped in collections. In contrast to the rows in a Relational Database, where each row stores data for a fixed number of columns, The structure of each document in Document-Oriented DB is flexible and does not need to be the same as other documents. These databases are fast for querying and easily scalable, Most popular Document Datastore are MongoDB, AWS DynamoDB(can act as both key/value and Document) CouchBase, and Elasticsearch.</p></li><li><p><strong>Columnar NoSQL</strong> : Data is stored in columnar families, unlike conventional database this database allows us to read specific column values. Best used for storing ragged datasets, for purpose of aggregation. These are extremely fast and scalable and suited for analytical applications. Most popular Columnar databases are Cassandra by Facebook, BigTable, and BigQuery by Google Cloud Platform.</p></li><li><p><strong>Graph NoSQL</strong> : Data is stored in the form of graph bases structures to store entities and their properties. These are extremely fast for relationship queries and easily scalable. Examples include Neo4j and AWS Neptune DB.</p></li><li><p><strong>Speciality NoSQL</strong> : These are specialty databases created and optimized for certain special types of data like Time based events, IoT events, and blockchain ledger data.<br>Example of this type of database includes Google Cloud Platform Firebase/Firestore, AWS IoT, and AWS QLDB.</p></li></ul><h2>Trends in NoSQL Landscape</h2><p>As NoSQL database are becoming widely used, different vendors are adding multiple features within the same offering, like the AWS DynamoDB can act both as a Key-Value and Document Database. Redis has support for in-memory and persistence on the disk. New features are getting added for elastic scaling, backup, and monitoring.</p>]]></content:encoded></item><item><title><![CDATA[Messaging System]]></title><description><![CDATA[Messaging System is a common approach to transfer data between systems and applications.]]></description><link>https://systemdesigntutorial.substack.com/p/messaging-system</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/messaging-system</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:32:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Messaging System is a common approach to transfer data between systems and applications. A producer generates a message containing some information that is transmitted to the consumers.</p><p>We can do this via direct communication via TCP connection between producer and consumer. However, such a system allows communication between exactly one producer and one consumer. Also what happens if producers are producing messages faster than the consumers can consume, And what happens if consumer nodes go down?</p><p>Depending upon the application, a popular way to send a message is via a message broker or a queue.</p><h2>Message Brokers</h2><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png 424w, https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png 848w, https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png 1272w, https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638802/messaging-system_kxjlpe.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Messaging System&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Messaging System" title="Messaging System" srcset="https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png 424w, https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png 848w, https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png 1272w, https://substackcdn.com/image/upload/v1648638802/messaging-system_kxjlpe.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>Message brokers (or Queues) run as servers sitting between producers and consumers. Producers can produce messages to the message broker. On the other hand, consumers can receive messages from the broker. Thus it helps to decouple different interacting systems providing them an asynchronous way of messaging.</p><p>One or more producers can communicate with one or more consumers using such messaging systems.</p><p>Producers can now produce data at any rate without worrying about the rate at how data is consumed or who is consuming the data.</p><p>These systems can tolerate consumers going down and coming back and messages can be persisted in the broker from the time they are produced by the producers till the time they are consumed or even later depending upon the implementation.</p><h2>Messaging Patterns</h2><p>Two common ways of handling the Messages are <strong>Load balancing</strong> and <strong>Fan-out</strong></p><h3>Load Balancing</h3><p>Each message in the queue is delivered to exactly one of the consumers. In general, multiple consumers will be present in the system parallelizing the processing of the messages from the queue. Once the message is extracted by the consumer, it is removed from the queue. This pattern is generally used to process messages that result in long-running tasks and you want to achieve some parallel processing.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png 424w, https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png 848w, https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png 1272w, https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Load Balancing&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Load Balancing" title="Load Balancing" srcset="https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png 424w, https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png 848w, https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png 1272w, https://substackcdn.com/image/upload/v1648638802/messaging-system-loadbalancing_bhz7ff.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>For example, Your credit card company generates a statement of transactions at the end of the billing cycle and sends an email with the statement. Such a system can be implemented by a scheduler that fetches user information from the DB and generates a message in the queue for processing data for each customer. A separate application receives this information, fetches data about the user transactions, crunches numbers, generates the statement, and delivers the email in our inbox.</p><h2>Fan-out</h2><p>Each message in the broker is delivered to all the consumers, allowing multiple consumers to listen to the same message and perform different tasks. This pattern is used to process messages that have to be handled in multiple ways by different systems.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png 424w, https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png 848w, https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png 1272w, https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Fan-out&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Fan-out" title="Fan-out" srcset="https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png 424w, https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png 848w, https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png 1272w, https://substackcdn.com/image/upload/v1648638802/messaging-system-fan-out_hmxiuu.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>For example: While logging in to some applications, we receive the same passcode via text and email. Such a system can be implemented by sending all passcode messages through a messaging system and having <strong>Text messaging application</strong> and <strong>Email messaging application</strong> listen to the same message.</p><h2>Hybrid</h2><p>We can combine the two patterns by using two consumer groups ( containing multiple consumers) listening to the same messages, such that each consumer group receives all messages but within a consumer group only one of the nodes receives each message. Kafka uses such an implementation.</p><h2>Benefits of using a Messaging System:</h2><p>Buffering: Messaging Systems provide a way to buffer the messages, persisting them in the broker (in memory or disc) while they wait to be processed by the consumers. Allowing our systems to deal with sudden traffic spikes up to some extent.<br>Message Delivery Guarantees: Using techniques described above messages are guaranteed to be delivered and processed by the consumers before they are marked as processed allowing retries in failure scenarios.<br>Easy Scaling: Because of the asynchronous nature of messaging systems and fault tolerance and configurability, it is rather easy to scale software systems that use messaging systems for communication, allowing multiple producers to communicate with multiple consumers.<br>Separation of concerns: Using a messaging system allows us flexibility in architecture by making the producers and consumers independent.</p><h2>Consideration while using a Messaging System:</h2><h3>Back Pressure.</h3><p>What happens if our system is under heavy sustained load, There are large amounts of messages flowing into the broker and it starts to grow significantly. Depending upon the server configuration, if the size becomes larger than the memory, it will result in cache misses and expensive disk reads. Having the entire system degrade under such circumstances is not the way to build scalable and fault-tolerant systems. Hence if the arrival rate is more than what the system can cope up with, can use backpressure in such scenarios by helping limit the queue size preventing throughput degradation, and maintaining good response times for the message processing. Once the broker gets filled, it can return a <strong>service unavailable</strong> using the HTTP status code 503, indicating to try at a later time.</p><p>Here is an interesting read on the this topic :</p><p><a href="https://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html">Apply back pressure</a></p><h3>Acknowledgement and redelivery</h3><p>If consumers crash while processing a message, which may result in loss of message. However, message brokers implement acknowledgments wherein the consumer must assure the message broker that it has completed processing the message and that broker can mark it as consumed and remove it from the queue.</p><p>If the broker does not receive the acknowledgment, it will assume that the message did not get processed successfully and it will redeliver the message again until it is processed with an acknowledgment. We will learn more about this when we discuss implementations of different messaging systems like Kafka, RabbitMQ, etc.</p>]]></content:encoded></item><item><title><![CDATA[Load Balancing]]></title><description><![CDATA[Load Balancing means efficiently distributing the network traffic across multiple machines to balance out the load and prevent any hotspots.]]></description><link>https://systemdesigntutorial.substack.com/p/load-balancing</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/load-balancing</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:19:31 GMT</pubDate><enclosure url="https://www.systemdesigntutorial.com/img/lb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Load Balancing means efficiently distributing the network traffic across multiple machines to balance out the load and prevent any hotspots. Load Balancers also keep track of servers which are not functional and avoid sending requests to those machines. When the server comes back up or new servers are added, Load Balancer will resume the distribution of traffic to that server again.</p><p>Apart from routing, Load balancers might also do other activities like <strong>SSL termination</strong>, which means SSL traffic is decrypted by the load balancer and then passed on to the server. This saves the work of SSL decryption on the web servers improving performance and reducing latency.</p><p>Load balancing can happen at Layer 4 (L4- Transport) or Layer 7 (L7- Application) of the OSI Model. Also, there are different kinds of Load Balancers.</p><h2>Hardware Load Balancer or Software Load Balancer</h2><p><strong>Hardware-based load balancers</strong> are high-performance solutions where specialized processors are used to achieving optimum performance. To handle increased loads you need to buy more machines. Hence they are generally a costly solution and require specialized maintenance. Due to the above limitations, these are not very popular. The most widely used ones are Citrix Netscaler and F5.</p><p><strong>Software-based load balancers</strong> uses generic hardware to run and are easy to scale and flexible in configuration. Some of the popular ones come as installable software and others are provided as a Service called Load Balancer as a Service (LBaas). Popular software-based load balancers included Nginx, Varnish, HAProxy, and LVS. Popular LBaas include AWS ELB and Stratoscale LBaas. LBaas are managed by the cloud vendors and handle fault tolerance and elasticity for the users.</p><p>Check out the following <a href="https://www.youtube.com/watch?v=bxhYNfFeVF4">video of how Facebook handles billions of request using their unique load balancing techniques.</a></p><h2>Load Balancing Layer</h2><p>Load Balancing can be introduced at any layer in the application increase scalability depending upon the number of requests served.</p><a class="image-link image2" target="_blank" href="https://www.systemdesigntutorial.com/img/lb.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://www.systemdesigntutorial.com/img/lb.png 424w, https://www.systemdesigntutorial.com/img/lb.png 848w, https://www.systemdesigntutorial.com/img/lb.png 1272w, https://www.systemdesigntutorial.com/img/lb.png 1456w" sizes="100vw"><img src="https://www.systemdesigntutorial.com/img/lb.png" data-attrs="{&quot;src&quot;:&quot;https://www.systemdesigntutorial.com/img/lb.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Load Balancing&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Load Balancing" title="Load Balancing" srcset="https://www.systemdesigntutorial.com/img/lb.png 424w, https://www.systemdesigntutorial.com/img/lb.png 848w, https://www.systemdesigntutorial.com/img/lb.png 1272w, https://www.systemdesigntutorial.com/img/lb.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><ul><li><p><strong>Frontend Layer</strong> - The most common use case of the load balancers is at the frontend of the application, between the client and the webserver. This helps to increase the number of requests that can be served by the system. Also, SSL Termination is done here to save CPU cycles of the application server.</p></li><li><p><strong>Application Layer</strong> - Load Balancers are placed between the webserver which is taking the requests and the application servers which are doing CPU intensive tasks. This helps in the proper utilization of the application servers without overloading them.</p></li><li><p><strong>Persistance Layer</strong> - Load Balancers are placed between the Application Layer and the Persistence Layer to serve more data requests without overloading Database servers.</p></li></ul><h2>Load Balancing Algorithms</h2><p>Several popular load balancing algorithms are used by Software-based load balancers. Depending upon the type of system and workloads, a particular scheme may be well suited than others.</p><ol><li><p>Round Robin: In this scheme, the load balancer forwards requests to different nodes sequentially in a round-robin fashion, This is simpler to implement, however, it is not very efficient as it doesn't account for the current load on the server, which may already be loaded when a new request is assigned. Hence is mostly suited for simpler use cases where loads are not processor intensive.</p></li><li><p>Least Connection: The requests will be forwarded to a server that has the least number of connections or serving the least number of requests at that point in time. This delivers better performance than the round-robin, but again suited not suited for workloads where the single request might load the server.</p></li><li><p>Least Response Time: This scheme is based upon the time taken by the server to respond to a dummy request sent to a server selected by an algorithm based on the least connections and response time.</p></li><li><p>Hash-Based: Requests are distributed based upon a defined key example: Request URL, Request IP address.</p></li><li><p>Custom Load Algorithm: In this scheme, load balancer determines which server to forward the request to based on a combination of server metrics like Memory usage, CPU Usage, Response Time.</p></li></ol><h2>Summary</h2><p>Load balancer has become an essential component of any large scale system as it helps to balance loads across multiple machines. As the systems become more complex, increasingly popular and traffic volume surges, Load balancers act as a traffic cop to route the loads systematically, preventing uneven loads and performance issues on the systems. We discussed different types of Load Balancers and algorithms, in a system design interview based upon the problem at hand a particular configuration may be well suited than the other.</p>]]></content:encoded></item><item><title><![CDATA[CDN]]></title><description><![CDATA[It&#8217;s a network of servers that are distributed geographically and deliver content at fast speeds.]]></description><link>https://systemdesigntutorial.substack.com/p/cdn</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/cdn</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:18:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It&#8217;s a network of servers that are distributed geographically and deliver content at fast speeds. It gives quick transfer of static content like files(HTML, js, text, etc.), images, and videos.<br>CDN plays a vital role in designing modern web applications and serves a majority of web traffic in recent times for sites like Instagram, Twitter, Netflix, Amazon, and many more.</p><p>Popular CDN offerings include Cloudflare, Akamai, and AWS Cloudfront.</p><h2>Working of CDN</h2><p>When the user requests a webpage or any other content, the content will be delivered from a CDN server close to the user. The more the distance from the CDN server the more time content will take to reach the user and the slower will be load time for the content.</p><p>If the content is not available on the CDN, it can be fetched from the backend servers. Content on the CDN can have an expiry set by the application or configuration. When the particular content is requested, it is returned with a TTL (time-to-live) header providing the value of expiry.</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg 424w, https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg 848w, https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg 1272w, https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg" data-attrs="{&quot;src&quot;:&quot;https://res.cloudinary.com/systemdesigntutorial/image/upload/v1648638790/cdn_cgtvre.jpg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;CDN&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="CDN" title="CDN" srcset="https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg 424w, https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg 848w, https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg 1272w, https://substackcdn.com/image/upload/v1648638790/cdn_cgtvre.jpg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>For example, if you are located in Asia, data fetched from a server in Europe will take more time than the same data fetched from a server in Asia itself due to the distance the data has to travel.</p><p>Similar to static content delivery, nowadays CDNs are also capable of serving dynamic content. To generate the dynamic content, scripts are run at the CDN server rather than the backend server, these scripts generate the content based on some variables and events like time, location, or input data from APIs and cache the content. For example, Cloudflare Workers offer Serverless Javascript functions that are executed on the Cloudflare CDN.</p><h2>Advantages of using CDN</h2><ul><li><p><strong>Faster Load times</strong> - Since files are fetched from a server closer to the user, load times are drastically improved. This leads to a better user experience overall, which in turn increases user engagement and reduces bounce rates.</p></li><li><p><strong>Reduced Infrastructure cost</strong> - Since the request does not receive the backend servers, it decreases the load on the servers, hence reducing the infrastructure cost.</p></li><li><p><strong>Increased Efficiency</strong> - By using the techniques like minification and file compression, CDNs reduce the size of files that are transferred to the client. CDN can speed up the TLS/SSL-based sites by connection reuse and TLS false start.</p></li><li><p><strong>Security</strong> - If configured correctly, CDN might help in mitigating security issues like DDoS attacks.</p></li></ul><p>Though there are so many advantages of using CDN, we need to be careful while using a CDN and configuring it. Here are some of the aspects we should keep in mind.</p><ul><li><p><strong>Need</strong> - Do we need a CDN?, In case our content is not accessed frequently or refreshed too quickly, then CDN might not be of any use.</p></li><li><p><strong>Cost</strong> - The major CDN providers mentioned in the introduction section have pricing based on the number of requests or the amount of data in/out. Hence we should be careful about the cost to prevent massive billings.</p></li><li><p><strong>Fallback</strong> - Clients should be coded in such a way that if CDN is not available, they should be able to connect to backend servers for the content.</p></li><li><p><strong>Cache Expiry</strong> - We should be mindful while setting the expiry for the content, Have too long of expiry, the content might be stale. Have too short of Expiry and there might be unnecessary reloading of the content from the origin servers to the CDN.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Caching]]></title><description><![CDATA[Caching means storing data into the cache, which is a high-speed low latency data storage.]]></description><link>https://systemdesigntutorial.substack.com/p/caching</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/caching</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 11:07:13 GMT</pubDate><content:encoded><![CDATA[<p>Caching means storing data into the cache, which is a high-speed low latency data storage. It will store a limited amount of information, that is very likely to be used in the immediate future. Caching layer typically stores the data in memory (or RAM) and hence avoids accessing the secondary memory (disk) which is an expensive operation in terms of time. Caching allows optimal use of available resources by using the principle of <strong>locality of reference</strong>, according to this principle processors tend to access the same set of memory locations repetitively over a short period of time.</p><h3>Caching Layers</h3><p>In modern large scale systems which serve billions of request, depending upon the needs caching layer may exist at multiple levels like at the front end between the load balancer and application server to cache response data of frequent requests, or between the application server and the persistence layer to prevent costly disc access.</p><h2>Caching at scale</h2><p>It is easy to maintain and use cache for small scale applications where a single node is applicable, but what happens when we scale the application to multiple nodes. We have the following options:</p><h3>Global Cache</h3><p>This is a simpler option where we maintain a single global cache space, which is used by all nodes. For every request, the application queries this cache to retrieve data. A global cache is simpler and effective up to a certain scale, however, it could become a potential performance bottleneck and point of failure, if the number of requests increases beyond the maximum capacity of the cache. This is where the distributed cache comes in.</p><h3>Distributed cache</h3><p>For a distributed system application, we need a distributed cache. We distribute the cached data to multiple nodes. When a request comes in, a lookup happens to determine on which machine the data is cached and then that node fulfills the cache request.</p><p>A distributed cache is very effective and performant and capable of handling requests for very large scale systems, however, it is more complex to maintain, as it has all the intricacies of a distributed system.</p><p>Examples of Cache systems used in Distributed Systems:<br>Redis<br>Memcached<br>Aerospike DBS<br>Apache Ignite</p><h3>Cache Write Policies</h3><p>Before reading the data from a cache, we need to write it to the cache when the data is first requested so that we can later serve the data in less time. The following policies are used to write and maintain data in the cache.</p><p>--<strong>Write through cache</strong> : As the name suggests, In this scheme data is written to a database through a cache, and write operations are confirmed only if write to the database as well as cache are successful. This ensures strong data consistency between cache and persistence layers but makes the write operation costly with respect to time due cache write overhead.<br>This scheme is suited for systems which are read-heavy and have much fewer write operations than read.</p><p>--<strong>Write around cache</strong> : In this scheme, data is written to the database while bypassing the cache. Hence write operation is faster. However, while reading recent data there will be a cache miss and will result in data being read from the disk.</p><p>The write-around scheme is good for use cases where the recently written data is not read frequently.</p><p>--<strong>Write back cache</strong> : In this scheme, data is written only to the cache, and then write operation is confirmed to the client. This results in blazing fast write operations as we don't write the data to the database. Subsequently, data is asynchronously written to the database in a separate thread. All read requests are served from the cache hence strong consistency is maintained.</p><p>This scheme is well suited for write-heavy applications, However, this poses a huge risk of data loses in case cache goes down, though the risk of data loss can be minimized through replication across multiple nodes in a distributed system.</p><h3>Cache Eviction policies</h3><p>Since caches can hold only a portion of data, we need to discard the data that is no more useful, How do we determine what data is useful and what is not? Following are the most popular cache eviction policies:</p><p>-- <strong>Least Recently Used (LRU)</strong>: Removes the items which have least recently used or in other words the items which were used recently are kept. Most popular eviction policy as it provides good performance while maintaining simplicity.<br>-- <strong>Most Recently Used (MRU)</strong>: Opposite of LRU, this policy discards the most recent items first.<br>-- <strong>Least Frequently Used (LFU)</strong>: Discards based upon the frequency of items and items with less frequency are evicted in comparison to more frequent ones.<br>-- <strong>First In First Out (FIFO)</strong>: One of the simpler strategies, where the item which was used first goes out first, like in a queue.<br>-- <strong>Last In First Out (LIFO)</strong>: The items which were used last go out first, like a stack.<br>-- <strong>Random Replacement (RR)</strong>: This eviction policy discards a random item.</p><p>Besides these classical approaches, there are some modern eviction policies that provide better hit rates and concurrencies like LIRS, Window-TinyLFU, and ARC. More information can be found at this <a href="http://highscalability.com/blog/2016/1/25/design-of-a-modern-cache.html">link</a></p><h3>Content Delivery Network (CDN)</h3><p>Content Delivery Networks are powerful systems distributed geographically, which are mostly used to cache and serve static content and media for any application. Requests are first sent to CDN for any static content and served from there. Only in case of a miss request will be directed to the application servers. Due to the distributed and powerful networks, CDNs today serve a majority of web traffic and allows blazing-fast transfer of static media.</p>]]></content:encoded></item><item><title><![CDATA[ACID, BASE AND CAP Theorem]]></title><description><![CDATA[ACID ACID is an acronym for Atomicity, Consistency, Isolation, and Durability coined by Andreas Reuter and Theo H&#228;rder as a set of properties of database transactions that every database storage engine should strive for.]]></description><link>https://systemdesigntutorial.substack.com/p/acid-base-cap-theorem</link><guid isPermaLink="false">https://systemdesigntutorial.substack.com/p/acid-base-cap-theorem</guid><dc:creator><![CDATA[Kapil Gupta]]></dc:creator><pubDate>Wed, 30 Mar 2022 10:58:00 GMT</pubDate><content:encoded><![CDATA[<h2>ACID</h2><p>ACID is an acronym for Atomicity, Consistency, Isolation, and Durability coined by Andreas Reuter and Theo H&#228;rder as a set of properties of database transactions that every database storage engine should strive for. ACID compliance guarantees the validity of data in events of errors, hardware, or power failures.</p><p><strong>Atomicity</strong> refers to All or nothing which means all the changes which are part of a single transaction are performed or everything is rolled back and none of these changes take effect.</p><p><strong>Consistency</strong> refers to guarantee that all data that is written to the database will conform to defined schema and constraints at the time of saving the data.</p><p><strong>Isolation</strong> refers to the ability of a database to isolate data among transactions providing an independent view of the data. Thus if multiple transactions are executed concurrently, these should not interfere or see intermediate or incorrect data and the result should be the same as if they were run sequentially. Isolation levels are configurable in most DBMS, providing control to Database Administrators to decide the level of isolation. Most DBMS provides the following levels of Isolation: Serializable, Repeatable reads, Read committed, Read uncommitted.</p><p><strong>Durability</strong> refers to the permanent nature of the data that was stored as a part of the transaction once it is successful. Once the transaction is complete the subsequent reads should fetch the last written data.</p><h2>BASE</h2><p>BASE refers to Basically available, soft state, eventual consistency.</p><p><strong>Basically Available</strong> means the system is mostly available and every working node responds to requests in a reasonable amount of time.<br><strong>Soft State</strong> refers that the state of a system might vary over time, even without any new input.<br><strong>Eventual Consistency</strong> refers to the ability of a system to become consistent over some time. The data across different nodes will reflect the same value.</p><h2>The CAP Theorem</h2><p>The CAP theorem provided by Eric Brewer in 2000 states that a network-shared system can only guarantee /strongly support two of the following three properties:</p><p><strong>Consistency</strong>: Data should be sequential consistent(refers to <a href="http://cs.brown.edu/~mph/HerlihyW90/p463-herlihy.pdf">linearizability</a> across all nodes of a distributed system, such that all nodes in a system return same data after a successful write operation.</p><p><strong>Availability</strong>: Every request served by a non-failing node must result in a response in a reasonable amount of time.</p><p><strong>Partition tolerance</strong>: In the case of partition failure, System will continue to work and provide consistent data.</p><p>The CAP theorem provides a tool to make design choices while building a distributed system. The CAP theorem is, however, an oversimplification, highly misunderstood, and has received negative publicity over the years. This is because any distributed network shared system is inherently prone to Partition failure. One should always try to tradeoff between consistency or availability in case of partition failure. Also in modern distributed systems, the notion of Availability is dependent on the latency of the system. It is not acceptable to call a system available if it takes more than 100 seconds.</p><p>Here is an interesting <a href="http://martin.kleppmann.com/2015/05/11/please-stop-calling-databases-cp-or-ap.html">article from Martin Kleppmann</a>, on how CAP theorem uses very narrow definitions that cannot describe modern distributed systems.</p>]]></content:encoded></item></channel></rss>