{"id":"design-pastebin","title":"Design Pastebin.com (or Bit.ly)","sections":[{"id":"overview","title":"Overview","html":"<p><em>Note: This document links directly to relevant areas found in the <a href=\"/\">system design topics</a> to avoid duplication.  Refer to the linked content for general talking points, tradeoffs, and alternatives.</em></p>\n<p><strong>Design Bit.ly</strong> - is a similar question, except pastebin requires storing the paste contents instead of the original unshortened url.</p>","headings":[]},{"id":"step-1-outline-use-cases-and-constraints","title":"Step 1: Outline use cases and constraints","html":"<h2 id=\"step-1-outline-use-cases-and-constraints\">Step 1: Outline use cases and constraints</h2>\n<blockquote>\n<p>Gather requirements and scope the problem.\nAsk questions to clarify use cases and constraints.\nDiscuss assumptions.</p>\n</blockquote>\n<p>Without an interviewer to address clarifying questions, we'll define some use cases and constraints.</p>\n<h3 id=\"use-cases\">Use cases</h3>\n<h4 id=\"well-scope-the-problem-to-handle-only-the-following-use-cases\">We'll scope the problem to handle only the following use cases</h4>\n<ul>\n<li><strong>User</strong> enters a block of text and gets a randomly generated link\n<ul>\n<li>Expiration\n<ul>\n<li>Default setting does not expire</li>\n<li>Can optionally set a timed expiration</li>\n</ul>\n</li>\n</ul>\n</li>\n<li><strong>User</strong> enters a paste's url and views the contents</li>\n<li><strong>User</strong> is anonymous</li>\n<li><strong>Service</strong> tracks analytics of pages\n<ul>\n<li>Monthly visit stats</li>\n</ul>\n</li>\n<li><strong>Service</strong> deletes expired pastes</li>\n<li><strong>Service</strong> has high availability</li>\n</ul>\n<h4 id=\"out-of-scope\">Out of scope</h4>\n<ul>\n<li><strong>User</strong> registers for an account\n<ul>\n<li><strong>User</strong> verifies email</li>\n</ul>\n</li>\n<li><strong>User</strong> logs into a registered account\n<ul>\n<li><strong>User</strong> edits the document</li>\n</ul>\n</li>\n<li><strong>User</strong> can set visibility</li>\n<li><strong>User</strong> can set the shortlink</li>\n</ul>\n<h3 id=\"constraints-and-assumptions\">Constraints and assumptions</h3>\n<h4 id=\"state-assumptions\">State assumptions</h4>\n<ul>\n<li>Traffic is not evenly distributed</li>\n<li>Following a short link should be fast</li>\n<li>Pastes are text only</li>\n<li>Page view analytics do not need to be realtime</li>\n<li>10 million users</li>\n<li>10 million paste writes per month</li>\n<li>100 million paste reads per month</li>\n<li>10:1 read to write ratio</li>\n</ul>\n<h4 id=\"calculate-usage\">Calculate usage</h4>\n<p><strong>Clarify with your interviewer if you should run back-of-the-envelope usage calculations.</strong></p>\n<ul>\n<li>Size per paste\n<ul>\n<li>1 KB content per paste</li>\n<li><code>shortlink</code> - 7 bytes</li>\n<li><code>expiration_length_in_minutes</code> - 4 bytes</li>\n<li><code>created_at</code> - 5 bytes</li>\n<li><code>paste_path</code> - 255 bytes</li>\n<li>total = ~1.27 KB</li>\n</ul>\n</li>\n<li>12.7 GB of new paste content per month\n<ul>\n<li>1.27 KB per paste * 10 million pastes per month</li>\n<li>~450 GB of new paste content in 3 years</li>\n<li>360 million shortlinks in 3 years</li>\n<li>Assume most are new pastes instead of updates to existing ones</li>\n</ul>\n</li>\n<li>4 paste writes per second on average</li>\n<li>40 read requests per second on average</li>\n</ul>\n<p>Handy conversion guide:</p>\n<ul>\n<li>2.5 million seconds per month</li>\n<li>1 request per second = 2.5 million requests per month</li>\n<li>40 requests per second = 100 million requests per month</li>\n<li>400 requests per second = 1 billion requests per month</li>\n</ul>","headings":[{"id":"use-cases","title":"Use cases","depth":3},{"id":"well-scope-the-problem-to-handle-only-the-following-use-cases","title":"We'll scope the problem to handle only the following use cases","depth":4},{"id":"out-of-scope","title":"Out of scope","depth":4},{"id":"constraints-and-assumptions","title":"Constraints and assumptions","depth":3},{"id":"state-assumptions","title":"State assumptions","depth":4},{"id":"calculate-usage","title":"Calculate usage","depth":4}]},{"id":"step-2-create-a-high-level-design","title":"Step 2: Create a high level design","html":"<h2 id=\"step-2-create-a-high-level-design\">Step 2: Create a high level design</h2>\n<blockquote>\n<p>Outline a high level design with all important components.</p>\n</blockquote>\n<p><img src=\"/repo/solutions/system_design/pastebin/pastebin_basic.png\" alt=\"Diagram\" loading=\"lazy\" decoding=\"async\"></p>","headings":[]},{"id":"step-3-design-core-components","title":"Step 3: Design core components","html":"<h2 id=\"step-3-design-core-components\">Step 3: Design core components</h2>\n<blockquote>\n<p>Dive into details for each core component.</p>\n</blockquote>\n<h3 id=\"use-case-user-enters-a-block-of-text-and-gets-a-randomly-generated-link\">Use case: User enters a block of text and gets a randomly generated link</h3>\n<p>We could use a <a href=\"/learn/database#relational-database-management-system-rdbms\">relational database</a> as a large hash table, mapping the generated url to a file server and path containing the paste file.</p>\n<p>Instead of managing a file server, we could use a managed <strong>Object Store</strong> such as Amazon S3 or a <a href=\"/learn/database#document-store\">NoSQL document store</a>.</p>\n<p>An alternative to a relational database acting as a large hash table, we could use a <a href=\"/learn/database#key-value-store\">NoSQL key-value store</a>.  We should discuss the <a href=\"/learn/database#sql-or-nosql\">tradeoffs between choosing SQL or NoSQL</a>.  The following discussion uses the relational database approach.</p>\n<ul>\n<li>The <strong>Client</strong> sends a create paste request to the <strong>Web Server</strong>, running as a <a href=\"/learn/reverse-proxy-web-server\">reverse proxy</a></li>\n<li>The <strong>Web Server</strong> forwards the request to the <strong>Write API</strong> server</li>\n<li>The <strong>Write API</strong> server does the following:\n<ul>\n<li>Generates a unique url\n<ul>\n<li>Checks if the url is unique by looking at the <strong>SQL Database</strong> for a duplicate</li>\n<li>If the url is not unique, it generates another url</li>\n<li>If we supported a custom url, we could use the user-supplied (also check for a duplicate)</li>\n</ul>\n</li>\n<li>Saves to the <strong>SQL Database</strong> <code>pastes</code> table</li>\n<li>Saves the paste data to the <strong>Object Store</strong></li>\n<li>Returns the url</li>\n</ul>\n</li>\n</ul>\n<p><strong>Clarify with your interviewer how much code you are expected to write</strong>.</p>\n<p>The <code>pastes</code> table could have the following structure:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span>shortlink char(7) NOT NULL</span></span>\n<span class=\"line\"><span>expiration_length_in_minutes int NOT NULL</span></span>\n<span class=\"line\"><span>created_at datetime NOT NULL</span></span>\n<span class=\"line\"><span>paste_path varchar(255) NOT NULL</span></span>\n<span class=\"line\"><span>PRIMARY KEY(shortlink)</span></span></code></pre>\n<p>Setting the primary key to be based on the <code>shortlink</code> column creates an <a href=\"/learn/database#use-good-indices\">index</a> that the database uses to enforce uniqueness. We'll create an additional index on <code>created_at</code> to speed up lookups (log-time instead of scanning the entire table) and to keep the data in memory.  Reading 1 MB sequentially from memory takes about 250 microseconds, while reading from SSD takes 4x and from disk takes 80x longer.<sup>&#x3C;a href=<a href=\"https://github.com/donnemartin/system-design-primer#latency-numbers-every-programmer-should-know%3E1\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/donnemartin/system-design-primer#latency-numbers-every-programmer-should-know>1</a></sup></p>\n<p>To generate the unique url, we could:</p>\n<ul>\n<li>Take the <a href=\"https://en.wikipedia.org/wiki/MD5\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>MD5</strong></a> hash of the user's ip_address + timestamp\n<ul>\n<li>MD5 is a widely used hashing function that produces a 128-bit hash value</li>\n<li>MD5 is uniformly distributed</li>\n<li>Alternatively, we could also take the MD5 hash of randomly-generated data</li>\n</ul>\n</li>\n<li><a href=\"https://www.kerstner.at/2012/07/shortening-strings-using-base-62-encoding/\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>Base 62</strong></a> encode the MD5 hash\n<ul>\n<li>Base 62 encodes to <code>[a-zA-Z0-9]</code> which works well for urls, eliminating the need for escaping special characters</li>\n<li>There is only one hash result for the original input and Base 62 is deterministic (no randomness involved)</li>\n<li>Base 64 is another popular encoding but provides issues for urls because of the additional <code>+</code> and <code>/</code> characters</li>\n<li>The following <a href=\"http://stackoverflow.com/questions/742013/how-to-code-a-url-shortener\" target=\"_blank\" rel=\"noopener noreferrer\">Base 62 pseudocode</a> runs in O(k) time where k is the number of digits = 7:</li>\n</ul>\n</li>\n</ul>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> base_encode</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(num, base</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">62</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">):</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">    digits </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> []</span></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    while</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> num </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">></span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 0</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">      remainder </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> modulo(num, base)</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">      digits.push(remainder)</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">      num </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> divide(num, base)</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">    digits </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> digits.reverse</span></span></code></pre>\n<ul>\n<li>Take the first 7 characters of the output, which results in 62^7 possible values and should be sufficient to handle our constraint of 360 million shortlinks in 3 years:</li>\n</ul>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">url </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> base_encode(md5(ip_address</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">+</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">timestamp))[:</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">URL_LENGTH</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">]</span></span></code></pre>\n<p>We'll use a public <a href=\"/learn/communication#representational-state-transfer-rest\"><strong>REST API</strong></a>:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span>$ curl -X POST --data '{ \"expiration_length_in_minutes\": \"60\", \\</span></span>\n<span class=\"line\"><span>    \"paste_contents\": \"Hello World!\" }' https://pastebin.com/api/v1/paste</span></span></code></pre>\n<p>Response:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span>{</span></span>\n<span class=\"line\"><span>    \"shortlink\": \"foobar\"</span></span>\n<span class=\"line\"><span>}</span></span></code></pre>\n<p>For internal communications, we could use <a href=\"/learn/communication#remote-procedure-call-rpc\">Remote Procedure Calls</a>.</p>\n<h3 id=\"use-case-user-enters-a-pastes-url-and-views-the-contents\">Use case: User enters a paste's url and views the contents</h3>\n<ul>\n<li>The <strong>Client</strong> sends a get paste request to the <strong>Web Server</strong></li>\n<li>The <strong>Web Server</strong> forwards the request to the <strong>Read API</strong> server</li>\n<li>The <strong>Read API</strong> server does the following:\n<ul>\n<li>Checks the <strong>SQL Database</strong> for the generated url\n<ul>\n<li>If the url is in the <strong>SQL Database</strong>, fetch the paste contents from the <strong>Object Store</strong></li>\n<li>Else, return an error message for the user</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n<p>REST API:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span>$ curl https://pastebin.com/api/v1/paste?shortlink=foobar</span></span></code></pre>\n<p>Response:</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span>{</span></span>\n<span class=\"line\"><span>    \"paste_contents\": \"Hello World\"</span></span>\n<span class=\"line\"><span>    \"created_at\": \"YYYY-MM-DD HH:MM:SS\"</span></span>\n<span class=\"line\"><span>    \"expiration_length_in_minutes\": \"60\"</span></span>\n<span class=\"line\"><span>}</span></span></code></pre>\n<h3 id=\"use-case-service-tracks-analytics-of-pages\">Use case: Service tracks analytics of pages</h3>\n<p>Since realtime analytics are not a requirement, we could simply <strong>MapReduce</strong> the <strong>Web Server</strong> logs to generate hit counts.</p>\n<p><strong>Clarify with your interviewer how much code you are expected to write</strong>.</p>\n<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">class</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> HitCounts</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\">MRJob</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">):</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> extract_url</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self, line):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Extract the generated url from the log line.\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">        ...</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> extract_year_month</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self, line):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Return the year and month portions of the timestamp.\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">        ...</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> mapper</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self, _, line):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Parse each log line, extract and transform relevant lines.</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        Emit key value pairs of the form:</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url0), 1</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url0), 1</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url1), 1</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">        url </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\"> self</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">.extract_url(line)</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">        period </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\"> self</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">.extract_year_month(line)</span></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">        yield</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (period, url), </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">1</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> reducer</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self, key, values):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Sum values for each key.</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url0), 2</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url1), 1</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">        yield</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> key, </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">sum</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(values)</span></span></code></pre>\n<h3 id=\"use-case-service-deletes-expired-pastes\">Use case: Service deletes expired pastes</h3>\n<p>To delete expired pastes, we could just scan the <strong>SQL Database</strong> for all entries whose expiration timestamp are older than the current timestamp.  All expired entries would then be deleted (or  marked as expired) from the table.</p>","headings":[{"id":"use-case-user-enters-a-block-of-text-and-gets-a-randomly-generated-link","title":"Use case: User enters a block of text and gets a randomly generated link","depth":3},{"id":"use-case-user-enters-a-pastes-url-and-views-the-contents","title":"Use case: User enters a paste's url and views the contents","depth":3},{"id":"use-case-service-tracks-analytics-of-pages","title":"Use case: Service tracks analytics of pages","depth":3},{"id":"use-case-service-deletes-expired-pastes","title":"Use case: Service deletes expired pastes","depth":3}]},{"id":"step-4-scale-the-design","title":"Step 4: Scale the design","html":"<h2 id=\"step-4-scale-the-design\">Step 4: Scale the design</h2>\n<blockquote>\n<p>Identify and address bottlenecks, given the constraints.</p>\n</blockquote>\n<p><img src=\"/repo/images/4edXG0T.png\" alt=\"Diagram\" loading=\"lazy\" decoding=\"async\"></p>\n<p><strong>Important: Do not simply jump right into the final design from the initial design!</strong></p>\n<p>State you would do this iteratively: 1) <strong>Benchmark/Load Test</strong>, 2) <strong>Profile</strong> for bottlenecks 3) address bottlenecks while evaluating alternatives and trade-offs, and 4) repeat.  See <a href=\"/learn/design-scaling-aws\">Design a system that scales to millions of users on AWS</a> as a sample on how to iteratively scale the initial design.</p>\n<p>It's important to discuss what bottlenecks you might encounter with the initial design and how you might address each of them.  For example, what issues are addressed by adding a <strong>Load Balancer</strong> with multiple <strong>Web Servers</strong>?  <strong>CDN</strong>?  <strong>Master-Slave Replicas</strong>?  What are the alternatives and <strong>Trade-Offs</strong> for each?</p>\n<p>We'll introduce some components to complete the design and to address scalability issues.  Internal load balancers are not shown to reduce clutter.</p>\n<p><em>To avoid repeating discussions</em>, refer to the following <a href=\"/\">system design topics</a> for main talking points, tradeoffs, and alternatives:</p>\n<ul>\n<li><a href=\"/learn/domain-name-system\">DNS</a></li>\n<li><a href=\"/learn/content-delivery-network\">CDN</a></li>\n<li><a href=\"/learn/load-balancer\">Load balancer</a></li>\n<li><a href=\"/learn/load-balancer#horizontal-scaling\">Horizontal scaling</a></li>\n<li><a href=\"/learn/reverse-proxy-web-server\">Web server (reverse proxy)</a></li>\n<li><a href=\"/learn/application-layer\">API server (application layer)</a></li>\n<li><a href=\"/learn/cache\">Cache</a></li>\n<li><a href=\"/learn/database#relational-database-management-system-rdbms\">Relational database management system (RDBMS)</a></li>\n<li><a href=\"/learn/availability-patterns#fail-over\">SQL write master-slave failover</a></li>\n<li><a href=\"/learn/database#master-slave-replication\">Master-slave replication</a></li>\n<li><a href=\"/learn/consistency-patterns\">Consistency patterns</a></li>\n<li><a href=\"/learn/availability-patterns\">Availability patterns</a></li>\n</ul>\n<p>The <strong>Analytics Database</strong> could use a data warehousing solution such as Amazon Redshift or Google BigQuery.</p>\n<p>An <strong>Object Store</strong> such as Amazon S3 can comfortably handle the constraint of 12.7 GB of new content per month.</p>\n<p>To address the 40 <em>average</em> read requests per second (higher at peak), traffic for popular content should be handled by the <strong>Memory Cache</strong> instead of the database.  The <strong>Memory Cache</strong> is also useful for handling the unevenly distributed traffic and traffic spikes.  The <strong>SQL Read Replicas</strong> should be able to handle the cache misses, as long as the replicas are not bogged down with replicating writes.</p>\n<p>4 <em>average</em> paste writes per second (with higher at peak) should be do-able for a single <strong>SQL Write Master-Slave</strong>.  Otherwise, we'll need to employ additional SQL scaling patterns:</p>\n<ul>\n<li><a href=\"/learn/database#federation\">Federation</a></li>\n<li><a href=\"/learn/database#sharding\">Sharding</a></li>\n<li><a href=\"/learn/database#denormalization\">Denormalization</a></li>\n<li><a href=\"/learn/database#sql-tuning\">SQL Tuning</a></li>\n</ul>\n<p>We should also consider moving some data to a <strong>NoSQL Database</strong>.</p>","headings":[]},{"id":"additional-talking-points","title":"Additional talking points","html":"<h2 id=\"additional-talking-points\">Additional talking points</h2>\n<blockquote>\n<p>Additional topics to dive into, depending on the problem scope and time remaining.</p>\n</blockquote>\n<h4 id=\"nosql\">NoSQL</h4>\n<ul>\n<li><a href=\"/learn/database#key-value-store\">Key-value store</a></li>\n<li><a href=\"/learn/database#document-store\">Document store</a></li>\n<li><a href=\"/learn/database#wide-column-store\">Wide column store</a></li>\n<li><a href=\"/learn/database#graph-database\">Graph database</a></li>\n<li><a href=\"/learn/database#sql-or-nosql\">SQL vs NoSQL</a></li>\n</ul>\n<h3 id=\"caching\">Caching</h3>\n<ul>\n<li>Where to cache\n<ul>\n<li><a href=\"/learn/cache#client-caching\">Client caching</a></li>\n<li><a href=\"/learn/cache#cdn-caching\">CDN caching</a></li>\n<li><a href=\"/learn/cache#web-server-caching\">Web server caching</a></li>\n<li><a href=\"/learn/cache#database-caching\">Database caching</a></li>\n<li><a href=\"/learn/cache#application-caching\">Application caching</a></li>\n</ul>\n</li>\n<li>What to cache\n<ul>\n<li><a href=\"/learn/cache#caching-at-the-database-query-level\">Caching at the database query level</a></li>\n<li><a href=\"/learn/cache#caching-at-the-object-level\">Caching at the object level</a></li>\n</ul>\n</li>\n<li>When to update the cache\n<ul>\n<li><a href=\"/learn/cache#cache-aside\">Cache-aside</a></li>\n<li><a href=\"/learn/cache#write-through\">Write-through</a></li>\n<li><a href=\"/learn/cache#write-behind-write-back\">Write-behind (write-back)</a></li>\n<li><a href=\"/learn/cache#refresh-ahead\">Refresh ahead</a></li>\n</ul>\n</li>\n</ul>\n<h3 id=\"asynchronism-and-microservices\">Asynchronism and microservices</h3>\n<ul>\n<li><a href=\"/learn/asynchronism#message-queues\">Message queues</a></li>\n<li><a href=\"/learn/asynchronism#task-queues\">Task queues</a></li>\n<li><a href=\"/learn/asynchronism#back-pressure\">Back pressure</a></li>\n<li><a href=\"/learn/application-layer#microservices\">Microservices</a></li>\n</ul>\n<h3 id=\"communications\">Communications</h3>\n<ul>\n<li>Discuss tradeoffs:\n<ul>\n<li>External communication with clients - <a href=\"/learn/communication#representational-state-transfer-rest\">HTTP APIs following REST</a></li>\n<li>Internal communications - <a href=\"/learn/communication#remote-procedure-call-rpc\">RPC</a></li>\n</ul>\n</li>\n<li><a href=\"/learn/application-layer#service-discovery\">Service discovery</a></li>\n</ul>\n<h3 id=\"security\">Security</h3>\n<p>Refer to the <a href=\"/learn/security\">security section</a>.</p>\n<h3 id=\"latency-numbers\">Latency numbers</h3>\n<p>See <a href=\"/learn/appendix#latency-numbers-every-programmer-should-know\">Latency numbers every programmer should know</a>.</p>\n<h3 id=\"ongoing\">Ongoing</h3>\n<ul>\n<li>Continue benchmarking and monitoring your system to address bottlenecks as they come up</li>\n<li>Scaling is an iterative process</li>\n</ul>","headings":[{"id":"nosql","title":"NoSQL","depth":4},{"id":"caching","title":"Caching","depth":3},{"id":"asynchronism-and-microservices","title":"Asynchronism and microservices","depth":3},{"id":"communications","title":"Communications","depth":3},{"id":"security","title":"Security","depth":3},{"id":"latency-numbers","title":"Latency numbers","depth":3},{"id":"ongoing","title":"Ongoing","depth":3}]}],"sourceFiles":[{"name":"pastebin.py","url":"https://github.com/donnemartin/system-design-primer/blob/master/solutions/system_design/pastebin/pastebin.py","html":"<pre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6A737D;--shiki-dark:#6A737D\"># -*- coding: utf-8 -*-</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">from</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> mrjob.job </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">import</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> MRJob</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">class</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> HitCounts</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\">MRJob</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">):</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> extract_url</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self, line):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Extract the generated url from the log line.\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">        pass</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> extract_year_month</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self, line):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Return the year and month portions of the timestamp.\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">        pass</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> mapper</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self, _, line):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Parse each log line, extract and transform relevant lines.</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        Emit key value pairs of the form:</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url0), 1</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url0), 1</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url1), 1</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">        url </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\"> self</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">.extract_url(line)</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">        period </span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\"> self</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">.extract_year_month(line)</span></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">        yield</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (period, url), </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">1</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> reducer</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self, key, values):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Sum values for each key.</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url0), 2</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        (2016-01, url1), 1</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">        yield</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> key, </span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">sum</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(values)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">    def</span><span style=\"color:#6F42C1;--shiki-dark:#B392F0\"> steps</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">(self):</span></span>\n<span class=\"line\"><span style=\"color:#032F62;--shiki-dark:#9ECBFF\">        \"\"\"Run the map and reduce steps.\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">        return</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\"> [</span></span>\n<span class=\"line\"><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">            self</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">.mr(</span><span style=\"color:#E36209;--shiki-dark:#FFAB70\">mapper</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">self</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">.mapper,</span></span>\n<span class=\"line\"><span style=\"color:#E36209;--shiki-dark:#FFAB70\">                    reducer</span><span style=\"color:#D73A49;--shiki-dark:#F97583\">=</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\">self</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">.reducer)</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">        ]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#D73A49;--shiki-dark:#F97583\">if</span><span style=\"color:#005CC5;--shiki-dark:#79B8FF\"> __name__</span><span style=\"color:#D73A49;--shiki-dark:#F97583\"> ==</span><span style=\"color:#032F62;--shiki-dark:#9ECBFF\"> '__main__'</span><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#24292E;--shiki-dark:#E1E4E8\">    HitCounts.run()</span></span></code></pre>"}]}