YouTube2Text

System Design Interview Prep for Beginners (Full Course) — Transcript

by KodeKloud · 23,662 words · 3,624 segments · language en · Watch on YouTube

Full transcript

  1. 0:00Welcome to the system design interview
  2. 0:01prep video. This video is designed for
  3. 0:04absolute beginners who do not have any
  4. 0:06prior system design experience.
  5. 0:09System design interviews are generally
  6. 0:11very intense and hectic because you have
  7. 0:13to think about multiple components and
  8. 0:15the pros and cons of each component. My
  9. 0:18goal is to provide you a framework that
  10. 0:21you can use to answer interview
  11. 0:22questions. Once you understand this
  12. 0:25framework, we will solve some real
  13. 0:27interview problems. You'll also get four
  14. 0:30hands-on labs along the video where you
  15. 0:33can actually get hands-on experience on
  16. 0:35some of the components. If that sounds
  17. 0:38useful, let's get started.
  18. 0:41I want to be honest about one thing
  19. 0:43before we start solving any system
  20. 0:45design problem. If you are new to system
  21. 0:48design, the first few problems you will
  22. 0:51solve will introduce you to concepts you
  23. 0:53have never seen before.
  24. 0:55And while you go through those new
  25. 0:57concepts, you will feel like you don't
  26. 1:00know anything. That feeling is normal.
  27. 1:04If you keep going, then after a few
  28. 1:07problems, you will start seeing the same
  29. 1:09patterns again and again, just in
  30. 1:12different problems.
  31. 1:14And to be completely frank, that is the
  32. 1:17only real way to prepare for system
  33. 1:19design. solve as many problems as you
  34. 1:22can until you know which component to
  35. 1:26use and when. That's exactly what we
  36. 1:29will do in this course. We will pick
  37. 1:32some of the famous problems and solve
  38. 1:34them. Now, most of the system design
  39. 1:38questions you will face in interviews
  40. 1:40are actually open-ended questions. You
  41. 1:43will face questions like design
  42. 1:45Instagram or design Dropbox.
  43. 1:49Now one thing should be absolutely clear
  44. 1:51in your mind. Nobody expects you to
  45. 1:55design a perfect system in 45 minutes.
  46. 1:59So the natural question is what do
  47. 2:01interviewers actually expect from you in
  48. 2:04these 45 minutes?
  49. 2:06When we look at the feedback that
  50. 2:08interviewers write for candidates, they
  51. 2:10do not write this person knows how a
  52. 2:13load balancer works or this person knows
  53. 2:17how a cache works. They write things
  54. 2:20like she can work with an ambiguous
  55. 2:23problem or he explained trade-offs of
  56. 2:27his design choices very clearly.
  57. 2:30or she took the hint I gave her and
  58. 2:34adjusted the design instead of defending
  59. 2:36it. If you notice, none of the above
  60. 2:40feedback lines is about a specific
  61. 2:43technology.
  62. 2:45And when the feedback is negative, there
  63. 2:48are two things that show up again and
  64. 2:50again. The first is the candidate who
  65. 2:54jumps straight into architecture.
  66. 2:56They do not ask any questions or confirm
  67. 2:59requirements with the interviewer.
  68. 3:02The second is the candidate who
  69. 3:05overengineers.
  70. 3:06They go into caches, cues, shards,
  71. 3:10multi-reion and are not able to complete
  72. 3:13within 45 minutes. If I have to
  73. 3:16summarize what interviewers are looking
  74. 3:18for, it comes down to four things. First
  75. 3:23is judgment under ambiguity.
  76. 3:26This means that when the problem is
  77. 3:28vague, can you narrow it down to
  78. 3:31something concrete?
  79. 3:34Second thing they're looking for is
  80. 3:35technical depth. When you say, "I will
  81. 3:39put a cash here," then can you explain
  82. 3:42what problem this cash will solve and
  83. 3:45what new problem the cash creates?
  84. 3:48Third, communication.
  85. 3:51Can you clearly explain to the
  86. 3:53interviewer what you're trying to do
  87. 3:55while you're doing it? And fourth,
  88. 3:57collaboration. You need to ask the right
  89. 4:00questions. Validate your assumptions
  90. 4:02with the interviewer. If you just assume
  91. 4:05things and go with what you think is
  92. 4:07right, you will most likely fail the
  93. 4:09interview. Picture two candidates
  94. 4:12answering the same design Instagram
  95. 4:14question. The first candidate grabs the
  96. 4:17marker and starts drawing all the
  97. 4:19application servers, databases, and
  98. 4:21arrows everywhere. The second candidate
  99. 4:24asks who the users are and how many,
  100. 4:27whether we care more about uploads or
  101. 4:30the feed, and then makes the problem
  102. 4:32concrete. I'll focus on posting photos
  103. 4:35and the home feed, and leave search and
  104. 4:38direct messages out of scope.
  105. 4:4110 minutes in, the first candidate has
  106. 4:43an impressive diagram of the wrong
  107. 4:45system. The second candidate has a
  108. 4:48smaller, clearer problem that they can
  109. 4:50actually solve in 45 minutes.
  110. 4:54Both of them may know the same
  111. 4:55technology, but they will get very
  112. 4:58different scores from the interviewer.
  113. 5:01So, the real question is, how do you
  114. 5:03become the second candidate every single
  115. 5:06time, even when you are under pressure?
  116. 5:10For this, you don't need talent or luck.
  117. 5:13You need a method. The method I want to
  118. 5:16give you is a well-known framework that
  119. 5:19you can follow in any system design
  120. 5:21interview. It has six steps in total.
  121. 5:25This is a very important framework. So,
  122. 5:28let's go through them one by one. Step
  123. 5:32one is clarifying requirements.
  124. 5:35Spend about 5 minutes to make the
  125. 5:37problem statement concrete.
  126. 5:39Your goal should be to convert design
  127. 5:41Instagram into something concrete. For
  128. 5:45example, users can post photos. Users
  129. 5:48can follow other users. Users can scroll
  130. 5:52through a feed where they can see photos
  131. 5:54posted by people they follow. And the
  132. 5:56feed should load fast as soon as the
  133. 5:59user opens the app.
  134. 6:01Step two is back of the envelope
  135. 6:03estimation.
  136. 6:05By this we mean we need to estimate some
  137. 6:07numbers to figure out how you need to
  138. 6:10scale this app. For example, let's say
  139. 6:14we have 10 million daily users and
  140. 6:17people scroll a lot more than they post.
  141. 6:20People scroll 100 photos every week but
  142. 6:24post only one every week. So our app is
  143. 6:27read heavy not write heavy. So we need
  144. 6:31to focus on reading photos.
  145. 6:33You confirm those numbers with the
  146. 6:35interviewer before you build on them.
  147. 6:38Step three is defining the API.
  148. 6:42Spend about 5 minutes here. For our
  149. 6:45Instagram, three calls are enough for
  150. 6:47all users. First, they make a call to
  151. 6:50upload a photo, then to follow a user,
  152. 6:55and third to get the home feed where
  153. 6:57they scroll photos. And if you notice,
  154. 6:59the calls that users make also tell you
  155. 7:01what your system needs to store for
  156. 7:02users. Photos, a follow list, and a
  157. 7:05feed. Step four is a highlevel design.
  158. 7:09Spend about 10 to 15 minutes drawing the
  159. 7:11plain boxes that satisfy the
  160. 7:13requirements for our Instagram. A user,
  161. 7:16an app server, a database for users and
  162. 7:19follows, a separate storage for the
  163. 7:21photos, and arrows showing how a photo
  164. 7:23goes in and how the feed comes out.
  165. 7:27Step five is the deep dives. Another 10
  166. 7:29to 15 minutes on the one or two
  167. 7:31components where this problem is
  168. 7:33actually hard for Instagram. That's the
  169. 7:36home feed. When someone with a million
  170. 7:38followers posts a photo, how does that
  171. 7:40photo show up in a million feeds without
  172. 7:42melting the system? And step six is
  173. 7:45bottlenecks and scaling the last 5
  174. 7:48minutes. Here you do a critical analysis
  175. 7:51of your system. What breaks first and
  176. 7:54what you would do about it. For example,
  177. 7:56the follows database becomes the
  178. 7:58bottleneck at 10 times the traffic. So
  179. 8:00we would add a cache in front of it.
  180. 8:03Then at the end you can summarize what
  181. 8:05you built in 30 to 60 seconds.
  182. 8:08So these are the six steps you need to
  183. 8:10keep in your mind when you are under
  184. 8:12pressure in the interview. Now we will
  185. 8:15use this framework on every problem we
  186. 8:17solve from here onwards.
  187. 8:20In the last video, I gave you the
  188. 8:22six-step framework and the first step
  189. 8:24was to clarify the requirements from the
  190. 8:26interviewer. These first five minutes of
  191. 8:29the interview are very important. If you
  192. 8:32don't ask the right questions, you will
  193. 8:34probably end up building the wrong
  194. 8:36system and that will surely be the end
  195. 8:38of your interview process. If the
  196. 8:41interviewer says that you need to design
  197. 8:43a photo sharing service, then you need
  198. 8:45to ask, "What kind of photo sharing
  199. 8:48service?" Because Instagram means
  200. 8:51billions of small images and a feed that
  201. 8:54loads in half a second. Flickr means
  202. 8:57fewer users will be uploading images.
  203. 9:00But these images will be huge in size
  204. 9:03and the images don't get compressed. To
  205. 9:06be honest, interviewers by default ask
  206. 9:09very vague questions because they want
  207. 9:12to see how clearly you can think.
  208. 9:15One thing I want to make absolutely
  209. 9:17clear when I say you need to ask
  210. 9:19questions, I do not mean that you will
  211. 9:22bombard the interviewer with a lot of
  212. 9:24questions. I have seen candidates ask 15
  213. 9:28memorized questions one after another in
  214. 9:31those five minutes. And these memorized
  215. 9:33questions shouldn't even be asked.
  216. 9:37Before you ask any question, you need to
  217. 9:39ask yourself, will the answer change my
  218. 9:43design? A good question creates a path
  219. 9:46for you. If the answer is A, then you
  220. 9:49design one way. If the answer is B, then
  221. 9:52you design in a different way. If the
  222. 9:55design is not dependent on the
  223. 9:57interviewer's answer, then don't ask
  224. 9:59that question at all. Just state your
  225. 10:02assumptions for the design and move on.
  226. 10:05You can quickly check with the
  227. 10:07interviewer if they are okay with your
  228. 10:09assumptions.
  229. 10:11Let me give you some concrete examples
  230. 10:13of questions that candidates ask. First
  231. 10:16question is, should I use SQL or NoSQL?
  232. 10:22Now, this question is bad because the
  233. 10:24database choice depends on what kind of
  234. 10:27data you are storing and how users will
  235. 10:30access your data. It's not a decision
  236. 10:33that the interviewer can make for you.
  237. 10:36It is your job to decide which database
  238. 10:38to use based on the requirements of your
  239. 10:41system. Let's see another question. Do
  240. 10:45we need the system to be scalable and
  241. 10:47highly available?
  242. 10:49This is also a rubbish question because
  243. 10:52if the interviewer says yes, then it
  244. 10:54doesn't add any information. So don't
  245. 10:57ask whether the system should scale. You
  246. 11:00should ask how many users are there and
  247. 11:03how fast the user base is growing. And
  248. 11:07let me tell you the biggest mistake.
  249. 11:10People ask a very good question but then
  250. 11:12they ignore the information that the
  251. 11:15interviewer gave them when they actually
  252. 11:17design the system.
  253. 11:19For example, the interviewer said, "Our
  254. 11:22system has 10,000 users, but then you
  255. 11:25ignored this information and created a
  256. 11:28massive distributed system.
  257. 11:30Yes, your massive distributed system is
  258. 11:33great, but we don't need this
  259. 11:35distributed system because we only have
  260. 11:3710,000 users.
  261. 11:40That's enough for bad questions.
  262. 11:43Now, let's see some good questions. The
  263. 11:46goal of a good question is to gather
  264. 11:48functional and non-functional
  265. 11:50requirements.
  266. 11:51Functional requirements are easy. You
  267. 11:54can directly ask the interviewer what
  268. 11:56this system is supposed to do. and
  269. 11:59interviewers generally describe the
  270. 12:01system for you.
  271. 12:03Let's see the non-functional requirement
  272. 12:05side. If you don't remember,
  273. 12:08non-functional requirement means how
  274. 12:10well the system should behave.
  275. 12:13There are four good questions that can
  276. 12:15take you a long way and those are how
  277. 12:18many users and how fast is that growing?
  278. 12:22Do people read more or write more?
  279. 12:26What data can we not afford to lose?
  280. 12:30How fast should the important actions in
  281. 12:32the system feel? In other words, what
  282. 12:36should be the latency of important
  283. 12:38actions?
  284. 12:39And when you ask for numbers, the
  285. 12:41interviewer will often ask you to decide
  286. 12:44the numbers. This means that the
  287. 12:47interviewer wants to see whether you can
  288. 12:49pick a defensible number yourself.
  289. 12:52If you are giving some numbers to the
  290. 12:54interviewer, you should always give the
  291. 12:56reasoning behind those numbers. For
  292. 12:59example, you should say this to the
  293. 13:01interviewer.
  294. 13:03This is a consumer photosharing app. So,
  295. 13:06let's say we have 10 million daily
  296. 13:08active users and people scroll a lot
  297. 13:10through the app, but they don't post
  298. 13:12very frequently. So, it is a read heavy
  299. 13:15system. Does that sound reasonable?
  300. 13:19And if the interviewer says it is
  301. 13:20reasonable, you go ahead with those
  302. 13:22numbers.
  303. 13:24Now, let me show you a set of good
  304. 13:26questions for one of the problems. Let's
  305. 13:29say the question is, design a ticket
  306. 13:32booking system like ticket master.
  307. 13:35Question one, are we only booking
  308. 13:38tickets or do we also need to handle the
  309. 13:40event organizers who create events? The
  310. 13:43answer decides the scope of the system.
  311. 13:47Question two, how many people show up
  312. 13:50when a big event goes on sale?
  313. 13:53Say the answer is 10 million.
  314. 13:56This decides the scale of our system.
  315. 13:59Question three. When one user starts
  316. 14:02checkout for one seat, does that seat
  317. 14:05become unavailable to everyone else? And
  318. 14:08if it becomes unavailable, then for how
  319. 14:11long? The answer to this question
  320. 14:14decides the hardest part of the design.
  321. 14:17how we hold the seats, the locks that we
  322. 14:19need to apply on the seats, and the
  323. 14:22timers that release the locks.
  324. 14:25Question four, do users search for
  325. 14:28events on our app and then book the
  326. 14:30ticket or do users arrive with a direct
  327. 14:33link for booking.
  328. 14:35The answer decides the features of our
  329. 14:37system.
  330. 14:39You see, we asked four questions here
  331. 14:42and we got to know about the scope of
  332. 14:44the system, the scale of the system, and
  333. 14:48the features of the system.
  334. 14:51Let's take a look at our first problem.
  335. 14:53The interviewer says, "Design a URL
  336. 14:56shortener like bit.ly." Basically, a
  337. 15:00user past a long link and the service
  338. 15:02returns a short and neat link back to
  339. 15:04the user. This is actually a very useful
  340. 15:07product because most of the links we
  341. 15:10deal with are long and not very neat.
  342. 15:13If you have a URL like this, then you
  343. 15:16wouldn't like to share it as it is. If
  344. 15:19you can convert this URL to something
  345. 15:21shorter like this, then it is sharable
  346. 15:25and much more useful. Now, anyone who
  347. 15:28clicks the short link gets redirected to
  348. 15:31the long link and that's why they land
  349. 15:33on the original page.
  350. 15:36It sounds almost too simple for an
  351. 15:38interview and that is exactly why
  352. 15:40interviewers love this question. So,
  353. 15:43let's apply our six-step framework on
  354. 15:46this problem.
  355. 15:48Step one, clarifying requirements.
  356. 15:51You need to only ask questions whose
  357. 15:53answers change the design.
  358. 15:56I would ask three questions here. First,
  359. 16:00can users pick their own custom short
  360. 16:02links? For example, can codecloud pick a
  361. 16:06branded short link with the word
  362. 16:07codecloud in it instead of a random
  363. 16:10code. If custom short links are allowed,
  364. 16:13we need to change how codes get created.
  365. 16:16So, this question can change the design.
  366. 16:20Let's say the interviewer says that we
  367. 16:22don't allow custom links.
  368. 16:25The second question I would ask is, do
  369. 16:27the short links created by us ever
  370. 16:29expire or do they live forever?
  371. 16:33Because if these links expire after a
  372. 16:35certain time, it means we need to do
  373. 16:38cleanup work and extra checks on every
  374. 16:40click.
  375. 16:42Let's say the interviewer says links
  376. 16:44live forever.
  377. 16:46The third question I'd ask is, do we
  378. 16:49need analytics for the links?
  379. 16:52basically a service which tracks how
  380. 16:54many times each link was opened. Let's
  381. 16:58say the interviewer says analytics is
  382. 17:00nice to have but not required.
  383. 17:03Keep this answer in mind because we will
  384. 17:06discuss this in a later part of the
  385. 17:08video in a surprising situation.
  386. 17:11So after getting answers to your
  387. 17:13questions, we repeat the scope of our
  388. 17:16system very clearly in front of the
  389. 17:18interviewer. We need to create a short
  390. 17:21link that redirects to the original
  391. 17:23link. We are not allowing custom links
  392. 17:27and there is no expiry date on the links
  393. 17:30our system generates
  394. 17:32and analytics is good to have so we can
  395. 17:35work on it if we have some spare time.
  396. 17:38The second step is to do back of the
  397. 17:40envelope estimation and we only try to
  398. 17:43estimate numbers that actually decide
  399. 17:46something in our system. Question one
  400. 17:49is, how many new links are created per
  401. 17:52day? Let's assume we create 1 million
  402. 17:55links per day. 1 million sounds big, but
  403. 17:59if you spread this number over a day, it
  404. 18:02is about 12 new links per second. And
  405. 18:06any database can handle 12 writes per
  406. 18:08second.
  407. 18:10Question two, how many clicks will your
  408. 18:13short links receive?
  409. 18:15The short links that we create exist to
  410. 18:17be shared with external users and every
  411. 18:20link can get tens or hundreds or even
  412. 18:24thousands of clicks. A reasonable
  413. 18:27assumption is a thousand clicks for
  414. 18:29every link created.
  415. 18:32These numbers give a big hint to you.
  416. 18:35You should notice that for every link
  417. 18:37you create, you will end up with 1,000
  418. 18:40clicks on it. Which means our system is
  419. 18:43massively redheavy.
  420. 18:45Therefore, we need to work on the read
  421. 18:48path where users click and we need to
  422. 18:51redirect them to the original link.
  423. 18:54And question three, how many links will
  424. 18:57we ever store?
  425. 18:59A million a day for 10 years is about
  426. 19:023.6 billion links.
  427. 19:05Now, we move to step three, which is
  428. 19:08defining the API of this system.
  429. 19:11To begin with, two API calls are enough.
  430. 19:16The first API call creates a short link
  431. 19:19for the user. The user sends the long
  432. 19:22URL and they get back a short code. The
  433. 19:27second API call is the redirect. When a
  434. 19:31user clicks on a short URL in the
  435. 19:33browser, the browser asks for the real
  436. 19:36URL and we send the browser to the long
  437. 19:40URL.
  438. 19:41And if you notice these two API calls,
  439. 19:45then it is clear that we actually need
  440. 19:47to store one table that maps a short
  441. 19:50code to a long URL.
  442. 19:53And this is actually our data model.
  443. 19:56Now we also need to decide which
  444. 19:59database would be good to store this
  445. 20:00data. A weak answer here is we need
  446. 20:04scale. That's why no SQL.
  447. 20:08This sentence doesn't tell anything to
  448. 20:10the interviewer. The strong answer comes
  449. 20:13from the access pattern. In our system,
  450. 20:16we are looking to access one large link
  451. 20:19from a small link. It's like a onetoone
  452. 20:22mapping. This is the simplest data shape
  453. 20:26that exists, a key and a value. And we
  454. 20:30are not looking for any joins, no
  455. 20:33operation that touches many rows in the
  456. 20:35database.
  457. 20:37A plain SQL database like Postgress with
  458. 20:41an index on the short code can handle
  459. 20:43this comfortably. So that is where we
  460. 20:46start.
  461. 20:48Step four, we will start with a
  462. 20:50high-level design. When a user sends a
  463. 20:53request to create a short URL, the
  464. 20:56request goes to an application server.
  465. 20:58The server creates a short URL or code
  466. 21:02and then the server stores the long URL
  467. 21:05and short URL in the database.
  468. 21:08Then the server shares this short URL
  469. 21:10with the user which they can share with
  470. 21:13the general public. Now anyone who is
  471. 21:16accessing this short link, they will
  472. 21:18send the request to the application
  473. 21:20server to fetch the real URL. The server
  474. 21:23finds the long URL in the database and
  475. 21:26returns the long URL to the user.
  476. 21:29Now you say one thing to the interviewer
  477. 21:32very clearly.
  478. 21:34This simple version already works. So
  479. 21:37now let's find the hard part.
  480. 21:40We said the server will create a short
  481. 21:42URL or code. But how exactly will we
  482. 21:46create that? If you think about it, we
  483. 21:50have two important constraints. The code
  484. 21:53we generate must be short and it must
  485. 21:56never repeat. If two different links get
  486. 21:59the same code, then it will be a huge
  487. 22:02mess because then you will have two long
  488. 22:06URLs for which you have one short URL.
  489. 22:11Now before I show you the two options,
  490. 22:14pause this video for a moment and think.
  491. 22:17How would you generate a code that is
  492. 22:19short and that never repeats?
  493. 22:22This is exactly the question the
  494. 22:24interviewer is watching you think
  495. 22:26through.
  496. 22:28Okay, let me show you the two options
  497. 22:31that we can use to generate the short
  498. 22:33code. The first option is hashing. We
  499. 22:37take the long URL and pass it through a
  500. 22:40hash function and we get a string of
  501. 22:43characters.
  502. 22:45We can pick the first seven characters
  503. 22:47for the code and this will become part
  504. 22:50of our URL.
  505. 22:52A hash function converts any input into
  506. 22:55a fixed jumble of characters. And the
  507. 22:58good thing about hash functions is that
  508. 23:01if you provide the same input twice, it
  509. 23:04creates the same output.
  510. 23:07Basically what I want to convey is that
  511. 23:10a hash function is not creating
  512. 23:12characters randomly.
  513. 23:15If the input is the same, the output
  514. 23:18will be the same. But two different URLs
  515. 23:22when passed through a hash function can
  516. 23:24produce the same first seven characters
  517. 23:28and this is called a collision.
  518. 23:31So every time we create a code, we need
  519. 23:34to check the database first and see if
  520. 23:37this code is already taken. If yes, we
  521. 23:41can add one extra character at the end
  522. 23:44of the long URL before hashing and due
  523. 23:47to that extra character, the input
  524. 23:50changes. So the hash output changes and
  525. 23:54we get a fresh code which might not
  526. 23:57collide. Now this works but if you think
  527. 24:01it through the major problem is that as
  528. 24:04your database grows in size collision
  529. 24:07will happen more frequently because you
  530. 24:10already have too many codes in your
  531. 24:12system. And now when you try to generate
  532. 24:15a new code it might collide with a
  533. 24:18previous code and you have to make one
  534. 24:21more attempt to generate new code. The
  535. 24:25busier your system becomes, the number
  536. 24:28of collisions will increase and because
  537. 24:31a lot of collisions are happening,
  538. 24:33system will take some more time to
  539. 24:35create a short URL.
  540. 24:38Option two is counting.
  541. 24:41We can keep one global counter and every
  542. 24:44new link that arrives at your system
  543. 24:47gets the next number. The first link is
  544. 24:50one, the next is two and so on. Then you
  545. 24:55can encode this number in base 62.
  546. 24:59I will not go into the detail of how
  547. 25:01encoding works but base 62 means you are
  548. 25:05dealing with 62 characters.
  549. 25:08We normally count with 10 digits using 0
  550. 25:11to 9. If we add 26 lowercase letters and
  551. 25:1626 uppercase letters, then you have 62
  552. 25:20symbols per position.
  553. 25:22So for a string of length seven, the
  554. 25:25first position can have 62 characters.
  555. 25:28The second can have 62 characters. The
  556. 25:32third can also have 62 characters and so
  557. 25:35on. With seven characters that gives
  558. 25:38about 3 and 12 trillion possible codes
  559. 25:42and we only need 3.6 billion for the
  560. 25:46next 10 years. And the good thing is a
  561. 25:49counter never repeats. So there are no
  562. 25:52collisions and no retries no matter how
  563. 25:55full the table gets.
  564. 25:59So we compare these two approaches
  565. 26:01against our requirements. With the
  566. 26:03counter approach, we will be able to
  567. 26:05make sure that the code generated never
  568. 26:07collides. So the counter wins. But we
  569. 26:10need to present the disadvantage of the
  570. 26:12counter approach before the interviewer
  571. 26:14does. The disadvantage is that we are
  572. 26:17using a counter and a counter is easy to
  573. 26:20guess.
  574. 26:21Let me show you how someone can misuse
  575. 26:23this. Say an attacker has one of our
  576. 26:26real short links. The attacker decodes
  577. 26:29the short code back into its number by
  578. 26:32using easily available decoders. There
  579. 26:34are many decoders that can convert base
  580. 26:3662 codes back into normal numbers.
  581. 26:40Let's say for the link that the attacker
  582. 26:42has, the number comes out as 1 million.
  583. 26:46Now the attacker adds one to it and then
  584. 26:48encodes 1 million and one and gets
  585. 26:51another valid short link, a link that
  586. 26:54was never shared with them. And they can
  587. 26:57keep adding one again and again and walk
  588. 27:00through every link in our system one by
  589. 27:02one. The fix is to shuffle the number
  590. 27:06before encoding it. For example, say the
  591. 27:09counter gives us the number 45 6 7 8 9.
  592. 27:14We will not encode that number directly.
  593. 27:17We first apply a fixed secret rule to
  594. 27:19it. Something like rearrange the digits
  595. 27:22in a fixed secret order and then shift
  596. 27:25every digit up by a secret amount. So 4
  597. 27:285 6 7 8 9 might become 07281
  598. 27:349. And only then do we encode it in base
  599. 27:3862.
  600. 27:39Notice that while shuffling the number,
  601. 27:42every step in the rule is reversible. So
  602. 27:44two different numbers can never shuffle
  603. 27:46into the same result. This makes sure
  604. 27:49that the uniqueness of the counter
  605. 27:50remains intact. So the codes look random
  606. 27:53from outside and the counter has a
  607. 27:55second weakness. If two requests arrive
  608. 27:58at the same time, then both request one
  609. 28:02and request two will try to read the
  610. 28:04counter at the same time. And because
  611. 28:08none of these two requests has actually
  612. 28:10updated the counter yet, both walk away
  613. 28:14with the same number and both encode to
  614. 28:17the same code.
  615. 28:19We now have two links with same code. So
  616. 28:23we need to make sure that the counter
  617. 28:25stays correct even when multiple
  618. 28:28requests from multiple users try to
  619. 28:31access it. So here is the summary of the
  620. 28:33design we did till now. We store one
  621. 28:36table that maps short codes to long URLs
  622. 28:39in a Postgress database. We create a
  623. 28:42link using a global counter. We shuffle
  624. 28:44the counter number and then encode it in
  625. 28:47base 62.
  626. 28:49And seven characters gives us trillions
  627. 28:51of codes with zero collisions.
  628. 28:54And we index the short code so that we
  629. 28:57can search it fast in the table.
  630. 29:00This design is good for the writing part
  631. 29:02where we are actually creating the short
  632. 29:04links. But remember we said that our
  633. 29:07system is read heavy a th00and to one.
  634. 29:10What happens when a celebrity posts one
  635. 29:12of our links and a million people click
  636. 29:14it in a short span of time?
  637. 29:17So if a celebrity posts one of our short
  638. 29:19links and now a million people are
  639. 29:21clicking on this link in a short span of
  640. 29:24time, we should be ready to absorb that
  641. 29:27traffic.
  642. 29:29First, let's take a look at a normal
  643. 29:31day. In step two, we estimated a
  644. 29:34thousand clicks for every link created,
  645. 29:38and we create 1 million links per day.
  646. 29:42So, that is about a billion redirect
  647. 29:45requests per day.
  648. 29:48If you spread them evenly over the day,
  649. 29:51that is more than 11,000 database reads
  650. 29:55per second.
  651. 29:57A well-tuned Postgress database might be
  652. 30:00able to do this, but 11,000 reads per
  653. 30:03second is already a lot of work for one
  654. 30:07database machine. And you should notice
  655. 30:10that every one of those reads from the
  656. 30:12database is asking a question whose
  657. 30:16answer never changes.
  658. 30:19So if we get a thousand queries for the
  659. 30:22same short URL,
  660. 30:25our database is replying with the same
  661. 30:28long URL 1,000 times.
  662. 30:32And honestly, that should feel wasteful
  663. 30:35to you.
  664. 30:37Keep this in mind because this can be
  665. 30:40easily fixed.
  666. 30:42But first, let's add the celebrity to
  667. 30:44our system.
  668. 30:47A million clicks arrive in the first
  669. 30:49hour. And if the link really goes
  670. 30:52global, it can be a 100 million clicks
  671. 30:56in a single day.
  672. 30:58And the painful part is that all of
  673. 31:01those requests are asking for the same
  674. 31:04short URL that the celebrity posted.
  675. 31:08This situation, where one thing goes
  676. 31:11viral and is accessed by many, is called
  677. 31:14a hotkey.
  678. 31:16and it is the signature failure of any
  679. 31:19readheavy system.
  680. 31:22So this is step five, the deep dives. We
  681. 31:25found the component where our problem is
  682. 31:28actually hard.
  683. 31:30Pause for a moment and think how can we
  684. 31:33fix this problem.
  685. 31:36Let's think about what we need. We don't
  686. 31:39want to send a million same queries to
  687. 31:42our database. So, we need a component
  688. 31:45that can store hotkeys.
  689. 31:47By hotkeys, we mean famous links that
  690. 31:50are getting clicked more frequently.
  691. 31:54That component is called a cache.
  692. 31:57And in our example, we will use Reddus,
  693. 32:00a popular cache that answers in under a
  694. 32:04millisecond.
  695. 32:06So when a user clicks on a short URL,
  696. 32:09the request goes to the application
  697. 32:11server and the server first checks with
  698. 32:14Reddus.
  699. 32:16If the cache has the URL, we call it a
  700. 32:19cache hit and we can return the long URL
  701. 32:23to the user. The request doesn't even go
  702. 32:27to the database.
  703. 32:30If the user requests answer is not in
  704. 32:32the cache, we read it from the Postgress
  705. 32:35database once and then put it into the
  706. 32:39cache and every user after that can
  707. 32:43access that link from the cache. So in
  708. 32:46case of a viral link, the first user
  709. 32:48reads from the database and then that
  710. 32:50data gets stored in the cache and the
  711. 32:52remaining millions of clicks are served
  712. 32:54from the cache. Now normally a cache
  713. 32:58comes with a famous headache and it is
  714. 33:00called staleness.
  715. 33:02Staleness basically means inconsistency
  716. 33:05between the cache and the database. It
  717. 33:08happens when the cache stores some data
  718. 33:10let's say x= 7. But then a user changes
  719. 33:13the data to x= 9 in the database. Now
  720. 33:16the cache is serving the old data 7 when
  721. 33:19we know in the database it was updated
  722. 33:21to 9. But if we look at our system
  723. 33:24carefully, a short code always points to
  724. 33:26the same long URL. We write the mapping
  725. 33:30once and we never update it. That means
  726. 33:33our cache can never serve a wrong
  727. 33:35answer. A URL shortener is a dream case
  728. 33:38for caching. And you should say this to
  729. 33:40the interviewer clearly. I can cache
  730. 33:43this aggressively because the data never
  731. 33:45changes. This one sentence shows your
  732. 33:48technical depth. But you should also
  733. 33:50mention that cache memory is limited. So
  734. 33:53Reddus removes entries that have not
  735. 33:54been used recently to make room for new
  736. 33:57ones. Popular links stay in the cache,
  737. 34:00dead links are removed from the cache.
  738. 34:02That is exactly what we want. Can we
  739. 34:05make further improvements? We can
  740. 34:07actually make two quick upgrades. First,
  741. 34:10if one link becomes so hot that even our
  742. 34:12cache gets overloaded, then we can move
  743. 34:14our top few thousand links even closer
  744. 34:16to the user. Basically, each application
  745. 34:19server can keep its own small copy of
  746. 34:22the top few thousand links in its own
  747. 34:24memory. So, the hottest links can be
  748. 34:26given out directly by the server. There
  749. 34:28is one more way to push hot links closer
  750. 34:30to the user and this is the content
  751. 34:33delivery network or CDN.
  752. 34:36A CDN is a fleet of servers sitting
  753. 34:39across different countries and
  754. 34:40continents. With a CDN, a user accessing
  755. 34:44a link from Tokyo gets its long URL from
  756. 34:47the server nearest to them instead of
  757. 34:50getting the URL from Virginia.
  758. 34:53Now comes one tricky part. When our
  759. 34:55server tells a browser, go to this long
  760. 34:58URL, there are two ways to do it. A 301
  761. 35:02redirect and a 302 redirect.
  762. 35:06301 means this link has moved
  763. 35:09permanently.
  764. 35:10A 302 redirect means go there for now,
  765. 35:14but ask me again next time you access
  766. 35:16this link. Most candidates pick 301
  767. 35:19without thinking because permanent
  768. 35:22sounds correct. Our mapping is permanent
  769. 35:24after all. But let's see what 301
  770. 35:27actually does. Once you click on a 301
  771. 35:30link, the browser remembers the answer.
  772. 35:33And the next time the same user clicks
  773. 35:35on this link, the browser goes straight
  774. 35:37to the destination URL. The browser
  775. 35:40doesn't even ask our system for the long
  776. 35:42URL. So, we actually don't know if the
  777. 35:45click happened or not. It's definitely
  778. 35:48faster for the user and less load for
  779. 35:50us. And it sounds like a pure win. But
  780. 35:54if the clicks never reach us, we can
  781. 35:56never count them. So, analytics becomes
  782. 35:59impossible.
  783. 36:00And there is a bigger problem. We lose
  784. 36:03control.
  785. 36:04If a short link turns out to point at a
  786. 36:06scam page, then we will want to kill
  787. 36:08that link. But the browsers have already
  788. 36:11memorized our answer and we cannot take
  789. 36:13it back. A 302 redirect keeps every
  790. 36:18click flowing through our service. Yes,
  791. 36:21we have to spend more to answer queries,
  792. 36:23but in exchange, we keep our analytics
  793. 36:25and control so that we can kill any link
  794. 36:28in the future.
  795. 36:30One thing I wanted to mention here is
  796. 36:31that the exact redirect behavior of the
  797. 36:33browsers also depends on the browser
  798. 36:36config and the headers we send. But what
  799. 36:39actually matters in the interview is the
  800. 36:40reasoning. If the business ever wants
  801. 36:43data analytics on clicks or they want
  802. 36:45the power to disable a link, I will
  803. 36:47choose the 302 redirect due to the
  804. 36:50reasons we just discussed.
  805. 36:53And now the last step of our framework
  806. 36:55which is bottlenecks and scaling. Here
  807. 36:58we need to do the critical analysis of
  808. 36:59our system. I will ask two questions for
  809. 37:02myself. What component of the system
  810. 37:05breaks first and what can we actually
  811. 37:07afford to lose? For example, if the
  812. 37:10reddis cache crashes, every read
  813. 37:13suddenly lands on the database again.
  814. 37:15The system becomes extremely slow if the
  815. 37:18cache fails. But the system does not
  816. 37:20become wrong. And when the cache comes
  817. 37:22back online, it will get filled as we
  818. 37:24process queries.
  819. 37:26Say this property out loud. The cache is
  820. 37:29a performance layer, not the source of
  821. 37:31truth. Next, what data can we never
  822. 37:35lose? I am sure that we should never
  823. 37:37lose the mapping table which tracks the
  824. 37:39short to long URL mapping. If we lose
  825. 37:43one row, then that link is dead forever
  826. 37:46for everyone who has that link.
  827. 37:50So the Postgress database should have a
  828. 37:52replica and regular backups.
  829. 37:55And one more thing, the counter must
  830. 37:58never repeat a number, even after a
  831. 38:01crash.
  832. 38:03If it repeats, two links get the same
  833. 38:06code. And that is what will really break
  834. 38:09the URL shortener system.
  835. 38:13So, let me summarize the full system
  836. 38:14once in the same way you would close
  837. 38:17this in the interview.
  838. 38:20A click hits our servers. Most of the
  839. 38:23time the answer comes straight from the
  840. 38:25cache and the user gets redirected
  841. 38:28within a few milliseconds with a 302
  842. 38:31redirect so that we keep our visibility
  843. 38:35and control.
  844. 38:37We make sure our database has replicas
  845. 38:40and is continuously backed up because
  846. 38:43the mapping table can never be lost.
  847. 38:47New links are generated using the global
  848. 38:49counter and the base 62 encoding.
  849. 38:53In our system, database reads scale
  850. 38:56through caching and writes were never
  851. 38:59the problem because our system is read
  852. 39:02heavy, not write heavy.
  853. 39:06From my side, here are the key takeaways
  854. 39:09from this interview question.
  855. 39:11First, this was a readheavy problem and
  856. 39:15that's why we used caching.
  857. 39:18So, whenever you have to serve the same
  858. 39:20thing again and again to users, the
  859. 39:23caching strategy should come to your
  860. 39:26mind.
  861. 39:27Secondly, we learned how to generate
  862. 39:30unique short codes using a counter and
  863. 39:33base 62 encoding
  864. 39:36and we also looked at the hashing
  865. 39:38technique. You should remember these
  866. 39:41techniques.
  867. 39:43Third, we learned the important concept
  868. 39:46of 301 and 302 redirects.
  869. 39:50Towards the end of the interview, you
  870. 39:52can expect some follow-up questions from
  871. 39:54the interviewer. And these follow-up
  872. 39:56questions test if you can point out what
  873. 39:58exactly you will change in your design.
  874. 40:02Let's quickly see three follow-up
  875. 40:04questions.
  876. 40:05Follow-up one. Two create requests
  877. 40:08arrive at the same instant. Can they get
  878. 40:11the same code?
  879. 40:13This question attacks the counter and in
  880. 40:16short the answer is no. They should not
  881. 40:19get the same code. We have to use a lock
  882. 40:22system so that only one request can
  883. 40:25fetch and change the counter. So if
  884. 40:28request one gets access to the counter,
  885. 40:31the counter should not be accessed by
  886. 40:33request two and the counter will become
  887. 40:35available only when request one has read
  888. 40:39the value of the counter, incremented
  889. 40:41the counter and stored the new value of
  890. 40:44the counter.
  891. 40:46After these three steps are done by
  892. 40:47request one, only then can the next
  893. 40:51request access the counter.
  894. 40:55Follow-up two.
  895. 40:57That counter is one shared thing. What
  896. 41:00happens when it goes down?
  897. 41:03It's a good question because the counter
  898. 41:05is a single point of failure for us. So
  899. 41:08what we can do to improve the design is
  900. 41:11instead of asking the counter for one
  901. 41:13number at a time, each application
  902. 41:16server gets a block of 10,000 numbers in
  903. 41:19one call.
  904. 41:21For example, server 1 gets the counts
  905. 41:24from 1 million to 1,ion10,000
  906. 41:28and then server 2 gets 1,10,01
  907. 41:32to 1,20,000
  908. 41:35and so on. And then each app server can
  909. 41:39allocate these numbers to the requests
  910. 41:41on their own.
  911. 41:43Now the counter is touched rarely and
  912. 41:46even if the counter goes down for a
  913. 41:48short time it will not be a single point
  914. 41:51of failure because the servers already
  915. 41:54have a block of numbers with them to
  916. 41:56handle incoming requests.
  917. 41:59Follow-up three. We want links to expire
  918. 42:03after a certain time. What will you do?
  919. 42:07Well, this is simple because in the URL
  920. 42:10mapping table, we can add an expiry date
  921. 42:14and when somebody tries to access this
  922. 42:16link, we compare if we have reached the
  923. 42:19expiry date and if we have reached it,
  924. 42:22we will block the URL or we can say the
  925. 42:26URL doesn't exist.
  926. 42:29The trade-off is that every URL redirect
  927. 42:32now does one extra comparison with the
  928. 42:35expiry date.
  929. 42:37With this, we wrap up the URL shortener
  930. 42:40problem. I want you to take a second to
  931. 42:44process all the questions and then you
  932. 42:47can move to the next problem.
  933. 42:50Now, it's time for the first lab of this
  934. 42:52course. And don't worry if you're not
  935. 42:54feeling confident because every lab
  936. 42:57comes with hints and full solutions. All
  937. 43:00you have to do is go to the link
  938. 43:02provided in the description, then enroll
  939. 43:04in the course and click start on the
  940. 43:07lab.
  941. 43:08In this first lab, you'll run the exact
  942. 43:11URL shortener that we just designed.
  943. 43:14You'll create real short links and then
  944. 43:16send 200 requests to one of the links
  945. 43:19and watch every single request hitting
  946. 43:22the database.
  947. 43:24The lab link is in the description. Go
  948. 43:26give it a try.
  949. 43:29Let's take a look at our second
  950. 43:30interview problem. And the interviewer
  951. 43:32asks you to design a rate limiter. Now
  952. 43:35rate limiter is one of the most
  953. 43:37important component of all the modern
  954. 43:39systems like chatgbt, claude, etc. In
  955. 43:44fact, rate limiter is also important for
  956. 43:46the URL shortener we designed earlier.
  957. 43:50We know that anyone can create an
  958. 43:52account on our website and call the
  959. 43:54create API endpoint to create a short
  960. 43:57URL.
  961. 43:58Now imagine one user named Shadow signs
  962. 44:01up on our platform and then writes a
  963. 44:04small script that runs in a loop and
  964. 44:07creates 1 million API calls in 1 hour.
  965. 44:12This will create 1 million junk URLs in
  966. 44:15our system.
  967. 44:17Due to this, our database will get
  968. 44:19filled with garbage links that doesn't
  969. 44:22serve any purpose.
  970. 44:24And because the shadow user is keeping
  971. 44:27the server busy, the legitimate users
  972. 44:30might see a slow system because the
  973. 44:32resources are overutilized by shadow
  974. 44:35user.
  975. 44:36And if you notice, this user has not
  976. 44:39hacked our server. They are simply
  977. 44:42calling our API too many times.
  978. 44:45So we need a component that can actually
  979. 44:48slow down or limit the noisy users.
  980. 44:52This component is called a rate limiter
  981. 44:56and design a rate limiter is a very
  982. 44:58common interview question on its own.
  983. 45:01Basically, we need to keep an upper cap
  984. 45:04on how many API calls per user can make.
  985. 45:08And if they try to make more than the
  986. 45:11upper limit, then we don't entertain
  987. 45:13their request.
  988. 45:15So, let's apply our six-step framework
  989. 45:18on this problem. Step one is to clarify
  990. 45:21the requirements.
  991. 45:23I would ask three questions for this
  992. 45:26problem.
  993. 45:27First question is how do we identify who
  994. 45:30is making API call. We can usually
  995. 45:33identify a API request by three things.
  996. 45:37First is which account it belongs to.
  997. 45:41Second way to identify is to see which
  998. 45:44IP address it came from. Third way is to
  999. 45:48track which API key the request is
  1000. 45:51carrying.
  1001. 45:52Now each of these three has a problem.
  1002. 45:56If we count per IP address, then one
  1003. 45:59office where 300 people share a single
  1004. 46:02internet connection looks like one very
  1005. 46:05noisy user and we will block all of
  1006. 46:08them. Also, an attacker can simply keep
  1007. 46:11changing their IP address to abuse the
  1008. 46:14system and IP addresses are easy to get.
  1009. 46:19If we count number of requests per API
  1010. 46:22key then it become niche because it only
  1011. 46:25works for developers who are given an
  1012. 46:28API key. Normal app users don't
  1013. 46:31generally use API keys
  1014. 46:34and if we count number of requests per
  1015. 46:37account that we can't do anything for
  1016. 46:40requests where nobody is logged in yet.
  1017. 46:43So let's say the interviewer tells us
  1018. 46:46that for loggedin user we need to track
  1019. 46:49number of requests per account and for
  1020. 46:52non-loggedin users we track IP address.
  1021. 46:56My second question is what is the limit
  1022. 46:59for each user?
  1023. 47:01Let's agree that every user can make 100
  1024. 47:05API requests per minute. And if the
  1025. 47:08interviewer asks, "How did you come up
  1026. 47:10with this number?" Then don't invent a
  1027. 47:13number confidently.
  1028. 47:15The honest answer is that you look at
  1029. 47:17your real traffic first.
  1030. 47:20In reality, you first check what a
  1031. 47:23normal user actually does in a minute
  1032. 47:26and then set the limit comfortably above
  1033. 47:29that.
  1034. 47:31And the third question is what should
  1035. 47:33happen when a user crosses this limit?
  1036. 47:36Will the extra requests wait in a queue
  1037. 47:39or should we reject them straight away?
  1038. 47:43Let's discuss about the queue for a
  1039. 47:45moment.
  1040. 47:46If a request is waiting on Q to be
  1041. 47:49processed, then there are high chances
  1042. 47:52the user's app has made the same request
  1043. 47:54again. So if we keep a Q then we might
  1044. 47:59unwantedly create duplicate requests.
  1045. 48:02I think it makes sense to reject the
  1046. 48:05requests if the limit exceed but we
  1047. 48:08should do it politely.
  1048. 48:10The user should know that they were
  1049. 48:12limited and when they can try again.
  1050. 48:16So we clearly state the scope of problem
  1051. 48:18clearly. We allow about 100 requests per
  1052. 48:23minute per user.
  1053. 48:25We count number of requests by account
  1054. 48:29for loggedin users.
  1055. 48:31Extra requests are rejected with a clear
  1056. 48:34message.
  1057. 48:36And users who do not exceed limit should
  1058. 48:39not even notice that rate limit system
  1059. 48:41exists.
  1060. 48:43The second step is to do back of the
  1061. 48:46envelope estimation.
  1062. 48:48And we only estimate the numbers that
  1063. 48:50actually decide something.
  1064. 48:53From my perspective, two numbers matter
  1065. 48:56here. The first one is how much do we
  1066. 48:59store per user.
  1067. 49:02If we use a simple counter, we need one
  1068. 49:05number and one time stamp. That is
  1069. 49:08roughly 50 bytes per user. So 10 million
  1070. 49:12active users is about 500 megabytes,
  1071. 49:16which fits in the memory of one machine
  1072. 49:19easily.
  1073. 49:20Keep this 50 bytes in mind. because it
  1074. 49:23will be useful later.
  1075. 49:26The second number is the API calls rate
  1076. 49:29because this rate limiter component
  1077. 49:32checks every single request that enters
  1078. 49:34our system.
  1079. 49:3610 million users and each user on
  1080. 49:39average makes 100 requests per day which
  1081. 49:43makes total requests to be 1 billion
  1082. 49:46requests a day which is roughly 12,000
  1083. 49:50requests per second on average.
  1084. 49:54And let's say in the busy time the
  1085. 49:56traffic goes by three times. That will
  1086. 50:00be 35,000 per second in the peak time.
  1087. 50:04Students usually get confused here. So I
  1088. 50:07want to make one thing absolutely clear.
  1089. 50:11This 100 per day is basically an average
  1090. 50:15of how many requests we get from each
  1091. 50:18user per day. Do not confuse this with
  1092. 50:21our limit. Our limit is set as 100 per
  1093. 50:26minute. And this limit is for heavy
  1094. 50:29users.
  1095. 50:30Normal users never key the 100 requests
  1096. 50:33per minute limit. In fact, they use 100
  1097. 50:37requests in an entire day. Okay, let's
  1098. 50:41move on. Next thing to notice is that a
  1099. 50:44user's 100 requests a day do not arrive
  1100. 50:47evenly. They come in a few short bursts
  1101. 50:51when somebody actually opens the app.
  1102. 50:54which is exactly why a per minute limit
  1103. 50:56can catch abuse without ever touching
  1104. 50:59normal use.
  1105. 51:02Step three is defining the API and this
  1106. 51:05one is very easy. A rate limiter check
  1107. 51:09only one thing. It checks if this
  1108. 51:12particular request is allowed or not.
  1109. 51:15When the answer is no, we respond to the
  1110. 51:18user with a status code 429, which means
  1111. 51:22too many requests. And we also attach a
  1112. 51:26retry after header. This retry after
  1113. 51:29header is basically a small note that
  1114. 51:32says try again in 40 seconds.
  1115. 51:35This note is important because without
  1116. 51:38this note, the blocked user will keep
  1117. 51:40retrying again and again. And our
  1118. 51:44limiter will now deal with even more
  1119. 51:46traffic than before. And it helps to
  1120. 51:50send two more small numbers on every
  1121. 51:52successful response as well. how many
  1122. 51:55requests the user has left and when
  1123. 51:58their limit resets so that a good user
  1124. 52:01can then slow itself down before it ever
  1125. 52:04gets blocked. Now before we draw the
  1126. 52:07components, there is one important
  1127. 52:09question that I need you to think about.
  1128. 52:12Where should this rate limit component
  1129. 52:14live in overall design?
  1130. 52:17This component rejects API requests. So
  1131. 52:21it should sit as early as possible at
  1132. 52:24the front door of our [music] system.
  1133. 52:26That front door is the API gateway, the
  1134. 52:30first server that every request passes
  1135. 52:33through. If we place the limiter deep
  1136. 52:36inside the system, let's say next to the
  1137. 52:39database, then a rejected request has
  1138. 52:42already traveled through half of our
  1139. 52:44system without any real purpose.
  1140. 52:48Step four is to do the high-level design
  1141. 52:51and we start with the simplest version.
  1142. 52:54We have one gateway server and in its
  1143. 52:56memory we keep one counter per user.
  1144. 53:00When a request from user named Allen
  1145. 53:03arrives, then we check the Allen's
  1146. 53:05counter. If the counter is less than 100
  1147. 53:09in the current minute, we add one and
  1148. 53:12let the request go through.
  1149. 53:14When the current minute ends in the
  1150. 53:16clock, the counter resets to zero.
  1151. 53:20If the count reaches to 100, then we
  1152. 53:23respond to user that request limit has
  1153. 53:25been reached. Try later.
  1154. 53:28This is called a fixed window technique
  1155. 53:31because we are looking at clock and
  1156. 53:33tracking requests for each minute and
  1157. 53:36each minute is fixed window. You can't
  1158. 53:39change the clock and honestly this
  1159. 53:41simple version already works.
  1160. 53:44So let's go and find the hard part of
  1161. 53:46this problem.
  1162. 53:48Here is the first problem that we will
  1163. 53:50face. Say a user named Allen sends 100
  1164. 53:54requests in the last second of a minute.
  1165. 53:58All of the requests are allowed because
  1166. 54:00the current minute has not ended.
  1167. 54:03Then the current minute ends and the
  1168. 54:06counter resets to zero.
  1169. 54:09Now Allan sends 100 more requests in the
  1170. 54:12first second of the new minute. These
  1171. 54:15are also allowed because it's a new
  1172. 54:17minute. So if you notice, Allen just
  1173. 54:20pushed 200 requests in about 2 seconds.
  1174. 54:24100 in the last second of earlier minute
  1175. 54:27and 100 in the first second of the
  1176. 54:29current minute. and our limiter which
  1177. 54:32promised 100 per minute allowed
  1178. 54:35everything because technically these
  1179. 54:38were two different minutes according to
  1180. 54:40the clock.
  1181. 54:41The problem is how we are resetting the
  1182. 54:43counter every minute. Our minute window
  1183. 54:46has a sharp edges.
  1184. 54:48Pause the video for a moment and think
  1185. 54:51how would you count the last 1 minute
  1186. 54:53more accurately.
  1187. 54:56The most obvious fix is to stop using
  1188. 54:58fixed minute-wise window at all and we
  1189. 55:00use a technique called sliding window
  1190. 55:03log.
  1191. 55:04Let's make this concrete with an
  1192. 55:06example.
  1193. 55:07Every time Allen sends a request, we
  1194. 55:10write down the exact timestamp when a
  1195. 55:12request arrives.
  1196. 55:14When a new request comes in, we look at
  1197. 55:16our list and throw away every time stamp
  1198. 55:19older than 60 seconds and count the
  1199. 55:21number of requests in last 60 seconds.
  1200. 55:25Let's say right now we have 94 requests
  1201. 55:28sitting inside that last minute and
  1202. 55:30Allen is still under the 100 requests
  1203. 55:33limit. So the new request comes and we
  1204. 55:36add its timestamp to the list as well
  1205. 55:38and this request goes through.
  1206. 55:4110 seconds later Allen sends another
  1207. 55:43request and we do exactly the same
  1208. 55:45thing. But now the last 60 seconds means
  1209. 55:49something different because time has
  1210. 55:51moved forward and some of those old
  1211. 55:53timestamps have dropped out of the
  1212. 55:55window of 60 seconds.
  1213. 55:58So the window slides forward with every
  1214. 56:00single request and checks only last 60
  1215. 56:03seconds.
  1216. 56:05To summarize, we remember the timestamp
  1217. 56:08of every request and when a new request
  1218. 56:10arrives, we count how many timestamps
  1219. 56:13fall inside the last 60 seconds. We
  1220. 56:16reject request if we have reached 100
  1221. 56:18requests quota. We don't have fixed
  1222. 56:21window anymore. Last 60 seconds window
  1223. 56:24moves as new requests arrive.
  1224. 56:27But this approach costs us a lot. Our
  1225. 56:29limit is 100 per minute. So for an
  1226. 56:32active user we are storing 100
  1227. 56:34timestamps about 2 and a half kilobytes
  1228. 56:37per user instead of 50 bytes.
  1229. 56:4010 million users now needs around 25 GB
  1230. 56:44of memory. And notice this cost is tied
  1231. 56:47directly to the API limit. So if the
  1232. 56:50interviewer had said 10,000 requests per
  1233. 56:52minute, this option would be not
  1234. 56:54feasible at all.
  1235. 56:57Now the last option to track API
  1236. 56:59requests is called a token bucket. This
  1237. 57:02token bucket approach is what Stripe,
  1238. 57:05GitHub, and most API gateways actually
  1239. 57:08use for tracking API calls. Let's see
  1240. 57:11how it works.
  1241. 57:13Every user gets a bucket that holds 100
  1242. 57:16tokens and every request from user takes
  1243. 57:19one token out of it and we will keep
  1244. 57:22filling the bucket at rate of 100 tokens
  1245. 57:24per minute which is approximately 1.67
  1246. 57:28tokens per second. If user doesn't make
  1247. 57:31any API call the bucket will remain at
  1248. 57:34100 and we don't need to fill. If the
  1249. 57:38bucket is empty, we will reject the
  1250. 57:40request. So, let's take Allen again and
  1251. 57:43watch this work. Allan drains all 100
  1252. 57:47tokens in 2 seconds. And then Allen has
  1253. 57:51to slow down because the bucket only
  1254. 57:53fills about 1.67
  1255. 57:56or approximately two tokens per second.
  1256. 57:59So, from this point on, he can make
  1257. 58:01about two calls per second. And he is
  1258. 58:04never fully locked out from the system
  1259. 58:07because each second he gets two tokens.
  1260. 58:11So let's be precise about what we are
  1261. 58:13promising to our users in this approach.
  1262. 58:16A token bucket does not give a hard
  1263. 58:19ceiling of 100 requests inside every
  1264. 58:2260-second window. User can also use 100
  1265. 58:26tokens in one go if bucket is full and
  1266. 58:30then bucket gives a sustained rate of
  1267. 58:321.67 tokens per second.
  1268. 58:36And this approach is different from the
  1269. 58:38fixed windows problem because here the
  1270. 58:41burst is a number we chose on purpose,
  1271. 58:44not an accident of where the clock
  1272. 58:46happens to reset. And if we want a
  1273. 58:48smaller burst, we make the bucket
  1274. 58:51smaller. For example, we can say bucket
  1275. 58:54size is 20 tokens without touching the
  1276. 58:57sustained rate of 1.67 tokens per
  1277. 59:00second.
  1278. 59:02What we store per user is only the
  1279. 59:04tokens left in the bucket and the time
  1280. 59:07of the last refill so that we can refill
  1281. 59:09the bucket continuously.
  1282. 59:12If we look at the memory needed to store
  1283. 59:14these info is again just 50 bytes.
  1284. 59:19Now you need to put the honest
  1285. 59:20comparison of all these approaches in
  1286. 59:23front of the interviewer. The sliding
  1287. 59:25window approach is the only one of these
  1288. 59:27three that gives an exact ceiling on
  1289. 59:30every rolling 60 seconds. But we are
  1290. 59:33rejecting it because we need to store a
  1291. 59:35lot of time stamps and it needs lot of
  1292. 59:37memory.
  1293. 59:39We are choosing the token bucket for two
  1294. 59:41different reasons. First, it lets us set
  1295. 59:45the continuous rate at which we fill the
  1296. 59:47bucket. Due to this continuous filling
  1297. 59:49of bucket, a user is never fully locked
  1298. 59:52out of system.
  1299. 59:54They still get two calls per second. And
  1300. 59:57if the bucket is full, they can still
  1301. 1:00:00spend all 100 tokens in one go if they
  1302. 1:00:03wish to.
  1303. 1:00:05Second, the bucket already knows how
  1304. 1:00:08many tokens are left, which is the
  1305. 1:00:10number we can send back to our users in
  1306. 1:00:12the response headers.
  1307. 1:00:15So let's see the summary of the design
  1308. 1:00:16we have till now.
  1309. 1:00:19The rate limiter sits at the API gateway
  1310. 1:00:22which is the front door of our system.
  1311. 1:00:25We count per account for loggedin users
  1312. 1:00:28and per IP address for non-loggedin
  1313. 1:00:31traffic.
  1314. 1:00:32Every user gets a token bucket which
  1315. 1:00:35gives them a sustained 100 requests a
  1316. 1:00:37minute with a burst allowance.
  1317. 1:00:40And when a user runs out of API
  1318. 1:00:42requests, we return 429 error with a
  1319. 1:00:46retry note.
  1320. 1:00:48And this design works beautifully on one
  1321. 1:00:51gateway server, but real system often
  1322. 1:00:54runs more than one gateway servers.
  1323. 1:00:57So what happens to our bucket when the
  1324. 1:00:59Allen's requests start landing on more
  1325. 1:01:02than one gateway?
  1326. 1:01:04Now in a real production system, we
  1327. 1:01:07would normally run multiple API gateways
  1328. 1:01:10to handle requests behind a load
  1329. 1:01:12balancer.
  1330. 1:01:14Also having one API gateway is also a
  1331. 1:01:18single point of failure. So let's say in
  1332. 1:01:21our case we run two API gateways to
  1333. 1:01:25track API requests made by users. Each
  1334. 1:01:28gateway store buckets in its own memory.
  1335. 1:01:32Now Allen makes multiple API calls and
  1336. 1:01:35the load balancer splits traffic and
  1337. 1:01:38sends some requests to gateway one and
  1338. 1:01:40some requests to gateway 2.
  1339. 1:01:43Can you spot the problem here? Each
  1340. 1:01:46gateway sees only half of Allen's
  1341. 1:01:48requests and each one is holding its own
  1342. 1:01:52separate bucket for Allen.
  1343. 1:01:54So Allen effectively has two buckets now
  1344. 1:01:57and he can make more API calls. Add a
  1345. 1:02:01third gateway and he can make even more
  1346. 1:02:03calls.
  1347. 1:02:05Basically, none of the gateways know how
  1348. 1:02:08many calls did Allen make because both
  1349. 1:02:10gateways will be storing different
  1350. 1:02:12numbers.
  1351. 1:02:14The fix is to move the buckets out of
  1352. 1:02:16the gateways memory and put them in one
  1353. 1:02:19shared place that every gateway can talk
  1354. 1:02:22to. This shared place should be fast.
  1355. 1:02:26Therefore, we can use Reddus for doing
  1356. 1:02:28this job. All the buckets will be stored
  1357. 1:02:31in Reddus. Now every gateway checks
  1358. 1:02:34Reddus to determine if the user is
  1359. 1:02:36eligible to make API call.
  1360. 1:02:39But we need to be careful here because
  1361. 1:02:42there is one more problem inside this
  1362. 1:02:44fix which interviewer may ask. So the
  1363. 1:02:48question is what happens if some of the
  1364. 1:02:50Allen's API requests reach to both API
  1365. 1:02:54gateways and both API gateways try to
  1366. 1:02:57change the data in Allen's bucket at the
  1367. 1:03:00same time.
  1368. 1:03:02Let's see what we can do. Now accessing
  1369. 1:03:06a token bucket is not one action. It is
  1370. 1:03:09three different action.
  1371. 1:03:11First you read how many tokens are left.
  1372. 1:03:15Calculate how many tokens we need to add
  1373. 1:03:17based on time and then write back the
  1374. 1:03:20new value of tokens left. If both
  1375. 1:03:23gateways do these three steps at the
  1376. 1:03:26same moment, both of them read Allen's
  1377. 1:03:28last remaining token. Both of them think
  1378. 1:03:31there is a token available and both of
  1379. 1:03:34them allow the API request.
  1380. 1:03:37The fix for this problem is called an
  1381. 1:03:39atomic operation.
  1382. 1:03:41We have to ensure that all three steps
  1383. 1:03:43are done and we consider these three
  1384. 1:03:46steps as one operation.
  1385. 1:03:49When one request access the bucket, we
  1386. 1:03:52lock this bucket so that these three
  1387. 1:03:54steps can be done and no other request
  1388. 1:03:57access this bucket while this operation.
  1389. 1:04:00So how does this lock works? Instead of
  1390. 1:04:04our gateway doing these three steps one
  1391. 1:04:06by one over the network, we hand all
  1392. 1:04:09three steps to Reddus as one small
  1393. 1:04:11script. In Reddus, these are called Luis
  1394. 1:04:14scripts. And Reddus runs that whole
  1395. 1:04:17script in one go. And while it is
  1396. 1:04:20running, no other gateway can touch
  1397. 1:04:22Allen's bucket.
  1398. 1:04:24Now, it does not matter how many
  1399. 1:04:26gateways we run because the bucket is
  1400. 1:04:29only ever changed by one request at a
  1401. 1:04:32time.
  1402. 1:04:34And now, the last step of our framework
  1403. 1:04:36is bottlenecks and scaling. Let's do the
  1404. 1:04:39critical analysis of our system. First,
  1405. 1:04:42a quick check on Reddus itself. One
  1406. 1:04:45Reddus instance can handle around
  1407. 1:04:48100,000 operations per second. And from
  1408. 1:04:51our requirement, we know that we need
  1409. 1:04:5335,000 operations per second. So, one
  1410. 1:04:57instance is enough for now with room to
  1411. 1:04:59grow.
  1412. 1:05:01Every API request now makes one extra
  1413. 1:05:04trip to Reddus, which costs around 1
  1414. 1:05:07millisecond if Reddus is sitting close
  1415. 1:05:09to our gateways. And that word close
  1416. 1:05:12matters a lot here. If your gateway is
  1417. 1:05:15in one region and Reddus is in another,
  1418. 1:05:18that 1 millisecond becomes 50
  1419. 1:05:21milliseconds. And now your rate limiter
  1420. 1:05:23is the slowest thing in your entire
  1421. 1:05:25system.
  1422. 1:05:271 millisecond is okay, but 50 is not.
  1423. 1:05:32But the most important question is what
  1424. 1:05:34happens if Reddus goes down? Our API
  1425. 1:05:38gateways cannot count anything. Now here
  1426. 1:05:42we have to choose between two failure
  1427. 1:05:44modes and this choice is a classic
  1428. 1:05:47interview moment. The first option is
  1429. 1:05:50fail open. Fail open means if we cannot
  1430. 1:05:53count we let everyone through the API
  1431. 1:05:56gateway. The product keeps working and
  1432. 1:05:59we accept the risk of abuse for a few
  1433. 1:06:02minutes.
  1434. 1:06:03Second option is fail closed. Fail
  1435. 1:06:06closed means that if we cannot count, we
  1436. 1:06:09block everyone. Nobody's request is
  1437. 1:06:12entertained until we get the Reddus back
  1438. 1:06:15online.
  1439. 1:06:17Which of these choices is correct? It
  1440. 1:06:20depends on kind of system we are
  1441. 1:06:22protecting. For a normal product API, we
  1442. 1:06:26should allow all users because blocking
  1443. 1:06:29every real user is worse than allowing
  1444. 1:06:31one attacker for a few minutes. One of
  1445. 1:06:35the example is Stripe. Stripe says that
  1446. 1:06:38their API stays functional if the rate
  1447. 1:06:40limiter store goes down. But for a
  1448. 1:06:43sensitive endpoint like login endpoint
  1449. 1:06:46where the rate limiter is actually
  1450. 1:06:48stopping users from password guessing
  1451. 1:06:50attacks, closing the system for all
  1452. 1:06:53users or fail closed can be the safer
  1453. 1:06:56choice.
  1454. 1:06:57And if red is tied because your system
  1455. 1:07:00is already under a heavy load from
  1456. 1:07:02users, then allowing all users to send
  1457. 1:07:05lot of requests straight to your servers
  1458. 1:07:08can create troubles for your system. So
  1459. 1:07:11say this trade-off clearly to your
  1460. 1:07:13interviewer. So let me summarize the
  1461. 1:07:16full system once. In the same way, you
  1462. 1:07:19will close this in real interview.
  1463. 1:07:22The rate limiter lives at the API
  1464. 1:07:24gateway which is the front door of our
  1465. 1:07:26system.
  1466. 1:07:27We count per account for logged in users
  1467. 1:07:31and per IP address for anonymous
  1468. 1:07:33traffic. Every user gets a token bucket
  1469. 1:07:37about 50 bytes. And we are refilling
  1470. 1:07:39this bucket at about 1.67 tokens per
  1471. 1:07:43second. That is a sustained 100 requests
  1472. 1:07:46a minute with a burst allowance on top.
  1473. 1:07:50The buckets live in Reddus which is
  1474. 1:07:52shared by all the gateways and each
  1475. 1:07:55check runs as one small atomic script.
  1476. 1:07:58So the count stays correct no matter how
  1477. 1:08:01many gateways try to access same bucket.
  1478. 1:08:05When a user crosses the limit we return
  1479. 1:08:07429 with the retry after note and if
  1480. 1:08:11reddis fails we fail open if security is
  1481. 1:08:15not the concern. From my side, here are
  1482. 1:08:18the key takeaways from this interview
  1483. 1:08:20question. First, a rate limiter belongs
  1484. 1:08:24at the front door of the system. We
  1485. 1:08:26should reject useless work before it
  1486. 1:08:29becomes work. Second, remember the token
  1487. 1:08:32bucket technique. It is small and it
  1488. 1:08:35lets you set the sustained rate and the
  1489. 1:08:37burst as two separate numbers, which is
  1490. 1:08:40how real API limits are actually
  1491. 1:08:42published.
  1492. 1:08:44And third, the most important lesson.
  1493. 1:08:47Local counters lie in distributed
  1494. 1:08:49systems. Whenever multiple servers are
  1495. 1:08:52counting the same thing, the count must
  1496. 1:08:55live in one shared place and the update
  1497. 1:08:58must happen as one atomic step. Keep an
  1498. 1:09:02eye out for this pattern because it is
  1499. 1:09:04coming back. When we make sure the same
  1500. 1:09:07concert seat is never sold to two people
  1501. 1:09:10and when we make sure the same driver is
  1502. 1:09:13never sent to two riders, it is going to
  1503. 1:09:16be this same problem in a new costume.
  1504. 1:09:20Towards the end of the interview, the
  1505. 1:09:21interviewer can probably cross question
  1506. 1:09:24and ask some tricky questions for some
  1507. 1:09:26of the parts of our design.
  1508. 1:09:28So the interviewer wants to see what
  1509. 1:09:30changes you will make to the design
  1510. 1:09:32based on the questions without throwing
  1511. 1:09:35the whole design away. Let's look at
  1512. 1:09:38three such questions.
  1513. 1:09:40Question number one. You assumed 100
  1514. 1:09:43requests a minute as the limit, but 100
  1515. 1:09:46API requests per minute cannot be the
  1516. 1:09:49right limit for every API.
  1517. 1:09:52How will you handle that? And to be
  1518. 1:09:54honest, this is a good question because
  1519. 1:09:57we might have different limits for
  1520. 1:09:59different APIs. For example, if users
  1521. 1:10:03are just reading the URL, the limit
  1522. 1:10:06might be 10,000. But if they are
  1523. 1:10:08creating a URL using API, then the limit
  1524. 1:10:11might just be 100 because creating
  1525. 1:10:14something takes a lot of resources
  1526. 1:10:17comparatively.
  1527. 1:10:18and login API would have a completely
  1528. 1:10:21different limit and it will be the
  1529. 1:10:24strictest of all. If you make three API
  1530. 1:10:27calls backto back, you might be
  1531. 1:10:30perceived as a bad actor. So the limit
  1532. 1:10:33is not one number sitting in our code.
  1533. 1:10:36Instead, we keep a small set of rules.
  1534. 1:10:40Different rules apply to the login
  1535. 1:10:42endpoint API. Different rules apply to
  1536. 1:10:45the create URL API. Different rules
  1537. 1:10:48apply to the read URL API.
  1538. 1:10:52When any request arrives, the gateway
  1539. 1:10:55looks up the relevant rule and uses the
  1540. 1:10:57right bucket.
  1541. 1:10:59Question number two, your system is now
  1542. 1:11:02taking 1 million API requests per
  1543. 1:11:05second. Is one Reddus still enough?
  1544. 1:11:09The answer is no. And we should say that
  1545. 1:11:12clearly. One Reddus handles around
  1546. 1:11:15100,000 operations per second. So for
  1547. 1:11:19one million requests per second, we will
  1548. 1:11:21roughly need around 10 Reddus instances.
  1549. 1:11:25And the good news for us is that you can
  1550. 1:11:27divide rate limiting very cleanly
  1551. 1:11:30because Allen's bucket has nothing to do
  1552. 1:11:32with John's or anyone else's bucket. So
  1553. 1:11:35we can easily run several Reddus
  1554. 1:11:37instances and we decide which Reddus
  1555. 1:11:40holds which user data.
  1556. 1:11:43Every request for Allen lands on the
  1557. 1:11:45same Reddus, maybe Reddus one, so that
  1558. 1:11:49the count stays correct and no Reddus
  1559. 1:11:52instance ever has to talk to another
  1560. 1:11:54one. But there is one issue that we
  1561. 1:11:57should think about. Let's say Alan has
  1562. 1:12:00reached his limit of 100, but he is not
  1563. 1:12:03stopping. He is continuously sending the
  1564. 1:12:07extra requests. And every time he is
  1565. 1:12:10sending a request, we confirm with the
  1566. 1:12:12Reddus bucket if he is eligible to make
  1567. 1:12:15an API call. So Allen is still keeping
  1568. 1:12:19the Reddus busy. So if Alan hammers us
  1569. 1:12:22at 50,000 requests per second, then we
  1570. 1:12:26will be making 50,000 calls to Reddus to
  1571. 1:12:30check if this API call is valid.
  1572. 1:12:33that keeps Reddus busy even though we
  1573. 1:12:36are not allowing more than 100 calls a
  1574. 1:12:39minute. Let's see the last question.
  1575. 1:12:43Every API request now makes a network
  1576. 1:12:45call to Reddus to check the limit. Can
  1577. 1:12:48you avoid this network call? Well, the
  1578. 1:12:51answer to this question is yes, we can
  1579. 1:12:53avoid it. And some large companies
  1580. 1:12:56actually do this. For example, what
  1581. 1:12:59Stripe does is that they keep a small
  1582. 1:13:01counter inside each API gateway's own
  1583. 1:13:04memory. And then this counter syncs with
  1584. 1:13:07Reddus every few hundred milliseconds
  1585. 1:13:10instead of asking Reddus on every single
  1586. 1:13:12API request.
  1587. 1:13:15And this local counter also fixes the
  1588. 1:13:17problem we just discussed because once
  1589. 1:13:20the gateway knows Allen is blocked, the
  1590. 1:13:23gateway can reject his requests straight
  1591. 1:13:25away instead of making a network call to
  1592. 1:13:28Reddus and confirming from Reddus.
  1593. 1:13:31The trade-off is that for those few
  1594. 1:13:34hundred milliseconds where the gateway
  1595. 1:13:36has not synced with Reddus, the limit
  1596. 1:13:38goes slightly loose.
  1597. 1:13:40We don't know about the exact limit
  1598. 1:13:42right now because we are not
  1599. 1:13:44communicating with Reddus for a short
  1600. 1:13:46period of time. This is it for the rate
  1601. 1:13:49limiter problem. Take a second to
  1602. 1:13:52process these three answers and then we
  1603. 1:13:54can move on to the next problem.
  1604. 1:13:57It's time for the next lab. In this lab,
  1605. 1:14:00you will place a rate limiter in front
  1606. 1:14:02of an API and watch it apply a limit of
  1607. 1:14:0510 requests per minute. If you try to
  1608. 1:14:07make more than 10 requests, you will be
  1609. 1:14:09rejected. The lab link is in the
  1610. 1:14:12description. Go try it yourself.
  1611. 1:14:16Let's now take a look at our next
  1612. 1:14:17interview question. The interviewer says
  1613. 1:14:20that you need to design a notification
  1614. 1:14:22system. So, first let's be absolutely
  1615. 1:14:25clear. What does notification system
  1616. 1:14:27actually do? And to be honest, it's very
  1617. 1:14:30obvious because we all use smartphones
  1618. 1:14:32and get notification from Uber, Amazon,
  1619. 1:14:36Gmail, Slack, and many other apps. You
  1620. 1:14:39might have got one notification for this
  1621. 1:14:41YouTube video as well. If you're a
  1622. 1:14:43subscriber
  1623. 1:14:45on a high level, we can say that when
  1624. 1:14:47some event happens inside the system,
  1625. 1:14:50then we have to send a nudge to a
  1626. 1:14:52particular user who should know about
  1627. 1:14:54this event. For example, your payment
  1628. 1:14:57goes through, you get a notification.
  1629. 1:15:00Someone replies to your comment and then
  1630. 1:15:02you get a notification.
  1631. 1:15:04Now, one simple thing companies can do
  1632. 1:15:07is send the updates to the app in your
  1633. 1:15:09phone and when you open the app, then
  1634. 1:15:12you get the notification.
  1635. 1:15:14But most of the time, you might not open
  1636. 1:15:16the app on time. So, we can't depend on
  1637. 1:15:19user opening the app or website. So we
  1638. 1:15:23need to notify them outside the app and
  1639. 1:15:25we can do that in four different ways.
  1640. 1:15:29First we can send a push notification
  1641. 1:15:32lighting up users phone screen. Second
  1642. 1:15:35we can use an SMS.
  1643. 1:15:38Third we can use email and last is as a
  1644. 1:15:43small red notification dot waiting
  1645. 1:15:45inside the app when you open the app. To
  1646. 1:15:48summarize notification system we can say
  1647. 1:15:51that when event happens on one side the
  1648. 1:15:54right user gets the right message on the
  1649. 1:15:57right channels.
  1650. 1:15:59This problem looks like a small side
  1651. 1:16:01service but it contains some system
  1652. 1:16:03design pattern that we will reuse in
  1653. 1:16:06almost every problem and that is exactly
  1654. 1:16:09why interviewers love this question.
  1655. 1:16:12So now let's apply our six-step
  1656. 1:16:15framework on this problem. Step one is
  1657. 1:16:18to clarify the requirements and remember
  1658. 1:16:21that our aim is to get functional and
  1659. 1:16:23non-functional requirements of this
  1660. 1:16:25system by asking minimum number of
  1661. 1:16:27questions. I would ask four questions
  1662. 1:16:30for this problem. First question is what
  1663. 1:16:34are these notifications for? Because a
  1664. 1:16:37product usually sends two very different
  1665. 1:16:39kinds of notifications.
  1666. 1:16:41We have transactional notifications that
  1667. 1:16:44we send when a payment goes through or
  1668. 1:16:46we share a login code or something
  1669. 1:16:48similar. And then there are marketing
  1670. 1:16:51notifications like a flash sale is live
  1671. 1:16:54or sale ends in 3 hours.
  1672. 1:16:57Let's say the interviewer says that we
  1673. 1:16:59need both kind of notifications.
  1674. 1:17:02Then interviewer is basically expecting
  1675. 1:17:04us to build one central notification
  1676. 1:17:07system that can take care of all the
  1677. 1:17:09different needs our product require.
  1678. 1:17:12My second question will be which
  1679. 1:17:14channels do we send notifications to?
  1680. 1:17:17Let's say the interviewer says four
  1681. 1:17:19types of notifications are needed that
  1682. 1:17:22includes push notification, SMS,
  1683. 1:17:25email and inapp notifications.
  1684. 1:17:29Then my third question will be do
  1685. 1:17:32transactional and marketing
  1686. 1:17:33notifications need to reach the user
  1687. 1:17:35equally fast.
  1688. 1:17:37I will ask this because two types of
  1689. 1:17:39notifications have very different
  1690. 1:17:41patience levels. A login code has to
  1691. 1:17:44reach the user. Let's call our user Alan
  1692. 1:17:47while he is still staring at the screen.
  1693. 1:17:50So transactional notifications need to
  1694. 1:17:53be delivered within few seconds.
  1695. 1:17:55But for a flash sale notification, we
  1696. 1:17:58might be able to afford slight delay for
  1697. 1:18:00marketing notifications.
  1698. 1:18:02So if we have a few seconds of headroom,
  1699. 1:18:05then we are allowed to accept the work
  1700. 1:18:07first and do the actual sending a moment
  1701. 1:18:10later. And that freedom is going to
  1702. 1:18:13shape our design. And the fourth
  1703. 1:18:16question is the most important one. Can
  1704. 1:18:19we lose a notification?
  1705. 1:18:21And can we send the same notification
  1706. 1:18:23twice?
  1707. 1:18:25The interviewer says losing a
  1708. 1:18:26notification is bad, but sending the
  1709. 1:18:29same notification twice is even worse.
  1710. 1:18:33This makes sense because if Alan makes a
  1711. 1:18:35payment and receives two payment SMS,
  1712. 1:18:38Allen will think he has been charged
  1713. 1:18:40twice for the same thing. So sending it
  1714. 1:18:43twice does make it worse.
  1715. 1:18:46And one important clarification you
  1716. 1:18:49should ask from interviewer.
  1717. 1:18:51Do users get notification preferences?
  1718. 1:18:54By user preference, we simply mean if
  1719. 1:18:57our users have ability to switch off
  1720. 1:18:59marketing notifications. But of course,
  1721. 1:19:02important notifications like payment
  1722. 1:19:04cannot be turned off. Let's say the
  1723. 1:19:07interviewer says yes to user preference
  1724. 1:19:10because in some countries they have law
  1725. 1:19:12that anyone can choose to opt out from
  1726. 1:19:15marketing messages. So before moving on
  1727. 1:19:18to next stage of interview, we state the
  1728. 1:19:20scope of problem very clearly. The
  1729. 1:19:23functional requirements say what the
  1730. 1:19:25system must do. Our notification system
  1731. 1:19:29takes one event and it delivers it on
  1732. 1:19:31four channels and it will respect users
  1733. 1:19:34preferences. That means users can turn
  1734. 1:19:37off marketing notifications.
  1735. 1:19:40The non-functional requirements say how
  1736. 1:19:42well the system must behave. We
  1737. 1:19:45mentioned to interviewer that a small
  1738. 1:19:47delay is acceptable. We try not to lose
  1739. 1:19:50notification and the user should ideally
  1740. 1:19:53never see the same notification twice.
  1741. 1:19:56And notice that we have used the word
  1742. 1:19:58ideally because as we will see later in
  1743. 1:20:01this problem, guaranteeing only one
  1744. 1:20:04notification is impossible. And this
  1745. 1:20:07will be the hardest part of this whole
  1746. 1:20:09design.
  1747. 1:20:13The second step is back of the envelope
  1748. 1:20:15estimation. And the first thing I will
  1749. 1:20:18do here is ask the interviewer for the
  1750. 1:20:20scale. How many notifications per day we
  1751. 1:20:23are looking at? And is the traffic
  1752. 1:20:25steady or it spikes during certain time
  1753. 1:20:28of the day. Let's say the answer is 10
  1754. 1:20:31million notifications per day. And if
  1755. 1:20:34these 10 million notifications were
  1756. 1:20:36spread evenly across the day, that is
  1757. 1:20:39around 115 notifications per second,
  1758. 1:20:43which is not scary at all. But as a
  1759. 1:20:46candidate, we should always ask for the
  1760. 1:20:48peak because that is where the problems
  1761. 1:20:50start arising. When the marketing team
  1762. 1:20:53sends a campaign to 1 million users at 9
  1763. 1:20:56in the morning, then those 1 million
  1764. 1:20:59notifications arrive at the same moment.
  1765. 1:21:03So our system must survive sudden
  1766. 1:21:05floods. And I want to tell you one
  1767. 1:21:08important fact. We do not deliver
  1768. 1:21:11anything ourselves. Push notifications
  1769. 1:21:14go through APNS and FCM.
  1770. 1:21:18APNS is Apple's push service. And FCM is
  1771. 1:21:23Google's version of the same thing. SMS
  1772. 1:21:26goes through an SMS gateway provider
  1773. 1:21:29like Twilio. and emails go through an
  1774. 1:21:32email provider like send grid. These
  1775. 1:21:35providers are external systems and not
  1776. 1:21:37part of your system and external systems
  1777. 1:21:41can go down. So we need to keep that in
  1778. 1:21:44mind. Also calling external systems
  1779. 1:21:47might take half a second might not be as
  1780. 1:21:50quick as you want to. So we need to keep
  1781. 1:21:53these facts in mind while designing the
  1782. 1:21:55system.
  1783. 1:21:57Step three is defining the API and APIs
  1784. 1:22:01in this is very simple. We just need one
  1785. 1:22:04API call and we can name it as notify.
  1786. 1:22:08When something happens inside the
  1787. 1:22:09product, the caller should call the
  1788. 1:22:11notify endpoint to give the user info.
  1789. 1:22:14The message that needs to be sent and
  1790. 1:22:17which channels we need to send it to.
  1791. 1:22:20This caller could be anything. a program
  1792. 1:22:22that takes new order or a program that
  1793. 1:22:25processes refund. The important part is
  1794. 1:22:28that the caller should not wait while we
  1795. 1:22:30send notification to the users. So the
  1796. 1:22:33caller does only one thing. It calls the
  1797. 1:22:36API and we record the notification in
  1798. 1:22:39our database with a unique notification
  1799. 1:22:42ID and notification system should
  1800. 1:22:45immediately reply accepted. The actual
  1801. 1:22:48sending of notification to the end user
  1802. 1:22:50happens later and this notification ID
  1803. 1:22:54is important to track all the
  1804. 1:22:56notifications that we are sending out to
  1805. 1:22:58users. Now we just said that we will
  1806. 1:23:01store the notification in our database.
  1807. 1:23:04So the next question is how exactly each
  1808. 1:23:07notification in database looks like. At
  1809. 1:23:10minimum we store the notification ID,
  1810. 1:23:13the user that we send this notification
  1811. 1:23:15to and list of channels where we send
  1812. 1:23:18the notification, the message body, a
  1813. 1:23:21status field that will track if
  1814. 1:23:23notification is sent or not. So this
  1815. 1:23:26field is by default filled with status
  1816. 1:23:28called pending because we haven't sent
  1817. 1:23:31the notification yet. And then we have a
  1818. 1:23:34timestamp field that stores the time
  1819. 1:23:36when this particular notification was
  1820. 1:23:38created in our system.
  1821. 1:23:41We can also keep one small flag that
  1822. 1:23:43tracks if the end user has actually seen
  1823. 1:23:46our notification.
  1824. 1:23:48Later you will see that this flag is
  1825. 1:23:49used to track the inapp notification.
  1826. 1:23:53And you might wonder why keep a scene
  1827. 1:23:55flag for inapp notifications only?
  1828. 1:23:59Because in app is the only channel where
  1829. 1:24:01we can actually track if user has seen
  1830. 1:24:03the notification.
  1831. 1:24:05When Allen opens our app, the app can
  1832. 1:24:07tell us that Allen opened and saw this
  1833. 1:24:09notification.
  1834. 1:24:11For SMS and email, we mostly have no
  1835. 1:24:15visibility after sending the message.
  1836. 1:24:17The phone network will never tell us if
  1837. 1:24:19Alan actually read that text or not.
  1838. 1:24:22Now the thing is that you might want to
  1839. 1:24:24store more fields but those extra fields
  1840. 1:24:27actually depends on your product. For
  1841. 1:24:29example, if you're running a shopping
  1842. 1:24:31website, then you might also store the
  1843. 1:24:34order ID. This is basically a table for
  1844. 1:24:37our notification system. Remember that
  1845. 1:24:39our system will also have other tables
  1846. 1:24:42like users table that stores all users
  1847. 1:24:45details. This users table stores each
  1848. 1:24:48user's name, user ID, contact details
  1849. 1:24:51like phone number and email address. We
  1850. 1:24:54also store the device token. This device
  1851. 1:24:57token is collected when a user installs
  1852. 1:24:59our app on their phone. And this device
  1853. 1:25:02token is actually used to send push
  1854. 1:25:04notifications.
  1855. 1:25:06And lastly, we also need to store the
  1856. 1:25:09user's notification preferences to know
  1857. 1:25:11if we can send marketing notifications
  1858. 1:25:13to this person or not.
  1859. 1:25:16Now a regular SQL database like
  1860. 1:25:18Postgress should be completely fine for
  1861. 1:25:21this design because every notification
  1862. 1:25:23is just one small row and even if we are
  1863. 1:25:26processing 10 million notifications a
  1864. 1:25:29day or around 100 small writes per
  1865. 1:25:31second it shouldn't be a problem for
  1866. 1:25:33Postgress.
  1867. 1:25:38Now we move to the next step which is
  1868. 1:25:40the highle design. Till now we know that
  1869. 1:25:44different applications in the system can
  1870. 1:25:46call our notification system by calling
  1871. 1:25:48notify endpoint. For example, order
  1872. 1:25:52service or refund service can call the
  1873. 1:25:54notify endpoint.
  1874. 1:25:56We should be absolutely clear that our
  1875. 1:25:59notification system is not any magical
  1876. 1:26:01box. It is just another application
  1877. 1:26:04program that runs on a normal server.
  1878. 1:26:08Now we store the incoming notification
  1879. 1:26:10requests in our Postgress database and
  1880. 1:26:13we initially set the status pending
  1881. 1:26:16because we haven't processed the
  1882. 1:26:17notification yet. So once we have the
  1883. 1:26:20notification details in database now we
  1884. 1:26:23need to see who actually does the
  1885. 1:26:25notification sending work. Let's start
  1886. 1:26:28with the simplest version and in this
  1887. 1:26:30version the notification server does the
  1888. 1:26:32sending work itself.
  1889. 1:26:34So right after saving the notification
  1890. 1:26:37in the database, the server calls the
  1891. 1:26:39Apple push notification service for the
  1892. 1:26:42push notification.
  1893. 1:26:44Then the server calls the SMS gateway
  1894. 1:26:47and calls the email provider and then we
  1895. 1:26:50wait for the confirmation by third-party
  1896. 1:26:53providers that message is sent.
  1897. 1:26:56Once all three external providers
  1898. 1:26:58confirm that message is sent, then we
  1899. 1:27:01update the status of notification in our
  1900. 1:27:03database from pending to sent.
  1901. 1:27:06This process can work on small scale but
  1902. 1:27:09not on large scale. Why? Because if you
  1903. 1:27:13notice our server is waiting from all
  1904. 1:27:15three providers for confirmation. So our
  1905. 1:27:18server is essentially waiting for 1 to 2
  1906. 1:27:20seconds. This wait time is huge. And if
  1907. 1:27:24a flood of 1 million notifications
  1908. 1:27:26arrive suddenly and each notification is
  1909. 1:27:28holding our application busy for more
  1910. 1:27:30than a second, then we won't be able to
  1911. 1:27:33process notifications on time and it
  1912. 1:27:35will be a chaos and it can get worse.
  1913. 1:27:39What happens if our notification server
  1914. 1:27:41crashes suddenly?
  1915. 1:27:43Let's say we were processing
  1916. 1:27:44notification number 2000 and our
  1917. 1:27:47notification server has already sent the
  1918. 1:27:49notification to email provider and SMS
  1919. 1:27:52provider but we haven't got the
  1920. 1:27:54confirmation from the providers yet and
  1921. 1:27:56the server crashes right now. In this
  1922. 1:27:59case we did only the sending part. We
  1923. 1:28:02don't know if we got response from SMS
  1924. 1:28:04or email provider. Basically we are not
  1925. 1:28:08sure what happened to notification
  1926. 1:28:09number 2000.
  1927. 1:28:12Now pause the video for a moment and
  1928. 1:28:14think how can you accept the
  1929. 1:28:16notification right now but send the
  1930. 1:28:19actual notification later without losing
  1931. 1:28:22anything even if a server crashes in
  1932. 1:28:25between.
  1933. 1:28:26So what we actually need is a component
  1934. 1:28:29which can do one simple job. This
  1935. 1:28:33component should be holding the pending
  1936. 1:28:35notifications safely until we send them.
  1937. 1:28:38and this component should keep holding
  1938. 1:28:40them even if a server crashes.
  1939. 1:28:43Now this can be easily done by a que.
  1940. 1:28:47Just to make things absolutely clear,
  1941. 1:28:50the database will still hold the full
  1942. 1:28:52notification record, the message body,
  1943. 1:28:55the channels, the status, the timestamp.
  1944. 1:28:58Database is our permanent register and
  1945. 1:29:01database will never go away. The Q
  1946. 1:29:04actually holds something much smaller. Q
  1947. 1:29:08will just hold a small ticket that
  1948. 1:29:10includes notification ID and a note
  1949. 1:29:13saying this notification is waiting to
  1950. 1:29:15be sent.
  1951. 1:29:17Now the final flow looks like this. The
  1952. 1:29:20notification server writes the full
  1953. 1:29:22record into the database and then drops
  1954. 1:29:25the small ticket into the queue. Now
  1955. 1:29:28what we do is we run separate servers
  1956. 1:29:31called workers. The role of worker is to
  1957. 1:29:34pick one ticket from the queue and then
  1958. 1:29:37get more information about the
  1959. 1:29:39notification from the database using the
  1960. 1:29:41notification ID.
  1961. 1:29:44Then worker sends the notification to
  1962. 1:29:46all the providers and once the worker
  1963. 1:29:49gets confirmation from these external
  1964. 1:29:51providers, it then acknowledges the
  1965. 1:29:54ticket and the ticket is removed from
  1966. 1:29:56the queue. And if a worker crashes in
  1967. 1:29:59the middle of the work, the Q notices
  1968. 1:30:02that the ticket was never acknowledged
  1969. 1:30:04in time. And the Q simply hands the same
  1970. 1:30:07ticket to another worker, so nothing
  1971. 1:30:10gets lost.
  1972. 1:30:12And you should remember that you can run
  1973. 1:30:14multiple workers in parallel to process
  1974. 1:30:17the notifications faster. And even if
  1975. 1:30:20one worker goes down, other workers can
  1976. 1:30:22still work on sending notifications. So
  1977. 1:30:25there is no single point of failure.
  1978. 1:30:28Let's make this concrete with one
  1979. 1:30:30example. The worker sees a notification
  1980. 1:30:33ticket in Q and sees that notification
  1981. 1:30:36ID is 35. Worker then checks the
  1982. 1:30:39database for this notification ID and
  1983. 1:30:42finds out that this notification needs
  1984. 1:30:44to send to Allen. Worker fetches Allen's
  1985. 1:30:48contact details from the users table
  1986. 1:30:50that we already saw. From this users
  1987. 1:30:53table, we get Allen's device token, his
  1988. 1:30:55phone number, and his email address.
  1989. 1:30:59Now, the worker makes one quick check.
  1990. 1:31:02The worker sees Alen's notification
  1991. 1:31:04preferences. And if Alan has switched
  1992. 1:31:06off marketing SMS, and if this
  1993. 1:31:08particular notification that we were
  1994. 1:31:10about to send is marketing related, then
  1995. 1:31:13the worker will drop this message and
  1996. 1:31:15remove it from Q.
  1997. 1:31:17Before we move on, I would like to
  1998. 1:31:19repeat that our notification server now
  1999. 1:31:22does two separate writes. It saves the
  2000. 1:31:25full notification record in the database
  2001. 1:31:27and it also drops the ticket into the
  2002. 1:31:30queue.
  2003. 1:31:33So when the interviewer asks what kind
  2004. 1:31:35of queue you want, you have to name
  2005. 1:31:38three properties. The Q must be durable.
  2006. 1:31:42That means the Q should store the
  2007. 1:31:44incoming tickets on disk, not in RAM.
  2008. 1:31:47Why? Because RAM is volatile and if Q
  2009. 1:31:51needs to restart due to some reason then
  2010. 1:31:54RAM will lose the data.
  2011. 1:31:57Second property of the queue we want is
  2012. 1:31:59that it must support acknowledgement.
  2013. 1:32:01A message is removed from the queue only
  2014. 1:32:04when a worker confirms the work is done.
  2015. 1:32:07And the Q must be able to work with
  2016. 1:32:09multiple workers. and we should be able
  2017. 1:32:12to add more workers when more
  2018. 1:32:14notifications pile up and queue grows
  2019. 1:32:16long.
  2020. 1:32:18One property we do not need is strict
  2021. 1:32:20ordering of messages within the queue.
  2022. 1:32:23Basically, we mean that if two unrelated
  2023. 1:32:25notifications swap places in the line,
  2024. 1:32:28then it doesn't matter because nothing
  2025. 1:32:30will break or go wrong if Allen's
  2026. 1:32:32notification gets delivered before Jon's
  2027. 1:32:35notification.
  2028. 1:32:37And in fact, dropping strict ordering
  2029. 1:32:39makes the queue much easier to scale
  2030. 1:32:42because you have less constraints.
  2031. 1:32:45If we see the overall design now, the
  2032. 1:32:48notification accepting side and the
  2033. 1:32:50sending side are now separated. And this
  2034. 1:32:53style has a name that you should
  2035. 1:32:55emphasize on in the interview. You
  2036. 1:32:57should say that we are doing
  2037. 1:32:59asynchronous processing of
  2038. 1:33:01notifications.
  2039. 1:33:02The caller program never waits for the
  2040. 1:33:05notification to be sent. It just
  2041. 1:33:07registers the notification to the
  2042. 1:33:09notification server. When a sudden flood
  2043. 1:33:12of notifications comes to our system, we
  2044. 1:33:14just store them and put them in the
  2045. 1:33:16queue and we simply add more worker
  2046. 1:33:18nodes to send notifications to end users
  2047. 1:33:21at faster rate.
  2048. 1:33:23Now the next logical question is should
  2049. 1:33:26all four channels share one single que?
  2050. 1:33:30The answer is no. We should have
  2051. 1:33:32separate Q for each channel.
  2052. 1:33:35Why is that? This is helpful in case
  2053. 1:33:38when one provider gets slow. For
  2054. 1:33:41example, if email provider becomes slow,
  2055. 1:33:44then every worker that picks up an email
  2056. 1:33:46notification gets stuck waiting on the
  2057. 1:33:49slow email provider. And soon most of
  2058. 1:33:52our workers are busy with slow email
  2059. 1:33:54provider while the push notification and
  2060. 1:33:57SMS messages sit in a growing queue even
  2061. 1:34:00when SMS provider is not slow.
  2062. 1:34:04The learning is that one slow channel
  2063. 1:34:06can keep worker nodes busy.
  2064. 1:34:09Therefore, we isolate the channels and
  2065. 1:34:11we deploy one Q for each channel. So, we
  2066. 1:34:15will have four Q's and each Q has its
  2067. 1:34:19own pool of workers. Now, each channel
  2068. 1:34:22can also scale independently and deploy
  2069. 1:34:25more workers on its own. If you see now
  2070. 1:34:29a slow email provider delays only the
  2071. 1:34:31email notifications and the other three
  2072. 1:34:34channels keep delivering the
  2073. 1:34:35notifications like nothing happened.
  2074. 1:34:38And notice that the inapp channel is the
  2075. 1:34:41easiest one here. Why? Because we are
  2076. 1:34:45not dependent on any external providers
  2077. 1:34:47for sending notification.
  2078. 1:34:49The notification row is already sitting
  2079. 1:34:51in our own database. So the worker just
  2080. 1:34:54marks the inapp part as sent. And notice
  2081. 1:34:58that the scene flag that we introduced
  2082. 1:34:59earlier is still false at this point.
  2083. 1:35:03Now when Alan opens the app on mobile,
  2084. 1:35:05the mobile app asks our server how many
  2085. 1:35:08unseen notifications Allen has. And then
  2086. 1:35:11we can check our database for scene
  2087. 1:35:13flag. If it is not seen, then we put
  2088. 1:35:16that notification in Allen's app. And
  2089. 1:35:19when Alan actually views the
  2090. 1:35:21notifications, the mobile app informs
  2091. 1:35:23our server and the notification server
  2092. 1:35:26changes the scene flag value to true. I
  2093. 1:35:29would like to introduce one more concept
  2094. 1:35:31here. The notification server publishes
  2095. 1:35:34one event and every channel Q gets its
  2096. 1:35:38own copy of the event and this copying
  2097. 1:35:40of one event into many cues is called
  2098. 1:35:43fan out. So let's see the summary of the
  2099. 1:35:46design we have till now. The notify API
  2100. 1:35:50records the notification in our database
  2101. 1:35:52with a unique notification ID and we
  2102. 1:35:55immediately reply accepted.
  2103. 1:35:58This notification then fans out into
  2104. 1:36:00four durable cues. Basically we are
  2105. 1:36:03maintaining one Q for each channel so
  2106. 1:36:06that one slow provider cannot block the
  2107. 1:36:09other channels.
  2108. 1:36:10Then workers pick messages from each
  2109. 1:36:12que. workers look up the user's contact
  2110. 1:36:15details, check the user's preferences,
  2111. 1:36:18and call the providers to send the
  2112. 1:36:20notification.
  2113. 1:36:22Now, the next big question is, what
  2114. 1:36:24should a worker do when the email
  2115. 1:36:26provider doesn't give any response and
  2116. 1:36:28this request times out?
  2117. 1:36:32Okay, we have four cues and our workers
  2118. 1:36:35are pulling notification tickets from
  2119. 1:36:37all four cues and calling the service
  2120. 1:36:39providers. And on a normal day,
  2121. 1:36:42everything works fine.
  2122. 1:36:44Now one day our email provider goes down
  2123. 1:36:46for 30 minutes and we are not getting
  2124. 1:36:49any response from the provider.
  2125. 1:36:51This is where we will discuss step five
  2126. 1:36:53of our framework. And step five is the
  2127. 1:36:56deep dive in design.
  2128. 1:36:59The first question is very simple. What
  2129. 1:37:02should a worker do when a provider call
  2130. 1:37:04fails?
  2131. 1:37:06The obvious answer is that the worker
  2132. 1:37:08should try again. Let's say in the
  2133. 1:37:10second attempt the provider call fails
  2134. 1:37:12again and the worker doesn't get any
  2135. 1:37:14response. Then the next question is how
  2136. 1:37:18many times we should retry.
  2137. 1:37:21If the email provider is already down
  2138. 1:37:23and then hundreds of our workers are
  2139. 1:37:25trying to contact the email provider
  2140. 1:37:26again and again then we are just
  2141. 1:37:29overloading somebody else's API.
  2142. 1:37:32Therefore we should always retry
  2143. 1:37:34politely.
  2144. 1:37:35Instead of retrying immediately, we wait
  2145. 1:37:381 second before the first retry. If it
  2146. 1:37:40fails, we wait for 2 seconds before the
  2147. 1:37:43second retry. And if that also fails,
  2148. 1:37:46then we wait for 4 seconds before the
  2149. 1:37:48third. Basically, we double the waiting
  2150. 1:37:51time before trying again so that we give
  2151. 1:37:54the struggling email provider some room
  2152. 1:37:56to breathe. And this technique is called
  2153. 1:37:58exponential backoff.
  2154. 1:38:01But of course, we won't be retrying
  2155. 1:38:02infinite times. Let's say after several
  2156. 1:38:05retries we do not get any response. Then
  2157. 1:38:09we do not throw the notification away.
  2158. 1:38:11Instead we move the notification to a
  2159. 1:38:14separate queue called a dead letter Q.
  2160. 1:38:18This dead letter Q is like a Q specially
  2161. 1:38:20for the failed messages so that a
  2162. 1:38:23support engineer can look at these
  2163. 1:38:24messages and take appropriate action.
  2164. 1:38:30But RQ has created a new problem for us.
  2165. 1:38:33And this problem is the real trap of
  2166. 1:38:35this interview. Let's walk through the
  2167. 1:38:38problem with one example.
  2168. 1:38:40Let's say a worker picks the ticket for
  2169. 1:38:42Allen's payment notification from the
  2170. 1:38:44queue, reads Allen's details from the
  2171. 1:38:46database, and calls the SMS provider.
  2172. 1:38:50And the SMS is sent to Allen's phone
  2173. 1:38:52number. And at that exact moment before
  2174. 1:38:55this worker could acknowledge the ticket
  2175. 1:38:57to the queue, the worker crashes.
  2176. 1:39:01Now look at this situation from the Q's
  2177. 1:39:03perspective. The Q only knows that a
  2178. 1:39:06ticket was picked up by this worker, but
  2179. 1:39:08this worker never acknowledged the queue
  2180. 1:39:10in time. The Q doesn't know whether the
  2181. 1:39:13SMS was actually sent or not. So the Q
  2182. 1:39:17assumes the safe thing. Given the Q
  2183. 1:39:20didn't get any acknowledgement, the Q
  2184. 1:39:22hands the same ticket to another worker
  2185. 1:39:24and Allen's phone buzzes twice with the
  2186. 1:39:27same payment message.
  2187. 1:39:29Now you can see the problem. We sent the
  2188. 1:39:32payment notification twice to Alen. And
  2189. 1:39:35if you think it through, we cannot fix
  2190. 1:39:37this problem by writing careful code
  2191. 1:39:40because the worker can always crash
  2192. 1:39:41after sending the message but before
  2193. 1:39:44acknowledging to the queue. Pause the
  2194. 1:39:47video here and think how can I prevent
  2195. 1:39:49one notification to be sent two times.
  2196. 1:39:52We have to think about this problem from
  2197. 1:39:54a different perspective.
  2198. 1:39:56First we accept the fact that the Q can
  2199. 1:39:59sometimes process the same ticket twice
  2200. 1:40:02which leads to duplicate sending. So we
  2201. 1:40:04let the duplication happen. But what we
  2202. 1:40:07can do is we can build a mechanism that
  2203. 1:40:09can identify duplication and drop the
  2204. 1:40:12duplicate instead of sending it.
  2205. 1:40:14Basically, we try to make the duplicate
  2206. 1:40:16harmless. So, how do we make a duplicate
  2207. 1:40:19harmless? Let me show you step by step.
  2208. 1:40:23If you remember, each notification has a
  2209. 1:40:26notification ID that we store in our
  2210. 1:40:28database.
  2211. 1:40:30Now, before calling any provider, the
  2212. 1:40:32worker first checks in one shared place
  2213. 1:40:35if this notification ID has already been
  2214. 1:40:38sent. If the answer is yes, the worker
  2215. 1:40:41silently drops the ticket because some
  2216. 1:40:44other worker has already handled this
  2217. 1:40:45notification and this ticket is just a
  2218. 1:40:48duplicate.
  2219. 1:40:49But if the answer is no, the worker
  2220. 1:40:52first writes a note in that shared place
  2221. 1:40:54saying this notification is taken and
  2222. 1:40:57only after writing the note, the worker
  2223. 1:40:59calls the provider.
  2224. 1:41:01And you need to pay attention to the
  2225. 1:41:03sequence here because the sequence is
  2226. 1:41:05the key here. the worker first adds a
  2227. 1:41:08note to the shared store and then sends
  2228. 1:41:11the notification
  2229. 1:41:13because if the worker crashes after
  2230. 1:41:15sending the notification, the note is
  2231. 1:41:18already there in the shared store. So
  2232. 1:41:21when the next worker picks this ticket
  2233. 1:41:23and checks the shared store, then the
  2234. 1:41:25next worker will find the note that this
  2235. 1:41:28notification was already processed.
  2236. 1:41:31Therefore, the worker will drop this
  2237. 1:41:34ticket.
  2238. 1:41:35There is one more scenario that we need
  2239. 1:41:37to think about. If the worker adds a
  2240. 1:41:40note to the shared store that this
  2241. 1:41:42notification was handled and then
  2242. 1:41:45crashes just before sending the
  2243. 1:41:47notification, then the notification was
  2244. 1:41:50not sent at all. But the note in the
  2245. 1:41:54shared store is already there. So any
  2246. 1:41:57future worker will drop this
  2247. 1:41:59notification and Allen never gets his
  2248. 1:42:02notification.
  2249. 1:42:04Well, this is why the note in the shared
  2250. 1:42:07store is not permanent. We write the
  2251. 1:42:09note with an expiry time and the note
  2252. 1:42:12deletes itself after a minute so that a
  2253. 1:42:15worker can pick it up after a minute if
  2254. 1:42:18it was not sent.
  2255. 1:42:20Let's take a look at what we changed
  2256. 1:42:22here. Earlier, the worst case was Allen
  2257. 1:42:26getting the same payment message twice,
  2258. 1:42:28but now the worst case is a notification
  2259. 1:42:31going out a little late. Now this little
  2260. 1:42:35note that we put in the shared store has
  2261. 1:42:37a name. The note is called an item
  2262. 1:42:40potency key. In our case, the item
  2263. 1:42:44potency key is the notification ID only.
  2264. 1:42:48I would like to take a moment to define
  2265. 1:42:50item potency here. Item potency means no
  2266. 1:42:54matter how many times you do a task, the
  2267. 1:42:57result is always the same. In our case,
  2268. 1:43:01no matter how many times a worker picks
  2269. 1:43:03up the same ticket, the notification is
  2270. 1:43:06sent only one time.
  2271. 1:43:09There are two small details that you
  2272. 1:43:11should note. First, if the provider call
  2273. 1:43:14fails and the worker doesn't get any
  2274. 1:43:17response from the provider, then the
  2275. 1:43:19worker removes the note from the shared
  2276. 1:43:22store so that we can try to send the
  2277. 1:43:24notification again.
  2278. 1:43:26And the second point is that once the
  2279. 1:43:28send succeeds the worker changes the
  2280. 1:43:31status in our database from pending to
  2281. 1:43:34sent.
  2282. 1:43:36Now we have said that we will add the
  2283. 1:43:38note to the shared store. But where does
  2284. 1:43:40this shared store live? Well, every
  2285. 1:43:44worker needs to check this shared store
  2286. 1:43:46before every single send. So it has to
  2287. 1:43:49be fast. And we already know one tool
  2288. 1:43:52that can do this and it's Reddus. Before
  2289. 1:43:59we move forward, let's quickly walk
  2290. 1:44:01through everything we have designed till
  2291. 1:44:03now. First, a notification request comes
  2292. 1:44:07in and we save the notification in our
  2293. 1:44:10database with a unique notification ID
  2294. 1:44:13and the status pending.
  2295. 1:44:16Then we drop a small ticket into the
  2296. 1:44:18relevant cues. Remember we have one Q
  2297. 1:44:22for each channel and we are using four
  2298. 1:44:25Q's so that one slow channel never
  2299. 1:44:28blocks the other channels.
  2300. 1:44:30Next a free worker picks the ticket from
  2301. 1:44:33the queue and before anything the worker
  2302. 1:44:36checks the shared store in Reddus to see
  2303. 1:44:39if this particular notification ID has
  2304. 1:44:42already been handled.
  2305. 1:44:44If the worker finds a note there, the
  2306. 1:44:46worker drops the ticket.
  2307. 1:44:49But if the worker finds no note, the
  2308. 1:44:52worker writes a note with an expiry time
  2309. 1:44:54and then calls the provider.
  2310. 1:44:57Now if the worker fails to call the
  2311. 1:45:00service provider, the worker retries
  2312. 1:45:03with exponential backoff.
  2313. 1:45:05And if the notification keeps failing
  2314. 1:45:08even after several attempts, the
  2315. 1:45:10notification is moved to the dead letter
  2316. 1:45:13Q where a support engineer can take a
  2317. 1:45:15look.
  2318. 1:45:17But if the worker is able to call the
  2319. 1:45:19provider and succeeds in sending the
  2320. 1:45:21notification, the worker changes the
  2321. 1:45:24status of the notification from pending
  2322. 1:45:27to sent in our database. And finally,
  2323. 1:45:31the worker acknowledges the ticket to
  2324. 1:45:32the Q so that the Q can remove this
  2325. 1:45:36ticket forever.
  2326. 1:45:38And if a worker crashes anywhere between
  2327. 1:45:40these steps, the queue will hand the
  2328. 1:45:43same ticket to another worker and the
  2329. 1:45:46note in the shared store make sure the
  2330. 1:45:49duplicate ticket is not sent again and
  2331. 1:45:51is dropped.
  2332. 1:45:54Also, we established that the note in
  2333. 1:45:56the shared store is actually an item
  2334. 1:45:59potency key and the item potency key
  2335. 1:46:02should be unique. So we can use the
  2336. 1:46:05notification ID as our item potency key
  2337. 1:46:09in our case.
  2338. 1:46:11Okay. Now I want to fix one more thing
  2339. 1:46:13in our design. If you notice our
  2340. 1:46:16notification server does two separate
  2341. 1:46:19rights. The first right is it saves the
  2342. 1:46:22full notification record in the database
  2343. 1:46:24and then the second right is it drops
  2344. 1:46:26the ticket into the queue. Again we are
  2345. 1:46:30doing two writes one to the database and
  2346. 1:46:33another to the queue. If the
  2347. 1:46:36notification server writes to the
  2348. 1:46:37database and then it goes to the queue
  2349. 1:46:40for writing but it crashes just before
  2350. 1:46:43writing to the queue then we are in a
  2351. 1:46:46situation where the notification is
  2352. 1:46:48saved in our database but it never
  2353. 1:46:50arrived in the queue. We can fix this
  2354. 1:46:54easily. At the end there should be only
  2355. 1:46:57one place which should be the source of
  2356. 1:46:59truth and in this case it will be the
  2357. 1:47:02database. So what we do is in the
  2358. 1:47:05beginning we only store the notification
  2359. 1:47:08in the database and then we run a small
  2360. 1:47:11background job that keeps looking at the
  2361. 1:47:14database to see which notifications are
  2362. 1:47:17still in the pending state and whichever
  2363. 1:47:20notifications are in the pending state.
  2364. 1:47:22This background job adds them to the
  2365. 1:47:24queue. Now the workers can pick the
  2366. 1:47:27tickets from the queue. If the
  2367. 1:47:30background job puts the same
  2368. 1:47:32notification twice into the queue, it's
  2369. 1:47:34not a problem because our design already
  2370. 1:47:37takes care of duplicate items.
  2371. 1:47:42Now the last step of our framework which
  2372. 1:47:45is bottlenecks and scaling and we want
  2373. 1:47:48to know where does our design break when
  2374. 1:47:51the number of notifications grows.
  2375. 1:47:54First of all, the Q is the heart of this
  2376. 1:47:57system. And in the interview, you can
  2377. 1:47:59name a real Q like Amazon SQS, which is
  2378. 1:48:03a natural fit in our case because SQS
  2379. 1:48:06gives us the acknowledgement behavior we
  2380. 1:48:09mentioned in our design and it also has
  2381. 1:48:12a built-in dead letter Q. Rabbit MQ is
  2382. 1:48:16another solid pick for Q's in our
  2383. 1:48:18system.
  2384. 1:48:19Second, if we get a notification flood
  2385. 1:48:22due to some event, then our cues keep
  2386. 1:48:25growing and their size doesn't come
  2387. 1:48:28down. If our cues are getting larger,
  2388. 1:48:31then it is a signal to add more workers
  2389. 1:48:34so that we can process more
  2390. 1:48:35notifications in less time.
  2391. 1:48:39And then there is that point that you
  2392. 1:48:41should mention to the interviewer
  2393. 1:48:42clearly that we never actually prevented
  2394. 1:48:45duplicates in our design. The queue
  2395. 1:48:48still processes the same ticket twice.
  2396. 1:48:51What we chose in our design is called at
  2397. 1:48:54least once delivery, which simply means
  2398. 1:48:57that when our system is not sure if the
  2399. 1:49:00notification is sent, the system sends
  2400. 1:49:02the notification again anyway because
  2401. 1:49:06sending twice is better than losing the
  2402. 1:49:08notification.
  2403. 1:49:10And then our item potency key catches
  2404. 1:49:13those extra sends at the very last step
  2405. 1:49:16right before the provider call. So Allen
  2406. 1:49:19almost never sees a duplicate.
  2407. 1:49:22So we should clearly mention that
  2408. 1:49:24exactly once delivery of notifications
  2409. 1:49:27in a distributed system is not possible.
  2410. 1:49:31You can only make the redely harmless.
  2411. 1:49:34So let me summarize the full
  2412. 1:49:36notification system once. In the same
  2413. 1:49:39way, you will close this in the real
  2414. 1:49:41interview.
  2415. 1:49:42The notify API records the notification
  2416. 1:49:46with a unique notification ID in the
  2417. 1:49:48database and immediately replies
  2418. 1:49:51accepted.
  2419. 1:49:52And a background job then puts a ticket
  2420. 1:49:55in the queue so that the notification
  2421. 1:49:58can be sent. We have one Q per channel
  2422. 1:50:01so that one slow provider cannot block
  2423. 1:50:04the other channels.
  2424. 1:50:06workers pick tickets from the cues and
  2425. 1:50:08check Reddus to see if we have already
  2426. 1:50:11sent this notification.
  2427. 1:50:13If we haven't sent it, then we check the
  2428. 1:50:15user's preferences to see if we can
  2429. 1:50:18actually send the notification. And if
  2430. 1:50:20yes, the worker calls the provider to
  2431. 1:50:23send the notification.
  2432. 1:50:25In case of failures, we retry sending
  2433. 1:50:28the notification with exponential
  2434. 1:50:30backoff, and whatever still fails goes
  2435. 1:50:33to a dead letter Q.
  2436. 1:50:35Before closing this topic, I also want
  2437. 1:50:38to mention that we could also solve this
  2438. 1:50:40problem using Kafka or a pub sub
  2439. 1:50:43architecture instead of Q's.
  2440. 1:50:46Conceptually, the idea remains the same
  2441. 1:50:48in both cases.
  2442. 1:50:50Now, you might be thinking how close
  2443. 1:50:52this design is to what real companies
  2444. 1:50:55actually run in production.
  2445. 1:50:57The design is actually very close. We
  2446. 1:51:01can take the example of LinkedIn. Every
  2447. 1:51:04team at LinkedIn used to send
  2448. 1:51:06notifications on its own and users were
  2449. 1:51:08getting multiple emails. So LinkedIn
  2450. 1:51:11built one central platform called air
  2451. 1:51:14traffic controller which is a single
  2452. 1:51:16gateway that every notification request
  2453. 1:51:19must pass through. So all teams send
  2454. 1:51:21their notifications to this traffic
  2455. 1:51:24controller. The requests flow in through
  2456. 1:51:26Kafka and the results were actually very
  2457. 1:51:29good. LinkedIn now sends 50% fewer
  2458. 1:51:33emails and complaints dropped by more
  2459. 1:51:36than 65%.
  2460. 1:51:38Uber uses a similar approach for its
  2461. 1:51:40push notifications with one extra
  2462. 1:51:43addition. And the addition is that every
  2463. 1:51:46message falls under different priority
  2464. 1:51:48buckets. For example, a trip update has
  2465. 1:51:52a higher priority than a promotional
  2466. 1:51:54offer and notifications with higher
  2467. 1:51:56priority are sent first and then lower
  2468. 1:52:00priority notifications are sent.
  2469. 1:52:02So this is how real companies use the
  2470. 1:52:05notification system.
  2471. 1:52:07Next, we will see some follow-up
  2472. 1:52:09questions asked by interviewers.
  2473. 1:52:14Towards the end of the interview, you
  2474. 1:52:16can expect some follow-up questions from
  2475. 1:52:18the interviewer. And these follow-up
  2476. 1:52:20questions test if you can point out what
  2477. 1:52:23exactly you will change in your design.
  2478. 1:52:27Let's quickly see three follow-up
  2479. 1:52:29questions.
  2480. 1:52:30Question number one is that a marketing
  2481. 1:52:33campaign just dropped 1 million
  2482. 1:52:35notifications into the cues. When Allan
  2483. 1:52:38triggers the login code, his
  2484. 1:52:40notification is sitting behind all the
  2485. 1:52:43marketing notifications in the queue.
  2486. 1:52:46What do you change? And to be honest,
  2487. 1:52:49you should see this coming as it's a
  2488. 1:52:51valid question. Remember that in our
  2489. 1:52:53clarifying questions, we learned that
  2490. 1:52:56transactional and marketing
  2491. 1:52:57notifications have completely different
  2492. 1:52:59patience levels. A login code should be
  2493. 1:53:03sent as soon as possible within a few
  2494. 1:53:05seconds. So we can fix this easily and
  2495. 1:53:09we do that by assigning separate cues to
  2496. 1:53:12marketing and transactional
  2497. 1:53:13notifications.
  2498. 1:53:15Inside each channel, we keep two cues
  2499. 1:53:18instead of one, a transactional queue
  2500. 1:53:21and a marketing queue. And we give the
  2501. 1:53:24transactional queue its own dedicated
  2502. 1:53:27workers that the marketing can never
  2503. 1:53:29borrow. And this is not our invention.
  2504. 1:53:32Remember, Uber's priority buckets are
  2505. 1:53:35already doing this. Question number two
  2506. 1:53:38is that the email provider goes down for
  2507. 1:53:401 hour. Walk me through what your system
  2508. 1:53:44does for 1 hour. In this case, the first
  2509. 1:53:48thing you should mention is that nothing
  2510. 1:53:50gets lost from the system. The queue is
  2511. 1:53:53durable, which means for 1 hour, the
  2512. 1:53:56email notifications accumulate safely in
  2513. 1:53:59the queue, but our workers still keep
  2514. 1:54:03retrying to send the notification.
  2515. 1:54:06If the provider is dead for an hour,
  2516. 1:54:08hundreds of workers are still running
  2517. 1:54:11with no outcome. So in this case, we
  2518. 1:54:14should add a circuit breaker. After some
  2519. 1:54:17number of consecutive failures, the
  2520. 1:54:19workers completely stop calling the
  2521. 1:54:21email provider. And every minute or so,
  2522. 1:54:24a single probe call checks whether the
  2523. 1:54:27provider has come back online.
  2524. 1:54:30Next thing you should know is that big
  2525. 1:54:32companies do not just rely on a single
  2526. 1:54:35email provider. They keep a second email
  2527. 1:54:38provider on standby or they use two
  2528. 1:54:41providers together so that they have a
  2529. 1:54:43backup option in case one provider
  2530. 1:54:46fails. And you should mention one more
  2531. 1:54:49thing to impress the interviewer. That a
  2532. 1:54:51login code is only valid for about a
  2533. 1:54:54minute. So delivering it 60 minutes late
  2534. 1:54:57doesn't make any sense.
  2535. 1:54:59So every notification can actually carry
  2536. 1:55:02an expiry time. And when a worker
  2537. 1:55:05finally picks up an expired message, the
  2538. 1:55:08worker drops the message. Let's see the
  2539. 1:55:11last question from the interviewer. You
  2540. 1:55:13keep saying exactly once delivery is
  2541. 1:55:16impossible. Show me exactly in which
  2542. 1:55:19case you will still send the
  2543. 1:55:21notification twice and your item potency
  2544. 1:55:24key will fail. Actually, this is a great
  2545. 1:55:27question. So let's show the exact
  2546. 1:55:30scenario.
  2547. 1:55:31A worker picks a ticket and checks
  2548. 1:55:33Reddus and the worker finds out that
  2549. 1:55:36this notification ID is not sent. The
  2550. 1:55:40worker writes the note to Reddus that
  2551. 1:55:42this notification is picked up and then
  2552. 1:55:45calls the SMS provider. The worker now
  2553. 1:55:48doesn't get the response back from the
  2554. 1:55:50SMS provider and hits a timeout. Now
  2555. 1:55:54think it through and answer. Did the SMS
  2556. 1:55:57go to the user or not? The answer is
  2557. 1:56:01that we cannot know with 100% certainty.
  2558. 1:56:05The provider might have sent the message
  2559. 1:56:07but got late in replying to our worker.
  2560. 1:56:10If we retry, Allen might get the SMS
  2561. 1:56:14twice. And if we do not retry, Allen
  2562. 1:56:17might get nothing.
  2563. 1:56:19And we can't do anything in this case
  2564. 1:56:22because our system does not know if the
  2565. 1:56:25SMS provider sent the message or not. In
  2566. 1:56:29the real world, many SMS or email
  2567. 1:56:31providers actually accept an item
  2568. 1:56:34potency key from our system. So what we
  2569. 1:56:37do is we send the item potency key along
  2570. 1:56:40with the SMS request and the provider
  2571. 1:56:43then checks if they have already sent
  2572. 1:56:46this notification.
  2573. 1:56:48In our case, our item potency key is our
  2574. 1:56:51notification ID because it is unique for
  2575. 1:56:54each notification.
  2576. 1:56:56Now, if they see the same notification
  2577. 1:56:58ID again, then they will not send the
  2578. 1:57:01notification and drop the request.
  2579. 1:57:04But then the phone networks like AT&T,
  2580. 1:57:07Verizon, Airtel themselves can send a
  2581. 1:57:10duplicate SMS on a bad day. So, we have
  2582. 1:57:14placed a lot of mechanisms to avoid
  2583. 1:57:16duplicates. But there are edge cases
  2584. 1:57:19that can always happen. And that is
  2585. 1:57:21exactly why our requirement said the
  2586. 1:57:24user should ideally never see the same
  2587. 1:57:27notification twice. This is it for the
  2588. 1:57:30notification system. Take a second to
  2589. 1:57:32process all the questions and answers we
  2590. 1:57:34discussed and then you can move on to
  2591. 1:57:37the next problem.
  2592. 1:57:40It's time for one more lab. In this lab,
  2593. 1:57:42you'll run the notification system where
  2594. 1:57:44one event will fan out to a push worker
  2595. 1:57:47and an email worker through separate
  2596. 1:57:49cues. You will also see how duplicate
  2597. 1:57:51notifications can be delivered and how
  2598. 1:57:53undelivered notifications go to the dead
  2599. 1:57:55letter Q. Go give this lab a shot.
  2600. 1:58:00Let's now take a look at our next
  2601. 1:58:01interview question. The interviewer says
  2602. 1:58:04that you need to design a news feed. So,
  2603. 1:58:07first let's be absolutely clear about
  2604. 1:58:09what a newsfeed actually is.
  2605. 1:58:11Now when you hear the term newsfeed, you
  2606. 1:58:14might be thinking that we want to make a
  2607. 1:58:16news app which will show the latest
  2608. 1:58:18news. But notice that he didn't say news
  2609. 1:58:22app. He said news feed. So newsfeed is a
  2610. 1:58:27general term which we use for any social
  2611. 1:58:30media feed. For example, when you open
  2612. 1:58:34your LinkedIn, the first screen that you
  2613. 1:58:37see is the series of items posted by
  2614. 1:58:40people you follow. This is the news feed
  2615. 1:58:44of LinkedIn.
  2616. 1:58:46Similarly, if you open X, the first
  2617. 1:58:48screen you see is the tweets posted by
  2618. 1:58:52people you follow. It is the newsfeed of
  2619. 1:58:55X. So newsfeed is a general term for the
  2620. 1:59:00first page where you have an endless
  2621. 1:59:02number of posts which you can scroll.
  2622. 1:59:05Now this feed is just a sorted list of
  2623. 1:59:08posts and we need to build a system that
  2624. 1:59:11can efficiently build this list.
  2625. 1:59:15So now let's apply our six-step
  2626. 1:59:17framework on this problem.
  2627. 1:59:20Step one is to clarify the requirements
  2628. 1:59:22and remember that our aim is to get
  2629. 1:59:25functional and non-functional
  2630. 1:59:26requirements of this system by asking
  2631. 1:59:29minimum number of questions. First
  2632. 1:59:32question is what exactly goes into the
  2633. 1:59:34feed and posts appear in what order?
  2634. 1:59:38Let's say the interviewer says that the
  2635. 1:59:40feed should only have posts from the
  2636. 1:59:43accounts you follow and you need to show
  2637. 1:59:46the newest post first.
  2638. 1:59:49And this answer actually simplified
  2639. 1:59:51things for us because we just have to
  2640. 1:59:53sort the posts by their timestamps and
  2641. 1:59:56we can make the feed. The interviewer
  2642. 1:59:59could have also asked us to build a
  2643. 2:00:01ranking algorithm and a ranking
  2644. 2:00:04algorithm is a separate problem in
  2645. 2:00:06itself because you have to look at
  2646. 2:00:08multiple things. For example, you need
  2647. 2:00:11to consider when this post was created,
  2648. 2:00:14who created this. Did anyone in your
  2649. 2:00:17connections like this? So, the ranking
  2650. 2:00:20algorithm needs us to build a whole
  2651. 2:00:23scoring mechanism so that we can score
  2652. 2:00:25the posts and we can show the highest
  2653. 2:00:28scoring post first.
  2654. 2:00:31So, in our case, the interviewer has
  2655. 2:00:33said we just store the posts and sort
  2656. 2:00:36them by time.
  2657. 2:00:38My second question will be when a user
  2658. 2:00:41creates a new post, how quickly should
  2659. 2:00:44this post show up in the followers feed?
  2660. 2:00:47Let's say the interviewer says a few
  2661. 2:00:49seconds is fine, but it shouldn't take
  2662. 2:00:52minutes.
  2663. 2:00:53Then my third question will be what is
  2664. 2:00:55the target for feed load latency? How
  2665. 2:00:59quickly do we want the feed to load? The
  2666. 2:01:02interviewer says the feed should load
  2667. 2:01:05within 1 second. And my fourth question
  2668. 2:01:08will be to know if the feed is text only
  2669. 2:01:12or do we need to show images and videos
  2670. 2:01:15in the feed. The interviewer says that
  2671. 2:01:18images and videos are both supported in
  2672. 2:01:21the newsfeed along with text. And the
  2673. 2:01:25fifth question is what if the same post
  2674. 2:01:27appears again in the user's feed after a
  2675. 2:01:31refresh?
  2676. 2:01:32The interviewer says the same post
  2677. 2:01:34appearing twice is not a big problem.
  2678. 2:01:38So before moving on to next stage of
  2679. 2:01:40interview, we state the scope of this
  2680. 2:01:43problem very clearly.
  2681. 2:01:46So you should clearly mention that users
  2682. 2:01:48can follow other accounts. Users can
  2683. 2:01:51create posts with text, images, or
  2684. 2:01:54videos. And users can read a feed. And
  2685. 2:01:59in the feed, we show the newest posts
  2686. 2:02:02from the accounts they follow. When
  2687. 2:02:05users open the app, the feed must load
  2688. 2:02:08within 1 second. A new post can take a
  2689. 2:02:12few seconds to reach the followers
  2690. 2:02:14feeds. And when a user refreshes their
  2691. 2:02:17feed, an occasional duplicate post on
  2692. 2:02:20the feed is acceptable.
  2693. 2:02:23The second step is back of the envelope
  2694. 2:02:25estimation. And the first thing I will
  2695. 2:02:28do here is ask the interviewer for the
  2696. 2:02:30scale. So my question will be how many
  2697. 2:02:34users are we serving and how often do
  2698. 2:02:37they open the feed? Let's say the
  2699. 2:02:40interviewer says that we have 10 million
  2700. 2:02:43daily active users.
  2701. 2:02:45Now, we know that on platforms like
  2702. 2:02:47LinkedIn or X, we read more than what we
  2703. 2:02:51post. So, let's say every user opens
  2704. 2:02:54their feed five times a day. And we know
  2705. 2:02:58that we have 10 million active users.
  2706. 2:03:01That means we have to serve 50 million
  2707. 2:03:05feed reads per day, which is roughly 600
  2708. 2:03:09feed reads every second on average. And
  2709. 2:03:13users post very rarely. There are 10
  2710. 2:03:16million active users and each posts
  2711. 2:03:19three times a month. That means 30
  2712. 2:03:23million posts in a month or we can say
  2713. 2:03:2730 million posts in 30 days. The final
  2714. 2:03:31number comes out to be around 1 million
  2715. 2:03:34posts per day. Now a day has about
  2716. 2:03:3886,000 seconds.
  2717. 2:03:40That means on an average we write 12
  2718. 2:03:43posts per second.
  2719. 2:03:46Now if we compare the number of scrolls
  2720. 2:03:48per second and posts per second the
  2721. 2:03:51ratio comes out to be 50 is to one. For
  2722. 2:03:56every one post that gets written on our
  2723. 2:03:58platform there are around 50 feed reads
  2724. 2:04:01happening. Basically this is a read
  2725. 2:04:04heavy system and this is the most
  2726. 2:04:07important fact in this problem.
  2727. 2:04:10Because we know that our app is read
  2728. 2:04:12heavy, we should focus more on the feed
  2729. 2:04:15reading path.
  2730. 2:04:18Step three is to define the API. One
  2731. 2:04:22thing should be very clear in your mind.
  2732. 2:04:24You don't have to do anything to make
  2733. 2:04:27API calls. If the user is using a mobile
  2734. 2:04:30application, the mobile application will
  2735. 2:04:33make these API calls on the user's
  2736. 2:04:36behalf.
  2737. 2:04:37Now a real newsfeed app like LinkedIn
  2738. 2:04:40will have multiple API calls available
  2739. 2:04:43but for our case we can focus on two
  2740. 2:04:45work items. The first is to create a new
  2741. 2:04:49post. When a user named Alen wants to
  2742. 2:04:52create a new post Allen's phone will
  2743. 2:04:55call the create post endpoint. And if
  2744. 2:04:59Alen opens the app, the mobile app on
  2745. 2:05:02Alen's phone should call the get feed
  2746. 2:05:04endpoint. so that the mobile app can
  2747. 2:05:07receive the feed which Allen can see.
  2748. 2:05:11When Allan calls the create post
  2749. 2:05:13endpoint, his phone will also send the
  2750. 2:05:16content of the new post so that our
  2751. 2:05:19server can save this post. And when Alan
  2752. 2:05:22calls the get feed endpoint, our server
  2753. 2:05:25should return the top 50 posts from all
  2754. 2:05:28the accounts that Alan follows. Now
  2755. 2:05:31obviously there will be other API calls
  2756. 2:05:34for likes, comments and follow but for
  2757. 2:05:37now we will not discuss them. Now we
  2758. 2:05:40just said we will store posts and we
  2759. 2:05:43will show those posts to our users. So
  2760. 2:05:45the next question is what exactly do we
  2761. 2:05:48store in our database?
  2762. 2:05:51We need three tables for this problem.
  2763. 2:05:54First is the users table. In this table,
  2764. 2:05:58we store the user information like the
  2765. 2:06:00user ID, the name of the user, and the
  2766. 2:06:03profile details. And interestingly, we
  2767. 2:06:07also keep one small flag in this table
  2768. 2:06:10that checks if this account is a
  2769. 2:06:12celebrity account. Right now, you
  2770. 2:06:15shouldn't worry about this flag, but
  2771. 2:06:17later you will see the importance of
  2772. 2:06:19this flag. The second table we store is
  2773. 2:06:23the follows table. And this table is
  2774. 2:06:25very simple. Every row is just two
  2775. 2:06:29values. The follower and the person
  2776. 2:06:31being followed. For example, Alan
  2777. 2:06:35follows John. This is one row. And this
  2778. 2:06:39table grows very fast because we have 10
  2779. 2:06:42million users. And if each user on an
  2780. 2:06:45average follows 200 users, it will
  2781. 2:06:48result in 2 billion rows. Now, here is
  2782. 2:06:52the question. How will we read this
  2783. 2:06:55table? One of the scenarios is when we
  2784. 2:06:58want to know which accounts Alen is
  2785. 2:07:00following. This is required when we
  2786. 2:07:03build Alen's feed because we want to
  2787. 2:07:06know which accounts are followed by
  2788. 2:07:08Allen so that we show posts only from
  2789. 2:07:11those accounts in Alen's feed. And there
  2790. 2:07:15is a second direction too. Who follows
  2791. 2:07:17John? Right now this direction looks
  2792. 2:07:21useless but later you will see that this
  2793. 2:07:24direction becomes the most important
  2794. 2:07:26query in the whole system. And the third
  2795. 2:07:29table that we will store is the posts
  2796. 2:07:31table. In this table we store all the
  2797. 2:07:34posts. Each row in this table includes
  2798. 2:07:38the post ID, the author of the post, the
  2799. 2:07:42actual content of the post and the
  2800. 2:07:45timestamp.
  2801. 2:07:46Also we should organize this table by
  2802. 2:07:49author and by time so that we can answer
  2803. 2:07:53queries like give me John's latest posts
  2804. 2:07:56very easily. Basically what we are
  2805. 2:07:59trying to do is we are trying to see
  2806. 2:08:01what the access pattern will be or how
  2807. 2:08:04we will fetch the information when we
  2808. 2:08:07serve end users and based on that we are
  2809. 2:08:10trying to organize our tables.
  2810. 2:08:15Now we move to the next step where we do
  2811. 2:08:17the highle design of the system. Until
  2812. 2:08:20now we know that the user's mobile app
  2813. 2:08:23talks to our servers with two API calls
  2814. 2:08:26and our data is sitting in three tables
  2815. 2:08:29in the database.
  2816. 2:08:31So the next question is that when Alan
  2817. 2:08:33opens the app, how do we actually build
  2818. 2:08:36his feed?
  2819. 2:08:38Let's start with the very simplest
  2820. 2:08:40version. And in this version, we do all
  2821. 2:08:43the work when Allen opens the app. Let
  2822. 2:08:47me walk you through one complete request
  2823. 2:08:49by Alan so you can see exactly how
  2824. 2:08:51things travel inside the system.
  2825. 2:08:55Alan opens the app and the app calls the
  2826. 2:08:58get feed endpoint. Our server first goes
  2827. 2:09:01to the follows table and asks who does
  2828. 2:09:04Alan follow? The query runs and the
  2829. 2:09:07answer comes back and the answer has 300
  2830. 2:09:11accounts that Allen follows.
  2831. 2:09:14Then the server goes to the posts table
  2832. 2:09:16and asks, "Give me the recent posts of
  2833. 2:09:19these 300 accounts."
  2834. 2:09:22The query runs for all 300 accounts that
  2835. 2:09:25Allen follows. And once the query
  2836. 2:09:27finishes, the server now has a few
  2837. 2:09:30hundred posts sitting in its memory.
  2838. 2:09:34The server sorts them by time and then
  2839. 2:09:36keeps the newest 50 and returns these 50
  2840. 2:09:40posts to Alen's app. Alan now sees his
  2841. 2:09:44feed.
  2842. 2:09:45To be honest, this design works, but
  2843. 2:09:48let's see some problems with this
  2844. 2:09:50design.
  2845. 2:09:52Now, let's see how this design behaves
  2846. 2:09:54when the number of users increases.
  2847. 2:09:57Whenever a user is trying to access his
  2848. 2:09:59feed, it triggers one read operation on
  2849. 2:10:03the follows table to find out which
  2850. 2:10:05accounts this user follows.
  2851. 2:10:08Once we have the accounts that this user
  2852. 2:10:10follows, then we trigger 300 separate
  2853. 2:10:13lookups to see what these 300 accounts
  2854. 2:10:16have posted.
  2855. 2:10:18Then we merge the posts that we get from
  2856. 2:10:20these 300 accounts and sort them by
  2857. 2:10:23time. Remember that we receive 600 feed
  2858. 2:10:27opens per second. So for 600 feed opens
  2859. 2:10:31each second, we have to read the follows
  2860. 2:10:34table 600 times. And we have to do
  2861. 2:10:39180,000 database lookups every second
  2862. 2:10:42just for building feeds.
  2863. 2:10:45In the evening time, we will probably
  2864. 2:10:47get peak traffic. If we assume peak
  2865. 2:10:50traffic to be five times of normal
  2866. 2:10:52traffic, then multiply this number by
  2867. 2:10:55five.
  2868. 2:10:56This will make our database extremely
  2869. 2:10:58slow and users will have to wait a while
  2870. 2:11:01to get the feed on their screen. And
  2871. 2:11:04remember that we promise the feed loads
  2872. 2:11:06within 1 second.
  2873. 2:11:09If you look carefully, most of this work
  2874. 2:11:11is wasted work. Why? Let's understand
  2875. 2:11:15this with an example.
  2876. 2:11:18Alan opens his feed in the morning and
  2877. 2:11:20then he goes through some posts and then
  2878. 2:11:23closes the app. But after a few seconds,
  2879. 2:11:26he opens the app again.
  2880. 2:11:29Because he opened the app very soon, new
  2881. 2:11:32posts might not have arrived from the
  2882. 2:11:34accounts he follows. Or maybe two or
  2883. 2:11:37three new posts arrived.
  2884. 2:11:40But we have to rebuild Allen's entire
  2885. 2:11:42feed from scratch again just to find out
  2886. 2:11:45that almost nothing has changed in
  2887. 2:11:47Allen's feed.
  2888. 2:11:49So can we do something with our design
  2889. 2:11:51to fix this?
  2890. 2:11:53The instinct might be to use read
  2891. 2:11:55replicas of the database. But even then
  2892. 2:11:59we are using more machines doing the
  2893. 2:12:01same wasteful query. Now pause the video
  2894. 2:12:04for a moment and think. Every feed is
  2895. 2:12:08rebuilding from scratch.
  2896. 2:12:10Is there a way where we don't have to
  2897. 2:12:12rebuild from scratch?
  2898. 2:12:14The answer is very logical. We shouldn't
  2899. 2:12:18build the feed when Allen reads it.
  2900. 2:12:21Therefore, we should actually have the
  2901. 2:12:23feed built before Allen accesses it and
  2902. 2:12:27we store this feed somewhere.
  2903. 2:12:29And then when somebody posts something
  2904. 2:12:31new, let's say John posts a new thing,
  2905. 2:12:35then we will go to Allen's feed and we
  2906. 2:12:38will just add J's post. Basically, we
  2907. 2:12:42change Allen's feed only when someone
  2908. 2:12:44posts a new thing. Otherwise, we won't
  2909. 2:12:48change Alen's feed. Let's see how we
  2910. 2:12:51will implement this.
  2911. 2:12:53Okay, let's now see how we build the
  2912. 2:12:56feeds for users before they open the
  2913. 2:12:58app. And before we build anything, one
  2914. 2:13:02thing should be absolutely clear in your
  2915. 2:13:04mind. The three tables we had in our
  2916. 2:13:08database are not going anywhere. The
  2917. 2:13:11users table, the posts table and the
  2918. 2:13:14follows table stay exactly as they are
  2919. 2:13:17and they hold the source of truth. Now
  2920. 2:13:21if we are premputing a feed for every
  2921. 2:13:24user the first question is where these
  2922. 2:13:27readymade feeds should live. Now if you
  2923. 2:13:30remember the requirement is that the
  2924. 2:13:33feed should load within 1 second. So the
  2925. 2:13:36readymade feeds should live somewhere
  2926. 2:13:38fast and in memory and that's why we
  2927. 2:13:41will store our feeds in Reddus.
  2928. 2:13:44For example, there is a list for John
  2929. 2:13:47with the posts we need to show to John.
  2930. 2:13:49And we have a list for Alan with the
  2931. 2:13:51posts we need to show to Alan. Because
  2932. 2:13:54we have 10 million users, we will have
  2933. 2:13:57to create 10 million small lists sitting
  2934. 2:14:00in our Reddis cache. And now when Alan
  2935. 2:14:03opens the app, the request comes to the
  2936. 2:14:05server. The server checks the cache for
  2937. 2:14:08the list. And Alen's list is already
  2938. 2:14:11there. The feed was prepared before Alan
  2939. 2:14:14ever asked for it. The next natural
  2940. 2:14:17question is who prepares these lists. So
  2941. 2:14:21let me walk you through one complete
  2942. 2:14:23journey. Let's say John writes good
  2943. 2:14:25morning and posts it to our platform.
  2944. 2:14:29The create post endpoint will save this
  2945. 2:14:31post in the posts table and the post
  2946. 2:14:34will get some ID. Let's say the ID is
  2947. 2:14:3771.
  2948. 2:14:39Now we have to show this post to every
  2949. 2:14:41user who follows John. To do this, we
  2950. 2:14:45keep a background job whose whole task
  2951. 2:14:47is to take each new post and deliver the
  2952. 2:14:50post to the right lists that are there
  2953. 2:14:53in cache.
  2954. 2:14:55This background job checks the follows
  2955. 2:14:57table and sees who follows John. The
  2956. 2:15:01answer comes back 800 followers and Alan
  2957. 2:15:04is one of them. So the background job
  2958. 2:15:07goes to Alen's list that is stored in
  2959. 2:15:09the cache and adds post 71 at the top
  2960. 2:15:14along with Alen. The background job goes
  2961. 2:15:16to the other 799 followers and adds this
  2962. 2:15:20post to the lists of those people. Now
  2963. 2:15:23when Alan opens his feed, John's post is
  2964. 2:15:27already there for Alan to see. Notice
  2965. 2:15:29that we did not rebuild Alen's whole
  2966. 2:15:32feed. We took one new post and slipped
  2967. 2:15:35it into Alen's list. Before we go
  2968. 2:15:38further, there is one thing you should
  2969. 2:15:40be clear about. What does each list in
  2970. 2:15:42the cache actually hold?
  2971. 2:15:45Each list in the cache only holds post
  2972. 2:15:48IDs that we need to show to the user.
  2973. 2:15:51For example, Allen's list has post ID
  2974. 2:15:5571, 68, 64, and so on. The newest post
  2975. 2:16:00is sitting at the top.
  2976. 2:16:02Basically, we are not storing the actual
  2977. 2:16:05post content here. So when Alan opens
  2978. 2:16:08the app, we still have to fetch the
  2979. 2:16:10actual post content for these ids. And
  2980. 2:16:14if we fetch these posts from the
  2981. 2:16:15database every time, we are again
  2982. 2:16:18sending a lot of queries to the database
  2983. 2:16:21and we will be putting unnecessary
  2984. 2:16:23pressure on the database because the
  2985. 2:16:25same fresh post from John appears in the
  2986. 2:16:28feeds of hundreds of people. So it
  2987. 2:16:31doesn't make sense to fetch the same
  2988. 2:16:33post content again and again from the
  2989. 2:16:35database.
  2990. 2:16:36So what we will do is deploy a second
  2991. 2:16:39cache whose only job is to hold the
  2992. 2:16:42actual content of recent posts. I am
  2993. 2:16:45naming this as the post cache. Now let's
  2994. 2:16:48revisit how our system looks. Now when
  2995. 2:16:51Allan opens the app, we read Allen's
  2996. 2:16:54list from the first cache and based on
  2997. 2:16:57the post ids in this list, we fetch the
  2998. 2:17:00actual content from the second cache or
  2999. 2:17:02the post cache. If some post is not
  3000. 2:17:06found in the post cache, then we go to
  3001. 2:17:08the database and fetch the post from the
  3002. 2:17:11database. And whenever we fetch a post
  3003. 2:17:14from the database like this, we also
  3004. 2:17:16update the post cache with this content
  3005. 2:17:19so that next time we don't have to go to
  3006. 2:17:21the database for the same post.
  3007. 2:17:24Overall, we are now running two caches
  3008. 2:17:27with two different jobs. The first cache
  3009. 2:17:31says which posts to show and the post
  3010. 2:17:34cache holds the posts themselves.
  3011. 2:17:37Remember that we promised the feed loads
  3012. 2:17:39within 1 second. And now our system is
  3013. 2:17:42indeed fast due to the use of two
  3014. 2:17:45caches.
  3015. 2:17:50Now let's zoom into the background job
  3016. 2:17:52because in an interview if you say a
  3017. 2:17:55background job handles the new post then
  3018. 2:17:57the interviewer may ask how you would
  3019. 2:17:59implement this background job. So let me
  3020. 2:18:02show you what happens when John adds a
  3021. 2:18:04new post.
  3022. 2:18:06When John clicks on the send button on
  3023. 2:18:08his phone, his phone calls the create
  3024. 2:18:10post endpoint on our servers.
  3025. 2:18:13Our server opens a database transaction.
  3026. 2:18:16And within this transaction, we do two
  3027. 2:18:18things. First, we add the new post
  3028. 2:18:21content into the posts table. And
  3029. 2:18:24second, we add one small note into a
  3030. 2:18:27second table called the outbox table.
  3031. 2:18:31This note simply says that post with ID
  3032. 2:18:3471 needs to be delivered and the note
  3033. 2:18:37carries a pending flag.
  3034. 2:18:40You should carefully notice that we have
  3035. 2:18:41said in one transaction we do both the
  3036. 2:18:44things. That means either both the
  3037. 2:18:47things happen or nothing happens at all.
  3038. 2:18:51Now a background worker keeps checking
  3039. 2:18:53this outbox table every few seconds and
  3040. 2:18:56it picks the jobs that are still marked
  3041. 2:18:58as pending and drops a ticket into the
  3042. 2:19:00queue.
  3043. 2:19:02Once we have dropped the ticket in the
  3044. 2:19:03queue, we change the flag in the outbox
  3045. 2:19:06table from pending to done.
  3046. 2:19:09The ticket that we have posted in the
  3047. 2:19:11queue simply says post with ID71
  3048. 2:19:15posted by John is new.
  3049. 2:19:18Once we have the ticket in the queue,
  3050. 2:19:20what happens next? On the other side of
  3051. 2:19:22the queue sits our worker node. This
  3052. 2:19:25worker picks this ticket and sees that
  3053. 2:19:28this ticket was posted by John. This
  3054. 2:19:30worker checks the follows table to know
  3055. 2:19:32who follows John. And the worker node
  3056. 2:19:35realizes that John is followed by 800
  3057. 2:19:38followers.
  3058. 2:19:40Then this worker pushes this new post
  3059. 2:19:42into the list of each follower. And once
  3060. 2:19:45the worker has added this new post to
  3061. 2:19:47the lists of all the concerned people,
  3062. 2:19:49the worker then acknowledges the ticket
  3063. 2:19:52and then the ticket is closed.
  3064. 2:19:55And you might be thinking, why have we
  3065. 2:19:57made this new outbox table? Why can't we
  3066. 2:20:00simply save the post in the database and
  3067. 2:20:03drop the ticket into the queue itself?
  3068. 2:20:06If we do that, then those would be two
  3069. 2:20:08separate writes into two separate
  3070. 2:20:10systems.
  3071. 2:20:11One write will be in the database and
  3072. 2:20:13one right will be in the queue. Now
  3073. 2:20:16imagine if the server crashes after
  3074. 2:20:18saving the post in the database but
  3075. 2:20:20before dropping the ticket in the queue.
  3076. 2:20:23Then the post is saved in the database
  3077. 2:20:25but the queue never gets it. That's why
  3078. 2:20:28we are doing everything within the
  3079. 2:20:29database with the help of the outbox
  3080. 2:20:32table.
  3081. 2:20:33Notice that now when someone posts
  3082. 2:20:35something then we write the details only
  3083. 2:20:38to the database. Both the post and the
  3084. 2:20:41note land there together in the
  3085. 2:20:42database.
  3086. 2:20:44And this trick of saving a small note in
  3087. 2:20:46the outbox table and then letting a
  3088. 2:20:48background worker deliver the
  3089. 2:20:50information is called the outbox
  3090. 2:20:52pattern. Now there is one more thing you
  3091. 2:20:55should notice in this flow. If a worker
  3092. 2:20:58writes the post with ID71
  3093. 2:21:01in the lists of all followers and
  3094. 2:21:03crashes before acknowledging the ticket
  3095. 2:21:05in the queue, another worker will pick
  3096. 2:21:07the same ticket and push the post with
  3097. 2:21:10ID71
  3098. 2:21:11into the same followers lists a second
  3099. 2:21:14time.
  3100. 2:21:15So now Allen's list is holding the same
  3101. 2:21:18post with ID71 twice. But this problem
  3102. 2:21:22can be solved easily. The worker can
  3103. 2:21:25simply check if this post ID already
  3104. 2:21:27exists. And if yes, it can simply skip
  3105. 2:21:30this post with the same post ID. And
  3106. 2:21:33even if some duplicate still slips
  3107. 2:21:36through after a refresh, remember that
  3108. 2:21:38the interviewer told us that an
  3109. 2:21:40occasional duplicate after a refresh is
  3110. 2:21:43not a big problem.
  3111. 2:21:47So premputing every feed looks like a
  3112. 2:21:49great upgrade to our system. But you
  3113. 2:21:52should remember that every solution
  3114. 2:21:53comes with its own problem. So let me
  3115. 2:21:56show you one big problem with this
  3116. 2:21:58system.
  3117. 2:22:00Let's say one of our account holders is
  3118. 2:22:02Leonel Messi and 100 million users
  3119. 2:22:05follow Leonel Messi.
  3120. 2:22:08Now as soon as Leonel Messi posts
  3121. 2:22:10something, we will have to add this post
  3122. 2:22:12to the lists of all these 100 million
  3123. 2:22:15users so that all of them can see Leonel
  3124. 2:22:18Messi's post. This one operation could
  3125. 2:22:22itself take minutes. And you can imagine
  3126. 2:22:26how big this operation is. One person is
  3127. 2:22:30posting something and we are writing the
  3128. 2:22:33same thing into 100 million lists.
  3129. 2:22:37We have one more issue here. We are
  3130. 2:22:40writing Leonel Messi's post into 100
  3131. 2:22:44million lists. But some of these users
  3132. 2:22:47might not open the app in upcoming days.
  3133. 2:22:50And that means some of the work done by
  3134. 2:22:52our system gets wasted.
  3135. 2:22:56So now we can see the full picture of
  3136. 2:22:58pros and cons. When we were building the
  3137. 2:23:01feed at the time the user opens the app,
  3138. 2:23:04it was leading to a lot of read queries
  3139. 2:23:07running on our database.
  3140. 2:23:09But if we premputee the feed, then we
  3141. 2:23:12are making the feed for all the users
  3142. 2:23:15even if they don't open the app in the
  3143. 2:23:17coming few days.
  3144. 2:23:19And in the preomputation approach, if a
  3145. 2:23:22celebrity posts, then we have to write
  3146. 2:23:25the same post into millions of lists.
  3147. 2:23:29So can we really say the precomputing
  3148. 2:23:32approach is the better approach for
  3149. 2:23:34building the feed? Let's answer this
  3150. 2:23:36question.
  3151. 2:23:38In reality, precomputing is actually a
  3152. 2:23:41good approach for normal users. But for
  3153. 2:23:45celebrities, we shouldn't premputee.
  3154. 2:23:48We should read the celebrity accounts
  3155. 2:23:49when we open the app. Therefore, the
  3156. 2:23:53best approach is the hybrid approach. We
  3157. 2:23:56divide our users into normal users and
  3158. 2:23:59celebrity users. How do we divide the
  3159. 2:24:02users? We can put a threshold on the
  3160. 2:24:06number of followers. Let's say at
  3161. 2:24:09100,000 followers.
  3162. 2:24:11Accounts below 100,000 followers are
  3163. 2:24:14normal accounts. And when these normal
  3164. 2:24:17users post anything, we push their posts
  3165. 2:24:21into the follower lists exactly the way
  3166. 2:24:24we just built.
  3167. 2:24:26Accounts above 100,000 followers get the
  3168. 2:24:29celebrity flag. And posts that come from
  3169. 2:24:32celebrity accounts are not pushed into
  3170. 2:24:35the followers lists.
  3171. 2:24:38Now when a user named Allen calls the
  3172. 2:24:40get feed functionality to get his home
  3173. 2:24:42feed, our system does two small jobs
  3174. 2:24:46instead of one. First, the system reads
  3175. 2:24:49Allen's ready-made list that lies in
  3176. 2:24:52Reddus. And the second job is that our
  3177. 2:24:55system separately fetches the latest
  3178. 2:24:57posts from the celebrity accounts that
  3179. 2:25:00Allen follows.
  3180. 2:25:02Now the data is coming from two streams.
  3181. 2:25:05one the premputed list that contains
  3182. 2:25:08posts from normal users and second the
  3183. 2:25:12direct fetch for celebrity users.
  3184. 2:25:15So our system will need to merge both
  3185. 2:25:18kinds of posts and return the first 50
  3186. 2:25:21posts to Allen.
  3187. 2:25:23And because we are reading celebrity
  3188. 2:25:25posts directly, we don't need to write
  3189. 2:25:28the celebrity posts to the lists of
  3190. 2:25:30millions of users.
  3191. 2:25:33And do you remember that small flag we
  3192. 2:25:35kept in our users table? The flag that
  3193. 2:25:38tells us whether an account is a
  3194. 2:25:40celebrity account. I told you back then
  3195. 2:25:43that you will see its importance later.
  3196. 2:25:46And this flag is what our system checks
  3197. 2:25:49to decide whether a post should be
  3198. 2:25:51pushed into the follower lists or not.
  3199. 2:25:55In summary, you should always remember
  3200. 2:25:58that normal accounts get pushed and
  3201. 2:26:00celebrity users get pulled.
  3202. 2:26:03And now that the design is complete, let
  3203. 2:26:06me tell you some industry terminologies
  3204. 2:26:08that your interviewer or your peers will
  3205. 2:26:10use frequently.
  3206. 2:26:12The approach where we build the fresh
  3207. 2:26:14feed when the user reads the feed is
  3208. 2:26:17called fan out on read. And the approach
  3209. 2:26:21where we premputee the feed when a new
  3210. 2:26:23post is written is called fan out on
  3211. 2:26:26write.
  3212. 2:26:27I disclosed the names late purposefully
  3213. 2:26:30because I wanted you to understand the
  3214. 2:26:32concept first and then understand the
  3215. 2:26:35terminology.
  3216. 2:26:39And now the last step of our framework
  3217. 2:26:42which is bottlenecks and scaling. We
  3218. 2:26:45need to think about how this design
  3219. 2:26:46behaves when the number of users grows
  3220. 2:26:49and where this design will fail.
  3221. 2:26:52First, the most important component for
  3222. 2:26:55our precomputed feeds is the cache. We
  3223. 2:26:58are using two caches in our design and
  3224. 2:27:01these two caches keep the system fast
  3225. 2:27:05and because of these two caches, we are
  3226. 2:27:07able to serve the feed very quickly.
  3227. 2:27:10So, let's see how much data we have to
  3228. 2:27:12store in these caches.
  3229. 2:27:15Now, let's take the first cache. This
  3230. 2:27:18cache holds the list of 10 million
  3231. 2:27:20users. But for each user, we can't have
  3232. 2:27:23an infinite list. So we have to decide
  3233. 2:27:26the maximum number of posts that can be
  3234. 2:27:29stored in one list. And it is common
  3235. 2:27:32practice to keep around 500 posts in
  3236. 2:27:35this list. So let's do the calculation
  3237. 2:27:38based on 500 posts per list.
  3238. 2:27:42One post ID takes around 8 bytes. So 500
  3239. 2:27:47post ids would be around 4 kilob.
  3240. 2:27:52That means for one user we have to store
  3241. 2:27:554 kilob
  3242. 2:27:57and for 10 million users we multiply
  3243. 2:28:00this by 10 million and this comes out to
  3244. 2:28:04around 40 GB.
  3245. 2:28:07So our first cache should be at least 40
  3246. 2:28:10GB.
  3247. 2:28:12Okay. Now let's take a look at the
  3248. 2:28:14second cache. And in this cache we
  3249. 2:28:16actually store the posts.
  3250. 2:28:19Earlier we calculated that each user on
  3251. 2:28:22an average posts three times per month
  3252. 2:28:26and we have 10 million active users. So
  3253. 2:28:30you can see that we will have 30 million
  3254. 2:28:32posts per month. So if we store all the
  3255. 2:28:36posts in the last one month then we have
  3256. 2:28:39to store around 30 million posts and
  3257. 2:28:43each post is around 1 kilobyte.
  3258. 2:28:47So the total storage for storing one
  3259. 2:28:49month of posts would be around 30 GB.
  3260. 2:28:54But in reality we might not be storing
  3261. 2:28:56all the posts that were posted in the
  3262. 2:28:58last 30 days. We simply give this cache
  3263. 2:29:02a fixed amount of memory and then the
  3264. 2:29:05cache only keeps the posts that are read
  3265. 2:29:07more frequently and we know that the new
  3266. 2:29:10posts will be read more frequently.
  3267. 2:29:14And there is one more important thing
  3268. 2:29:16about these lists.
  3269. 2:29:18Note that we are not making lists for
  3270. 2:29:20every user. We are making lists only for
  3271. 2:29:23the active users.
  3272. 2:29:25For our design, let's say an active user
  3273. 2:29:28is somebody who opened the app in the
  3274. 2:29:30last 30 days, but 30 days is not fixed.
  3275. 2:29:35And this duration can change from
  3276. 2:29:37company to company.
  3277. 2:29:39So when Lionol Messi posts something, we
  3278. 2:29:42don't push his post into the lists of
  3279. 2:29:44people who have not opened the app in
  3280. 2:29:46months because all that work would be
  3281. 2:29:49wasted.
  3282. 2:29:50And when a user comes back after two
  3283. 2:29:52months and opens the app, we build the
  3284. 2:29:55feed for this user once at read time.
  3285. 2:29:59One more point we need to discuss is
  3286. 2:30:01that somewhere the interviewer will ask
  3287. 2:30:03about the database because a real system
  3288. 2:30:06rarely uses one database for everything.
  3289. 2:30:10For example, a post is written to the
  3290. 2:30:13posts table exactly once. This post is
  3291. 2:30:16usually never updated. And data in this
  3292. 2:30:19post row is always read by looking at
  3293. 2:30:22author and by time.
  3294. 2:30:24This shape fits well with a database
  3295. 2:30:27like Cassandra which is built for
  3296. 2:30:29exactly this type of timeordered data.
  3297. 2:30:33And if you look at the other tables like
  3298. 2:30:34the users table and the follows table,
  3299. 2:30:37they are very different. They have small
  3300. 2:30:40rows. They have relationships and
  3301. 2:30:43lookups are done in both directions.
  3302. 2:30:46And a relational database handles these
  3303. 2:30:49very well.
  3304. 2:30:50And one more thing about our database,
  3305. 2:30:53we are writing 30 million posts every
  3306. 2:30:56month. And these posts are never deleted
  3307. 2:30:59and they stay with our system forever.
  3308. 2:31:02So in one year we are looking at around
  3309. 2:31:05360
  3310. 2:31:06million posts. And this number only
  3311. 2:31:09keeps growing.
  3312. 2:31:12One single database machine will not be
  3313. 2:31:14able to hold all these posts. So we will
  3314. 2:31:17have to store our posts across multiple
  3315. 2:31:20database machines.
  3316. 2:31:22And this splitting of data across
  3317. 2:31:24multiple machines is called sharding.
  3318. 2:31:30Okay, we have come a very long way and I
  3319. 2:31:34want to turn your attention to our
  3320. 2:31:36fourth clarifying question that we asked
  3321. 2:31:38where the interviewer said that posts
  3322. 2:31:40can carry images and videos along with
  3323. 2:31:43the text.
  3324. 2:31:45So what about these photos and videos?
  3325. 2:31:48Where do we store them? We do not store
  3326. 2:31:50the image and the video in our table
  3327. 2:31:53where we store all the posts because
  3328. 2:31:56this will make our database very heavy.
  3329. 2:31:59Therefore, in the database row, we will
  3330. 2:32:02only store the link of the image and
  3331. 2:32:04video that needs to be attached with the
  3332. 2:32:06post.
  3333. 2:32:08Basically what we are saying is that in
  3334. 2:32:10the database we have the text and then
  3335. 2:32:13we ask our user's app to load the image
  3336. 2:32:16or video from object storage like S3.
  3337. 2:32:22Typically a CDN sits in front of the
  3338. 2:32:24object storage and caches the media to
  3339. 2:32:27ship it faster to the end user. So the
  3340. 2:32:30app normally downloads the image or
  3341. 2:32:32video through the CDN. The image is
  3342. 2:32:35loaded directly from the content
  3343. 2:32:37delivery network.
  3344. 2:32:39Let's summarize this. A post row should
  3345. 2:32:42carry the text of the post and a link to
  3346. 2:32:45the image or video. The image goes to
  3347. 2:32:48the object storage and is delivered
  3348. 2:32:50through the content delivery network so
  3349. 2:32:53that it can be served to the user much
  3350. 2:32:55faster.
  3351. 2:32:56You should also note that the post cache
  3352. 2:32:59or our second cache where we store the
  3353. 2:33:01actual post will also store the image or
  3354. 2:33:05video URL.
  3355. 2:33:07This way the heavy images and videos
  3356. 2:33:10don't have any impact on our system.
  3357. 2:33:13And finally, let's look at what happens
  3358. 2:33:16when one of the posts goes viral.
  3359. 2:33:19The problem is that everyone on the
  3360. 2:33:21planet is reading the same post at the
  3361. 2:33:24same time. And this post sits in our
  3362. 2:33:27post cache on one single cache. So that
  3363. 2:33:31one single cache takes the whole world's
  3364. 2:33:34reads and is not able to keep up.
  3365. 2:33:37Therefore, to distribute the extreme
  3366. 2:33:39load from users, we put that one viral
  3367. 2:33:42post on several cache instances and
  3368. 2:33:46spread the reads across these copies of
  3369. 2:33:48the cache.
  3370. 2:33:50At the end of the interview, we have to
  3371. 2:33:52summarize what we have done in this
  3372. 2:33:54design.
  3373. 2:33:55So let me summarize this system for you.
  3374. 2:33:59When a normal account posts, the post
  3375. 2:34:01and its small outbox note are saved
  3376. 2:34:04together in the database in one
  3377. 2:34:06transaction.
  3378. 2:34:08A background worker will check the note
  3379. 2:34:10in the outbox and then add a ticket in
  3380. 2:34:12the queue.
  3381. 2:34:14Then one of the workers will pick this
  3382. 2:34:16ticket and push the post ID into the
  3383. 2:34:19feed list of every follower. And this
  3384. 2:34:22feed list is stored in Reddus.
  3385. 2:34:25Each feed list can store up to 500 post
  3386. 2:34:29ids. Celebrity accounts function
  3387. 2:34:32differently. When Allen opens his mobile
  3388. 2:34:35app, the get feed API is called and the
  3389. 2:34:39server reads his Reddit list and gets
  3390. 2:34:41the actual posts from the second cache.
  3391. 2:34:45And along with this, we also pull posts
  3392. 2:34:47from the few celebrities he follows.
  3393. 2:34:50We merge both the normal users posts and
  3394. 2:34:53the celebrity posts and then return the
  3395. 2:34:56top 50 posts to Allen. We do not
  3396. 2:35:00premputee the feed list for inactive
  3397. 2:35:02users. We build the feed when they
  3398. 2:35:05return to the platform.
  3399. 2:35:07And at last, a viral post is served from
  3400. 2:35:10multiple cache copies.
  3401. 2:35:15Towards the end of the interview, you
  3402. 2:35:17can expect some follow-up questions from
  3403. 2:35:19the interviewer. And these follow-up
  3404. 2:35:22questions test if you can improve your
  3405. 2:35:24design based on the questions.
  3406. 2:35:27Let's quickly see three follow-up
  3407. 2:35:29questions.
  3408. 2:35:31Question number one is that Allan is a
  3409. 2:35:34power user and he follows 20,000
  3410. 2:35:37accounts and 800 of those accounts are
  3411. 2:35:40celebrity accounts.
  3412. 2:35:42So, every time Alan opens the app, our
  3413. 2:35:45system has to go and pull the recent
  3414. 2:35:48posts of 800 celebrities before Allen
  3415. 2:35:52can see anything on his screen. Will you
  3416. 2:35:55change anything for this power user?
  3417. 2:35:58And this is a good question because in
  3418. 2:36:00our hybrid design, we said that
  3419. 2:36:03celebrity posts are never pushed into
  3420. 2:36:05the follower lists and instead we read
  3421. 2:36:09the celebrity posts at the time the feed
  3422. 2:36:11is opened. For a normal user who follows
  3423. 2:36:15a few celebrities, this operation is
  3424. 2:36:17cheap. For Allen, this becomes 800
  3425. 2:36:21separate fetches from the database
  3426. 2:36:23because he follows 800 celebrities.
  3427. 2:36:27Now before answering the interviewer,
  3428. 2:36:30you should notice one thing carefully.
  3429. 2:36:33The 20,000 number by itself is not the
  3430. 2:36:36problem here because Allen's list is
  3431. 2:36:39already built and sitting in Reddis
  3432. 2:36:41cache. The problem is only the 800
  3433. 2:36:45celebrity accounts of those 20,000
  3434. 2:36:48accounts that he follows.
  3435. 2:36:50So the fix is that we should stop
  3436. 2:36:53fetching a celebrity's posts from
  3437. 2:36:55database for every single user. Instead,
  3438. 2:36:59we keep one small list in the cache for
  3439. 2:37:02each celebrity account. And this list
  3440. 2:37:05holds the recent post ids of that
  3441. 2:37:08celebrity.
  3442. 2:37:09For example, we will keep one list for
  3443. 2:37:12Leonel Messi and this list will keep all
  3444. 2:37:16the recent posts by Leonel Messi.
  3445. 2:37:19This one list is shared by everybody who
  3446. 2:37:21follows Leonel Messi and there are only
  3447. 2:37:24a few thousand celebrity accounts on our
  3448. 2:37:27platform. So we will have to keep a few
  3449. 2:37:30thousand small lists to serve the
  3450. 2:37:32celebrity posts.
  3451. 2:37:34Now Allen's feed request reads 800 small
  3452. 2:37:37lists from the cache instead of sending
  3453. 2:37:40800 queries to our database.
  3454. 2:37:43The interviewer could have asked this
  3455. 2:37:45question differently. They could have
  3456. 2:37:47asked that if Leonel Messi has a 100
  3457. 2:37:50million followers then when Leonel Messi
  3458. 2:37:53posts something thousands of users will
  3459. 2:37:56pull the new post from Leonel Messi. Due
  3460. 2:38:00to this all the users will be sending a
  3461. 2:38:02lot of queries to our database and can
  3462. 2:38:05overload our database.
  3463. 2:38:07So now we have made it clear that we
  3464. 2:38:09will keep the celebrity posts in cache
  3465. 2:38:12as well.
  3466. 2:38:13There is a second smaller thing you
  3467. 2:38:15should be honest about with the
  3468. 2:38:17interviewer. If 20,000 accounts are
  3469. 2:38:20followed by Allen, then Allen's feed
  3470. 2:38:22list, which can hold only 500 posts,
  3471. 2:38:26will be filled very quickly.
  3472. 2:38:29And because new posts are pouring into
  3473. 2:38:31the list, the older posts will be pushed
  3474. 2:38:34out of the list before Alen ever sees
  3475. 2:38:37them. And that is acceptable because
  3476. 2:38:40Allan is going to read only the top 50
  3477. 2:38:42posts anyway.
  3478. 2:38:45Let's see the second question that the
  3479. 2:38:46interviewer can ask.
  3480. 2:38:48Your system has a backlog and new posts
  3481. 2:38:51are taking 8 minutes to reach the
  3482. 2:38:53follower lists. What will you do to fix
  3483. 2:38:56the situation?
  3484. 2:38:59First of all, whenever there is a
  3485. 2:39:01latency or speed issue in your system,
  3486. 2:39:04you should first identify where the
  3487. 2:39:06problem is. And to identify a problem,
  3488. 2:39:09you should be measuring the right thing.
  3489. 2:39:12So the first thing you should say is, I
  3490. 2:39:15would watch the time between the post
  3491. 2:39:17getting saved in our database and the
  3492. 2:39:19post landing in the last followers list.
  3493. 2:39:23If posts are accumulating in the queue,
  3494. 2:39:25then you can add more worker nodes so
  3495. 2:39:28that multiple worker nodes can work in
  3496. 2:39:30parallel. They take the tickets from the
  3497. 2:39:33queue and write the new post into the
  3498. 2:39:35followers lists. But adding worker nodes
  3499. 2:39:39costs money and time. So you need to be
  3500. 2:39:42slightly more strategic on how you
  3501. 2:39:45handle new posts. Here is the answer
  3502. 2:39:48that will impress the interviewer.
  3503. 2:39:50We do not need to treat all the
  3504. 2:39:52followers equally. When we are writing
  3505. 2:39:55the post into the feed of all the
  3506. 2:39:57followers, we can prioritize the
  3507. 2:39:59followers who have been active on the
  3508. 2:40:01app in the last few minutes or hours or
  3509. 2:40:04if they are active right now.
  3510. 2:40:07When the worker picks the ticket for J's
  3511. 2:40:09new post, the worker first pushes this
  3512. 2:40:12post into the lists of the followers who
  3513. 2:40:15are using the app right now or open the
  3514. 2:40:19app in the last few hours. And all the
  3515. 2:40:22remaining followers get the same post in
  3516. 2:40:24a second pass.
  3517. 2:40:27A user whose app is not open and is not
  3518. 2:40:30looking at the screen will not even know
  3519. 2:40:32it took a few minutes to write the post
  3520. 2:40:35into their feed.
  3521. 2:40:37Let's see one last question from the
  3522. 2:40:39interviewer. They say, "Right now, you
  3523. 2:40:43are showing the newest post first to the
  3524. 2:40:45user, but now the product team wants a
  3525. 2:40:49ranked feed. They don't want you to show
  3526. 2:40:51the newest post first. They want to show
  3527. 2:40:55the most relevant post first.
  3528. 2:40:58What will you change in your design?"
  3529. 2:41:00And remember in our very first
  3530. 2:41:03clarifying question the interviewer told
  3531. 2:41:06us to sort the posts by time and we said
  3532. 2:41:09that a ranking algorithm is a separate
  3533. 2:41:12problem in itself.
  3534. 2:41:14So the interviewer is now bringing it
  3535. 2:41:16back to see if your design can absorb
  3536. 2:41:19this change.
  3537. 2:41:21So the first thing you should tell the
  3538. 2:41:22interviewer is what does not change.
  3539. 2:41:26We won't change the three tables we had
  3540. 2:41:28in the database and the Q doesn't change
  3541. 2:41:31as well. The workers pushing post ids
  3542. 2:41:35into the follower lists don't change and
  3543. 2:41:38the post cache doesn't change.
  3544. 2:41:42So when Allen opens the app, we take the
  3545. 2:41:45newest 500 post ids from Alen's list. We
  3546. 2:41:49add the celebrity post to them and we
  3547. 2:41:52hand all of these candidates to a
  3548. 2:41:54separate service called the scoring
  3549. 2:41:57service.
  3550. 2:41:58This scoring service gives every
  3551. 2:42:00candidate a score and we sort by that
  3552. 2:42:03score and then we return the top 50
  3553. 2:42:07posts to Allen.
  3554. 2:42:09Ranking or scoring algorithms generally
  3555. 2:42:12look at how old the post is, how many
  3556. 2:42:16likes and comments the post has right
  3557. 2:42:18now and how much Allen interacts with
  3558. 2:42:21the author of that post. Notice one very
  3559. 2:42:25very important thing here. We can't
  3560. 2:42:28really premputee the score because
  3561. 2:42:30things like comments, likes, and
  3562. 2:42:32interactions change very frequently.
  3563. 2:42:36That means a score cannot be precomputed
  3564. 2:42:39when a new item is posted by someone
  3565. 2:42:42because the score depends on likes and
  3566. 2:42:44comments which happen after the post
  3567. 2:42:46goes live. Therefore, the ranking has to
  3568. 2:42:50happen at read time and it should happen
  3569. 2:42:53very quickly because we want to serve
  3570. 2:42:56the feed almost instantly
  3571. 2:42:58and that is why we score only a few
  3572. 2:43:01hundred candidates and not the whole
  3573. 2:43:03list.
  3574. 2:43:05But you should also tell the interviewer
  3575. 2:43:07the cost of this change. The first cost
  3576. 2:43:10is that every time a user opens the
  3577. 2:43:13feed, we are running the scoring step.
  3578. 2:43:16And to compute these scores, we also
  3579. 2:43:18need the current like and comment counts
  3580. 2:43:21of these posts.
  3581. 2:43:23Basically, we are doing a lot of work
  3582. 2:43:26here which will consume time.
  3583. 2:43:29The second cost is that the order of
  3584. 2:43:31posts will keep changing every time the
  3585. 2:43:33user opens the app because the score of
  3586. 2:43:36each post keeps changing.
  3587. 2:43:39Let me show you this with an example.
  3588. 2:43:42Alan opens his feed and John's post
  3589. 2:43:45appears at position 5. So Alan sees the
  3590. 2:43:48post and keeps scrolling.
  3591. 2:43:51After 2 minutes, Alan refreshes his
  3592. 2:43:53feed. But in these two minutes, J's post
  3593. 2:43:57got 200 new likes. So the score of J's
  3594. 2:44:01post went up. And now J's post is
  3595. 2:44:05sitting at position two in Allen's feed.
  3596. 2:44:08So Allen is seeing the same post again
  3597. 2:44:11after a refresh.
  3598. 2:44:14This is fine because the interviewer
  3599. 2:44:16said in the beginning that duplicate
  3600. 2:44:18post after refresh is acceptable.
  3601. 2:44:21But in reality, if you don't want to
  3602. 2:44:23show the same post again, then you have
  3603. 2:44:26to also track what posts Allan has
  3604. 2:44:28already seen. And then we don't show
  3605. 2:44:31those posts again.
  3606. 2:44:34This is it for the news feed. And one
  3607. 2:44:36last thing, if an interviewer asks you
  3608. 2:44:39to design Twitter or the Instagram feed
  3609. 2:44:42or the Facebook feed, it is the same
  3610. 2:44:45problem wearing a different name.
  3611. 2:44:48So everything we discussed here applies
  3612. 2:44:51directly.
  3613. 2:44:53Take a second to process all the
  3614. 2:44:55questions and answers we discussed and
  3615. 2:44:57then you can move on to the next
  3616. 2:44:59problem.
  3617. 2:45:01Now let's try one more lab. In this lab,
  3618. 2:45:04you'll run a real news feed. When
  3619. 2:45:07someone posts something, a worker copies
  3620. 2:45:10that post into every followers feed list
  3621. 2:45:13ahead of time. So when a follower opens
  3622. 2:45:16their feed, the new post loads
  3623. 2:45:19instantly.
  3624. 2:45:21Go give this lab a try.

About this transcript

This page contains the full transcript of System Design Interview Prep for Beginners (Full Course) by KodeKloud, generated from the public captions YouTube serves with the video. The transcript has 23,662 words across 3,624 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.

What you can do with it

Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to translate the transcript into 100+ languages.

Free YouTube transcript tool

YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, translation to 100+ languages, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.