{
  "courses": {
    "amazonclone": {
      "title": "Amazon Clone Complete Course",
      "description": "Learn how to build a full-stack Amazon clone application completely FREE. ($100 Udemy Course) using React Native, Firebase Authentication, Firebase storage, Redux, Spring boot, REST API and finally deploy the complete project into Heroku Cloud Platform.",
      "videos": [
        {
          "id": "01",
          "title": "Part 1: Build a Full Stack AMAZON Clone with REACT JS",
          "videoUrl": "https://youtu.be/397AZniLo04",
          "description": "In this video, we will clone the complete Amazon application using React framework. Starting from the Home page, product display page, product detail page as well cart & checkout page.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault.jpg"
        },
        {
          "id": "02",
          "title": "Part 2: Build the complete Backend AMAZON Clone with Spring Boot | MongoDB | Intercommunication",
          "videoUrl": "https://youtu.be/elxkmIxAaSA",
          "description": "In this video, we will clone the complete backend of Amazon application using Java Spring. Starting from the Home page, product display page, product detail page as well cart & checkout page.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/35637.webp?updatedAt=1738940542728"
        },
        {
          "id": "03",
          "title": "Part 3: Deploying Complete Apps to Cloud FREE | Resolving CORS Error | Setting up Database in Cloudn",
          "videoUrl": "https://youtu.be/fUQmGRsjo58",
          "description": "In this video, we will see how to deploy a complete full-stack application into the cloud server. Over here we will be using Heroku, as our deployment cloud server, and we will deploy a complete project over there.Starting from deploying the front end till the backend, and also we will learn how to set up and configure a database into the cloud application. So without wasting much time, let's get started.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(1).jpg?updatedAt=1738079780191"
        }
      ]
    },
    "instagramclone": {
      "title": "Instagram Clone Complete Course",
      "description": "Learn how to build a full-stack Instagram clone application completely FREE. ($100 Udemy Course) using React Native, Firebase Authentication, Firebase storage, Redux, Spring boot, REST API and finally deploy the complete project into Heroku Cloud Platform.",
      "videos": [
        {
          "id": "01",
          "title": "Instagram Cloning ( Part 1/4 ) [ Using React ]: Introduction & Project Setup",
          "videoUrl": "https://youtu.be/IZMN6N9vpcE",
          "description": "In this video, we will cover the Introduction and the preliminary project setup, that you must have before starting with this course.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/%20qk24g24goi4gv2oi.jpg?updatedAt=1746562748950"
        },
        {
          "id": "02",
          "title": "Instagram Cloning (2/4) [ using React ]: Complete UI Development",
          "videoUrl": "https://youtu.be/KSDYjonD95A",
          "description": "In this video, we will clone the complete Instagram application using React framework. Starting from Sign Up and Sign-in page, till the Home page.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/34ntl34ntg3jknwnv.webp?updatedAt=1746562798369"
        },
        {
          "id": "03",
          "title": "Instagram Cloning ( Part 3/4 ): [ Using React ]:  Complete Back End Development using Spring-Boot",
          "videoUrl": "https://youtu.be/cvfiTRO8ZVw",
          "description": "In this video, we will cover the complete Back End part of the application. Here, we will create the REST API using the Spring Boot Framework, and connect it with the MySQL database. And then we'll test the application using PostMan Client.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/4jnt24ktb2kt4tr.webp?updatedAt=1746562843119"
        },
        {
          "id": "04",
          "title": "Part 4: Instagram Cloning ( Part 4/4 ): [Using React ]: Integrating the Front End with REST API & FireBase",
          "videoUrl": "https://youtu.be/-79pfrlsOPU",
          "description": "In this video, we will cover the integration of the Front End part of our application with the Back End REST API. Then we will integrate both Google FireBase Authentication and Storage Service. And at the last we'll deploy the complete project to the cloud server, using Heroku Cloud Platform.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(2).jpg?updatedAt=1738080129284"
        },
        {
          "id": "05",
          "title": "Part 5: Deploying Complete Apps to Cloud FREE | Resolving CORS Error | Setting up Database in Cloudn",
          "videoUrl": "https://youtu.be/fUQmGRsjo58",
          "description": "In this video, we will see how to deploy a complete full-stack application into the cloud server. Over here we will be using Heroku, as our deployment cloud server, and we will deploy a complete project over there.Starting from deploying the front end till the backend, and also we will learn how to set up and configure a database into the cloud application. So without wasting much time, let's get started.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(1).jpg?updatedAt=1738079780191"
        }
      ]
    },
    "facebookclone": {
      "title": "Facebook Clone Complete Course",
      "description": "Learn how to build a full-stack Facebook clone application completely FREE. ($100 Udemy Course) using React, Firebase Authentication, Firebase storage, Redux, Spring boot, REST API, NoSQL database Cassandra and finally deploy the complete project into Heroku Cloud Platform.",
      "videos": [
        {
          "id": "01",
          "title": "Part (1/4) Facebook Cloning : Introduction & Project Setup",
          "videoUrl": "https://youtu.be/6tRqPTJgcw8",
          "description": "In this video, we will clone the complete introduction and setup that you need to have, to start developing the Facebook clone.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/0972.webp?updatedAt=1738940620841"
        },
        {
          "id": "02",
          "title": "Part (2/4) Facebook Cloning : Complete UI Development using React",
          "videoUrl": "https://youtu.be/OyH7DNphJ0c",
          "description": "In this video, we will clone the complete Instagram application using React framework. Starting from Sign Up and Sign-in page, till the Home page.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/4yb43b.webp?updatedAt=1738940664053"
        },
        {
          "id": "03",
          "title": "Part (3/4) Facebook Cloning : Complete Back End Development using Spring-Boot",
          "videoUrl": "https://youtu.be/OyH7DNphJ0c",
          "description": "In this video, we will cover the complete Back End part of the application. Here, we will create the REST API using the Spring Boot Framework, and connect it with the Cassandra database. And then we'll test the application using PostMan Client.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/bqerbeb.webp?updatedAt=1738940703056"
        },
        {
          "id": "04",
          "title": "Part (4/4) Facebook Cloning: Integrating the Front End with REST API & FireBase",
          "videoUrl": "https://youtu.be/2QlZPF4ku8o",
          "description": "In this video, we will cover the integration of the Front End part of our application with the Back End REST API. Then we will integrate both Google FireBase Authentication and Storage Service. And at the last we'll deploy the complete project to the cloud server, using Heroku Cloud Platform.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/b24kjb24tjb2.webp?updatedAt=1746562651676"
        },
        {
          "id": "05",
          "title": "Part 5: Deploying Complete Apps to Cloud FREE | Resolving CORS Error | Setting up Database in Cloudn",
          "videoUrl": "https://youtu.be/fUQmGRsjo58",
          "description": "In this video, we will see how to deploy a complete full-stack application into the cloud server. Over here we will be using Heroku, as our deployment cloud server, and we will deploy a complete project over there.Starting from deploying the front end till the backend, and also we will learn how to set up and configure a database into the cloud application. So without wasting much time, let's get started.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(1).jpg?updatedAt=1738079780191"
        }
      ]
    },
    "multithreading": {
      "title": "Multi-Threading Bible",
      "description": "Complete guide to Java multi-threading",
      "videos": [
        {
          "id": "01",
          "title": "Part 1: What is Multithreading in JAVA ? Understanding all the concepts with EASE",
          "videoUrl": "https://youtu.be/HQoLpvRlsyg",
          "description": "In this video, we will understand in great detail with the help of a simple real-world example, that what is multithreading. How does it work? Why do we need multithreading in Java? What are the pros and cons of a multithreaded application? Along with that, we will discuss and clear all the doubts on multithreading and will answer all the most commonly questions asked in the technical interview",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/qr334y3y43g.webp?updatedAt=1746562305432"
        },
        {
          "id": "02",
          "title": "Part 2: Concurrency & Synchronization in Java Multithreading",
          "videoUrl": "https://youtu.be/6YLGGLkVR98",
          "description": "In this video, we will understand in great detail with the help of a simple real-world example, that what is concurrency in multithreading and how with the help of a simple synchronized keyword, we can implement Concurrency & Synchronization in Java Multithreaded application. Along with that, we will discuss and clear all the doubts on multithreading and will answer all the most commonly questions asked in the technical interview.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(3).jpg?updatedAt=1738080683962"
        },
        {
          "id": "03",
          "title": "Part 3: Multithreading InterThread Communication | Producer Consumer using wait and notify",
          "videoUrl": "https://youtu.be/DfKRNMRXgzY",
          "description": "In this video, we will discuss in detail how multiple threads can communicate with each other with the help of wait and notify. And to explain the interthread communication, we will consider the classical problem of java i.e Producer-consumer problem....",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(4).jpg?updatedAt=1738080747359"
        },
        {
          "id": "04",
          "title": "Part 4: Print Even and Odd Numbers Using 2 Threads in Java | Inter thread communication",
          "videoUrl": "https://youtu.be/mV60tgY2sfA",
          "description": "Given an integer N, the task is to write Java Program to print the first N natural numbers in increasing order using two threads...",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/fq24jt23ktb3kb23k43k423kt23kt324k234kt34ffcrwc.webp"
        }
      ]
    },
    "lowlevelsystemdesign": {
      "title": "Low Level Designing",
      "description": "Complete guide to Object Oriented Low Level Designing (OOPS)",
      "videos": [
        {
          "id": "01",
          "title": "Low Level Designing (LLD -1) - Flight Booking System | MakeMyTrip | Booking.com | Airlines",
          "videoUrl": "https://youtu.be/5yEoh3toRyE",
          "description": "So today in this video we will discuss, one the most commonly asked interview questions on low level designing. And today's topic is Design a flight booking system / Design MakeMyTrip / Design airlines booking system",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(10).webp?updatedAt=1738080991492"
        },
        {
          "id": "02",
          "title": "Low Level Designing (LLD -2) - Car Rental System | ZoomCar.com | Revv | Drivezy",
          "videoUrl": "https://youtu.be/AXa6jCfziRY",
          "description": "So today in this video we will discuss, one of the most commonly asked interview questions on low level designing. And today's topic is Design a Car Rental System / Design ZoomCar / Revv / Car booking system",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(5).jpg?updatedAt=1738081058955"
        },
        {
          "id": "03",
          "title": "Low Level Designing (LLD-3): Hotel Booking System | Booking.com | AirBnB | MakeMyTrip",
          "videoUrl": "https://youtu.be/6zZALeDMFew",
          "description": "So today in this video we will discuss, one of the most commonly asked interview questions on low level designing. And today's topic is Design a Hotel Booking System | Booking.com | Goibibo | AirBnb | MakeMyTrip",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(11).webp?updatedAt=1738081125608"
        },
        {
          "id": "04",
          "title": "Low Level Designing (LLD -4) - Food Ordering System | Low Level Design | LLD | Zomato | Swiggy",
          "videoUrl": "https://youtu.be/Zo_YfDmkZSQ",
          "description": "In this video I have discussed about the Low level System design of the online food delivery app like Swiggy, zomato, uber eats, food panda etc.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(12).webp?updatedAt=1738081194606"
        },
        {
          "id": "05",
          "title": "Low Level Designs (LLD - 5) : Cricket Score Update System / CricInfo / Cricbuzz",
          "videoUrl": "https://youtu.be/9iJ-iTpZH-A",
          "description": "In this video I have discussed about the Low level System design of the cricket score update system like cricbuzz, cricinfo etc.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(6).jpg?updatedAt=1738081273876"
        }
      ]
    },
    "graphplaylist": {
      "title": "Graph Playlist Complete Course",
      "description": "",
      "videos": [
        {
          "id": "01",
          "title": "Graph Tutorial 1: Introduction to Graph | Types | Terminologies | Data Structures | Graph",
          "videoUrl": "https://youtu.be/yF-n_vjhoAw",
          "description": "Welcome to the first chapter of our brand-new Data Structures Playlist, where we explore one of the most fundamental and versatile topics in computer science\u2014Graphs. This video serves as a comprehensive introduction, designed to lay the groundwork for understanding graphs and their applications in solving complex problems.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault.webp?updatedAt=1738600658778"
        },
        {
          "id": "02",
          "title": "Graph Tutorial 2: Representation of Graph in Java | Which one is Best ? Data Structures | Graph",
          "videoUrl": "https://youtu.be/cseN3bt_Vhw",
          "description": "Most of the video tutorials cover graph representations, but do you know which one form of representation is best for your specific needs? or how to choose between different graph representations to optimise your solutions during your coding interview? In this video, we dive deep into graph representations in Java, covering all the major forms: Adjacency List, Adjacency Matrix, and Edge List. Understanding graph representation is crucial for solving various problems in data structures and algorithms, especially in competitive programming and coding interviews. ",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(1).webp?updatedAt=1738600741541"
        },
        {
          "id": "03",
          "title": "Graph Tutorial 3: Graph Implementation Simplified |  Master Graph Implementation in Java | Graph",
          "videoUrl": "https://youtu.be/psNSeh15jJ8",
          "description": "Most YouTube videos and books stick to teaching graph representation using lists or arraylists. But did you know there are smarter ways to represent graphs in memory? When your graph isn\u2019t 0-indexed, list or array-based approaches can fall short. In this video, we\u2019ll explore better techniques to efficiently implement graphs in memory using more flexible data structures. Ready to level up your graph skills? Let\u2019s dive in!",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(2).webp?updatedAt=1738600809872"
        },
        {
          "id": "04",
          "title": "Graph Tutorial 4: Breadth First Search (BFS) | Connected Graph | Traversal | Data Structures | Graph",
          "videoUrl": "https://youtu.be/Z4plqYyalBQ",
          "description": "In this video, we're going to explore Graph Traversal, specifically looking at Breadth-First Search (BFS). Graph traversal is a key idea in graph theory, and it plays a significant role in solving real-life problems, such as finding the shortest path through a map or checking how networks connect with each other. By getting a good grasp of graph traversal methods, you'll also be able to tackle more complex subjects like connected components, bipartite graphs, and tree structures.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(3).webp?updatedAt=1738600879109"
        },
        {
          "id": "05",
          "title": "Graph Tutorial 5 : Depth First Search (DFS) | Connected Graph | Traversal | Data Structures | Graph",
          "videoUrl": "https://youtu.be/RNKPWjmyTe4",
          "description": "Did you know that Depth-First Search (DFS) is the backbone of many advanced algorithms, including those used for solving puzzles, navigating mazes, detecting cycles, and even performing topological sorting? Whether you're analyzing social networks, optimizing games, or designing AI decision trees, DFS plays a crucial role by exploring paths deeply before backtracking! Curious to see how DFS works step by step, how to visualize recursive calls, and implement it in real-world problems? Watch this video where I break down the entire DFS algorithm with clear visual diagrams and detailed coding explanations! \ud83d\ude80",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(4).webp?updatedAt=1738600958523"
        },
        {
          "id": "06",
          "title": "Graph Tutorial 6: BFS & DFS Traversal For Disconnected Graph | Traversal | Data Structures | Graph",
          "videoUrl": "https://youtu.be/IWc2NU1yrik",
          "description": "In this video, I explain in detail how to implement BFS (Breadth-First Search) and DFS (Depth-First Search) traversal for disconnected graphs. Unlike a connected graph, where a single BFS or DFS call can explore the entire graph, a disconnected graph requires multiple traversal calls to ensure all components are visited. This video focuses only on disconnected graphs, as I have already covered BFS and DFS for connected graphs in a previous video.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(5).webp?updatedAt=1738601028275"
        },
        {
          "id": "07",
          "title": "Graph Tutorial 7: Number of Provinces | Connected Components | Leetcode | Data Structures | Graph",
          "videoUrl": "https://youtu.be/tvOrIT2fBT8",
          "description": "Problem:There are n cities. Some of them are connected, while some are not. If city a is connected directly with city b, and city b is connected directly with city c, then city a is connected indirectly with city c. A province is a group of directly or indirectly connected cities and no other cities outside of the group.You are given an n x n matrix isConnected where isConnected[i][j] = 1 if the ith city and the jth city are directly connected, and isConnected[i][j] = 0 otherwise.Return the total number of provinces.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(6).webp?updatedAt=1738601110190"
        },
        {
          "id": "08",
          "title": "Graph Tutorial 8: Number of Islands | Count Connected Components in Matrix | Data Structures | Graph",
          "videoUrl": "https://youtu.be/dX6RZYxw41E",
          "description": "Problem: Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(7).webp?updatedAt=1738601193940"
        },
        {
          "id": "09",
          "title": "Graph Tutorial 9: Rotten Oranges Leetcode | BFS Traversal | Java | Data Structures | Graph",
          "videoUrl": "https://youtu.be/nt-rCF_D0iI",
          "description": "You are given an m x n grid where each cell can have one of three values: 0 representing an empty cell, 1 representing a fresh orange, or 2 representing a rotten orange. Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten. Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(8).webp?updatedAt=1738601273842"
        },
        {
          "id": "10",
          "title": "Graph Tutorial 10: Detect a Cycle in an Undirected Graph using BFS | Java | Data Structures | Graph",
          "videoUrl": "https://youtu.be/p69HDt9XNlE",
          "description": "In this video, I dive deep into cycle detection in an undirected graph using the Breadth-First Search (BFS) approach. Detecting cycles is a fundamental problem in graph theory and a crucial concept for coding interviews and competitive programming. This video is designed to help you understand how to use BFS effectively to determine whether a cycle exists in an undirected graph.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/maxresdefault%20(9).webp?updatedAt=1738601345526"
        },
        {
          "id": "11",
          "title": "Graph tutorial 11: Detect a Cycle in an Undirected Graph using DFS | Java | Data Structures | Graph",
          "videoUrl": "https://youtu.be/Y8bOGiLkBqg",
          "description": "In this video, I explain how to detect a cycle in an undirected graph using the Depth-First Search (DFS) approach. Cycle detection is one of the most important topics in graph theory, commonly asked in coding interviews and used in real-world applications like detecting circular dependencies and analyzing networks. This detailed tutorial will help you master the DFS algorithm for solving this problem efficiently.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/estlkbne.webp?updatedAt=1738940782341"
        },
        {
          "id": "12",
          "title": "Graph Tutorial 12 : Detect Cycle in a Directed Graph using DFS | Java | Data Structures | Graph",
          "videoUrl": "https://youtu.be/Exvy4GvTets",
          "description": "In this video, I dive deep into solving the cycle detection problem in a directed graph using the Depth-First Search (DFS) approach. Detecting cycles in directed graphs is a critical concept in graph theory, with applications in dependency resolution, scheduling tasks, and avoiding deadlocks in systems. This tutorial will provide you with a comprehensive understanding of how to solve this problem using DFS efficiently.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/vlnbvlwr.webp?updatedAt=1738940897493"
        },
        {
          "id": "13",
          "title": "Graph Tutorial 13: What is Topological Sorting? Why Do We Use It? Java | Data Structures | Graph",
          "videoUrl": "https://youtu.be/-LFvAXxcxTQ",
          "description": "In this video, I explain Topological Sorting in Graphs in detail, covering what it is, why we use it, and how it applies to real-world scenarios. Topological sorting is a crucial graph algorithm that helps in scheduling tasks, resolving dependencies, and analyzing workflows. If you're preparing for coding interviews or want to strengthen your graph theory knowledge, this video is a must-watch!",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/lknwelgn.webp?updatedAt=1738940977587"
        },
        {
          "id": "14",
          "title": "Graph Tutorial 14: Topological Sort Algorithm using DFS Traversal | Java | Data Structures | Graph",
          "videoUrl": "https://youtu.be/ldsh1gHm4qo",
          "description": "In this video, I explain how to find the Topological Sort of a graph using the Depth-First Search (DFS) algorithm. Topological Sorting is a fundamental graph algorithm used for scheduling tasks, resolving dependencies, and processing directed acyclic graphs (DAGs). It is widely applied in operating systems, project planning, compiler design, and more. If you're preparing for coding interviews or competitive programming, mastering this concept is essential!",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/tbenwefe.webp?updatedAt=1738941055413"
        },
        {
          "id": "15",
          "title": "Graph Tutorial 15: Kahn's Algorithm | Topological Sort Algorithm | Detect Cycle | BFS | Java | Graph",
          "videoUrl": "https://youtu.be/Fmcy7CbwSIg",
          "description": "In this video, I explain how to find the Topological Sort of a graph using Kahn\u2019s Algorithm (BFS approach). Topological Sorting is a fundamental algorithm in graph theory, widely used in task scheduling, dependency resolution, build systems, and more. If you're preparing for coding interviews or want to strengthen your understanding of graph algorithms, this video is perfect for you!",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/15.webp?updatedAt=1740587882487"
        },
        {
          "id": "16",
          "title": "Graph Tutorial 16: Course Schedule I and II | Kahn's | Topological Sort Algorithm  | Java | Graph",
          "videoUrl": "https://youtu.be/Q6pP5Bf6QzM",
          "description": "In this video, I provide a detailed solution to the LeetCode problems: Course Schedule I (LC 207) & Course Schedule II (LC 210) using Kahn\u2019s Algorithm (BFS approach). These problems are fundamental for understanding Topological Sorting in Directed Acyclic Graphs (DAGs) and are frequently asked in coding interviews. If you're preparing for LeetCode, Codeforces, or FAANG interviews, this video will help you master dependency resolution using graph algorithms.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/16.webp?updatedAt=1740587951982"
        },
        {
          "id": "17",
          "title": "Graph Tutorial 17: Surrounded Regions | Replace O's with X's | BFS & DFS | Leetcode | Java | Graph",
          "videoUrl": "https://youtu.be/aUt6pYAZhx8",
          "description": "Are you struggling with the LeetCode Surrounded Regions problem? In this comprehensive tutorial, I break down both BFS and DFS approaches to solve this classic boundary traversal challenge",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/17.webp?updatedAt=1740588024942"
        }
      ]
    },
    "systemdesign": {
      "title": "System Design Complete Course",
      "description": "This playlist covers all the important system design interview questions, breaking down complex architectures into simple, easy-to-understand concepts. Whether you're preparing for FAANG interviews or want to strengthen your system design skills, this course provides a structured approach with real-world examples \ud83d\ude80",
      "videos": [
        {
          "id": "01",
          "title": "System Design 1: Design Tiny URL (with Advanced Design)/ URL Shortner / Bitly / TinyURL / HLD/ LLD",
          "videoUrl": "https://youtu.be/Y-BO_4XNw8c",
          "description": "In this System Design Interview video, we dive deep into designing a URL shortener like Bitly or TinyURL. \ud83d\ude80 This is a common system design question asked in FAANG and other top tech interviews.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/wenflwnflwenflwnflewn.jpg?updatedAt=1743325181394",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/wbrgibwrbgqwjbrgowqgnbeow.jpg?updatedAt=1769455579711",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 18,
            "lastUpdated": "2026-01-26",
            "hero": {
              "title": "URL Shortener (Bit.ly / TinyURL)",
              "oneLinerToMemorize": "Long URL \u2192 Server generates unique ID (Zookeeper counter/Snowflake) \u2192 Base62 encode \u2192 Store mapping (DB + Redis cache) \u2192 Redirect requests via short URL with analytics tracking"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Create a short url from a long url (e.g., https://www.facebook.com/anindya.s.dasgupta/ \u2192 http://bit.ly/4kycpo)",
                  "Feature 2: Optional: support custom url (user chooses short code like 'mylink' instead of random)",
                  "Feature 3: Optional: support expiration date (short URL expires after 30 days, 1 year, etc.)",
                  "Feature 4: User should get redirected to the original url from short url (301/302 redirect)",
                  "Feature 5: Optional: Track analytics (click count, user metadata, timestamps for each redirect)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Latency",
                    "pairs": [
                      {
                        "k": "Scale",
                        "v": "100M DAU / 1B url - massive read traffic (billions of redirects daily)"
                      },
                      {
                        "k": "Latency",
                        "v": "Low latency (url-creating, on redirect ~200ms) - redirect must be fast (<100ms)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Consistency & Availability",
                    "pairs": [
                      {
                        "k": "Short url uniqueness",
                        "v": "Short url should be unique (no collisions, two users can't get same short URL)"
                      },
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> Consistency (eventual consistency acceptable for analytics)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity (from image)",
                "type": "bullets",
                "items": [
                  "Entity 1: short_url - The shortened URL code (e.g., '4kycpo', '9i91c0')",
                  "Entity 2: long_url - Original full URL",
                  "Entity 3: user - User who created the short URL (optional, for tracking user's URLs)",
                  "Entity 4: From image shows: User metadata (user info), URL (shortURL, longURL, expirationDate, customURL)"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing (from image)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "API Creation (from image shows POST /s/url ->short-url)",
                    "pairs": [
                      {
                        "k": "POST /s/url ->short-url",
                        "v": "Create short URL from long URL with optional custom alias and expiration"
                      },
                      {
                        "k": "GET /s/url/{shortURL} ->longURL",
                        "v": "Get long URL for given short URL (for redirect or analytics)"
                      },
                      {
                        "k": "Request body example",
                        "v": "{longURL: 'https://www.facebook.com/...', customURL: 'myprofile' (optional), expirationDate: '2026-12-31' (optional)}"
                      },
                      {
                        "k": "Response example",
                        "v": "{shortURL: 'http://bit.ly/4kycpo', longURL: 'https://www.facebook.com/...', createdAt: '2026-01-26', expiresAt: '2026-12-31'}"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design (from image)",
                "type": "bullets",
                "items": [
                  "From image shows: Client \u2192 redirect(short) / longURL \u2192 Server (generate the short url) \u2192 Database (User metadata, URL shortURL/longURL/expirationDate/customURL)",
                  "Client: Web browser, mobile app, API client sending requests",
                  "Server: Generates short URL using counter/Snowflake ID, handles redirects, encryption/decryption",
                  "Database: Stores URL mappings (short \u2192 long), user metadata, expiration dates, custom URLs"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level - from image)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: URL Shortening - ID Generation (from image shows multiple approaches)",
                    "steps": [
                      "From image: Shows three approaches - Counter (Zookeeper), Snowflake, and Encryption Logic",
                      "Challenge: Generate unique short code (6-7 characters) for each long URL, must be collision-free",
                      "Approach 1 - Counter with Zookeeper (from image shows 'counter' with Zookeeper): Use distributed counter to generate sequential IDs, Base62 encode to short string. Flow: (1) User submits: POST /s/url with {longURL: 'https://www.facebook.com/anindya.s.dasgupta/'}, (2) Server requests counter: Zookeeper atomic increment: counter++ (e.g., counter = 3748547), (3) Base62 encode: encode(3748547) \u2192 '9i91c0' (6 chars), (4) Store mapping: INSERT INTO urls (short_url, long_url, created_at) VALUES ('9i91c0', 'https://www.facebook.com/...', now()), (5) Cache: SET url:9i91c0 {longURL, metadata} EX 86400 (24 hour TTL), (6) Response: {shortURL: 'http://bit.ly/9i91c0', longURL: '...'}",
                      "From image note: 'Counter' with 'Zms-3ms' latency, Redis shown for caching counter values",
                      "Base62 encoding: Characters: [0-9a-zA-Z] = 62 chars (0-9: 10, a-z: 26, A-Z: 26), Algorithm: num = 3748547; result = ''; while (num > 0) { result = chars[num % 62] + result; num = Math.floor(num / 62); }, Example: 3748547 \u2192 '9i91c0'. Length: 62^6 = 56 billion unique URLs (6 chars), 62^7 = 3.5 trillion (7 chars)",
                      "From image: 'Zookeeper: Zookeeper is a distributed, open-source coordination service for distributed applications, providing centralized services for configuration management, naming, synchronization, and group services, making it easy to build large clusters. Used for: maintaining configuration information, naming, providing distributed synchronization, and providing group service etc'",
                      "Zookeeper counter implementation: (1) Zookeeper node: /counters/url_counter with value = 0, (2) Atomic increment: zk.setData('/counters/url_counter', currentValue + 1), guaranteed atomic across all servers, (3) Multiple servers: All encryption servers read from same Zookeeper counter \u2192 no collisions, (4) High availability: Zookeeper cluster (3-5 nodes), if one fails \u2192 others maintain counter state",
                      "From image shows: Multiple encryption servers (round robin load balancing) \u2192 Zookeeper counter \u2192 ensures same counter value never reused"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Approach 2 - Snowflake ID (from image shows Snowflake with timestamp, workerID)",
                    "steps": [
                      "From image: 'Snowflake' with 'workerID: 123, workerID: 234, workerID: 345' and 'timestamp' component",
                      "Snowflake ID structure (64-bit): [1 bit unused][41 bits timestamp][10 bits workerID][12 bits sequence], Total: 64 bits = can represent 2^64 unique IDs",
                      "Components: (1) Timestamp (41 bits): Milliseconds since custom epoch (e.g., Jan 1, 2020), 41 bits \u2192 2^41 ms \u2248 69 years, (2) WorkerID (10 bits): Unique ID per server (0-1023), 10 bits \u2192 2^10 = 1024 workers, (3) Sequence (12 bits): Counter within same millisecond per worker, 12 bits \u2192 2^12 = 4096 IDs per ms per worker",
                      "Example generation: (1) Current time: Jan 26, 2026 10:30:45.123 \u2192 ms since epoch: 189432045123, (2) WorkerID: Server 123, (3) Sequence: 1st ID this millisecond \u2192 0, (4) Combine: ID = (189432045123 << 22) | (123 << 12) | 0 = 792345678901234567 (64-bit number), (5) Base62 encode: encode(792345678901234567) \u2192 'aBc123XyZ' (10 chars)",
                      "From image shows: Snowflake ID formula with bit positions: 2^41 / 365 / 24 / 3600 / 1000 = 69.73 Years, 41 bit timestamp",
                      "Advantages: (1) No coordination needed (unlike Zookeeper counter), each worker generates IDs independently, (2) Time-ordered: IDs increase monotonically (newer URLs have higher IDs), (3) High throughput: 4096 IDs/ms/worker \u00d7 1024 workers = 4M IDs/sec, (4) Decentralized: Workers don't communicate, no single point of failure",
                      "Disadvantages: (1) Longer short URLs: 64-bit number \u2192 10-11 chars in Base62 (vs 6-7 for counter), workaround: Use only lower 42 bits (still 69 years + 1024 workers + 1 sequence) \u2192 7-8 chars, (2) Clock dependency: Requires synchronized clocks (NTP), clock drift can cause collisions"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Approach 3 - Encryption/Hash Logic (from image shows 'sha1(longUrl) -> (shortURL, 6, 7)')",
                    "steps": [
                      "From image: 'Encryption Logic: sha1(longUrl) -> (shortURL, 6, 7) -> response' with note about encryption/decryption overhead",
                      "Hash-based approach: Hash long URL \u2192 extract 6-7 characters \u2192 use as short URL",
                      "Flow: (1) User submits: POST /s/url with {longURL: 'https://www.facebook.com/anindya.s.dasgupta/'}, (2) Hash URL: hash = SHA1(longURL) = 'd3e8f9a2b1c4e5f6a7b8c9d0e1f2a3b4c5d6e7f8' (40 hex chars), (3) Extract substring: shortCode = hash.substring(0, 7) = 'd3e8f9a' (7 chars), (4) Check collision: SELECT COUNT(*) FROM urls WHERE short_url='d3e8f9a', if exists \u2192 try hash.substring(7, 14), hash.substring(14, 21), etc. (multiple attempts), if all 5 attempts collide \u2192 append timestamp: hash(longURL + timestamp), (5) Store: INSERT INTO urls (short_url, long_url), (6) Response: {shortURL: 'http://bit.ly/d3e8f9a'}",
                      "From image note: 'Server - encryption (20%), decryption (80%)' - shows read-heavy workload",
                      "Collision handling: (1) Probability: SHA1 output space = 2^160, 7 chars from hash = 16^7 possible values, with 1M URLs \u2192 collision probability \u2248 (1M^2) / (2 \u00d7 16^7) \u2248 0.000002% (extremely low), (2) If collision detected: Try next 7 characters from hash, or rehash with salt: hash(longURL + random_salt), (3) Max retries: 5 attempts, if all fail \u2192 fall back to counter-based ID",
                      "Advantages: (1) No external dependency (no Zookeeper, no coordination), (2) Same long URL \u2192 same hash \u2192 same short URL (deterministic, deduplication), if user creates same long URL twice \u2192 returns existing short URL, (3) Stateless: Any server can generate short URL without querying database first",
                      "Disadvantages: (1) Collision risk: Need retry logic, more complex than counter, (2) Cannot guarantee short URL length (hash extraction might need 8-9 chars after retries), (3) No time-ordering: Random distribution, can't query 'recent URLs' easily"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: URL Redirect Flow (from image shows redirect path)",
                    "steps": [
                      "From image: Client \u2192 redirect(short) / longURL \u2192 Server \u2192 generatedLongURL \u2192 Database",
                      "User clicks short URL: GET http://bit.ly/9i91c0 (browser request)",
                      "Server redirect flow: (1) Extract short code: '9i91c0' from URL path, (2) Check Redis cache: GET url:9i91c0 \u2192 {longURL: 'https://www.facebook.com/anindya.s.dasgupta/', expiresAt: '2027-01-26'}, if cache hit (90% of requests) \u2192 skip DB query, (3) If cache miss: Query DB: SELECT long_url, expires_at FROM urls WHERE short_url='9i91c0', if not found \u2192 return 404 'Short URL not found', if found but expired (expires_at < now()) \u2192 return 410 'Link expired', if valid \u2192 cache result: SET url:9i91c0 {longURL, expiresAt} EX 86400, (4) Return redirect: HTTP 301 Moved Permanently with Location: https://www.facebook.com/anindya.s.dasgupta/, (5) Browser automatically redirects to long URL",
                      "From image note: 'encrypt: 1ms' for encryption, '5ms' for server to DB latency, total '=16ms + 16ms' round trip",
                      "301 vs 302 redirect: (1) 301 Permanent: Browser caches redirect, future clicks go directly to long URL (no server request), Pros: Reduces server load (cached redirects), Cons: Can't track analytics after first click (browser bypasses server), (2) 302 Temporary: Browser doesn't cache, every click goes through server, Pros: Can track every click for analytics, Cons: Higher server load, (3) Bit.ly uses 302 for analytics tracking",
                      "Performance: (1) Cache hit: <10ms (Redis lookup + redirect response), (2) Cache miss: ~50ms (DB query + Redis write + redirect), (3) 90% cache hit rate \u2192 avg latency ~15ms"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Custom Short URLs (from image shows customURL field)",
                    "steps": [
                      "From image: Database schema shows 'customURL' as optional field",
                      "User wants custom alias: POST /s/url with {longURL: 'https://myportfolio.com', customURL: 'mywork'}",
                      "Server validates custom URL: (1) Check length: 3-20 characters (enforce minimum to prevent single-char URLs like 'a'), (2) Check allowed chars: [a-zA-Z0-9-_] only (no special chars, no spaces), (3) Check profanity: Filter against blacklist (e.g., 'badword', 'spam'), (4) Check availability: SELECT COUNT(*) FROM urls WHERE short_url='mywork', if exists \u2192 return 409 'Custom URL already taken', (5) Reserve: INSERT INTO urls (short_url: 'mywork', long_url: 'https://myportfolio.com', is_custom: true)",
                      "Response: {shortURL: 'http://bit.ly/mywork', longURL: 'https://myportfolio.com'}",
                      "Collision with auto-generated: (1) Problem: Counter generates 'abc123', user later requests custom 'abc123' \u2192 collision, (2) Solution: Separate namespaces, Partition ID space: Counter uses range [0 - 100B], custom URLs are strings (can include hyphens, underscores not in Base62), OR prefix custom URLs: /c/mywork vs /a/abc123 (auto), (3) Bit.ly approach: No prefix, custom URLs checked first before auto-generation (custom has priority)"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Expiration & Cleanup (from image shows expirationDate)",
                    "steps": [
                      "From image: Database schema includes 'expirationDate' field",
                      "User creates URL with expiration: POST /s/url with {longURL: '...', expirationDate: '2026-12-31'}",
                      "Store: INSERT INTO urls (short_url, long_url, expires_at: '2026-12-31 23:59:59')",
                      "Redirect check: (1) User clicks short URL after expiration (Jan 1, 2027), (2) Server queries: SELECT long_url, expires_at FROM urls WHERE short_url='...', (3) Check: if expires_at < NOW() \u2192 return 410 Gone with message 'This link has expired', else redirect normally",
                      "From image shows: 'cron' job for cleanup",
                      "Cleanup job (cron): (1) Runs daily at 2 AM, (2) Query: SELECT short_url FROM urls WHERE expires_at < NOW() - INTERVAL '30 days' AND expires_at IS NOT NULL (find expired URLs older than 30 days), (3) Delete: DELETE FROM urls WHERE short_url IN (...) (batch delete 10K at a time), (4) Invalidate cache: DEL url:{short_url} for each deleted URL, (5) Reclaim IDs: Expired short URLs can be reused (counter doesn't go backward but hash space is reusable)",
                      "TTL optimization: (1) Default expiration: If user doesn't specify \u2192 expires_at = NULL (never expires), (2) Common presets: 1 day, 7 days, 30 days, 1 year, never, (3) Storage savings: Delete expired URLs frees DB space (with 1B URLs \u00d7 1KB = 1TB, deleting 10% = 100GB saved)"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Analytics Tracking (from image shows metadata)",
                    "steps": [
                      "From image: Database stores 'User metadata' for analytics",
                      "Track redirect events: (1) User clicks: GET http://bit.ly/9i91c0, (2) Server logs event: INSERT INTO analytics (short_url, timestamp, ip_address, user_agent, referrer, country, device_type), Example: {short_url: '9i91c0', timestamp: '2026-01-26 10:30:45', ip: '203.0.113.5', user_agent: 'Mozilla/5.0...', referrer: 'https://twitter.com', country: 'US' (from IP geolocation), device_type: 'mobile' (from user agent)}, (3) Async insert: Don't block redirect, publish to Kafka \u2192 Analytics Consumer inserts to DB",
                      "Kafka event: {event_type: 'url_click', short_url: '9i91c0', timestamp, metadata: {ip, user_agent, referrer}}",
                      "Analytics aggregation: (1) Real-time: Redis counters: INCR click_count:9i91c0 (total clicks), HINCRBY click_count:9i91c0:daily {date} 1 (clicks per day), PFADD unique_users:9i91c0 {ip} (HyperLogLog for unique visitors), (2) Batch: Hourly job aggregates: SELECT short_url, COUNT(*) as clicks, COUNT(DISTINCT ip) as unique_users FROM analytics WHERE timestamp >= NOW() - INTERVAL '1 hour' GROUP BY short_url, UPDATE urls SET click_count = click_count + {clicks}",
                      "Dashboard: GET /api/v1/analytics/{shortURL} returns: {totalClicks: 15234, uniqueVisitors: 8765, clicksByDate: {'2026-01-25': 543, '2026-01-26': 432}, topReferrers: [{'twitter.com': 4532}, {'facebook.com': 2341}], topCountries: [{'US': 6543}, {'IN': 3421}], deviceBreakdown: {'mobile': 60%, 'desktop': 35%, 'tablet': 5%}}",
                      "Performance: (1) Async logging: Redirect responds in <10ms, analytics inserted later (doesn't block user), (2) Sampling: For very popular URLs (>1M clicks/day), sample 10% of clicks (reduces storage, maintains accuracy)"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Caching Strategy (from image shows Redis with 2ms latency)",
                    "steps": [
                      "From image: Redis shown with '2ms' latency for caching layer",
                      "Cache design: (1) Key: url:{short_url} e.g., url:9i91c0, (2) Value: JSON {longURL, expiresAt, isCustom}, (3) TTL: 24 hours (86400 sec), popular URLs stay cached, unpopular expire and free memory",
                      "Cache flow: (1) Redirect request: GET url:9i91c0 from Redis, if hit \u2192 return longURL (<1ms), if miss \u2192 query DB \u2192 cache result: SET url:9i91c0 {longURL, expiresAt} EX 86400 \u2192 return longURL (~50ms first request)",
                      "Cache hit rate: (1) Target: 90%+ hit rate, (2) Popular URLs: Clicked thousands of times/day \u2192 always cached (TTL refreshed on each access), (3) Unpopular URLs: Clicked once a month \u2192 may not be cached (acceptable, DB can handle 10% traffic)",
                      "Write-through cache: (1) URL creation: INSERT INTO urls (...) \u2192 immediately SET url:{short_url} (...) EX 86400 (cache on creation), (2) Ensures first redirect is fast (no cache miss)",
                      "Cache invalidation: (1) URL updated: User changes longURL or expiration \u2192 UPDATE urls \u2192 DEL url:{short_url} (invalidate cache, next request rebuilds), (2) URL deleted: DELETE FROM urls \u2192 DEL url:{short_url}, (3) Expiration: Cron deletes expired URLs \u2192 DEL url:{short_url}",
                      "Bloom filter optimization: (1) Problem: Cache miss for non-existent short URL \u2192 DB query returns nothing \u2192 wasted query, (2) Solution: Bloom filter (5MB for 10M URLs, 1% false positive rate), (3) Check Bloom filter before DB: if bf.contains('xyz123') == false \u2192 return 404 immediately (no DB query), if bf.contains('xyz123') == true \u2192 might exist, query DB, (4) Update Bloom filter: On URL creation \u2192 bf.add(short_url)"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Load Balancing & High Availability (from image shows load balancer)",
                    "steps": [
                      "From image: Client \u2192 Load Balancer (round robin) \u2192 Multiple Encryption Servers",
                      "Load balancer: (1) Round robin: Distributes requests evenly across servers (Server 1 \u2192 Server 2 \u2192 Server 3 \u2192 Server 1), (2) Health checks: Ping each server every 10 sec, if server unresponsive \u2192 remove from pool, (3) Sticky sessions: Not needed (stateless servers, any server can handle any request)",
                      "Server scaling: (1) URL creation: CPU-bound (Base62 encoding, DB write), 10 servers \u00d7 1000 req/sec = 10K URL creations/sec, (2) Redirects: I/O-bound (Redis/DB read), 100 servers \u00d7 10K req/sec = 1M redirects/sec, (3) Auto-scale: If CPU > 80% \u2192 add servers, if CPU < 20% \u2192 remove servers",
                      "Database replication: (1) Master-slave: Master handles writes (URL creation), 5 slaves handle reads (redirects), (2) Read/write split: URL creation \u2192 master, redirects \u2192 slaves (round robin across slaves), (3) Replication lag: <1 sec acceptable (user creates URL \u2192 might not redirect immediately for 1 sec \u2192 eventually consistent)",
                      "Zookeeper cluster: (1) 5 nodes: Quorum = 3 (majority), can tolerate 2 node failures, (2) Leader election: One node is leader (handles counter increments), if leader fails \u2192 new leader elected (<10 sec), (3) Counter persistence: Zookeeper persists counter to disk, survives cluster restart"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Security & Abuse Prevention",
                    "steps": [
                      "Rate limiting: (1) Per IP: 100 URL creations/hour (prevent spam), Redis: INCR rate_limit:{ip} EX 3600, if count > 100 \u2192 return 429 'Too many requests', (2) Per user: Authenticated users get 1000/hour (higher limit), (3) Per short URL: 10K clicks/min (prevent DDoS via popular short URL)",
                      "Malicious URL detection: (1) Blacklist check: Query Google Safe Browsing API: POST https://safebrowsing.googleapis.com/v4/threatMatches:find with {url: longURL}, if response.matches.length > 0 \u2192 return 400 'Malicious URL detected', (2) Phishing patterns: Regex check for common phishing domains (e.g., 'paypa1.com' instead of 'paypal.com'), (3) User reports: If URL reported 10 times \u2192 flag for manual review, if confirmed malicious \u2192 DELETE short URL",
                      "CAPTCHA: (1) Trigger: If IP creates 10 URLs in 1 minute \u2192 require CAPTCHA, (2) reCAPTCHA v3: Invisible, scores user 0-1 (bot likelihood), if score < 0.5 \u2192 challenge with image CAPTCHA, (3) Prevents: Bots generating millions of short URLs (spam, abuse)",
                      "URL validation: (1) Length: longURL < 2048 chars (browser limit), (2) Protocol: Must start with http:// or https:// (no javascript:, data:, file: schemes \u2192 XSS prevention), (3) Domain: Valid DNS (resolve domain name, if fails \u2192 invalid URL)",
                      "Authentication: (1) Optional: Allow anonymous URL creation (like Bit.ly), (2) Authenticated users: OAuth login (Google, GitHub), benefits: (a) Track user's URLs (GET /api/v1/users/{userId}/urls), (b) Edit/delete URLs, (c) Private URLs (only creator can access analytics), (3) API keys: For programmatic access, rate limit per API key (10K req/day)"
                    ]
                  },
                  {
                    "blockTitle": "Step 11: Database Schema Design",
                    "steps": [
                      "From image: Shows 'User metadata', 'URL (shortURL, longURL, expirationDate, customURL)'",
                      "URLs table: CREATE TABLE urls (short_url varchar(10) PRIMARY KEY, long_url text NOT NULL, user_id uuid (nullable, for authenticated users), created_at timestamptz NOT NULL, expires_at timestamptz (nullable), is_custom boolean DEFAULT false, click_count bigint DEFAULT 0, INDEX on (user_id, created_at) for user's URL listing, INDEX on (expires_at) for cleanup job);",
                      "Analytics table: CREATE TABLE analytics (id bigserial PRIMARY KEY, short_url varchar(10) NOT NULL, clicked_at timestamptz NOT NULL, ip_address inet, user_agent text, referrer text, country varchar(2), device_type varchar(20), INDEX on (short_url, clicked_at) for time-series queries, PARTITION BY RANGE (clicked_at) (analytics_2026_01, analytics_2026_02) for performance);",
                      "Users table (optional): CREATE TABLE users (user_id uuid PRIMARY KEY, email varchar(255) UNIQUE, created_at timestamptz, quota_used int DEFAULT 0, quota_limit int DEFAULT 1000);",
                      "Sharding strategy: (1) Shard URLs by short_url hash: hash('9i91c0') mod 10 \u2192 shard 3, (2) 10 shards: Each handles 100M URLs (total 1B URLs), (3) Cross-shard queries: User's URLs (user_id) \u2192 query all shards, aggregate results (acceptable for small result set)",
                      "Storage estimation: (1) 1 URL: short_url (10 bytes) + long_url (500 bytes avg) + metadata (100 bytes) \u2248 610 bytes, (2) 1B URLs: 610 bytes \u00d7 1B = 610 GB (~600 GB), (3) With indexes (30% overhead): 600 GB \u00d7 1.3 = 780 GB, (4) Analytics: 1B clicks/day \u00d7 200 bytes = 200 GB/day, with 90 day retention = 18 TB (partition by month, archive old data to S3)"
                    ]
                  },
                  {
                    "blockTitle": "Step 12: Encoding Comparison (Base62 vs Base64 vs Hex)",
                    "steps": [
                      "Base62: (1) Characters: [0-9a-zA-Z] = 62 chars, (2) Example: encode(3748547) \u2192 '9i91c0' (6 chars), (3) Pros: URL-safe (no special chars), human-readable (no confusing chars like 0 vs O), short output (62^6 = 56B URLs), (4) Cons: Case-sensitive (9i91c0 \u2260 9I91C0, users may mistype)",
                      "Base64: (1) Characters: [0-9a-zA-Z+/] = 64 chars, (2) Example: encode(3748547) \u2192 '9i91c+' (6 chars), (3) Pros: Slightly more compact than Base62, standard encoding (libraries available), (4) Cons: Not URL-safe (+ and / need escaping: %2B, %2F \u2192 makes URL longer), less human-readable",
                      "Hexadecimal (Base16): (1) Characters: [0-9a-f] = 16 chars, (2) Example: encode(3748547) \u2192 '392b23' (6 chars for small numbers, but 3748547 in hex = '392b23' also 6 chars), (3) Pros: Case-insensitive (392b23 = 392B23), very simple, (4) Cons: Much longer for large numbers (16^6 = 16M URLs vs 62^6 = 56B), need 8-10 chars for 1B URLs",
                      "Why Base62 chosen: (1) Optimal balance: URL-safe + short + readable, (2) 62^7 = 3.5 trillion URLs (enough for decades), (3) Bit.ly, TinyURL, Google (goo.gl) all use Base62, (4) Case-sensitive caveat: Use lowercase-only variant [0-9a-z] (36 chars) for case-insensitive systems (36^7 = 78B URLs, still enough)"
                    ]
                  },
                  {
                    "blockTitle": "Step 13: Monitoring & Debugging",
                    "steps": [
                      "Metrics: (1) URL creation rate: URLs created/sec (track spikes, capacity planning), (2) Redirect latency: p50, p95, p99 latency for redirects, (3) Cache hit rate: Redis hit rate (should be >90%), (4) Error rate: 404 rate (broken links), 410 rate (expired links), 5xx errors, (5) Popular URLs: Top 100 URLs by click count (identify viral links)",
                      "Dashboards: (1) Real-time: Current requests/sec, active connections, server health, (2) Historical: Daily URL creation trend, redirect volume over time, (3) Alerts: If error rate > 1% \u2192 alert, if cache hit rate < 80% \u2192 alert (Redis issue?), if Zookeeper down \u2192 alert (critical, counter unavailable)",
                      "Logging: (1) Access logs: Timestamp, IP, short_url, status_code, latency, (2) Error logs: Stack traces for 5xx errors, invalid URL requests, (3) Audit logs: URL creations (who, when, what), deletions, updates, (4) Retention: Access logs 7 days (high volume), error logs 30 days, audit logs 1 year",
                      "Debugging: (1) Short URL not working: Check DB: SELECT * FROM urls WHERE short_url='...', check Redis: GET url:..., check expiration: expires_at < now()?, (2) Redirect slow: Check cache hit: Did request hit Redis or DB?, check DB replication lag, check network latency, (3) Counter collision: Check Zookeeper: Is counter value correct?, check for duplicate short_url in DB (should be impossible with unique constraint)",
                      "Testing: (1) Load test: Simulate 100K req/sec redirects (JMeter, Locust), measure latency, identify bottlenecks, (2) Chaos engineering: Kill random server, kill Zookeeper leader, partition network \u2192 system should recover gracefully, (3) Edge cases: Expired URL redirect, custom URL collision, very long URL (2KB), malicious URL detection"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details (from image)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "URLs Table (Primary storage)",
                    "pairs": [
                      {
                        "k": "short_url",
                        "v": "varchar(10) PRIMARY KEY - Unique short code (e.g., '9i91c0', 'mywork')"
                      },
                      {
                        "k": "long_url",
                        "v": "text NOT NULL - Original URL (up to 2048 chars)"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid (nullable) - FK to Users table for authenticated users"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz NOT NULL - When short URL was created"
                      },
                      {
                        "k": "expires_at",
                        "v": "timestamptz (nullable) - Expiration date, NULL = never expires"
                      },
                      {
                        "k": "is_custom",
                        "v": "boolean DEFAULT false - True if user chose custom alias"
                      },
                      {
                        "k": "click_count",
                        "v": "bigint DEFAULT 0 - Total clicks (aggregated from analytics)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (user_id, created_at) for user's URLs, INDEX on (expires_at) for cleanup job"
                      },
                      {
                        "k": "Sharding",
                        "v": "Shard by hash(short_url) mod N for horizontal scaling"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Analytics Table (Click tracking)",
                    "pairs": [
                      {
                        "k": "id",
                        "v": "bigserial PRIMARY KEY"
                      },
                      {
                        "k": "short_url",
                        "v": "varchar(10) NOT NULL - FK to URLs table"
                      },
                      {
                        "k": "clicked_at",
                        "v": "timestamptz NOT NULL - When redirect happened"
                      },
                      {
                        "k": "ip_address",
                        "v": "inet - User IP (for geolocation, unique visitor count)"
                      },
                      {
                        "k": "user_agent",
                        "v": "text - Browser/device info"
                      },
                      {
                        "k": "referrer",
                        "v": "text - Where user came from (twitter.com, facebook.com)"
                      },
                      {
                        "k": "country",
                        "v": "varchar(2) - Country code from IP geolocation (US, IN, UK)"
                      },
                      {
                        "k": "device_type",
                        "v": "varchar(20) - mobile, desktop, tablet, bot"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (short_url, clicked_at) for time-series queries"
                      },
                      {
                        "k": "Partitioning",
                        "v": "PARTITION BY RANGE (clicked_at) - Monthly partitions (analytics_2026_01, analytics_2026_02)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Users Table (Optional - for authenticated users)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE - User email"
                      },
                      {
                        "k": "oauth_provider",
                        "v": "varchar(50) - google, github, twitter"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "quota_used",
                        "v": "int DEFAULT 0 - URLs created this month"
                      },
                      {
                        "k": "quota_limit",
                        "v": "int DEFAULT 1000 - Monthly creation limit"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Caching (from image shows Redis with 2ms latency)",
                    "pairs": [
                      {
                        "k": "url:{shortURL}",
                        "v": "STRING (JSON) - {longURL, expiresAt, isCustom} - TTL 24 hours (86400 sec)"
                      },
                      {
                        "k": "click_count:{shortURL}",
                        "v": "INT - Real-time click counter (INCR on each redirect)"
                      },
                      {
                        "k": "click_count:{shortURL}:daily",
                        "v": "HASH - {date: count} - Clicks per day (HINCRBY)"
                      },
                      {
                        "k": "unique_users:{shortURL}",
                        "v": "HYPERLOGLOG - Unique visitor count (PFADD ip_address)"
                      },
                      {
                        "k": "rate_limit:{ip}",
                        "v": "INT - Rate limiting counter, TTL 3600 sec (1 hour)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Zookeeper - Counter (from image shows Zookeeper for distributed counter)",
                    "pairs": [
                      {
                        "k": "Node path",
                        "v": "/counters/url_counter"
                      },
                      {
                        "k": "Value",
                        "v": "Current counter (e.g., 3748547), atomically incremented"
                      },
                      {
                        "k": "Purpose",
                        "v": "Distributed counter for sequential ID generation across multiple servers"
                      },
                      {
                        "k": "Cluster",
                        "v": "3-5 Zookeeper nodes for high availability, quorum = majority"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "id-generation-comparison",
                "title": "8. ID Generation Approaches - Deep Comparison (from image shows 3 approaches)",
                "type": "bullets",
                "items": [
                  "From image shows: Three distinct approaches - Counter (Zookeeper), Snowflake, Hash-based (sha1)",
                  "Approach 1 - Counter with Zookeeper (from image): Centralized counter incremented atomically. Flow: Server \u2192 Zookeeper: increment counter \u2192 get 3748547 \u2192 Base62 encode \u2192 '9i91c0'. Pros: (1) Sequential IDs (predictable, time-ordered), (2) Shortest possible URLs (6-7 chars for billions of URLs), (3) Guaranteed uniqueness (atomic increment = no collisions). Cons: (1) Single point of coordination (Zookeeper required, adds latency ~3ms), (2) Bottleneck: All servers must query same counter (though Zookeeper handles 10K+ req/sec), (3) Dependency: If Zookeeper down \u2192 cannot create URLs (mitigated by Zookeeper cluster). When to use: Production systems requiring shortest URLs (Bit.ly, TinyURL use this), can tolerate Zookeeper dependency.",
                  "Approach 2 - Snowflake ID (from image shows timestamp + workerID): Decentralized ID generation per server. Flow: Server generates 64-bit ID locally (timestamp + workerID + sequence) \u2192 Base62 encode \u2192 'aBc123XyZ'. Structure: [41 bits timestamp][10 bits workerID][12 bits sequence] = 69 years + 1024 workers + 4096 IDs/ms. Pros: (1) No coordination needed (each worker independent), (2) High throughput (4096 IDs/ms/worker = 4M IDs/sec across 1K workers), (3) No single point of failure (workers don't communicate), (4) Time-ordered (IDs increase monotonically). Cons: (1) Longer URLs (10-11 chars vs 6-7 for counter), (2) Clock dependency (requires NTP synchronization, clock drift = collisions), (3) WorkerID management (must assign unique ID to each server). When to use: High-throughput systems, decentralized architecture, can tolerate longer URLs (Twitter uses Snowflake for tweet IDs).",
                  "Approach 3 - Hash-based (from image shows 'sha1(longUrl)'): Hash long URL, extract substring as short code. Flow: SHA1(longURL) \u2192 'd3e8f9a2b1c4e5f6...' (40 hex chars) \u2192 substring(0, 7) \u2192 'd3e8f9a'. Pros: (1) Deterministic (same long URL \u2192 same short URL = deduplication), if user creates same URL twice \u2192 returns existing short URL without DB check, (2) No external dependency (no Zookeeper, no coordination), (3) Stateless (any server can generate without querying others). Cons: (1) Collision risk (birthday paradox: ~1M URLs \u2192 0.01% collision probability with 7-char hash), need retry logic: try substring(7, 14), (14, 21), etc., (2) Cannot guarantee URL length (retries may need 8-9 chars), (3) No time-ordering (random distribution). When to use: Systems where deduplication is valuable, can tolerate retry logic, lower creation volume (<1M URLs).",
                  "From image note: Encryption overhead shown as 'encrypt: 1ms' for hash approach, 'Zms-3ms' for Zookeeper approach",
                  "Production choices: (1) Bit.ly: Counter-based (Zookeeper-like system), prioritizes shortest URLs, (2) Twitter: Snowflake for tweets, high throughput > short length, (3) Google (goo.gl, discontinued): Hash-based with retry, leveraged deduplication, (4) Hybrid approach: Use counter for normal users, Snowflake for API/high-volume users (different URL prefixes: /s/ vs /a/)",
                  "Collision handling: Counter approach: No collisions (atomic increment guarantees uniqueness). Snowflake: Collision if (timestamp, workerID, sequence) tuple repeats \u2192 requires unique workerID + clock sync. Hash approach: Collision probability = (n^2) / (2 \u00d7 62^k) where n=URLs, k=chars, with k=7, n=1M \u2192 0.01% collision \u2192 retry with different substring or append timestamp salt.",
                  "Migration strategy: (1) Start with counter (simple, reliable), (2) If Zookeeper becomes bottleneck (>100K URL creations/sec) \u2192 migrate to Snowflake (allocate workerID to each server), (3) If deduplication becomes important (same URLs shortened repeatedly) \u2192 add hash-based caching layer (check if long URL already shortened before generating new ID)"
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "9. Scaling & Optimization Techniques",
                "type": "bullets",
                "items": [
                  "Technique 1: Base62 encoding - Converts sequential counter (3748547) to short string ('9i91c0'), 6 chars = 56B URLs, URL-safe and human-readable",
                  "Technique 2: Redis caching - 90% cache hit rate, <10ms redirect latency, 24-hour TTL balances freshness and memory",
                  "Technique 3: Zookeeper counter - Distributed atomic counter, guarantees unique IDs across servers, 3-5 node cluster for high availability",
                  "Technique 4: Snowflake ID - Decentralized generation, 4M IDs/sec, no coordination, 64-bit structure (timestamp + workerID + sequence)",
                  "Technique 5: Database sharding - Shard by hash(short_url), 10 shards = 100M URLs each, scales to billions",
                  "Technique 6: Read replicas - 1 master (writes) + 5 slaves (reads), read/write split, handles 1M redirects/sec",
                  "Technique 7: Bloom filter - 5MB filter for 10M URLs, avoids DB queries for non-existent short URLs, 1% false positive acceptable",
                  "Technique 8: Analytics partitioning - Monthly partitions (analytics_2026_01), archive old data to S3, keeps recent queries fast",
                  "Technique 9: Async analytics - Kafka event stream, don't block redirect, insert analytics later, <10ms redirect latency",
                  "Technique 10: Rate limiting - 100 URLs/hour per IP, prevents spam, Redis INCR with TTL, CAPTCHA for suspicious traffic",
                  "Technique 11: CDN for redirects - Cache popular short URLs at edge, <50ms global latency, reduces server load 80%",
                  "Technique 12: Custom URL namespace - Separate custom from auto-generated, prevents collisions, custom gets priority"
                ]
              },
              {
                "id": "interview-qa",
                "title": "10. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you generate unique short URLs? Compare different approaches (counter, hash, Snowflake).",
                    "a": "Three main approaches for unique short URL generation: Approach 1 - Counter with Zookeeper (from image): Centralized distributed counter. Implementation: (1) Zookeeper maintains counter: /counters/url_counter with current value (e.g., 3748547), (2) Server requests ID: zk.getData('/counters/url_counter') \u2192 3748547, zk.setData('/counters/url_counter', 3748548) (atomic increment), (3) Base62 encode: encode(3748547) \u2192 characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', num = 3748547; result = ''; while (num > 0) { result = chars[num % 62] + result; num = floor(num / 62); } \u2192 result = '9i91c0', (4) Store: INSERT INTO urls (short_url: '9i91c0', long_url: 'https://...'), (5) Response: {shortURL: 'http://bit.ly/9i91c0'}. Uniqueness guarantee: Zookeeper atomic increment ensures no two servers get same counter value, even under concurrent requests. Zookeeper cluster (3-5 nodes) maintains consistency via Paxos consensus, if leader fails \u2192 new leader elected, counter state preserved. Base62 encoding: 62^6 = 56.8 billion unique URLs with 6 characters, 62^7 = 3.5 trillion with 7 characters, sufficient for decades. Pros: (1) Shortest possible URLs (6-7 chars), optimal for user experience, (2) Sequential IDs (time-ordered, can query 'recent URLs'), (3) Guaranteed uniqueness (no collision handling needed), (4) Predictable capacity (know exactly when to add more chars). Cons: (1) Zookeeper dependency (single point of coordination, adds 2-3ms latency), (2) Bottleneck: All servers query same counter (though Zookeeper handles 10K+ req/sec, can scale to 100K with read replicas), (3) If Zookeeper down \u2192 cannot create URLs (mitigated by cluster). Production: Bit.ly uses counter-based approach. Approach 2 - Hash-based (SHA1/MD5): Hash long URL, extract substring. Implementation: (1) Hash URL: hash = SHA1('https://www.facebook.com/...') = 'd3e8f9a2b1c4e5f6a7b8c9d0e1f2a3b4c5d6e7f8' (160 bits = 40 hex chars), (2) Extract 7 chars: shortCode = hash.substring(0, 7) = 'd3e8f9a', (3) Check collision: SELECT COUNT(*) FROM urls WHERE short_url='d3e8f9a', if count > 0: Try next 7 chars: hash.substring(7, 14), hash.substring(14, 21), ... (up to 5 attempts), if all collide: Append timestamp: hash(longURL + currentTimestamp), (4) Store: INSERT INTO urls (short_url: 'd3e8f9a', long_url: 'https://...'), (5) Response: {shortURL: 'http://bit.ly/d3e8f9a'}. Collision probability: SHA1 output space = 2^160, 7 hex chars = 16^7 = 268M possible values, with 1M URLs created: collision_prob = (1M^2) / (2 \u00d7 268M) \u2248 0.002% (extremely low), with 10M URLs: \u2248 0.2% (need retries). Pros: (1) Deterministic (same long URL \u2192 same hash \u2192 same short URL), enables deduplication: if user creates same URL twice \u2192 SELECT * FROM urls WHERE long_url='...' \u2192 return existing short URL (no new creation), (2) No external dependency (no Zookeeper, no coordination), each server generates independently, (3) Stateless (any server can generate without DB query first), (4) Deduplication saves storage (10% of URLs are duplicates \u2192 10% storage saving). Cons: (1) Collision risk (requires retry logic, complex), (2) Cannot guarantee URL length (after retries might need 8-9 chars), (3) No time-ordering (hash output is random), (4) More DB queries (check collision on each attempt). Production: Google's goo.gl used hash-based approach (now discontinued). Approach 3 - Snowflake ID (Twitter-style): Decentralized 64-bit ID generation. Structure: [1 bit unused][41 bits timestamp][10 bits workerID][12 bits sequence], Components: (1) Timestamp (41 bits): Milliseconds since custom epoch (e.g., Jan 1, 2020 00:00:00 UTC), 41 bits \u2192 2^41 ms \u2248 69 years, current time: Jan 26, 2026 10:30:45.123 \u2192 ms since epoch: 189432045123, (2) WorkerID (10 bits): Unique ID per server (0-1023), assigned during server startup (e.g., from config, environment variable, or auto-assigned), 10 bits \u2192 2^10 = 1024 workers (servers), (3) Sequence (12 bits): Counter within same millisecond per worker, resets every millisecond, 12 bits \u2192 2^12 = 4096 IDs per millisecond per worker. Generation: (1) Get current timestamp: ts = currentTimeMillis() - CUSTOM_EPOCH, (2) Check sequence: if (lastTimestamp == ts) { sequence = (sequence + 1) & 0xFFF; if (sequence == 0) wait until next ms; } else { sequence = 0; lastTimestamp = ts; }, (3) Combine bits: id = (ts << 22) | (workerID << 12) | sequence, Example: ts=189432045123, workerID=123, sequence=0 \u2192 id = (189432045123 << 22) | (123 << 12) | 0 = 792345678901234567 (64-bit number), (4) Base62 encode: encode(792345678901234567) \u2192 'aBc123XyZ' (10 chars). Pros: (1) No coordination (each worker generates IDs independently, no Zookeeper), (2) High throughput: 4096 IDs/ms/worker \u00d7 1024 workers = 4M IDs/sec, (3) No single point of failure (workers don't communicate), (4) Time-ordered (IDs increase monotonically, newer URLs have higher IDs), (5) Scalable (add more workers up to 1024). Cons: (1) Longer URLs: 64-bit number \u2192 10-11 chars in Base62 (vs 6-7 for counter), can mitigate by using only lower 42 bits (still 69 years) \u2192 7-8 chars, (2) Clock dependency: Requires synchronized clocks (NTP), clock drift/going backward can cause collisions, (3) WorkerID management: Must assign unique ID to each server (manual config or centralized registry). Production: Twitter uses Snowflake for tweet IDs (IDs visible in URLs). Decision matrix: Choose Counter if: (1) Need shortest URLs (6-7 chars critical), (2) Can tolerate Zookeeper dependency, (3) Creation rate <100K/sec. Choose Hash if: (1) Deduplication important (same URLs shortened repeatedly), (2) Want stateless generation (no coordination), (3) Can handle retry logic. Choose Snowflake if: (1) High throughput (>100K creations/sec), (2) Want decentralization (no coordination), (3) Can tolerate longer URLs (10-11 chars). Hybrid approach: Use counter for normal users (/s/ prefix), Snowflake for API/high-volume users (/a/ prefix), best of both worlds."
                  },
                  {
                    "q": "How do you handle 1 billion redirects per day with low latency (<100ms)?",
                    "a": "Handling 1B redirects/day with low latency requires aggressive caching and optimized architecture: Scale calculation: 1B redirects/day = 1,000,000,000 / 86400 sec \u2248 11,500 req/sec average, Peak (10\u00d7 average) = 115,000 req/sec during traffic spikes. Caching strategy (critical from image shows Redis with 2ms latency): Redis cache layer stores URL mappings in-memory. Cache design: (1) Key: url:{short_url}, Example: url:9i91c0, (2) Value: JSON {longURL: 'https://www.facebook.com/...', expiresAt: '2027-01-26', isCustom: false}, (3) TTL: 86400 sec (24 hours), Popular URLs stay cached (TTL refreshed on access), Unpopular URLs expire after 24h (free memory). Redirect flow with caching: (1) User clicks: GET http://bit.ly/9i91c0, (2) Server extracts short code: '9i91c0', (3) Redis lookup: result = redis.get('url:9i91c0'), if (result != null): Parse JSON, check expiration: if (expiresAt && expiresAt < now()) return 410 'Link expired', else return 301 redirect to longURL, Latency: <1ms (Redis in-memory lookup), (4) Cache miss (10% of requests): Query database: SELECT long_url, expires_at FROM urls WHERE short_url='9i91c0', if not found: return 404 'Short URL not found', if found: Write to cache: redis.set('url:9i91c0', JSON.stringify({longURL, expiresAt}), 'EX', 86400), return 301 redirect, Latency: ~50ms (DB query + Redis write + redirect), (5) Browser redirects to long URL automatically. Cache hit rate optimization: Target: >90% hit rate (90% requests served from Redis, 10% hit DB). Popular URLs: Clicked thousands of times/day \u2192 always in cache (TTL refreshed on each access via GET), Example: Viral link shared on Twitter \u2192 1M clicks/day \u2192 cached after first request, next 999,999 requests from cache (<1ms each). Unpopular URLs: Clicked once a month \u2192 may not be cached \u2192 acceptable (DB can handle 10% of traffic = 11.5K req/sec), Example: Old link from 2020 \u2192 user clicks \u2192 cache miss \u2192 DB query \u2192 cache for 24h \u2192 if no more clicks \u2192 expires from cache. Write-through caching: On URL creation: INSERT INTO urls (...), Immediately: redis.set('url:' + shortURL, ..., 'EX', 86400), Ensures first redirect is fast (no cache miss). Database optimization: Read replicas: 1 master (writes: URL creation) + 5 read replicas (reads: redirects), Read/write split: URL creation \u2192 master, Redirects \u2192 replicas (round-robin load balancing), Each replica handles: 11.5K req/sec \u00f7 5 = 2.3K req/sec (easily handled by PostgreSQL). Connection pooling: Each application server: 50 DB connections (connection pool), Reuse connections across requests (avoid connection overhead ~10ms), PgBouncer: DB connection pooler (100 connections from 1000 app servers \u2192 100 connections to DB = connection multiplexing). Indexes: PRIMARY KEY on short_url (B-tree index, O(log N) lookup), With 1B URLs: Index depth \u2248 4-5 levels, Query time: <10ms even at 1B scale. Database sharding (if needed): Shard by hash(short_url) mod 10 \u2192 10 shards, Each shard: 100M URLs, Query routing: Determine shard from short code, route request to correct shard. Application server scaling: Redirect handling: CPU-light (Redis lookup, JSON parse), I/O-bound (network calls to Redis/DB), Servers: 100 servers \u00d7 1K req/sec = 100K req/sec capacity (handles peak 115K with headroom). Auto-scaling: If CPU > 70% \u2192 add servers (horizontal scaling), If CPU < 30% \u2192 remove servers (cost optimization), Kubernetes: Auto-scale based on CPU/memory metrics. Load balancing: Round-robin: Distributes requests evenly across 100 servers, Health checks: Ping each server every 10 sec, remove unhealthy servers from pool, Sticky sessions: NOT needed (stateless, any server can handle any request). Redis cluster: Single Redis instance: 10K-50K req/sec (bottleneck at high scale), Redis Cluster: 10 master nodes + 10 replicas, Sharding: Shard by short_url hash, each node handles 11.5K req/sec (total 115K req/sec), Replication: Each master has 1 replica (failover if master crashes). Bloom filter optimization (avoid DB queries for non-existent URLs): Problem: User typos short URL: http://bit.ly/xyz123 (doesn't exist), Cache miss \u2192 DB query \u2192 returns nothing \u2192 wasted query (happens 1% of requests = 1.15K req/sec). Solution: Bloom filter (probabilistic data structure), Size: 5 MB for 10M URLs (1% false positive rate), Check: if (bloomFilter.contains('xyz123') == false) return 404 immediately (no Redis/DB query), if (bloomFilter.contains('xyz123') == true) might exist \u2192 proceed with Redis/DB query. Benefit: Eliminates 1% of unnecessary DB queries = 1.15K req/sec saved, False positives: 1% of bloom filter checks \u2192 query DB anyway (acceptable overhead). CDN for popular URLs (optional): Cache redirects at CDN edge: For top 1000 most-clicked URLs, Cloudflare/Fastly caches 301 redirects at edge (200+ global locations), User request \u2192 CDN edge \u2192 cached redirect (no origin server request), Latency: <50ms globally (vs 100-200ms to origin), Benefit: Reduces origin server load 80% (top 1000 URLs account for 80% of traffic). Latency breakdown: Cache hit (90% requests): Redis lookup: 0.5ms, JSON parse: 0.1ms, Redirect response: 0.5ms, Total: ~1-2ms (well under 100ms target). Cache miss (10% requests): DB query: 30ms (replica, indexed lookup), Redis write: 0.5ms, Redirect response: 0.5ms, Total: ~31ms (still under 100ms). Monitoring: Track p50, p95, p99 latency: p50: <5ms (median, mostly cache hits), p95: <50ms (some cache misses), p99: <100ms (DB queries, acceptable tail latency), Alert if p99 > 100ms (investigate: DB slow? Redis down? Network issue?). Result: System handles 1B redirects/day (115K req/sec peak) with <100ms latency, 90%+ cache hit rate = <2ms for most requests, 10% cache misses = <50ms (DB optimized), scales horizontally (add servers/Redis nodes as needed)."
                  },
                  {
                    "q": "How do you prevent abuse (spam URLs, malicious links) and implement rate limiting?",
                    "a": "Comprehensive abuse prevention with multiple layers: Rate limiting (prevent spam URL creation): Per-IP rate limiting: (1) Limit: 100 URL creations per hour per IP address, (2) Implementation: Redis counter, On POST /s/url request: key = 'rate_limit:create:' + ip_address, count = redis.incr(key), if (count == 1) redis.expire(key, 3600) (set 1-hour TTL on first request), if (count > 100) return 429 'Too Many Requests, try again in ' + (ttl / 60) + ' minutes', (3) Bypass for authenticated users: If user logged in (OAuth) \u2192 higher limit (1000/hour), Encourages users to create accounts. Per-user rate limiting (authenticated): (1) Limit: 1000 URLs per day per user, (2) Implementation: Database counter, On URL creation: SELECT quota_used, quota_limit FROM users WHERE user_id={user_id}, if (quota_used >= quota_limit) return 429 'Daily quota exceeded, upgrade to premium for unlimited', UPDATE users SET quota_used = quota_used + 1, (3) Reset daily: Cron job runs at midnight: UPDATE users SET quota_used = 0. CAPTCHA challenge (detect bots): (1) Trigger conditions: If IP creates 10 URLs in 1 minute \u2192 require CAPTCHA, If IP has 5 failed attempts \u2192 require CAPTCHA, New IP (first-time user) creating URL \u2192 optional CAPTCHA (based on risk score), (2) Implementation: Google reCAPTCHA v3 (invisible, scores user 0.0-1.0), Frontend: Include reCAPTCHA widget, on form submit: token = await grecaptcha.execute(SITE_KEY, {action: 'create_url'}), POST /s/url with {longURL, captchaToken: token}, Backend: Verify token with Google: response = await fetch('https://www.google.com/recaptcha/api/siteverify', {method: 'POST', body: {secret: SECRET_KEY, response: captchaToken}}), score = response.score (0.0 = bot, 1.0 = human), if (score < 0.5) return 400 'CAPTCHA failed, please try again' (show image challenge), if (score >= 0.5) proceed with URL creation. Malicious URL detection (prevent phishing, malware): Blacklist check: (1) Google Safe Browsing API: On URL creation: response = await fetch('https://safebrowsing.googleapis.com/v4/threatMatches:find', {method: 'POST', body: {client: {clientId: 'myapp', clientVersion: '1.0'}, threatInfo: {threatTypes: ['MALWARE', 'SOCIAL_ENGINEERING', 'UNWANTED_SOFTWARE'], platformTypes: ['ANY_PLATFORM'], threatEntryTypes: ['URL'], threatEntries: [{url: longURL}]}}}), if (response.matches && response.matches.length > 0) return 400 'Malicious URL detected: ' + response.matches[0].threatType, (2) Cost: Free tier (10K lookups/day), paid tier (500K lookups/day). Phishing pattern detection: (1) Domain similarity check: User submits 'http://paypa1.com' (phishing paypal.com with '1' instead of 'l'), Check edit distance: levenshteinDistance('paypa1.com', 'paypal.com') = 1 (very similar), if distance < 2 and domain in popular_domains_list \u2192 flag as suspicious, (2) URL obfuscation: Check for IP addresses: /^https?:\\/\\/\\d+\\.\\d+\\.\\d+\\.\\d+/ (e.g., http://192.168.1.1), Check for excessive subdomains: 'http://login.secure.account.paypal-verify.com' (red flag), Check for URL shorteners in long URL: 'http://bit.ly/...' (shortening a short URL = suspicious). User reporting: (1) Report button: Each short URL page has 'Report abuse' button, (2) Submission: POST /api/v1/report with {shortURL, reason: 'phishing'/'spam'/'malware'/'inappropriate'}, (3) Threshold: If URL reported 10 times \u2192 auto-flag for review, If URL reported 3 times by trusted users (verified accounts) \u2192 auto-flag, (4) Manual review: Moderator views reported URLs, decides: (a) Confirmed malicious \u2192 DELETE FROM urls, DEL url:{shortURL} from cache, (b) False positive \u2192 mark as reviewed, ignore future reports. Content scanning (for uploaded pages): (1) If long URL points to user-uploaded content (e.g., image hosting site), Scan content: Virus scan (ClamAV), Image moderation (AWS Rekognition for inappropriate content), (2) If flagged \u2192 reject URL creation. Abuse monitoring: (1) Metrics: Track: URLs created per IP histogram (identify IPs creating 100s of URLs), Track: Short URLs with 0 clicks after 24 hours (likely spam), Track: URLs with high report rate, (2) Automated bans: If IP creates 500 URLs in 1 hour (despite rate limit, using multiple IPs) \u2192 ban IP range, If user account creates 50 reported URLs \u2192 ban account, (3) Honeypot: Create fake short URLs (never shared publicly), If clicked \u2192 IP is likely bot (scanning URL space) \u2192 ban. URL validation: (1) Protocol check: Must start with http:// or https://, Reject: javascript:alert('XSS'), data:text/html,<script>..., file:///etc/passwd, (2) Length check: longURL < 2048 chars (browser limit), (3) DNS check: Resolve domain: dns.lookup(domain), if fails \u2192 reject 'Invalid domain', if domain is internal IP (192.168.x.x, 10.x.x.x) \u2192 reject (SSRF prevention). Authentication & authorization: (1) Optional login: Allow anonymous URL creation (Bit.ly model) OR require login (enterprise model), (2) OAuth providers: Google, GitHub, Twitter (trusted identity), (3) Benefits: Track user's URLs: GET /api/v1/users/{userId}/urls, Edit/delete URLs (only creator can modify), Private URLs (only creator sees analytics), Higher rate limits (1000/day vs 100/day for anonymous). API keys (for programmatic access): (1) Generate API key: user.api_key = generateRandomString(32), Store hash: users.api_key_hash = bcrypt.hash(api_key), (2) Usage: POST /s/url with header 'Authorization: Bearer {api_key}', (3) Rate limit per API key: 10K requests/day, Track: redis.incr('api_key_quota:' + api_key_hash), (4) Revocation: If API key compromised \u2192 regenerate, old key invalidated. Legal compliance: (1) Terms of Service: Users agree not to create malicious/spam URLs, Violation \u2192 account termination, (2) DMCA takedown: Copyright holders can request URL removal, Process: Verify request \u2192 DELETE URL \u2192 notify creator, (3) Law enforcement: Comply with legal requests (court orders) for URL creator identity. Result: Multi-layered defense: Rate limiting (100/hour per IP, 1000/day per user), CAPTCHA (bot detection, reCAPTCHA v3 scoring), Malicious URL detection (Safe Browsing API, pattern matching), User reporting (community moderation), Monitoring & bans (automated abuse detection), reduces spam 99%, legitimate users unaffected, malicious URLs blocked before creation or flagged post-creation."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "11. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Performance",
                    "pairs": [
                      {
                        "k": "DAU / URLs",
                        "v": "100M DAU, 1B URLs stored, 1B redirects/day (11.5K req/sec average, 115K peak)"
                      },
                      {
                        "k": "Latency",
                        "v": "Redirect <100ms target (Redis cache hit <2ms, DB miss ~50ms)"
                      },
                      {
                        "k": "Cache hit rate",
                        "v": "90%+ (popular URLs always cached, 24-hour TTL)"
                      },
                      {
                        "k": "Read:Write ratio",
                        "v": "100:1 (reads >> writes, redirect-heavy workload)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "ID Generation (from image shows 3 approaches)",
                    "pairs": [
                      {
                        "k": "Base62 capacity",
                        "v": "62^6 = 56.8 billion (6 chars), 62^7 = 3.5 trillion (7 chars)"
                      },
                      {
                        "k": "Zookeeper counter",
                        "v": "2-3ms latency (from image: 'Zms-3ms'), atomic increment, sequential IDs"
                      },
                      {
                        "k": "Snowflake structure",
                        "v": "64-bit: [41 bits timestamp][10 bits workerID][12 bits sequence] = 69 years + 1024 workers + 4096 IDs/ms"
                      },
                      {
                        "k": "Hash collision",
                        "v": "SHA1 7 chars: 0.01% collision at 1M URLs, 0.2% at 10M URLs (need retries)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching & Database (from image)",
                    "pairs": [
                      {
                        "k": "Redis latency",
                        "v": "From image: '2ms' for cache lookup, <1ms in-memory read"
                      },
                      {
                        "k": "DB latency",
                        "v": "From image: '5ms' server to DB, ~10ms indexed query, 30-50ms total for cache miss"
                      },
                      {
                        "k": "Encryption overhead",
                        "v": "From image: 'encrypt: 1ms' for hash-based approach, 20% encryption / 80% decryption workload"
                      },
                      {
                        "k": "Total redirect time",
                        "v": "From image: '=16ms + 16ms' round trip (server processing + DB query)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Storage & Scaling",
                    "pairs": [
                      {
                        "k": "Storage per URL",
                        "v": "~610 bytes (short_url 10B + long_url 500B avg + metadata 100B)"
                      },
                      {
                        "k": "1B URLs storage",
                        "v": "610B \u00d7 1B \u2248 600 GB (with indexes ~780 GB)"
                      },
                      {
                        "k": "Analytics storage",
                        "v": "200 bytes/click, 1B clicks/day = 200 GB/day, 90 days = 18 TB"
                      },
                      {
                        "k": "Bloom filter",
                        "v": "5 MB for 10M URLs (1% false positive rate)"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Short URL uniqueness is NON-NEGOTIABLE. Use Zookeeper atomic counter (guaranteed uniqueness) OR Snowflake with proper workerID management OR hash-based with collision retry. Database UNIQUE constraint on short_url column is mandatory backup. Two users getting same short URL = catastrophic failure."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How generate unique short URLs?'. Answer: Three approaches from image: (1) Counter (Zookeeper atomic increment \u2192 Base62 encode, shortest 6-7 chars), (2) Snowflake (64-bit: timestamp + workerID + sequence, 10-11 chars), (3) Hash (SHA1 \u2192 substring, deterministic deduplication). Production: Bit.ly uses counter, Twitter uses Snowflake."
              },
              {
                "type": "tip",
                "text": "Base62 encoding (from image): Characters [0-9a-zA-Z] = 62 chars. Algorithm: num=3748547; result=''; while(num>0){result=chars[num%62]+result; num=floor(num/62);} \u2192 '9i91c0'. Capacity: 62^6 = 56B URLs (6 chars), 62^7 = 3.5T (7 chars). URL-safe, human-readable, optimal for shortening."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Redis caching (from image shows 2ms latency). Cache hit 90%+ \u2192 <2ms redirect. Cache miss 10% \u2192 DB query ~50ms. Write-through: INSERT DB \u2192 SET cache immediately. TTL 24 hours (popular URLs stay cached). Bloom filter prevents DB queries for non-existent URLs (5MB for 10M URLs)."
              },
              {
                "type": "warning",
                "text": "NEVER skip malicious URL detection. Use Google Safe Browsing API before URL creation. Check phishing patterns (paypa1.com vs paypal.com edit distance). Validate protocol (reject javascript:, data:, file: schemes \u2192 XSS/SSRF prevention). User reports + manual review for flagged URLs. Abuse = service shutdown risk."
              },
              {
                "type": "tip",
                "text": "From image: Zookeeper counter with 'Zms-3ms' latency. Distributed coordination for unique IDs across multiple encryption servers (round-robin load balancing). 3-5 node cluster (quorum = majority), leader election on failure. Atomic increment guarantees no collisions. Alternative: Snowflake if Zookeeper is bottleneck (>100K creations/sec)."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'Handle 1B redirects/day?'. Answer: (1) Redis cache 90% hits <2ms, (2) 5 DB read replicas handle 10% misses (11.5K req/sec \u00f7 5 = 2.3K/replica), (3) 100 app servers \u00d7 1K req/sec = 100K capacity (peak 115K), (4) CDN caches top 1000 URLs (80% traffic) at edge <50ms globally. Scales horizontally."
              },
              {
                "type": "warning",
                "text": "NEVER use simple hash as short URL without collision handling. Hash collision at 1M URLs \u2248 0.01% (birthday paradox). MUST implement retry logic: try substring(0,7), (7,14), (14,21) or append timestamp salt. Database UNIQUE constraint catches collisions. 5 retry attempts max, fallback to counter if all fail."
              },
              {
                "type": "tip",
                "text": "Rate limiting (abuse prevention): 100 URLs/hour per IP (Redis INCR with 3600 sec TTL), 1000/day for authenticated users. CAPTCHA if 10 URLs/minute (reCAPTCHA v3 scoring). Ban IP if 500 URLs/hour despite limits. Malicious URL detection via Safe Browsing API. User reporting threshold: 10 reports \u2192 manual review."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention from image: Three approaches compared. Counter (Zookeeper): shortest URLs 6-7 chars, sequential, 2-3ms overhead. Snowflake: decentralized, 10-11 chars, 4M IDs/sec, no coordination. Hash (SHA1): deterministic deduplication, same URL \u2192 same short code, 0.01% collision need retry. Choose based on: shortest URL (counter), highest throughput (Snowflake), deduplication (hash)."
              }
            ],
            "tags": [
              "system-design",
              "url-shortener",
              "bitly",
              "tinyurl",
              "base62-encoding",
              "zookeeper-counter",
              "snowflake-id",
              "redis-caching",
              "distributed-systems",
              "unique-id-generation",
              "hash-collision",
              "rate-limiting"
            ]
          }
        },
        {
          "id": "02",
          "title": "System Design 2: Design Ticket Booking System like BookMyShow, District / HLD / LLD",
          "videoUrl": "https://youtu.be/uamf0wRMx5o",
          "description": "In this System Design Interview video, we walk through designing a ticket booking system like BookMyShow, Ticketmaster, or Eventbrite. \ud83c\udf9f\ufe0f This is a frequently asked system design question in FAANG and top tech company interviews.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/b2ui92t9822jg2.webp?updatedAt=1743325881933",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/TicketBookingApplication%20(1).png?updatedAt=1746560493385",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-22",
            "hero": {
              "title": "Ticket Booking System (BookMyShow/Paytm/District)",
              "oneLinerToMemorize": "Search events (Elasticsearch) \u2192 Select seats \u2192 HOLD (Redis TTL + DB constraint) \u2192 Payment (idempotent) \u2192 CONFIRM \u2192 Kafka events for notifications"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User should be able to search an event based on title, location and date",
                  "Feature 2: User should be able to view the details of the events (seat map, description, venue, showtime metadata)",
                  "Feature 3: User should be able to do a booking for that event (select seats, create temporary hold)",
                  "Feature 4: Complete payment and confirm booking (ticket issuance with booking confirmation)",
                  "Feature 5: Prevent double-booking for the same seat in the same showtime (strong consistency requirement)",
                  "Feature 6: View booking status/history and receive notifications (email/SMS for booking confirmation)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Latency",
                    "pairs": [
                      {
                        "k": "Scale",
                        "v": "100M DAU (Daily Active Users), high read traffic for search/browse, massive spikes during popular movie releases and concert ticket sales"
                      },
                      {
                        "k": "Read vs Write",
                        "v": "Read >> Write (90% searches, 10% bookings)"
                      },
                      {
                        "k": "Latency",
                        "v": "Search/browse: low latency <200ms; Booking/confirm: correctness-first (can trade availability for consistency)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Consistency (CAP Theorem)",
                    "pairs": [
                      {
                        "k": "Seat booking",
                        "v": "Strong consistency for a seat in a showtime - NO double booking allowed (CP from CAP)"
                      },
                      {
                        "k": "Search index",
                        "v": "Event search can be eventually consistent via CDC to Elasticsearch (AP from CAP)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities",
                "type": "bullets",
                "items": [
                  "Entity 1: User - Customer with user_id, name, email, phone, booking_history[]",
                  "Entity 2: Event (Movie/Concert) - event_id, name, venue_id, artist_list[], description, duration, genre",
                  "Entity 3: Venue (Hall/Location) - venue_id, name, location, total_seats, seat_map (rows/columns/types)",
                  "Entity 4: Showtime - show_id, event_id, venue_id, start_time, end_time, ticket_price_by_seat_type",
                  "Entity 5: Ticket/Seat - seat_id within show_id, row, number, type (regular/premium/VIP), status (available/held/booked)",
                  "Entity 6: SeatHold - hold_id, show_id, seat_ids[], user_id, expires_at (TTL), status (pending/expired/confirmed)",
                  "Entity 7: Booking - booking_id, hold_id, user_id, show_id, seat_ids[], payment_id, total_amount, status (pending/confirmed/cancelled)"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Search & Discovery",
                    "pairs": [
                      {
                        "k": "GET /v1/search?q={searchTerm}&location={location}&date={date}",
                        "v": "Search events \u2192 List<EventId> with pagination"
                      },
                      {
                        "k": "GET /v1/event/{eventId}",
                        "v": "Get event details \u2192 Event + showtimes + seat summary (available count by type)"
                      },
                      {
                        "k": "GET /v1/event/{eventId}/showtimes",
                        "v": "Get all showtimes for an event \u2192 List<Showtime> with seat availability"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Seat Selection & Booking",
                    "pairs": [
                      {
                        "k": "GET /v1/showtime/{showId}/seats",
                        "v": "Get seat map with availability \u2192 Seat[][] grid with status (available/held/booked)"
                      },
                      {
                        "k": "POST /v1/booking/reserve",
                        "v": "Reserve seats {showId, seatIds[], userId} \u2192 holdId + expiresAt (10-15 min)"
                      },
                      {
                        "k": "POST /v1/booking/confirm",
                        "v": "Confirm booking {holdId, paymentDetails, idempotencyKey} \u2192 bookingId"
                      },
                      {
                        "k": "DELETE /v1/booking/release/{holdId}",
                        "v": "Release held seats (user cancels before payment)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Booking Management",
                    "pairs": [
                      {
                        "k": "GET /v1/bookings/{userId}",
                        "v": "Get user's booking history \u2192 List<Booking>"
                      },
                      {
                        "k": "GET /v1/booking/{bookingId}",
                        "v": "Get booking details with QR code for entry"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design (from image)",
                "type": "bullets",
                "items": [
                  "Clients \u2192 API Gateway: Authentication, authorization, rate limiting, routing",
                  "Search Service \u2192 Elasticsearch: Fast event/venue search with location-based queries, read-heavy",
                  "Event Service \u2192 Cassandra Database: Source of truth for events, venues, showtimes; publishes CDC to Elasticsearch",
                  "Booking Service \u2192 MySQL/PostgreSQL: Handles seat hold and booking confirmation with ACID transactions",
                  "Redis: Short-lived seat holds (TTL 10-15 min), high-QPS availability checks, distributed locks",
                  "Payment Gateway: External payment processor (Stripe/Razorpay) for payment processing",
                  "Kafka: Event streaming for async operations (notifications, analytics, invoicing)",
                  "CDC (Change Data Capture): Event database \u2192 Kafka \u2192 Elasticsearch for search index sync"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Event Search (Elasticsearch)",
                    "steps": [
                      "User searches: GET /v1/search?q=SpiderMan&location=Bangalore&date=2026-01-25",
                      "Search Service queries Elasticsearch: { 'query': { 'bool': { 'must': [{ 'match': { 'name': 'SpiderMan' }}], 'filter': [{ 'term': { 'location': 'Bangalore' }}, { 'range': { 'date': { 'gte': '2026-01-25' }}}] }}, 'sort': [{ 'relevance': 'desc' }], 'size': 20 }",
                      "CDC pipeline (from image): Cassandra Event DB \u2192 Kafka \u2192 Elasticsearch indexer keeps search up-to-date within 1-2 seconds",
                      "Response: [{ event_id, movie_id, title, artist_list[], description, venue, seat_types[], showtimes[], poster_url }]",
                      "Cache popular searches in Redis with TTL=10 min for faster repeated queries"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: View Event Details & Seat Map",
                    "steps": [
                      "User selects event: GET /v1/event/{event_id} to view full details",
                      "Event Service fetches: SELECT * FROM events e JOIN showtimes s ON e.event_id = s.event_id WHERE e.event_id = {event_id}",
                      "For each showtime: Query seat availability summary (not full seat map yet): SELECT seat_type, COUNT(*) as available FROM seats WHERE show_id = {show_id} AND status = 'AVAILABLE' GROUP BY seat_type",
                      "Response: { event: {...}, showtimes: [{ show_id, start_time, venue, available_seats: { regular: 150, premium: 80, VIP: 20 } }] }",
                      "User selects showtime: GET /v1/showtime/{show_id}/seats to view seat map",
                      "Booking Service queries: Check Redis first: GET seat_map:{show_id}, if miss: SELECT seat_id, row, number, type, status FROM seats WHERE show_id = {show_id} ORDER BY row, number",
                      "Generate seat grid: Convert flat list to 2D array based on venue layout (e.g., 20 rows \u00d7 30 columns), mark held/booked seats",
                      "Cache seat map in Redis with TTL=30 seconds (frequently changing data), key: seat_map:{show_id}"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Seat Hold (CRITICAL - Prevent Double Booking)",
                    "steps": [
                      "User selects seats: POST /v1/booking/reserve with { show_id, seat_ids: ['L12', 'L13'], user_id }",
                      "Booking Service validation: (1) Check if seats are available, (2) Acquire distributed lock to prevent race condition",
                      "Step 1 - Redis distributed lock: FOR EACH seat in seat_ids: SETNX lock:seat:{show_id}:{seat_id} {hold_id} EX 30 (30 seconds), if any SETNX returns 0 (lock exists) \u2192 another user is holding this seat \u2192 abort and return 'Seats being held by another user'",
                      "Step 2 - Database check with pessimistic lock: SELECT seat_id, status FROM seats WHERE show_id = {show_id} AND seat_id IN ('L12', 'L13') FOR UPDATE (row-level lock)",
                      "Step 3 - Validate all seats available: if ANY seat has status != 'AVAILABLE' \u2192 Release Redis locks, return 'Seats not available'",
                      "Step 4 - Create hold in Redis (fast check): SET hold:{hold_id} '{show_id, seat_ids, user_id, created_at}' EX 900 (15 min TTL), FOR EACH seat: SET seat_hold:{show_id}:{seat_id} {hold_id} EX 900",
                      "Step 5 - Create hold in Database (source of truth): BEGIN TRANSACTION; INSERT INTO seat_holds (hold_id, show_id, seat_ids, user_id, expires_at, status) VALUES ({hold_id}, {show_id}, ARRAY['L12','L13'], {user_id}, NOW() + INTERVAL '15 minutes', 'PENDING'); UPDATE seats SET status = 'HELD', held_by = {hold_id}, held_at = NOW() WHERE show_id = {show_id} AND seat_id IN ('L12', 'L13'); COMMIT;",
                      "Step 6 - Release Redis locks: FOR EACH seat: DEL lock:seat:{show_id}:{seat_id}",
                      "Response: { hold_id, expires_at: '2026-01-25T15:30:00Z', total_amount: 600, seats: ['L12', 'L13'] }",
                      "Why both Redis and DB?: Redis for fast concurrent hold attempts (10ms vs 50ms DB), DB is source of truth with ACID guarantees, Redis hold expires automatically after 15 min (no manual cleanup needed)"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Payment Processing with Idempotency",
                    "steps": [
                      "User redirected to payment: POST /v1/booking/confirm with { hold_id, payment_method_id, idempotency_key: {hold_id} } (idempotency_key prevents double payment on retry)",
                      "Booking Service validates: (1) Check hold exists and not expired: SELECT status, expires_at FROM seat_holds WHERE hold_id = {hold_id}, if expired or status != 'PENDING' \u2192 return error, (2) Check payment not already processed: SELECT * FROM bookings WHERE hold_id = {hold_id}, if exists \u2192 return existing booking_id (idempotency)",
                      "Payment flow: (1) Create payment intent with Payment Gateway: stripe.paymentIntents.create({ amount: 600, currency: 'INR', customer_id, metadata: { hold_id, user_id }, idempotency_key: {hold_id} }), (2) Return payment_url to client, user completes payment",
                      "Payment webhook: POST /webhooks/payment with { hold_id, payment_id, status: 'success', amount }, (1) Validate signature (security), (2) Idempotency check: SELECT * FROM bookings WHERE payment_id = {payment_id}, if exists return 200 (already processed)",
                      "Confirm booking atomically: BEGIN TRANSACTION; INSERT INTO bookings (booking_id, hold_id, user_id, show_id, seat_ids, payment_id, total_amount, status, confirmed_at) VALUES ({booking_id}, {hold_id}, {user_id}, {show_id}, ARRAY['L12','L13'], {payment_id}, 600, 'CONFIRMED', NOW()); UPDATE seats SET status = 'BOOKED', booked_by = {user_id}, booking_id = {booking_id} WHERE show_id = {show_id} AND seat_id IN ('L12', 'L13'); UPDATE seat_holds SET status = 'CONFIRMED' WHERE hold_id = {hold_id}; COMMIT;",
                      "Delete Redis hold: DEL hold:{hold_id}, FOR EACH seat: DEL seat_hold:{show_id}:{seat_id}",
                      "Publish Kafka event: 'booking.confirmed' topic with { booking_id, user_id, show_id, event_id, seat_ids, amount, timestamp }",
                      "Response: { booking_id, qr_code_url, booking_details: {...} }"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Hold Expiry & Auto-Release",
                    "steps": [
                      "Redis TTL expiry: After 15 minutes, Redis automatically deletes hold:{hold_id} and seat_hold:{show_id}:{seat_id} keys (no manual cleanup)",
                      "Database sweeper job: Runs every 1 minute, finds expired holds: SELECT hold_id, show_id, seat_ids FROM seat_holds WHERE status = 'PENDING' AND expires_at < NOW()",
                      "Release seats: FOR EACH expired hold: BEGIN TRANSACTION; UPDATE seats SET status = 'AVAILABLE', held_by = NULL WHERE show_id = {show_id} AND seat_id = ANY({seat_ids}) AND status = 'HELD'; UPDATE seat_holds SET status = 'EXPIRED' WHERE hold_id = {hold_id}; COMMIT;",
                      "Invalidate Redis seat map cache: DEL seat_map:{show_id} to force refresh on next view",
                      "Why sweeper needed?: Redis expiry doesn't trigger DB update, sweeper ensures DB and Redis eventually consistent, typically 1-2 min lag acceptable"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Notification Flow (Kafka Event-Driven)",
                    "steps": [
                      "Kafka topics (from image): 'booking.confirmed', 'booking.cancelled', 'payment.failed'",
                      "Notification Service: Consumes 'booking.confirmed' events, (1) Fetch user email/phone from User DB, (2) Generate ticket with QR code: booking_id encoded as QR, (3) Send email via SendGrid/SES with booking details + QR code attachment, (4) Send SMS via Twilio with booking confirmation and show details",
                      "Analytics Service: Consumes same events, tracks: booking conversion rate, popular shows, revenue per show, user behavior",
                      "Invoice Service: Generates invoice PDF with booking details, stores in S3, sends download link via email"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Viewing Bookings & History",
                    "steps": [
                      "User requests: GET /v1/bookings/{user_id} with header { Authorization: Bearer {token} }",
                      "Booking Service queries: SELECT b.booking_id, b.show_id, e.name as event_name, e.poster_url, v.name as venue_name, s.start_time, b.seat_ids, b.total_amount, b.status FROM bookings b JOIN showtimes s ON b.show_id = s.show_id JOIN events e ON s.event_id = e.event_id JOIN venues v ON s.venue_id = v.venue_id WHERE b.user_id = {user_id} ORDER BY b.confirmed_at DESC LIMIT 50",
                      "Categorize: upcoming_bookings (start_time > NOW()), past_bookings (start_time < NOW())",
                      "Response: { upcoming: [...], past: [...] }",
                      "Specific booking: GET /v1/booking/{booking_id} returns full details + QR code for entry verification"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Cancellation & Refund",
                    "steps": [
                      "User cancels: DELETE /v1/booking/{booking_id} with { reason: 'user_requested' }",
                      "Booking Service validates: (1) Check booking belongs to user, (2) Check cancellation policy: if show_time - NOW() < 2 hours \u2192 no refund, if < 24 hours \u2192 50% refund, if >= 24 hours \u2192 100% refund",
                      "Process cancellation: BEGIN TRANSACTION; UPDATE bookings SET status = 'CANCELLED', cancelled_at = NOW(), refund_amount = {calculated_refund} WHERE booking_id = {booking_id}; UPDATE seats SET status = 'AVAILABLE', booked_by = NULL, booking_id = NULL WHERE booking_id = {booking_id}; COMMIT;",
                      "Initiate refund: Call Payment Gateway: stripe.refunds.create({ payment_intent_id, amount: refund_amount })",
                      "Publish Kafka event: 'booking.cancelled' topic",
                      "Notification: Email/SMS confirmation of cancellation and refund timeline"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Admin Operations (Event Creation)",
                    "steps": [
                      "Admin creates event: POST /v1/admin/events with { name, description, duration, genre, artist_list[], poster_url }",
                      "Event Service: INSERT INTO events (...) RETURNING event_id",
                      "Admin creates showtimes: POST /v1/admin/showtimes with { event_id, venue_id, start_times[], ticket_prices_by_seat_type }",
                      "For EACH showtime: (1) INSERT INTO showtimes (show_id, event_id, venue_id, start_time, end_time), (2) Generate seats: Fetch venue layout, create seat records: INSERT INTO seats (seat_id, show_id, row, number, type, status) SELECT generate_series(...) for all seats in venue, (3) Initialize all seats with status = 'AVAILABLE'",
                      "Publish to Kafka: 'event.created' \u2192 Elasticsearch indexer adds to search index",
                      "Cache warm-up: Preload seat maps to Redis for popular shows"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Handling Concurrency (The Interview Trap)",
                    "steps": [
                      "Scenario: 100 users try to book last 2 seats (L12, L13) simultaneously at 10:00:00",
                      "User A, B, C... all send: POST /v1/booking/reserve with seat_ids = ['L12', 'L13'] at the same millisecond",
                      "Redis lock acquisition: User A: SETNX lock:seat:{show_id}:L12 {hold_id_A} \u2192 returns 1 (acquired), User B: SETNX lock:seat:{show_id}:L12 {hold_id_B} \u2192 returns 0 (failed, locked by A), User B immediately gets error 'Seats being held', User A proceeds to acquire L13 lock: SETNX lock:seat:{show_id}:L13 {hold_id_A} \u2192 returns 1",
                      "User A continues to DB: SELECT FOR UPDATE \u2192 validates seats available \u2192 creates hold \u2192 releases Redis locks, Total time: ~50-100ms",
                      "User B retries: SETNX lock:seat:{show_id}:L12 \u2192 returns 1 (A released) \u2192 proceeds to DB \u2192 SELECT FOR UPDATE \u2192 seats now status='HELD' by User A \u2192 validation fails \u2192 returns 'Seats not available'",
                      "Key insight: Redis lock is SHORT-LIVED (30 sec TTL) for atomic check-and-hold, DB FOR UPDATE ensures no phantom reads during transaction, Both mechanisms together prevent any double booking",
                      "Edge case: If User A's service crashes after acquiring Redis lock but before releasing \u2192 lock auto-expires after 30s \u2192 seat becomes available again \u2192 no deadlock"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Events (Cassandra - from image)",
                    "pairs": [
                      {
                        "k": "event_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "movie_id / concert_id",
                        "v": "uuid (reference to movie/concert catalog)"
                      },
                      {
                        "k": "name",
                        "v": "text (movie/concert name)"
                      },
                      {
                        "k": "artist_list",
                        "v": "list<text> (actors or musicians)"
                      },
                      {
                        "k": "description",
                        "v": "text"
                      },
                      {
                        "k": "seat_types",
                        "v": "list<text> (regular, premium, VIP)"
                      },
                      {
                        "k": "genre",
                        "v": "text (action, concert, comedy)"
                      },
                      {
                        "k": "duration",
                        "v": "int (minutes)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Venues (Cassandra)",
                    "pairs": [
                      {
                        "k": "venue_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "text (cinema hall or concert venue name)"
                      },
                      {
                        "k": "location",
                        "v": "text (city, address)"
                      },
                      {
                        "k": "total_seats",
                        "v": "int"
                      },
                      {
                        "k": "seat_layout",
                        "v": "map<text, int> ({ 'regular': 200, 'premium': 100, 'VIP': 50 })"
                      },
                      {
                        "k": "rows",
                        "v": "int (number of rows)"
                      },
                      {
                        "k": "columns_per_row",
                        "v": "int"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Showtimes (Cassandra)",
                    "pairs": [
                      {
                        "k": "show_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "event_id",
                        "v": "uuid FK \u2192 Events"
                      },
                      {
                        "k": "venue_id",
                        "v": "uuid FK \u2192 Venues"
                      },
                      {
                        "k": "start_time",
                        "v": "timestamp"
                      },
                      {
                        "k": "end_time",
                        "v": "timestamp"
                      },
                      {
                        "k": "ticket_prices",
                        "v": "map<text, decimal> ({ 'regular': 200, 'premium': 400, 'VIP': 800 })"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Seats (MySQL/PostgreSQL - from image)",
                    "pairs": [
                      {
                        "k": "Composite PK",
                        "v": "(show_id, seat_id)"
                      },
                      {
                        "k": "show_id",
                        "v": "uuid FK \u2192 Showtimes"
                      },
                      {
                        "k": "seat_id",
                        "v": "varchar(10) (e.g., 'L12', 'A05')"
                      },
                      {
                        "k": "row",
                        "v": "varchar(5) (e.g., 'L', 'A')"
                      },
                      {
                        "k": "number",
                        "v": "int (seat number within row)"
                      },
                      {
                        "k": "type",
                        "v": "enum (regular, premium, VIP)"
                      },
                      {
                        "k": "status",
                        "v": "enum (AVAILABLE, HELD, BOOKED)"
                      },
                      {
                        "k": "held_by",
                        "v": "uuid (hold_id if status=HELD)"
                      },
                      {
                        "k": "held_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "booked_by",
                        "v": "uuid (user_id if status=BOOKED)"
                      },
                      {
                        "k": "booking_id",
                        "v": "uuid (if status=BOOKED)"
                      },
                      {
                        "k": "Index",
                        "v": "INDEX on (show_id, status) for availability queries"
                      }
                    ]
                  },
                  {
                    "groupTitle": "SeatHolds (MySQL/PostgreSQL)",
                    "pairs": [
                      {
                        "k": "hold_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "show_id",
                        "v": "uuid FK \u2192 Showtimes"
                      },
                      {
                        "k": "seat_ids",
                        "v": "text[] (array of seat_ids like ['L12', 'L13'])"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "expires_at",
                        "v": "timestamp (created_at + 15 minutes)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, CONFIRMED, EXPIRED, CANCELLED)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Bookings (MySQL/PostgreSQL)",
                    "pairs": [
                      {
                        "k": "booking_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "hold_id",
                        "v": "uuid FK \u2192 SeatHolds, UNIQUE (idempotency)"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users, INDEXED"
                      },
                      {
                        "k": "show_id",
                        "v": "uuid FK \u2192 Showtimes"
                      },
                      {
                        "k": "seat_ids",
                        "v": "text[] (array of booked seat_ids)"
                      },
                      {
                        "k": "payment_id",
                        "v": "varchar(255) (from payment gateway)"
                      },
                      {
                        "k": "total_amount",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "status",
                        "v": "enum (CONFIRMED, CANCELLED, REFUNDED)"
                      },
                      {
                        "k": "confirmed_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "cancelled_at",
                        "v": "timestamp (nullable)"
                      },
                      {
                        "k": "qr_code_url",
                        "v": "varchar(500) (for entry verification)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Locks & Holds",
                    "pairs": [
                      {
                        "k": "lock:seat:{showId}:{seatId}",
                        "v": "STRING {hold_id} EX 30 (distributed lock, 30 sec TTL)"
                      },
                      {
                        "k": "hold:{holdId}",
                        "v": "STRING (JSON) EX 900 (hold details, 15 min TTL)"
                      },
                      {
                        "k": "seat_hold:{showId}:{seatId}",
                        "v": "STRING {hold_id} EX 900 (which hold owns this seat)"
                      },
                      {
                        "k": "seat_map:{showId}",
                        "v": "STRING (JSON seat grid) EX 30 (30 sec cache)"
                      },
                      {
                        "k": "search:{hash(query)}",
                        "v": "STRING (JSON search results) EX 600 (10 min cache)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch - Search Index",
                    "pairs": [
                      {
                        "k": "event_id",
                        "v": "keyword"
                      },
                      {
                        "k": "name",
                        "v": "text (analyzed for search)"
                      },
                      {
                        "k": "location",
                        "v": "keyword (venue location)"
                      },
                      {
                        "k": "date",
                        "v": "date (showtime dates)"
                      },
                      {
                        "k": "genre",
                        "v": "keyword"
                      },
                      {
                        "k": "artist_list",
                        "v": "text"
                      },
                      {
                        "k": "description",
                        "v": "text"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "8. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Redis Distributed Locks - SETNX with 30s TTL prevents concurrent seat holds, auto-releases on crash, <10ms lock acquisition",
                  "Technique 2: Database Pessimistic Locking - SELECT FOR UPDATE prevents phantom reads, ensures ACID for seat booking",
                  "Technique 3: Redis Hold TTL - Automatic expiry after 15 min, no manual cleanup needed, sweeper job syncs DB within 1-2 min",
                  "Technique 4: Elasticsearch for Search - CDC from Cassandra keeps search fresh within 1-2s, handles 100K queries/sec",
                  "Technique 5: Seat Map Caching - Redis cache with 30s TTL reduces DB load by 95%, frequent refresh for accuracy",
                  "Technique 6: Kafka Event Streaming - Decouples notifications, analytics, invoicing, enables async processing",
                  "Technique 7: Payment Idempotency - hold_id as idempotency_key prevents double charge, UNIQUE constraint on bookings(hold_id)",
                  "Technique 8: Connection Pooling - API servers maintain 200 DB connections, prevents exhaustion during ticket sale spikes",
                  "Technique 9: Database Sharding - Shard seats table by show_id for horizontal scaling, each show independent",
                  "Technique 10: Read Replicas - Route read queries (search, view bookings) to replicas, writes only to primary",
                  "Technique 11: Rate Limiting - 10 hold attempts per user per minute prevents bot abuse during popular shows",
                  "Technique 12: CDN for Images - Event posters and QR codes served via CloudFront, 95% cache hit rate"
                ]
              },
              {
                "id": "interview-qa",
                "title": "9. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you prevent double booking when 100 users try to book the same last seat?",
                    "a": "Multi-layer approach with distributed locks + database pessimistic locking: (1) Redis SETNX lock BEFORE DB check: FOR EACH seat: SETNX lock:seat:{show_id}:{seat_id} {hold_id} EX 30, if any returns 0 (lock exists) \u2192 abort immediately with 'Seats being held', this provides fast rejection (<10ms) before expensive DB operations. (2) If all locks acquired: Query DB with pessimistic lock: SELECT seat_id, status FROM seats WHERE show_id={show_id} AND seat_id IN (...) FOR UPDATE, this row-level exclusive lock prevents concurrent transactions from reading same rows. (3) Validate all seats have status='AVAILABLE', if not \u2192 release Redis locks, return 'Seats not available'. (4) Create hold atomically: BEGIN TRANSACTION; INSERT INTO seat_holds (...); UPDATE seats SET status='HELD', held_by={hold_id} WHERE seat_id IN (...); COMMIT;. (5) Release Redis locks: DEL lock:seat:... for each seat. Why both mechanisms?: Redis provides fast concurrent rejection (100 users \u2192 1 proceeds, 99 rejected in <10ms), PostgreSQL FOR UPDATE ensures ACID within transaction + prevents phantom reads, Redis lock TTL=30s auto-releases if service crashes (no deadlock). Example timeline: T=0ms: 100 users send request, T=5ms: User A acquires all Redis locks, Users B-Z fail Redis lock (rejected), T=50ms: User A completes DB transaction and hold creation, T=55ms: User A releases Redis locks, T=60ms: Users who retry can now attempt (but seats status='HELD' in DB). Result: Zero double bookings, fast rejection for losers, automatic recovery from crashes."
                  },
                  {
                    "q": "Why use Redis holds with TTL instead of just database expiry?",
                    "a": "Redis TTL provides automatic cleanup with zero overhead: (1) Performance: Redis handles 100K+ ops/sec for seat availability checks vs PostgreSQL ~10K ops/sec, during ticket sale spikes (popular movie release), millions of users checking seat maps simultaneously - Redis serves from memory in <1ms vs DB 10-50ms. (2) Auto-expiry: SET hold:{hold_id} {...} EX 900 automatically deletes after 15 minutes, no background job needed for Redis cleanup, contrast with DB: requires periodic sweeper job running every 1 min to find and expire holds. (3) Fast distributed lock: SETNX provides atomic check-and-set in <1ms across multiple API servers, prevents race conditions during concurrent bookings. (4) Eventual consistency acceptable: Redis hold expires at 15:00:00, DB sweeper runs at 15:01:30 and updates seats.status='AVAILABLE', 1-2 min lag acceptable since user sees 'Hold expired' immediately from Redis. (5) DB is still source of truth: Redis serves as fast check layer, DB has authoritative state with ACID guarantees, payment confirmation always checks DB not Redis. (6) Failure handling: If Redis crashes, system falls back to DB-only mode (slower but functional), if DB crashes, bookings stop but existing holds in Redis still prevent conflicts. Trade-off: Redis holds can drift from DB during network partitions, sweeper job reconciles within minutes, but final booking confirmation always validates against DB with FOR UPDATE lock. Storage: 1 hold = ~500 bytes in Redis, 10K concurrent holds = 5MB (tiny), vs DB transaction logs growing continuously. The combination provides speed (Redis) + durability (DB) + automatic cleanup (TTL)."
                  },
                  {
                    "q": "How does the CDC pipeline work for syncing Event DB to Elasticsearch?",
                    "a": "Change Data Capture ensures search index stays fresh: (1) Source: Cassandra database stores events, venues, showtimes as source of truth, admin creates/updates events via Event Service which writes to Cassandra. (2) CDC mechanism: Cassandra commitlog captures all INSERT/UPDATE/DELETE operations, Debezium connector or custom CDC service tails commitlog and converts to change events, publishes to Kafka topic 'event.changes' with schema: {operation: 'insert'/'update'/'delete', table: 'events', key: event_id, data: {full_event_object}, timestamp}. (3) Kafka as buffer: Provides durability and replay capability, multiple consumers can process same events, handles backpressure if Elasticsearch indexer slows down. (4) Elasticsearch indexer consumer: Subscribes to 'event.changes' topic with consumer group 'es-indexer', processes events: if operation='insert' or 'update': es.index(index='events', id=event.event_id, body=event.data), if operation='delete': es.delete(index='events', id=event.event_id), uses bulk API: batches 100 events, indexes every 2 seconds for efficiency. (5) Error handling: Failed indexing \u2192 retry with exponential backoff, persistent failures \u2192 push to dead letter queue (DLQ) for manual investigation, monitor consumer lag: alert if lag >1000 events. (6) Consistency: Eventual consistency with typical lag 1-2 seconds (Cassandra write \u2192 Kafka publish \u2192 ES index), acceptable since search doesn't need real-time (user can wait 2s to see new movie in search). (7) Initial load: On first setup, bulk export all events from Cassandra: cqlsh COPY events TO 'events.csv', bulk import to ES: curl -X POST 'localhost:9200/_bulk' with all events, then CDC maintains sync going forward. (8) Schema evolution: When adding new fields to events table, update ES index mapping, reindex from Cassandra with new fields, CDC picks up changes going forward. Performance: Cassandra handles 10K writes/sec, Kafka buffers peaks, ES indexes at 5K docs/sec sustained, system can handle high event creation rate during bulk imports. Monitoring: Track Kafka consumer lag, ES indexing rate, replication lag from Cassandra, alert if lag increases beyond threshold."
                  },
                  {
                    "q": "How do you handle payment failures and refunds?",
                    "a": "Payment failure and refund flows with idempotency: (1) Payment timeout: User confirms booking \u2192 Payment Gateway processes \u2192 webhook delayed/lost, Booking Service sets timeout: hold_id expires in 15 minutes, background job checks: SELECT hold_id, user_id FROM seat_holds WHERE status='PENDING' AND created_at < NOW() - INTERVAL '15 minutes', if payment not received \u2192 release seats: UPDATE seats SET status='AVAILABLE' WHERE held_by={hold_id}, publish 'payment.timeout' event \u2192 Notification Service sends 'Payment failed' email. (2) Payment declined: Stripe webhook: POST /webhooks/payment with {status: 'failed', error: 'card_declined'}, Booking Service: (a) Idempotency check (handle duplicate webhooks), (b) Release hold: UPDATE seat_holds SET status='EXPIRED', UPDATE seats SET status='AVAILABLE', (c) Publish 'payment.failed' \u2192 notify user with reason and retry option. (3) Refund request: User cancels booking: DELETE /v1/booking/{booking_id}, Service validates: (a) Booking belongs to user, (b) Cancellation policy: if show_time - NOW() > 24 hours \u2192 100% refund, 2-24 hours \u2192 50% refund, <2 hours \u2192 no refund, (c) Prevent duplicate cancellation: if status='CANCELLED' \u2192 return existing refund_id. (4) Process refund atomically: BEGIN TRANSACTION; UPDATE bookings SET status='CANCELLED', refund_amount={calculated}, cancelled_at=NOW(); UPDATE seats SET status='AVAILABLE', booked_by=NULL, booking_id=NULL; INSERT INTO refunds (refund_id, booking_id, amount, status='PENDING'); COMMIT; (5) Async refund via Payment Gateway: Call Stripe API: stripe.refunds.create({payment_intent_id, amount}), Stripe processes (takes 5-7 business days), webhook confirms: POST /webhooks/refund with {status: 'succeeded'} \u2192 UPDATE refunds SET status='COMPLETED'. (6) Idempotency everywhere: Use booking_id as idempotency_key for refund API, prevents double refund if user clicks 'Cancel' multiple times, UNIQUE constraint on refunds(booking_id) ensures single refund per booking. (7) Partial refunds: If cancellation policy gives 50% refund, create payment intent for 50% of original amount, track: refunds.refund_type='partial', original_amount, refunded_amount. (8) Webhook failures: Stripe retries webhook 3 times with exponential backoff, if all fail \u2192 poll Stripe API: stripe.refunds.retrieve({refund_id}) to check status, reconciliation job runs daily: compares bookings DB with Stripe records, flags discrepancies for manual review. Edge cases: Concurrent cancellation + showtime started \u2192 cancellation rejected with 'Show already started', User cancels during refund processing \u2192 idempotency prevents duplicate, refund shows 'Already processing'."
                  },
                  {
                    "q": "How do you handle popular event launches (e.g., Marvel movie premiere)?",
                    "a": "Spike traffic handling with queue-based seat allocation: (1) Pre-sale preparation: Admin creates event + showtimes 1 week before sale, pre-generates all seats: INSERT INTO seats for all shows (10K+ seats per cinema), pre-warm caches: load seat maps into Redis for all showtimes, scale infrastructure: auto-scale API servers 5x (50 \u2192 250 instances), increase DB connection pool limits. (2) Virtual waiting room: Before sale time (e.g., sale opens at 12:00 PM), users join waiting room at 11:50 AM, Cloudflare Waiting Room or custom queue manages 1M+ users, at 12:00 PM, release users in batches (1000 users/sec), each user gets token with expiry. (3) Seat reservation queue: Instead of synchronous seat hold, POST /v1/booking/reserve publishes to Kafka 'seat_reservation.requests' topic: {request_id, show_id, seat_ids, user_id, timestamp}, consumer group (50 workers) processes queue at steady rate (100 reservations/sec), user sees 'In queue, position #523, estimated wait 5 min'. (4) Optimistic locking with retry: Worker attempts reservation: (a) Acquire Redis lock: SETNX lock:seat:{show_id}:{seat_id}, (b) If conflict \u2192 requeue with different seats (suggest alternative nearby seats), (c) If success \u2192 create hold in DB, send confirmation to user via WebSocket: {hold_id, seats, expires_at}. (5) Rate limiting: Per user: 1 hold attempt per 30 seconds (prevents spamming), Per IP: 10 requests/sec (blocks bots), CAPTCHA required before reservation (filters automated scripts). (6) Database optimization: Read replicas for seat availability queries (90% read, 10% write), Primary handles only seat hold writes (strongly consistent), Sharding: shard seats table by show_id (each show on separate shard), Index optimization: INDEX on (show_id, status) for fast availability queries. (7) Cache strategy: Seat map cached with very short TTL (5-10 seconds) during sale, Redis Cluster (10 nodes) handles 500K ops/sec, Cache-aside pattern: check Redis \u2192 if miss \u2192 query DB \u2192 cache for 5 sec. (8) Graceful degradation: If system overloaded, disable non-critical features: turn off real-time seat map updates (show count instead), disable recommendations and personalization, show static 'High demand' message instead of exact availability. (9) Monitoring: Real-time dashboard: concurrent users, queue depth, booking rate, error rate, alerts: if queue depth >10K or error rate >5%, auto-scale triggers: CPU >70% \u2192 add 50 more API servers within 2 min. (10) Post-spike cleanup: After initial rush (first 10 minutes), gradually reduce infrastructure, process expired holds (sweeper job), reconcile Redis vs DB state. Real example: Avengers Endgame premiere - 2M users online, 100K bookings in first 10 minutes, queue-based system handled 1000 reservations/sec peak, 99.5% success rate (no crashes), average wait time 3 minutes. Cost: Spike infrastructure (5x normal) for 2 hours costs $500, but prevents lost revenue of $50K+ from crashes."
                  },
                  {
                    "q": "How does seat map rendering work for large venues (1000+ seats)?",
                    "a": "Efficient seat map rendering with caching and pagination: (1) Data structure: Seats stored flat in DB: [(show_id, seat_id='A01', row='A', number=1, type='regular', status='AVAILABLE'), ...], UI needs 2D grid: [ ['A01', 'A02', 'A03', ...], ['B01', 'B02', ...] ] with status for each. (2) Seat map generation: API call: GET /v1/showtime/{show_id}/seats, Service checks Redis cache: GET seat_map:{show_id}, if miss: (a) Query DB: SELECT seat_id, row, number, type, status FROM seats WHERE show_id={show_id} ORDER BY row, number, (b) Build grid: Group by row, sort by number, create 2D array with seat objects, (c) Cache in Redis: SET seat_map:{show_id} {json_grid} EX 30 (30 sec TTL), Response: { rows: [{ row: 'A', seats: [{seat_id, number, type, status, price}, ...] }, ...], summary: {available: 150, held: 20, booked: 80} }. (3) Client-side rendering: React/Vue component receives grid, renders using virtual scrolling (only visible rows in DOM), Color coding: green=available, yellow=held, red=booked, grey=wheelchair/blocked, User hovers \u2192 tooltip with price and seat details, Click to select (multi-select with Shift+Click). (4) Real-time updates: WebSocket connection: ws://api/showtime/{show_id}/seats, Server publishes seat status changes: {seat_id: 'A01', status: 'HELD'} when another user holds, Client updates UI without full reload, Throttled updates: batch changes every 2-3 seconds (don't send individual seat updates), Optimistic UI: mark selected seats as 'SELECTING' immediately, revert if hold fails. (5) Large venue optimization: For 1000+ seats, paginate seat map: Load by section: balcony, mezzanine, orchestra separately, User selects section first \u2192 load only that section's seats (200-300 seats), Alternative: Send only seat summary initially: {type: 'regular', available: 150}, Show full seat picker only after user clicks 'Select seats'. (6) Mobile optimization: Mobile app: simpler grid view with zoom/pan, Touch-friendly: larger tap targets (40px vs 20px), Progressive loading: load first 5 rows immediately, lazy load rest on scroll. (7) Cache invalidation: After any hold/booking/cancellation: DEL seat_map:{show_id} in Redis, Next request regenerates fresh map from DB, During high traffic: increase TTL to 60 seconds (acceptable staleness during rush). (8) Seat type filtering: User filters: 'Show only Regular seats', Backend returns filtered grid: WHERE type='regular', Frontend hides unavailable types (grey out). (9) Accessibility: Screen reader support: announce seat status changes, Keyboard navigation: arrow keys to move between seats, Enter to select, Color-blind mode: use patterns + colors for status (stripes for held, solid for booked). Performance: 1000 seats = ~100KB JSON gzipped = ~20KB, loads in <200ms on 3G, Redis serves 10K requests/sec for seat maps, DB query (if cache miss) takes ~50ms with proper indexing. Real venue example: IMAX theater (450 seats) \u2192 seat map loads in 100ms, user selects 2 seats \u2192 hold created in 200ms, real-time update to other users within 3 seconds."
                  },
                  {
                    "q": "How do you prevent bots from hoarding seats during high-demand shows?",
                    "a": "Multi-layer bot prevention and rate limiting: (1) CAPTCHA verification: Require CAPTCHA before seat selection (Google reCAPTCHA v3 with score >0.5), Challenge-response for suspicious behavior (rapid requests, unusual patterns), CAPTCHA bypass for trusted users (verified accounts with booking history). (2) Rate limiting: Per user: 3 hold attempts per 10 minutes (prevents rapid seat cycling), Per IP address: 10 requests per minute (blocks bot farms), Per session: 1 active hold at a time (can't hold multiple shows simultaneously), Token bucket algorithm: burst allowed (5 requests in 1 sec) but sustained rate limited. (3) Device fingerprinting: Collect browser fingerprint (canvas, WebGL, fonts, timezone), Track fingerprint + IP combination, Flag suspicious patterns: same fingerprint from multiple IPs, multiple accounts from same fingerprint. (4) Behavioral analysis: Track user journey: must view event details before booking (can't directly POST /reserve), Minimum interaction time: 5 seconds on seat map before reservation (humans need time to choose), Mouse movement tracking: bots have unnatural movement patterns or no movement. (5) Account verification: Email verification required before booking, Phone verification for high-value events (concerts, sports finals), Payment method pre-authorization (verify valid card before allowing holds). (6) Progressive delays: First hold attempt: immediate, Second attempt (after cancellation): 30 second wait, Third attempt: 60 second wait, exponentially increasing, Penalize accounts that frequently hold and release without payment. (7) Automated bot detection: Machine learning model trained on: successful vs abandoned bookings, time patterns (bots active 24/7, humans concentrated in evenings), API access patterns (bots follow predictable sequences), Flag suspicious accounts: {suspicious_score: 0.85} \u2192 require additional verification. (8) Hold limits: Maximum 10 seats per user per show (prevents hoarding entire sections), Maximum 3 concurrent holds across all shows, Expired hold penalty: if hold expires 3 times in a row \u2192 1 hour cooldown before next attempt. (9) Dynamic pricing during detection: If bot activity detected (e.g., 100+ hold attempts from 50 IPs in 5 min), Temporarily increase prices by 10-20% for next batch (bots programmed for specific price points), Enable 'verified user' discount (real users with history get original price). (10) Post-booking validation: After payment, verify: booking pattern normal, user IP matches payment card country, email/phone reachable (send verification code), Suspicious bookings flagged for manual review before ticket issuance. (11) Enforcement: Detected bots: block IP + fingerprint + account for 24 hours, Repeat offenders: permanent ban + void bookings, Collaborate with payment processors: share bot fingerprints across platforms. (12) Honeypot seats: Inject fake 'available' seats visible only to bots (hidden in HTML but not rendered), Bots selecting honeypot seats \u2192 immediate flagging and blocking, Helps identify bot scripts without affecting real users. Real example: Taylor Swift concert - Bot attacks detected: 50K reservation attempts from 500 IPs in first minute, Bot prevention activated: CAPTCHA + rate limiting + fingerprinting, Result: 95% bot requests blocked, real fans got tickets, bots wasted resources on fake seats. Trade-off: Legitimate users may experience slight friction (CAPTCHA, wait times), but prevents unfair advantage for bots, essential for high-demand events to ensure fair access."
                  },
                  {
                    "q": "What happens if the database goes down during a booking transaction?",
                    "a": "Database failure handling with high availability setup: (1) Architecture: Primary DB (us-east-1) for all writes, Replica 1 (us-east-1, same region) with synchronous replication (lag <1 second), Replica 2 (us-west-2, cross-region) with asynchronous replication (lag <10 seconds), Health checks every 30 seconds: TCP connect + simple query (SELECT 1). (2) Failure detection: If primary fails to respond to 3 consecutive health checks (90 seconds), Connection pool exhaustion (all connections timing out), Application sees connection errors or transaction rollbacks. (3) Automatic failover: Orchestrator or cloud-managed service (AWS RDS, Google Cloud SQL) detects failure, Promotes Replica 1 to primary (2-3 minutes for promotion), DNS updated to point to new primary endpoint, Application reconnects automatically (connection pool retry logic). (4) During transaction failure: User submits: POST /v1/booking/confirm with {hold_id, payment}, (a) Booking Service attempts: BEGIN TRANSACTION, (b) Database dies mid-transaction \u2192 connection lost, (c) Transaction automatically rolls back (no partial state), (d) Service catches exception: DatabaseConnectionError, (e) Returns 503 Service Unavailable with { error: 'Temporary service disruption, please retry', retry_after: 60 }. (5) Idempotency protection: Client retries after 60 seconds with same hold_id and idempotency_key, By this time, failover complete (new primary active), Retry succeeds: SELECT * FROM bookings WHERE hold_id={hold_id}, if exists (somehow completed before failure) \u2192 return existing booking_id, if not exists \u2192 process normally with INSERT INTO bookings. (6) Redis holds during outage: Seat holds in Redis continue to function, Users can still select seats and create holds (Redis remains available), Holds stored but can't be confirmed (DB required for payment), Hold TTL (15 min) automatically releases seats if DB not recovered. (7) Read queries during outage: Seat availability checks: routed to read replicas (unaffected), Search, browse, view events: continue normally (served by Elasticsearch + replicas), Only bookings/payments blocked (write operations require primary). (8) Data consistency after failover: Synchronous replica promoted \u2192 zero data loss (up to last committed transaction), Asynchronous replica promoted \u2192 potential loss of last 1-10 seconds of transactions, Reconciliation: check Redis holds vs DB bookings, cleanup orphaned holds. (9) Payment handling: Payment Gateway already processed payment \u2192 user charged but booking not created, Solution: reconciliation job runs after recovery, finds orphaned payments: SELECT p.payment_id FROM payments p LEFT JOIN bookings b ON p.hold_id = b.hold_id WHERE b.booking_id IS NULL, creates bookings retroactively or refunds if seats no longer available. (10) User communication: Real-time status: display 'System temporarily unavailable, retrying...' in UI, Notifications: email users with pending holds about temporary disruption, Compensation: offer discount or free booking fee for affected users. (11) Recovery validation: After failover, verify data integrity: check seat status consistency, verify no double bookings occurred, reconcile Redis vs DB state, manual review of transactions during failure window. (12) Preventive measures: Database connection pooling: gracefully handle connection losses with retries, Circuit breaker pattern: temporarily block booking attempts if failure rate >10%, Graceful degradation: show 'View only' mode during DB issues (search works, bookings disabled). Real scenario: Database primary fails at 8:00 PM (peak time), Failover triggers at 8:01:30 PM, replica promoted at 8:03 PM, Bookings resume at 8:04 PM, Impact: 3-4 minutes of booking unavailability, 20 users mid-transaction affected (all retry successfully), zero double bookings, zero lost payments (idempotency + reconciliation). Cost: Multi-region setup adds 30-40% infrastructure cost, insurance against revenue loss (1 hour outage = $100K+ lost bookings)."
                  },
                  {
                    "q": "How do you implement QR code generation for ticket verification at venue entry?",
                    "a": "QR code generation and venue verification system: (1) QR code content: Contains encrypted booking_id + show_id + user_id + timestamp, Encryption: AES-256 with secret key (only backend and venue scanners have key), Format: encrypted_data = encrypt(json_string), QR contains: {enc: base64(encrypted_data), v: 'v1'} where 'v' is version for future schema changes. (2) Generation on booking confirmation: After successful payment and booking creation, Generate QR code: import qrcode, data = json.dumps({booking_id, show_id, user_id, timestamp}), encrypted = aes_encrypt(data, secret_key), qr_img = qrcode.make(encrypted), upload to S3: s3.upload_file(qr_img, bucket, f'qr_codes/{booking_id}.png'), store URL in DB: UPDATE bookings SET qr_code_url={s3_url} WHERE booking_id={booking_id}. (3) Delivery to user: Email: attach QR code image + booking details, Mobile app: display QR code in 'My Tickets' section (works offline - QR stored locally), SMS: send link to web page with QR code. (4) Venue entry flow: User shows QR code at entry gate, Venue staff scans with mobile app or handheld scanner, Scanner sends: POST /v1/verify/qr with {qr_data, venue_id, timestamp}, Backend decrypts: booking_data = aes_decrypt(qr_data, secret_key), Validates: (a) Decryption successful (invalid QR returns error), (b) Booking exists: SELECT * FROM bookings WHERE booking_id={booking_id}, (c) Booking confirmed: status='CONFIRMED', (d) Show hasn't happened yet: showtime > NOW(), (e) Not already checked in: checked_in_at IS NULL, (f) Correct venue: showtime.venue_id matches venue_id from request. (5) Check-in atomically: BEGIN TRANSACTION; UPDATE bookings SET checked_in_at=NOW(), checked_in_venue={venue_id} WHERE booking_id={booking_id} AND checked_in_at IS NULL; if affected_rows == 0: ROLLBACK; return 'Already checked in or invalid'; COMMIT; Response: {valid: true, user_name, show_name, seat_ids, booking_id}. (6) Duplicate scan prevention: UNIQUE constraint on bookings(booking_id, checked_in_at) ensures single check-in, Attempting to scan again (e.g., user tries to enter twice) \u2192 query returns checked_in_at != NULL \u2192 reject with 'Already checked in at {time}', Fraud detection: if same QR scanned at multiple venues \u2192 flag for security review. (7) Offline verification (optional): For venues with poor connectivity, embed full booking details in QR: {booking_id, user_name, show_id, seat_ids, show_time, signature}, Signature: HMAC-SHA256(booking_data, secret_key) verifies authenticity, Venue scanner validates signature offline, syncs check-ins when connectivity restored. (8) QR code security features: Expiry: QR expires 2 hours after showtime (prevents reuse), One-time use: becomes invalid after successful scan, Tamper detection: any modification invalidates HMAC signature, Rate limiting: max 5 scan attempts per QR per minute (prevents brute force). (9) Multiple tickets per booking: If user books 3 seats, single QR code for entire booking, Scanner shows: 'Booking for 3 seats: L12, L13, L14', Entry coordinator allows 3 people through gate, Alternative: generate separate QR per seat (more granular tracking). (10) Lost ticket handling: User forgot phone or deleted email, Venue staff can look up by: name + phone + booking_id (last 4 digits), Backend validates identity: phone number matches booking, generates temporary QR code valid for 10 minutes: temp_qr = generate_temp(booking_id, expires_at=NOW()+10min), user can enter with temp QR. (11) Refund/cancellation handling: If booking cancelled after QR generated, mark QR as invalid: UPDATE bookings SET qr_code_valid=false, Scanner rejects: 'This ticket has been cancelled and refunded'. (12) Analytics: Track check-in metrics: on-time vs late arrivals, no-show rate (booked but not checked in), popular entry gates (load balancing), fraud attempts (invalid QRs, duplicate scans). Real example: Concert with 10K attendees, Average check-in time: 3 seconds per person, Peak entry rate: 500 people/minute (30 seconds before showtime), Zero fraud incidents (secure QR + check-in validation), 98% check-in rate (2% no-shows). Additional features: Push notification: 'Your show starts in 1 hour, have your QR ready', QR code styling: embed venue logo, colorful design (QR still scans), Wallet integration: add QR to Apple Wallet / Google Pay for quick access."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "10. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Volume",
                    "pairs": [
                      {
                        "k": "Daily Active Users",
                        "v": "100M DAU, with 10x spike during popular releases"
                      },
                      {
                        "k": "Peak Concurrency",
                        "v": "1M concurrent users during Marvel movie premiere"
                      },
                      {
                        "k": "Seats per Show",
                        "v": "200-500 (cinema), 5K-50K (stadium concerts)"
                      },
                      {
                        "k": "Booking Rate",
                        "v": "Normal: 100 bookings/sec, Spike: 1000 bookings/sec"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Timing & Latency",
                    "pairs": [
                      {
                        "k": "Search Latency",
                        "v": "<200ms (Elasticsearch with Redis cache)"
                      },
                      {
                        "k": "Seat Hold Time",
                        "v": "<100ms (Redis lock + DB transaction)"
                      },
                      {
                        "k": "Payment Confirmation",
                        "v": "<2 seconds (includes webhook processing)"
                      },
                      {
                        "k": "Hold Expiry",
                        "v": "15 minutes (Redis TTL + DB sweeper)"
                      },
                      {
                        "k": "Redis Lock TTL",
                        "v": "30 seconds (auto-release on crash)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Consistency & Locking",
                    "pairs": [
                      {
                        "k": "Seat Booking",
                        "v": "Strong consistency (CP from CAP) - no double booking"
                      },
                      {
                        "k": "Search Index",
                        "v": "Eventual consistency (AP from CAP) - 1-2s CDC lag"
                      },
                      {
                        "k": "Redis vs DB",
                        "v": "Redis for speed (1ms), DB for ACID (50ms)"
                      },
                      {
                        "k": "Lock Acquisition",
                        "v": "Redis SETNX: <10ms, DB FOR UPDATE: ~50ms"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching Strategy",
                    "pairs": [
                      {
                        "k": "Seat Map Cache",
                        "v": "Redis TTL 30 seconds (frequent updates needed)"
                      },
                      {
                        "k": "Search Results",
                        "v": "Redis TTL 10 minutes (slower changing data)"
                      },
                      {
                        "k": "Hold Data",
                        "v": "Redis TTL 15 minutes (automatic expiry)"
                      },
                      {
                        "k": "Cache Hit Rate",
                        "v": "95% for searches, 80% for seat maps during rush"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Business Metrics",
                    "pairs": [
                      {
                        "k": "Average Ticket Price",
                        "v": "$10-30 (cinema), $50-500 (concerts)"
                      },
                      {
                        "k": "Conversion Rate",
                        "v": "15-20% (searches \u2192 bookings)"
                      },
                      {
                        "k": "Hold Abandonment",
                        "v": "30-40% (holds expire without payment)"
                      },
                      {
                        "k": "Platform Fee",
                        "v": "10-15% of ticket price (convenience charge)"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: NEVER use Redis alone for seat booking. Always validate with database FOR UPDATE lock. Redis provides fast rejection but DB is source of truth with ACID guarantees. Without both, double booking will occur during concurrent requests."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How to prevent double booking?'. Answer must include: (1) Redis SETNX distributed lock for fast rejection, (2) PostgreSQL SELECT FOR UPDATE for ACID within transaction, (3) Redis lock auto-expires (30s) to prevent deadlock, (4) Both mechanisms together guarantee zero double bookings."
              },
              {
                "type": "tip",
                "text": "Hold expiry optimization: Redis TTL auto-deletes after 15 min (zero overhead), DB sweeper runs every 1 min to sync, 1-2 min lag acceptable since user sees expiry from Redis immediately. This saves 90% cleanup overhead vs checking every hold."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Why Redis TTL + DB sweeper combination. Redis gives automatic expiry with zero code, DB sweeper ensures eventual consistency with source of truth, system works even if sweeper fails temporarily (Redis cleans up memory)."
              },
              {
                "type": "warning",
                "text": "NEVER skip payment idempotency. Use hold_id as idempotency_key + UNIQUE constraint on bookings(hold_id). Without this, payment retry after timeout can cause double booking and double charge."
              },
              {
                "type": "tip",
                "text": "Spike handling: Queue-based reservation during popular shows processes 100 bookings/sec steadily vs 1000 concurrent \u2192 prevents database meltdown. Virtual waiting room gates 1M users \u2192 release 1K/sec \u2192 smooth traffic."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'How does CDC work?'. Answer: Cassandra commitlog \u2192 Debezium \u2192 Kafka \u2192 Elasticsearch indexer. Eventual consistency (1-2s lag) acceptable for search. Source DB remains available even if ES indexer fails."
              },
              {
                "type": "warning",
                "text": "NEVER trust client-provided seat status. Always fetch from DB/Redis with FOR UPDATE lock. Malicious users can modify requests to claim already-booked seats. Server-side validation is mandatory."
              },
              {
                "type": "tip",
                "text": "Bot prevention: CAPTCHA + rate limiting (3 holds/10min) + device fingerprinting + behavioral analysis. Detected bots \u2192 block IP + account for 24h. Honeypot seats trap bot scripts without affecting real users."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Database failover strategy. Primary + synchronous replica (lag <1s) + async replica (lag <10s). Failover in 2-3 minutes with zero data loss. Idempotency ensures retries work after recovery."
              }
            ],
            "tags": [
              "system-design",
              "ticket-booking",
              "BookMyShow",
              "Paytm",
              "District",
              "Ticketmaster",
              "distributed-locks",
              "Redis-SETNX",
              "seat-reservation",
              "double-booking-prevention",
              "payment-idempotency",
              "PostgreSQL-FOR-UPDATE",
              "Cassandra",
              "Elasticsearch-CDC",
              "Kafka-events",
              "TTL-expiry",
              "queue-based-booking",
              "spike-traffic-handling",
              "bot-prevention",
              "QR-code-verification"
            ]
          }
        },
        {
          "id": "03",
          "title": "System Design 3: E-Commerce Platform like Amazon / Flipkart Architecture Design | HLD / LLD",
          "videoUrl": "https://youtu.be/-wJuExkI97s",
          "description": "Learn how to design an e-commerce platform like Amazon or Flipkart from scratch! This video provides a detailed system design breakdown, covering scalability, microservices, API design, caching, event-driven architecture, and database choices.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/in348t33ng3ng3.webp?updatedAt=1743325968479",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/EcommerceSystemDesign%20(1).png?updatedAt=1746560605689",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "E-Commerce Platform (Amazon/Flipkart)",
              "oneLinerToMemorize": "Product search (Elasticsearch) \u2192 Cart management (Redis) \u2192 Inventory check (Redis lock) \u2192 Order placement \u2192 Payment (idempotency) \u2192 Order fulfillment (Kafka events)"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User should be able to search and finds products based on product title or names",
                  "Feature 2: User should be able to view the details of the product (description, image, available quantity, review)",
                  "Feature 3: User should place the select the quantity and move the item to cart",
                  "Feature 4: User should should be able to make the payment and do the checkout",
                  "Feature 5: User should be able to check the status of the order",
                  "Feature 6: Manage purchase of items having limited stock"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Orders per Second",
                        "v": "10 orders/second, 10M MAU (Monthly Active Users)"
                      },
                      {
                        "k": "Products",
                        "v": "100M+ products across multiple categories"
                      },
                      {
                        "k": "Search Queries",
                        "v": "100K+ searches per second during peak (sales events)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance & Availability",
                    "pairs": [
                      {
                        "k": "Low Latency",
                        "v": "Searching - fast product search <500ms with filters"
                      },
                      {
                        "k": "CAP Theorem",
                        "v": "Highly available with respect to searching and viewing the items, and highly consistent with respect to placing the order"
                      },
                      {
                        "k": "Inventory Consistency",
                        "v": "Strong consistency for inventory updates - no overselling of limited stock items"
                      },
                      {
                        "k": "Payment Reliability",
                        "v": "Exactly-once payment processing with idempotency"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities (Identify Core Entity)",
                "type": "bullets",
                "items": [
                  "Entity 1: User - Customer with user_id, name, email, password, address[], payment_methods[]",
                  "Entity 2: Product - Item with product_id, title, category, price, qty (quantity), currency, description, images[]",
                  "Entity 3: Cart - Shopping cart with cart_id, user_id, items (product_id, qty, price, currency)",
                  "Entity 4: Order - Purchase order with order_id, user_id, items (product_id, qty, price, currency), total, status",
                  "Entity 5: Checkout - Payment flow tracking"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Product Search & Details",
                    "pairs": [
                      {
                        "k": "GET /v1/product/search?q={searchTerm}",
                        "v": "Search products \u2192 List<ProductId (Partial Info)> with pagination"
                      },
                      {
                        "k": "GET /v1/product/{productId}",
                        "v": "Get product details \u2192 all product ids with extra CRUD operations and return Details"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cart Management",
                    "pairs": [
                      {
                        "k": "POST /v1/cart/add",
                        "v": "Add to cart {post body: all product ids + all other CRUD} \u2192 return cartId"
                      },
                      {
                        "k": "GET /v1/cart/{userId}",
                        "v": "Get cart items for user"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Checkout & Payment",
                    "pairs": [
                      {
                        "k": "POST /v1/checkout",
                        "v": "Initiate checkout {post body: all product ids with qty and price} \u2192 return orderId"
                      },
                      {
                        "k": "POST /v1/payment",
                        "v": "Process payment {post body: orderId}"
                      },
                      {
                        "k": "GET /v1/status/{orderId}",
                        "v": "Get order status"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design (from Image)",
                "type": "bullets",
                "items": [
                  "Clients \u2192 API Gateway: Authentication, authorization, routing",
                  "User Service \u2192 User DB (MySQL): User profiles, authentication",
                  "Search Service \u2192 Elasticsearch: Product search with full-text and filters, Redis cache for hot queries",
                  "Product Service \u2192 Product DB (MongoDB - DocumentDB): Product catalog with flexible schema, CDC to Elasticsearch via Kafka",
                  "Cart Service \u2192 Cart DB (PostgreSQL): Cart state with items (product_id, qty, price, currency)",
                  "Inventory Service \u2192 Inventory DB (PostgreSQL): Stock tracking, source of truth for quantity, indexed on product_id",
                  "Order Status Service \u2192 Order DB (MySQL): Order lifecycle tracking",
                  "Checkout Service: Orchestrates inventory check (Redis lock) \u2192 payment \u2192 order creation",
                  "Payment Service \u2192 Payment DB: Payment transactions, Payment Gateway integration",
                  "Kafka: Event streaming (order.created, payment.success, inventory.updated, order.shipped)",
                  "Notification System: Consumes Kafka events, sends email/SMS/push notifications",
                  "Redis: Cart caching, inventory locking, search result caching"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level - from Image)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Product Search (Elasticsearch + CDC)",
                    "steps": [
                      "User searches: GET /v1/product/search?q=laptop&category=electronics&priceMax=1000&sortBy=rating",
                      "Search Service queries: Elasticsearch with full-text + filters",
                      "ES query: { 'query': { 'bool': { 'must': [ { 'multi_match': { 'query': 'laptop', 'fields': ['title^3', 'description', 'category'] } } ], 'filter': [ { 'term': { 'category': 'electronics' } }, { 'range': { 'price': { 'lte': 1000 } } } ] } }, 'sort': [ { 'rating': 'desc' } ], 'size': 20 }",
                      "CDC Pipeline (from image): Product DB (MongoDB) \u2192 Sync Kafka \u2192 CDC Consumer \u2192 Elasticsearch indexing",
                      "Why CDC?: Product updates (price, title, qty) automatically sync to ES within 1-2 seconds, maintains search index fresh",
                      "Caching: Popular searches cached in Redis with TTL=10 min, key: hash(query + filters + sort)",
                      "Response: [{ product_id, title, category, price, qty, currency, image_url, rating }]",
                      "Note from image: Product stored in MongoDB (flexible schema for different product types - mobiles have specs, books have authors, etc.)"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Product Details & Images",
                    "steps": [
                      "User views product: GET /v1/product/{product_id}",
                      "Product Service queries: MongoDB db.products.findOne({product_id: {product_id}})",
                      "Document structure: { product_id, title, category, price, qty, currency, description, images[], specifications: {...}, reviews_summary: {avg_rating, total_reviews} }",
                      "Images: Stored in S3, URLs in MongoDB, served via CDN (CloudFront) for fast global access",
                      "Reviews: Separate Reviews DB, aggregated ratings cached in Redis, detailed reviews fetched on-demand",
                      "Caching: Product details cached in Redis with TTL=30 min, invalidated on product updates via Kafka event"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Add to Cart (Redis + PostgreSQL)",
                    "steps": [
                      "User adds item: POST /v1/cart/add with {user_id, product_id, qty: 2}",
                      "Cart Service flow: (1) Validates product exists and qty available (query Inventory Service), (2) Updates cart in PostgreSQL: INSERT INTO cart_items (cart_id, user_id, product_id, qty, price, currency) ON CONFLICT (cart_id, product_id) DO UPDATE SET qty=qty+2, (3) Caches cart in Redis: HSET cart:{user_id} product:{product_id} '{qty: 2, price: 899, currency: USD}' with TTL=7 days",
                      "Cart DB schema (PostgreSQL - from image): cart_id, user_id, items (product_id, qty, price, currency)",
                      "Why PostgreSQL for cart?: ACID guarantees, relational structure (user \u2192 cart \u2192 items), supports complex queries",
                      "Get cart: GET /v1/cart/{user_id} \u2192 Check Redis first (HGETALL cart:{user_id}), if miss fetch from PostgreSQL and cache",
                      "Update quantity: PATCH /v1/cart/{cart_id}/items/{product_id} with {qty: 5} \u2192 updates both PostgreSQL and Redis",
                      "Remove item: DELETE /v1/cart/{cart_id}/items/{product_id} \u2192 removes from PostgreSQL and Redis HDEL"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Checkout - Inventory Check with Redis Lock (CRITICAL)",
                    "steps": [
                      "User initiates checkout: POST /v1/checkout with {cart_id, shipping_address_id, payment_method_id}",
                      "Checkout Service orchestration (from image shows 'check the stock availability before checkout'):",
                      "Step 1: Fetch cart items from Cart DB",
                      "Step 2: FOR EACH product in cart: Acquire Redis distributed lock: SETNX lock:inventory:{product_id} {checkout_session_id} EX 30 (30 second expiry)",
                      "Step 3: If lock acquired (SETNX returns 1): Query Inventory Service: SELECT qty FROM inventory WHERE product_id={product_id} FOR UPDATE (pessimistic lock)",
                      "Step 4: Validate qty >= cart_qty, if insufficient: Release lock (DEL lock:inventory:{product_id}), return error 'Product X has insufficient stock'",
                      "Step 5: If all products have sufficient stock: Reserve inventory (don't deduct yet): UPDATE inventory SET reserved_qty = reserved_qty + {cart_qty} WHERE product_id={product_id}",
                      "Step 6: Create order in PENDING_PAYMENT status: INSERT INTO orders (order_id, user_id, items, total, status='PENDING_PAYMENT', created_at)",
                      "Step 7: Release Redis locks: FOR EACH product DEL lock:inventory:{product_id}",
                      "Step 8: Return {order_id, payment_url} to client",
                      "Note: Redis lock prevents race condition where multiple users checkout last item simultaneously, Inventory DB has 'reserved_qty' column to hold stock during payment"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Payment Processing with Idempotency",
                    "steps": [
                      "User redirected to payment: Payment Gateway (Stripe/Razorpay) with payment_url",
                      "Payment Service (from image): Creates payment intent with idempotency_key=order_id",
                      "Payment Gateway processes: User enters card details, gateway charges card",
                      "Webhook: POST /webhooks/payment with {order_id, payment_id, status: 'success', amount}",
                      "Payment Service webhook handler: (1) Validates signature, (2) Idempotency check: SELECT * FROM payments WHERE order_id={order_id}, if exists return 200 (already processed), (3) BEGIN TRANSACTION; INSERT INTO payments (payment_id, order_id, amount, status='SUCCESS', timestamp); UPDATE orders SET status='PAYMENT_CONFIRMED', payment_id={payment_id}; COMMIT;",
                      "Deduct inventory (from image shows 'CDC to invalidate and update qty change'): UPDATE inventory SET qty = qty - {reserved_qty}, reserved_qty = reserved_qty - {reserved_qty} WHERE product_id IN ({order_product_ids})",
                      "Publish Kafka events: 'payment.success' and 'order.confirmed' topics with {order_id, user_id, items, total}",
                      "Image shows: Kafka \u2192 Consumer updates Payment info, triggers Notification System",
                      "Clear cart: DELETE FROM cart_items WHERE cart_id={cart_id} (order placed, cart no longer needed)",
                      "If payment fails: Webhook 'payment.failed' \u2192 Release reserved inventory: UPDATE inventory SET reserved_qty = reserved_qty - {cart_qty}, order status='PAYMENT_FAILED'"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Order Status Tracking (Kafka Event-Driven)",
                    "steps": [
                      "Order lifecycle: PENDING_PAYMENT \u2192 PAYMENT_CONFIRMED \u2192 PROCESSING \u2192 SHIPPED \u2192 OUT_FOR_DELIVERY \u2192 DELIVERED",
                      "Status updates via Kafka (from image): Order Status Service consumes 'order.confirmed', 'order.shipped', 'order.delivered' events",
                      "Warehouse system: Picks items, updates via POST /v1/orders/{order_id}/ship \u2192 Publishes 'order.shipped' to Kafka",
                      "Delivery partner: Updates location and status via API \u2192 Publishes 'order.out_for_delivery' to Kafka",
                      "User checks status: GET /v1/status/{order_id} \u2192 Order Status Service queries Order DB: SELECT status, tracking_id, estimated_delivery FROM orders WHERE order_id={order_id}",
                      "Real-time updates: WebSocket connection or polling (every 30s) for tracking updates",
                      "Notification flow (from image): Kafka events \u2192 Notification System \u2192 Email/SMS/Push: 'Order confirmed', 'Order shipped', 'Out for delivery', 'Delivered'"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Inventory Management & CDC (Source of Truth)",
                    "steps": [
                      "Inventory DB (PostgreSQL - from image): Indexed on product_id, columns: {product_id, qty, reserved_qty, warehouse_id, last_updated}",
                      "Inventory is 'Qty-source of truth' as noted in image - authoritative for stock levels",
                      "Stock updates: (1) New stock arrival: UPDATE inventory SET qty = qty + {new_stock} WHERE product_id={product_id}, (2) Publish Kafka 'inventory.updated' event",
                      "CDC pipeline (from image): Inventory DB \u2192 CDC (Change Data Capture) \u2192 Kafka \u2192 Updates Redis cache, Elasticsearch product qty",
                      "Redis caching: HSET inventory:{product_id} 'qty' {qty} 'reserved_qty' {reserved_qty} with TTL=5 min, used for quick availability checks",
                      "Low stock alerts: Background job monitors inventory.qty < threshold (e.g., 10 units) \u2192 notifies warehouse to restock",
                      "Overselling prevention: reserved_qty column ensures items in checkout but not yet paid don't show as available, actual deduction only after payment success"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Product Catalog Management (MongoDB - Flexible Schema)",
                    "steps": [
                      "Why MongoDB for products (from image shows 'MongoDB (DocumentDB)'): Different product categories have different attributes - Mobile (specs: RAM, storage, camera), Book (author, ISBN, publisher), Clothing (size, color, material)",
                      "Schema-less advantage: No need to define all possible attributes upfront, easy to add new product types",
                      "Example document: { product_id: 'LAPTOP_123', title: 'MacBook Pro', category: 'electronics', price: 1999, qty: 50, currency: 'USD', specs: { ram: '16GB', storage: '512GB SSD', processor: 'M3' }, images: ['s3://...'], brand: 'Apple', warranty: '1 year' }",
                      "Indexing: db.products.createIndex({product_id: 1}), db.products.createIndex({category: 1, price: 1}), db.products.createIndex({title: 'text', description: 'text'}) for basic search",
                      "CDC to Elasticsearch (from image): MongoDB oplog \u2192 Kafka \u2192 ES indexer, ensures search index always in sync with product catalog",
                      "Product updates: Seller updates price/qty via admin API \u2192 MongoDB updated \u2192 CDC pipeline syncs to ES and Redis within 1-2 seconds"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Search Optimization (Elasticsearch Architecture)",
                    "steps": [
                      "ES Index mapping: PUT /products { 'mappings': { 'properties': { 'product_id': {'type': 'keyword'}, 'title': {'type': 'text', 'analyzer': 'standard'}, 'description': {'type': 'text'}, 'category': {'type': 'keyword'}, 'price': {'type': 'float'}, 'qty': {'type': 'integer'}, 'rating': {'type': 'float'}, 'brand': {'type': 'keyword'}, 'images': {'type': 'keyword'} } } }",
                      "Synonyms: Configure analyzer with synonyms (laptop = notebook, mobile = phone) for better search relevance",
                      "Faceted search: Aggregations for filters: 'aggs': { 'brands': { 'terms': { 'field': 'brand' } }, 'price_ranges': { 'range': { 'field': 'price', 'ranges': [{'to': 500}, {'from': 500, 'to': 1000}, {'from': 1000}] } } } \u2192 Returns counts for UI filters",
                      "Autocomplete: Edge n-gram tokenizer for type-ahead suggestions, updates as user types (debounced 300ms)",
                      "Performance: ES cluster with 10 nodes, 5 primary shards, 1 replica per shard, handles 100K queries/sec during sales events",
                      "Caching: Redis caches top 10K search queries with results, TTL=10 min, serves 60-70% from cache"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: System Integration via Kafka (Event-Driven Architecture)",
                    "steps": [
                      "Kafka topics (from image shows Sync Kafka connecting components):",
                      "Topic 1: 'product.updated' - Product catalog changes \u2192 consumed by ES indexer, cache invalidator",
                      "Topic 2: 'order.created' - New order placed \u2192 consumed by Inventory Service (reserve stock), Notification Service",
                      "Topic 3: 'payment.success' - Payment confirmed \u2192 consumed by Order Service (update status), Inventory Service (deduct stock), Notification Service",
                      "Topic 4: 'order.shipped' - Warehouse ships order \u2192 consumed by Order Status Service, Notification Service, Tracking Service",
                      "Topic 5: 'inventory.updated' - Stock levels changed \u2192 consumed by Product Service (update qty), ES indexer, Redis cache updater",
                      "Consumer groups: Each service has dedicated consumer group, enables horizontal scaling and fault tolerance",
                      "Event schema: {event_id, event_type, timestamp, payload: {...}, metadata: {source_service, correlation_id}}",
                      "Idempotent consumers: Use event_id to deduplicate, prevents duplicate processing if event replayed",
                      "Dead letter queue: Failed events moved to DLQ after 3 retries, manual investigation and replay"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details (from Image)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "User DB (MySQL - from image)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "password",
                        "v": "varchar(255) (bcrypt hash)"
                      },
                      {
                        "k": "address[]",
                        "v": "JSON [{street, city, state, zip, country, is_default}]"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Product DB (MongoDB/DocumentDB - from image)",
                    "pairs": [
                      {
                        "k": "product_id",
                        "v": "string (unique identifier)"
                      },
                      {
                        "k": "title",
                        "v": "string (product name)"
                      },
                      {
                        "k": "category",
                        "v": "string (electronics, books, clothing)"
                      },
                      {
                        "k": "price",
                        "v": "float"
                      },
                      {
                        "k": "qty",
                        "v": "integer (available quantity)"
                      },
                      {
                        "k": "currency",
                        "v": "string (USD, EUR, INR)"
                      },
                      {
                        "k": "description",
                        "v": "string (long text)"
                      },
                      {
                        "k": "images",
                        "v": "array of strings (S3 URLs)"
                      },
                      {
                        "k": "specifications",
                        "v": "object (flexible schema - RAM, storage, author, ISBN, size, color, etc.)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cart DB (PostgreSQL - from image)",
                    "pairs": [
                      {
                        "k": "cart_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "items",
                        "v": "JSONB [{product_id, qty, price, currency}] or separate cart_items table"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cart_Items (PostgreSQL - normalized approach)",
                    "pairs": [
                      {
                        "k": "cart_id",
                        "v": "uuid"
                      },
                      {
                        "k": "product_id",
                        "v": "uuid"
                      },
                      {
                        "k": "qty",
                        "v": "integer"
                      },
                      {
                        "k": "price",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "currency",
                        "v": "varchar(3)"
                      },
                      {
                        "k": "Composite PK",
                        "v": "(cart_id, product_id)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Inventory DB (PostgreSQL - from image, 'Qty-source of truth')",
                    "pairs": [
                      {
                        "k": "product_id",
                        "v": "uuid PRIMARY KEY, INDEXED"
                      },
                      {
                        "k": "qty",
                        "v": "integer (available stock)"
                      },
                      {
                        "k": "reserved_qty",
                        "v": "integer (reserved during checkout, not yet paid)"
                      },
                      {
                        "k": "warehouse_id",
                        "v": "uuid (location)"
                      },
                      {
                        "k": "last_updated",
                        "v": "timestamp"
                      },
                      {
                        "k": "Index",
                        "v": "INDEX on product_id for fast lookups during checkout"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Order DB (MySQL - from image)",
                    "pairs": [
                      {
                        "k": "order_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "items",
                        "v": "JSON [{product_id, qty, price, currency}]"
                      },
                      {
                        "k": "total",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING_PAYMENT, PAYMENT_CONFIRMED, PROCESSING, SHIPPED, DELIVERED, CANCELLED)"
                      },
                      {
                        "k": "payment_id",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "shipping_address",
                        "v": "JSON {street, city, state, zip}"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp INDEXED"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Payment DB (from image)",
                    "pairs": [
                      {
                        "k": "payment_id",
                        "v": "varchar(255) PRIMARY KEY (from gateway)"
                      },
                      {
                        "k": "order_id",
                        "v": "uuid FK \u2192 Orders, UNIQUE (idempotency)"
                      },
                      {
                        "k": "timestamp",
                        "v": "timestamp"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, SUCCESS, FAILED, REFUNDED)"
                      },
                      {
                        "k": "amount",
                        "v": "decimal(10,2)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Caching & Locks",
                    "pairs": [
                      {
                        "k": "cart:{userId}",
                        "v": "HASH {product_id: {qty, price, currency}} TTL 7 days"
                      },
                      {
                        "k": "product:{productId}",
                        "v": "STRING (JSON product details) TTL 30 min"
                      },
                      {
                        "k": "search:{hash(query)}",
                        "v": "STRING (JSON search results) TTL 10 min"
                      },
                      {
                        "k": "inventory:{productId}",
                        "v": "HASH {qty, reserved_qty} TTL 5 min"
                      },
                      {
                        "k": "lock:inventory:{productId}",
                        "v": "STRING {checkout_session_id} EX 30 (distributed lock)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch - Product Index",
                    "pairs": [
                      {
                        "k": "product_id",
                        "v": "keyword"
                      },
                      {
                        "k": "title",
                        "v": "text (analyzed for full-text search)"
                      },
                      {
                        "k": "description",
                        "v": "text"
                      },
                      {
                        "k": "category",
                        "v": "keyword (exact match for filters)"
                      },
                      {
                        "k": "price",
                        "v": "float"
                      },
                      {
                        "k": "qty",
                        "v": "integer (synced from Inventory via CDC)"
                      },
                      {
                        "k": "rating",
                        "v": "float"
                      },
                      {
                        "k": "brand",
                        "v": "keyword"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "key-architectural-decisions",
                "title": "8. Key Architectural Decisions from Image",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Database Choices",
                    "pairs": [
                      {
                        "k": "User DB - MySQL",
                        "v": "Why: ACID transactions for user accounts, relational integrity (user \u2192 addresses \u2192 orders)"
                      },
                      {
                        "k": "Product DB - MongoDB",
                        "v": "Why: Flexible schema for diverse product types (electronics, books, clothing have different attributes), easy to add new categories"
                      },
                      {
                        "k": "Cart DB - PostgreSQL",
                        "v": "Why: ACID guarantees for cart operations, relational structure (cart \u2192 items), supports complex queries"
                      },
                      {
                        "k": "Inventory - PostgreSQL",
                        "v": "Why: Source of truth for stock, requires ACID (strong consistency), supports SELECT FOR UPDATE (pessimistic locking)"
                      },
                      {
                        "k": "Order DB - MySQL",
                        "v": "Why: ACID transactions critical for orders, relational (order \u2192 items \u2192 payment), complex queries for reporting"
                      }
                    ]
                  },
                  {
                    "groupTitle": "CDC (Change Data Capture) from Image",
                    "pairs": [
                      {
                        "k": "Product \u2192 ES",
                        "v": "MongoDB \u2192 Kafka \u2192 Elasticsearch indexer, keeps search up-to-date within 1-2s"
                      },
                      {
                        "k": "Inventory \u2192 Redis",
                        "v": "PostgreSQL \u2192 CDC \u2192 Kafka \u2192 Redis cache updater, invalidates and updates qty changes"
                      },
                      {
                        "k": "Why CDC?",
                        "v": "Decouples source DB from derived data stores (ES, Redis), eventual consistency acceptable for search/cache, enables independent scaling"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis Lock Pattern (Critical for Inventory)",
                    "pairs": [
                      {
                        "k": "Lock Acquisition",
                        "v": "SETNX lock:inventory:{product_id} {session_id} EX 30"
                      },
                      {
                        "k": "Purpose",
                        "v": "Prevents race condition where multiple users checkout last item simultaneously"
                      },
                      {
                        "k": "Timeout",
                        "v": "30 seconds auto-expiry prevents deadlock if service crashes"
                      },
                      {
                        "k": "Pattern",
                        "v": "Acquire lock \u2192 Check DB stock \u2192 Reserve \u2192 Release lock (even on failure)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Kafka Event-Driven Architecture",
                    "pairs": [
                      {
                        "k": "Image shows",
                        "v": "'Sync Kafka' connecting Product, Inventory, Payment, Order, Notification services"
                      },
                      {
                        "k": "Benefits",
                        "v": "(1) Decouples services, (2) Async processing, (3) Event replay for debugging, (4) Scales independently per consumer"
                      },
                      {
                        "k": "Topics",
                        "v": "product.updated, order.created, payment.success, inventory.updated, order.shipped"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "9. Scaling & Optimization Techniques",
                "type": "bullets",
                "items": [
                  "Technique 1: Elasticsearch Sharding - Product index sharded by category or hash(product_id), enables parallel queries",
                  "Technique 2: Redis Caching - Cart (7d TTL), Product details (30min), Search results (10min), Inventory (5min), 70% cache hit rate",
                  "Technique 3: CDN for Images - Product images served via CloudFront, 95% cache hit, <50ms latency globally",
                  "Technique 4: Database Read Replicas - MySQL/PostgreSQL replicas for read queries (product views, order history), writes to primary",
                  "Technique 5: CDC Pipeline - MongoDB/PostgreSQL \u2192 Kafka \u2192 Elasticsearch/Redis, decouples data sync, eventual consistency",
                  "Technique 6: Redis Distributed Locks - Prevents inventory overselling during concurrent checkouts, SETNX with expiry",
                  "Technique 7: Kafka Event Streaming - Decouples order flow, async notifications, enables event replay and audit",
                  "Technique 8: Connection Pooling - API servers maintain 200 DB connections per pool, prevents exhaustion",
                  "Technique 9: API Rate Limiting - 1000 req/min per user, protects against abuse and DDoS",
                  "Technique 10: Database Partitioning - Partition orders table by month (order_date), improves query performance",
                  "Technique 11: Lazy Loading - Paginate search results (20 per page), infinite scroll, reduces initial payload",
                  "Technique 12: Inventory Reservation - reserved_qty column holds stock during checkout\u2192payment flow, prevents overselling"
                ]
              },
              {
                "id": "interview-qa",
                "title": "10. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you prevent overselling when multiple users try to buy the last item?",
                    "a": "Multi-layer approach with distributed locks + database pessimistic locking (from image's 'check the stock availability before checkout' with Redis Lock): (1) Redis distributed lock BEFORE inventory check: During checkout, FOR EACH product in cart: SETNX lock:inventory:{product_id} {checkout_session_id} EX 30, if SETNX returns 0 (lock exists) \u2192 wait and retry or return 'Another user is checking out this item, please wait', if returns 1 (acquired) \u2192 proceed to step 2, (2) Database pessimistic lock: SELECT qty, reserved_qty FROM inventory WHERE product_id={product_id} FOR UPDATE (row-level exclusive lock), (3) Validate: if qty < cart_qty \u2192 Release Redis lock, return 'Insufficient stock', (4) Reserve inventory: UPDATE inventory SET reserved_qty = reserved_qty + {cart_qty} WHERE product_id={product_id}, this removes items from available pool but doesn't deduct yet (payment might fail), (5) Release Redis lock: DEL lock:inventory:{product_id}, (6) Create order with status='PENDING_PAYMENT', (7) Payment webhook success: UPDATE inventory SET qty = qty - {cart_qty}, reserved_qty = reserved_qty - {cart_qty}, now actually deduct from stock, (8) Payment failure: UPDATE inventory SET reserved_qty = reserved_qty - {cart_qty}, release reservation. Why both locks?: Redis lock is fast (1-2ms) early rejection for concurrent requests (100 users trying to checkout simultaneously), PostgreSQL FOR UPDATE ensures ACID guarantees within transaction, prevents phantom reads. Lock expiry: Redis lock auto-expires in 30s if service crashes, prevents deadlock. Example: Product has qty=1, User A and User B both click checkout \u2192 User A's SETNX succeeds \u2192 User B's SETNX fails (waits) \u2192 User A reserves qty \u2192 User B's SETNX succeeds after A releases \u2192 User B checks qty=0 (reserved=1) \u2192 Returns 'Out of stock' \u2192 No overselling. Reserved_qty column explained: qty=10, reserved_qty=3 means 10 total in warehouse, 3 in active checkouts, 7 available for new purchases, prevents showing items as available when they're locked in checkout flow."
                  },
                  {
                    "q": "How do you implement product search with filters (category, price, rating)?",
                    "a": "Elasticsearch-based search with CDC sync (from image shows Product \u2192 Sync Kafka \u2192 Elasticsearch): (1) Index mapping: PUT /products { 'mappings': { 'properties': { 'product_id': {'type': 'keyword'}, 'title': {'type': 'text', 'analyzer': 'standard', 'fields': {'keyword': {'type': 'keyword'}}}, 'description': {'type': 'text'}, 'category': {'type': 'keyword'}, 'price': {'type': 'float'}, 'rating': {'type': 'float'}, 'brand': {'type': 'keyword'}, 'qty': {'type': 'integer'} } } }, (2) CDC pipeline (from image): MongoDB product updates \u2192 Kafka 'product.updated' topic \u2192 ES indexer consumer \u2192 index.update(product_id, updated_fields), keeps ES in sync within 1-2 seconds, (3) Search query: GET /v1/product/search?q=laptop&category=electronics&priceMin=500&priceMax=1500&rating>=4 \u2192 Backend builds: POST /products/_search { 'query': { 'bool': { 'must': [ { 'multi_match': { 'query': 'laptop', 'fields': ['title^3', 'description', 'brand'], 'type': 'best_fields' } } ], 'filter': [ { 'term': { 'category': 'electronics' } }, { 'range': { 'price': { 'gte': 500, 'lte': 1500 } } }, { 'range': { 'rating': { 'gte': 4 } } }, { 'range': { 'qty': { 'gt': 0 } } } ] } }, 'sort': [ { 'rating': 'desc' } ], 'from': 0, 'size': 20 }, (4) Scoring: 'title^3' boosts title matches 3x over description, relevance score combines text match + filters, (5) Faceted search: Add aggregations: 'aggs': { 'categories': { 'terms': { 'field': 'category' } }, 'price_ranges': { 'range': { 'field': 'price', 'ranges': [{'to': 500}, {'from': 500, 'to': 1000}, {'from': 1000}] } }, 'brands': { 'terms': { 'field': 'brand', 'size': 20 } } } \u2192 Returns counts: {Electronics: 5000, Books: 3000, ...} for UI filter checkboxes, (6) Autocomplete: Separate index with edge_ngram tokenizer, queries on every keystroke (debounced 300ms), suggests 'laptop', 'laptop bag', 'laptop stand', (7) Synonyms: Configure analyzer: 'laptop' \u2192 ['laptop', 'notebook', 'portable computer'], improves recall, (8) Caching: Redis caches popular queries (key: hash(query + filters + sort)) with TTL=10 min, serves 60-70% from cache, (9) Performance: ES cluster 10 nodes, 5 primary shards (100M products / 5 = 20M per shard), query p95 <100ms. Why Elasticsearch over PostgreSQL full-text?: ES designed for full-text search with relevance scoring, fuzzy matching, synonyms, faceted search - PostgreSQL's tsvector can't match this. MongoDB vs PostgreSQL for products?: Image shows MongoDB (DocumentDB) because different product types have vastly different schemas (electronics: specs={RAM, processor}, books: {author, ISBN}, clothing: {size, color}) - flexible schema avoids sparse columns and NULL hell in relational DB."
                  },
                  {
                    "q": "How do you manage shopping cart state across sessions and devices?",
                    "a": "Hybrid Redis + PostgreSQL cart management (from image shows Cart DB PostgreSQL + Redis caching): (1) Cart creation: User logs in \u2192 Check if cart exists: SELECT cart_id FROM carts WHERE user_id={user_id}, if not: INSERT INTO carts (cart_id, user_id, created_at) VALUES ({uuid}, {user_id}, now()), (2) Add to cart: POST /v1/cart/add with {product_id, qty: 2}, (a) Validate product exists and has stock (query Product Service), (b) PostgreSQL update: INSERT INTO cart_items (cart_id, product_id, qty, price, currency) VALUES ({cart_id}, {product_id}, 2, 899, 'USD') ON CONFLICT (cart_id, product_id) DO UPDATE SET qty = cart_items.qty + 2, updated_at = now(), (c) Redis cache: HSET cart:{user_id} product:{product_id} '{qty: 2, price: 899, currency: USD, added_at: timestamp}' with TTL=7 days, (3) Get cart: (a) Check Redis: HGETALL cart:{user_id}, if hit: return cached data (1ms latency), (b) If miss: Query PostgreSQL: SELECT ci.product_id, ci.qty, ci.price, ci.currency, p.title, p.image_url FROM cart_items ci JOIN products p ON ci.product_id = p.product_id WHERE ci.cart_id={cart_id}, then cache in Redis, (4) Update quantity: PATCH /v1/cart/items/{product_id} with {qty: 5} \u2192 Update PostgreSQL and Redis atomically, (5) Remove item: DELETE /v1/cart/items WHERE cart_id={cart_id} AND product_id={product_id} \u2192 Also Redis HDEL cart:{user_id} product:{product_id}, (6) Cross-device sync: Cart tied to user_id (not session_id), user logs in on mobile \u2192 same cart appears (fetched from PostgreSQL), Redis cache per user ensures fast access, (7) Guest cart: For non-logged-in users, use session_id instead of user_id, store in Redis only with TTL=24h, on login: migrate guest cart to user cart: UPDATE cart_items SET cart_id={user_cart_id} WHERE cart_id={guest_cart_id}, (8) Cart expiry: Background job deletes carts inactive >30 days: DELETE FROM carts WHERE updated_at < now() - INTERVAL 30 DAY, (9) Price updates: If product price changes after adding to cart, show both: 'Added at $899, now $799', let user decide to keep or remove, (10) Stock validation at checkout: Before payment, re-validate all items still in stock, if any out of stock \u2192 remove from cart, notify user. Why PostgreSQL for cart?: ACID guarantees (cart updates must be reliable), survives Redis cache evictions (source of truth), supports complex queries (JOIN with products for enriched data), relational integrity (cart \u2192 items FK). Redis for performance: Cart reads are 10x more frequent than writes (user browses, views cart multiple times before checkout), Redis serves reads in <1ms, PostgreSQL ~50ms, at scale 10K concurrent users \u00d7 100 cart reads/min = 1M reads/min \u2192 Redis essential. TTL strategy: 7 days for active carts (user likely to return), auto-cleanup prevents memory bloat, PostgreSQL keeps cart forever (until user deletes account) for business analytics."
                  },
                  {
                    "q": "How do you handle the checkout flow from cart to order confirmation?",
                    "a": "Multi-step checkout orchestration (from image shows Checkout \u2192 Redis Lock \u2192 Kafka flow): (1) Initiate checkout: POST /v1/checkout with {cart_id, shipping_address_id, payment_method_id}, (2) Checkout Service orchestration: Step 1: Fetch cart items: SELECT product_id, qty, price FROM cart_items WHERE cart_id={cart_id}, Step 2: Acquire distributed locks: FOR EACH product in cart: SETNX lock:inventory:{product_id} {checkout_session_id} EX 30, if any lock fails \u2192 abort, release acquired locks, return 'Product being checked out by another user', Step 3: Validate inventory (with DB lock): FOR EACH product: SELECT qty, reserved_qty FROM inventory WHERE product_id={product_id} FOR UPDATE, if qty < cart_qty \u2192 abort, release locks, return 'Product X out of stock', Step 4: Reserve inventory: UPDATE inventory SET reserved_qty = reserved_qty + {cart_qty} WHERE product_id={product_id}, Step 5: Calculate total: total = SUM(price \u00d7 qty) + shipping_fee + tax, Step 6: Create order: BEGIN TRANSACTION; INSERT INTO orders (order_id, user_id, items, total, status='PENDING_PAYMENT', shipping_address, created_at); COMMIT;, Step 7: Release Redis locks: FOR EACH product: DEL lock:inventory:{product_id}, Step 8: Create payment intent: Call Payment Service \u2192 payment_gateway.createPaymentIntent({amount: total, currency, idempotency_key: order_id}), Step 9: Return to client: {order_id, payment_url, expires_at: now() + 15min}, (3) User completes payment: Redirected to Payment Gateway, enters card details, confirms payment, (4) Payment webhook: POST /webhooks/payment with {order_id, payment_id, status: 'success'}, (a) Idempotency check: SELECT * FROM payments WHERE order_id={order_id}, if exists return 200 (already processed), (b) BEGIN TRANSACTION; INSERT INTO payments (payment_id, order_id, amount, status='SUCCESS', timestamp); UPDATE orders SET status='PAYMENT_CONFIRMED', payment_id={payment_id} WHERE order_id={order_id}; UPDATE inventory SET qty = qty - {cart_qty}, reserved_qty = reserved_qty - {cart_qty} WHERE product_id IN ({order_product_ids}); DELETE FROM cart_items WHERE cart_id={cart_id}; COMMIT;, (c) Publish Kafka events: 'payment.success' topic: {order_id, user_id, items, total, timestamp}, 'order.confirmed' topic: {order_id, items, shipping_address, warehouse_id}, (5) Kafka consumers: (a) Notification Service \u2192 sends email/SMS 'Order confirmed', (b) Inventory Service \u2192 updates Redis cache (invalidate product:{product_id} cache, update inventory:{product_id} qty), (c) Warehouse Service \u2192 creates pick list for order fulfillment, (6) Payment timeout: Background job checks orders with status='PENDING_PAYMENT' created >15 min ago \u2192 Release reserved inventory: UPDATE inventory SET reserved_qty = reserved_qty - {cart_qty}, order status \u2192 'PAYMENT_TIMEOUT', send notification 'Checkout expired, items returned to stock', (7) Failure scenarios: (a) Inventory insufficient during reserve \u2192 abort checkout, don't create order, release locks, (b) Payment fails (card declined) \u2192 webhook 'payment.failed' \u2192 release reserved_qty, order status='PAYMENT_FAILED', (c) Service crash during checkout \u2192 Redis locks auto-expire after 30s, reserved_qty released by timeout job. Why this flow?: (1) Locks prevent race conditions on limited stock, (2) Reserved_qty column ensures stock not shown as available during 15min payment window, (3) Idempotency prevents double charging on webhook replay, (4) Kafka decouples payment from fulfillment (warehouse can be slow, doesn't block checkout), (5) Transaction ensures atomicity (payment recorded + inventory deducted + cart cleared all-or-nothing). Performance: Average checkout time 2-3 seconds (lock acquisition ~10ms, DB queries ~50ms, payment intent creation ~200ms), can handle 10K checkouts/min with horizontal scaling of Checkout Service."
                  },
                  {
                    "q": "How do you implement CDC (Change Data Capture) for syncing Product DB to Elasticsearch?",
                    "a": "CDC pipeline for near real-time search index updates (from image shows Product \u2192 Sync Kafka \u2192 Elasticsearch): (1) CDC source setup: (a) MongoDB: Enable oplog (operations log) which records all write operations, use MongoDB Change Streams to listen: db.products.watch([{$match: {operationType: {$in: ['insert', 'update', 'replace', 'delete']}}}]), (b) PostgreSQL (for other tables): Enable logical replication, use Debezium connector to capture WAL (Write-Ahead Log) changes, (2) Kafka connector: (a) MongoDB Change Stream \u2192 Kafka Connect MongoDB Source Connector \u2192 publishes to Kafka topic 'product.changes', (b) Event schema: {operation: 'update', product_id: 'LAPTOP_123', fullDocument: {product_id, title, price, qty, ...}, timestamp}, (3) Elasticsearch indexer consumer: (a) Kafka Consumer Group 'es-product-indexer' subscribes to 'product.changes', (b) Consumer code: for event in kafka.consume(): if event.operation == 'insert' or 'update': es.index(index='products', id=event.product_id, body=event.fullDocument), elif event.operation == 'delete': es.delete(index='products', id=event.product_id), (c) Batch indexing: Buffer 100 events, bulk index to ES every 2 seconds for efficiency: es.bulk(operations=[...]), (4) Error handling: (a) ES indexing fails \u2192 Log error, push event to DLQ (Dead Letter Queue), retry with exponential backoff, (b) Kafka consumer lag monitoring: Alert if lag >1000 events (indexer falling behind), auto-scale consumer instances, (5) Initial load: On first setup, bulk export all products from MongoDB \u2192 Elasticsearch: db.products.find().forEach(doc => es.index({...})), then start CDC to keep in sync, (6) Consistency: (a) Eventual consistency: Product updated in MongoDB \u2192 1-2 seconds later appears in ES search, acceptable for e-commerce search, (b) Ordering guarantee: Kafka partition by product_id ensures updates for same product processed in order, (7) Performance: (a) MongoDB oplog tail: <10ms latency to capture change, (b) Kafka latency: ~50ms end-to-end, (c) ES indexing: Bulk API can index 10K docs/sec, (d) Total lag: Product update \u2192 searchable in 1-2 seconds, (8) Why CDC over direct writes?: (a) Separation of concerns: Product Service doesn't need to know about ES, (b) Fault tolerance: If ES down, MongoDB still accepts writes, indexer catches up later, (c) Replay: Can rebuild entire ES index from Kafka topic (retention 7 days), (d) Multiple consumers: Same events used for Redis cache invalidation, analytics, audit logs. Alternative: Dual writes (Product Service writes to both MongoDB and ES) - bad because: (1) Tight coupling, (2) Transaction complexity (what if ES write fails?), (3) Product Service needs to know search implementation. Real-world: Amazon uses similar CDC pipeline with Kinesis Streams, millions of product updates/hour, ES cluster 100+ nodes, <5 second lag p99."
                  },
                  {
                    "q": "How do you design the payment integration with idempotency and webhook handling?",
                    "a": "Payment flow with strong consistency guarantees (from image shows Payment Svc \u2192 Payment Gateway, Kafka \u2192 Consumer updates payment info): (1) Payment initiation: Checkout Service calls Payment Service: POST /v1/payments with {order_id, amount: 1499.99, currency: 'USD', user_id, return_url}, (2) Payment Service creates intent: (a) Generate idempotency_key = order_id (ensures same order can't be charged twice), (b) Call Stripe/Razorpay API: stripe.paymentIntents.create({ amount: 149999, currency: 'usd', customer: stripe_customer_id, metadata: {order_id, user_id}, idempotency_key: order_id }), (c) Stripe returns: {payment_intent_id, client_secret, status: 'requires_payment_method'}, (d) Store in Payment DB: INSERT INTO payments (payment_id, order_id, amount, currency, status='PENDING', created_at) VALUES ({payment_intent_id}, {order_id}, 1499.99, 'USD', now()), (e) Return to client: {payment_url: 'https://checkout.stripe.com/...'}, (3) User payment flow: User redirected to Stripe checkout page, enters card details, Stripe processes (3D Secure if needed), on success: Stripe redirects back to return_url with payment_intent_id, (4) Webhook handling (CRITICAL): Stripe sends POST /webhooks/payment with {type: 'payment_intent.succeeded', data: {object: {id: payment_intent_id, amount, status: 'succeeded', metadata: {order_id}}}}, (a) Signature validation: Extract stripe_signature header, verify HMAC: stripe.webhooks.constructEvent(payload, signature, webhook_secret), prevents replay attacks and unauthorized requests, (b) Idempotency check: SELECT * FROM payments WHERE payment_id={payment_intent_id}, if status='SUCCESS' \u2192 return 200 OK (webhook already processed, duplicate event), (c) Process payment: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; UPDATE payments SET status='SUCCESS', gateway_response=jsonb({webhook_payload}), updated_at=now() WHERE payment_id={payment_intent_id} AND status='PENDING'; if affected_rows == 0: ROLLBACK; return 200 (concurrent webhook, lost race); UPDATE orders SET status='PAYMENT_CONFIRMED', payment_id={payment_intent_id}, paid_at=now() WHERE order_id={order_id}; UPDATE inventory SET qty = qty - {cart_qty}, reserved_qty = reserved_qty - {cart_qty} WHERE product_id IN (SELECT product_id FROM order_items WHERE order_id={order_id}); COMMIT;, (d) Publish Kafka events: 'payment.success': {order_id, payment_id, amount, timestamp}, 'order.confirmed': {order_id, user_id, items, total}, (e) Return 200 OK to Stripe (acknowledges receipt, Stripe won't retry), (5) Kafka consumers (from image shows Kafka \u2192 Consumer): (a) Notification Service: Sends email 'Payment successful, order #{order_id} confirmed', (b) Inventory Service: Invalidates Redis cache for affected products, (c) Order Fulfillment: Creates warehouse pick list, (6) Failure scenarios: (a) Payment declined: Webhook 'payment_intent.payment_failed' \u2192 UPDATE orders SET status='PAYMENT_FAILED', release reserved inventory, notify user, (b) Webhook timeout: Stripe retries webhook 3 times (exponential backoff), if still fails \u2192 manual reconciliation via admin dashboard or polling Stripe API, (c) Database transaction fails: ROLLBACK, return 500 to Stripe \u2192 Stripe retries webhook \u2192 eventual consistency, (7) Idempotency guarantees: (a) order_id as idempotency_key: User clicks 'Pay' multiple times \u2192 Stripe creates single payment_intent, subsequent calls return existing intent, (b) Database UNIQUE constraint: UNIQUE INDEX on payments(order_id) ensures one payment per order, prevents double charge on race condition, (c) Webhook deduplication: Check payment.status before processing, prevents double inventory deduction on webhook replay, (8) Reconciliation: Nightly job compares Payment DB with Stripe API: stripe.paymentIntents.list({created: {gte: yesterday}}), flags discrepancies (payment succeeded in Stripe but order still PENDING_PAYMENT in DB) \u2192 admin investigation, (9) Testing: Use Stripe test webhooks: stripe trigger payment_intent.succeeded, verify idempotency: send same webhook twice, assert order processed once. Security: (1) Webhook signature verification prevents forged webhooks, (2) HTTPS enforced for webhook endpoint, (3) Rate limiting 100 webhooks/min per IP, (4) Payment DB has separate credentials from other services (least privilege). Performance: Payment webhook processing <200ms (transaction + Kafka publish), can handle 1000 payments/sec with horizontal scaling, Kafka decouples from slow consumers (email sending)."
                  },
                  {
                    "q": "How do you track order status and implement real-time updates for users?",
                    "a": "Event-driven order tracking with Kafka (from image shows Order DB \u2192 Kafka \u2192 Consumer \u2192 Notification System): (1) Order lifecycle states: PENDING_PAYMENT \u2192 PAYMENT_CONFIRMED \u2192 PROCESSING (warehouse) \u2192 SHIPPED \u2192 OUT_FOR_DELIVERY \u2192 DELIVERED \u2192 (optional) RETURNED/CANCELLED, (2) State transitions via Kafka events: (a) Order creation: Checkout Service publishes 'order.created' to Kafka: {order_id, user_id, items, total, status: 'PENDING_PAYMENT', timestamp}, (b) Payment success: Payment webhook publishes 'payment.success' \u2192 Order Status Service consumes \u2192 UPDATE orders SET status='PAYMENT_CONFIRMED', (c) Warehouse picks items: Warehouse system POST /v1/orders/{order_id}/process \u2192 publishes 'order.processing' \u2192 Order Status Service updates \u2192 status='PROCESSING', (d) Shipment: Warehouse POST /v1/orders/{order_id}/ship with {tracking_id, carrier, estimated_delivery} \u2192 publishes 'order.shipped' \u2192 status='SHIPPED', (e) Delivery partner updates: External API calls POST /v1/orders/{order_id}/tracking with {location, status} \u2192 publishes 'order.out_for_delivery' or 'order.delivered', (3) Kafka topic structure: Topics: 'order.lifecycle' (all state changes), 'order.tracking' (location updates), Event schema: {event_id, event_type: 'order.shipped', order_id, timestamp, payload: {tracking_id, carrier, estimated_delivery, warehouse_location}}, (4) Consumer groups: (a) Order Status Service: Updates Order DB with new status, stores history: INSERT INTO order_status_history (order_id, status, timestamp, metadata), (b) Notification Service: Sends email/SMS/push for each status change: 'Order confirmed', 'Order shipped - Track here', 'Out for delivery - ETA 2 PM', 'Delivered', (c) Analytics Service: Tracks metrics (avg delivery time, on-time rate), (5) Real-time updates to user: Approach 1 - WebSocket: User opens order tracking page \u2192 Client establishes WebSocket: WS /v1/orders/{order_id}/track, Backend WebSocket Manager subscribes to Redis Pub/Sub channel: order:{order_id}:updates, Order Status Service publishes status changes to Redis channel, WebSocket Manager forwards to client's connection, Client displays real-time status and map (if location tracking), Approach 2 - Polling (fallback): Client polls GET /v1/status/{order_id} every 30 seconds, Less efficient but works if WebSocket unavailable, (6) Order status API: GET /v1/status/{order_id} \u2192 Query Order DB: SELECT status, tracking_id, estimated_delivery, carrier FROM orders WHERE order_id={order_id}, Query status history: SELECT status, timestamp FROM order_status_history WHERE order_id={order_id} ORDER BY timestamp ASC, Response: {order_id, current_status: 'SHIPPED', tracking_id: 'TRK123', carrier: 'FedEx', estimated_delivery: '2025-01-25', history: [{status: 'PAYMENT_CONFIRMED', timestamp: '...'}, {status: 'PROCESSING', timestamp: '...'}, {status: 'SHIPPED', timestamp: '...'}]}, (7) Location tracking integration: Delivery partner API pushes updates: POST /v1/orders/{order_id}/location with {lat, lng, timestamp}, Order Tracking Service: Stores in time-series DB (InfluxDB): INSERT INTO delivery_locations (order_id, lat, lng, timestamp), Publishes to Redis Pub/Sub for real-time map updates, Client shows driver location on map, recalculates ETA using Google Maps Distance Matrix API, (8) Notification customization: User preferences: Allow users to opt-in/out per notification type (email, SMS, push), send only opted-in channels, Template: Email template: 'Hi {user_name}, your order #{order_id} has been shipped! Track it here: {tracking_url}', (9) Cancellation flow: User cancels: POST /v1/orders/{order_id}/cancel with {reason: 'changed_mind'}, Validation: if status in ['SHIPPED', 'OUT_FOR_DELIVERY', 'DELIVERED'] \u2192 return 400 'Cannot cancel, order already shipped', if status in ['PENDING_PAYMENT', 'PAYMENT_CONFIRMED', 'PROCESSING'] \u2192 allow cancellation, Update: UPDATE orders SET status='CANCELLED', cancelled_at=now(), cancellation_reason={reason}, Refund: If payment captured, initiate refund via Payment Gateway, Inventory: Release stock: UPDATE inventory SET qty = qty + {order_qty} WHERE product_id IN (order items), Kafka: Publish 'order.cancelled' \u2192 Notification Service sends 'Order cancelled, refund in 5-7 days', (10) SLA monitoring: Track metrics: % orders delivered on time (status='DELIVERED' AND delivered_at <= estimated_delivery), avg time from PAYMENT_CONFIRMED to DELIVERED, % orders stuck in PROCESSING >24h (alert warehouse), Dashboard: Grafana shows real-time order funnel: PENDING (100) \u2192 CONFIRMED (80) \u2192 PROCESSING (60) \u2192 SHIPPED (40) \u2192 DELIVERED (20). Performance: Kafka handles 100K order events/sec, Order Status Service processes events in <100ms, WebSocket can support 1M concurrent connections (scaled horizontally), Real-time updates delivered <500ms from event publish to user's screen."
                  },
                  {
                    "q": "How do you handle high-volume flash sales (100K orders in 1 minute)?",
                    "a": "Flash sale architecture with queuing and rate limiting: (1) Pre-sale preparation: (a) Product flagged as flash_sale=true with sale_start_time, sale_end_time, sale_quantity=1000, (b) Cache warm-up: Pre-load product details, images to CDN and Redis 1 hour before sale, (c) Database optimization: Dedicated read replicas for flash sale product queries, index on (product_id, sale_start_time), (d) Auto-scaling: Scale Checkout Service 5x normal capacity (50 \u2192 250 instances) 10 min before sale, (2) Virtual waiting room: (a) Before sale_start_time: Users directed to waiting room page (Cloudflare Waiting Room or custom), shows countdown timer, ~1M users in queue, (b) At sale_start_time: Waiting room releases users in batches (1000 users/sec) with random token, prevents thundering herd on backend, (c) Token validation: Backend accepts requests only with valid token from waiting room, (3) Inventory handling: (a) Pre-allocate inventory: CREATE TABLE flash_sale_inventory (product_id, queue_position INT, user_id NULL, status ENUM('available', 'reserved', 'sold'), reserved_at TIMESTAMP), INSERT 1000 rows with status='available', queue_position=1 to 1000, (b) Checkout flow: User clicks 'Buy Now' \u2192 Backend: BEGIN TRANSACTION; SELECT queue_position, status FROM flash_sale_inventory WHERE product_id={product_id} AND status='available' ORDER BY queue_position LIMIT 1 FOR UPDATE SKIP LOCKED; if found: UPDATE SET status='reserved', user_id={user_id}, reserved_at=now(); COMMIT; else: ROLLBACK; return 'Sold out'; (c) SKIP LOCKED: PostgreSQL feature, if row locked by concurrent transaction, skip it and try next \u2192 prevents contention, (4) Queue-based checkout: (a) Add to Kafka queue: Instead of synchronous checkout, publish to Kafka 'checkout.requests' topic: {user_id, product_id, timestamp}, FIFO guarantee within partition (partition by product_id), (b) Checkout workers: Consumer group of 50 workers processes queue at steady rate (100 checkouts/sec), prevents overwhelming Payment Gateway and Inventory DB, (c) User sees: 'You're in line, position #523. Estimated wait: 5 min', WebSocket updates queue position in real-time, (5) Timeout & release: Reserved inventory timeout: Background job every 10 sec: UPDATE flash_sale_inventory SET status='available', user_id=NULL WHERE status='reserved' AND reserved_at < now() - INTERVAL 5 MINUTE, if user doesn't complete payment in 5 min, inventory released for next user, (6) Rate limiting: (a) Per-user: 1 checkout attempt per 10 seconds (Redis: SETEX ratelimit:user:{user_id} 10 '1'), prevents bots spamming checkout, (b) Per-IP: 10 requests/sec (API Gateway rate limit), (c) CAPTCHA: Require CAPTCHA before checkout to filter bots, (7) Caching strategy: (a) Product details: Cached in Redis + CDN, 99% hit rate, only 1% hits DB (1M users \u00d7 0.01 = 10K DB queries vs 1M without cache), (b) Inventory check: Redis counter for quick check: DECR flash_sale:{product_id}:remaining, if result >= 0: proceed to checkout (queue-based), if <0: return 'Sold out' immediately, (c) Sync Redis with DB: Periodically (every 5 sec) update Redis counter from DB to correct drift, (8) Payment optimization: (a) Batch payment processing: Instead of 1 payment intent per order, batch 100 orders \u2192 100 payment intents in parallel via Payment Gateway, (b) Async confirmation: User sees 'Order placed, confirming payment...' immediately, payment processes in background, (c) Webhook handling: Scale webhook consumer 10x to handle 1000 payment webhooks/sec, (9) Database write optimization: (a) Batch inserts: Buffer 100 order records, INSERT in single transaction every 2 seconds, reduces DB load 100x, (b) Write to primary: All order writes to primary DB, (c) Read from replicas: User order status queries hit replicas (eventual consistency acceptable, 1-2 sec lag), (10) Post-sale cleanup: (a) Disable rate limiting, scale down instances to normal capacity, (b) Analyze metrics: % users got product, avg checkout time, peak load handled, (c) Notify users on waitlist: 'Sold out in 3 minutes, sign up for restock alert'. Real-world example: Amazon Prime Day: 10M products on sale, 100K orders/minute peak, waiting room gates 5M concurrent users, queue-based checkout processes 50K checkouts/min, 99.9% uptime during sale, inventory sold out products in <10 min. Performance numbers: Without optimizations: 100K concurrent checkout requests \u2192 DB meltdown (10K writes/sec = deadlocks), Payment Gateway rate limit exceeded (max 1K/sec), overselling due to race conditions, With optimizations: Waiting room \u2192 1K users/sec enter (controlled), Queue-based checkout \u2192 100 checkouts/sec (sustainable), Redis SKIP LOCKED \u2192 no deadlocks, Batch DB writes \u2192 500 writes/sec (comfortable), no overselling (flash_sale_inventory table serializes access)."
                  },
                  {
                    "q": "How do you implement product recommendations and personalization?",
                    "a": "Multi-strategy recommendation engine: (1) Data collection: (a) User events: Kafka topics capture: 'product.viewed': {user_id, product_id, timestamp, session_id}, 'product.added_to_cart': {user_id, product_id, qty}, 'product.purchased': {user_id, product_id, order_id}, 'product.searched': {user_id, query, filters}, (b) Event processing: Apache Flink/Spark Streaming consumes events, aggregates per user: recent_views (last 20 products), recent_searches (last 10 queries), purchase_history (all purchases), (c) Storage: User profile in Cassandra partitioned by user_id: {user_id, recent_views[], recent_searches[], purchase_history[], preferred_categories[], avg_price_range}, (2) Recommendation strategies: Strategy 1 - Collaborative filtering: (a) User-based: Find similar users (Jaccard similarity on purchase history), recommend products those users bought: similar_users = users with >50% overlap in purchase_history, recommendations = products purchased by similar_users NOT purchased by current user, (b) Item-based: Find similar products (cosine similarity on co-purchase matrix), recommend: 'Users who bought X also bought Y', co-purchase matrix: If product A and B purchased together 1000 times, similarity(A,B) = 1000 / sqrt(purchases(A) \u00d7 purchases(B)), (c) Matrix factorization: Offline training (ALS algorithm) on user-product interaction matrix (100M users \u00d7 10M products = sparse), factorizes into user_factors (100M \u00d7 50) and product_factors (10M \u00d7 50), recommendation_score(user, product) = dot(user_factors[user], product_factors[product]), top 100 products per user stored in Redis, refreshed daily, Strategy 2 - Content-based: (a) Product embeddings: Use product metadata (title, description, category, brand) to generate embeddings (BERT/Sentence Transformers), 768-dimensional vector per product, (b) User profile vector: Average of embeddings of products user viewed/purchased, (c) Recommendation: Find products with highest cosine similarity to user profile vector, Elasticsearch kNN search with vector field for fast retrieval, Strategy 3 - Trending & popular: (a) Global trending: Products with highest view_count in last 24h, shown to cold-start users (no history), (b) Category trending: Top products per category, updated hourly, (c) Personalized trending: Trending in user's preferred_categories[], Strategy 4 - Business rules: (a) Margin-aware: Boost products with higher profit margin, (b) Inventory-aware: Boost overstocked products (qty >1000), (c) Promotional: Priority to products on sale or new arrivals, (3) Hybrid ranking: Combine all strategies with weighted scoring: final_score = 0.4 \u00d7 collaborative_score + 0.3 \u00d7 content_score + 0.2 \u00d7 trending_score + 0.1 \u00d7 business_score, Sort products by final_score, return top 20, (4) Real-time personalization: (a) Home page: GET /v1/recommendations/home \u2192 returns: {trending_products: [...], recommended_for_you: [...], recently_viewed: [...], based_on_cart: [...]}, (b) Product detail page: GET /v1/recommendations/product/{product_id} \u2192 returns 'Similar products' and 'Frequently bought together', (c) Search results: Personalize ranking based on user history (boost products in preferred_categories), (5) Serving architecture: (a) Pre-compute: Nightly batch job generates top 100 recommendations per user (collaborative filtering + content-based), stores in Redis: SET recs:{user_id} '[{product_id, score}, ...]' EX 86400, (b) Real-time blending: On API call, fetch from Redis (pre-computed), blend with trending and business rules, apply diversity (don't show 10 similar products, mix categories), (c) Fallback: If user has no history (cold start), show global trending + editorial curated, (6) A/B testing: (a) Experiment framework: Randomly assign users to variant A (old algo) or B (new algo), track metrics: click-through rate (CTR), add-to-cart rate, purchase conversion, revenue per user, (b) Winner: If variant B has +5% conversion with p-value <0.05, roll out to 100%, (7) Model training: (a) Offline: Apache Spark job on S3 data lake (1 year of events, 10 TB), trains ALS model (collaborative filtering) weekly, trains BERT embeddings monthly, (b) Feature store: Tecton/Feast stores features (user_avg_cart_value, product_category_popularity), used by ML models, (c) Online: Low-latency feature serving from Redis (user recent_views) or PostgreSQL (user purchase_history), (8) Privacy & ethics: (a) No PII in logs: Hash user_id before storing in analytics DB, (b) Diversity: Don't create filter bubbles, 20% recommendations should be from new categories (exploration vs exploitation), (c) Explainability: Show reason: 'Based on your purchase of Product X' or 'Trending in Electronics', (9) Performance: (a) Recommendation API latency: <100ms p95 (Redis lookup + blending), (b) Pre-computation: 100M users \u00d7 100 recs each = 10B recs/day, Spark job takes 4 hours, (c) Storage: Redis 100GB for pre-computed recs + user profiles. Business impact: Recommendations drive 30-40% of e-commerce revenue, avg +25% cart size when users add recommended products, reduces bounce rate (users discover more relevant products)."
                  },
                  {
                    "q": "What's your disaster recovery and data consistency strategy for the e-commerce platform?",
                    "a": "Multi-region DR with tiered consistency (from image shows strong consistency for orders, eventual for search): (1) Architecture: Primary region us-east-1, Secondary us-west-2, Tertiary eu-west-1, (2) Database replication: (a) MySQL (User, Order DB): Primary in us-east-1, synchronous replication to us-west-2 (hot standby, <1s lag), asynchronous to eu-west-1 (warm standby, <10s lag), uses MySQL Group Replication or AWS Aurora Global Database, (b) PostgreSQL (Cart, Inventory): Similar setup, streaming replication with replication slots, (c) MongoDB (Product): Multi-region cluster with replica sets per region, write concern majority ensures data written to >=2 nodes, read preference nearest for low latency, (3) Consistency tiers: Tier 1 - Strong consistency (inventory, orders, payments): (a) Inventory updates: Synchronous replication to secondary before commit, ensures no overselling even during failover, (b) Orders: ACID transactions with 2PC (two-phase commit) if spanning services, (c) Payments: Idempotency + unique constraints prevent double charge, (d) Trade-off: Slower writes (50-100ms extra for replication), acceptable for low-frequency operations, Tier 2 - Eventual consistency (product catalog, search, cache): (a) Product updates: Written to primary MongoDB, replicated asynchronously (1-5s lag), (b) Elasticsearch: CDC pipeline syncs within 1-2 seconds, (c) Redis cache: Invalidated via Kafka events, stale cache for <10s acceptable, (d) Benefit: Fast writes, high availability, user impact minimal (showing product at old price for 5s is fine), (4) Failover procedure: (a) Health checks: Route53 checks primary region every 30s (TCP, HTTP, database connectivity), (b) Trigger: If 3 consecutive failures (90s) \u2192 initiate failover, (c) Database promotion: us-west-2 promoted to primary (pg_ctl promote for PostgreSQL, aurora.promoteReadReplicaDBCluster for Aurora), takes 2-5 min, (d) Application routing: Route53 updates DNS to point to us-west-2 load balancer, (e) Kafka: MirrorMaker replicates events to secondary Kafka cluster, consumers switch to secondary, (f) Data loss: <1s for synchronous replicas (Tier 1), <10s for async (Tier 2), (5) Split-brain prevention: (a) Fencing: Primary region fences itself (stops accepting writes) when it detects partition, (b) Witness node: Third region (eu-west-1) acts as tiebreaker for quorum, (c) Distributed lock: Consul/etcd ensures only one region is primary at a time, (6) RTO/RPO targets: (a) Tier 1 (orders, inventory): RPO <1 second (sync replication), RTO <5 minutes (automated failover), (b) Tier 2 (products, search): RPO <10 seconds (async replication), RTO <5 minutes, (c) Backups: RPO <1 hour (hourly backups to S3 Glacier), RTO <4 hours (manual restore from backup), (7) Testing: (a) Monthly DR drills: Simulate primary region failure, execute failover, verify RTO met, test backup restore, (b) Chaos engineering: Randomly terminate instances (during low traffic 2-4 AM), verify auto-recovery, (c) Tabletop exercises: Team walks through disaster scenarios (DDoS, data corruption, AWS outage), (8) Data corruption recovery: (a) Point-in-time recovery: Restore database to timestamp before corruption, supported for last 7 days, (b) Logical backups: Daily mysqldump/pg_dump to S3, retention 30 days, (c) Binary logs: MySQL binlogs for replay, PostgreSQL WAL archives, (9) Kafka durability: (a) Replication factor 3: Events written to >=3 brokers before acknowledged, (b) min.insync.replicas=2: Producer write fails if <2 replicas available, (c) Event retention 7 days: Can replay events to rebuild state, (10) Monitoring & alerts: (a) Replication lag: Alert if lag >5 seconds (Tier 1) or >30 seconds (Tier 2), (b) Database metrics: Connections, query latency, deadlocks, slow queries, (c) Application metrics: Order placement errors, payment failures, checkout abandonment rate, (d) Business metrics: Revenue/hour, conversion rate, average order value (anomaly detection), (11) Consistency validation: (a) Reconciliation job: Nightly job compares Order DB with Payment DB, flags discrepancies (order PAYMENT_CONFIRMED but no payment record), (b) Inventory audit: Compare Inventory DB qty with sum(order_items.qty) for sold products, detect leaks, (c) Financial reconciliation: Match revenue in Order DB with bank settlements, (12) Cost: Multi-region adds 60-80% infrastructure cost vs single region, Trade-off: $1M/month extra cost vs $10M revenue loss for 1-hour outage, insurance for business continuity. Example: Primary region outage at 2:00 PM \u2192 Health checks fail at 2:01:30 \u2192 Failover initiated at 2:02 \u2192 Database promoted at 2:05 \u2192 DNS updated at 2:06 \u2192 Users routed to secondary at 2:08 \u2192 Service fully restored, data loss <1s (in-flight orders may need retry), total downtime <8 minutes, business impact <$50K revenue loss vs $1M for 1-hour outage without DR."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "11. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Volume (from image)",
                    "pairs": [
                      {
                        "k": "Orders per Second",
                        "v": "10 orders/second normal, 100 orders/sec peak (flash sales)"
                      },
                      {
                        "k": "Monthly Active Users",
                        "v": "10M MAU"
                      },
                      {
                        "k": "Products",
                        "v": "100M+ products in catalog"
                      },
                      {
                        "k": "Search Queries",
                        "v": "100K queries/sec during peak events"
                      },
                      {
                        "k": "Concurrent Users",
                        "v": "50K-100K concurrent users during normal hours, 1M during sales"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance Requirements",
                    "pairs": [
                      {
                        "k": "Search Latency",
                        "v": "<500ms for product search with filters (Elasticsearch p95)"
                      },
                      {
                        "k": "Product Details",
                        "v": "<200ms to load product page (Redis cache hit <50ms, DB miss <200ms)"
                      },
                      {
                        "k": "Checkout Flow",
                        "v": "<3 seconds from cart to payment URL (includes inventory lock + reservation)"
                      },
                      {
                        "k": "Payment Processing",
                        "v": "<5 seconds for payment gateway response"
                      },
                      {
                        "k": "Redis Lock",
                        "v": "<10ms to acquire distributed lock for inventory"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching & TTL",
                    "pairs": [
                      {
                        "k": "Cart Cache",
                        "v": "Redis TTL 7 days for active carts"
                      },
                      {
                        "k": "Product Cache",
                        "v": "Redis TTL 30 min (invalidated on product update via Kafka)"
                      },
                      {
                        "k": "Search Cache",
                        "v": "Redis TTL 10 min for popular queries, 60-70% hit rate"
                      },
                      {
                        "k": "Inventory Cache",
                        "v": "Redis TTL 5 min (synced from DB via CDC)"
                      },
                      {
                        "k": "CDN Cache",
                        "v": "Product images cached 24 hours at edge, 95% hit rate"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Consistency & Locking",
                    "pairs": [
                      {
                        "k": "Redis Lock TTL",
                        "v": "30 seconds (prevents deadlock if service crashes)"
                      },
                      {
                        "k": "Inventory Reservation",
                        "v": "5 minutes timeout (released if payment not completed)"
                      },
                      {
                        "k": "Payment Timeout",
                        "v": "15 minutes from order creation to payment completion"
                      },
                      {
                        "k": "CDC Lag",
                        "v": "1-2 seconds (Product DB \u2192 Kafka \u2192 Elasticsearch sync)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Database & Storage",
                    "pairs": [
                      {
                        "k": "Product Catalog",
                        "v": "100M products \u00d7 5 KB avg = 500 GB (MongoDB)"
                      },
                      {
                        "k": "User Data",
                        "v": "10M users \u00d7 2 KB = 20 GB (MySQL)"
                      },
                      {
                        "k": "Orders",
                        "v": "100M orders/year \u00d7 2 KB = 200 GB/year (partitioned monthly)"
                      },
                      {
                        "k": "Elasticsearch",
                        "v": "100M products \u00d7 3 KB (indexed fields) = 300 GB, 10 nodes cluster"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Business Metrics",
                    "pairs": [
                      {
                        "k": "Conversion Rate",
                        "v": "2-5% (searches \u2192 orders)"
                      },
                      {
                        "k": "Cart Abandonment",
                        "v": "60-70% (items added but not purchased)"
                      },
                      {
                        "k": "Average Order Value",
                        "v": "$50-150 depending on category"
                      },
                      {
                        "k": "Repeat Purchase Rate",
                        "v": "30-40% of users order again within 90 days"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Prevent inventory overselling with Redis distributed lock BEFORE database check. SETNX lock:inventory:{product_id} {session_id} EX 30. Follow with SELECT FOR UPDATE on inventory table. Use reserved_qty column to hold stock during checkout\u2192payment flow."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How to prevent overselling?'. Answer: (1) Redis SETNX lock before inventory check, (2) PostgreSQL SELECT FOR UPDATE pessimistic lock, (3) reserved_qty column for checkout\u2192payment window, (4) Deduct qty only after payment success webhook, (5) Lock auto-expires 30s to prevent deadlock."
              },
              {
                "type": "tip",
                "text": "CDC pipeline (from image): Product DB (MongoDB) \u2192 Kafka \u2192 Elasticsearch indexer. Enables eventual consistency (1-2s lag) for search, decouples Product Service from search implementation, supports multiple consumers (ES, Redis cache, analytics)."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Image shows different DBs for different purposes - User (MySQL - ACID), Product (MongoDB - flexible schema), Cart (PostgreSQL - relational), Inventory (PostgreSQL - source of truth). Each chosen for specific characteristics."
              },
              {
                "type": "warning",
                "text": "NEVER update inventory synchronously in checkout. Use reserved_qty during checkout, actual qty deduction only after payment.success webhook. Prevents stock lock if user abandons payment. Background job releases reserved_qty after 5-15 min timeout."
              },
              {
                "type": "tip",
                "text": "Image shows Kafka event-driven: order.created, payment.success, inventory.updated, order.shipped. Decouples services (Notification, Analytics, Fulfillment consume independently), enables async processing, supports event replay for debugging."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'Checkout flow orchestration'. Walk through: Fetch cart \u2192 Acquire Redis locks \u2192 Validate inventory (SELECT FOR UPDATE) \u2192 Reserve stock (reserved_qty) \u2192 Create order PENDING_PAYMENT \u2192 Release locks \u2192 Payment webhook \u2192 Deduct qty \u2192 Publish Kafka events \u2192 Clear cart."
              },
              {
                "type": "warning",
                "text": "NEVER use eventual consistency for payments and inventory. Strong consistency (ACID transactions) required. Image emphasizes: 'highly consistent with respect to placing the order'. Search can be eventual (1-2s lag acceptable)."
              },
              {
                "type": "tip",
                "text": "Flash sales optimization: Virtual waiting room gates users (1K/sec), queue-based checkout (Kafka), PostgreSQL SKIP LOCKED prevents contention, Redis counter for quick stock check, reserved_qty with 5-min timeout, batch DB writes (100 orders/txn)."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Payment idempotency. order_id as idempotency_key to Stripe, UNIQUE constraint on payments(order_id), webhook signature validation, status check before processing. Prevents double charge on retry, handles webhook replay gracefully."
              }
            ],
            "tags": [
              "system-design",
              "e-commerce",
              "Amazon",
              "Flipkart",
              "Elasticsearch",
              "product-search",
              "inventory-management",
              "distributed-locks",
              "Redis-SETNX",
              "cart-management",
              "CDC-pipeline",
              "Kafka-events",
              "payment-idempotency",
              "MongoDB-flexible-schema",
              "PostgreSQL-ACID",
              "flash-sales",
              "order-tracking",
              "webhook-handling",
              "overselling-prevention"
            ]
          }
        },
        {
          "id": "04",
          "title": "System Design 4: Design OTT Platform like Netflix / Amazon Prime / Hotstar | HLD | LLD",
          "videoUrl": "https://youtu.be/NH-f955mvGg",
          "description": "In this video, we dive deep into the **System Design of OTT platforms** like Netflix, Hotstar, and Prime Video. Learn how to design a scalable, reliable, and efficient video streaming system from scratch!",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/ghweh4th4.webp?updatedAt=1746473737048",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/oqwnrgnogng42ogOTTDeisng.png?updatedAt=1746565533019",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 15,
            "lastUpdated": "2025-01-20",
            "hero": {
              "title": "OTT Platform (Netflix/Amazon Prime/Hotstar)",
              "oneLinerToMemorize": "Video upload (transcoding) \u2192 CDN delivery \u2192 Adaptive Streaming (HLS/DASH) \u2192 Low-latency playback \u2192 Subscription management \u2192 Analytics"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User should be able to create account and opt for subscription (register/login/logout/subscription plans)",
                  "Feature 2: User should be able to search and find movies/shows based on product title or names",
                  "Feature 3: Users can watch videos in different resolutions (1080p, 4K, 720p, 480p, etc.) based on device and network",
                  "Feature 4: User should be able to watch the video without nominal or negligible buffering (adaptive streaming)",
                  "Feature 5: Content creators can upload videos (movies, series, documentaries) with metadata"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Users",
                        "v": "200M users and 10k total videos (~1 hour each)"
                      },
                      {
                        "k": "Daily Active Users",
                        "v": "20M DAU streaming video"
                      },
                      {
                        "k": "Storage",
                        "v": "Total: 400M = 10k videos \u00d7 400k GB = 400TB (accounting for multiple resolutions)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance & Availability",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Highly available >> consistency (eventual consistency acceptable for metadata)"
                      },
                      {
                        "k": "Video Streaming",
                        "v": "Low-latency streaming with adaptive bitrate (HLS/DASH)"
                      },
                      {
                        "k": "Payment Consistency",
                        "v": "Consistent with respect to placing the order & payment (strong consistency for subscriptions)"
                      },
                      {
                        "k": "Buffering",
                        "v": "Minimal buffering <2-3 seconds, smooth playback even on poor network"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities (Identify Core Entity)",
                "type": "bullets",
                "items": [
                  "Entity 1: User - Customer with user_id, name, email, subscription_status, watch_history[], preferences",
                  "Entity 2: User Metadata - Additional user data like device info, viewing patterns, subscription details",
                  "Entity 3: Video - Content with video_id, title, description, genre, duration, content_rating, release_date",
                  "Entity 4: Video Metadata & Static Image - Thumbnail, poster, metadata (actors, director, etc.), subtitles, audio tracks"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "User Management",
                    "pairs": [
                      {
                        "k": "POST /v1/users/register",
                        "v": "Register user {name, email, password} \u2192 {userId, token} (login/logout/fetch)"
                      },
                      {
                        "k": "GET /v1/subscription/plans",
                        "v": "Get available subscription plans \u2192 List<Plan>"
                      },
                      {
                        "k": "POST /v1/subscriptions",
                        "v": "Create subscription {userId, planId, paymentDetails} \u2192 SubscriptionId"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Video Search & Discovery",
                    "pairs": [
                      {
                        "k": "GET /v1/videos/search",
                        "v": "Search videos {q={name}} \u2192 List<VideoId (Partial)> with pagination"
                      },
                      {
                        "k": "GET /api/v1/videos/{videoId}",
                        "v": "Return the video details {metadata, thumbnails, available resolutions} (json)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Video Playback",
                    "pairs": [
                      {
                        "k": "GET /api/v1/videos/{videoId}/play",
                        "v": "Start the video \u2192 Return master playlist URL (HLS/DASH manifest)"
                      },
                      {
                        "k": "GET /api/v1/videos/{videoId}/stream/{resolution}",
                        "v": "Stream video chunks for specific resolution"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Users \u2192 API Gateway: Load balancing, authentication, authorization, rate limiting",
                  "User Service \u2192 User DB: User profiles, authentication, preferences",
                  "Search Service \u2192 Elasticsearch: Video search by title, genre, actors with full-text search",
                  "Play Service: Generates manifest file (HLS/DASH) for video streaming",
                  "Uploader: Content creators upload raw video files to Blob storage (S3)",
                  "Video Processing Pipeline: Transcoding, compression, multiple resolutions (4K, 1080p, 720p, 480p, 360p)",
                  "Video Metadata DB: Stores video metadata, thumbnail URLs, available resolutions, subtitle tracks",
                  "Blob Storage (S3): Raw uploaded videos and transcoded video files (organized by video_id/resolution/)",
                  "CDN: Delivers video chunks with edge caching for low-latency global playback"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Video Upload & Metadata",
                    "steps": [
                      "Content creator uploads: Raw video file (e.g., movie.mp4, 50GB 4K), metadata {title, description, genre, actors, duration, language}",
                      "Uploader service: (1) Generates video_id (UUID), (2) Creates multipart upload to S3: s3://videos-raw/{video_id}/original.mp4, (3) Stores metadata in Video Metadata DB: {video_id, title, description, genre, duration, upload_status: 'PROCESSING', created_at}",
                      "Upload completion: Triggers Lambda/SQS event with {video_id, s3_path}",
                      "Metadata includes: Title, description, genre[], actors[], director, release_date, content_rating (PG-13, R), language[], subtitle_tracks[], audio_tracks[], thumbnail_url, poster_url",
                      "Images uploaded separately: Thumbnails and posters to S3 \u2192 s3://video-images/{video_id}/thumbnail.jpg, served via CDN"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Video Transcoding Pipeline (Critical for Adaptive Streaming)",
                    "steps": [
                      "Trigger: Lambda consumes SQS message with {video_id, s3_path: 's3://videos-raw/{video_id}/original.mp4'}",
                      "Video transcoding service: (1) Downloads original video from S3, (2) Uses FFmpeg/AWS MediaConvert to transcode into multiple resolutions and bitrates, (3) Resolutions: 4K (3840\u00d72160, 20 Mbps), 1080p (1920\u00d71080, 8 Mbps), 720p (1280\u00d7720, 5 Mbps), 480p (854\u00d7480, 2.5 Mbps), 360p (640\u00d7360, 1 Mbps)",
                      "For EACH resolution: (a) Transcode video, (b) Split into segments/chunks (10-second chunks for HLS/DASH), (c) Upload to S3: s3://videos-processed/{video_id}/{resolution}/segment_001.ts, segment_002.ts, ..., (d) Generate manifest file (master.m3u8 for HLS or master.mpd for DASH)",
                      "HLS (HTTP Live Streaming): Apple standard, .m3u8 playlist, .ts video segments",
                      "DASH (Dynamic Adaptive Streaming over HTTP): MPEG standard, .mpd manifest, .m4s segments",
                      "Chunker service: Splits video into 10-second chunks, uploads to S3 organized by resolution",
                      "Manifest file structure: Master playlist points to resolution-specific playlists \u2192 each resolution playlist lists segment URLs",
                      "Example master.m3u8: #EXTM3U, #EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1920x1080 \u2192 1080p/playlist.m3u8, #EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1280x720 \u2192 720p/playlist.m3u8",
                      "Video encoder: Compresses at different bitrates (lower bitrate for same resolution = smaller file, lower quality)",
                      "Update metadata: UPDATE video_metadata SET processing_status='COMPLETED', resolutions=['4K','1080p','720p','480p','360p'], manifest_url='s3://videos-processed/{video_id}/master.m3u8' WHERE video_id={video_id}",
                      "Transcoding time: ~30-60 min for 2-hour 4K movie using parallel processing on GPU instances",
                      "Cost optimization: Use spot instances for transcoding, auto-scale based on queue depth"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Video Search (Elasticsearch)",
                    "steps": [
                      "User searches: GET /v1/videos/search?q=Inception&genre=Sci-Fi&language=English",
                      "Search Service queries: Elasticsearch with full-text search on title, description, actors, director",
                      "ES query: { 'query': { 'bool': { 'must': [ { 'multi_match': { 'query': 'Inception', 'fields': ['title^3', 'description', 'actors', 'director'] } } ], 'filter': [ { 'term': { 'genre': 'Sci-Fi' } }, { 'term': { 'language': 'English' } } ] } }, 'sort': [ { 'popularity_score': 'desc' } ], 'size': 20 }",
                      "Popularity score: Combination of view_count, rating, recency, calculated offline and updated periodically",
                      "Response: [{ video_id, title, thumbnail_url, duration, rating, genre }]",
                      "Caching: Popular searches cached in Redis with TTL=10 min",
                      "CDC pipeline: Video Metadata DB \u2192 Kafka \u2192 Elasticsearch indexer keeps search index in sync",
                      "Index structure: Each video document has video_id, title (text), description (text), genre (keyword), actors (text), language (keyword), popularity_score (float), release_date (date)"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Video Playback Request",
                    "steps": [
                      "User clicks play: GET /api/v1/videos/{video_id}/play",
                      "Play Service: (1) Validates user subscription status (check if active subscription exists), (2) Fetches manifest URL from Video Metadata DB: SELECT manifest_url, resolutions FROM video_metadata WHERE video_id={video_id}, (3) Generates CDN-signed URL (CloudFront signed URL with expiry=4 hours), (4) Returns manifest URL to client: { manifest_url: 'https://cdn.example.com/{video_id}/master.m3u8?Expires=...&Signature=...' }",
                      "Client video player: (1) Fetches master.m3u8 manifest, (2) Parses available resolutions and bitrates, (3) Selects initial resolution based on network speed (adaptive bitrate logic), (4) Starts downloading video segments",
                      "Adaptive streaming: Player monitors buffer level and network speed, switches resolution dynamically (e.g., 1080p \u2192 720p if network degrades)",
                      "Segment requests: GET https://cdn.example.com/{video_id}/1080p/segment_001.ts, segment_002.ts, ... (each segment ~2-3 MB for 10-second chunk)",
                      "CDN caching: Edge locations cache segments, 95%+ cache hit rate for popular content, <50ms latency",
                      "Player buffering: Downloads 3-5 segments ahead (30-50 seconds), ensures smooth playback even during network fluctuations"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Adaptive Streaming - How it Works",
                    "steps": [
                      "Adaptive bitrate streaming: Dynamically switches video quality based on network conditions",
                      "Player logic: (1) Measure download speed: time to download last segment / segment size = throughput (Mbps), (2) Estimate available bandwidth, (3) Select appropriate resolution: if bandwidth > 8 Mbps \u2192 1080p, 5-8 Mbps \u2192 720p, 2.5-5 Mbps \u2192 480p, <2.5 Mbps \u2192 360p",
                      "Switching: Player seamlessly switches between playlists (resolution levels) mid-playback without rebuffering",
                      "Buffer management: Maintain 20-40 second buffer, if buffer drops below 10 seconds \u2192 switch to lower quality to prevent stalling",
                      "Quality metrics: Track and minimize: (1) Initial buffering time (time to first frame), (2) Rebuffering events (mid-playback stalls), (3) Bitrate switches (too frequent = poor UX)",
                      "Example scenario: User starts watching on WiFi (8 Mbps) \u2192 Player selects 1080p \u2192 User moves, WiFi degrades to 3 Mbps \u2192 Player detects slow download \u2192 Switches to 480p \u2192 Buffering continues smoothly",
                      "Keyframe alignment: Ensure video segments start with I-frames (keyframes) for seamless quality switching",
                      "How this Adaptive Streaming works: (1) Switching happens only if keyframe is set at 0th second, (2) Now lets assume the keyframe is set at 0th second, (3) At this keyframe point depending on the client bandwidth, player's either can resume from the old video format(1080) or some or different resolution"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: CDN Architecture for Video Delivery",
                    "steps": [
                      "CDN setup: Origin = S3 bucket (s3://videos-processed/), Edge locations = CloudFront PoPs (200+ globally)",
                      "Request flow: (1) User in India requests segment: GET cdn.example.com/{video_id}/1080p/segment_050.ts, (2) DNS routes to nearest edge (Mumbai PoP), (3) Edge checks cache, (4) If miss: Fetch from origin S3 (us-east-1) \u2192 200ms, cache at edge for 24 hours, (5) If hit: Serve from edge cache \u2192 10ms",
                      "Cache hit ratio: 95%+ for popular content (trending movies, recent releases)",
                      "Cache eviction: LRU (Least Recently Used), prioritize popular content",
                      "Origin shield: Additional caching layer between edge and origin, reduces origin load by 90%",
                      "Compression: Gzip/Brotli compression for manifest files (.m3u8, .mpd), saves bandwidth",
                      "Security: Signed URLs with expiry prevent hotlinking and unauthorized access, URL valid for 4 hours",
                      "Monitoring: CloudWatch tracks: cache hit rate, error rate (4xx, 5xx), bandwidth usage, origin requests",
                      "Cost: CDN bandwidth ~$0.085/GB, 10M users streaming 2 hours/day = 20M hours \u00d7 3 GB/hour \u00d7 $0.085 = $5.1M/month in bandwidth"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Subscription Management",
                    "steps": [
                      "User subscribes: POST /v1/subscriptions with {user_id, plan_id: 'premium', payment_method_id}",
                      "Subscription Service: (1) Validates plan exists: SELECT * FROM subscription_plans WHERE plan_id={plan_id}, (2) Creates payment intent via Payment Gateway (Stripe/Razorpay) with {amount, currency, customer_id, metadata: {user_id, plan_id}}, (3) Returns payment_url to client",
                      "Payment success webhook: POST /webhooks/payment with {payment_id, status: 'success', metadata}, (1) Subscription Service: BEGIN TRANSACTION; INSERT INTO subscriptions (subscription_id, user_id, plan_id, status='ACTIVE', start_date=now(), end_date=now()+30days, payment_id); UPDATE users SET subscription_status='ACTIVE'; COMMIT;, (2) Publish Kafka event: 'subscription.created'",
                      "Subscription plans: Basic ($9/month, 720p, 1 screen), Standard ($13/month, 1080p, 2 screens), Premium ($17/month, 4K, 4 screens)",
                      "Auto-renewal: Cron job runs daily, checks subscriptions ending in 3 days, triggers payment for renewal, if payment fails \u2192 send notification, grace period 7 days before downgrade",
                      "Cancellation: PUT /v1/subscriptions/{subscription_id}/cancel \u2192 UPDATE subscriptions SET status='CANCELLED', cancelled_at=now(), access until end_date",
                      "Access control: On every video play request, validate: SELECT status, end_date FROM subscriptions WHERE user_id={user_id} AND status='ACTIVE' AND end_date > now(), if fails return 403 Forbidden with upgrade prompt"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Watch History & Recommendations",
                    "steps": [
                      "Tracking playback: Client sends heartbeat every 10 seconds: POST /v1/videos/{video_id}/progress with {user_id, current_time_seconds, device_id}",
                      "Play Service: (1) Updates watch progress in Redis: HSET watch_progress:{user_id}:{video_id} 'current_time' {current_time}, 'last_updated' {timestamp}, TTL=7 days, (2) Periodically (every 60s) batch writes to Cassandra/DynamoDB for persistence",
                      "Resume playback: GET /v1/videos/{video_id}/play \u2192 check Redis for watch_progress:{user_id}:{video_id}, return {resume_from: {current_time}}, client seeks to position",
                      "Watch history: Cassandra table {user_id, video_id, watched_at, completion_percentage, device_type} partitioned by user_id",
                      "Kafka event stream: Publish 'video.watched' events with {user_id, video_id, duration_watched, timestamp} \u2192 consumed by Recommendation Service and Analytics Service",
                      "Recommendation engine: (1) Collaborative filtering: Users who watched video X also watched Y, (2) Content-based: Recommend similar genre/actors, (3) ML model (TensorFlow) trained on watch history, ratings, search queries \u2192 predicts user preferences, (4) Results cached in Redis per user, refreshed daily"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Analytics & Monitoring",
                    "steps": [
                      "Real-time analytics: Kafka stream processing with Apache Flink/Spark Streaming",
                      "Metrics tracked: (1) Play events (video started, paused, completed), (2) Quality metrics (buffering events, bitrate changes, errors), (3) Engagement (watch time, completion rate, drop-off points), (4) Popular content (trending videos by region)",
                      "Event schema: {event_type: 'play_started', user_id, video_id, device_type, resolution, timestamp, session_id, geo_location}",
                      "Aggregations: (1) Hourly: View counts per video, average watch time, buffering ratio, (2) Daily: User retention, content popularity rankings, revenue metrics",
                      "Storage: Raw events \u2192 Kafka \u2192 S3 (data lake) for long-term storage and batch analytics, Aggregated metrics \u2192 PostgreSQL/BigQuery for dashboards",
                      "Dashboards: Grafana/Tableau showing: concurrent viewers, bandwidth usage, cache hit rate, error rates, top videos, user growth",
                      "Alerts: PagerDuty/Opsgenie notifications for: CDN errors >1%, origin load >80%, transcoding queue depth >1000, subscription payment failures >5%"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Content Delivery Optimization",
                    "steps": [
                      "Pre-warming CDN cache: For new releases, pre-fetch segments to edge locations before launch (predictive caching)",
                      "Peak load handling: Auto-scale video processing workers, CDN bandwidth, API servers during new release (e.g., new season drops)",
                      "Geo-based optimization: Store video files in regional S3 buckets (us-east-1, eu-west-1, ap-south-1), CDN fetches from nearest origin",
                      "Compression: Use H.265/HEVC codec instead of H.264 for 40-50% better compression (smaller files, same quality), but requires decoder support",
                      "Thumbnail optimization: Generate multiple thumbnail sizes (small, medium, large), serve WebP format (30% smaller than JPEG)",
                      "Preloading: Client preloads first 2-3 segments of next episode for binge-watching (reduce time to start next episode)",
                      "Offline downloads: Mobile app feature, download encrypted video to local storage, DRM ensures can't be copied, expires after subscription ends"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Users (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "password",
                        "v": "varchar(255) (bcrypt hash)"
                      },
                      {
                        "k": "subscription_status",
                        "v": "enum (ACTIVE, INACTIVE, CANCELLED)"
                      },
                      {
                        "k": "subscription_id",
                        "v": "uuid FK \u2192 Subscriptions"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb (preferences, device info, viewing patterns)"
                      },
                      {
                        "k": "expire_date",
                        "v": "timestamp (subscription expiry)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Video Metadata (PostgreSQL or MongoDB)",
                    "pairs": [
                      {
                        "k": "video_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "title",
                        "v": "varchar(500)"
                      },
                      {
                        "k": "description",
                        "v": "text"
                      },
                      {
                        "k": "genre",
                        "v": "varchar(100)[] (array: ['Action', 'Thriller'])"
                      },
                      {
                        "k": "actors",
                        "v": "varchar(255)[] (with external actor_id mapping)"
                      },
                      {
                        "k": "director",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "duration",
                        "v": "integer (seconds)"
                      },
                      {
                        "k": "release_date",
                        "v": "date"
                      },
                      {
                        "k": "content_rating",
                        "v": "varchar(10) (PG-13, R, TV-MA)"
                      },
                      {
                        "k": "language",
                        "v": "varchar(50)[] (primary language + dubbed versions)"
                      },
                      {
                        "k": "subtitle_tracks",
                        "v": "jsonb [{lang: 'en', url: 's3://...'}, {lang: 'es', url: '...'}]"
                      },
                      {
                        "k": "audio_tracks",
                        "v": "jsonb [{lang: 'en', type: '5.1'}, {lang: 'hi', type: 'stereo'}]"
                      },
                      {
                        "k": "thumbnail_url",
                        "v": "varchar(500) (CDN URL)"
                      },
                      {
                        "k": "poster_url",
                        "v": "varchar(500)"
                      },
                      {
                        "k": "manifest_url",
                        "v": "varchar(500) (master.m3u8 or master.mpd)"
                      },
                      {
                        "k": "resolutions",
                        "v": "varchar(10)[] (['4K', '1080p', '720p', '480p', '360p'])"
                      },
                      {
                        "k": "processing_status",
                        "v": "enum (UPLOADING, PROCESSING, COMPLETED, FAILED)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Subscriptions (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "subscription_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "plan_id",
                        "v": "varchar(50) FK \u2192 SubscriptionPlans"
                      },
                      {
                        "k": "status",
                        "v": "enum (ACTIVE, CANCELLED, EXPIRED, PAYMENT_FAILED)"
                      },
                      {
                        "k": "start_date",
                        "v": "timestamp"
                      },
                      {
                        "k": "end_date",
                        "v": "timestamp"
                      },
                      {
                        "k": "payment_id",
                        "v": "varchar(255) (from payment gateway)"
                      },
                      {
                        "k": "auto_renew",
                        "v": "boolean"
                      }
                    ]
                  },
                  {
                    "groupTitle": "SubscriptionPlans (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "plan_id",
                        "v": "varchar(50) PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(100) (Basic, Standard, Premium)"
                      },
                      {
                        "k": "price",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "currency",
                        "v": "varchar(3)"
                      },
                      {
                        "k": "max_resolution",
                        "v": "varchar(10) (720p, 1080p, 4K)"
                      },
                      {
                        "k": "max_screens",
                        "v": "integer (concurrent devices allowed)"
                      },
                      {
                        "k": "billing_period",
                        "v": "varchar(20) (monthly, annual)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Watch_History (Cassandra/DynamoDB - high write throughput)",
                    "pairs": [
                      {
                        "k": "Partition Key",
                        "v": "user_id"
                      },
                      {
                        "k": "Clustering Key",
                        "v": "watched_at DESC (sort by recency)"
                      },
                      {
                        "k": "video_id",
                        "v": "uuid"
                      },
                      {
                        "k": "current_time",
                        "v": "integer (seconds watched)"
                      },
                      {
                        "k": "completion_percentage",
                        "v": "integer (0-100)"
                      },
                      {
                        "k": "device_type",
                        "v": "varchar(50) (web, iOS, Android, TV)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch - Video Search Index",
                    "pairs": [
                      {
                        "k": "video_id",
                        "v": "keyword"
                      },
                      {
                        "k": "title",
                        "v": "text (analyzed for full-text search)"
                      },
                      {
                        "k": "description",
                        "v": "text"
                      },
                      {
                        "k": "genre",
                        "v": "keyword (exact match for filters)"
                      },
                      {
                        "k": "actors",
                        "v": "text"
                      },
                      {
                        "k": "director",
                        "v": "text"
                      },
                      {
                        "k": "language",
                        "v": "keyword"
                      },
                      {
                        "k": "popularity_score",
                        "v": "float (for ranking)"
                      },
                      {
                        "k": "release_date",
                        "v": "date"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Caching & Session",
                    "pairs": [
                      {
                        "k": "watch_progress:{userId}:{videoId}",
                        "v": "HASH {current_time, last_updated} TTL 7 days"
                      },
                      {
                        "k": "search:{hash(query+filters)}",
                        "v": "STRING (JSON search results) TTL 10 min"
                      },
                      {
                        "k": "user:{userId}:subscription",
                        "v": "STRING (JSON subscription details) TTL 1 hour"
                      },
                      {
                        "k": "recommendations:{userId}",
                        "v": "LIST [video_ids] TTL 24 hours"
                      }
                    ]
                  },
                  {
                    "groupTitle": "S3 - Blob Storage Structure",
                    "pairs": [
                      {
                        "k": "videos-raw/",
                        "v": "{video_id}/original.mp4 (uploaded raw files)"
                      },
                      {
                        "k": "videos-processed/",
                        "v": "{video_id}/{resolution}/segment_001.ts, segment_002.ts, ... + manifest files"
                      },
                      {
                        "k": "video-images/",
                        "v": "{video_id}/thumbnail.jpg, poster.jpg"
                      },
                      {
                        "k": "subtitles/",
                        "v": "{video_id}/{language}.vtt (WebVTT subtitle files)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Kafka Topics",
                    "pairs": [
                      {
                        "k": "video.uploaded",
                        "v": "{video_id, s3_path, metadata} - triggers transcoding"
                      },
                      {
                        "k": "video.transcoded",
                        "v": "{video_id, resolutions[], manifest_url} - updates search index"
                      },
                      {
                        "k": "video.watched",
                        "v": "{user_id, video_id, duration, timestamp} - analytics & recommendations"
                      },
                      {
                        "k": "subscription.created",
                        "v": "{user_id, plan_id, payment_id} - notifications"
                      },
                      {
                        "k": "subscription.renewed",
                        "v": "{subscription_id, end_date} - consumer updates user access"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "streaming-protocols",
                "title": "8. Streaming Protocols Deep Dive",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "HLS (HTTP Live Streaming) - Apple Standard",
                    "pairs": [
                      {
                        "k": "Format",
                        "v": "Master playlist (.m3u8) \u2192 Resolution playlists (.m3u8) \u2192 Segments (.ts files, 10 sec each)"
                      },
                      {
                        "k": "Master Playlist",
                        "v": "#EXTM3U\\n#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1920x1080\\n1080p/playlist.m3u8\\n#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1280x720\\n720p/playlist.m3u8"
                      },
                      {
                        "k": "Resolution Playlist",
                        "v": "#EXTM3U\\n#EXT-X-TARGETDURATION:10\\n#EXTINF:10.0,\\nsegment_001.ts\\n#EXTINF:10.0,\\nsegment_002.ts"
                      },
                      {
                        "k": "Browser Support",
                        "v": "Safari (native), Chrome/Firefox (via hls.js library)"
                      },
                      {
                        "k": "Advantages",
                        "v": "Wide support (iOS, macOS, most smart TVs), adaptive bitrate, reliable"
                      }
                    ]
                  },
                  {
                    "groupTitle": "DASH (Dynamic Adaptive Streaming over HTTP) - MPEG Standard",
                    "pairs": [
                      {
                        "k": "Format",
                        "v": "Media Presentation Description (.mpd XML) \u2192 Segments (.m4s files)"
                      },
                      {
                        "k": "MPD Structure",
                        "v": "XML with <AdaptationSet> per resolution, <Representation> defines bitrate/resolution, <SegmentTemplate> for segment URLs"
                      },
                      {
                        "k": "Browser Support",
                        "v": "Chrome, Firefox, Edge (via dash.js), NOT Safari (use HLS for Apple)"
                      },
                      {
                        "k": "Advantages",
                        "v": "Codec agnostic (H.264, H.265, VP9), DRM support, open standard"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Comparison: HLS vs DASH",
                    "pairs": [
                      {
                        "k": "HLS Pros",
                        "v": "Apple ecosystem support, simpler format, broad device compatibility"
                      },
                      {
                        "k": "DASH Pros",
                        "v": "Codec flexibility, lower latency possible, open standard"
                      },
                      {
                        "k": "Production Choice",
                        "v": "Most platforms support BOTH: serve HLS for Apple devices, DASH for others"
                      },
                      {
                        "k": "Segment Duration",
                        "v": "HLS: 10 sec typical, DASH: 2-10 sec (lower = better adaptability, more requests)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "For Reference: DASH vs HLS Differences",
                    "pairs": [
                      {
                        "k": "Manifest Format",
                        "v": "HLS: .m3u8 (Media Playlist), DASH: .mpd (Media Presentation Description)"
                      },
                      {
                        "k": "Segment Format",
                        "v": "HLS: .ts (Transport Stream) or .m4s, DASH: .m4s (fragmented MP4) or .webm"
                      },
                      {
                        "k": "Codec Binding",
                        "v": "HLS: Mostly H.264 (can use H.265), DASH: Codec agnostic (H.264, H.265, VP9, AV1)"
                      },
                      {
                        "k": "Segment Loading",
                        "v": "HLS: Can fetch bytes/s at once, DASH: Loads only only one bitrate at a time"
                      },
                      {
                        "k": "Latency",
                        "v": "HLS: Lower latency (DASH-LL supports <2s), DASH: Higher latency (4-8 sec typically), supports LL-DASH"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "estimation",
                "title": "9. Estimation & Capacity Planning",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Storage Calculation (from image)",
                    "pairs": [
                      {
                        "k": "1 Min Video",
                        "v": "720p: 100MB/hr, 1080p: 200MB/hr, 4K: 400MB/hr"
                      },
                      {
                        "k": "1 Hour Video",
                        "v": "720p: 6GB, 1080p: 12GB, 4K: 24GB (approximate)"
                      },
                      {
                        "k": "Total Videos",
                        "v": "10k videos \u00d7 ~1 hour each"
                      },
                      {
                        "k": "Per Video Storage",
                        "v": "Original (4K): 24GB + Transcoded (4K + 1080p + 720p + 480p + 360p): ~16GB = 40GB per video"
                      },
                      {
                        "k": "Total Storage",
                        "v": "10k videos \u00d7 40GB = 400TB \u2248 400M (400 million GB as mentioned in image)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Bandwidth & Traffic",
                    "pairs": [
                      {
                        "k": "Daily Active Users",
                        "v": "20M users streaming"
                      },
                      {
                        "k": "Avg Watch Time",
                        "v": "2 hours per user per day"
                      },
                      {
                        "k": "Avg Bitrate",
                        "v": "5 Mbps (mix of resolutions, weighted average)"
                      },
                      {
                        "k": "Data per User",
                        "v": "5 Mbps \u00d7 2 hours = 10 Mb/sec \u00d7 7200 sec = 72,000 Mb = 9 GB per user"
                      },
                      {
                        "k": "Total Daily Traffic",
                        "v": "20M users \u00d7 9 GB = 180M GB = 180 PB per day"
                      },
                      {
                        "k": "Peak Concurrent Users",
                        "v": "2M concurrent (10% of DAU during prime time 8-11 PM)"
                      },
                      {
                        "k": "Peak Bandwidth",
                        "v": "2M users \u00d7 5 Mbps = 10 Tbps (Terabits per second)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Transcoding Capacity",
                    "pairs": [
                      {
                        "k": "New Uploads",
                        "v": "100 new videos per day (movies + series episodes)"
                      },
                      {
                        "k": "Transcoding Time",
                        "v": "1 hour video \u2192 ~30-60 min transcoding (parallel, GPU instances)"
                      },
                      {
                        "k": "Worker Capacity",
                        "v": "50 transcoding workers (GPU instances) processing 24/7"
                      },
                      {
                        "k": "Daily Capacity",
                        "v": "50 workers \u00d7 24 hours / 0.5 hour per video = 2400 videos/day (20x buffer)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Database & Cache",
                    "pairs": [
                      {
                        "k": "Video Metadata",
                        "v": "10k videos \u00d7 5 KB avg = 50 MB (tiny, fits in memory)"
                      },
                      {
                        "k": "User Data",
                        "v": "200M users \u00d7 1 KB = 200 GB"
                      },
                      {
                        "k": "Watch History",
                        "v": "200M users \u00d7 100 entries \u00d7 500 bytes = 10 TB (Cassandra)"
                      },
                      {
                        "k": "Redis Cache",
                        "v": "100 GB (watch progress, search cache, recommendations)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "10. Scaling & Optimization Techniques",
                "type": "bullets",
                "items": [
                  "Technique 1: CDN Multi-Tier Caching - CloudFront edge (200+ PoPs) \u2192 Origin Shield \u2192 S3, 95% cache hit rate, <50ms latency globally",
                  "Technique 2: Adaptive Bitrate Streaming (HLS/DASH) - Client auto-switches 4K \u2194 1080p \u2194 720p based on bandwidth, prevents buffering",
                  "Technique 3: Video Chunking (10-second segments) - Progressive download, fast seek, parallel downloads, efficient caching",
                  "Technique 4: Transcoding Pipeline Auto-Scaling - SQS queue triggers auto-scale (10-100 GPU workers), processes 100 videos/day",
                  "Technique 5: Elasticsearch for Search - Full-text search on title/actors/genre, <100ms latency, sharded by popularity",
                  "Technique 6: Redis for Watch Progress - HSET per user+video with TTL=7 days, batch write to Cassandra every 60s",
                  "Technique 7: Geo-Sharding S3 Buckets - Regional buckets (US, EU, Asia), CDN fetches from nearest origin, reduces latency 60%",
                  "Technique 8: Lazy Loading Thumbnails - Load images on-demand as user scrolls, WebP format (30% smaller than JPEG)",
                  "Technique 9: Pre-warming CDN for Releases - Push segments to edge locations before new season launch, instant playback",
                  "Technique 10: Compression Codecs - H.265/HEVC for 50% better compression vs H.264, AV1 for 30% better than H.265 (future)",
                  "Technique 11: Connection Pooling - API servers maintain 200 DB connections, prevents exhaustion at scale",
                  "Technique 12: Kafka Event Streaming - Async analytics, recommendations, notifications, decouples services, horizontal scaling"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How does adaptive bitrate streaming work in Netflix/YouTube?",
                    "a": "Adaptive bitrate streaming (ABR) dynamically adjusts video quality based on network conditions: (1) Video preparation: Original video transcoded into multiple resolutions (4K/2160p at 20 Mbps, 1080p at 8 Mbps, 720p at 5 Mbps, 480p at 2.5 Mbps, 360p at 1 Mbps) using FFmpeg/MediaConvert, (2) Segmentation: Each resolution split into 10-second chunks (segments), (3) Manifest file: HLS master.m3u8 or DASH master.mpd lists all available resolutions with bitrates, (4) Player logic: (a) Client requests manifest, (b) Measures current bandwidth by timing segment downloads: bandwidth = segment_size / download_time, (c) Selects appropriate resolution: if bandwidth > 8 Mbps \u2192 request 1080p segments, 5-8 Mbps \u2192 720p, 2.5-5 Mbps \u2192 480p, <2.5 Mbps \u2192 360p, (d) Downloads segments sequentially, (e) Monitors buffer level (20-40 sec ahead), (f) If bandwidth drops or buffer depletes \u2192 switch to lower resolution for next segment, (5) Seamless switching: Segments aligned on keyframes (I-frames at 0th second), player switches between playlists mid-playback without rebuffering, (6) Quality metrics: Track initial buffering time, rebuffering events, bitrate switches (too frequent = poor UX). Example: User on WiFi (8 Mbps) \u2192 Player starts with 1080p \u2192 WiFi degrades to 3 Mbps \u2192 Player detects slow download after 2-3 segments \u2192 Switches to 480p \u2192 User continues watching without stall. Key insight from image: 'Switching happens only if keyframe is set at 0th second, at this keyframe point depending on client bandwidth, player either can resume from old video format (1080) or some or different resolution.' HLS vs DASH: HLS uses .m3u8 + .ts segments (Apple standard), DASH uses .mpd + .m4s segments (MPEG standard, codec agnostic). Production: Serve both HLS (for Apple devices) and DASH (for others)."
                  },
                  {
                    "q": "Explain the video upload and transcoding pipeline end-to-end.",
                    "a": "End-to-end video processing flow: (1) Upload: Content creator uploads raw video (e.g., movie.mp4, 50GB 4K) + metadata {title, description, genre, actors, duration}, (2) Uploader Service: (a) Generates video_id (UUID), (b) Multipart upload to S3: s3://videos-raw/{video_id}/original.mp4 (parallel chunks for faster upload), (c) Stores metadata: INSERT INTO video_metadata (video_id, title, processing_status='UPLOADING'), (d) On upload completion \u2192 Publish Kafka event 'video.uploaded' with {video_id, s3_path}, (3) Transcoding Pipeline: (a) Lambda/Worker consumes Kafka event, (b) Downloads original from S3, (c) FFmpeg/AWS MediaConvert transcodes to multiple resolutions: 4K (3840\u00d72160, H.265, 20 Mbps), 1080p (1920\u00d71080, H.264, 8 Mbps), 720p (1280\u00d7720, 5 Mbps), 480p (2.5 Mbps), 360p (1 Mbps), (d) For EACH resolution: Encode video, Split into 10-sec segments (.ts for HLS or .m4s for DASH), Upload to S3: s3://videos-processed/{video_id}/{resolution}/segment_001.ts, ..., (e) Generate manifest files: Master.m3u8 (points to all resolution playlists), 1080p/playlist.m3u8 (lists segment URLs for 1080p), (f) Upload manifests to S3, (4) Metadata Update: UPDATE video_metadata SET processing_status='COMPLETED', manifest_url='s3://.../master.m3u8', resolutions=['4K','1080p','720p','480p','360p'], (5) Search Indexing: Kafka CDC event \u2192 Elasticsearch indexes video for search, (6) CDN Distribution: CloudFront pulls segments from S3, caches at edge locations globally. Transcoding optimizations: (a) Parallel processing: Each resolution transcoded on separate GPU instance, total time ~30 min for 2-hour movie, (b) Spot instances for cost savings (transcoding is batch, interruptible), (c) Two-pass encoding for better quality, (d) Auto-scale workers based on SQS queue depth (10-100 instances). Storage: Original 4K (24GB) + Transcoded (4K+1080p+720p+480p+360p \u2248 16GB) = 40GB per 1-hour video."
                  },
                  {
                    "q": "How do you minimize video buffering and ensure smooth playback?",
                    "a": "Multi-layer buffering prevention strategy: (1) Adaptive bitrate: Automatically reduce quality when bandwidth drops (8 Mbps \u2192 1080p, 3 Mbps \u2192 480p), prevents stalling during network fluctuations, (2) Buffering strategy: Player maintains 20-40 sec buffer (downloads 3-5 segments ahead), if buffer <10 sec \u2192 aggressive switch to lower resolution + increase segment prefetch, (3) CDN edge caching: Segments cached at 200+ CloudFront PoPs globally, 95% cache hit rate, <50ms latency vs 200ms from origin, (4) Segment size: 10-sec segments (vs 30-sec) enable faster quality switching and seeking, smaller download chunks reduce impact of network interruptions, (5) Preloading: On video start, download first 2-3 segments at lower quality for instant playback, then switch to optimal quality, (6) Connection optimization: HTTP/2 multiplexing for parallel segment downloads, connection keep-alive reduces handshake overhead, (7) Predictive prefetching: If user watching series, preload first 2 segments of next episode, (8) Network condition monitoring: Player tracks: segment download time, throughput (Mbps), buffer level, packet loss \u2192 adjusts quality proactively, (9) Fallback quality: If multiple segments fail to download at current quality, immediately drop 2 levels (1080p \u2192 480p instead of gradual 1080p \u2192 720p \u2192 480p), (10) CDN failover: If edge location errors (5xx), automatically retry from different PoP or origin shield, (11) Client-side optimization: Hardware decoding (GPU) for H.264/H.265 reduces CPU load, video.js/hls.js optimized players with smart buffering logic. Quality metrics tracked: Initial buffering time (time to first frame, target <2 sec), Rebuffering ratio (time spent buffering / total watch time, target <0.5%), Bitrate switches (too frequent = annoying, target <5 switches per hour). Example scenario: User starts watching \u2192 Player requests 1080p \u2192 First 2 segments download fast (WiFi, buffer at 20 sec) \u2192 User moves, WiFi weakens \u2192 Segment 3 download slow (detected) \u2192 Player switches to 720p for segment 4 \u2192 Buffering continues uninterrupted \u2192 User never sees spinner."
                  },
                  {
                    "q": "How do you design the CDN architecture for global video delivery?",
                    "a": "Multi-tier CDN architecture for global scale: (1) Origin: S3 buckets in multiple regions (us-east-1, eu-west-1, ap-south-1) with cross-region replication, stores transcoded video segments, (2) Origin Shield: CloudFront feature, centralized caching layer between edge and origin, acts as 'shield' to collapse requests (100 edge requests \u2192 1 shield request to origin), reduces origin load by 90%, improves cache hit rate, (3) Edge Locations: 200+ CloudFront PoPs globally (NYC, London, Mumbai, Tokyo, etc.), cache segments with TTL=24 hours, serve 95% of requests without origin hit, (4) Request flow: User in Mumbai requests segment \u2192 DNS routes to nearest edge (Mumbai PoP) \u2192 Edge checks cache (cache key: video_id/resolution/segment_number) \u2192 If HIT: Return cached segment (10-50ms) \u2192 If MISS: Request from Origin Shield (us-east-1) \u2192 Shield checks cache \u2192 If miss: Fetch from S3 \u2192 Shield caches + returns \u2192 Edge caches + returns (200ms first time, <50ms subsequent), (5) Cache optimization: (a) Popular content (trending movies): Pre-warmed to edges before release (push invalidation), (b) Long-tail content: Cached on-demand (first request slower, subsequent fast), (c) Purge strategy: Invalidate segments only when video updated (rare), otherwise rely on TTL, (6) Geo-routing: S3 buckets per region (us-west-2, eu-west-1, ap-south-1), CloudFront fetches from nearest origin based on edge location, reduces latency by 60% (500ms \u2192 200ms for origin fetches), (7) Signed URLs: CloudFront signed URLs with expiry=4 hours prevent hotlinking and unauthorized access, signature validates user has active subscription, (8) Compression: Gzip/Brotli for manifest files (.m3u8, .mpd) saves 70% bandwidth, not for video segments (already compressed), (9) HTTP/2: Multiplexing allows parallel segment downloads over single connection, reduces handshake overhead, (10) Monitoring: CloudWatch tracks: cache hit rate (target >95%), origin requests (target <5%), error rate (4xx, 5xx <0.1%), bandwidth usage. Cost calculation: 20M DAU \u00d7 2 hours \u00d7 5 Mbps / 8 = 20M \u00d7 2 \u00d7 5/8 GB = 25M GB/day = 750M GB/month \u2192 750M GB \u00d7 $0.085/GB = $63M/month in CDN bandwidth. Optimization: Cache hit rate 95% \u2192 only 5% hits origin \u2192 $63M \u00d7 0.05 = $3.15M origin bandwidth cost."
                  },
                  {
                    "q": "How do you implement video search with filters (genre, actor, language)?",
                    "a": "Elasticsearch-based video search with filters: (1) Index mapping: PUT /videos { 'mappings': { 'properties': { 'video_id': {'type': 'keyword'}, 'title': {'type': 'text', 'analyzer': 'standard', 'fields': {'keyword': {'type': 'keyword'}}}, 'description': {'type': 'text'}, 'genre': {'type': 'keyword'}, 'actors': {'type': 'text', 'fields': {'keyword': {'type': 'keyword'}}}, 'director': {'type': 'text'}, 'language': {'type': 'keyword'}, 'content_rating': {'type': 'keyword'}, 'release_date': {'type': 'date'}, 'popularity_score': {'type': 'float'} } } }, (2) Indexing: Video metadata from PostgreSQL \u2192 Kafka CDC \u2192 Elasticsearch indexer, each video document indexed with all searchable fields, (3) Search query: GET /v1/videos/search?q=Inception&genre=Sci-Fi&actor=Leonardo DiCaprio&language=English&sort=popularity \u2192 Backend builds ES query: POST /videos/_search { 'query': { 'bool': { 'must': [ { 'multi_match': { 'query': 'Inception', 'fields': ['title^3', 'description', 'actors', 'director'], 'type': 'best_fields' } } ], 'filter': [ { 'term': { 'genre': 'Sci-Fi' } }, { 'match': { 'actors': 'Leonardo DiCaprio' } }, { 'term': { 'language': 'English' } } ] } }, 'sort': [ { 'popularity_score': 'desc' } ], 'from': 0, 'size': 20 }, (4) Scoring: (a) Text relevance: 'title^3' boosts title matches 3x over description, (b) Popularity: popularity_score = log(view_count) + 0.5 \u00d7 avg_rating + recency_boost, calculated offline daily via batch job, (c) Personalization (advanced): Boost genres user previously watched, use ML model (collaborative filtering) to rerank results, (5) Faceted search: Add aggregations: 'aggs': { 'genres': { 'terms': { 'field': 'genre', 'size': 20 } }, 'languages': { 'terms': { 'field': 'language' } }, 'rating_buckets': { 'histogram': { 'field': 'popularity_score', 'interval': 1 } } } \u2192 Returns counts: {Sci-Fi: 150, Action: 200, ...} for UI filters, (6) Autocomplete: Separate index with edge_ngram tokenizer for type-ahead suggestions, queries update every keystroke with debouncing (300ms), (7) Caching: Redis caches popular queries: Key = hash(query_text + filters + sort), Value = JSON result, TTL = 10 min, Cache hit rate 60-70%, (8) Performance: ES cluster: 10 data nodes (5 primary shards, 1 replica each), query latency p95 <100ms for 10M videos, (9) Index updates: Near real-time via Kafka, new videos searchable within 1-2 seconds of metadata creation. Example: User searches 'leonardo' \u2192 Autocomplete suggests 'Leonardo DiCaprio', 'The Revenant', 'Inception' \u2192 User selects 'Inception' \u2192 Full search returns Inception + similar movies \u2192 User filters by 'Sci-Fi' \u2192 Results filtered client-side or re-query with filter."
                  },
                  {
                    "q": "How do you handle subscription management and payment processing?",
                    "a": "Subscription lifecycle with payment integration: (1) Subscription plans: Table subscription_plans {plan_id: 'basic', name: 'Basic', price: 9.99, currency: 'USD', max_resolution: '720p', max_screens: 1, billing_period: 'monthly'}, Plans: Basic ($9/mo, 720p, 1 screen), Standard ($13/mo, 1080p, 2 screens), Premium ($17/mo, 4K, 4 screens), (2) User subscribes: POST /v1/subscriptions with {user_id, plan_id: 'premium', payment_method_id: 'pm_abc123' (from Stripe)}, (3) Payment flow: (a) Subscription Service calls Stripe API: stripe.subscriptions.create({ customer: user.stripe_customer_id, items: [{price: plan.stripe_price_id}], payment_behavior: 'default_incomplete', expand: ['latest_invoice.payment_intent'] }), (b) Returns client_secret for confirming payment on frontend, (c) User confirms payment in browser (3D Secure if required), (d) Stripe sends webhook POST /webhooks/stripe with {type: 'invoice.paid', subscription_id, payment_intent}, (4) Webhook handling: (a) Validate signature (security), (b) Idempotency check: SELECT * FROM subscriptions WHERE stripe_subscription_id={subscription_id}, if exists return 200 (already processed), (c) BEGIN TRANSACTION; INSERT INTO subscriptions (subscription_id, user_id, plan_id, status='ACTIVE', start_date=now(), end_date=now()+30days, stripe_subscription_id, stripe_payment_intent_id); UPDATE users SET subscription_status='ACTIVE', subscription_id={subscription_id}; COMMIT;, (d) Invalidate Redis cache: DEL user:{user_id}:subscription, (e) Publish Kafka 'subscription.created' \u2192 Notification Service sends welcome email, (5) Access control: On video playback request: (a) GET /v1/videos/{video_id}/play \u2192 Validate subscription: SELECT status, end_date, plan_id FROM subscriptions WHERE user_id={user_id} AND status='ACTIVE', (b) Check plan allows video quality: if requesting 4K but plan.max_resolution='1080p' \u2192 return 403 with upgrade prompt, (c) Check concurrent streams: SELECT COUNT(*) FROM active_sessions WHERE user_id={user_id} \u2192 if count >= plan.max_screens \u2192 return 403 'Too many devices', (d) Generate signed CDN URL with expiry, (6) Auto-renewal: (a) Stripe automatically charges on renewal date (30 days from start), (b) If payment succeeds \u2192 webhook 'invoice.paid' \u2192 UPDATE subscriptions SET end_date=end_date+30days, (c) If payment fails \u2192 webhook 'invoice.payment_failed' \u2192 Notification Service sends email, grace period 7 days before downgrade, retry payment 3 times, (d) After grace period: UPDATE subscriptions SET status='EXPIRED' \u2192 user downgraded to free tier (if exists) or blocked, (7) Cancellation: PUT /v1/subscriptions/{subscription_id}/cancel \u2192 stripe.subscriptions.update({subscription_id, cancel_at_period_end: true}) \u2192 User keeps access until end_date, then status='CANCELLED', (8) Billing history: GET /v1/users/{user_id}/billing \u2192 fetch from Stripe API: stripe.invoices.list({customer: stripe_customer_id}) \u2192 display in user dashboard. Edge cases: (1) Dunning: Failed payment \u2192 retry with exponential backoff, send notification, (2) Refunds: if user cancels within 7 days, pro-rated refund via stripe.refunds.create(), (3) Upgrades: Mid-cycle upgrade from Basic to Premium \u2192 calculate pro-rated charge for remaining days, (4) Regional pricing: Different prices per country stored in subscription_plans_regional table."
                  },
                  {
                    "q": "How do you track and store watch history for 200M users?",
                    "a": "Scalable watch history with Cassandra + Redis: (1) Real-time progress tracking: Client sends heartbeat every 10 seconds: POST /v1/videos/{video_id}/progress with {user_id, current_time_seconds: 1245, timestamp}, (2) Redis for hot data: (a) Write to Redis: HSET watch_progress:{user_id}:{video_id} 'current_time' {current_time} 'last_updated' {timestamp} 'device_id' {device_id}, (b) TTL: EXPIRE watch_progress:{user_id}:{video_id} 604800 (7 days), auto-cleanup old sessions, (c) Read on resume: GET /v1/videos/{video_id}/play \u2192 HGET watch_progress:{user_id}:{video_id} 'current_time' \u2192 return {resume_from: 1245}, client seeks to position, (3) Cassandra for persistence: (a) Background job (every 60s): Batch write from Redis to Cassandra, (b) Table schema: CREATE TABLE watch_history (user_id uuid, video_id uuid, watched_at timestamp, current_time int, completion_percentage int, device_type text, PRIMARY KEY (user_id, watched_at)) WITH CLUSTERING ORDER BY (watched_at DESC), partition by user_id for fast user-specific queries, (c) INSERT: INSERT INTO watch_history (user_id, video_id, watched_at, current_time, completion_percentage, device_type) VALUES ({user_id}, {video_id}, now(), {current_time}, {percentage}, {device_type}), (4) Analytics pipeline: (a) Kafka event stream: On every progress update, publish 'video.watched' event: {user_id, video_id, session_id, current_time, duration_watched, timestamp, geo_location, device_type}, (b) Stream processing: Apache Flink/Spark Streaming consumes events, calculates aggregations: total_watch_time, unique_viewers, completion_rate, drop-off_points, (c) Results stored in analytics DB (BigQuery/Redshift) for dashboards, (5) Recommendations: (a) Collaborative filtering: Users who watched X also watched Y, (b) Content-based: Similar genre, actors, (c) ML model (TensorFlow) trained on: watch_history (implicit feedback: user watched = positive signal), explicit ratings if available, search queries, (d) Inference: Batch job runs nightly, generates top 20 recommendations per user, stored in Redis: SET recommendations:{user_id} '[video_ids]' EX 86400, (e) Real-time updates: As user watches, update recommendations incrementally, (6) Scalability: (a) Cassandra: Partitioned by user_id, 200M users \u00d7 100 entries avg \u00d7 500 bytes = 10 TB, replicated 3x = 30 TB, (b) Redis: Only active sessions (10M concurrent users \u00d7 1 KB = 10 GB), (c) Cassandra write throughput: 20M DAU \u00d7 1 update/10s = 2M writes/sec, Cassandra handles 1M writes/sec per node, 3 nodes sufficient with replication, (7) Data retention: Cassandra TTL on rows: WITH default_time_to_live = 31536000 (1 year), old data auto-deleted. Example: User pauses at 20:45 \u2192 Client sends progress \u2192 Redis updated \u2192 Background job syncs to Cassandra \u2192 User opens app next day \u2192 Resume API queries Redis \u2192 Returns last_position=1245s \u2192 Player seeks to 20:45 \u2192 Seamless resume."
                  },
                  {
                    "q": "How do you optimize video delivery cost at scale (CDN, storage, transcoding)?",
                    "a": "Multi-layer cost optimization: (1) CDN caching: 95% cache hit rate \u2192 only 5% requests hit origin, saves 95% of origin bandwidth cost, Cost: 20M DAU \u00d7 2h \u00d7 5Mbps / 8 = 25M GB/day \u2192 750M GB/month, CDN $0.085/GB \u2192 $63M/month, Origin (5%) $0.023/GB \u2192 $63M \u00d7 0.05 \u00d7 0.023/0.085 = $0.86M/month, Total CDN: $63.86M/month \u2192 Optimization: Negotiate volume discounts (50% off at petabyte scale) \u2192 ~$32M/month, (2) Storage tiering: (a) Hot data (recent releases, popular content): S3 Standard ($0.023/GB) \u2192 20% of catalog = 80 TB \u00d7 $0.023 = $1,840/month, (b) Warm data (1-year old): S3 Infrequent Access ($0.0125/GB) \u2192 30% = 120 TB \u00d7 $0.0125 = $1,500/month, (c) Cold data (>2 years old, rarely watched): S3 Glacier ($0.004/GB) \u2192 50% = 200 TB \u00d7 $0.004 = $800/month, Total storage: $4,140/month vs $9,200 all-standard (55% savings), (3) Transcoding optimization: (a) Spot instances: Use EC2 spot for 70% of transcoding workers \u2192 70% discount vs on-demand ($2.50/hr \u2192 $0.75/hr), Risk: Interruption \u2192 batch job idempotent, restarts from checkpoint, (b) Auto-scaling: Scale workers 10-100 based on queue depth (SQS), average 30 instances \u00d7 24h \u00d7 30 days \u00d7 $0.75 = $16,200/month, (c) Smart resolution selection: Don't transcode 4K for all content (only blockbusters), save 30% transcoding cost, (4) Compression: (a) H.265/HEVC: 50% better compression vs H.264, reduces storage and bandwidth by 40%, 10k videos \u00d7 40GB \u2192 with HEVC 10k \u00d7 24GB = 240 TB vs 400 TB, savings: 160 TB \u00d7 $0.023 = $3,680/month storage + proportional CDN savings, (b) AV1 (future): 30% better than HEVC, but slower encoding (2x time), not yet widely supported, (5) Lazy transcoding: Don't transcode all resolutions immediately, transcode on-demand when requested (e.g., only 1% watch 4K), saves 40% transcoding cost, (6) Content pruning: Delete unpopular content after 5 years (watched <100 times), frees storage, saves $500/month, (7) Regional optimization: Store content in S3 bucket closest to main audience region (US content in us-east-1), reduces cross-region transfer costs, (8) Manifest file compression: Gzip .m3u8 files saves 70% \u2192 10k videos \u00d7 10 KB \u00d7 0.7 = negligible but good practice, (9) Reserved capacity: Reserve 50% baseline CDN bandwidth for 1-year term \u2192 30% discount vs on-demand, (10) Monitoring and alerting: Track cost per video, cost per user, shutdown zombie resources (unused transcoding workers, orphaned S3 objects). Total monthly cost (before optimizations): CDN $63M + Storage $9K + Transcoding $50K + Misc $100K \u2248 $63.2M \u2192 After optimizations: CDN $32M + Storage $4K + Transcoding $16K + Misc $50K \u2248 $32.1M (49% savings)."
                  },
                  {
                    "q": "How do you implement DRM (Digital Rights Management) to prevent piracy?",
                    "a": "DRM implementation for content protection: (1) DRM standards: (a) Widevine: Google standard, used by Chrome, Android, (b) FairPlay: Apple standard, used by Safari, iOS, (c) PlayReady: Microsoft standard, used by Edge, Xbox, (2) Encryption: (a) Video segments encrypted during transcoding using AES-128 or AES-256, (b) Each segment encrypted with unique encryption key, (c) Encryption keys stored in License Server (AWS Elemental MediaPackage, BuyDRM, etc.), (3) Key delivery: (a) Player requests manifest \u2192 receives encrypted segment URLs + license_url, (b) Player requests decryption key: GET {license_url} with user_token and device_id, (c) License Server validates: User has active subscription, Device is registered (max 5 devices per user), DRM client installed (not emulator/rooted device), (d) If valid: Returns decryption key encrypted with device's public key (only that device can decrypt), (e) Player decrypts segments in memory, renders video to screen (encrypted buffer \u2192 decryption \u2192 display pipeline), (4) Content protection: (a) HDCP (High-bandwidth Digital Content Protection): Prevents screen recording via HDMI, enforced by hardware, (b) Watermarking: Invisible forensic watermark embedded with user_id, if leaked can trace source, (c) Screen capture blocking: DRM API blocks screen recording apps, screenshots show black screen, (5) Offline downloads: (a) Download encrypted segments to local storage (mobile app), (b) License downloaded with expiry (e.g., 30 days), (c) On playback: Validate license not expired, decrypt and play, (d) On subscription end: License invalidated, downloaded content unplayable, (6) Implementation: (a) Transcoding: FFmpeg with DRM packaging: ffmpeg -i input.mp4 -c:v libx264 -encryption_scheme cenc -encryption_key {key} -encryption_kid {key_id} output.mp4, (b) Manifest: HLS includes #EXT-X-KEY:METHOD=SAMPLE-AES,URI={license_server_url},KEYFORMAT='com.apple.streamingkeydelivery', (c) Player: Use ExoPlayer (Android), AVPlayer (iOS), Shaka Player (web) with DRM plugin, (7) Multi-DRM: Support all 3 standards for cross-platform: if Safari \u2192 FairPlay, if Chrome \u2192 Widevine, if Edge \u2192 PlayReady, use unified license server (BuyDRM, Irdeto) that handles all, (8) Challenges: (a) Performance: Decryption overhead ~5-10% CPU, use hardware-accelerated decryption (GPU), (b) Compatibility: Older devices may not support, fallback to lower DRM level or no DRM, (c) Cost: License server $0.01-0.05 per view, 20M daily views = $200K-$1M/month, (9) Anti-piracy: (a) Geofencing: Block VPNs, restrict content by region, (b) Concurrent stream limits: Max 2-4 devices simultaneously, (c) Token expiry: Signed URLs valid for 4 hours, forces re-authentication. Example: User clicks play \u2192 Player requests manifest \u2192 Manifest includes encrypted segment URLs + license_url \u2192 Player requests license from server with user_token \u2192 Server validates subscription + device \u2192 Returns decryption key (encrypted) \u2192 Player decrypts using device key \u2192 Segments decrypted in memory and rendered \u2192 User cannot save or share video file."
                  },
                  {
                    "q": "What's your disaster recovery and fault tolerance strategy for a streaming platform?",
                    "a": "Multi-region DR with automated failover: (1) Architecture: Primary region us-east-1, Secondary us-west-2, Tertiary eu-west-1, (2) Data replication: (a) PostgreSQL: Primary in us-east-1, streaming replication to us-west-2 (warm standby, lag <5s) and eu-west-1 (read replica, lag <10s), (b) S3: Cross-region replication (CRR) enabled, video segments replicated to all 3 regions automatically within minutes, (c) Redis: Redis Sentinel for automatic failover, master-slave replication (lag <1s), AOF persistence for durability, (d) Cassandra: Multi-DC deployment with replication_factor=3 across all regions, LOCAL_QUORUM for reads/writes, (3) CDN resilience: CloudFront automatically routes to healthy origins, if us-east-1 S3 fails \u2192 CloudFront fetches from us-west-2 bucket transparently, no user impact, (4) Failover procedure: (a) Health checks: Route53 health checks on primary region every 30s (TCP, HTTP, latency), (b) Trigger: If 3 consecutive checks fail (90s) \u2192 Route53 updates DNS to point to us-west-2, (c) Database promotion: us-west-2 PostgreSQL promoted to primary (pg_ctl promote), takes ~2-3 min, (d) Application servers: Auto-scaling groups in us-west-2 pre-warmed (min 10% capacity always running), scale up to 100% within 5 min, (e) DNS propagation: 5-10 min for DNS TTL, progressive migration as clients refresh, (5) RPO/RTO targets: (a) RPO (Recovery Point Objective): <5 seconds data loss (streaming replication lag), (b) RTO (Recovery Time Objective): <10 minutes service restoration, (6) Service degradation: (a) Read-only mode: If primary DB fails but replicas healthy \u2192 serve read-only, users can watch but not subscribe/update profiles, (b) CDN-only mode: If all origins down \u2192 serve cached content only (95% of requests), new uploads fail, (c) Graceful degradation: Disable non-critical features (recommendations, continue watching) to reduce load, (7) Testing: (a) Monthly DR drills: Simulate primary region failure, execute failover, verify RTO met, (b) Chaos engineering: Randomly terminate instances in production (during low-traffic hours), verify auto-recovery, (c) Game days: Simulate various failure scenarios (DB corruption, CDN outage, DDoS attack), (8) Monitoring: (a) Datadog/New Relic: Real-time metrics (latency, error rate, throughput), (b) Alerts: PagerDuty for critical issues (DB lag >10s, API error rate >1%, CDN cache hit rate <90%), (c) Dashboards: Grafana showing: concurrent viewers, video playback errors, buffering ratio, DB replication lag, (9) Backup and restore: (a) S3: Versioning enabled, accidental deletes recoverable, (b) Database: Daily full backups + hourly incremental to S3 Glacier, retention 30 days, (c) Point-in-time recovery: Can restore DB to any point within last 7 days, (d) Backup restore testing: Quarterly restore from backup to staging environment, verify data integrity, (10) DDoS protection: (a) AWS Shield Advanced: Protects against L3/L4 DDoS, (b) CloudFront rate limiting: Geo-blocking suspicious traffic, WAF rules block malicious patterns, (c) Auto-scaling: Absorb traffic spikes up to 10x normal load. Example failure: Primary region (us-east-1) suffers power outage at 2:00 PM \u2192 Route53 health checks fail at 2:01:30 \u2192 DNS updated to us-west-2 at 2:02 \u2192 PostgreSQL promoted at 2:04 \u2192 Users experience brief buffering (current segment completes from cache, next segment from us-west-2) \u2192 Service fully restored at 2:10 PM \u2192 Data loss: <5s of watch progress for in-flight requests. Total cost: Multi-region setup adds 30-40% infrastructure cost, insurance against revenue loss during outages (1 hour outage = $500K lost revenue + reputation damage)."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "12. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Volume",
                    "pairs": [
                      {
                        "k": "Total Users",
                        "v": "200M registered users globally"
                      },
                      {
                        "k": "Daily Active Users",
                        "v": "20M DAU streaming video"
                      },
                      {
                        "k": "Total Videos",
                        "v": "10k videos (~1 hour avg duration each)"
                      },
                      {
                        "k": "Total Storage",
                        "v": "400TB = 10k videos \u00d7 40GB (original + transcoded)"
                      },
                      {
                        "k": "Daily Traffic",
                        "v": "180 PB/day = 20M users \u00d7 2 hours \u00d7 5 Mbps"
                      },
                      {
                        "k": "Peak Concurrent",
                        "v": "2M concurrent viewers (10% of DAU, prime time 8-11 PM)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Video Bitrates & Storage (from image)",
                    "pairs": [
                      {
                        "k": "1080p",
                        "v": "~8 Mbps bitrate, ~200 MB/hr, 12 GB for 1-hour video"
                      },
                      {
                        "k": "720p",
                        "v": "~5 Mbps bitrate, ~100 MB/hr, 6 GB for 1-hour video"
                      },
                      {
                        "k": "4K",
                        "v": "~20 Mbps bitrate, ~400 MB/hr, 24 GB for 1-hour video"
                      },
                      {
                        "k": "480p",
                        "v": "~2.5 Mbps bitrate, ~50 MB/hr, 3 GB for 1-hour video"
                      },
                      {
                        "k": "360p",
                        "v": "~1 Mbps bitrate, ~20 MB/hr, 1.2 GB for 1-hour video"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance & Latency",
                    "pairs": [
                      {
                        "k": "CDN Edge Latency",
                        "v": "10-50ms (cache hit), 200ms (cache miss to origin)"
                      },
                      {
                        "k": "Initial Buffering",
                        "v": "Target <2 seconds (time to first frame)"
                      },
                      {
                        "k": "Segment Duration",
                        "v": "10 seconds (HLS/DASH standard)"
                      },
                      {
                        "k": "Player Buffer",
                        "v": "Maintain 20-40 seconds ahead (3-5 segments)"
                      },
                      {
                        "k": "Transcoding Time",
                        "v": "30-60 min for 2-hour 4K movie (parallel GPU instances)"
                      },
                      {
                        "k": "Search Latency",
                        "v": "<100ms (Elasticsearch p95)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Streaming Protocols",
                    "pairs": [
                      {
                        "k": "HLS",
                        "v": ".m3u8 manifest + .ts segments (Apple standard)"
                      },
                      {
                        "k": "DASH",
                        "v": ".mpd manifest + .m4s segments (MPEG standard)"
                      },
                      {
                        "k": "Codec H.264",
                        "v": "Standard, widely supported, baseline compression"
                      },
                      {
                        "k": "Codec H.265/HEVC",
                        "v": "50% better compression than H.264, 4K/HDR"
                      },
                      {
                        "k": "Codec AV1",
                        "v": "30% better than HEVC, future standard, slower encoding"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Costs (Monthly)",
                    "pairs": [
                      {
                        "k": "CDN Bandwidth",
                        "v": "$32M/month (750M GB \u00d7 $0.085/GB with 50% volume discount)"
                      },
                      {
                        "k": "Storage (S3)",
                        "v": "$4K/month (tiered: Standard + IA + Glacier)"
                      },
                      {
                        "k": "Transcoding",
                        "v": "$16K/month (30 spot instances \u00d7 $0.75/hr)"
                      },
                      {
                        "k": "DRM License",
                        "v": "$200K-$1M/month ($0.01-0.05 per view \u00d7 20M daily views)"
                      },
                      {
                        "k": "Total Infrastructure",
                        "v": "~$32.5M/month (dominated by CDN bandwidth)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Business Metrics",
                    "pairs": [
                      {
                        "k": "Subscription Plans",
                        "v": "Basic $9/mo (720p, 1 screen), Standard $13/mo (1080p, 2 screens), Premium $17/mo (4K, 4 screens)"
                      },
                      {
                        "k": "Avg Watch Time",
                        "v": "2 hours per user per day"
                      },
                      {
                        "k": "Cache Hit Rate",
                        "v": "95% (CDN edge locations)"
                      },
                      {
                        "k": "Conversion Rate",
                        "v": "5-10% (free trial to paid subscription)"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Adaptive streaming requires keyframes at segment boundaries (0th second). Without this, quality switching causes stuttering. FFmpeg: -g 300 -keyint_min 300 for 10-sec segments at 30fps (300 frames)."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How does adaptive bitrate streaming work?'. Answer: (1) Transcode to multiple resolutions (4K, 1080p, 720p, 480p), (2) Split into 10-sec segments, (3) Generate manifest with all options, (4) Player measures bandwidth, (5) Selects appropriate resolution dynamically, (6) Switches seamlessly on keyframes."
              },
              {
                "type": "tip",
                "text": "CDN optimization: 95% cache hit rate is critical. Popular content (trending movies) pre-warmed to edges before release. Long-tail cached on-demand. Saves $30M/month in origin bandwidth costs."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: HLS vs DASH. HLS (.m3u8 + .ts) for Apple devices, DASH (.mpd + .m4s) for others. Production platforms serve BOTH. Segments aligned on keyframes enable seamless quality switching."
              },
              {
                "type": "warning",
                "text": "NEVER transcode synchronously on upload. Use async pipeline: Upload \u2192 S3 \u2192 Kafka event \u2192 Workers \u2192 Transcoding \u2192 S3. 2-hour 4K movie takes 30-60 min to transcode. Queue-based with auto-scaling."
              },
              {
                "type": "tip",
                "text": "Storage tiering: 20% hot (S3 Standard), 30% warm (S3 IA), 50% cold (S3 Glacier). Saves 55% storage cost vs all-standard. Automate with S3 Lifecycle policies based on view frequency."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'How to minimize buffering?'. Answer: (1) Adaptive bitrate (auto-reduce quality on network drop), (2) CDN edge caching (95% hit rate, <50ms), (3) 20-40 sec buffer, (4) Preload 3-5 segments ahead, (5) HTTP/2 multiplexing."
              },
              {
                "type": "warning",
                "text": "NEVER store watch progress in PostgreSQL directly. Use Redis (1ms latency) for hot data, batch write to Cassandra every 60s. 20M users \u00d7 1 update/10s = 2M writes/sec, Redis handles this, PostgreSQL does not."
              },
              {
                "type": "tip",
                "text": "DRM for piracy prevention: Widevine (Chrome/Android), FairPlay (Apple), PlayReady (Microsoft). Segments encrypted, license server validates subscription + device. Costs $0.01-0.05 per view but essential for premium content."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Transcoding pipeline. Upload raw video \u2192 S3 \u2192 Kafka event \u2192 FFmpeg workers transcode to 4K, 1080p, 720p, 480p, 360p \u2192 Split into 10-sec segments \u2192 Generate HLS/DASH manifests \u2192 Upload to S3 \u2192 CDN distributes globally."
              }
            ],
            "tags": [
              "system-design",
              "OTT-platform",
              "Netflix",
              "Amazon-Prime",
              "Hotstar",
              "video-streaming",
              "adaptive-bitrate",
              "HLS",
              "DASH",
              "CDN",
              "CloudFront",
              "transcoding",
              "FFmpeg",
              "Elasticsearch",
              "subscription-management",
              "DRM",
              "Cassandra",
              "Redis",
              "S3",
              "Kafka"
            ]
          }
        },
        {
          "id": "05",
          "title": "System Design 5: Design Hotel Booking App like AirBnb / Booking.com | Proximity | HLD | LLD",
          "videoUrl": "https://youtu.be/mH2Ye3_vErw",
          "description": "In this video, we walk through the complete system design of a hotel booking platform where users can search, filter, and book hotels based on location, date, and availability.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/njo23n4636n3on.webp?updatedAt=1746473843516",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/HotelBookingSystem%20(1).png?updatedAt=1746560534433",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "Hotel Booking System (Airbnb/MakeMyTrip/Bookings.com)",
              "oneLinerToMemorize": "Search hotels (Elasticsearch geo) \u2192 Check availability (Redis lock) \u2192 Book room (distributed lock) \u2192 Payment (idempotency) \u2192 Confirmation (Kafka events)"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User should be able to create account in our application (register/login/logout/update profile)",
                  "Feature 2: Search for hotels based on name, location, dates with filters (price, rating, amenities)",
                  "Feature 3: View hotel details with available rooms, prices, photos, reviews",
                  "Feature 4: Make the payment to confirm booking for selected room and dates",
                  "Feature 5: View past and future booking details on profile with ability to cancel/modify bookings"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Users",
                        "v": "50M users, 1M hotels globally"
                      },
                      {
                        "k": "Daily Searches",
                        "v": "100M searches/day, 1M bookings/day"
                      },
                      {
                        "k": "Rooms",
                        "v": "10M+ hotel rooms across all properties"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance & Availability",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >>> consistency. Application should be highly available based on searching and system should be highly consistent based on booking. No two users should be allowed to book the same room/hotel on same time frames"
                      },
                      {
                        "k": "Search Latency",
                        "v": "< 500ms for hotel search with location, dates, filters"
                      },
                      {
                        "k": "Booking Consistency",
                        "v": "Strong consistency - prevent double booking using distributed locks"
                      },
                      {
                        "k": "Payment Reliability",
                        "v": "Exactly-once semantics with idempotency, no double charging"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities (Identify Core Entity)",
                "type": "bullets",
                "items": [
                  "Entity 1: User - Customer with user_id, name, email, phone, password, preferences, booking_history[]",
                  "Entity 2: Hotel - Property with hotel_id, name, location (lat, lng), address, rating, amenities[], images[], description",
                  "Entity 3: Room - Room inventory with room_id, hotel_id, room_type, capacity, price, amenities[], availability calendar",
                  "Entity 4: Booking - Reservation with booking_id, user_id, hotel_id, room_id, check_in_date, check_out_date, status, amount, payment_id"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "User Management",
                    "pairs": [
                      {
                        "k": "POST /v1/users/register",
                        "v": "Register user {name, email, password} \u2192 {userId, token} (login/logout/update)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Hotel Search & Details",
                    "pairs": [
                      {
                        "k": "GET /v1/hotels/search",
                        "v": "Search hotels {location, checkIn, checkOut, guests} \u2192 List<Hotel(partial)> with pagination"
                      },
                      {
                        "k": "GET /v1/hotels/{hotelId}",
                        "v": "Get hotel details \u2192 HotelDetails with rooms, reviews, images"
                      },
                      {
                        "k": "GET /v1/hotels/room/{hotelId}/{dates}",
                        "v": "Get available rooms for specific dates \u2192 List<Room> with pricing and availability"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Booking Management",
                    "pairs": [
                      {
                        "k": "POST /v1/booking/roomId",
                        "v": "Create booking {roomId, userId, dates} \u2192 Return Booking ID"
                      },
                      {
                        "k": "PUT /v1/booking/bookingId",
                        "v": "Modify or cancel booking \u2192 Updated booking status"
                      },
                      {
                        "k": "GET /v1/bookings",
                        "v": "Get user bookings {header: userId} \u2192 List of bookings (past & future)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Users \u2192 LB + API Gateway: Authentication, authorization, rate limiting, round robin routing",
                  "User Service \u2192 User DB (PostgreSQL): User profiles, authentication, preferences",
                  "Search Service \u2192 Elasticsearch: Hotel search with geo queries, filters (price, rating, amenities)",
                  "Hotel DB (PostgreSQL): Hotel metadata, room types, pricing, images (S3/CDN references)",
                  "Review Service \u2192 Review DB: Hotel and room reviews with ratings",
                  "Booking Service \u2192 Booking DB + Redis Lock: Room availability check, booking creation with distributed locks",
                  "Room Availability Service: Checks availability if room is available, updates availability table once booking is done",
                  "Payment Service \u2192 Payment Gateway: Payment processing with acknowledgement, idempotency handling",
                  "Kafka: Event streaming for booking.created, booking.confirmed, booking.cancelled, payment.success events",
                  "Notification Service: Consumes Kafka events, sends email/SMS/push notifications to users"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: User Registration & Authentication",
                    "steps": [
                      "User sends: POST /v1/users/register with {name: 'John', email: 'john@email.com', password: 'pass123'}",
                      "User Service validates: Email uniqueness via SELECT * FROM users WHERE email = {email}",
                      "Service creates: User record in PostgreSQL {user_id: UUID, name, email, password_hash: bcrypt(password), created_at}",
                      "Service generates: JWT token with {user_id, email, exp: 7 days}, stores refresh token in Redis with TTL",
                      "Response: {user_id, token, refresh_token}",
                      "Login: POST /v1/users/login with {email, password} \u2192 validates bcrypt hash \u2192 returns JWT"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Hotel Search (Elasticsearch Geo + Filters)",
                    "steps": [
                      "User searches: GET /v1/hotels/search?location=London&checkIn=2025-02-01&checkOut=2025-02-05&guests=2&priceMax=200",
                      "Search Service geocodes: Location 'London' \u2192 (51.5074, -0.1278) using Google Geocoding API or internal cache",
                      "Alternative: If user provides lat/lng directly, skip geocoding",
                      "Service queries Elasticsearch: { 'query': { 'bool': { 'filter': [ { 'geo_distance': { 'distance': '20km', 'location': { 'lat': 51.5074, 'lon': -0.1278 } } }, { 'range': { 'price_per_night': { 'lte': 200 } } }, { 'term': { 'min_guests': { 'lte': 2 } } } ] } }, 'sort': [ { '_geo_distance': { 'location': {...}, 'order': 'asc' } } ], 'size': 50 }",
                      "Note: Elasticsearch query does NOT check date availability (too complex), returns hotels matching location + filters only",
                      "Service fetches: Partial hotel data {hotel_id, name, location, price_range, rating, image_url} from ES",
                      "CDC (Change Data Capture): Hotel DB \u2192 Kafka \u2192 Elasticsearch indexer keeps ES in sync",
                      "For availability: Client calls separate API GET /v1/hotels/{hotelId}/availability?checkIn=...&checkOut=... after selecting hotel",
                      "Caching: Popular location searches cached in Redis with TTL=10 min (key: hash(location, filters))"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Hotel Details & Room Availability",
                    "steps": [
                      "User selects hotel: GET /v1/hotels/{hotel_id} to view details",
                      "Service fetches: Full hotel details from Hotel DB {hotel_id, name, description, amenities, images[], address, geo_pos (lat, lng)}",
                      "Images: Stored in S3/Blob storage, URLs served via CDN for fast global access",
                      "User checks availability: GET /v1/hotels/{hotel_id}/rooms?checkIn=2025-02-01&checkOut=2025-02-05&guests=2",
                      "Room Availability Service queries: SELECT r.room_id, r.room_type, r.capacity, p.date, p.price FROM rooms r JOIN room_availability a ON r.room_id = a.room_id AND a.hotel_id = {hotel_id} JOIN price_calendar p ON r.room_id = p.room_id WHERE a.date BETWEEN '2025-02-01' AND '2025-02-05' AND a.status IN ('Available', 'Booked', 'Maintenance') GROUP BY r.room_id HAVING COUNT(CASE WHEN a.status = 'Available' THEN 1 END) = {num_nights}",
                      "Alternative approach (not overcomplicated for this): Use availability table with alternative Q tree or geohashing (but overcomplicated for this)",
                      "Service calculates: Total price = SUM(price_per_night) for date range, applies dynamic pricing if needed",
                      "Response: [{ room_id, room_type, capacity, total_price, available_count, amenities[], images[] }]"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Booking Creation with Distributed Lock (Critical)",
                    "steps": [
                      "User confirms: POST /v1/booking/roomId with {room_id: 'room_123', user_id, check_in: '2025-02-01', check_out: '2025-02-05', guests: 2}",
                      "Booking Service FIRST acquires Redis distributed lock: SETNX lock:hotel:{hotel_id}:room:{room_id}:{check_in}:{check_out} {booking_attempt_id} EX 30 (30 second expiry)",
                      "Critical: Lock key includes hotel_id, room_id, and FULL date range to prevent concurrent bookings",
                      "If SETNX returns 0 (lock already held): Return error 'Room currently being booked, please try again' (HTTP 409 Conflict)",
                      "If SETNX returns 1 (lock acquired): Proceed with availability check",
                      "Double-check availability: SELECT status FROM room_availability WHERE room_id = {room_id} AND hotel_id = {hotel_id} AND date BETWEEN {check_in} AND {check_out} FOR UPDATE (pessimistic lock)",
                      "If ANY date shows status != 'Available': Release lock (DEL lock:...), return 'Room not available'",
                      "If all dates available: BEGIN TRANSACTION; (1) INSERT INTO bookings (booking_id, user_id, hotel_id, room_id, check_in, check_out, status='PENDING_PAYMENT', amount, created_at), (2) UPDATE room_availability SET status='Booked', booking_id={booking_id} WHERE room_id={room_id} AND date BETWEEN {check_in} AND {check_out}; COMMIT;",
                      "Release lock: DEL lock:hotel:{hotel_id}:room:{room_id}:{check_in}:{check_out}",
                      "Publish event: Kafka 'booking.created' topic with {booking_id, user_id, hotel_id, room_id, dates, amount}",
                      "Invoke Payment Service: Return {booking_id, payment_url, expires_at: now() + 15min}",
                      "Lock timeout handling: If service crashes while holding lock, Redis auto-expires lock after 30s (EX 30), preventing deadlock"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Payment Processing with Idempotency",
                    "steps": [
                      "User redirected to: Payment Gateway (Stripe/Razorpay) with payment_url",
                      "Payment Service creates: Payment intent with idempotency_key = booking_id (ensures same booking can't be charged twice)",
                      "User completes payment: Payment Gateway processes card, sends webhook POST /webhooks/payment with {booking_id, payment_id, status: 'success', amount}",
                      "Payment Service webhook handler: (1) Validates webhook signature (security), (2) Checks idempotency: SELECT * FROM payments WHERE booking_id = {booking_id}, if exists return 200 (already processed), (3) BEGIN TRANSACTION; INSERT INTO payments (payment_id, booking_id, amount, status='SUCCESS', gateway_response, created_at); UPDATE bookings SET status='CONFIRMED', payment_id={payment_id} WHERE booking_id={booking_id}; COMMIT;",
                      "Publish event: Kafka 'payment.success' and 'booking.confirmed' topics",
                      "Set booking expiry in Redis: SETEX booking:{booking_id}:expires 900 '1' (15 min TTL), if payment not received, background job cancels booking and releases room",
                      "Notification: Notification Service consumes Kafka event, sends confirmation email/SMS to user with booking details and QR code"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Booking Cancellation & Modification",
                    "steps": [
                      "User cancels: PUT /v1/booking/{booking_id} with {action: 'cancel', reason: 'user_requested'}",
                      "Booking Service validates: (1) Fetch booking, check user_id matches, (2) Check cancellation policy (free cancel if >24h before check_in, else charge fee)",
                      "Service updates: BEGIN TRANSACTION; UPDATE bookings SET status='CANCELLED', cancelled_at=now() WHERE booking_id={booking_id}; UPDATE room_availability SET status='Available', booking_id=NULL WHERE booking_id={booking_id}; COMMIT;",
                      "Refund: If applicable, call Payment Service \u2192 gateway.refund(payment_id, amount), status='REFUND_PROCESSING'",
                      "Publish event: Kafka 'booking.cancelled' topic",
                      "Notification: Email/SMS confirmation of cancellation and refund timeline (5-7 business days)",
                      "Modification: User can modify dates if new dates available \u2192 similar flow: check availability with lock, update booking and room_availability tables"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: View Bookings (Past & Future)",
                    "steps": [
                      "User requests: GET /v1/bookings with header {Authorization: Bearer {token}}",
                      "Booking Service decodes: JWT token to extract user_id",
                      "Service queries: SELECT b.booking_id, b.hotel_id, h.name as hotel_name, h.image_url, b.room_id, r.room_type, b.check_in, b.check_out, b.status, b.amount FROM bookings b JOIN hotels h ON b.hotel_id = h.hotel_id JOIN rooms r ON b.room_id = r.room_id WHERE b.user_id = {user_id} ORDER BY b.created_at DESC LIMIT 50",
                      "Categorization: Client-side or service filters: future bookings (check_in > today), past bookings (check_out < today)",
                      "Caching: User booking list cached in Redis with key user:{user_id}:bookings, TTL=5 min, invalidated on new booking/cancellation",
                      "Response: { bookings: [{ booking_id, hotel: {...}, room: {...}, dates: {...}, status, amount, can_cancel: boolean }] }"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Reviews & Ratings",
                    "steps": [
                      "User submits review: POST /v1/reviews with {hotel_id, booking_id, rating: 4.5, comment: 'Great stay!', images[]}",
                      "Review Service validates: (1) Check booking_id exists and belongs to user, (2) Check booking is completed (check_out < today), (3) Check user hasn't already reviewed this booking",
                      "Service creates: Review record in Review DB {review_id, user_id, hotel_id, booking_id, rating, comment, images[], created_at, helpful_count: 0}",
                      "Aggregation: Background job updates hotel avg_rating: UPDATE hotels SET avg_rating = (SELECT AVG(rating) FROM reviews WHERE hotel_id = {hotel_id}), review_count = (SELECT COUNT(*) FROM reviews WHERE hotel_id = {hotel_id}) WHERE hotel_id = {hotel_id}",
                      "Elasticsearch sync: CDC pipeline pushes updated hotel rating to Elasticsearch for search ranking",
                      "Images: Upload to S3, store URLs in review record",
                      "Moderation: ML service scans review for inappropriate content, flags for manual review if needed"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Dynamic Pricing & Availability Updates",
                    "steps": [
                      "Price Calendar: Hotels can set different prices per date in price_calendar table {hotel_id, room_id, date, price, currency}",
                      "Dynamic pricing: ML model adjusts prices based on demand, season, events (e.g., conference in city \u2192 +30% price)",
                      "Bulk availability updates: Hotel admin updates multiple dates via POST /v1/hotels/{hotel_id}/availability with {room_id, dates[], status: 'Available'/'Maintenance'}",
                      "Service updates: Batch UPDATE room_availability SET status={status} WHERE room_id={room_id} AND date IN ({dates}) AND status != 'Booked' (don't override existing bookings)",
                      "Real-time sync: Changes published to Kafka, Search Service updates Elasticsearch cache within 1-2 seconds",
                      "Overbooking prevention: System maintains max_rooms per room_type, validation: SELECT COUNT(*) FROM room_availability WHERE room_id IN (SELECT room_id FROM rooms WHERE room_type={type}) AND date={date} AND status='Booked' must be < max_rooms"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Notification Flow (Kafka Event-Driven)",
                    "steps": [
                      "Event sources: Booking Service, Payment Service publish to Kafka topics: 'booking.created', 'booking.confirmed', 'booking.cancelled', 'payment.success', 'payment.failed'",
                      "Notification Service: Consumes all topics with separate consumer groups for reliability",
                      "Event: 'booking.confirmed' \u2192 (1) Fetch user email/phone from User DB, (2) Fetch booking details from Booking DB, (3) Generate email template with QR code (booking_id encoded), hotel details, check-in instructions, (4) Send via SendGrid/SES (email) and Twilio (SMS), (5) Store notification log in Notification DB",
                      "Push notifications: For mobile app users, send via FCM (Android) / APNS (iOS) with deep link to booking details",
                      "Reminder notifications: Scheduled jobs (1 day before check-in) send reminder emails/SMS with booking details",
                      "Kafka offset management: Consumer commits offset only after successful notification send, ensures exactly-once delivery on retry"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Users (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE, INDEXED"
                      },
                      {
                        "k": "password",
                        "v": "varchar(255) (bcrypt hash)"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb (preferences, saved hotels, etc.)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Hotels (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "hotel_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "address",
                        "v": "text"
                      },
                      {
                        "k": "geo_pos",
                        "v": "geography(Point, 4326) - latitude + longitude"
                      },
                      {
                        "k": "rating",
                        "v": "decimal(2,1) (avg rating from reviews)"
                      },
                      {
                        "k": "amenities",
                        "v": "jsonb array ['WiFi', 'Pool', 'Parking']"
                      },
                      {
                        "k": "images",
                        "v": "jsonb array of S3/CDN URLs"
                      },
                      {
                        "k": "description",
                        "v": "text"
                      },
                      {
                        "k": "Index",
                        "v": "GIST index on geo_pos for spatial queries"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Rooms (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "room_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "hotel_id",
                        "v": "uuid FK \u2192 Hotels, INDEXED"
                      },
                      {
                        "k": "room_type",
                        "v": "varchar(100) (Deluxe, Suite, Standard)"
                      },
                      {
                        "k": "capacity",
                        "v": "integer (max guests)"
                      },
                      {
                        "k": "amenities",
                        "v": "jsonb array"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb (bed type, view, etc.)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Price_Calendar (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "hotel_id",
                        "v": "uuid"
                      },
                      {
                        "k": "room_id",
                        "v": "uuid"
                      },
                      {
                        "k": "date",
                        "v": "date"
                      },
                      {
                        "k": "price",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "currency",
                        "v": "varchar(3) (USD, EUR, INR)"
                      },
                      {
                        "k": "Composite PK",
                        "v": "(hotel_id, room_id, date)"
                      },
                      {
                        "k": "Index",
                        "v": "INDEX on (hotel_id, date) for range queries"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Room_Availability (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "hotel_id",
                        "v": "uuid"
                      },
                      {
                        "k": "room_id",
                        "v": "uuid"
                      },
                      {
                        "k": "date",
                        "v": "date"
                      },
                      {
                        "k": "status",
                        "v": "enum (Available, Booked, Maintenance)"
                      },
                      {
                        "k": "booking_id",
                        "v": "uuid (nullable, set when status=Booked)"
                      },
                      {
                        "k": "Composite PK",
                        "v": "(hotel_id, room_id, date)"
                      },
                      {
                        "k": "Index",
                        "v": "INDEX on (room_id, date, status) for availability queries"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Bookings (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "booking_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "hotel_id",
                        "v": "uuid FK \u2192 Hotels"
                      },
                      {
                        "k": "room_id",
                        "v": "uuid FK \u2192 Rooms"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users, INDEXED"
                      },
                      {
                        "k": "check_in",
                        "v": "date"
                      },
                      {
                        "k": "check_out",
                        "v": "date"
                      },
                      {
                        "k": "start_date",
                        "v": "timestamp (check-in date)"
                      },
                      {
                        "k": "end_date",
                        "v": "timestamp (check-out date)"
                      },
                      {
                        "k": "amount",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "currency",
                        "v": "varchar(3)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING_PAYMENT, CONFIRMED, CANCELLED, COMPLETED)"
                      },
                      {
                        "k": "payment_id",
                        "v": "varchar(255) (from payment gateway)"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb (guest details, special requests, etc.)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reviews (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "review_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "hotel_id",
                        "v": "uuid FK \u2192 Hotels, INDEXED"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "booking_id",
                        "v": "uuid FK \u2192 Bookings, UNIQUE (one review per booking)"
                      },
                      {
                        "k": "rating",
                        "v": "decimal(2,1) (1.0 to 5.0)"
                      },
                      {
                        "k": "comment",
                        "v": "text"
                      },
                      {
                        "k": "images",
                        "v": "jsonb array of S3 URLs"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Payment (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "payment_id",
                        "v": "varchar(255) PRIMARY KEY (from gateway)"
                      },
                      {
                        "k": "booking_id",
                        "v": "uuid FK \u2192 Bookings, UNIQUE (idempotency)"
                      },
                      {
                        "k": "amount",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "currency",
                        "v": "varchar(3)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, SUCCESS, FAILED, REFUNDED)"
                      },
                      {
                        "k": "gateway_response",
                        "v": "jsonb (full webhook payload)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch - Hotel Search Index",
                    "pairs": [
                      {
                        "k": "hotel_id",
                        "v": "keyword"
                      },
                      {
                        "k": "name",
                        "v": "text (searchable)"
                      },
                      {
                        "k": "location",
                        "v": "geo_point (lat, lng)"
                      },
                      {
                        "k": "amenities",
                        "v": "keyword array"
                      },
                      {
                        "k": "rating",
                        "v": "float"
                      },
                      {
                        "k": "price_range",
                        "v": "object {min, max}"
                      },
                      {
                        "k": "start_date",
                        "v": "date (for availability - optional)"
                      },
                      {
                        "k": "end_date",
                        "v": "date"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Locks & Cache",
                    "pairs": [
                      {
                        "k": "lock:hotel:{hotelId}:room:{roomId}:{checkIn}:{checkOut}",
                        "v": "STRING {booking_attempt_id} EX 30 (distributed lock for booking, 15min TTL)"
                      },
                      {
                        "k": "search:{hash(location,filters)}",
                        "v": "STRING (JSON) - cached search results, TTL 10min"
                      },
                      {
                        "k": "user:{userId}:bookings",
                        "v": "STRING (JSON) - cached user bookings, TTL 5min"
                      },
                      {
                        "k": "booking:{bookingId}:expires",
                        "v": "STRING '1' EX 900 (15min payment timeout)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Blob Storage (S3 / Azure Blob)",
                    "pairs": [
                      {
                        "k": "Hotel Images",
                        "v": "S3 bucket: hotels/{hotel_id}/{image_id}.jpg, served via CDN"
                      },
                      {
                        "k": "Review Images",
                        "v": "S3 bucket: reviews/{review_id}/{image_id}.jpg"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "8. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Elasticsearch Geo Sharding - Shard hotel index by region (US, EU, Asia), parallel queries",
                  "Technique 2: Redis Distributed Locks - SETNX with expiry prevents double booking race conditions, auto-release on timeout",
                  "Technique 3: Database Read Replicas - Route read queries (search, view bookings) to replicas, writes to primary",
                  "Technique 4: CDN for Images - Hotel/room images served from edge locations, 95% cache hit rate, <50ms latency",
                  "Technique 5: Kafka Event Streaming - Async notifications, booking events, payment confirmations, decouples services",
                  "Technique 6: Caching Layer - Redis caches search results (TTL=10min), user bookings (TTL=5min), hotel details (TTL=30min)",
                  "Technique 7: Connection Pooling - API servers maintain 100-200 DB connections, prevents exhaustion at scale",
                  "Technique 8: API Rate Limiting - 100 req/min per user with token bucket, protects against abuse",
                  "Technique 9: Database Partitioning - Partition bookings table by date (monthly partitions), improves query performance",
                  "Technique 10: Lazy Loading - Load hotel images on-demand, paginate search results (50 per page), reduces payload",
                  "Technique 11: Geo-Based Routing - Route users to nearest data center (US-East, EU-West, Asia), reduces latency",
                  "Technique 12: CDC Pipeline - PostgreSQL \u2192 Debezium \u2192 Kafka \u2192 Elasticsearch, real-time search index updates"
                ]
              },
              {
                "id": "interview-qa",
                "title": "9. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you prevent double booking (two users booking same room for overlapping dates)?",
                    "a": "Multi-layer approach with distributed locks: (1) Redis distributed lock BEFORE availability check: SETNX lock:hotel:{hotel_id}:room:{room_id}:{check_in}:{check_out} {booking_attempt_id} EX 30, lock key includes FULL date range, (2) If SETNX returns 0 (lock exists), return 409 Conflict 'Room being booked, try again', (3) If SETNX returns 1 (lock acquired), proceed to database, (4) Double-check availability with pessimistic database lock: SELECT status FROM room_availability WHERE room_id={room_id} AND date BETWEEN {check_in} AND {check_out} FOR UPDATE (row-level lock), (5) If ANY date shows status != 'Available', release Redis lock, return 'Not available', (6) If all available: BEGIN TRANSACTION; INSERT booking; UPDATE room_availability SET status='Booked', booking_id={id} WHERE room_id={room_id} AND date BETWEEN {dates}; COMMIT;, (7) Release Redis lock: DEL lock:..., (8) Lock auto-expires after 30s if service crashes, prevents deadlock. Why both locks?: Redis is fast (1-2ms) for early rejection, PostgreSQL FOR UPDATE ensures ACID guarantees within transaction. Example race: User A and B both try booking room_123 for Feb 1-5 \u2192 User A's SETNX succeeds \u2192 User B's SETNX fails immediately \u2192 User B sees 'being booked' \u2192 User A completes booking \u2192 User B can retry and see 'not available' \u2192 no double booking."
                  },
                  {
                    "q": "How do you implement hotel search with location, dates, and price filters efficiently?",
                    "a": "Elasticsearch with geo_distance + compound filters: (1) User searches: location='Paris', checkIn='2025-03-01', checkOut='2025-03-05', priceMax=200, guests=2, (2) Geocode location to (48.8566, 2.3522) using Google Geocoding API or cached mappings, (3) Query Elasticsearch: POST /hotels/_search { 'query': { 'bool': { 'filter': [ { 'geo_distance': { 'distance': '20km', 'location': { 'lat': 48.8566, 'lon': 2.3522 } } }, { 'range': { 'price_range.min': { 'lte': 200 } } }, { 'terms': { 'amenities': ['WiFi'] } } ] } }, 'sort': [ { '_geo_distance': { 'location': {...}, 'order': 'asc' } } ], 'size': 50 }, (4) NOTE: Elasticsearch returns hotels matching location + static filters only, does NOT check date availability (too complex for search index), (5) For availability: Separate API call GET /v1/hotels/{hotel_id}/availability?dates=... after user selects hotel, queries room_availability table, (6) Alternative overcomplicated: Store availability bitmap in ES (not recommended - high write volume, inconsistency risk). Why this approach?: (a) ES is optimized for text + geo search, not dynamic availability, (b) Availability changes frequently (every booking), reindexing ES on every booking = high latency + cost, (c) Better UX: Show all hotels nearby, then check availability on-demand. Caching: Cache popular searches in Redis with TTL=10min, invalidate on hotel metadata changes only. CDC: PostgreSQL hotel updates \u2192 Kafka \u2192 ES indexer keeps search index fresh within 1-2s."
                  },
                  {
                    "q": "How do you ensure payment idempotency (prevent double charging if user clicks pay twice)?",
                    "a": "Idempotency at multiple layers: (1) Client generates idempotency_key = booking_id before payment, (2) Payment Service: POST /payments with {booking_id, amount, idempotency_key: {booking_id}}, (3) Payment Gateway (Stripe/Razorpay) uses same idempotency_key for createPaymentIntent API, (4) Gateway guarantees: Same idempotency_key within 24h = return same payment_intent_id, no duplicate charge, (5) Database constraint: UNIQUE INDEX on payments(booking_id) ensures single payment record per booking, (6) Webhook handling: POST /webhooks/payment with {booking_id, payment_id, status: 'success'}, (a) Check: SELECT * FROM payments WHERE booking_id = {booking_id}, if exists return 200 OK (already processed), (b) If not: BEGIN TRANSACTION; INSERT INTO payments (payment_id, booking_id, amount, status='SUCCESS'); UPDATE bookings SET status='CONFIRMED', payment_id={payment_id}; COMMIT;, (c) Database constraint prevents race condition: Two concurrent webhooks \u2192 first INSERT succeeds, second fails on UNIQUE constraint \u2192 handled gracefully. (7) Kafka event published only once due to database constraint. Example: User double-clicks 'Pay' \u2192 Request 1 creates payment_intent_123 \u2192 Request 2 with same idempotency_key \u2192 Gateway returns existing payment_intent_123 \u2192 user charged once. Alternative: Use distributed lock SETNX lock:payment:{booking_id} before payment, but gateway idempotency + DB constraint simpler and more reliable."
                  },
                  {
                    "q": "How do you handle room availability updates and booking modifications?",
                    "a": "Availability updates flow: (1) Hotel admin updates availability: PUT /v1/hotels/{hotel_id}/rooms/{room_id}/availability with {dates: ['2025-03-01', '2025-03-02'], status: 'Maintenance'}, (2) Service validates: Cannot change status if date already booked (status='Booked'), (3) Bulk update: UPDATE room_availability SET status={status} WHERE room_id={room_id} AND date IN ({dates}) AND status != 'Booked', (4) Publish Kafka event: 'availability.updated' for cache invalidation, (5) Cache invalidation: Clear Redis cached availability for affected hotel/room. Booking modification (date change): (1) User modifies: PUT /v1/bookings/{booking_id} with {new_check_in, new_check_out}, (2) Service validates: (a) Check cancellation policy (some bookings non-refundable), (b) Check new dates available, (3) Acquire distributed lock: SETNX lock:hotel:{hotel_id}:room:{room_id}:{new_check_in}:{new_check_out}, (4) Check new availability: SELECT status FROM room_availability WHERE room_id={room_id} AND date BETWEEN {new_dates} FOR UPDATE, (5) If available: BEGIN TRANSACTION; (a) Release old dates: UPDATE room_availability SET status='Available', booking_id=NULL WHERE booking_id={booking_id}, (b) Book new dates: UPDATE room_availability SET status='Booked', booking_id={booking_id} WHERE room_id={room_id} AND date BETWEEN {new_dates}, (c) Update booking: UPDATE bookings SET check_in={new_check_in}, check_out={new_check_out}, amount={new_amount} WHERE booking_id={booking_id}; COMMIT;, (6) Release lock, (7) Publish 'booking.modified' event. Price adjustment: If new dates have different pricing, calculate difference and either charge extra or refund. Edge cases: (1) New dates more expensive: Charge difference via payment gateway, (2) New dates cheaper: Issue partial refund, (3) No availability: Return error with alternative dates suggestions."
                  },
                  {
                    "q": "How do you implement the review and rating system with aggregation?",
                    "a": "Review submission and aggregation: (1) User submits review: POST /v1/reviews with {hotel_id, booking_id, rating: 4.5, comment: 'Great location!', images: [file1, file2]}, (2) Validation: (a) Check booking exists and belongs to user: SELECT * FROM bookings WHERE booking_id={booking_id} AND user_id={user_id}, (b) Check booking completed (check_out < today), can't review before stay, (c) Check no existing review: SELECT * FROM reviews WHERE booking_id={booking_id}, only one review per booking, (3) Upload images to S3: Multi-part upload \u2192 s3://reviews/{review_id}/{image_id}.jpg, get CDN URLs, (4) Insert review: INSERT INTO reviews (review_id, hotel_id, user_id, booking_id, rating, comment, images[], created_at, helpful_count: 0), (5) Background aggregation job (async): (a) Calculate new avg: UPDATE hotels SET avg_rating = (SELECT AVG(rating) FROM reviews WHERE hotel_id={hotel_id}), review_count = (SELECT COUNT(*) FROM reviews WHERE hotel_id={hotel_id}) WHERE hotel_id={hotel_id}, (b) Can use materialized view for better performance: CREATE MATERIALIZED VIEW hotel_ratings AS SELECT hotel_id, AVG(rating), COUNT(*) FROM reviews GROUP BY hotel_id, refresh every 10 min, (6) CDC pipeline: hotel.avg_rating update \u2192 Kafka \u2192 Elasticsearch indexer updates search index, (7) Moderation: ML service (AWS Comprehend / Cloud NLP) scans review text for: profanity, spam, fake reviews, flags for manual review if confidence >0.8, (8) Helpful votes: Users can vote reviews helpful: POST /v1/reviews/{review_id}/helpful, increments helpful_count for ranking. Display: GET /v1/hotels/{hotel_id}/reviews?sortBy=helpful&page=1&size=20, sorted by helpful_count DESC or created_at DESC. Alternative aggregation: Use Apache Flink for real-time rating updates, but materialized view + periodic refresh simpler and sufficient."
                  },
                  {
                    "q": "How do you handle time zones and date calculations for global hotels?",
                    "a": "Time zone handling strategy: (1) Database storage: ALL timestamps stored in UTC in PostgreSQL (timestamp without timezone type), (2) Hotels table: Add timezone column: hotel_timezone varchar(50) (e.g., 'America/New_York', 'Europe/London') using IANA tz database, (3) Check-in/check-out dates: Stored as DATE type (not timestamp), represents local date at hotel location, (4) Business logic: When user searches, dates are in hotel's local timezone: User searches 'Paris hotels for March 1-5' \u2192 interpreted as Paris local dates, not user's timezone, (5) Display to user: Backend returns dates + hotel_timezone: {check_in: '2025-03-01', check_out: '2025-03-05', timezone: 'Europe/Paris'}, client displays in local terms: 'Checking in March 1, 2025 (Paris time)', (6) Availability calculation: room_availability.date is in hotel local date, no timezone conversion needed for availability checks, (7) Payment deadlines: Stored as UTC timestamp: booking.payment_deadline = booking.created_at + 15 min (UTC), displayed to user in their local timezone, (8) Notifications: Email/SMS include explicit timezone: 'Check-in: March 1, 2025 at 3:00 PM CET', (9) Edge case - DST: Use proper timezone library (moment-timezone / date-fns-tz) not just UTC offset, handles daylight saving automatically. Example: User in California (UTC-8) books London hotel (UTC+0) for March 1-5 \u2192 Backend stores: {check_in: DATE '2025-03-01', check_out: DATE '2025-03-05', hotel_timezone: 'Europe/London', created_at: TIMESTAMP '2025-01-15 20:30:00 UTC'} \u2192 User sees: 'Booking confirmed for March 1-5, 2025 (London time), made on Jan 15, 2025 12:30 PM PST'. Alternative: Store all dates with timezone as TIMESTAMPTZ, but DATE simpler for booking dates as time-of-day not relevant for check-in dates."
                  },
                  {
                    "q": "How do you implement cancellation policies and refund processing?",
                    "a": "Multi-tier cancellation policy: (1) Policy rules stored per hotel/room: cancellation_policy table {hotel_id, room_id, policy_type: 'flexible'/'moderate'/'strict', rules: jsonb}, (2) Flexible: { free_cancellation_hours: 24, partial_refund_percent: 50 until 12h, no_refund_hours: 0 }, Moderate: { 72h free, 24h 50%, <24h no refund }, Strict: { 7 days free, <7 days no refund }, (3) Booking creation: Copy policy to bookings.cancellation_policy jsonb (snapshot at booking time, protects user if policy changes later), (4) User cancels: PUT /v1/bookings/{booking_id}/cancel with {reason: 'user_requested'}, (5) Calculate refund: (a) Fetch booking.cancellation_policy and booking.check_in, (b) Calculate hours_until_checkin = (check_in - now()) in hours, (c) Apply policy rules: if hours_until_checkin >= 24: refund_amount = booking.amount (100%), elif hours_until_checkin >= 12: refund_amount = booking.amount * 0.5 (50%), else: refund_amount = 0, (6) Process cancellation: BEGIN TRANSACTION; (a) UPDATE bookings SET status='CANCELLED', cancelled_at=now(), refund_amount={refund_amount}, refund_status='PROCESSING', (b) UPDATE room_availability SET status='Available', booking_id=NULL WHERE booking_id={booking_id}, (c) If refund_amount > 0: INSERT INTO refunds (refund_id, booking_id, amount, status='PENDING'); COMMIT;, (7) Async refund: Background job calls Payment Gateway: stripe.refunds.create({payment_intent_id, amount}), (8) Webhook confirms: POST /webhooks/refund with {refund_id, status: 'succeeded'} \u2192 UPDATE refunds SET status='COMPLETED', bookings.refund_status='REFUNDED', (9) Notification: Email user 'Booking cancelled, refund of ${amount} will appear in 5-7 business days', (10) Publish Kafka 'booking.cancelled' event. Partial refunds: Some policies allow partial cancellation (e.g., 3-night booking, cancel 1 night) \u2192 more complex, need to track per-night cancellations. Abuse prevention: Track user cancellation_rate = cancelled_bookings / total_bookings, if >30% in last 10 bookings, flag account for review or increase cancellation fee. Example: User books on Jan 1 for March 1 check-in (flexible policy) \u2192 Cancels Feb 28 (1 day before) \u2192 24 hours remaining \u2192 100% refund \u2192 refund processed \u2192 room becomes available."
                  },
                  {
                    "q": "How do you scale the search service to handle 100M searches per day?",
                    "a": "Multi-tier scaling strategy: (1) Elasticsearch cluster: 10 data nodes, 3 master nodes, shard hotel index by region (US, EU, Asia), each region has 5 primary shards + 1 replica, (2) Geo-based routing: API Gateway routes search requests to nearest ES cluster: User in US \u2192 us-east-1 cluster, EU \u2192 eu-west-1 cluster, reduces latency from 500ms to 50ms, (3) Caching layer: Redis caches popular searches: Key: hash(location, checkIn, checkOut, filters) \u2192 Value: JSON search results, TTL: 10 min, Cache hit rate: ~60-70% (many users search same popular destinations), Cache size: 100GB Redis cluster (10 nodes) stores ~10M cached searches, (4) Query optimization: (a) Use filter context (not query context) for filters: { 'bool': { 'filter': [...] } } \u2192 faster, cacheable, (b) Disable scoring for geo queries: 'track_scores': false, (c) Fetch only required fields: '_source': ['hotel_id', 'name', 'location', 'rating'], (5) API rate limiting: Per-user: 100 searches/min, Per-IP: 1000 searches/min (protects against scraping bots), (6) Database read replicas: PostgreSQL primary + 5 read replicas in each region, search service queries replicas for hotel metadata after ES returns IDs, (7) CDN for images: Hotel images served from CloudFront edge locations, <50ms latency globally, 95% cache hit rate, (8) Load balancing: 100 search service instances behind ALB, auto-scaling based on CPU >70%, (9) Async indexing: Hotel updates \u2192 Kafka \u2192 ES indexer consumer (separate service), doesn't block write operations, eventual consistency (1-2s delay acceptable for search), (10) Connection pooling: Each search service instance maintains 50 ES connections (HTTP keep-alive), (11) Monitoring: Prometheus + Grafana track: search latency p50/p95/p99, cache hit rate, ES cluster health, alert if p95 >500ms. Load: 100M searches/day = 1,157 searches/sec average, peak 3x = 3,500 searches/sec, Scaling math: 100 instances \u00d7 50 req/sec capacity = 5,000 req/sec headroom. Cost: ES cluster $5K/month, Redis $1K/month, API servers $3K/month = $9K/month for search infrastructure."
                  },
                  {
                    "q": "How do you handle overbooking scenarios and inventory management?",
                    "a": "Overbooking prevention with inventory tracking: (1) Room inventory model: Hotels have multiple rooms of same type (e.g., 20 Deluxe rooms), rooms table: {hotel_id, room_type, total_count: 20}, individual_rooms table: {room_id, hotel_id, room_type, room_number: '101'}, (2) Availability tracking: Two approaches: (a) Aggregate: room_availability_aggregate {hotel_id, room_type, date, available_count, booked_count}, faster queries but complex updates, (b) Individual: room_availability {room_id, date, status, booking_id}, simpler but more rows. (3) Booking validation: (a) User books 'Deluxe room' (not specific room_id initially), (b) Query: SELECT COUNT(*) as booked FROM room_availability WHERE hotel_id={hotel_id} AND room_type='Deluxe' AND date BETWEEN {dates} AND status='Booked', (c) Check: booked_count < total_deluxe_rooms for ALL dates in range, (d) Acquire distributed lock: SETNX lock:hotel:{hotel_id}:room_type:Deluxe:{dates}, (e) Assign specific room: SELECT room_id FROM rooms WHERE hotel_id={hotel_id} AND room_type='Deluxe' AND room_id NOT IN (SELECT room_id FROM room_availability WHERE date BETWEEN {dates} AND status='Booked') LIMIT 1 FOR UPDATE, (f) Update: UPDATE room_availability SET status='Booked', booking_id={booking_id} WHERE room_id={assigned_room_id} AND date BETWEEN {dates}. (4) Controlled overbooking: Some hotels deliberately overbook by 5-10% (like airlines) to account for cancellations, overselling_limit table: {hotel_id, room_type, max_overbook_percent: 0.05}, validation: allow booking if booked_count <= total_count * 1.05, risk mitigation: if actually overbooked on check-in day, hotel upgrades guest to better room or relocates to partner hotel (hotel's responsibility). (5) Maintenance blocking: Hotel can block rooms: room_availability.status = 'Maintenance' \u2192 excluded from available_count. (6) Edge case - race condition: Two bookings at exact same time for last available room \u2192 distributed lock + database FOR UPDATE ensures only one succeeds. Example: Hotel has 20 Deluxe rooms, March 1: 18 booked, 2 available \u2192 User A and B both try booking \u2192 User A acquires lock \u2192 checks availability (2 available) \u2192 assigns room_123 \u2192 updates to 19 booked \u2192 releases lock \u2192 User B acquires lock \u2192 checks availability (1 available) \u2192 assigns room_124 \u2192 updates to 20 booked \u2192 releases lock \u2192 Third user tries \u2192 no rooms available."
                  },
                  {
                    "q": "What's your strategy for disaster recovery and data consistency?",
                    "a": "Multi-region DR with RPO/RTO targets: (1) Primary region: us-east-1, Secondary: us-west-2, Tertiary: eu-west-1, (2) Database replication: PostgreSQL primary in us-east-1, streaming replication to us-west-2 (warm standby) and eu-west-1 (read replica), Replication lag: <5 seconds under normal load, (3) RPO (Recovery Point Objective): 5 seconds (maximum data loss acceptable), achieved via synchronous replication to secondary, (4) RTO (Recovery Time Objective): 5 minutes (time to restore service), automated failover via Route53 health checks, (5) Elasticsearch: Multi-cluster setup, each region has independent ES cluster, synced via CDC pipeline (PostgreSQL \u2192 Kafka \u2192 ES in all regions), eventual consistency acceptable for search, (6) Redis: Redis Sentinel for automatic failover (master-slave replication), persistence via AOF (Append-Only File) for data durability, (7) S3 cross-region replication: Hotel images replicated to backup bucket in secondary region automatically, (8) Kafka: Multi-region setup with MirrorMaker replicates events to secondary Kafka cluster, (9) Failover procedure: (a) Route53 health check detects primary region down (failed health checks for 30s), (b) Automatically updates DNS to point to secondary region, (c) Secondary PostgreSQL promoted to primary (pg_ctl promote), (d) API Gateway in secondary region starts accepting traffic, (e) Total downtime: ~2-5 minutes for DNS propagation, (10) Booking consistency during failover: In-flight bookings during failover may fail \u2192 retry mechanism + idempotency ensures no double booking, Users see temporary error \u2192 retry automatically succeeds in new region, (11) Testing: Monthly DR drills, simulate primary region failure, verify RTO <5 min, identify gaps, (12) Backups: Daily full backup + hourly incremental backups to S3 Glacier, retention: 30 days, quarterly disaster recovery from backup test. Example: Primary region (us-east-1) suffers outage at 10:00 AM \u2192 Route53 detects failure at 10:00:30 \u2192 Initiates failover \u2192 us-west-2 promoted to primary at 10:02 \u2192 DNS updated at 10:03 \u2192 Users routed to new region at 10:05 \u2192 Service restored, data loss <5 sec (last replicated at 09:59:55). Cost: Multi-region setup adds ~40% infrastructure cost but provides business continuity."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "10. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Volume",
                    "pairs": [
                      {
                        "k": "Total Users",
                        "v": "50M registered users globally"
                      },
                      {
                        "k": "Hotels",
                        "v": "1M hotels, 10M+ rooms"
                      },
                      {
                        "k": "Daily Searches",
                        "v": "100M searches/day = 1,157 searches/sec average, 3x peak"
                      },
                      {
                        "k": "Daily Bookings",
                        "v": "1M bookings/day = 11.5 bookings/sec average"
                      },
                      {
                        "k": "Conversion Rate",
                        "v": "1% (100M searches \u2192 1M bookings)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Latency Requirements",
                    "pairs": [
                      {
                        "k": "Hotel Search",
                        "v": "< 500ms (Elasticsearch geo query + filters)"
                      },
                      {
                        "k": "Availability Check",
                        "v": "< 300ms (PostgreSQL query on room_availability)"
                      },
                      {
                        "k": "Booking Creation",
                        "v": "< 2s (includes lock acquisition, DB transaction, payment initiation)"
                      },
                      {
                        "k": "Redis Lock Acquisition",
                        "v": "< 10ms (SETNX operation)"
                      },
                      {
                        "k": "Payment Processing",
                        "v": "< 5s (payment gateway external call)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Consistency & Locks",
                    "pairs": [
                      {
                        "k": "Redis Lock TTL",
                        "v": "30 seconds (prevents deadlock if service crashes)"
                      },
                      {
                        "k": "Payment Timeout",
                        "v": "15 minutes (booking expires if payment not completed)"
                      },
                      {
                        "k": "Idempotency Key Validity",
                        "v": "24 hours (payment gateway)"
                      },
                      {
                        "k": "Database Replication Lag",
                        "v": "< 5 seconds (PostgreSQL streaming replication)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching",
                    "pairs": [
                      {
                        "k": "Search Cache TTL",
                        "v": "10 minutes (Redis cached search results)"
                      },
                      {
                        "k": "User Bookings Cache TTL",
                        "v": "5 minutes (invalidated on new booking)"
                      },
                      {
                        "k": "Hotel Details Cache TTL",
                        "v": "30 minutes"
                      },
                      {
                        "k": "Cache Hit Rate",
                        "v": "60-70% for search queries, 95% for images (CDN)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Business Metrics",
                    "pairs": [
                      {
                        "k": "Average Booking Value",
                        "v": "$150-300 per night"
                      },
                      {
                        "k": "Average Stay Duration",
                        "v": "2-3 nights"
                      },
                      {
                        "k": "Cancellation Rate",
                        "v": "15-20% of bookings"
                      },
                      {
                        "k": "Platform Commission",
                        "v": "15-25% of booking value (paid by hotel)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Disaster Recovery",
                    "pairs": [
                      {
                        "k": "RPO (Recovery Point)",
                        "v": "< 5 seconds (data loss acceptable)"
                      },
                      {
                        "k": "RTO (Recovery Time)",
                        "v": "< 5 minutes (time to restore service)"
                      },
                      {
                        "k": "Backup Frequency",
                        "v": "Daily full + hourly incremental"
                      },
                      {
                        "k": "Backup Retention",
                        "v": "30 days (compliance requirement)"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Prevent double booking with Redis distributed lock BEFORE database check. Lock key MUST include hotel_id, room_id, AND full date range. SETNX lock:hotel:{id}:room:{id}:{checkIn}:{checkOut} with EX 30. Without this, race conditions cause overbooking."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How to prevent double booking?'. Answer: (1) Redis SETNX lock with date range, (2) PostgreSQL SELECT FOR UPDATE on room_availability, (3) Transaction to INSERT booking + UPDATE availability atomically, (4) Lock auto-expires 30s to prevent deadlock."
              },
              {
                "type": "tip",
                "text": "Search optimization: Elasticsearch returns hotels with static filters (location, price, amenities) only, NOT date availability. Separate API checks availability after user selects hotel. Why? Availability changes every booking - reindexing ES constantly = expensive + slow."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Payment idempotency using booking_id as idempotency_key. Payment gateway + database UNIQUE constraint ensures no double charge even if user clicks 'Pay' multiple times or webhook replays."
              },
              {
                "type": "warning",
                "text": "NEVER check availability in Elasticsearch. ES is for search (location + filters), not transactional data. Availability is highly dynamic - use PostgreSQL room_availability table with pessimistic locks (SELECT FOR UPDATE)."
              },
              {
                "type": "tip",
                "text": "Multi-region DR: Primary region us-east-1, warm standby us-west-2 with streaming replication (lag <5s). Route53 health checks auto-failover in 2-5 min. RPO=5s, RTO=5min. Test DR monthly."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'How to handle cancellations?'. Answer: Store cancellation_policy snapshot in booking at creation (protects user if policy changes). Calculate refund based on hours_until_checkin. Release room_availability, process async refund via payment gateway webhook."
              },
              {
                "type": "warning",
                "text": "NEVER use eventual consistency for bookings. Use ACID transactions (BEGIN; INSERT booking; UPDATE room_availability; COMMIT;). Strong consistency required - no two users can book same room/dates."
              },
              {
                "type": "tip",
                "text": "Caching strategy: Search results (TTL=10min, 60% hit rate), hotel details (TTL=30min), user bookings (TTL=5min, invalidate on changes). Redis cluster serves 70% of searches without hitting Elasticsearch/PostgreSQL."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Lock key design lock:hotel:{hotel_id}:room:{room_id}:{checkIn}:{checkOut} - includes FULL date range. Why? Prevents concurrent bookings for overlapping dates. Lock expires automatically in 30s if service crashes."
              }
            ],
            "tags": [
              "system-design",
              "hotel-booking",
              "Airbnb",
              "MakeMyTrip",
              "Bookings.com",
              "distributed-locks",
              "Redis-SETNX",
              "Elasticsearch-geo",
              "double-booking-prevention",
              "payment-idempotency",
              "ACID-transactions",
              "PostgreSQL-FOR-UPDATE",
              "Kafka-events",
              "disaster-recovery",
              "multi-region",
              "availability-management",
              "cancellation-policy",
              "CDC-pipeline"
            ]
          }
        },
        {
          "id": "06",
          "title": "System Design 6: Design Food Delivery Application like Zomato / Swiggy / Uber Eats | HLD | LLD",
          "videoUrl": "https://youtu.be/rZyAgZuuZiA",
          "description": "In this video, we dive deep into the **system design of a scalable food delivery application** like Zomato, Swiggy, or Uber Eats.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/34n32n46jk34.jpg?updatedAt=1746473923991",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/FoodDeliveryApplication%20(1).png?updatedAt=1746560569003",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "Food Delivery Application (Zomato/Swiggy/Uber Eats)",
              "oneLinerToMemorize": "Proximity search (Elasticsearch) \u2192 Cart & Order management \u2192 Driver matching (geospatial) \u2192 Real-time tracking (WebSocket) \u2192 Payment integration"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User registration and login with profile management",
                  "Feature 2: List all nearby restaurants based on user location (proximity search)",
                  "Feature 3: Search restaurants by name, cuisine, dish with filters (rating, price, delivery time)",
                  "Feature 4: Show restaurant menu with items, prices, images, customization options",
                  "Feature 5: Add items to cart, modify quantities, apply promo codes, calculate total with taxes",
                  "Feature 6: Place order with multiple payment methods (card, wallet, UPI, cash on delivery)",
                  "Feature 7: Find nearby delivery partner based on driver location and optimize delivery time",
                  "Feature 8: Track order status in real-time (order placed, restaurant accepted, food prepared, driver assigned, picked up, out for delivery, delivered)",
                  "Feature 9: Real-time location tracking of delivery partner on map",
                  "Feature 10: Order history, ratings & reviews for restaurants and drivers"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Users",
                        "v": "50M users, 1M restaurants globally"
                      },
                      {
                        "k": "Orders",
                        "v": "10M orders per day, peak 100K orders/hour during lunch/dinner"
                      },
                      {
                        "k": "Concurrent Users",
                        "v": "100K-500K active users during peak hours"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> Consistency (eventual consistency acceptable for search, strong for payments)"
                      },
                      {
                        "k": "Search Latency",
                        "v": "< 500ms for restaurant search with filters"
                      },
                      {
                        "k": "Order Placement",
                        "v": "< 2s for checkout and payment confirmation"
                      },
                      {
                        "k": "Location Updates",
                        "v": "Driver location updated every 10-20s for real-time tracking"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability",
                    "pairs": [
                      {
                        "k": "Uptime",
                        "v": "Highly available, application should be highly available based on searching and our application should be highly consistent based on payments and order of food from restaurant"
                      },
                      {
                        "k": "Payment Integrity",
                        "v": "Strong consistency for payments (ACID transactions), no double charging or lost payments"
                      },
                      {
                        "k": "Order Consistency",
                        "v": "Exactly-once order processing, idempotent order placement"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities",
                "type": "bullets",
                "items": [
                  "Entity 1: User - Customer profile with user_id, name, email, phone, addresses[], payment_methods[], order_history",
                  "Entity 2: Restaurant - Restaurant details with restaurant_id, name, location (lat, lng), cuisine, rating, delivery_time, menu[]",
                  "Entity 3: FoodMenu - Menu items with item_id, restaurant_id, name, price, image_url, category, customizations[]",
                  "Entity 4: Cart - Shopping cart with cart_id, user_id, items[], total_amount, promo_code",
                  "Entity 5: Order - Order details with order_id, user_id, restaurant_id, items[], status, payment_status, delivery_partner_id, timestamps",
                  "Entity 6: Delivery Partner - Driver with driver_id, name, phone, vehicle, current_location, availability_status, assigned_orders[]",
                  "Entity 7: Payment - Payment transaction with payment_id, order_id, amount, method, status, gateway_response"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "User Operations",
                    "pairs": [
                      {
                        "k": "POST /v1/users/register",
                        "v": "Register user {name, email, phone, password} \u2192 {user_id, token}"
                      },
                      {
                        "k": "GET /v1/users/{userId}",
                        "v": "Get user profile with saved addresses, payment methods"
                      },
                      {
                        "k": "POST /v1/users/{userId}/addresses",
                        "v": "Add delivery address {street, city, lat, lng, label}"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Restaurant & Search",
                    "pairs": [
                      {
                        "k": "GET /v1/restaurants/nearby",
                        "v": "List nearby restaurants {lat, lng, radius} \u2192 [restaurants] with distance"
                      },
                      {
                        "k": "GET /v1/restaurants/search",
                        "v": "Search {query, lat, lng, cuisine, rating, sortBy} \u2192 filtered results"
                      },
                      {
                        "k": "GET /v1/restaurants/{id}",
                        "v": "Get restaurant details with menu"
                      },
                      {
                        "k": "GET /v1/restaurants/{id}/menu",
                        "v": "Paginated menu items with categories"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cart & Orders",
                    "pairs": [
                      {
                        "k": "GET /v1/cart",
                        "v": "Get current cart items"
                      },
                      {
                        "k": "POST /v1/cart/items",
                        "v": "Add item {item_id, quantity, customizations} \u2192 updated cart"
                      },
                      {
                        "k": "DELETE /v1/cart/items/{itemId}",
                        "v": "Remove item from cart"
                      },
                      {
                        "k": "POST /v1/orders",
                        "v": "Place order {cart_id, address_id, payment_method, promo_code} \u2192 {order_id, payment_link}"
                      },
                      {
                        "k": "GET /v1/orders/{orderId}",
                        "v": "Get order details with status, items, delivery partner info"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Tracking & Updates",
                    "pairs": [
                      {
                        "k": "WS /v1/orders/{orderId}/track",
                        "v": "WebSocket for real-time order status and driver location updates"
                      },
                      {
                        "k": "GET /v1/delivery/{orderId}/tracking",
                        "v": "Get current delivery partner location and ETA"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Users \u2192 LB + Gateway: Authentication, rate limiting, routing",
                  "User Service \u2192 User DB (PostgreSQL): User profiles, addresses, preferences",
                  "Search Service \u2192 Elasticsearch + RestaurantDB: Restaurant search with proximity, filters, full-text",
                  "Search Service \u2192 S3: Restaurant images, food images served via CDN",
                  "Cart Service \u2192 Cart DB (Redis): Shopping cart state, session management",
                  "Order Service \u2192 Order DB (PostgreSQL): Order lifecycle, ACID transactions",
                  "Payment Service \u2192 Payment Gateway: Payment processing (Stripe/Razorpay), transaction management",
                  "Driver Matching Service \u2192 Driver DB + Redis: Geospatial matching, availability tracking",
                  "Order Service \u2192 Kafka: Event-driven order lifecycle (order_placed, restaurant_accepted, driver_assigned, etc.)",
                  "Restaurant Service \u2192 Kafka: Notifies restaurant when order placed, updates preparation status",
                  "Notification Service: Push notifications to user, restaurant, driver for status updates",
                  "WebSocket Gateway: Real-time location tracking and order status updates",
                  "Order Confirmation Service: Validates order, checks inventory, confirms with restaurant"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: User Registration & Login",
                    "steps": [
                      "User sends: POST /v1/users/register with {name: 'John', email: 'john@email.com', phone: '+1234567890', password: 'hashed'}",
                      "User Service validates: Email/phone uniqueness, password strength, sends OTP for phone verification",
                      "Service creates: User record in PostgreSQL {user_id: UUID, name, email, phone, password_hash (bcrypt), created_at}",
                      "Service generates: JWT token with {user_id, role: 'customer', exp: 30 days}",
                      "Login: POST /v1/users/login with {email, password} \u2192 validates bcrypt hash \u2192 returns JWT token",
                      "Service initializes: Empty cart in Redis cart:{user_id} = {}"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Search Nearby Restaurants (Proximity Search)",
                    "steps": [
                      "User requests: GET /v1/restaurants/nearby?lat=12.9716&lng=77.5946&radius=5km",
                      "Search Service queries: Elasticsearch with geo_distance filter: { query: { bool: { filter: [ { geo_distance: { distance: '5km', location: { lat: 12.9716, lon: 77.5946 } } }, { term: { is_active: true } } ] } }, sort: [{ _geo_distance: { location: {...}, order: 'asc' } }], size: 50 }",
                      "Elasticsearch returns: [{ restaurant_id, name, cuisine, rating, avg_delivery_time, distance: 1.2km }] sorted by distance",
                      "Service enriches: Fetch additional metadata from RestaurantDB (menu preview, offers, images from S3/CDN)",
                      "Caching: Popular queries cached in Redis search:lat:lng:5km with TTL=5 min",
                      "Response: { restaurants: [{id, name, cuisine, rating, distance, estimated_delivery: '30-40 min', image_url}] }"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Restaurant Search with Filters",
                    "steps": [
                      "User searches: GET /v1/restaurants/search?q=pizza&lat=12.97&lng=77.59&cuisine=Italian&rating>=4.0&sortBy=rating",
                      "Search Service builds: Elasticsearch query { query: { bool: { must: [ { match: { menu_items: 'pizza' } } ], filter: [ { geo_distance: {...} }, { term: { cuisine: 'Italian' } }, { range: { rating: { gte: 4.0 } } } ] } }, sort: [{ rating: 'desc' }] }",
                      "Query execution: (1) Full-text search on menu_items \u2192 5K candidates, (2) Proximity filter \u2192 500, (3) Cuisine + rating filter \u2192 50 results",
                      "Faceted search: Aggregations for filters: 'aggs': { 'cuisines': { terms: { field: 'cuisine' } }, 'avg_price': { stats: { field: 'avg_item_price' } } }",
                      "Response includes: Matching restaurants + facets for UI filters (Italian: 50, Chinese: 30, etc.)"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Menu Display & Cart Management",
                    "steps": [
                      "User views menu: GET /v1/restaurants/{restaurant_id}/menu \u2192 fetches from RestaurantDB with categories (Starters, Main Course, Desserts)",
                      "Add to cart: POST /v1/cart/items with {item_id: 'burger_123', quantity: 2, customizations: [{type: 'add-on', value: 'extra_cheese'}]}",
                      "Cart Service: (1) Validates item exists, restaurant is open, (2) Updates Redis cart:{user_id} HSET items:burger_123 '{quantity: 2, customizations, price: 8.99}', (3) Recalculates total: base_price + customizations + taxes",
                      "Cart state in Redis: { items: { burger_123: {quantity: 2, price: 17.98}, fries_456: {quantity: 1, price: 3.99} }, subtotal: 21.97, tax: 2.20, delivery_fee: 2.00, total: 26.17, restaurant_id, TTL: 24h }",
                      "Apply promo: POST /v1/cart/promo with {code: 'SAVE20'} \u2192 validates in Promo DB \u2192 applies 20% discount \u2192 updates total"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Order Placement & Payment",
                    "steps": [
                      "User checkout: POST /v1/orders with {cart_id, address_id, payment_method: 'card', payment_method_id: 'pm_123'}",
                      "Order Service validates: (1) Cart not empty, (2) Restaurant open and accepting orders, (3) Address within delivery radius, (4) Items still available (inventory check)",
                      "Service creates: Order record in PostgreSQL {order_id: UUID, user_id, restaurant_id, items: JSON, total: 26.17, status: 'PENDING_PAYMENT', created_at} with transaction isolation",
                      "Payment flow: (1) Call Payment Service \u2192 Stripe/Razorpay createPaymentIntent, (2) Return payment_link to client, (3) Client confirms payment, (4) Webhook POST /webhooks/payment with {order_id, status: 'success'}",
                      "On payment success: (1) Update order status='CONFIRMED', payment_status='PAID', (2) Publish to Kafka 'order.placed' topic with order details, (3) Clear cart from Redis, (4) Send notifications to user + restaurant"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Restaurant Acceptance Flow",
                    "steps": [
                      "Restaurant Service consumes: Kafka 'order.placed' event",
                      "Service sends: Push notification to restaurant app 'New order #12345 - \u20b926.17'",
                      "Restaurant accepts: POST /v1/restaurants/orders/{order_id}/accept with estimated_prep_time=20min",
                      "Service updates: Order status='RESTAURANT_ACCEPTED', prep_completion_time=now()+20min",
                      "Service publishes: Kafka 'order.restaurant_accepted' event",
                      "Timeout handling: If restaurant doesn't respond in 5 min, auto-reject order, refund payment, notify user, offer alternative restaurants"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Driver Matching & Assignment",
                    "steps": [
                      "Driver Matching Service consumes: Kafka 'order.restaurant_accepted' event",
                      "Service queries: Redis GEORADIUS drivers:online {restaurant_lng} {restaurant_lat} 5 km WITHDIST ASC to find nearby available drivers",
                      "Filtering: (1) Status='online' and available=true, (2) Not currently on delivery, (3) Vehicle type suitable (bike for small orders, car for large)",
                      "Scoring: score = (1/distance) \u00d7 0.5 + rating \u00d7 0.3 + acceptance_rate \u00d7 0.2, select top driver",
                      "Assignment: (1) Acquire distributed lock SETNX lock:driver:{driver_id} {order_id} EX 60, (2) If acquired, assign order to driver, publish to Kafka 'driver.assigned', (3) If driver rejects, release lock, try next driver",
                      "Notification: Push to driver app 'New delivery request: Pickup from Restaurant X, deliver to Location Y, earn \u20b950'",
                      "Driver confirms: POST /v1/drivers/orders/{order_id}/accept \u2192 update order status='DRIVER_ASSIGNED', driver_id={driver_id}"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Real-Time Location Tracking",
                    "steps": [
                      "Driver app: Updates location every 10-20s via POST /v1/drivers/location with {driver_id, lat, lng, timestamp}",
                      "Update Location Service: (1) Updates Redis GEOADD drivers:online {lng} {lat} {driver_id}, (2) If driver on active delivery, publish to Kafka 'driver.location.update' with {driver_id, order_id, location}",
                      "Consumer Service: (1) Consumes location updates, (2) Calculates ETA using Google Maps Distance Matrix API, (3) Publishes to Redis Pub/Sub channel location:{order_id}",
                      "User tracking: (1) WebSocket connection WS /v1/orders/{order_id}/track, (2) WebSocket Manager subscribes to Redis channel location:{order_id}, (3) Forwards location updates to user's WebSocket: {driver_location: {lat, lng}, eta: '8 min', distance_remaining: '1.2 km'}",
                      "Optimization: Batch location updates every 10s instead of real-time (10-20s is sufficient for delivery tracking)"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Order Status Updates & Lifecycle",
                    "steps": [
                      "Status transitions: PENDING_PAYMENT \u2192 CONFIRMED \u2192 RESTAURANT_ACCEPTED \u2192 FOOD_PREPARING \u2192 DRIVER_ASSIGNED \u2192 DRIVER_AT_RESTAURANT \u2192 PICKED_UP \u2192 OUT_FOR_DELIVERY \u2192 DELIVERED",
                      "Driver picks up: POST /v1/drivers/orders/{order_id}/pickup \u2192 status='PICKED_UP', pickup_time=now(), publish Kafka event",
                      "Driver delivers: POST /v1/drivers/orders/{order_id}/deliver \u2192 status='DELIVERED', delivered_time=now()",
                      "Order Status Service: Consumes all Kafka events, updates Order DB, publishes to WebSocket for real-time UI updates",
                      "Notification flow: Each status change \u2192 Notification Service sends push to user ('Your food is being prepared', 'Driver is on the way', 'Food delivered')",
                      "Timeout monitoring: Background job checks if order stuck in any state >threshold time (e.g., FOOD_PREPARING >60 min), escalates to support"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Post-Delivery Rating & Review",
                    "steps": [
                      "User rates: POST /v1/orders/{order_id}/rating with {restaurant_rating: 5, driver_rating: 4, review: 'Great food!', tags: ['quick_delivery', 'well_packed']}",
                      "Service stores: Rating in separate Ratings table {rating_id, order_id, user_id, restaurant_rating, driver_rating, review, timestamp}",
                      "Aggregation: Background job updates: (1) Restaurant avg_rating = SELECT AVG(restaurant_rating) FROM ratings WHERE restaurant_id={id}, (2) Driver avg_rating similarly, (3) Updates Restaurant and Driver tables, (4) Reindexes in Elasticsearch for search ranking",
                      "Review moderation: ML service scans review for abuse/spam, flags for manual review if needed",
                      "Driver sees rating: Displayed in driver app for transparency, low ratings (<4.0 avg) trigger retraining or suspension"
                    ]
                  }
                ]
              },
              {
                "id": "client-components",
                "title": "7. Client-Side Components",
                "type": "bullets",
                "items": [
                  "Component 1: Location Picker - Autocomplete address search with Google Places API, GPS location detection",
                  "Component 2: Restaurant List - Infinite scroll with lazy loading, filters (cuisine, rating, delivery time)",
                  "Component 3: Menu Browser - Categorized menu with search, item customization modal (add-ons, special instructions)",
                  "Component 4: Cart Widget - Persistent cart icon with item count, cart summary with price breakdown",
                  "Component 5: Checkout Flow - Address selection, payment method, promo code input, order confirmation",
                  "Component 6: Order Tracking - Real-time map with driver location, status timeline, ETA countdown",
                  "Component 7: WebSocket Manager - Maintains connection, handles reconnection, receives location and status updates"
                ]
              },
              {
                "id": "database-schema",
                "title": "8. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Users (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "phone",
                        "v": "varchar(15) UNIQUE"
                      },
                      {
                        "k": "password_hash",
                        "v": "varchar(255) (bcrypt)"
                      },
                      {
                        "k": "addresses",
                        "v": "jsonb [{street, city, lat, lng, label: 'home'}]"
                      },
                      {
                        "k": "payment_methods",
                        "v": "jsonb [{type: 'card', last4: '1234', is_default: true}]"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Restaurants (PostgreSQL + Elasticsearch)",
                    "pairs": [
                      {
                        "k": "restaurant_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "location",
                        "v": "geography(Point, 4326) (PostGIS) or geo_point (Elasticsearch)"
                      },
                      {
                        "k": "cuisine",
                        "v": "varchar(100)[] (array: ['Italian', 'Pizza'])"
                      },
                      {
                        "k": "rating",
                        "v": "decimal(2,1) (4.5 stars)"
                      },
                      {
                        "k": "avg_delivery_time",
                        "v": "integer (minutes)"
                      },
                      {
                        "k": "is_active",
                        "v": "boolean (open/closed)"
                      },
                      {
                        "k": "menu_items",
                        "v": "jsonb or separate MenuItems table"
                      },
                      {
                        "k": "image_url",
                        "v": "varchar(500) (S3/CDN link)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "MenuItems (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "item_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "restaurant_id",
                        "v": "uuid FK \u2192 Restaurants"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "category",
                        "v": "varchar(100) (Starters, Main Course)"
                      },
                      {
                        "k": "price",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "image_url",
                        "v": "varchar(500)"
                      },
                      {
                        "k": "customizations",
                        "v": "jsonb [{type: 'size', options: ['Small', 'Large']}]"
                      },
                      {
                        "k": "is_available",
                        "v": "boolean"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Orders (PostgreSQL - ACID critical)",
                    "pairs": [
                      {
                        "k": "order_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users, INDEXED"
                      },
                      {
                        "k": "restaurant_id",
                        "v": "uuid FK \u2192 Restaurants"
                      },
                      {
                        "k": "driver_id",
                        "v": "uuid FK \u2192 Drivers (nullable until assigned)"
                      },
                      {
                        "k": "items",
                        "v": "jsonb [{item_id, name, quantity, price, customizations}]"
                      },
                      {
                        "k": "delivery_address",
                        "v": "jsonb {street, city, lat, lng}"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING_PAYMENT, CONFIRMED, RESTAURANT_ACCEPTED, FOOD_PREPARING, DRIVER_ASSIGNED, PICKED_UP, OUT_FOR_DELIVERY, DELIVERED, CANCELLED)"
                      },
                      {
                        "k": "payment_status",
                        "v": "enum (PENDING, PAID, FAILED, REFUNDED)"
                      },
                      {
                        "k": "subtotal",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "delivery_fee",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "tax",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "discount",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "total",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "promo_code",
                        "v": "varchar(50)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp INDEXED"
                      },
                      {
                        "k": "confirmed_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "delivered_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Drivers (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "driver_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "phone",
                        "v": "varchar(15)"
                      },
                      {
                        "k": "vehicle_type",
                        "v": "enum (bike, car, scooter)"
                      },
                      {
                        "k": "vehicle_number",
                        "v": "varchar(20)"
                      },
                      {
                        "k": "current_location",
                        "v": "geography(Point, 4326) (updated periodically)"
                      },
                      {
                        "k": "status",
                        "v": "enum (online, offline, on_delivery)"
                      },
                      {
                        "k": "avg_rating",
                        "v": "decimal(2,1)"
                      },
                      {
                        "k": "total_deliveries",
                        "v": "integer"
                      },
                      {
                        "k": "acceptance_rate",
                        "v": "decimal(3,2) (0.85 = 85%)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cart (Redis - session data)",
                    "pairs": [
                      {
                        "k": "cart:{user_id}",
                        "v": "HASH {restaurant_id, items: {item_id: {quantity, price, customizations}}, subtotal, total, updated_at}"
                      },
                      {
                        "k": "TTL",
                        "v": "24 hours (expire inactive carts)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Driver Location (Redis Geospatial)",
                    "pairs": [
                      {
                        "k": "drivers:online",
                        "v": "GEORADIUS key with {lng, lat, driver_id}"
                      },
                      {
                        "k": "driver:{driver_id}:location",
                        "v": "HASH {lat, lng, timestamp, heading, speed}"
                      },
                      {
                        "k": "lock:driver:{driver_id}",
                        "v": "STRING {order_id} with EX 60 (distributed lock for assignment)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch - Restaurant Search Index",
                    "pairs": [
                      {
                        "k": "restaurant_id",
                        "v": "keyword"
                      },
                      {
                        "k": "name",
                        "v": "text with standard analyzer"
                      },
                      {
                        "k": "location",
                        "v": "geo_point"
                      },
                      {
                        "k": "cuisine",
                        "v": "keyword (exact match for filters)"
                      },
                      {
                        "k": "menu_items",
                        "v": "text (full-text search on dish names)"
                      },
                      {
                        "k": "rating",
                        "v": "float"
                      },
                      {
                        "k": "avg_delivery_time",
                        "v": "integer"
                      },
                      {
                        "k": "is_active",
                        "v": "boolean"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "additional-mechanism",
                "title": "9. Key Mechanisms",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Idempotent Order Placement",
                    "steps": [
                      "Problem: User clicks 'Place Order' multiple times due to slow network \u2192 risk of duplicate orders and double charging",
                      "Solution: Idempotency key approach",
                      "Implementation: (1) Client generates UUID idempotency_key before submit, (2) Client sends POST /v1/orders with {cart_id, idempotency_key: '550e8400-e29b-41d4-a716-446655440000'}, (3) Order Service checks if order with this idempotency_key exists: SELECT * FROM orders WHERE idempotency_key = {key}, (4) If exists, return existing order_id (idempotent response), (5) If not exists, create order with BEGIN TRANSACTION; INSERT INTO orders (..., idempotency_key) ...; COMMIT;, (6) Idempotency key expires after 24 hours",
                      "Database constraint: UNIQUE INDEX on idempotency_key column ensures atomicity"
                    ]
                  },
                  {
                    "blockTitle": "Driver Matching Algorithm",
                    "steps": [
                      "Inputs: Restaurant location (pickup), delivery address (drop-off), order value, current time",
                      "Step 1: Find candidates - Redis GEORADIUS drivers:online {restaurant_lng} {restaurant_lat} 5 km \u2192 returns [driver_1: 0.8km, driver_2: 1.5km, driver_3: 2.3km]",
                      "Step 2: Filter - status='online', available=true, not on another delivery, vehicle suitable for order size",
                      "Step 3: Score each driver: score = (proximity_score \u00d7 0.4) + (rating_score \u00d7 0.3) + (acceptance_rate \u00d7 0.2) + (idle_time \u00d7 0.1)",
                      "Proximity: 1 / (1 + distance_km), closer = higher score",
                      "Rating: driver_rating / 5.0 (normalize to 0-1)",
                      "Acceptance: acceptance_rate (historical accept/reject ratio)",
                      "Idle: Minutes since last delivery (prefer busy drivers to keep them engaged)",
                      "Step 4: Select top driver, attempt assignment with lock, if rejected cascade to next",
                      "Optimization: Pre-compute driver availability zones, update every 5 min"
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "10. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Elasticsearch Sharding - Restaurant index sharded by location (geo_hash prefix), enables parallel queries across regions",
                  "Technique 2: Redis Caching - Cache popular restaurant queries (search:lat:lng:5km) with TTL=5 min, serves 70% of searches from cache",
                  "Technique 3: CDN for Images - Restaurant and food images served via CloudFront, reduces origin load by 95%, faster global delivery",
                  "Technique 4: Database Read Replicas - PostgreSQL replicas for read queries (restaurant details, order history), writes to primary only",
                  "Technique 5: Cart in Redis - Fast cart operations (add/remove items), prevents database overload, TTL=24h auto-cleanup",
                  "Technique 6: Kafka for Event Streaming - Decouple order lifecycle events, enables async processing, horizontal scaling of consumers",
                  "Technique 7: WebSocket Connection Pooling - Gateway maintains 50K connections per instance, load balanced across multiple instances",
                  "Technique 8: Geo-Sharding - Partition restaurants and orders by region (US-West, US-East, India, etc.), reduces cross-region queries",
                  "Technique 9: Driver Location Batching - Update Redis every 10-20s instead of real-time, reduces write load by 80%",
                  "Technique 10: Lazy Menu Loading - Load only first 20 menu items, paginate on scroll, reduces initial payload from 500KB to 50KB",
                  "Technique 11: API Rate Limiting - 100 requests/min per user prevents abuse, protects against DDoS",
                  "Technique 12: Connection Pooling - API servers maintain 100 DB connections, prevents connection exhaustion"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you search for nearby restaurants efficiently?",
                    "a": "Elasticsearch with geo_distance query: (1) Index restaurants with geo_point mapping: PUT /restaurants { mappings: { properties: { location: { type: 'geo_point' } } } }, (2) Query: POST /restaurants/_search { query: { bool: { filter: [ { geo_distance: { distance: '5km', location: { lat: 12.97, lon: 77.59 } } }, { term: { is_active: true } } ] } }, sort: [{ _geo_distance: { location: {...}, order: 'asc' } }], size: 50 }, (3) Elasticsearch uses BKD tree internally for spatial index, O(log n) queries, (4) Returns restaurants sorted by distance in ~50ms for millions of docs. Caching: Popular queries cached in Redis search:{lat}:{lng}:{radius} with TTL=5 min, serves 70% from cache <5ms. Alternative: PostgreSQL PostGIS with ST_DWithin for exact distance, Redis GEORADIUS for real-time driver locations. Hybrid: Elasticsearch for search (complex filters), Redis for hot cache, PostgreSQL for source of truth."
                  },
                  {
                    "q": "How do you implement cart functionality with fast add/remove operations?",
                    "a": "Redis-based cart with session management: (1) Schema: HASH key cart:{user_id} with fields {restaurant_id, items: {item_id: {quantity, price, customizations}}, subtotal, tax, total, updated_at}, (2) Add item: HSET cart:{user_id} items:item_123 '{quantity: 2, price: 17.98, customizations: [extra_cheese]}', then recalculate total using HGETALL cart:{user_id}, update subtotal/total fields, (3) Remove item: HDEL cart:{user_id} items:item_123, recalculate total, (4) Get cart: HGETALL cart:{user_id} returns entire cart in <1ms, (5) TTL: EXPIRE cart:{user_id} 86400 (24 hours), auto-cleanup inactive carts. Advantages: (1) Fast operations ~1ms vs 50ms for database, (2) Scales to millions of concurrent users, (3) Reduces DB load, (4) Session state persists across client restarts. Persistence: On order placement, serialize cart to Order.items JSONB column in PostgreSQL for audit trail, then DEL cart:{user_id}. Edge case: Cart from different restaurant \u2192 prompt user 'Clear existing cart?' before adding items from new restaurant."
                  },
                  {
                    "q": "How do you ensure exactly-once order processing and prevent double charging?",
                    "a": "Multi-layer idempotency: (1) Client-side idempotency key: Client generates UUID before submit, includes in request header X-Idempotency-Key: {uuid}, (2) Database constraint: Orders table has UNIQUE INDEX on idempotency_key column, (3) Order creation: BEGIN TRANSACTION; SELECT * FROM orders WHERE idempotency_key = {key} FOR UPDATE; If found: ROLLBACK; return existing order_id (HTTP 200, idempotent). If not found: INSERT INTO orders (..., idempotency_key) VALUES (...); COMMIT; return new order_id (HTTP 201), (4) Payment idempotency: Payment gateway (Stripe/Razorpay) uses same idempotency_key, prevents double charging even if retry, (5) Key expiry: Idempotency keys valid for 24 hours, then removed. Example: User clicks 'Place Order' twice within 2s \u2192 Request 1 creates order, Request 2 finds existing order by idempotency_key, returns same order_id \u2192 user charged once. Race condition: Two concurrent requests with same key \u2192 database UNIQUE constraint ensures only one INSERT succeeds, other gets constraint violation \u2192 retry with SELECT. Alternative: Distributed lock with Redis SETNX before order creation, but database constraint simpler."
                  },
                  {
                    "q": "How do you match drivers to orders efficiently and prevent double assignment?",
                    "a": "Geospatial matching with distributed locks: (1) Trigger: Kafka 'order.restaurant_accepted' event consumed by Driver Matching Service, (2) Find candidates: Redis GEORADIUS drivers:online {restaurant_lng} {restaurant_lat} 5 km WITHDIST ASC returns nearby drivers sorted by distance, (3) Filter: status='online' AND available=true AND current_orders=0, vehicle_type suitable, (4) Scoring: score = (1/distance_km) \u00d7 0.4 + (rating/5) \u00d7 0.3 + acceptance_rate \u00d7 0.2 + (idle_time/60) \u00d7 0.1, select highest score, (5) Lock acquisition: Redis SETNX lock:driver:{driver_id} {order_id} EX 60, if returns 1 (success) proceed, if 0 (already locked) try next driver, (6) Send request: Push notification to driver 'New order: \u20b950 earnings', driver has 30s to accept, (7) Acceptance: Driver accepts \u2192 update order.driver_id, status='DRIVER_ASSIGNED', release lock, (8) Rejection/timeout: DEL lock:driver:{driver_id}, cascade to next driver in queue. Double assignment prevention: Lock ensures only one order can claim driver at a time, even if two orders match same driver concurrently. Example: Order O1 and O2 both match Driver D1 \u2192 O1 acquires lock first \u2192 O2's SETNX fails \u2192 O2 tries D2 instead \u2192 no conflict. Alternative: Database pessimistic locking with SELECT ... FOR UPDATE on drivers table, but slower than Redis."
                  },
                  {
                    "q": "How do you implement real-time order tracking with driver location updates?",
                    "a": "WebSocket + Redis Pub/Sub architecture: (1) Driver location updates: Driver app sends POST /v1/drivers/location every 10-20s with {driver_id, lat, lng, timestamp}, (2) Update Location Service: (a) Updates Redis GEOADD drivers:online {lng} {lat} {driver_id}, (b) If driver on active delivery, publish to Kafka 'driver.location.update' topic, (3) Location Consumer: Consumes Kafka events, publishes to Redis Pub/Sub channel location:{order_id} with {driver_location, eta, distance_remaining}, (4) WebSocket Gateway: User connects WS /v1/orders/{order_id}/track, gateway subscribes to Redis Pub/Sub location:{order_id}, (5) Real-time push: When new location published to Redis channel, WebSocket Gateway forwards to user's WebSocket connection in <100ms, (6) Client renders: Update driver marker on map, recalculate ETA, display distance. ETA calculation: Call Google Maps Distance Matrix API with {origin: driver_location, destination: delivery_address, mode: 'driving'}, returns duration considering traffic. Optimization: Batch location updates every 10s instead of every update, send only when distance changed >50m. Scaling: 100K active deliveries \u00d7 1 update/10s = 10K updates/sec, Redis Pub/Sub handles 100K msg/sec. Fallback: If WebSocket fails, client falls back to HTTP polling GET /v1/orders/{order_id}/location every 10s. Example: Driver moves from Point A to B \u2192 Location Service publishes to Redis \u2192 WebSocket Gateway forwards to user \u2192 map updates driver position \u2192 ETA recalculated '8 min remaining'."
                  },
                  {
                    "q": "How do you handle order cancellations and refunds?",
                    "a": "Multi-stage cancellation policy: (1) Before restaurant acceptance: User can cancel free, POST /v1/orders/{order_id}/cancel \u2192 status='CANCELLED', full refund initiated, (2) After restaurant acceptance: Cancellation fee applies (\u20b920-50 depending on time), partial refund = total - cancellation_fee, (3) After driver assignment: Higher fee (\u20b950-100), compensates driver for time, (4) Refund processing: Call Payment Service \u2192 payment_gateway.refund(payment_id, amount), status='REFUNDING', webhook confirms \u2192 status='REFUNDED', (5) Notification: Push to user 'Order cancelled, \u20b9X refunded', email with refund receipt. Database updates: UPDATE orders SET status='CANCELLED', cancelled_by='user', cancelled_at=now(), refund_amount={amount}, refund_status='PROCESSING' WHERE order_id={id}. Kafka event: Publish 'order.cancelled' \u2192 Restaurant Service notifies restaurant, Driver Service releases driver if assigned. Edge cases: (1) Food already prepared: No refund, but offer credit for future order, (2) Driver already picked up: Cannot cancel, must receive delivery, (3) Payment failed originally: No refund needed, just mark cancelled. Abuse prevention: Track user cancellation_rate, if >30% in last 10 orders, add 5 min waiting period before allowing next order. Example: User cancels 2 min after ordering \u2192 restaurant not yet accepted \u2192 full refund \u20b926.17 \u2192 refund appears in wallet/card in 5-7 days."
                  },
                  {
                    "q": "What's your strategy for handling peak traffic during lunch/dinner hours?",
                    "a": "Multi-tier scaling strategy: (1) Auto-scaling: Kubernetes HPA scales API servers based on CPU/memory (threshold 70%), pre-warm 30 min before peak (11:30 AM, 6:30 PM), scale from 20 to 100 instances, (2) Database connection pooling: Maintain 100 connections per API server (10K total), prevents connection exhaustion, use pgBouncer for connection management, (3) Read replicas: Route read queries (restaurant search, order history) to 5 PostgreSQL replicas, writes to primary only, (4) Redis cluster: Shard cart data across 10 Redis nodes using hash(user_id), each node handles 10K concurrent users, (5) Kafka buffering: Order placement events buffered in Kafka, consumers process at steady rate even if spike in orders, prevents overwhelming downstream services, (6) CDN caching: Restaurant images, menu items cached at edge, reduces origin load by 95%, (7) API rate limiting: 100 req/min per user with token bucket algorithm, prevents single user from overwhelming system, (8) Database query optimization: Materialized views for popular queries (top restaurants per area), refresh every 5 min, (9) Async processing: Non-critical tasks (email receipts, analytics) processed async via Kafka, doesn't block order flow. Peak load: 100K orders/hour = 1.67K orders/min = 28 orders/sec. With scaling: 100 API instances \u00d7 100 orders/sec capacity = 10K orders/sec headroom. Monitoring: Prometheus + Grafana for real-time metrics, alerts if latency >500ms or error rate >1%, auto-scale triggers."
                  },
                  {
                    "q": "How do you ensure data consistency across multiple microservices?",
                    "a": "Event-driven architecture with saga pattern: (1) Order Service is orchestrator, manages distributed transaction, (2) Order placement saga: Step 1: Create order in Order DB (PENDING_PAYMENT), Step 2: Call Payment Service \u2192 if success proceed, if fail rollback (mark order FAILED), Step 3: Publish 'order.paid' to Kafka, Step 4: Restaurant Service updates inventory \u2192 if out of stock, compensating transaction (refund, cancel order), Step 5: Driver Matching assigns driver \u2192 if no drivers available, retry with backoff or cancel order. (3) Eventual consistency: Each service has its own database (database-per-service pattern), updates propagated via Kafka events, (4) Compensating transactions: If downstream step fails, previous steps reversed via 'order.cancelled' event \u2192 Payment Service refunds, Restaurant Service restores inventory. (5) Idempotency: All event handlers idempotent (use event_id to deduplicate), prevents duplicate processing if event replayed. (6) Outbox pattern: Order Service writes order + event to same database in single transaction, background job publishes events to Kafka \u2192 ensures order creation and event publishing are atomic. Example: User orders last pizza \u2192 Order created \u2192 Payment succeeds \u2192 Inventory check fails (out of stock) \u2192 Compensating transaction: refund payment, cancel order, notify user 'Item unavailable, refund processed'. Data consistency levels: (1) Strong: Payments, inventory (use ACID transactions), (2) Eventual: Search index, analytics (sync async via Kafka). Trade-off: Strong consistency = slower, limited scalability; Eventual = faster, more scalable, acceptable for most use cases."
                  },
                  {
                    "q": "How do you implement search with multiple filters (cuisine, rating, delivery time)?",
                    "a": "Elasticsearch with compound boolean query: (1) Index mapping: PUT /restaurants { mappings: { properties: { name: {type: 'text'}, location: {type: 'geo_point'}, cuisine: {type: 'keyword'}, rating: {type: 'float'}, avg_delivery_time: {type: 'integer'}, price_range: {type: 'keyword'}, is_active: {type: 'boolean'} } } }, (2) Search query: POST /restaurants/_search { query: { bool: { must: [ { match: { name: 'pizza' } } ], filter: [ { geo_distance: { distance: '5km', location: { lat: 12.97, lon: 77.59 } } }, { terms: { cuisine: ['Italian', 'American'] } }, { range: { rating: { gte: 4.0 } } }, { range: { avg_delivery_time: { lte: 45 } } }, { term: { is_active: true } } ] } }, sort: [{ rating: 'desc' }], aggs: { cuisine_facets: { terms: { field: 'cuisine', size: 20 } }, price_facets: { terms: { field: 'price_range' } } }, size: 50 }, (3) Query execution: (a) Full-text match on 'pizza' \u2192 10K candidates, (b) Proximity filter \u2192 2K, (c) Cuisine filter \u2192 500, (d) Rating + delivery time \u2192 100 results, (4) Aggregations: Returns facet counts {Italian: 50, Chinese: 30, Mexican: 20} for UI filters. Sorting: (1) Default: Sort by distance (closest first), (2) Rating: Sort by rating DESC, (3) Delivery time: Sort by avg_delivery_time ASC, (4) Popularity: Sort by total_orders DESC. Pagination: Use from/size for first 10K results, search_after for deep pagination (>10K). Performance: ~50ms p95 for 100M restaurants with 5 filters. Caching: Cache popular filter combinations in Redis with TTL=5 min. Example: User searches 'pizza' near SF, filters: Italian, rating \u22654, delivery \u226445 min \u2192 Elasticsearch returns 50 restaurants + facets \u2192 user sees results with applied filters + counts for refinement."
                  },
                  {
                    "q": "How do you calculate delivery ETA accurately?",
                    "a": "Multi-factor ETA prediction: (1) Initial estimate: When order placed, estimate = restaurant_prep_time + travel_time_to_restaurant + travel_time_to_customer, (2) Restaurant prep time: Use historical avg_prep_time from restaurant, adjust for current load (busy hours +10 min), (3) Travel time: Call Google Maps Distance Matrix API with {origin: driver_current_location, destination: restaurant_location, mode: 'driving', departure_time: 'now'}, returns duration considering real-time traffic, (4) Dynamic updates: As order progresses, recalculate ETA every 1-2 min, formula: ETA = (distance_remaining / driver_speed) + traffic_adjustment, (5) Machine learning: Train model on historical data (features: time_of_day, day_of_week, weather, distance, restaurant, driver), predict ETA more accurately than static calculation, (6) Traffic data: Integrate with Google Maps Traffic API, adjust for congestion (red roads +50% time), (7) Driver speed: Calculate from last 5 location updates, driver_speed = distance / time, use for real-time ETA. Display: Show range 'Arrives in 25-35 min' instead of exact '30 min' to manage expectations. Update frequency: Recalculate every 60s during preparation, every 10s during delivery. Accuracy target: \u00b13 min from actual delivery time. Example: Order placed 12:00 PM \u2192 initial ETA: prep_time 20 min + travel 15 min = 35 min (arrive 12:35 PM) \u2192 at 12:15 driver assigned, recalc: travel 12 min = arrive 12:32 PM \u2192 at 12:25 driver picked up, recalc with actual speed and traffic: 10 min = arrive 12:35 PM \u2192 actual delivery 12:33 PM (within 2 min of estimate)."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "12. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Volume",
                    "pairs": [
                      {
                        "k": "Total Users",
                        "v": "50M registered users globally"
                      },
                      {
                        "k": "Restaurants",
                        "v": "1M restaurants, 10M menu items"
                      },
                      {
                        "k": "Daily Orders",
                        "v": "10M orders/day, peak 100K orders/hour"
                      },
                      {
                        "k": "Concurrent Users",
                        "v": "100K-500K during lunch/dinner hours"
                      },
                      {
                        "k": "Active Drivers",
                        "v": "100K-500K drivers online during peak"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Latency Requirements",
                    "pairs": [
                      {
                        "k": "Restaurant Search",
                        "v": "< 500ms with proximity + filters"
                      },
                      {
                        "k": "Cart Operations",
                        "v": "< 100ms (Redis add/remove items)"
                      },
                      {
                        "k": "Order Placement",
                        "v": "< 2s for checkout to confirmation"
                      },
                      {
                        "k": "Location Updates",
                        "v": "10-20s interval for driver tracking"
                      },
                      {
                        "k": "WebSocket Push",
                        "v": "< 100ms for real-time status updates"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Search & Matching",
                    "pairs": [
                      {
                        "k": "Search Radius",
                        "v": "5km default, max 20km for restaurants"
                      },
                      {
                        "k": "Driver Matching Radius",
                        "v": "5km from restaurant, expand to 10km if no drivers"
                      },
                      {
                        "k": "Elasticsearch Query",
                        "v": "~50ms for 100M documents with geo + filters"
                      },
                      {
                        "k": "Redis GEORADIUS",
                        "v": "<10ms for finding nearby drivers"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching & Storage",
                    "pairs": [
                      {
                        "k": "Redis Cart TTL",
                        "v": "24 hours (auto-cleanup inactive)"
                      },
                      {
                        "k": "Search Cache TTL",
                        "v": "5 minutes for popular queries"
                      },
                      {
                        "k": "Driver Location Update",
                        "v": "Every 10-20s during active delivery"
                      },
                      {
                        "k": "Image CDN Cache",
                        "v": "30 days, 95% hit rate"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Payment & Pricing",
                    "pairs": [
                      {
                        "k": "Average Order Value",
                        "v": "\u20b9300-500 ($10-15)"
                      },
                      {
                        "k": "Delivery Fee",
                        "v": "\u20b920-50 based on distance"
                      },
                      {
                        "k": "Platform Commission",
                        "v": "20-30% of order value from restaurant"
                      },
                      {
                        "k": "Driver Earnings",
                        "v": "\u20b930-100 per delivery"
                      },
                      {
                        "k": "Cancellation Fee",
                        "v": "\u20b920-100 based on order stage"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Timeouts & SLAs",
                    "pairs": [
                      {
                        "k": "Restaurant Acceptance",
                        "v": "5 min timeout, auto-reject if no response"
                      },
                      {
                        "k": "Driver Response",
                        "v": "30s to accept/reject delivery request"
                      },
                      {
                        "k": "Idempotency Key",
                        "v": "Valid for 24 hours"
                      },
                      {
                        "k": "Avg Delivery Time",
                        "v": "30-45 min (prep 20-30 min + delivery 10-15 min)"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "NEVER allow duplicate orders. Use idempotency keys (client-generated UUID) with UNIQUE database constraint. Without this, user clicking 'Place Order' twice = double charge and two food deliveries."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How to find nearby restaurants?'. Answer: Elasticsearch geo_distance query for proximity + filters + text search. Cache popular queries in Redis (TTL=5 min). Alternative: PostgreSQL PostGIS for exact distance, Redis GEORADIUS for real-time drivers."
              },
              {
                "type": "tip",
                "text": "Key optimization: Store cart in Redis, not database. Operations are 50x faster (<1ms vs 50ms), scales to millions of users, auto-expires inactive carts after 24h. Serialize to PostgreSQL only on order placement."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Distributed lock for driver assignment. Redis SETNX lock:driver:{id} prevents double assignment when two orders match same driver concurrently. Lock expires in 60s to prevent deadlock."
              },
              {
                "type": "warning",
                "text": "NEVER update driver location in real-time (every second). Use 10-20s interval to reduce load. 100K drivers \u00d7 1 update/sec = 100K writes/sec vs 100K drivers \u00d7 1 update/10s = 10K writes/sec (10x reduction)."
              },
              {
                "type": "tip",
                "text": "Event-driven with Kafka: Order lifecycle events (placed, accepted, assigned, delivered) published to Kafka. Enables async processing, decouples services, supports replay for debugging. All status updates flow through Kafka."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'Real-time tracking implementation'. Answer: Driver updates location \u2192 Kafka \u2192 Redis Pub/Sub \u2192 WebSocket Gateway \u2192 User's WebSocket. Alternative: HTTP polling every 10s as fallback. ETA from Google Maps API with traffic."
              },
              {
                "type": "warning",
                "text": "NEVER use strong consistency for search. Elasticsearch eventual consistency (~1s refresh) acceptable. Use strong consistency (ACID) only for payments and order placement. Trade-off: availability vs consistency."
              },
              {
                "type": "tip",
                "text": "Multi-tier caching: (1) Redis for hot queries (70% hit rate, <5ms), (2) Elasticsearch for complex search (~50ms), (3) PostgreSQL for source of truth (~100ms). Serves 70% from cache, 95% from ES, 5% from DB."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Driver matching algorithm. (1) Find candidates via GEORADIUS within 5km, (2) Filter (online, available, suitable vehicle), (3) Score: proximity 40% + rating 30% + acceptance_rate 20% + idle_time 10%, (4) Assign to highest score with distributed lock."
              }
            ],
            "tags": [
              "system-design",
              "food-delivery",
              "Zomato",
              "Swiggy",
              "Uber-Eats",
              "proximity-search",
              "Elasticsearch-geo",
              "driver-matching",
              "real-time-tracking",
              "WebSocket",
              "cart-management",
              "Redis",
              "Kafka",
              "payment-integration",
              "order-management",
              "geospatial",
              "GEORADIUS",
              "idempotency",
              "event-driven"
            ]
          }
        },
        {
          "id": "07",
          "title": "System Design 7: Design Proximity Search | Quad Tree vs Geohash vs PostGIS vs Elasticsearch | HLD",
          "videoUrl": "https://youtu.be/6K6ihHNkO_0",
          "description": "Unlock the power of proximity search! In this video, we dive deep into multiple techniques for implementing location-based search",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/ub2ou4btu24h8.png?updatedAt=1747166699726",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/497571165_24020210430896276_2961534887528530562_n.jpg?updatedAt=1747167465961",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "Proximity Search Algorithm (Find Nearby Restaurants/Drivers)",
              "oneLinerToMemorize": "QuadTree/Geohash spatial indexing \u2192 PostgreSQL PostGIS (GIST) \u2192 Elasticsearch geo_distance \u2192 Redis GEORADIUS \u2192 Haversine distance"
            },
            "sections": [
              {
                "id": "problem-statement",
                "title": "1. Problem Statement",
                "type": "bullets",
                "items": [
                  "Goal: Find all points of interest (restaurants, drivers, hotels) within radius R of user location (lat, lng)",
                  "Example: User at (37.7749, -122.4194) searches 5km radius \u2192 return all restaurants within 5km sorted by distance",
                  "Requirements: <100ms query latency, support millions of locations, accurate distance calculations",
                  "Use cases: Uber (find drivers), Yelp (find restaurants), Airbnb (find hotels), Google Maps (nearby POIs)"
                ]
              },
              {
                "id": "approaches-overview",
                "title": "2. Five Main Approaches",
                "type": "bullets",
                "items": [
                  "Approach 1: QuadTree - Recursive grid subdivision into 4 quadrants, in-memory spatial index",
                  "Approach 2: Geohash - Encode lat/lng as base32 string, prefix matching with SQL LIKE queries",
                  "Approach 3: PostgreSQL with PostGIS - GEOGRAPHY type, ST_DWithin for radius, GIST spatial index",
                  "Approach 4: Elasticsearch - Distributed search with geo_point mapping and geo_distance filter",
                  "Approach 5: Redis Geospatial - In-memory GEORADIUS command for real-time moving objects"
                ]
              },
              {
                "id": "quadtree-approach",
                "title": "3. Approach 1: QuadTree",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "QuadTree Structure",
                    "steps": [
                      "Concept: Recursively divide 2D space into 4 quadrants (NW, NE, SW, SE) until each cell contains \u2264 max_capacity points (e.g., 100 points per leaf node)",
                      "Tree structure: Each internal node has 4 children, leaf nodes store actual location points",
                      "Example: World map \u2192 divide into 4 quadrants \u2192 each quadrant divides into 4 \u2192 continues until depth limit (e.g., 20 levels) or capacity reached",
                      "Bounding box: Each node stores min/max lat/lng defining its geographic bounds"
                    ]
                  },
                  {
                    "blockTitle": "Search Algorithm",
                    "steps": [
                      "Input: Query point (lat, lng) and radius R",
                      "Step 1: Start at root node, check if search circle intersects node's bounding box",
                      "Step 2: If yes, recursively search all 4 children; if no, prune entire subtree (skip)",
                      "Step 3: At leaf nodes, collect all points and calculate exact distance using Haversine formula",
                      "Step 4: Filter points where distance \u2264 R, sort by distance, return top N results",
                      "Time complexity: O(log N + K) where K is number of results, pruning eliminates 75%+ of tree"
                    ]
                  },
                  {
                    "blockTitle": "Insertion & Updates",
                    "steps": [
                      "Insert: Find appropriate leaf node by traversing tree based on coordinates, add point to leaf",
                      "Split: If leaf exceeds capacity (100 points), split into 4 children, redistribute points",
                      "Update: For moving objects (drivers), remove old location, insert new location (2 operations)",
                      "Delete: Mark point as deleted, periodically rebuild tree to reclaim space"
                    ]
                  },
                  {
                    "blockTitle": "Pros & Cons",
                    "steps": [
                      "Pros: (1) Fast queries O(log N) for balanced tree, (2) Efficient for sparse data, (3) Easy to implement in-memory, (4) Good for dynamic data (frequent inserts/deletes)",
                      "Cons: (1) Not suitable for distributed systems (tree on single server), (2) Memory overhead for internal nodes, (3) Tree rebalancing on splits, (4) Lost on server restart unless persisted",
                      "Best for: Game engines, in-memory caches for hot regions, <1M points on single server",
                      "Not for: Production databases (use PostGIS), billions of points (use Elasticsearch), distributed systems"
                    ]
                  }
                ]
              },
              {
                "id": "geohash-approach",
                "title": "4. Approach 2: Geohash",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Geohash Encoding Algorithm",
                    "steps": [
                      "Step 1: Interleave latitude and longitude bits - lat=37.7749 (binary: 01011010...), lng=-122.4194 (binary: 10011101...) \u2192 interleave \u2192 0110011101010...",
                      "Step 2: Convert binary to base32 (0-9, a-z excluding a, i, l, o) \u2192 '9q8yy' for San Francisco",
                      "Example: SF (37.7749, -122.4194) \u2192 geohash='9q8yy9mz' (8 characters)",
                      "Precision: Length 1 = \u00b12500km, 3 = \u00b178km, 5 = \u00b12.4km, 7 = \u00b176m, 9 = \u00b12m (see table in image)"
                    ]
                  },
                  {
                    "blockTitle": "Proximity Property",
                    "steps": [
                      "Key property: Nearby locations share common prefix",
                      "Example: '9q8yy9mz' (SF downtown), '9q8yy9mt' (1 block away) \u2192 share prefix '9q8yy9m'",
                      "Hierarchical: Zoom levels on maps - zoom out \u2192 use shorter prefix (9q8 = SF area), zoom in \u2192 longer prefix (9q8yy9m = street level)",
                      "This enables efficient SQL queries with LIKE operator on indexed geohash column"
                    ]
                  },
                  {
                    "blockTitle": "Database Schema & Query",
                    "steps": [
                      "Schema: CREATE TABLE restaurants (id SERIAL, name VARCHAR, lat DECIMAL, lng DECIMAL, geohash VARCHAR(12))",
                      "Index: CREATE INDEX idx_geohash ON restaurants (geohash)",
                      "Query: SELECT * FROM restaurants WHERE geohash LIKE '9q8yy%' (returns all in ~2.4km box)",
                      "Radius to precision: 500m \u2192 use 6 chars (\u00b1610m), 2km \u2192 5 chars (\u00b12.4km), 5km \u2192 4 chars (\u00b120km)"
                    ]
                  },
                  {
                    "blockTitle": "Edge Case: Neighbor Cells",
                    "steps": [
                      "Problem: Two points 50m apart on geohash boundary have different prefixes \u2192 query misses one",
                      "Example: Point A: '9q8yyzzz', Point B: '9q8z0000' (different 5th character, but only 50m apart)",
                      "Solution: Calculate 8 neighboring geohash cells (N, NE, E, SE, S, SW, W, NW) using geohash library",
                      "Query expansion: WHERE geohash IN ('9q8yy', '9q8yz', '9q8yx', '9q8yw', '9q8yv', '9q8yt', '9q8ys', '9q8yq', '9q8yu') (9 cells total)",
                      "Post-filter: Calculate exact Haversine distance for all results, filter to points actually within circular radius"
                    ]
                  },
                  {
                    "blockTitle": "Pros & Cons",
                    "steps": [
                      "Pros: (1) Simple SQL with B-tree index (no special extensions), (2) Works with any database (MySQL, PostgreSQL, MongoDB), (3) Human-readable (can share geohash in URLs), (4) Hierarchical for zoom levels",
                      "Cons: (1) Returns square area not circle (must post-filter), (2) Edge case requires 9 cell queries, (3) Approximate (not exact distance), (4) Geohash cell size varies by latitude",
                      "Best for: Simple databases without spatial extensions, distributed systems (easy to shard), approximate searches",
                      "Example query: SELECT * FROM restaurants WHERE geohash LIKE '9q8yy%' \u2192 500 candidates \u2192 Haversine filter \u2192 200 within exact 5km radius"
                    ]
                  }
                ]
              },
              {
                "id": "postgis-approach",
                "title": "5. Approach 3: PostgreSQL with PostGIS",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "PostGIS Setup",
                    "steps": [
                      "Install extension: CREATE EXTENSION postgis;",
                      "Data type: GEOGRAPHY(POINT, 4326) for lat/lng on sphere (accurate distances in meters) or GEOMETRY for planar coordinates (faster but less accurate)",
                      "SRID 4326: WGS 84 coordinate system (standard GPS coordinates)",
                      "Example: CREATE TABLE restaurants (id SERIAL, name VARCHAR, location GEOGRAPHY(POINT, 4326))"
                    ]
                  },
                  {
                    "blockTitle": "Spatial Index: GIST",
                    "steps": [
                      "Create index: CREATE INDEX idx_location ON restaurants USING GIST (location)",
                      "GIST: Generalized Search Tree, creates R-tree structure, groups nearby points into bounding boxes hierarchically",
                      "Index structure: Root node \u2192 bounding box covering all points \u2192 children cover subregions \u2192 leaves contain individual points",
                      "Query optimization: Index scan O(log N) instead of sequential scan O(N)"
                    ]
                  },
                  {
                    "blockTitle": "Radius Query with ST_DWithin",
                    "steps": [
                      "Insert data: INSERT INTO restaurants (name, location) VALUES ('Taco Bell', ST_GeomFromText('POINT(77.5946 12.9716)', 4326))",
                      "Query: SELECT name, ST_Distance(location, ST_GeogFromText('SRID=4326;POINT(77.6080 12.9700)')) AS distance FROM restaurants WHERE ST_DWithin(location, ST_GeogFromText('SRID=4326;POINT(77.6080 12.9700)'), 5000) ORDER BY distance LIMIT 20",
                      "ST_DWithin: Pre-filter using bounding box (fast), then exact distance check (slower but accurate)",
                      "ST_Distance: Returns distance in meters for GEOGRAPHY type, uses Vincenty formula (accurate to \u00b10.5mm)",
                      "Performance: ~50-100ms for 10M points with GIST index"
                    ]
                  },
                  {
                    "blockTitle": "Polygon & Complex Shapes",
                    "steps": [
                      "Polygon search: SELECT * FROM restaurants WHERE ST_Contains(ST_GeomFromGeoJSON('{polygon}'), location)",
                      "Use case: Find restaurants in specific neighborhood, delivery zones with irregular boundaries",
                      "GeoJSON polygon: { 'type': 'Polygon', 'coordinates': [[[lng1, lat1], [lng2, lat2], [lng3, lat3], [lng1, lat1]]] }",
                      "ST_Intersects: Check if point intersects with line/polygon",
                      "ST_Buffer: Create buffer zone around point/line (e.g., 100m buffer around road)"
                    ]
                  },
                  {
                    "blockTitle": "Pros & Cons",
                    "steps": [
                      "Pros: (1) Exact distance calculations (Vincenty formula), (2) ACID guarantees, (3) Complex spatial queries (polygons, buffers, intersections), (4) Mature ecosystem, well-documented",
                      "Cons: (1) PostgreSQL-specific (not portable), (2) Single server bottleneck (harder to shard), (3) Slower than Redis for real-time, (4) Requires extension install",
                      "Best for: Source of truth for location data, static locations (restaurants, hotels), complex spatial operations",
                      "Scaling: Shard by region (US-West DB, US-East DB), read replicas for queries"
                    ]
                  }
                ]
              },
              {
                "id": "elasticsearch-approach",
                "title": "6. Approach 4: Elasticsearch Geo Queries",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Index Mapping Setup",
                    "steps": [
                      "Create index: PUT /restaurants { 'mappings': { 'properties': { 'location': { 'type': 'geo_point' }, 'name': { 'type': 'text' }, 'category': { 'type': 'keyword' }, 'rating': { 'type': 'float' } } } }",
                      "geo_point: Stores lat/lng, automatically creates spatial index (BKD tree internally)",
                      "Index document: POST /restaurants/_doc { 'name': 'Taco Bell', 'location': { 'lat': 12.9716, 'lon': 77.5946 }, 'category': 'Fast Food', 'rating': 4.2 }"
                    ]
                  },
                  {
                    "blockTitle": "Geo Distance Query",
                    "steps": [
                      "Basic query: GET /restaurants/_search { 'query': { 'geo_distance': { 'distance': '5km', 'location': { 'lat': 12.97, 'lon': 77.59 } } } }",
                      "Sort by distance: 'sort': [ { '_geo_distance': { 'location': { 'lat': 12.97, 'lon': 77.59 }, 'order': 'asc', 'unit': 'km' } } ]",
                      "Response includes: { '_source': { 'name': 'Taco Bell' }, 'sort': [ 0.5 ] } (0.5 km distance)",
                      "Distance units: km, mi, m (meters), ft (feet)"
                    ]
                  },
                  {
                    "blockTitle": "Combined Filters (Proximity + Text + Facets)",
                    "steps": [
                      "Complex query: { 'query': { 'bool': { 'must': [ { 'match': { 'name': 'pizza' } } ], 'filter': [ { 'geo_distance': { 'distance': '5km', 'location': {...} } }, { 'term': { 'category': 'Italian' } }, { 'range': { 'rating': { 'gte': 4.0 } } } ] } } }",
                      "Query execution: (1) Full-text search on 'pizza' \u2192 10K candidates, (2) Filter by category \u2192 2K, (3) Filter by rating \u2192 1K, (4) geo_distance \u2192 50 results",
                      "Aggregations: 'aggs': { 'distance_buckets': { 'geo_distance': { 'field': 'location', 'origin': {...}, 'ranges': [ {'to': 1}, {'from': 1, 'to': 3}, {'from': 3, 'to': 5} ] } } } \u2192 returns count per distance bucket",
                      "Use case: 'Pizza restaurants within 5km, rating \u22654.0, categorized by distance ranges'"
                    ]
                  },
                  {
                    "blockTitle": "Geo Bounding Box (Faster for Large Results)",
                    "steps": [
                      "Query: { 'query': { 'geo_bounding_box': { 'location': { 'top_left': { 'lat': 13.0, 'lon': 77.5 }, 'bottom_right': { 'lat': 12.9, 'lon': 77.7 } } } } }",
                      "Faster than geo_distance for rectangular areas (no distance calculation)",
                      "Use case: Find all restaurants on map viewport (when user scrolls map)",
                      "Optimization: Use geo_bounding_box to get candidates, then geo_distance for exact circular radius"
                    ]
                  },
                  {
                    "blockTitle": "Pros & Cons",
                    "steps": [
                      "Pros: (1) Horizontally scalable (shard across nodes), (2) Combine proximity + full-text search, (3) Fast aggregations/facets, (4) ~10-50ms latency for 100M documents",
                      "Cons: (1) Eventual consistency (~1s refresh interval), (2) More expensive (memory intensive), (3) Complex setup, (4) No ACID guarantees",
                      "Best for: Search engines (Yelp, Google Maps), complex filtering, billions of documents, read-heavy workloads",
                      "Scaling: Auto-shards based on document count, can scale to petabytes of data"
                    ]
                  }
                ]
              },
              {
                "id": "redis-geospatial",
                "title": "7. Approach 5: Redis Geospatial",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Redis Geo Commands",
                    "steps": [
                      "Add location: GEOADD drivers 77.5946 12.9716 'driver_1' 77.6000 12.9800 'driver_2' \u2192 stores in sorted set with geohash as score",
                      "Search radius: GEORADIUS drivers 77.5900 12.9700 5 km WITHDIST ASC COUNT 10 \u2192 returns [('driver_1', '0.5 km'), ('driver_2', '1.2 km')]",
                      "Search by member: GEORADIUSBYMEMBER drivers 'driver_1' 2 km \u2192 find all drivers within 2km of driver_1",
                      "Get position: GEOPOS drivers 'driver_1' \u2192 returns [(77.5946, 12.9716)]",
                      "Distance between: GEODIST drivers 'driver_1' 'driver_2' km \u2192 returns 1.2 (km)"
                    ]
                  },
                  {
                    "blockTitle": "Update & Delete",
                    "steps": [
                      "Update location: Just GEOADD again with new coordinates (upsert operation)",
                      "Example: Driver moves \u2192 GEOADD drivers 77.6100 12.9850 'driver_1' \u2192 updates existing entry",
                      "Delete: ZREM drivers 'driver_1' (geospatial data stored as sorted set internally)",
                      "Expire: Set TTL on key \u2192 EXPIRE drivers:online 3600 (auto-delete after 1 hour if no updates)"
                    ]
                  },
                  {
                    "blockTitle": "Performance & Scaling",
                    "steps": [
                      "Time complexity: O(log N + M) where N = total points, M = results returned",
                      "Latency: ~1ms for 10K points, ~5ms for 100K points, ~10ms for 1M points (all in-memory)",
                      "Memory: ~50 bytes per point (lat, lng, member name, geohash) \u2192 1M points \u2248 50MB",
                      "Sharding: Use multiple Redis instances, shard by region or geohash prefix (drivers:sf, drivers:nyc)",
                      "Persistence: Use RDB snapshots or AOF for durability, but typically used for ephemeral hot data"
                    ]
                  },
                  {
                    "blockTitle": "Pros & Cons",
                    "steps": [
                      "Pros: (1) Fastest queries <10ms, (2) Real-time updates (drivers moving), (3) Simple API, (4) Perfect for hot/ephemeral data",
                      "Cons: (1) In-memory only (expensive for millions of points), (2) No complex filtering, (3) Data lost on restart (unless persisted), (4) Limited query capabilities",
                      "Best for: Real-time moving objects (Uber drivers, delivery agents), active sessions, frequently accessed locations",
                      "Hybrid approach: Redis for hot data (active drivers) + PostgreSQL for cold data (all drivers) + sync every 60s"
                    ]
                  }
                ]
              },
              {
                "id": "haversine-distance",
                "title": "8. Haversine Distance Formula",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Formula & Implementation",
                    "steps": [
                      "Haversine formula: Calculates great-circle distance between two points on sphere (Earth)",
                      "Formula: a = sin\u00b2(\u0394lat/2) + cos(lat1) \u00d7 cos(lat2) \u00d7 sin\u00b2(\u0394lng/2), c = 2 \u00d7 atan2(\u221aa, \u221a(1\u2212a)), distance = R \u00d7 c",
                      "R = Earth's radius: 6371 km (or 3959 miles)",
                      "Example: SF (37.7749, -122.4194) to LA (34.0522, -118.2437) \u2192 Haversine \u2192 559 km",
                      "Accuracy: \u00b10.5% error (assumes perfect sphere, Earth is actually oblate spheroid)"
                    ]
                  },
                  {
                    "blockTitle": "Optimization: Bounding Box Pre-filter",
                    "steps": [
                      "Problem: Computing Haversine for 1M points takes ~1 second CPU time",
                      "Solution: Pre-filter with bounding box \u2192 eliminate 90% of points \u2192 Haversine only for 10%",
                      "Bounding box: WHERE lat BETWEEN (user_lat - delta) AND (user_lat + delta) AND lng BETWEEN (user_lng - delta) AND (user_lng + delta)",
                      "Delta calculation: delta \u2248 radius_km / 111 (rough conversion: 1 degree \u2248 111 km)",
                      "Example: 5km radius \u2192 delta = 5/111 \u2248 0.045 degrees \u2192 bounding box filters from 1M to 10K \u2192 Haversine for 10K \u2192 <10ms"
                    ]
                  },
                  {
                    "blockTitle": "Alternatives",
                    "steps": [
                      "Vincenty formula: More accurate (accounts for ellipsoid), \u00b10.5mm error, but 10x slower computation",
                      "Euclidean distance: sqrt((x2-x1)\u00b2 + (y2-y1)\u00b2), only for small areas on flat projection, very fast but inaccurate for large distances",
                      "Manhattan distance: |x2-x1| + |y2-y1|, rough approximation, useful for quick filtering before exact calculation",
                      "Database functions: PostGIS ST_Distance (uses Vincenty for GEOGRAPHY), Elasticsearch (uses Haversine), Redis GEODIST (Haversine)"
                    ]
                  }
                ]
              },
              {
                "id": "comparison-table",
                "title": "9. Approach Comparison",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Decision Matrix",
                    "pairs": [
                      {
                        "k": "QuadTree",
                        "v": "When: In-memory single server, <1M points, game engines | Latency: <1ms | Accuracy: Exact | Complexity: High | Scaling: Vertical only"
                      },
                      {
                        "k": "Geohash",
                        "v": "When: Simple SQL database, distributed system, approximate OK | Latency: 10-50ms | Accuracy: Approximate (box) | Complexity: Low | Scaling: Easy (shard by prefix)"
                      },
                      {
                        "k": "PostGIS",
                        "v": "When: Static data, ACID needed, complex queries | Latency: 50-100ms | Accuracy: Exact (Vincenty) | Complexity: Medium | Scaling: Moderate (shard by region)"
                      },
                      {
                        "k": "Elasticsearch",
                        "v": "When: Billions of points, text+proximity search | Latency: 10-50ms | Accuracy: Exact (Haversine) | Complexity: Medium | Scaling: Excellent (horizontal)"
                      },
                      {
                        "k": "Redis Geo",
                        "v": "When: Real-time moving objects, hot data | Latency: <10ms | Accuracy: Exact (Haversine) | Complexity: Low | Scaling: Good (shard by region)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "hybrid-architecture",
                "title": "10. Production Hybrid Architecture",
                "type": "bullets",
                "items": [
                  "Tier 1 - Redis (Hot Cache): Top 1000 active/popular locations per city, TTL=10 min, serves 80% of queries in <5ms, GEORADIUS for real-time",
                  "Tier 2 - Elasticsearch (Search Layer): 100M+ documents, complex filters (category, rating, text search), ~50ms latency, handles cache misses with complex requirements",
                  "Tier 3 - PostgreSQL PostGIS (Source of Truth): All locations, detailed metadata, ~100ms latency, handles writes and rare queries, ACID guarantees",
                  "Write flow: Client updates location \u2192 PostgreSQL (primary) \u2192 async indexer \u2192 Elasticsearch \u2192 hot locations cached in Redis",
                  "Query flow: Check Redis cache \u2192 if miss, query Elasticsearch with filters \u2192 if still miss or need exact metadata, query PostGIS \u2192 cache hot results in Redis",
                  "Real-time updates: Moving objects (drivers) update Redis every 5s, batch sync to PostgreSQL every 60s",
                  "Invalidation: On restaurant update \u2192 invalidate Redis keys, reindex in Elasticsearch via Kafka event stream"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "Explain geohash and how to handle edge cases at cell boundaries.",
                    "a": "Geohash encoding: (1) Interleave lat/lng bits \u2192 binary string \u2192 convert to base32 \u2192 'sgkztus' (7 chars \u2248 \u00b176m), (2) Precision table: length 5 = \u00b12.4km, length 7 = \u00b176m, length 9 = \u00b12m. Proximity property: Nearby locations share common prefix ('sgkztus', 'sgkztur' both in SF area). Edge case problem: Two points 50m apart on cell boundary have different hashes ('sgkztzzz' vs 'sgku000' - different 5th char). Solution: (1) Calculate 8 neighbor geohash cells using library (N, NE, E, SE, S, SW, W, NW), (2) Query all 9 cells (center + 8 neighbors): WHERE geohash IN ('sgkzt', 'sgkzu', 'sgkzs', ...) - 9 total, (3) Post-filter with Haversine to get exact circular radius. Optimization: Use larger geohash precision (fewer chars) to get bigger box that definitely contains circle, then filter. Example: 5km radius \u2192 use precision 5 (\u00b12.4km cell) \u2192 single query \u2192 Haversine filter \u2192 exact results."
                  },
                  {
                    "q": "Compare QuadTree vs Geohash vs PostGIS - when to use each?",
                    "a": "QuadTree: (1) Structure: Recursive 4-way partitioning, each node has NW/NE/SW/SE children, (2) Search: O(log n) traversal, prune subtrees that don't intersect search circle, (3) Use: In-memory single server (<1M points), game engines, dynamic data, (4) Pros: Fast (<1ms), easy updates, (5) Cons: Not distributable, lost on restart, complex implementation. Geohash: (1) Encoding: Lat/lng \u2192 base32 string, prefix matching, (2) Search: SQL LIKE 'prefix%' with B-tree index, query 9 cells (center + 8 neighbors), (3) Use: Simple SQL databases, distributed systems, approximate OK, (4) Pros: Works with any DB, easy to shard, human-readable, (5) Cons: Returns box not circle, edge cases. PostGIS: (1) Extension: GEOGRAPHY type, ST_DWithin for radius, GIST spatial index, (2) Search: O(log n) with R-tree, exact Vincenty distance, (3) Use: Static data, ACID needed, complex shapes (polygons), (4) Pros: Exact distance, mature, complex queries, (5) Cons: PostgreSQL-only, harder to shard, ~100ms latency. Decision: QuadTree for single-server in-memory, Geohash for distributed approximate, PostGIS for ACID + exact distance. Production: Hybrid - Redis Geo (hot data) + Elasticsearch (search) + PostGIS (source of truth)."
                  },
                  {
                    "q": "How do you optimize proximity search for millions of locations?",
                    "a": "Multi-layer optimization: (1) Spatial indexing: Use GIST index (PostGIS), B-tree on geohash, or Redis GEORADIUS - reduces from O(n) scan to O(log n) lookup, (2) Bounding box pre-filter: Calculate lat/lng bounds, WHERE lat BETWEEN (lat-delta) AND (lat+delta) AND lng BETWEEN (lng-delta) AND (lng+delta), eliminates 90% before expensive distance calc, delta = radius_km / 111, (3) Caching: Redis cache for hot locations (top 1000 per city), TTL=10 min, serves 80% queries in <5ms, (4) Geographic sharding: Partition by region (US-West, US-East, EU) or geohash prefix, reduces per-shard dataset by 5-10x, (5) Materialized views: Pre-compute popular queries (restaurants by city + 5km radius), refresh hourly, (6) Read replicas: PostgreSQL replicas in each region for geo queries, route to nearest replica, (7) Lazy distance calc: Return top N by bounding box first, calculate exact distance only for displayed results, (8) Result limit: LIMIT 20 instead of returning all matches (user only sees first page anyway). Example: 10M restaurants \u2192 geohash prefix filter \u2192 10K candidates \u2192 bounding box \u2192 1K candidates \u2192 Haversine \u2192 200 within exact radius \u2192 sort \u2192 return top 20 \u2192 total latency 50ms vs 5000ms without optimization."
                  },
                  {
                    "q": "Implement proximity search with PostgreSQL PostGIS - show schema and query.",
                    "a": "Setup: (1) Install: CREATE EXTENSION postgis;, (2) Schema: CREATE TABLE restaurants (id SERIAL PRIMARY KEY, name VARCHAR(255), location GEOGRAPHY(POINT, 4326), category VARCHAR(100), rating DECIMAL(2,1)), (3) Index: CREATE INDEX idx_location_gist ON restaurants USING GIST (location), (4) Insert: INSERT INTO restaurants (name, location, category, rating) VALUES ('Taco Bell', ST_GeomFromText('POINT(77.5946 12.9716)', 4326), 'Fast Food', 4.2). Query: SELECT id, name, category, rating, ST_Distance(location, ST_GeogFromText('SRID=4326;POINT(77.6080 12.9700)')) AS distance_meters FROM restaurants WHERE ST_DWithin(location, ST_GeogFromText('SRID=4326;POINT(77.6080 12.9700)'), 5000) AND category = 'Fast Food' AND rating >= 4.0 ORDER BY distance_meters LIMIT 20; Explanation: (1) ST_GeogFromText: Creates geography point from user location (77.6080, 12.9700), (2) ST_DWithin: Pre-filters using bounding box, then exact distance check \u2264 5000 meters, (3) ST_Distance: Calculates exact distance in meters using Vincenty formula, (4) GIST index: Enables O(log n) spatial lookup, (5) Additional filters: category and rating for combined search, (6) Performance: ~50-100ms for 10M rows. Alternative using geometry (faster but less accurate): Change GEOGRAPHY to GEOMETRY, use ST_MakePoint(lng, lat) instead of ST_GeomFromText, distance in degrees not meters."
                  },
                  {
                    "q": "How does Redis GEORADIUS work internally and when should you use it?",
                    "a": "Internal implementation: (1) Data structure: Redis stores geospatial data as sorted set (ZSET), score = geohash of location, member = location ID, (2) Geohash encoding: Internally encodes lat/lng to 52-bit geohash integer, (3) GEORADIUS algorithm: (a) Calculate geohash of center point, (b) Determine geohash prefix range for radius, (c) ZRANGEBYLEX to get all members in geohash range (O(log n)), (d) Calculate exact Haversine distance for each candidate, (e) Filter to points within radius, sort by distance, (4) Time complexity: O(log n + m) where m = results. Commands: (1) GEOADD key lng lat member: Add/update location, (2) GEORADIUS key lng lat radius km WITHDIST ASC COUNT 10: Search circle, return distance sorted, (3) GEORADIUSBYMEMBER key member radius km: Find neighbors of existing member, (4) GEODIST key member1 member2 km: Distance between two members. When to use: (1) Real-time moving objects (drivers, delivery agents update every 5s), (2) Hot/ephemeral data (active sessions, current locations), (3) <10ms latency requirement, (4) <10M points (memory constraint), (5) High read frequency (100K queries/sec). Don't use for: (1) Billions of points (too expensive in RAM), (2) Complex filtering (category, rating) - use Elasticsearch, (3) ACID persistence - use PostGIS. Hybrid pattern: Redis for active drivers (10K online) + PostgreSQL for all drivers (1M total), sync every 60s."
                  },
                  {
                    "q": "How do you calculate Haversine distance and why use bounding box pre-filter?",
                    "a": "Haversine formula: (1) Purpose: Calculate great-circle distance between two points on sphere (Earth), (2) Inputs: Point A (lat1, lng1), Point B (lat2, lng2), Earth radius R = 6371 km, (3) Formula: a = sin\u00b2((lat2-lat1)/2) + cos(lat1)\u00b7cos(lat2)\u00b7sin\u00b2((lng2-lng1)/2), c = 2\u00b7atan2(\u221aa, \u221a(1-a)), distance = R\u00b7c, (4) Implementation: Convert degrees to radians (multiply by \u03c0/180), apply trig functions, result in km, (5) Example: SF (37.7749, -122.4194) to LA (34.0522, -118.2437): \u0394lat = -3.7227 rad, \u0394lng = 4.1757 rad \u2192 a = 0.0382 \u2192 c = 0.391 \u2192 distance = 6371 \u00d7 0.391 = 559 km. Accuracy: \u00b10.5% error (assumes sphere, Earth is ellipsoid). Bounding box optimization: (1) Problem: Computing Haversine for 1M points = 1M trig calculations = ~1 second CPU time, (2) Solution: Pre-filter with simple lat/lng range check: WHERE lat BETWEEN (user_lat - delta) AND (user_lat + delta) AND lng BETWEEN (user_lng - delta) AND (user_lng + delta), (3) Delta: delta = radius_km / 111 (rough: 1 degree \u2248 111 km), for 5km radius \u2192 delta = 0.045 degrees, (4) Effect: Eliminates 90-95% of points using index scan (fast), (5) Final step: Haversine only for remaining 5-10% \u2192 total time <10ms. Example: 1M points, 5km radius \u2192 bounding box reduces to 10K candidates \u2192 Haversine for 10K \u2192 <10ms vs 1000ms without optimization."
                  },
                  {
                    "q": "Design Elasticsearch mapping and query for 'pizza restaurants within 5km, rating \u22654'.",
                    "a": "Index mapping: PUT /restaurants { 'mappings': { 'properties': { 'name': { 'type': 'text', 'analyzer': 'standard' }, 'category': { 'type': 'keyword' }, 'location': { 'type': 'geo_point' }, 'rating': { 'type': 'float' }, 'price_range': { 'type': 'keyword' }, 'is_open': { 'type': 'boolean' } } } }. Index document: POST /restaurants/_doc { 'name': 'Taco Bell', 'category': 'Fast Food', 'location': { 'lat': 12.9716, 'lon': 77.5946 }, 'rating': 4.2, 'price_range': '$', 'is_open': true }. Search query: POST /restaurants/_search { 'query': { 'bool': { 'must': [ { 'match': { 'name': 'pizza' } } ], 'filter': [ { 'geo_distance': { 'distance': '5km', 'location': { 'lat': 12.97, 'lon': 77.59 } } }, { 'range': { 'rating': { 'gte': 4.0 } } }, { 'term': { 'is_open': true } } ] } }, 'sort': [ { '_geo_distance': { 'location': { 'lat': 12.97, 'lon': 77.59 }, 'order': 'asc', 'unit': 'km' } } ], 'size': 20 }. Result: { 'hits': { 'hits': [ { '_source': { 'name': 'Pizza Hut', 'rating': 4.5 }, 'sort': [ 0.8 ] } ] } } (0.8 km distance). Aggregations for facets: 'aggs': { 'price_facets': { 'terms': { 'field': 'price_range' } }, 'distance_buckets': { 'geo_distance': { 'field': 'location', 'origin': { 'lat': 12.97, 'lon': 77.59 }, 'ranges': [ {'to': 1}, {'from': 1, 'to': 3}, {'from': 3, 'to': 5} ] } } }. Performance: ~20-50ms for 100M documents, scales horizontally."
                  },
                  {
                    "q": "How do you shard geospatial data for horizontal scaling?",
                    "a": "Sharding strategies: (1) Geographic sharding: Divide by region (US-West, US-East, EU-West, EU-East, Asia-Pacific), each region = separate database/cluster, route queries based on location bounds, Pros: Simple routing, isolated failures, Cons: Unbalanced load (NYC has more restaurants than Montana), cross-region queries complex. (2) Geohash prefix sharding: Shard by first N characters of geohash (e.g., first 2 chars = 1024 possible prefixes \u2192 16 shards), use consistent hashing to map geohash prefix to shard, Pros: Even distribution, Cons: Radius queries may span multiple shards. (3) Hybrid: Regions + geohash - shard by region first (US-East), then by geohash prefix within region, Example: US-East shard 1: geohashes starting with '9q', '9r', US-East shard 2: '9w', '9x'. Query routing: (1) Extract location from query, (2) Determine geohash or region, (3) Route to appropriate shard(s), (4) If query spans multiple shards (large radius near boundary), fan out to all relevant shards, merge results. Example: User at NYC (40.7128, -74.0060) searches 10km \u2192 all results in US-East shard, User at Philadelphia (border of US-East/US-Mid) searches 50km \u2192 query both shards, merge by distance. Cross-shard complexity: (1) Small radius (<10km): Usually hits 1-2 shards, (2) Large radius (>50km): May hit 3-5 shards, scatter-gather pattern. Performance: Per-shard dataset reduced 10x (10M \u2192 1M), query latency improved 5x (500ms \u2192 100ms). Elasticsearch: Auto-shards by document ID, manual routing by geohash possible using custom routing key."
                  },
                  {
                    "q": "What's your caching strategy for proximity searches?",
                    "a": "Multi-layer caching: (1) Result cache: Cache entire query results in Redis, key = hash(lat, lng, radius, filters), value = [location_ids], TTL=5 min, handles identical repeated queries, Example: search: {lat: 37.77, lng: -122.42, radius: 5km, category: Italian} \u2192 cache key: hash(37.77:-122.42:5000:Italian) \u2192 value: [id1, id2, id3, ...], (2) Location cache: Cache popular/active locations in Redis GEOSPATIAL, GEOADD restaurants:hot:{city_id}, TTL=10 min, serves 80% queries via GEORADIUS in <5ms, Eviction: LRU (Least Recently Used), keep top 1000 per city. (3) Application cache: In-memory cache (Caffeine, Guava) in API servers for very frequent queries, 100-element LRU cache, serves in <1ms without network hop. (4) CDN cache: For static location data (restaurant metadata, images), cache at edge locations globally. Cache warming: (1) Background job analyzes query logs, (2) Identifies top 100 queries per city by frequency, (3) Pre-populates Redis cache before peak hours. Invalidation: (1) On location update: Invalidate all cache keys containing that location, Use Redis Pub/Sub or Kafka to broadcast invalidation event, (2) Pattern matching: Use Redis SCAN to find keys matching location_id, DEL all matching keys, (3) Time-based: Regardless of updates, expire cache entries after TTL to prevent indefinite staleness. Example cache hit flow: User searches \u2192 check Redis result cache \u2192 hit (identical query 2 min ago) \u2192 return cached results \u2192 <5ms latency. Cache miss flow: \u2192 query Elasticsearch \u2192 50ms \u2192 cache result \u2192 return to user."
                  },
                  {
                    "q": "How do you handle polygon-based searches (e.g., restaurants in a neighborhood)?",
                    "a": "Polygon search implementation: (1) PostGIS: SELECT * FROM restaurants WHERE ST_Contains(ST_GeomFromGeoJSON('{polygon}'), location), polygon format: GeoJSON {'type': 'Polygon', 'coordinates': [[[lng1, lat1], [lng2, lat2], [lng3, lat3], [lng1, lat1]]]}, must close loop (first point = last point), GIST index supports containment queries O(log n). (2) Elasticsearch: { 'query': { 'geo_polygon': { 'location': { 'points': [ {'lat': 12.97, 'lon': 77.59}, {'lat': 12.98, 'lon': 77.60}, {'lat': 12.97, 'lon': 77.61} ] } } } }, supports up to 1000 vertices, automatically closes polygon. Optimization: (1) Bounding box pre-filter: Calculate min/max lat/lng of polygon (bounding rectangle), pre-filter with WHERE lat BETWEEN min_lat AND max_lat AND lng BETWEEN min_lng AND max_lng (fast), then exact containment check ST_Contains (slower), reduces candidates by 90%, (2) Polygon simplification: For complex polygons (1000+ vertices), use Douglas-Peucker algorithm to reduce to 100-200 vertices while maintaining shape, 5-10x faster queries. (3) Pre-computation: For static zones (delivery areas, neighborhoods), assign zone_id to each location, query becomes WHERE zone_id = {id} (instant via index). Use cases: (1) Delivery zones: Restaurant serves specific polygonal area, (2) Geofencing: Driver dispatch only within polygon boundary, (3) Neighborhood search: Find restaurants in 'Greenwich Village' (irregular shape). Performance: Simple polygon (10 vertices) with 10M points \u2192 ~50ms with GIST index, Complex polygon (100 vertices) \u2192 ~200ms. Example: Uber Eats delivery zone \u2192 restaurant draws polygon on map \u2192 frontend converts to GeoJSON \u2192 backend stores in zones table \u2192 orders only accepted from customers within ST_Contains(delivery_zone, customer_location)."
                  }
                ]
              },
              {
                "id": "geohash-precision-table",
                "title": "12. Geohash Precision Reference",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Precision Table (from image)",
                    "pairs": [
                      {
                        "k": "Length 1",
                        "v": "\u00b12500 km (5009.4 km width \u00d7 4992.6 km height) - Country level"
                      },
                      {
                        "k": "Length 2",
                        "v": "\u00b1630 km (1252.3 km \u00d7 624.1 km) - Large region"
                      },
                      {
                        "k": "Length 3",
                        "v": "\u00b178 km (156.5 km \u00d7 156 km) - City level"
                      },
                      {
                        "k": "Length 4",
                        "v": "\u00b120 km (39.1 km \u00d7 19.5 km) - District"
                      },
                      {
                        "k": "Length 5",
                        "v": "\u00b12.4 km (4.9 km \u00d7 4.9 km) - Neighborhood"
                      },
                      {
                        "k": "Length 6",
                        "v": "\u00b10.61 km (1.2 km \u00d7 609.4 m) - Street"
                      },
                      {
                        "k": "Length 7",
                        "v": "\u00b176 m (152.9 m \u00d7 152.4 m) - Building"
                      },
                      {
                        "k": "Length 8",
                        "v": "\u00b119 m (38.2 m \u00d7 19 m) - Precise"
                      },
                      {
                        "k": "Length 9",
                        "v": "\u00b12.4 m (4.8 m \u00d7 4.8 m) - Sub-building"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Precision Selection Guide",
                    "pairs": [
                      {
                        "k": "500m radius",
                        "v": "Use length 6 (\u00b1610m cell size) - ensures all points in radius are in queried cells"
                      },
                      {
                        "k": "2km radius",
                        "v": "Use length 5 (\u00b12.4km cell) - single cell query possible"
                      },
                      {
                        "k": "5km radius",
                        "v": "Use length 4 (\u00b120km cell) - larger cell, more candidates but fewer DB queries"
                      },
                      {
                        "k": "100m radius",
                        "v": "Use length 7 (\u00b176m cell) - precise, need 9 cell query (center + 8 neighbors)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "13. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Performance Benchmarks",
                    "pairs": [
                      {
                        "k": "Redis GEORADIUS",
                        "v": "~1ms for 10K points, ~5ms for 100K, ~10ms for 1M points"
                      },
                      {
                        "k": "PostgreSQL PostGIS",
                        "v": "~50-100ms for 10M points with GIST index"
                      },
                      {
                        "k": "Elasticsearch",
                        "v": "~10-50ms for 100M documents with geo queries"
                      },
                      {
                        "k": "Geohash LIKE",
                        "v": "~10-50ms with B-tree index on geohash column"
                      },
                      {
                        "k": "QuadTree in-memory",
                        "v": "<1ms for balanced tree with 1M points"
                      },
                      {
                        "k": "Haversine calculation",
                        "v": "~0.001ms per calculation (1M in 1 second)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Distance Formulas",
                    "pairs": [
                      {
                        "k": "Earth Radius",
                        "v": "6371 km (3959 miles) for Haversine formula"
                      },
                      {
                        "k": "1 degree latitude",
                        "v": "\u2248111 km (constant everywhere on Earth)"
                      },
                      {
                        "k": "1 degree longitude",
                        "v": "\u2248111 km \u00d7 cos(latitude) - varies by latitude, 0 at poles"
                      },
                      {
                        "k": "Haversine accuracy",
                        "v": "\u00b10.5% error (assumes perfect sphere)"
                      },
                      {
                        "k": "Vincenty accuracy",
                        "v": "\u00b10.5mm error (accounts for ellipsoid), 10x slower"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Memory & Storage",
                    "pairs": [
                      {
                        "k": "Redis memory",
                        "v": "~50 bytes per location \u2192 1M points \u2248 50MB"
                      },
                      {
                        "k": "PostGIS GIST index",
                        "v": "~10-20% of table size for spatial index"
                      },
                      {
                        "k": "Elasticsearch geo_point",
                        "v": "~16 bytes per location (stored + indexed)"
                      },
                      {
                        "k": "QuadTree overhead",
                        "v": "~100-200 bytes per node (internal nodes + pointers)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Scaling Limits",
                    "pairs": [
                      {
                        "k": "Redis capacity",
                        "v": "~10M points per instance (memory constraint)"
                      },
                      {
                        "k": "PostgreSQL",
                        "v": "10M-100M points per database with proper indexing"
                      },
                      {
                        "k": "Elasticsearch",
                        "v": "Billions of points across cluster (horizontal scaling)"
                      },
                      {
                        "k": "QuadTree",
                        "v": "Efficient for <1M points in-memory, single server"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "NEVER use simple lat/lng BETWEEN without spatial indexing for large datasets. Scanning millions of rows is O(n). Use GIST index (PostGIS), B-tree on geohash, or Redis GEORADIUS for O(log n)."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'Geohash vs QuadTree vs PostGIS?'. Answer: Geohash for distributed systems (standard indexes), QuadTree for in-memory single-server, PostGIS for ACID + complex queries. Production uses hybrid: Redis (hot) + Elasticsearch (search) + PostGIS (source of truth)."
              },
              {
                "type": "tip",
                "text": "Key optimization: Bounding box pre-filter before Haversine. WHERE lat BETWEEN (user_lat - delta) AND (user_lat + delta) eliminates 90% of points, then Haversine only for remaining 10%. Delta \u2248 radius_km / 111."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Geohash edge case. Points on cell boundary have different prefixes despite being close. Solution: query 8 neighboring cells (9 total). Precision based on radius: 500m \u2192 length 6, 5km \u2192 length 4."
              },
              {
                "type": "warning",
                "text": "NEVER assume 1 degree latitude = 1 degree longitude in distance. Latitude is constant (111km), longitude varies by latitude (111km at equator, 0km at poles). Always use Haversine or PostGIS ST_Distance."
              },
              {
                "type": "tip",
                "text": "Hybrid caching: Redis GEORADIUS for hot data (~5ms), Elasticsearch for complex queries (~50ms), PostgreSQL PostGIS for source of truth (~100ms). Serves 80% from Redis, 95% from Elasticsearch, 5% from PostgreSQL."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'Why Elasticsearch over PostgreSQL?'. Answer: Elasticsearch: horizontally scalable (billions of points), fast filtering (category + rating + distance), ~50ms p95. PostgreSQL: single server bottleneck, ~100ms, ACID guarantees. Use both: ES for reads, PG for writes."
              },
              {
                "type": "warning",
                "text": "NEVER compute Haversine for every point. Pre-filter with bounding box or geohash (index-based), then Haversine only for candidates. Haversine cheap per calc but expensive at scale (1M calculations = 1 second CPU)."
              },
              {
                "type": "tip",
                "text": "Geographic sharding: Shard by region (US-West, US-East, EU, Asia). 60% queries hit single shard, 30% hit 2 shards (border), reduces per-shard dataset 5x, latency 50ms vs 200ms. Use overlapping zones (10km buffer)."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: SQL query with geohash. SELECT * FROM restaurants WHERE geohash LIKE 'sgkzt%' OR geohash LIKE 'sgkzu%' ... (9 cells) \u2192 500 candidates \u2192 Haversine filter \u2192 200 within exact 5km. Show understanding of neighbor calculation."
              }
            ],
            "tags": [
              "system-design",
              "proximity-search",
              "geospatial",
              "geohash",
              "quadtree",
              "PostGIS",
              "Elasticsearch-geo",
              "Redis-GEORADIUS",
              "haversine-formula",
              "location-based-services",
              "nearby-search",
              "spatial-indexing",
              "GIST-index",
              "geo_distance",
              "bounding-box"
            ]
          }
        },
        {
          "id": "08",
          "title": "System Design 8: Design Chat Application like Whatsapp | Messenger | HLD / LLD",
          "videoUrl": "https://youtu.be/3HXFy_7M12E",
          "description": "In this video, we walk through the complete system design of a real-time chat application like WhatsApp or Messenger",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/n2j4nrj2notn43t3.jpg?updatedAt=1748722684738",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/ejnjk243bnkj2b4n5jn245n2.jpg?updatedAt=1748723507446",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "Chat Server (Messenger/WhatsApp)",
              "oneLinerToMemorize": "WebSocket for real-time messaging \u2192 Redis Streams for message queuing \u2192 Cassandra for message persistence \u2192 Last seen tracking with heartbeat"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Users should be able to register/login with phone number or email",
                  "Feature 2: One-to-one messaging - send text, images, videos, files",
                  "Feature 3: Group messaging - create groups, add/remove members, group admin controls",
                  "Feature 4: Send and receive media messages (images, videos up to 100MB)",
                  "Feature 5: Message history - retrieve past messages with pagination",
                  "Feature 6: Delivery and read receipts - single tick (sent), double tick (delivered), blue tick (read)",
                  "Feature 7: Last seen status - show when user was last active",
                  "Feature 8: Online/offline status - real-time presence indicator"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Users",
                        "v": "1 Billion users globally, 100M daily active users"
                      },
                      {
                        "k": "Messages",
                        "v": ">100B messages/day (1M+ msg/sec), 100TB+ messages/day"
                      },
                      {
                        "k": "Concurrent Connections",
                        "v": "10M-100M concurrent WebSocket connections"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> Consistency (eventual consistency acceptable)"
                      },
                      {
                        "k": "Message Latency",
                        "v": "< 100ms delivery for messages, highly reliable with no message loss"
                      },
                      {
                        "k": "Delivery Guarantee",
                        "v": "At-least-once delivery (may have duplicates, handle with message_id deduplication)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities",
                "type": "bullets",
                "items": [
                  "Entity 1: User - Profile with user_id, phone/email, username, profile_pic, last_seen, status",
                  "Entity 2: Message/Chat - Message data with message_id, sender_id, receiver_id, content, type (text/image/video), timestamp",
                  "Entity 3: Group - Group chat with group_id, name, members[], admin_ids[], created_at"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "User Operations",
                    "pairs": [
                      {
                        "k": "POST /v1/user/register",
                        "v": "Register with phone/OTP or email/password"
                      },
                      {
                        "k": "POST /v1/user/login",
                        "v": "Login and receive JWT token"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Messaging",
                    "pairs": [
                      {
                        "k": "WS /v1/messages/send",
                        "v": "Send message via WebSocket {receiver_id, content, type}"
                      },
                      {
                        "k": "GET /v1/messages/{userId}",
                        "v": "Retrieve message history with pagination"
                      },
                      {
                        "k": "WS /v1/user/{userId}/isOnline",
                        "v": "Subscribe to user's online status via WebSocket"
                      },
                      {
                        "k": "POST /v1/messages/{messageId}/read",
                        "v": "Mark message as read, update read receipt"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Group Operations",
                    "pairs": [
                      {
                        "k": "POST /v1/groups/create",
                        "v": "Create group with members list"
                      },
                      {
                        "k": "GET /v1/groups/{groupId}/messages",
                        "v": "Lazy load group messages with pagination"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Users/Clients \u2192 LB + API Gateway: Authentication, WebSocket upgrade, rate limiting",
                  "User Service \u2192 User DB (PostgreSQL): Stores user profiles, contacts, settings",
                  "Chat Service (WS) \u2192 WebSocket Gateway: Maintains persistent connections, routes messages",
                  "Group Service \u2192 Group DB (PostgreSQL): Manages group metadata, members",
                  "Media Upload Service \u2192 S3 (Blob): Stores images, videos, files with CDN delivery",
                  "Message Service \u2192 Chat DB (Cassandra): Persistent message storage with wide-column model",
                  "Redis Streams: Message queue for offline users, ensures delivery when they come online",
                  "Redis Cache: WebSocket registry (user_id \u2192 connection_id), online status, last seen",
                  "Notification Service \u2192 FCM/APNS: Push notifications for offline users",
                  "Elasticsearch: Message search service for searching chat history"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: User Registration & Login",
                    "steps": [
                      "User sends: POST /v1/user/register with {phone: '+1234567890'}",
                      "User Service validates: Phone number format, sends OTP via SMS (Twilio)",
                      "User submits: OTP for verification, service creates user record in PostgreSQL {user_id: UUID, phone, username, created_at}",
                      "Login flow: POST /v1/user/login with {phone, otp} \u2192 validates OTP \u2192 generates JWT token with {user_id, exp: 7 days}",
                      "Service returns: {user_id, token, profile_data}"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: WebSocket Connection Establishment",
                    "steps": [
                      "Client initiates: WebSocket handshake to WS /v1/connect with Authorization: Bearer {JWT}",
                      "WebSocket Gateway validates: JWT token, extracts user_id",
                      "Gateway registers: Connection in Redis - HSET websocket_registry user_{user_id} {connection_id, server_instance, connected_at} with TTL=None (persistent until disconnect)",
                      "Gateway updates: User status - SET user:{user_id}:status 'online', publish to Redis Pub/Sub channel user:{user_id}:presence",
                      "Client subscribes: To personal message channel messages:{user_id} and presence updates",
                      "Heartbeat: Client sends ping every 30s, server responds pong, updates last_seen in Redis"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Sending One-to-One Message (Online Recipient)",
                    "steps": [
                      "User1 sends: Message via WebSocket {type: 'text', receiver_id: user2, content: 'Hello', client_message_id: UUID}",
                      "Chat Service validates: User1 authenticated, receiver exists, content not empty (<10KB text)",
                      "Service generates: Server message_id (UUID), timestamp",
                      "Service checks: Redis HGET websocket_registry user_{user2} \u2192 finds connection_id (user2 is online)",
                      "Service delivers: Message to user2's WebSocket connection immediately via WebSocket Gateway",
                      "Service persists: Message to Cassandra messages table {message_id, sender_id: user1, receiver_id: user2, content, type: 'text', timestamp, status: 'delivered'}",
                      "Service sends: Delivery receipt to user1 (double tick) via WebSocket",
                      "User2 sends: Read receipt when message displayed \u2192 POST /v1/messages/{message_id}/read \u2192 update status='read', send read receipt to user1 (blue tick)"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Sending Message (Offline Recipient)",
                    "steps": [
                      "User1 sends: Message to user2 who is offline",
                      "Service checks: Redis HGET websocket_registry user_{user2} \u2192 returns null (offline)",
                      "Service writes: Message to Redis Stream XADD stream:user:{user2}:messages * message_id {msg_id} sender_id {user1} content {text} timestamp {ts}",
                      "Service persists: To Cassandra (same as online case)",
                      "Service sends: Single tick to user1 (sent, not delivered yet)",
                      "Service triggers: Push notification via Notification Service \u2192 FCM/APNS 'New message from User1: Hello'",
                      "When user2 comes online: WebSocket connect \u2192 service pulls unread messages from Redis Stream XREAD stream:user:{user2}:messages \u2192 delivers via WebSocket \u2192 acknowledges and deletes from stream \u2192 updates status to 'delivered'"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Group Messaging",
                    "steps": [
                      "User sends: Message to group {group_id: 'group_123', content: 'Hello group'}",
                      "Group Service fetches: Group members from Group DB - SELECT members FROM groups WHERE group_id = 'group_123' \u2192 returns [user2, user3, user4, user5]",
                      "Service iterates: Through members list, for each member check online status",
                      "For online members: Deliver immediately via WebSocket to their respective connections",
                      "For offline members: Write to Redis Stream stream:user:{member_id}:messages",
                      "Service stores: Single message copy in Cassandra group_messages table {message_id, group_id, sender_id, content, timestamp}",
                      "Optimization: Don't create N copies for N members, store once with group_id, retrieve using (group_id, timestamp) index"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Media Upload (Image/Video)",
                    "steps": [
                      "Client requests: POST /v1/media/upload/url with {file_type: 'image/jpeg', file_size: 2MB}",
                      "Media Upload Service validates: File size <= 100MB, type in [image/*, video/*, application/pdf]",
                      "Service generates: Presigned S3 PUT URL valid for 15 min - s3://media-bucket/{user_id}/{message_id}/{filename}.jpg",
                      "Service returns: {upload_url, media_id}",
                      "Client uploads: File directly to S3 using presigned URL (parallel upload for large files)",
                      "Client sends: Message via WebSocket {type: 'image', receiver_id, media_id, thumbnail_url, metadata: {width, height, size}}",
                      "Chat Service: Stores message with media_url pointing to S3, receiver downloads from CDN url: https://cdn.example.com/media/{media_id}"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Message History Retrieval",
                    "steps": [
                      "Client requests: GET /v1/messages/{user_id}?limit=50&before={timestamp} (pagination)",
                      "Chat Service queries: Cassandra - SELECT * FROM messages WHERE (sender_id = {current_user} AND receiver_id = {user_id}) OR (sender_id = {user_id} AND receiver_id = {current_user}) AND timestamp < {before} ORDER BY timestamp DESC LIMIT 50",
                      "Cassandra model: Partition key = (user1_id, user2_id) sorted pair to ensure both users query same partition, clustering key = timestamp DESC",
                      "Service returns: {messages: [{message_id, sender_id, content, timestamp, status}], has_more: true, next_before: {oldest_timestamp}}",
                      "Client renders: Messages in reverse order (newest at bottom), lazy loads more on scroll to top"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Last Seen & Online Status",
                    "steps": [
                      "Heartbeat mechanism: Client sends ping via WebSocket every 30s",
                      "Chat Service updates: Redis - SET user:{user_id}:last_seen {current_timestamp} EX 60 (60s expiry)",
                      "Online status: If last_seen exists and age < 60s \u2192 user is 'online', else 'offline'",
                      "Last seen display: When user2 queries user1's status \u2192 GET user:{user1}:last_seen \u2192 if exists, calculate 'Last seen 5 min ago', if expired 'Last seen today at 10:30 AM'",
                      "Presence broadcasting: When user1 goes offline (WebSocket disconnect) \u2192 publish to Redis Pub/Sub channel user:{user1}:presence with {status: 'offline', last_seen: timestamp}",
                      "Subscribers: User1's contacts who are online receive presence update via their WebSocket connections \u2192 UI updates to show 'offline'"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Read Receipts & Delivery Status",
                    "steps": [
                      "Message states: sent (single tick) \u2192 delivered (double tick) \u2192 read (blue tick)",
                      "Sent: When message persisted to Cassandra, sender receives sent receipt",
                      "Delivered: When message delivered to recipient's WebSocket or added to Redis Stream, update message status='delivered' in Cassandra, send double tick to sender",
                      "Read: When recipient's app displays message (onVisible event), client sends POST /v1/messages/{message_id}/read \u2192 update status='read' \u2192 send blue tick to sender via WebSocket",
                      "Group read receipts: Track per-member read status {message_id \u2192 {user2: read, user3: delivered, user4: read}}, show 'Read by 2 of 3' in UI"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Message Search",
                    "steps": [
                      "User searches: GET /v1/messages/search?q='hello world'&chat_id={user_id}",
                      "Message Consumer: Async consumer reads from Cassandra, indexes messages in Elasticsearch",
                      "Elasticsearch index: messages-{date} with fields {message_id, sender_id, receiver_id, content (text analyzed), timestamp}",
                      "Search query: Uses Elasticsearch match query on content field, filters by chat_id (sender OR receiver), sorted by timestamp DESC",
                      "Service returns: Matching messages with highlights, client displays with 'Jump to message' action",
                      "Optimization: Only index last 6 months of messages, older messages require direct Cassandra scan (slower)"
                    ]
                  },
                  {
                    "blockTitle": "Step 11: WebSocket Reconnection & Message Sync",
                    "steps": [
                      "Client disconnects: Due to network failure, WebSocket connection drops",
                      "Client reconnects: Sends last_received_message_id or last_sync_timestamp in handshake",
                      "Service fetches: Missed messages from Redis Stream + Cassandra WHERE timestamp > last_sync_timestamp ORDER BY timestamp",
                      "Service delivers: Batch of missed messages in chronological order",
                      "Client deduplicates: Using message_id (server-generated UUID) to prevent showing duplicates if message was already received",
                      "Stream cleanup: After delivery acknowledged, delete messages from Redis Stream - XDEL stream:user:{user_id}:messages {message_id}"
                    ]
                  }
                ]
              },
              {
                "id": "client-components",
                "title": "7. Client-Side Components",
                "type": "bullets",
                "items": [
                  "Component 1: Chat UI - Message list with infinite scroll, input box, typing indicators",
                  "Component 2: WebSocket Manager - Maintains connection, auto-reconnect with exponential backoff, handles ping/pong",
                  "Component 3: Message Queue - Local queue for outgoing messages when offline, syncs when online",
                  "Component 4: Media Handler - Uploads media to S3, shows progress, handles retry on failure",
                  "Component 5: Presence Tracker - Subscribes to contacts' online status, updates UI indicators",
                  "Component 6: Notification Handler - Receives push notifications via FCM/APNS, shows alerts",
                  "Component 7: Local Storage - SQLite DB for message cache, offline access, search"
                ]
              },
              {
                "id": "database-schema",
                "title": "8. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Users (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "phone",
                        "v": "varchar(15) UNIQUE"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "username",
                        "v": "varchar(50)"
                      },
                      {
                        "k": "profile_pic_url",
                        "v": "varchar(500)"
                      },
                      {
                        "k": "status_message",
                        "v": "varchar(255) (custom status like 'Available')"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Messages (Cassandra - optimized for writes)",
                    "pairs": [
                      {
                        "k": "partition_key",
                        "v": "(sender_id, receiver_id) or conversation_id (deterministic: min(user1, user2) + max(user1, user2))"
                      },
                      {
                        "k": "clustering_key",
                        "v": "timestamp DESC (sorts messages newest first within partition)"
                      },
                      {
                        "k": "message_id",
                        "v": "uuid (unique identifier)"
                      },
                      {
                        "k": "sender_id",
                        "v": "uuid"
                      },
                      {
                        "k": "receiver_id",
                        "v": "uuid (null for group messages)"
                      },
                      {
                        "k": "content",
                        "v": "text (message text, max 10KB)"
                      },
                      {
                        "k": "type",
                        "v": "text (text, image, video, file, audio)"
                      },
                      {
                        "k": "media_url",
                        "v": "text (S3 URL for media messages)"
                      },
                      {
                        "k": "metadata",
                        "v": "map<text, text> (file size, dimensions, duration)"
                      },
                      {
                        "k": "status",
                        "v": "text (sent, delivered, read)"
                      },
                      {
                        "k": "timestamp",
                        "v": "timestamp"
                      },
                      {
                        "k": "TTL",
                        "v": "Optional - auto-delete after N days (ephemeral messages)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Group_Messages (Cassandra)",
                    "pairs": [
                      {
                        "k": "partition_key",
                        "v": "group_id"
                      },
                      {
                        "k": "clustering_key",
                        "v": "timestamp DESC"
                      },
                      {
                        "k": "message_id",
                        "v": "uuid"
                      },
                      {
                        "k": "sender_id",
                        "v": "uuid"
                      },
                      {
                        "k": "content",
                        "v": "text"
                      },
                      {
                        "k": "type",
                        "v": "text"
                      },
                      {
                        "k": "media_url",
                        "v": "text"
                      },
                      {
                        "k": "read_by",
                        "v": "set<uuid> (user_ids who read the message)"
                      },
                      {
                        "k": "timestamp",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Groups (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "group_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "group_name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "group_pic_url",
                        "v": "varchar(500)"
                      },
                      {
                        "k": "created_by",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "admin_ids",
                        "v": "uuid[] (array of admin user_ids)"
                      },
                      {
                        "k": "member_ids",
                        "v": "uuid[] (array of member user_ids)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - WebSocket Registry & Presence",
                    "pairs": [
                      {
                        "k": "websocket_registry",
                        "v": "HASH user_{user_id} \u2192 {connection_id, server_instance, connected_at}"
                      },
                      {
                        "k": "user:{user_id}:status",
                        "v": "STRING 'online' or 'offline'"
                      },
                      {
                        "k": "user:{user_id}:last_seen",
                        "v": "STRING timestamp with EX 60 (60s expiry, indicates online if present)"
                      },
                      {
                        "k": "stream:user:{user_id}:messages",
                        "v": "REDIS STREAM - offline message queue, XADD for push, XREAD for pull"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch - Message Search",
                    "pairs": [
                      {
                        "k": "index",
                        "v": "messages-{yyyy-MM-dd}"
                      },
                      {
                        "k": "message_id",
                        "v": "keyword"
                      },
                      {
                        "k": "sender_id",
                        "v": "keyword"
                      },
                      {
                        "k": "receiver_id",
                        "v": "keyword"
                      },
                      {
                        "k": "group_id",
                        "v": "keyword (if group message)"
                      },
                      {
                        "k": "content",
                        "v": "text (analyzed for full-text search)"
                      },
                      {
                        "k": "type",
                        "v": "keyword"
                      },
                      {
                        "k": "timestamp",
                        "v": "date"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "additional-mechanism",
                "title": "9. Message Delivery Guarantee",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "At-Least-Once Delivery Strategy",
                    "steps": [
                      "Trigger: User sends message, system ensures it's delivered at least once (may duplicate)",
                      "Acknowledgment chain: Client \u2192 WebSocket Gateway (ack1) \u2192 Cassandra write (ack2) \u2192 Recipient delivery/stream write (ack3)",
                      "Client retry: If no ack1 within 5s, client resends with same client_message_id",
                      "Server deduplication: Check if message_id exists in Cassandra before inserting, if exists return ack (idempotent)",
                      "Recipient deduplication: Client maintains set of received message_ids (last 1000), ignores duplicates"
                    ]
                  },
                  {
                    "blockTitle": "Redis Streams for Offline Messages",
                    "steps": [
                      "Purpose: Queue messages for offline users without losing them",
                      "Write: XADD stream:user:{user_id}:messages * message_id {id} sender {user1} content {text} timestamp {ts}",
                      "Read: On user connect, XREAD COUNT 100 STREAMS stream:user:{user_id}:messages 0 (get all unread from beginning)",
                      "Acknowledge: After successful delivery to WebSocket, XDEL stream:user:{user_id}:messages {message_id}",
                      "Expiry: Messages in stream expire after 7 days (older than 7 days retrieved from Cassandra on demand)"
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "10. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: WebSocket Sharding - Distribute users across WebSocket Gateway instances using consistent hashing on user_id",
                  "Technique 2: Cassandra Partitioning - Partition messages by conversation_id (pair of user_ids), prevents hotspots, enables parallel queries",
                  "Technique 3: Redis Cluster - Shard websocket_registry and presence data across Redis cluster (16K slots), handle 10M+ connections",
                  "Technique 4: Message Batching - Batch multiple messages in single WebSocket frame (group sends), reduces network overhead by 80%",
                  "Technique 5: CDN for Media - Images/videos served via CloudFront, reduces origin load by 95%, faster delivery globally",
                  "Technique 6: Read Replicas - PostgreSQL read replicas for user profile queries, writes to primary only",
                  "Technique 7: Connection Pooling - WebSocket Gateway maintains 100K connections per instance, 100 instances = 10M concurrent users",
                  "Technique 8: Pub/Sub for Presence - Redis Pub/Sub channels for broadcasting online/offline status to subscribed contacts, scales to millions",
                  "Technique 9: Lazy Loading - Load last 50 messages on chat open, fetch older messages on scroll (infinite scroll pagination)",
                  "Technique 10: Compression - Compress WebSocket messages with zlib (3:1 ratio), saves bandwidth for mobile users",
                  "Technique 11: Local Cache - Client caches last 1000 messages in SQLite, instant load on app open, background sync",
                  "Technique 12: TTL for Old Messages - Cassandra TTL deletes messages >2 years old automatically, saves storage"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you ensure messages are delivered even when the recipient is offline?",
                    "a": "Multi-layer offline message handling: (1) Detection - when sending message, check Redis websocket_registry for recipient's connection_id, if null recipient is offline, (2) Redis Stream queueing - write message to Redis Stream XADD stream:user:{recipient_id}:messages with message data, stream persists until user comes online, (3) Cassandra persistence - simultaneously write message to Cassandra for durability (Redis Stream is cache, Cassandra is source of truth), (4) Push notification - trigger FCM/APNS notification 'New message from {sender}', (5) On reconnect - when recipient connects WebSocket, service executes XREAD stream:user:{recipient_id}:messages 0 to fetch all queued messages, delivers via WebSocket, client acknowledges, service deletes from stream XDEL, (6) Fallback - if messages in stream >7 days old (expired), retrieve from Cassandra. Example: User A sends to offline User B \u2192 message stored in Cassandra + Redis Stream + push sent \u2192 B comes online after 1 hour \u2192 B receives 15 queued messages \u2192 stream cleared \u2192 B now receives new messages in real-time."
                  },
                  {
                    "q": "How do you implement read receipts and delivery status at scale?",
                    "a": "Event-driven status tracking: (1) Sent status - when message written to Cassandra, update status='sent', send single tick to sender via WebSocket, (2) Delivered status - when message delivered to recipient (either via WebSocket if online, or written to Redis Stream if offline), update message status='delivered' in Cassandra, send double tick to sender via WebSocket event {message_id, status: 'delivered', delivered_at}, (3) Read status - when recipient's app displays message (onVisible/onScroll event), client sends POST /v1/messages/{message_id}/read \u2192 service updates status='read' \u2192 sends blue tick to sender, (4) Group read receipts - for groups, maintain map in Cassandra read_status: {message_id \u2192 {user2: {status: 'read', read_at}, user3: {status: 'delivered'}}}, query to show 'Read by 5 of 10 members', (5) Optimization - batch read receipts: instead of sending API call for each message, batch 10 message_ids in single request. Scaling: 1B messages/day \u00d7 3 status updates (sent, delivered, read) = 3B writes/day. Cassandra handles with partition by message_id, lightweight transactions for idempotency."
                  },
                  {
                    "q": "What happens if a WebSocket connection drops in the middle of sending a message?",
                    "a": "Client-side retry with idempotency: (1) Client generates - client_message_id (UUID) before sending message, (2) Send attempt - client sends via WebSocket {type: 'message', client_message_id, receiver_id, content}, (3) Acknowledgment wait - client waits for server ACK {client_message_id, server_message_id, status: 'sent'}, (4) Connection drops - if no ACK within 5s, client assumes failure, (5) Reconnection - WebSocket reconnects with exponential backoff (1s, 2s, 4s, 8s), (6) Retry logic - client resends message with same client_message_id, (7) Server deduplication - server checks if message with client_message_id already exists in Cassandra (lightweight transaction or SELECT before INSERT), if exists returns existing server_message_id without re-inserting, (8) Client reconciliation - client updates local DB mapping client_message_id \u2192 server_message_id. Edge case: message sent but ACK lost due to network \u2192 client retries \u2192 server detects duplicate \u2192 returns success \u2192 client updates UI with server_message_id. Result: at-least-once delivery, no lost messages, handled client-side deduplication using client_message_id + server_message_id mapping."
                  },
                  {
                    "q": "How do you handle last seen and online status efficiently?",
                    "a": "Heartbeat-based presence system: (1) Online detection - when user connects WebSocket, set Redis key user:{user_id}:status='online', also set user:{user_id}:last_seen with current timestamp EX 60 (60s expiry), (2) Heartbeat - client sends ping every 30s via WebSocket, server updates last_seen timestamp, resets TTL to 60s, (3) Offline detection - if client misses 2 consecutive pings (60s), last_seen key expires in Redis, user status switches to 'offline', (4) Last seen calculation - when querying user's status, GET user:{user_id}:last_seen, if key exists \u2192 'online', if key missing \u2192 user offline, check PostgreSQL for last_seen_timestamp stored on disconnect, display 'Last seen 2 hours ago', (5) Presence broadcasting - when user goes offline, publish to Redis Pub/Sub channel user:{user_id}:presence with {status: 'offline', last_seen: timestamp}, all subscribers (user's contacts) receive update via their WebSocket connections, (6) Privacy - user can disable last seen in settings, service returns 'Last seen recently' instead of exact timestamp. Optimization: don't broadcast every heartbeat (every 30s), only broadcast status changes (online \u2194 offline transitions). Scaling: 100M active users \u00d7 1 heartbeat/30s = 3.3M updates/sec, Redis handles with SET (O(1)) and Pub/Sub (100K msg/sec per instance)."
                  },
                  {
                    "q": "How do you design the message schema in Cassandra for efficient queries?",
                    "a": "Wide-column model optimized for conversations: (1) Partition key - conversation_id (deterministic from pair of user_ids: sorted to ensure both users query same partition), e.g., conversation between user_A and user_B \u2192 conversation_id = sha256(min(user_A, user_B) + max(user_A, user_B)), (2) Clustering key - timestamp DESC (sorts messages newest first within partition), allows efficient range queries for pagination, (3) Query pattern - SELECT * FROM messages WHERE conversation_id = {id} AND timestamp < {before} ORDER BY timestamp DESC LIMIT 50, hits single partition, retrieves messages in O(log N + 50) time, (4) Denormalization - store both sender_id and receiver_id in each row for bidirectional queries, (5) Write optimization - Cassandra optimized for writes (append-only, no read-before-write), handles 100K writes/sec per node, (6) TTL - set TTL for old messages, auto-delete after 2 years to save storage. Group messages: separate table group_messages with partition_key=group_id, clustering_key=timestamp DESC, allows querying all messages in group efficiently. Index strategy: no secondary indexes needed, primary key design handles all query patterns. Example: Conversation between Alice and Bob, 10K messages over 1 year \u2192 stored in single partition, latest 50 messages retrieved in <10ms, pagination efficient with timestamp cursor."
                  },
                  {
                    "q": "How do you implement group messaging efficiently?",
                    "a": "Fanout-on-write with optimization: (1) Message ingestion - user sends message to group {group_id: 'group_123', content: 'Hello'}, (2) Member retrieval - query PostgreSQL SELECT member_ids FROM groups WHERE group_id = 'group_123' \u2192 returns [user2, user3, user4, user5] (4 members), (3) Storage - write single message copy to Cassandra group_messages table with partition_key=group_id, clustering_key=timestamp DESC, (4) Fanout - iterate through member_ids, for each member: (a) check if online via Redis websocket_registry, (b) if online, deliver via WebSocket immediately, (c) if offline, write to Redis Stream stream:user:{member_id}:messages with pointer to group message_id, (5) Read receipts - track per-member read status in read_by set<uuid> in Cassandra, update when member reads, display 'Read by 3 of 4' in UI. Optimization: don't create 4 copies of message for 4 members, store once indexed by group_id, members retrieve via SELECT * FROM group_messages WHERE group_id = 'group_123' ORDER BY timestamp DESC. Large groups (>100 members): paginate fanout delivery, use Kafka for async processing to avoid blocking sender, send to 100 members in parallel batches. Example: 1000-member group, single message \u2192 stored once in Cassandra (1 write) \u2192 fanout to 1000 members (1000 WebSocket sends or stream writes) \u2192 processed in <1s via parallel workers."
                  },
                  {
                    "q": "What's your media upload and delivery strategy?",
                    "a": "Direct S3 upload with presigned URLs: (1) Client request - POST /v1/media/upload/url with {file_type: 'image/jpeg', file_size: 5MB}, (2) Validation - Media Service checks file_size <= 100MB, type in whitelist [image/*, video/*, application/pdf, audio/*], (3) Presigned URL generation - generate S3 presigned PUT URL with key media/{user_id}/{message_id}/{uuid}.jpg, valid for 15 min, (4) Client upload - client uploads file directly to S3 bypassing backend servers (reduces load), shows progress bar, (5) Upload completion - S3 triggers Lambda on object creation, Lambda generates thumbnail (for images) and stores in media/{user_id}/{message_id}/thumb.jpg, (6) Message send - client sends chat message {type: 'image', media_id, media_url, thumbnail_url, metadata: {width: 1920, height: 1080, size: 5MB}}, (7) Delivery - recipient receives message with thumbnail_url (small, loads fast), clicks to view full image fetched from CDN https://cdn.example.com/media/{media_id}, (8) CDN - CloudFront caches media at edge locations, 95% cache hit rate, reduces S3 reads and latency. Optimization: adaptive bitrate for videos (transcoding via MediaConvert), progressive JPEG for images. Security: presigned URLs with expiry prevent unauthorized access. Example: User sends 20MB video \u2192 generates presigned URL in 50ms \u2192 uploads to S3 in 30s \u2192 Lambda transcodes to 720p, 480p, 360p (takes 2 min) \u2192 recipient loads thumbnail instantly, streams video from CDN with adaptive quality."
                  },
                  {
                    "q": "How do you implement typing indicators in real-time?",
                    "a": "Ephemeral event broadcasting via WebSocket: (1) Typing start - when user starts typing, client sends {type: 'typing_start', chat_id: {receiver_id}} via WebSocket, (2) Throttling - client throttles typing events (send at most once per 3 seconds) to reduce traffic, (3) Server broadcast - WebSocket Gateway checks if receiver online, if yes broadcasts to receiver's connection {type: 'typing', user_id: {sender_id}}, (4) Client display - receiver's UI shows '{Sender Name} is typing...' indicator, (5) Typing stop - after 3s of no typing or when message sent, client sends {type: 'typing_stop'}, server broadcasts, receiver hides indicator, (6) No persistence - typing events are NOT stored in database or Redis, completely ephemeral (reduces load). Group typing: similar flow but broadcast to all online group members except sender. Scaling: 100M concurrent users, 10% typing at any moment = 10M typing events, but throttled to 1 event per 3s = 3.3M events/sec, handled by WebSocket Gateway with direct forwarding (no DB writes). Alternative: use Redis Pub/Sub - publish to channel typing:{chat_id}, subscribers receive updates, but WebSocket direct routing is simpler and faster for 1-to-1 chats."
                  },
                  {
                    "q": "How do you handle message ordering and prevent race conditions?",
                    "a": "Server-assigned timestamps with Lamport clocks: (1) Client-side timestamp - client includes local_timestamp when sending message (for optimistic UI update), (2) Server timestamp - server assigns authoritative server_timestamp (millisecond precision) when message received, this is used for ordering, (3) Cassandra clustering - messages stored with clustering_key=server_timestamp DESC, ensures consistent ordering, (4) Race condition scenario - two messages sent simultaneously from different clients (A sends M1, B sends M2 to same chat), (5) Server serialization - WebSocket Gateway processes messages sequentially per chat (maintains order using queue or single-threaded event loop per chat), assigns timestamps T1, T2 where T1 < T2 based on arrival order, (6) Client reconciliation - clients may show messages in different order temporarily (optimistic UI), but when they fetch from Cassandra, all see same order (by server_timestamp), (7) Logical clocks - for distributed servers, use Lamport timestamps or vector clocks to maintain causal ordering across servers. Edge case: Client A's clock is 5 minutes ahead, sends message with future timestamp \u2192 server ignores client timestamp, uses server_timestamp \u2192 prevents ordering issues. Example: User A sends M1 at 10:00:00.100 (client time), User B sends M2 at 10:00:00.050 (client time) \u2192 server receives M1 first (assigns T=1000), M2 second (assigns T=1001) \u2192 all clients display M1 before M2 regardless of client timestamps."
                  },
                  {
                    "q": "What's your disaster recovery strategy for chat data?",
                    "a": "Multi-region replication with backup: (1) Cassandra replication - multi-datacenter replication with RF=3 (replication factor) in primary region (us-east-1), async replication to secondary region (us-west-2), (2) Write consistency - use QUORUM for writes (2 of 3 replicas acknowledge), balances durability with latency, (3) Read consistency - use ONE for reads (fastest response), eventual consistency acceptable for chat, (4) Failure scenarios - if 1 node fails, other 2 replicas serve requests, writes still succeed with QUORUM, (5) Region failover - if primary region down, DNS failover to secondary region (us-west-2) in <5 min (RTO), secondary has replica of all data (RPO=1 min, async replication lag), (6) Redis persistence - use Redis AOF (Append-Only File) + RDB snapshots, if Redis crashes rebuild from snapshots + Cassandra (offline messages), (7) S3 replication - enable cross-region replication for media bucket, media available in both regions, (8) Backup strategy - daily snapshots of Cassandra to S3, retention 30 days, allows point-in-time recovery. Testing: monthly DR drills, simulate region failure, validate failover automation. Example: Primary region experiences outage \u2192 DNS switches to secondary in 3 min \u2192 users reconnect WebSockets to secondary region \u2192 message history loaded from replicated Cassandra \u2192 service continues with <5 min disruption."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "12. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Volume",
                    "pairs": [
                      {
                        "k": "Total Users",
                        "v": "1 Billion globally, 100M daily active users (DAU)"
                      },
                      {
                        "k": "Messages/Day",
                        "v": ">100 Billion messages per day (1M+ msg/sec average)"
                      },
                      {
                        "k": "Concurrent Connections",
                        "v": "10M-100M concurrent WebSocket connections"
                      },
                      {
                        "k": "Data Volume",
                        "v": "100TB+ of messages per day (text + media)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Latency & Performance",
                    "pairs": [
                      {
                        "k": "Message Delivery",
                        "v": "< 100ms end-to-end latency for online users"
                      },
                      {
                        "k": "WebSocket Ping/Pong",
                        "v": "30s interval for heartbeat, 60s timeout"
                      },
                      {
                        "k": "Typing Indicator",
                        "v": "Throttled to 1 event per 3s"
                      },
                      {
                        "k": "Message Acknowledgment",
                        "v": "< 50ms for sent receipt, < 100ms for delivered"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Storage & Caching",
                    "pairs": [
                      {
                        "k": "Redis TTL",
                        "v": "Last seen: 60s, WebSocket registry: no expiry (until disconnect)"
                      },
                      {
                        "k": "Redis Stream",
                        "v": "Offline messages retained 7 days, then Cassandra fallback"
                      },
                      {
                        "k": "Cassandra TTL",
                        "v": "Messages auto-delete after 2 years (optional)"
                      },
                      {
                        "k": "Message Cache",
                        "v": "Client caches last 1000 messages in SQLite"
                      },
                      {
                        "k": "CDN Cache",
                        "v": "Media cached 30 days, 95% hit rate"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Messaging Limits",
                    "pairs": [
                      {
                        "k": "Text Message",
                        "v": "Max 10KB per message"
                      },
                      {
                        "k": "Media Upload",
                        "v": "Max 100MB per file (image/video/document)"
                      },
                      {
                        "k": "Group Size",
                        "v": "Max 256 members per group (WhatsApp limit)"
                      },
                      {
                        "k": "Presigned URL",
                        "v": "Valid for 15 minutes for S3 upload"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability Metrics",
                    "pairs": [
                      {
                        "k": "Uptime SLA",
                        "v": "99.99% availability (52 minutes/year downtime)"
                      },
                      {
                        "k": "Message Delivery",
                        "v": "At-least-once guarantee (may duplicate)"
                      },
                      {
                        "k": "RTO (Recovery Time)",
                        "v": "< 5 minutes for region failover"
                      },
                      {
                        "k": "RPO (Recovery Point)",
                        "v": "< 1 minute (async replication lag)"
                      },
                      {
                        "k": "Cassandra Replication",
                        "v": "RF=3, QUORUM writes (2 of 3 must acknowledge)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "WebSocket Scaling",
                    "pairs": [
                      {
                        "k": "Connections/Server",
                        "v": "100K WebSocket connections per gateway instance"
                      },
                      {
                        "k": "Total Gateway Servers",
                        "v": "100-1000 instances for 10M-100M connections"
                      },
                      {
                        "k": "Message Rate",
                        "v": "10K messages/sec per gateway instance"
                      },
                      {
                        "k": "Reconnect Backoff",
                        "v": "Exponential: 1s, 2s, 4s, 8s, max 16s"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Example Calculation - Message Flow",
                    "pairs": [
                      {
                        "k": "Step 1: WebSocket Send",
                        "v": "10ms (client to gateway, validate)"
                      },
                      {
                        "k": "Step 2: Cassandra Write",
                        "v": "30ms (insert message, replication)"
                      },
                      {
                        "k": "Step 3: Recipient Lookup",
                        "v": "5ms (Redis check if online)"
                      },
                      {
                        "k": "Step 4: WebSocket Deliver",
                        "v": "10ms (gateway to recipient)"
                      },
                      {
                        "k": "Total Latency",
                        "v": "55ms (well under 100ms target)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cost Optimization",
                    "pairs": [
                      {
                        "k": "Direct S3 Upload",
                        "v": "Saves server bandwidth, 100TB/day uploaded directly"
                      },
                      {
                        "k": "CDN Cache Hit",
                        "v": "95% hit rate = 95% fewer S3 reads"
                      },
                      {
                        "k": "Message Compression",
                        "v": "3:1 ratio with zlib, saves 66% bandwidth"
                      },
                      {
                        "k": "TTL for Old Messages",
                        "v": "Auto-delete after 2 years = 50% storage savings"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "NEVER use HTTP polling for real-time messaging. WebSocket is mandatory for persistent bidirectional communication. Polling every 1s for 10M users = 10M req/sec unnecessary load. WebSocket: single connection, server pushes updates."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How to handle offline messages?'. Answer: Redis Streams for queueing (fast, 7-day retention) + Cassandra for persistence (durable, long-term). On reconnect, pull from Stream first, fallback to Cassandra for older messages. Push notifications via FCM/APNS."
              },
              {
                "type": "tip",
                "text": "Key optimization: Direct S3 upload with presigned URLs. Client uploads media directly to S3, bypassing backend. Saves bandwidth, reduces latency from 5s (via backend) to 2s (direct), handles 100MB files efficiently."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Cassandra partition key design. Use conversation_id (pair of user_ids) as partition key, timestamp as clustering key. Ensures single partition for conversation, efficient queries, sorted by timestamp for pagination."
              },
              {
                "type": "warning",
                "text": "NEVER store typing indicators in database. They're ephemeral events, broadcast via WebSocket/Pub/Sub only. Storing creates 1B+ unnecessary writes/day. Typing events should be fire-and-forget with throttling (1 per 3s)."
              },
              {
                "type": "tip",
                "text": "At-least-once delivery with client deduplication. Server may deliver message multiple times (retry logic). Client uses message_id to dedupe. Trade-off: simpler than exactly-once (no complex distributed transactions), acceptable for chat."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love asking: 'How to implement read receipts at scale?'. Answer: Event-driven updates - when message displayed, client sends read event \u2192 update Cassandra status \u2192 broadcast to sender via WebSocket. Batch read receipts (10 messages) to reduce API calls."
              },
              {
                "type": "warning",
                "text": "NEVER synchronously wait for all group members to receive message. Use fanout-on-write with async delivery. Sender gets immediate ACK after Cassandra write, member delivery happens async (parallel workers). Prevents timeout for large groups."
              },
              {
                "type": "tip",
                "text": "Heartbeat-based presence with Redis TTL. Client pings every 30s \u2192 server updates last_seen with EX 60. If client dies, TTL expires in 60s \u2192 automatic offline status. No manual cleanup needed, Redis handles it."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: WebSocket sharding with consistent hashing. Distribute users across gateway instances using hash(user_id) % num_instances. Enables horizontal scaling from 100K to 100M connections. User always connects to same instance for session continuity."
              }
            ],
            "tags": [
              "system-design",
              "chat-server",
              "messaging",
              "WhatsApp",
              "Messenger",
              "WebSocket",
              "real-time-communication",
              "Cassandra",
              "Redis-Streams",
              "presence-detection",
              "message-delivery",
              "offline-messaging",
              "read-receipts",
              "group-chat",
              "media-upload",
              "S3",
              "push-notifications",
              "at-least-once-delivery"
            ]
          }
        },
        {
          "id": "09",
          "title": "System Design 9: Design Distributed Job Scheduler like Airflow | Temporal | Celery | HLD | LLD",
          "videoUrl": "https://youtu.be/hLvB2haod5w",
          "description": "In this video, we walk through the complete design of a scalable and distributed job scheduler system, capable of handling 10K+ jobs per second with support for cron, retries, failure handling, and real-time execution.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/3g4g34g3n4oi348jg03ng4.png?updatedAt=1749843631804",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/kjn24ht84tyq384tq3t4o3gb4og42b.jpeg?updatedAt=1749843500164",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "Distributed Job Scheduler (Airflow/Temporal)",
              "oneLinerToMemorize": "DAG-based scheduling \u2192 Redis/DB for job queue \u2192 Multiple executors \u2192 Kafka for retry/dead-letter \u2192 Watcher for monitoring"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Ability to schedule a job at specified times (immediate/future/cron expression)",
                  "Feature 2: Monitor the status of jobs in real-time (pending, running, success, failed, cancelled)",
                  "Feature 3: Support update/cancel scheduled jobs before execution",
                  "Feature 4: Support job dependencies - DAG (Directed Acyclic Graph) execution order",
                  "Feature 5: Retry mechanism with configurable retry count and backoff strategy",
                  "Feature 6: Dead letter queue for permanently failed jobs",
                  "Feature 7: Job prioritization and resource allocation (CPU, memory limits per job)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Performance",
                    "pairs": [
                      {
                        "k": "Job Volume",
                        "v": "Millions of jobs per day, thousands of jobs per second"
                      },
                      {
                        "k": "Executors",
                        "v": "100s of executor instances for parallel job execution"
                      },
                      {
                        "k": "Latency",
                        "v": "Jobs should execute within 2s of scheduled time (\u00b12s tolerance)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability & Consistency",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> Consistency (Job should run at least once, eventual consistency acceptable)"
                      },
                      {
                        "k": "Execution Guarantee",
                        "v": "At-least-once execution (jobs may retry on failure, idempotency required)"
                      },
                      {
                        "k": "Durability",
                        "v": "Job state persisted, no jobs lost even on system failure"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Scheduling Requirements",
                    "pairs": [
                      {
                        "k": "Cron Support",
                        "v": "Standard cron expressions (0 0 * * * for daily at midnight)"
                      },
                      {
                        "k": "Time Zones",
                        "v": "Support scheduling in different time zones"
                      },
                      {
                        "k": "Backfill",
                        "v": "Ability to run missed jobs (if scheduler was down)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities",
                "type": "bullets",
                "items": [
                  "Entity 1: Job - Task definition with job_id, name, schedule (cron/timestamp), payload, dependencies",
                  "Entity 2: Scheduler - Component that schedules jobs based on time/cron expressions",
                  "Entity 3: Executor - Worker that executes jobs, pulls from queue and runs job logic",
                  "Entity 4: JobRun - Execution instance with run_id, job_id, status, start_time, end_time, logs",
                  "Entity 5: DAG (Directed Acyclic Graph) - Workflow with multiple dependent jobs"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Job Management",
                    "pairs": [
                      {
                        "k": "POST /v1/api/jobs",
                        "v": "Create/schedule a job {name, schedule: 'cron/timestamp', payload, retry: 3, dependencies: []}"
                      },
                      {
                        "k": "GET /v1/api/jobs/{jobId}",
                        "v": "Get job details (schedule, status, last run time)"
                      },
                      {
                        "k": "GET /v1/api/jobs/{jobId}/status",
                        "v": "Get current status of job execution"
                      },
                      {
                        "k": "PUT /v1/api/jobs/{jobId}",
                        "v": "Update job schedule or payload"
                      },
                      {
                        "k": "POST /v1/api/jobs/{jobId}/cancel",
                        "v": "Cancel a scheduled/running job"
                      },
                      {
                        "k": "POST /v1/api/jobs/{jobId}/runnow",
                        "v": "Run job immediately (trigger ad-hoc execution)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Monitoring",
                    "pairs": [
                      {
                        "k": "GET /v1/api/jobs/runs",
                        "v": "List all job runs with filters (status, date range)"
                      },
                      {
                        "k": "GET /v1/api/jobs/{jobId}/runs",
                        "v": "Get execution history for specific job"
                      },
                      {
                        "k": "GET /v1/api/jobs/stats",
                        "v": "Get statistics (total jobs, success rate, avg execution time)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Clients/Users \u2192 LB + API Gateway: Authentication, authorization, rate limiting, routing",
                  "Job Service \u2192 Job DB (PostgreSQL): Stores job definitions (schedule, payload, dependencies, retry config)",
                  "Job Executor \u2192 Job DB: Pulls jobs that need to be executed, updates status",
                  "Scheduler (Watcher) \u2192 Redis + Kafka: Polls jobs scheduled for execution, publishes to Kafka topics",
                  "Kafka Topics: run (immediate execution), retry (failed jobs with retry), dead (permanently failed)",
                  "Job Consumer Service \u2192 Executor Pool: Consumes from Kafka, dispatches to available executors",
                  "Executor Services (100s instances): Execute job logic, update status in DB, publish results to Kafka",
                  "Redis: Stores last-polled-time for scheduler, distributed locks for executors"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Job Creation & Scheduling",
                    "steps": [
                      "User sends: POST /v1/api/jobs with {name: 'ETL Pipeline', schedule_type: 'cron', schedule_value: '0 0 * * *', payload: {db_connection}, retry_count: 3, timeout: 3600}",
                      "Job Service validates: Cron expression valid, schedule_time in future if one-time, dependencies don't create cycles (DAG validation)",
                      "Service creates: Job record in PostgreSQL {job_id: UUID, name, schedule_type: 'cron', schedule_value, status: 'scheduled', next_run_time: '2025-01-21T00:00:00Z', created_at}",
                      "Service calculates: next_run_time using cron parser library (croniter in Python), stores in indexed column for efficient polling",
                      "Service returns: {job_id, status: 'scheduled', next_run_time}"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Scheduler (Watcher) Polling",
                    "steps": [
                      "Watcher service runs: Infinite loop with 20-second interval (configurable)",
                      "Service queries: SELECT * FROM jobs WHERE status IN ('scheduled', 'running') AND next_run_time <= NOW() AND last_polled_time < (NOW - 30s) LIMIT 1000",
                      "Service fetches: 1000 jobs ready for execution, updates last_polled_time = NOW() to prevent duplicate processing by other watchers",
                      "For each job: Calculate if should run based on schedule_type - for cron, check if current time matches expression, for one-time check if next_run_time passed",
                      "Service publishes: Job to Kafka topic 'run' with {job_id, run_id: UUID, payload, timestamp, attempt: 1}"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Job Consumption & Execution",
                    "steps": [
                      "Job Consumer Service: Consumes from Kafka topics 'run', 'retry' with consumer group 'job-consumers', partitioned by job_id for ordering",
                      "Consumer receives: Job message {job_id, run_id, payload, attempt: 1}",
                      "Consumer creates: JobRun record in DB {run_id, job_id, status: 'pending', scheduled_at, created_at}",
                      "Consumer checks: Available executor capacity from Redis executors:available (tracks active jobs per executor), implements load balancing",
                      "Consumer dispatches: Job to Executor Service via internal API POST /executor/run with {run_id, job_id, payload}, updates status to 'running'",
                      "Executor updates: DB with status='running', start_time=NOW(), publishes heartbeat every 10s to Redis executor:{executor_id}:heartbeat"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Job Execution by Executor",
                    "steps": [
                      "Executor receives: Job details {run_id, job_id, payload}",
                      "Executor fetches: Full job definition from Job DB (or Redis cache with TTL=5 min) to get retry_count, timeout, dependencies",
                      "Executor runs: Job logic in isolated process/container (Docker container or separate thread with resource limits)",
                      "Job execution: Runs user-defined code (Python script, shell command, HTTP request to external service), streams logs to centralized logging (CloudWatch, ELK)",
                      "Timeout handling: If execution exceeds timeout (3600s), kill process, mark as 'timeout' status",
                      "Executor updates: DB with status='success' or 'failed', end_time, error_msg (if failed), execution_time_ms"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Retry Mechanism",
                    "steps": [
                      "On job failure: Executor checks job.retry_count > current_attempt (e.g., retry_count=3, attempt=1)",
                      "Executor publishes: To Kafka 'retry' topic with {job_id, run_id, payload, attempt: 2, retry_delay: 60s (exponential backoff)}",
                      "Delay calculation: retry_delay = base_delay * (2 ^ attempt) + jitter, e.g., 60s, 120s, 240s for attempts 1, 2, 3",
                      "Retry Consumer: Consumes from 'retry' topic, sleeps for retry_delay duration, then republishes to 'run' topic for re-execution",
                      "Final failure: If attempt > retry_count (e.g., attempt=4, retry_count=3), publish to 'dead' topic, update job status='failed_permanently', send alert"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: DAG Dependency Resolution",
                    "steps": [
                      "Job definition: Job B depends on Job A completion - stored as jobs.dependencies = [job_a_id]",
                      "Scheduler checks: Before scheduling Job B, query JobRuns table WHERE job_id IN (dependencies) AND status='success' AND run_date = TODAY",
                      "Conditional execution: If all dependencies met, publish Job B to Kafka 'run', else skip and wait for next watcher cycle",
                      "Parallel execution: Jobs C and D both depend on B - once B succeeds, both C and D published simultaneously for parallel execution",
                      "Cycle detection: On job creation, run DFS (Depth-First Search) on dependency graph, reject job if cycle detected (e.g., A\u2192B\u2192C\u2192A)"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Job Cancellation",
                    "steps": [
                      "User sends: POST /v1/api/jobs/{job_id}/cancel",
                      "Job Service checks: Current status - if 'scheduled' (not running), update status='cancelled' in DB, remove from next scheduling cycle",
                      "If status='running': Publish cancellation event to Kafka 'cancel' topic with {job_id, run_id}",
                      "Executor receives: Cancellation event via subscription to 'cancel' topic, sends SIGTERM to job process",
                      "Graceful shutdown: Process has 30s to cleanup (close DB connections, save state), then SIGKILL if not exited",
                      "Executor updates: DB with status='cancelled', end_time=NOW(), publishes completion event"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Monitoring & Health Checks",
                    "steps": [
                      "Watcher service monitors: Executor heartbeats in Redis, if executor:{id}:heartbeat not updated in 60s, mark executor as 'unhealthy'",
                      "Service identifies: Jobs running on unhealthy executor (query JobRuns WHERE executor_id={id} AND status='running')",
                      "Service reschedules: Those jobs by publishing to Kafka 'run' topic with note 'rescheduled_from_dead_executor', updates old run status='executor_died'",
                      "Dashboard metrics: Total jobs scheduled, running, success, failed (last 24h), avg execution time, success rate %, executors online/offline",
                      "Alerts: Triggered on: job failure rate >10%, executor capacity >80%, dead letter queue size >100 jobs"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Distributed Locking (Prevent Duplicate Execution)",
                    "steps": [
                      "Problem: Multiple watcher instances poll jobs, could schedule same job twice",
                      "Solution: Redis distributed lock using SETNX - before scheduling job, acquire lock: SET lock:job:{job_id}:schedule {watcher_id} NX EX 60",
                      "Lock acquired: Watcher schedules job (publishes to Kafka), releases lock DELETE lock:job:{job_id}:schedule",
                      "Lock failed: Another watcher already processing this job, skip to next job in query result",
                      "Auto-expiry: Lock expires in 60s if watcher crashes, prevents deadlock, next watcher cycle picks up job"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Handling Missed Jobs (Backfill)",
                    "steps": [
                      "Scenario: Scheduler down for 2 hours, 100 hourly jobs missed",
                      "Backfill detection: On watcher restart, query jobs WHERE next_run_time < (NOW - 2 * schedule_interval) AND status='scheduled'",
                      "Backfill strategy: For each missed job, create JobRun with scheduled_at = missed_time, status='backfill', publish to Kafka",
                      "Throttling: Publish backfill jobs at controlled rate (100 jobs/sec) to prevent overwhelming executors",
                      "User notification: Send alert 'Scheduler was down, running 100 missed jobs' for transparency"
                    ]
                  }
                ]
              },
              {
                "id": "client-components",
                "title": "7. Client-Side Components (UI/CLI)",
                "type": "bullets",
                "items": [
                  "Component 1: Job Definition UI - Form to create jobs with cron expression builder, payload editor",
                  "Component 2: DAG Visualizer - Graph view showing job dependencies with nodes and edges",
                  "Component 3: Job Monitor Dashboard - Real-time status board with job counts (running, success, failed)",
                  "Component 4: Execution History - Timeline view of past job runs with logs and error details",
                  "Component 5: Cron Expression Helper - Interactive cron builder with natural language (every day at 2 AM)",
                  "Component 6: Alert Configuration - UI to set alerts on job failures, SLA breaches",
                  "Component 7: CLI Tool - Command-line interface for power users (airflow trigger dag_id, airflow list_jobs)"
                ]
              },
              {
                "id": "database-schema",
                "title": "8. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Jobs (PostgreSQL - master job definitions)",
                    "pairs": [
                      {
                        "k": "job_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "schedule_type",
                        "v": "enum (cron, one_time, interval)"
                      },
                      {
                        "k": "schedule_value",
                        "v": "varchar(255) (cron expression or ISO timestamp)"
                      },
                      {
                        "k": "next_run_time",
                        "v": "timestamp INDEXED (for efficient polling)"
                      },
                      {
                        "k": "last_polled_time",
                        "v": "timestamp (prevents duplicate scheduling)"
                      },
                      {
                        "k": "status",
                        "v": "enum (scheduled, running, paused, cancelled)"
                      },
                      {
                        "k": "payload",
                        "v": "jsonb (job-specific parameters)"
                      },
                      {
                        "k": "dependencies",
                        "v": "uuid[] (array of job_ids this job depends on)"
                      },
                      {
                        "k": "retry_count",
                        "v": "integer DEFAULT 3"
                      },
                      {
                        "k": "timeout",
                        "v": "integer (seconds, e.g., 3600)"
                      },
                      {
                        "k": "priority",
                        "v": "integer (1-10, higher = more important)"
                      },
                      {
                        "k": "owner_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "JobRuns (PostgreSQL - execution history)",
                    "pairs": [
                      {
                        "k": "run_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "job_id",
                        "v": "uuid FK \u2192 Jobs, INDEXED"
                      },
                      {
                        "k": "status",
                        "v": "enum (pending, running, success, failed, timeout, cancelled, executor_died)"
                      },
                      {
                        "k": "scheduled_at",
                        "v": "timestamp (when job was supposed to run)"
                      },
                      {
                        "k": "start_time",
                        "v": "timestamp (actual start time)"
                      },
                      {
                        "k": "end_time",
                        "v": "timestamp (completion time)"
                      },
                      {
                        "k": "execution_time_ms",
                        "v": "bigint (duration in milliseconds)"
                      },
                      {
                        "k": "executor_id",
                        "v": "varchar(100) (which executor ran this job)"
                      },
                      {
                        "k": "attempt",
                        "v": "integer (1, 2, 3 for retries)"
                      },
                      {
                        "k": "error_msg",
                        "v": "text (error details if failed)"
                      },
                      {
                        "k": "logs_url",
                        "v": "varchar(500) (S3 path or CloudWatch link)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp INDEXED (for history queries)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Distributed State",
                    "pairs": [
                      {
                        "k": "lock:job:{job_id}:schedule",
                        "v": "STRING {watcher_id} with NX EX 60 (distributed lock)"
                      },
                      {
                        "k": "executor:{executor_id}:heartbeat",
                        "v": "STRING {timestamp} updated every 10s (health check)"
                      },
                      {
                        "k": "executors:available",
                        "v": "HASH {executor_id: active_job_count} (load balancing)"
                      },
                      {
                        "k": "job:{job_id}:cache",
                        "v": "HASH (cached job definition, TTL: 5 min)"
                      },
                      {
                        "k": "last_poll_time",
                        "v": "STRING timestamp of last successful watcher poll"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Kafka Topics",
                    "pairs": [
                      {
                        "k": "run",
                        "v": "Jobs ready for immediate execution (10 partitions by job_id)"
                      },
                      {
                        "k": "retry",
                        "v": "Failed jobs with remaining retry attempts (5 partitions)"
                      },
                      {
                        "k": "dead",
                        "v": "Permanently failed jobs after all retries (1 partition, low volume)"
                      },
                      {
                        "k": "cancel",
                        "v": "Cancellation requests for running jobs (3 partitions)"
                      },
                      {
                        "k": "completed",
                        "v": "Job completion events for downstream systems (10 partitions)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "additional-mechanism",
                "title": "9. Alternative Scheduling Solutions",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Approach 1: Event-Driven (Amazon EventBridge)",
                    "steps": [
                      "Concept: Jobs triggered by events instead of time-based polling",
                      "Implementation: EventBridge rules match cron expressions, trigger Lambda functions or SQS queues",
                      "Pros: Serverless, auto-scaling, no polling overhead, pay-per-execution",
                      "Cons: Vendor lock-in (AWS), limited to 300 targets per rule, cold start latency",
                      "Use case: Simple scheduled tasks, cloud-native architectures"
                    ]
                  },
                  {
                    "blockTitle": "Approach 2: Managed Service (AWS Step Functions, Temporal)",
                    "steps": [
                      "Concept: Workflow orchestration as a service with built-in retry, state management",
                      "Implementation: Define workflows as JSON (Step Functions) or code (Temporal), service handles execution",
                      "Pros: No infrastructure management, built-in monitoring, durable execution (survives crashes)",
                      "Cons: Cost (per state transition), learning curve, less control over execution",
                      "Use case: Complex workflows with multiple steps, enterprise applications"
                    ]
                  },
                  {
                    "blockTitle": "Approach 3: Custom with Delay Queue (RabbitMQ, SQS)",
                    "steps": [
                      "Concept: Use message queue's delay feature to schedule jobs",
                      "Implementation: Publish message with delay = (scheduled_time - now), consumer picks up when delay expires",
                      "Pros: Simple, leverages existing message queue, supports priorities",
                      "Cons: Limited to max delay (SQS: 15 min, need rescheduling), no cron support, poor for recurring jobs",
                      "Use case: One-time delayed jobs, reminder systems"
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "10. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Horizontal Executor Scaling - Add more executor instances, Kafka partitioning ensures parallel processing",
                  "Technique 2: Database Indexing - Index on (next_run_time, status, last_polled_time) for fast watcher queries",
                  "Technique 3: Job Caching - Cache job definitions in Redis (TTL: 5 min), reduces DB reads by 90%",
                  "Technique 4: Kafka Partitioning - Partition 'run' topic by job_id ensures ordering, prevents race conditions",
                  "Technique 5: Read Replicas - Route job history queries to PostgreSQL read replicas, writes to primary only",
                  "Technique 6: Watcher Sharding - Multiple watcher instances with different polling intervals (watcher-1: every 20s, watcher-2: every 60s for low-priority jobs)",
                  "Technique 7: Priority Queues - Separate Kafka topics for high/low priority jobs, high-priority consumers have more instances",
                  "Technique 8: Batch Processing - Watcher fetches 1000 jobs per query instead of 1, publishes in batch to Kafka",
                  "Technique 9: Circuit Breaker - If external job dependency (API) fails >5 times, pause job for 10 min, prevent spam",
                  "Technique 10: Execution Pooling - Executors maintain pool of worker threads/processes, reuse for multiple jobs (avoid cold start)",
                  "Technique 11: Log Aggregation - Stream job logs to S3/CloudWatch async, don't block job execution on log writes",
                  "Technique 12: Dead Letter Queue Monitoring - Alert when dead topic has >100 messages, indicates systemic issue"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you prevent the same job from being scheduled twice by multiple watcher instances?",
                    "a": "Distributed locking with Redis: (1) Lock acquisition - before scheduling job, watcher attempts SET lock:job:{job_id}:schedule {watcher_id} NX EX 60 (set if not exists, 60s expiry), (2) Lock success - if returns 1, watcher proceeds to publish job to Kafka, updates last_polled_time in DB, releases lock, (3) Lock failure - if returns 0, another watcher already processing this job, skip to next job, (4) Database-level protection - UPDATE jobs SET last_polled_time = NOW() WHERE job_id = {id} AND last_polled_time < (NOW - 30s), only updates if not recently polled, (5) Auto-expiry - lock expires in 60s if watcher crashes, prevents deadlock. Alternative: Use Kafka as single-source scheduling - only 1 watcher publishes to Kafka (leader election via Zookeeper), consumers handle rest. Trade-off: Redis lock is faster, simpler vs Kafka leader election is more robust for multi-datacenter. Example: Job J1 next_run_time=10:00 AM \u2192 Watcher-1 and Watcher-2 poll at same time \u2192 Watcher-1 acquires lock \u2192 publishes J1 to Kafka \u2192 releases lock \u2192 Watcher-2's lock fails \u2192 skips J1."
                  },
                  {
                    "q": "How do you handle jobs that are scheduled while the scheduler is down?",
                    "a": "Backfill mechanism: (1) Detection - on watcher restart, query jobs WHERE next_run_time < NOW() AND status IN ('scheduled', 'running'), identifies missed jobs, (2) Categorization - jobs with next_run_time in last 2 hours = recent misses (high priority), >2 hours = stale (may skip or backfill based on policy), (3) Backfill execution - for each missed job, create JobRun with scheduled_at = original_next_run_time, status='backfill', publish to Kafka 'run' topic, (4) Rate limiting - publish backfill jobs at 100 jobs/sec to prevent overwhelming executors (if 1000 missed jobs, takes 10s to queue all), (5) SLA check - if job has SLA (must run within 1 hour of schedule), skip if SLA breached, mark as 'missed_sla', alert user, (6) Recurring jobs - for cron jobs, calculate all missed runs: if daily job down for 3 days, create 3 JobRuns for Day 1, 2, 3, or skip to most recent (configurable). Example: Scheduler down from 10:00-12:00, Job A scheduled hourly (10:00, 11:00, 12:00) \u2192 on restart at 12:05, detect 2 missed runs \u2192 backfill creates runs for 10:00, 11:00 \u2192 publish both to Kafka \u2192 executors process \u2192 update next_run_time to 13:00."
                  },
                  {
                    "q": "How do you implement retry mechanism with exponential backoff?",
                    "a": "Multi-stage retry pipeline: (1) Initial failure - executor catches exception, checks job.retry_count (e.g., 3) vs current attempt (1), if retries remaining, proceed, (2) Delay calculation - retry_delay = base_delay * (2 ^ (attempt - 1)) + random(0, base_delay/2), example: attempt 1 \u2192 60s, attempt 2 \u2192 120s + jitter (0-30s), attempt 3 \u2192 240s + jitter, (3) Publish to retry topic - executor publishes to Kafka 'retry' with {job_id, run_id, payload, attempt: 2, scheduled_retry_at: NOW() + retry_delay}, (4) Retry consumer - dedicated consumer group reads 'retry' topic, for each message: calculate wait_time = scheduled_retry_at - NOW(), if wait_time > 0: sleep(wait_time), then republish to 'run' topic, (5) Final failure - if attempt > retry_count, publish to 'dead' topic, update DB status='failed_permanently', send alert via SNS/email, (6) Jitter rationale - prevents thundering herd (100 jobs fail at same time, all retry at exact same moment without jitter). Alternative: Use Kafka delayed message feature (Kafka 3.0+) or SQS delay queue for retry scheduling. Example: Job fails at 10:00 \u2192 retry_count=3 \u2192 attempt 1 fails \u2192 retry at 10:01 (60s) \u2192 attempt 2 fails \u2192 retry at 10:03 (120s) \u2192 attempt 3 fails \u2192 retry at 10:07 (240s) \u2192 attempt 4 (exceeds retry_count) \u2192 dead letter queue."
                  },
                  {
                    "q": "How do you handle job dependencies in a DAG (Directed Acyclic Graph)?",
                    "a": "Dependency resolution with topological ordering: (1) Storage - Job table has dependencies column: job_b.dependencies = [job_a_id, job_c_id] means B depends on A and C, (2) Cycle detection - on job creation/update, run DFS (Depth-First Search) starting from new job, if visit same node twice, cycle exists, reject with 'Circular dependency detected: A\u2192B\u2192C\u2192A', (3) Scheduling check - watcher before scheduling Job B, queries: SELECT COUNT(*) FROM job_runs WHERE job_id IN (job_a_id, job_c_id) AND status='success' AND DATE(scheduled_at) = TODAY, if count != 2 (missing dependencies), skip Job B this cycle, (4) Trigger on completion - when Job A completes successfully, publish 'job.completed' event to Kafka with {job_id: A, run_date}, (5) Dependency watcher - separate service consumes 'job.completed', queries jobs WHERE dependencies CONTAINS job_a_id, checks if all dependencies now met, if yes, publishes dependent jobs to 'run' topic, (6) Parallel execution - jobs at same level (C and D both depend only on B) published simultaneously for parallel execution. Alternative: Airflow's approach - precompute DAG at deployment, store as graph in memory, traverse on each run. Example: DAG: A \u2192 B \u2192 C, D (B depends on A, C and D depend on B) \u2192 A runs at 10:00, succeeds \u2192 B scheduled for 10:05, succeeds \u2192 C and D both scheduled for 10:10 in parallel."
                  },
                  {
                    "q": "What happens if an executor crashes while running a job?",
                    "a": "Executor failure detection and recovery: (1) Heartbeat monitoring - executors publish heartbeat to Redis executor:{id}:heartbeat every 10s with timestamp, (2) Watcher health check - separate health check service polls Redis every 30s, checks if any executor's heartbeat > 60s old (3 missed heartbeats), marks as 'unhealthy', (3) Job identification - query JobRuns WHERE executor_id = {unhealthy_id} AND status='running', finds orphaned jobs, (4) Rescheduling - for each orphaned job: update status='executor_died', create new JobRun with attempt = old_attempt + 1 (counts as retry), publish to Kafka 'run' topic, (5) Cleanup - remove executor:{id} from Redis executors:available pool, alert ops team, (6) Idempotency requirement - jobs must be idempotent (safe to run multiple times) because crashed job may have partially completed before executor died. Example: Executor E1 running Job J1 (writing to database) \u2192 E1 crashes at 50% completion \u2192 heartbeat stops \u2192 health check detects after 60s \u2192 Job J1 rescheduled to Executor E2 \u2192 E2 re-runs full job \u2192 job's code must handle 'partial completion' scenario (check if data already written, skip or upsert). Prevention: Use distributed locks for critical sections (job locks specific resource before modifying)."
                  },
                  {
                    "q": "How do you implement priority-based job scheduling?",
                    "a": "Multi-tier priority queue system: (1) Priority definition - jobs have priority field (1-10, 10=highest), stored in Job table, (2) Topic separation - Kafka topics: high_priority_run (priority 8-10), medium_priority_run (5-7), low_priority_run (1-4), (3) Watcher routing - when scheduling job, publish to appropriate topic based on job.priority, (4) Consumer allocation - high priority topic has 50 consumer instances, medium has 30, low has 20 (2.5x more resources for high priority), (5) Within-topic ordering - jobs in same priority topic processed FIFO (First In First Out) using Kafka partitioning, (6) Starvation prevention - if low priority jobs waiting >1 hour, temporarily boost to medium priority (aging algorithm). Alternative: Single topic with priority header, consumers poll high-priority partitions more frequently. Example: Job A (priority 9) and Job B (priority 3) scheduled at same time \u2192 A published to high_priority_run, B to low_priority_run \u2192 high topic has 50 consumers, low has 20 \u2192 A assigned to executor in <1s, B waits 5s for executor. Trade-off: More topics = more complexity but better isolation, single topic = simpler but consumers need priority-aware logic."
                  },
                  {
                    "q": "How do you handle time zone conversions for scheduled jobs?",
                    "a": "Server-side time zone normalization: (1) Storage - all timestamps in DB stored as UTC (jobs.next_run_time = '2025-01-21T00:00:00Z'), (2) User input - job creation accepts schedule with time zone: {schedule: '0 0 * * *', timezone: 'America/New_York'}, (3) Conversion on save - backend converts to UTC using timezone library (pytz in Python): cron '0 0 * * *' in EST (UTC-5) \u2192 UTC '0 5 * * *', stores UTC version, (4) Display - when user views job, convert back to their timezone for display: '2025-01-21T00:00:00Z' UTC \u2192 '2025-01-20T19:00:00' EST, (5) DST handling - recalculate next_run_time when daylight saving time changes (March/November), e.g., job '9 AM EST' shifts 1 hour in UTC, (6) Watcher logic - always works in UTC, no timezone awareness needed for scheduling. Complexity: Recurring jobs across DST boundary - job scheduled for '2 AM EST daily' on DST start day (2 AM doesn't exist), skip to 3 AM or run at 1 AM (configurable). Example: User in India (IST, UTC+5:30) schedules job for daily at midnight local time \u2192 backend stores as UTC 18:30 previous day \u2192 watcher at 18:30 UTC triggers job \u2192 user sees '00:00 IST' in UI. Alternative: Store timezone with each job, watcher converts on-the-fly (more flexible but complex)."
                  },
                  {
                    "q": "How do you implement job cancellation for already running jobs?",
                    "a": "Graceful cancellation protocol: (1) User request - POST /jobs/{job_id}/cancel, service checks status, if 'running', publish to Kafka 'cancel' topic with {job_id, run_id}, (2) Executor subscription - all executors subscribe to 'cancel' topic with consumer group per executor (ensures all get message), (3) Executor matching - executor checks if run_id matches currently running job, if yes, initiates cancellation, (4) Signal sending - executor sends SIGTERM (signal 15) to job process, allows graceful shutdown (close connections, save state), (5) Timeout - if process doesn't exit in 30s, send SIGKILL (signal 9) to force terminate, (6) Status update - executor updates DB status='cancelled', end_time=NOW(), publishes 'job.cancelled' event, (7) Cleanup - release any distributed locks held by job, rollback transactions if applicable. Edge case: Job already completed before cancellation received \u2192 executor ignores cancel message, DB status remains 'success'. Alternative: Use shared memory flag (Redis cancel:{run_id} = true), job periodically checks flag and exits if set (requires job code cooperation). Example: Long-running ETL job (2 hours) \u2192 user cancels after 30 min \u2192 cancel event published \u2192 executor sends SIGTERM \u2192 job's cleanup handler runs (commits partial data) \u2192 exits gracefully \u2192 status='cancelled', user can retry or analyze partial results."
                  },
                  {
                    "q": "How do you prevent job duplication on executor restart?",
                    "a": "Idempotent job execution with state tracking: (1) Job uniqueness - each JobRun has unique run_id, executor stores currently_running_job = run_id in Redis on start, (2) Restart detection - on executor restart, check Redis for currently_running_job, if exists, it was killed mid-execution, (3) Status reconciliation - query DB for run_id status, if still 'running', mark as 'executor_died' (executor crashed), don't re-execute (prevents duplication), (4) Kafka offset management - executor commits Kafka offset AFTER job completes, on restart, uncommitted messages re-delivered (at-least-once), (5) Idempotency enforcement - job_id + scheduled_at combination ensures uniqueness, if executor tries to start job with same (job_id, scheduled_at), DB unique constraint fails, skip execution, (6) Exactly-once attempt - use Kafka transactions (Kafka 0.11+) with transactional.id per executor, ensures message processed and offset committed atomically. Trade-off: At-least-once (simpler, requires idempotent jobs) vs exactly-once (complex, guarantees no duplication but slower). Example: Executor E1 running Job J1 (run_id=R1) \u2192 E1 crashes \u2192 E1 restarts \u2192 checks Redis, finds currently_running_job=R1 \u2192 queries DB, R1 status='running' \u2192 updates R1 to 'executor_died', doesn't re-run \u2192 health check reschedules R1 with new run_id=R2 \u2192 counted as retry."
                  },
                  {
                    "q": "What's your strategy for monitoring and alerting on job failures?",
                    "a": "Multi-level monitoring and alerting: (1) Metrics collection - executors publish metrics to Prometheus/CloudWatch: job_success_count, job_failure_count, job_duration_ms, jobs_in_queue (by priority), (2) Failure thresholds - alert rules: single job failure rate >50% (6 of last 10 runs failed) = critical alert, overall system failure rate >10% in 1 hour = warning, (3) SLA monitoring - if job has SLA (must complete within 2 hours of schedule), alert if end_time - scheduled_at > 2 hours, (4) Dead letter queue size - alert if dead topic has >100 messages (indicates systemic issue, not isolated failures), (5) Executor health - alert if <30% executors healthy (capacity issue), (6) Dashboard - Grafana dashboard showing: jobs by status (pie chart), execution time trends (line graph), failure reasons (top 10 errors). Alert channels: (7) PagerDuty for critical (job X failed 5 times), (8) Slack for warnings (10% system failure rate), (9) Email daily digest of all failures. Example: ETL job fails 3 times in row \u2192 alert 'Job ETL_Pipeline failing (3/3 attempts), error: DB connection timeout' sent to Slack \u2192 on-call engineer investigates, finds DB overloaded \u2192 scales DB \u2192 job succeeds on retry 4."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "12. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Throughput",
                    "pairs": [
                      {
                        "k": "Job Volume",
                        "v": "Millions of jobs per day, 1000s of jobs per second at peak"
                      },
                      {
                        "k": "Executors",
                        "v": "100-1000 executor instances for parallel execution"
                      },
                      {
                        "k": "Watcher Polling",
                        "v": "Every 20 seconds, fetches up to 1000 jobs per poll"
                      },
                      {
                        "k": "Kafka Throughput",
                        "v": "10K messages/sec across all topics (run, retry, dead)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Latency & Timing",
                    "pairs": [
                      {
                        "k": "Scheduling Latency",
                        "v": "\u00b12 seconds from scheduled time (watcher interval + Kafka latency)"
                      },
                      {
                        "k": "Executor Heartbeat",
                        "v": "Every 10 seconds to Redis for health monitoring"
                      },
                      {
                        "k": "Health Check Interval",
                        "v": "Every 30 seconds, marks executor dead after 60s no heartbeat"
                      },
                      {
                        "k": "Lock Expiry",
                        "v": "60 seconds for distributed locks (prevents deadlock)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Retry & Recovery",
                    "pairs": [
                      {
                        "k": "Default Retry Count",
                        "v": "3 retries (4 total attempts)"
                      },
                      {
                        "k": "Exponential Backoff",
                        "v": "60s, 120s, 240s for attempts 1, 2, 3 with jitter"
                      },
                      {
                        "k": "Job Timeout",
                        "v": "Default 3600s (1 hour), configurable per job"
                      },
                      {
                        "k": "Cancellation Timeout",
                        "v": "30s for SIGTERM, then SIGKILL"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Database & Caching",
                    "pairs": [
                      {
                        "k": "Watcher Query Limit",
                        "v": "1000 jobs per query (batch processing)"
                      },
                      {
                        "k": "Job Cache TTL",
                        "v": "5 minutes in Redis for job definitions"
                      },
                      {
                        "k": "Index Fields",
                        "v": "next_run_time, status, last_polled_time for fast queries"
                      },
                      {
                        "k": "Kafka Retention",
                        "v": "7 days for replay capability"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Priority Scheduling",
                    "pairs": [
                      {
                        "k": "Priority Levels",
                        "v": "1-10 scale (10=highest, 1=lowest)"
                      },
                      {
                        "k": "Consumer Allocation",
                        "v": "High: 50 instances, Medium: 30, Low: 20"
                      },
                      {
                        "k": "Starvation Prevention",
                        "v": "Boost priority if waiting >1 hour"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Monitoring Thresholds",
                    "pairs": [
                      {
                        "k": "Failure Rate Alert",
                        "v": "Single job >50% failure rate (6/10 runs)"
                      },
                      {
                        "k": "System Failure Alert",
                        "v": "Overall >10% failure rate in 1 hour"
                      },
                      {
                        "k": "Dead Queue Alert",
                        "v": ">100 messages in dead letter queue"
                      },
                      {
                        "k": "Executor Capacity Alert",
                        "v": "<30% healthy executors remaining"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Example Calculation - Job Scheduling",
                    "pairs": [
                      {
                        "k": "Watcher Poll",
                        "v": "20s interval, fetches 1000 jobs"
                      },
                      {
                        "k": "Lock Acquisition",
                        "v": "10ms per job (Redis SETNX)"
                      },
                      {
                        "k": "Kafka Publish",
                        "v": "50ms for batch of 1000 jobs"
                      },
                      {
                        "k": "Consumer Processing",
                        "v": "100ms from Kafka to executor assignment"
                      },
                      {
                        "k": "Total Latency",
                        "v": "20s (polling) + 0.16s (processing) = ~20s from schedule time"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Resource Allocation",
                    "pairs": [
                      {
                        "k": "Executor Pool Size",
                        "v": "100 executors \u00d7 10 threads/executor = 1000 concurrent jobs"
                      },
                      {
                        "k": "Memory per Job",
                        "v": "512MB default, configurable up to 4GB"
                      },
                      {
                        "k": "CPU per Job",
                        "v": "0.5 vCPU default, configurable up to 4 vCPU"
                      },
                      {
                        "k": "Backfill Rate",
                        "v": "100 jobs/sec to prevent overload"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "NEVER assume jobs are idempotent. Always design for at-least-once execution. Executors may crash mid-job, jobs may be rescheduled, retries happen. Jobs MUST handle duplicate execution safely (check state, upsert not insert)."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How to prevent duplicate scheduling?'. Answer: (1) Redis distributed lock (SETNX) before publishing to Kafka, (2) Database last_polled_time update with WHERE clause, (3) Kafka exactly-once semantics with transactional.id. Show understanding of multiple layers."
              },
              {
                "type": "tip",
                "text": "Key optimization: Batch watcher queries. Fetching 1 job per query = 1000 queries/min overhead. Fetching 1000 jobs per query = 1 query/min, 1000x reduction in DB load. Pagination + batch Kafka publishing critical for scale."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Exponential backoff with jitter. Without jitter, 100 jobs failing at same time all retry at exact same moment \u2192 thundering herd \u2192 system overload. Jitter (random 0-30s) spreads retries over time."
              },
              {
                "type": "warning",
                "text": "NEVER use polling interval <10 seconds for watcher. 5s interval = 12 polls/min = 12 DB queries/min + 12 lock acquisitions. Minimal latency improvement but 2x overhead. 20s interval is sweet spot for most use cases."
              },
              {
                "type": "tip",
                "text": "DAG cycle detection is critical. Allow A\u2192B\u2192C\u2192A dependency creates infinite loop, system hangs. Run DFS on dependency graph during job creation, reject if cycle found. Also limit dependency depth (e.g., max 10 levels)."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love asking: 'What if executor crashes during job execution?'. Answer: (1) Heartbeat monitoring detects dead executor, (2) Orphaned jobs rescheduled as retries, (3) Jobs must be idempotent. Show understanding of failure recovery."
              },
              {
                "type": "warning",
                "text": "NEVER store job logs in database. 100K jobs/day \u00d7 10KB logs = 1GB/day, 365GB/year in DB (expensive, slow queries). Stream logs to S3/CloudWatch, store only URL in DB. DB for metadata, object storage for logs."
              },
              {
                "type": "tip",
                "text": "Priority queue via separate Kafka topics is simpler than single-topic priority. 3 topics (high/med/low) with different consumer counts gives natural prioritization. Single topic requires custom consumer logic to peek at priority header."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: At-least-once vs exactly-once execution. At-least-once is simpler (Kafka default), requires idempotent jobs. Exactly-once needs Kafka transactions (complex, performance hit). For job scheduling, at-least-once + idempotency is standard."
              }
            ],
            "tags": [
              "system-design",
              "job-scheduler",
              "distributed-system",
              "Airflow",
              "Temporal",
              "cron-jobs",
              "Kafka",
              "Redis",
              "PostgreSQL",
              "retry-mechanism",
              "DAG",
              "workflow-orchestration",
              "distributed-locking",
              "backfill",
              "executor-pool",
              "at-least-once",
              "idempotency",
              "exponential-backoff"
            ]
          }
        },
        {
          "id": "10",
          "title": "System Design 10: Design Real-time Collaborative Text Editor like Google Docs | Notion | HLD | LLD",
          "videoUrl": "https://youtu.be/-eMtcFqj8vI",
          "description": "In this system design tutorial, we walk through the architecture, components, and data flow needed to build a Google Docs clone that supports multiple users editing the same document simultaneously.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/qwenongot34t.png?updatedAt=1750952624908",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/bnijbiub87g86uyv76f675d6t.jpeg?updatedAt=1750953263660",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-22",
            "hero": {
              "title": "Real-Time Collaborative Text Editor (Google Docs)",
              "oneLinerToMemorize": "WebSocket session \u2192 Operations (insert/delete) \u2192 OT (Operational Transformation) or CRDT \u2192 Op-log persistence \u2192 Periodic snapshots \u2192 Eventual convergence"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Users should be able to create/update/delete documents with rich text formatting",
                  "Feature 2: Multiple users can edit the same document simultaneously in real-time (concurrent editing)",
                  "Feature 3: Users should be able to view each other's changes in real-time with cursor positions and presence",
                  "Feature 4: Share documents with permissions (view only, edit, comment)",
                  "Feature 5: Version history and ability to rollback to previous versions",
                  "Feature 6: Offline editing with automatic sync when connection restored",
                  "Feature 7: Auto-save to prevent data loss (periodic snapshots)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Users",
                        "v": "Millions of users with millions of documents"
                      },
                      {
                        "k": "Concurrent Editors",
                        "v": "Hot docs: Up to 50-100 users editing same document simultaneously"
                      },
                      {
                        "k": "Document Size",
                        "v": "Support large documents (1.02M characters Google Docs limit)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance & Consistency",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability & Consistency (formal Document) - eventual consistency acceptable with guaranteed convergence"
                      },
                      {
                        "k": "Latency",
                        "v": "Updates should be live (< 300ms) for real-time collaboration experience"
                      },
                      {
                        "k": "Convergence",
                        "v": "Goal: All clients must end up with the same document state (strong eventual consistency)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities (from image)",
                "type": "bullets",
                "items": [
                  "Entity 1: Document - doc_id, owner_id, title, metadata (created_at, updated_at, permissions)",
                  "Entity 2: Operation/Edit - op_id, doc_id, user_id, type (insert/delete), position, content, timestamp, client_version",
                  "Entity 3: Snapshot/Version - version_id, doc_id, snapshot_blob_path (S3), operations_count, timestamp",
                  "Entity 4: Cursor/Presence - Ephemeral data (user_id, cursor_position, selection_range, color) - not persisted in DB"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing (from image)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Document Management",
                    "pairs": [
                      {
                        "k": "POST /v1/api/docs/create",
                        "v": "Create a document - Returns a document ID"
                      },
                      {
                        "k": "GET /v1/api/docs/{docId}",
                        "v": "View Document (ReadOnly) - Fetch latest snapshot"
                      },
                      {
                        "k": "GET /v1/api/docs/{docId}/version",
                        "v": "Get all the versions (version history)"
                      },
                      {
                        "k": "GET /v1/api/docs/{docId}/version/{versionId}",
                        "v": "Open a specific version (rollback capability)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Real-Time Editing (WebSocket)",
                    "pairs": [
                      {
                        "k": "WS /v1/api/docs/{docId}/edit",
                        "v": "WebSocket connection for collaborative editing - bidirectional operation stream"
                      },
                      {
                        "k": "Client \u2192 Server",
                        "v": "Send operations (insert/delete/format) with optimistic local updates"
                      },
                      {
                        "k": "Server \u2192 Client",
                        "v": "Broadcast transformed operations to all connected clients for synchronization"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design (from image)",
                "type": "bullets",
                "items": [
                  "Clients (users/clients) \u2192 LB/API Gateway: Load balancing and authentication & authorization & routing",
                  "WebSocket LB + Gateway: Maintains persistent WebSocket connections, handles 10K connections per instance",
                  "Document Metadata Service \u2192 DB: Stores document metadata (owner, permissions, title) - PostgreSQL",
                  "Document Editor Service: Core business logic, maintains canonical state in memory, processes operations",
                  "Blob Storage (S3): Stores document snapshots for fast recovery, organized as docs/{doc_id}/v{version}.txt",
                  "Redis (Canonical Copy): Short-lived cache of current document state for fast access across instances",
                  "Op-log (DB): Persistent storage of all operations (append-only), enables replay and versioning",
                  "Kafka: Event streaming for operations, decouples editor service from consumers",
                  "Reconciliation Service (from image): Rebuilds document from op-log, resolves conflicts, generates new snapshots",
                  "CDN: Serves static assets (editor UI, fonts, styles) for fast global access"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level - from image)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Document Creation & Loading",
                    "steps": [
                      "User creates: POST /v1/api/docs/create with {title: 'Meeting Notes', owner_id: user_A}",
                      "Document Metadata Service: INSERT INTO documents (doc_id, owner_id, title, created_at, updated_at, permissions) VALUES ({uuid}, {user_A}, 'Meeting Notes', now(), now(), '{}')",
                      "Create initial snapshot: Upload to S3 docs/{doc_id}/v0.txt with empty content, store reference: INSERT INTO snapshots (snapshot_id, doc_id, version, blob_path, operations_since_last: 0, created_at)",
                      "Response: {doc_id, edit_url: '/docs/{doc_id}/edit'}",
                      "User opens document: GET /v1/api/docs/{doc_id}",
                      "Check permissions: SELECT permissions FROM documents WHERE doc_id={doc_id}, if user not owner and not in shared_with[] \u2192 return 403 Forbidden",
                      "Fetch latest snapshot: SELECT blob_path, version FROM snapshots WHERE doc_id={doc_id} ORDER BY version DESC LIMIT 1",
                      "Download from S3: GET docs/{doc_id}/v0.txt \u2192 current content",
                      "Fetch operations since snapshot: SELECT * FROM operations WHERE doc_id={doc_id} AND timestamp > {snapshot_timestamp} ORDER BY timestamp ASC",
                      "Apply operations: Replay operations on snapshot to get current state (if any ops exist since last snapshot)",
                      "Return: {doc_id, content, current_version, permissions}"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: WebSocket Connection & Presence",
                    "steps": [
                      "User clicks 'Edit': Client establishes WebSocket: WS /v1/api/docs/{doc_id}/edit with Authorization: Bearer {JWT}",
                      "WebSocket Gateway: (1) Authenticates token, (2) Validates user has 'edit' permission, (3) Creates session {session_id, user_id, doc_id, connected_at}",
                      "Load current state: (a) Check Redis: GET doc:{doc_id}:state, if hit return cached state (1ms), (b) If miss: fetch snapshot from S3 + replay recent operations from op-log, cache in Redis with TTL=30 min",
                      "Send initial state: Server \u2192 Client via WebSocket: {type: 'init', content: {current_document_text}, current_version: 42, active_users: [{user_id, name, cursor_pos, color}]}",
                      "Broadcast presence: Server \u2192 All connected clients: {type: 'user_joined', user: {user_id: A, name: 'Alice', color: '#FF5733'}}",
                      "Client displays: Active users list in top bar, colored cursor indicators for each user"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Real-Time Editing - Operation Flow (from image)",
                    "steps": [
                      "User A types: insert(R, 29) - inserts character 'R' at position 29",
                      "Client optimistic update: Immediately shows 'R' in local editor (no wait for server), queues operation for sending",
                      "Client sends operation: Via WebSocket \u2192 {type: 'operation', op: {op_id: uuid(), type: 'insert', position: 29, content: 'R', client_version: 42, user_id: A, timestamp: now()}}",
                      "WebSocket Gateway receives: Validates session active, forwards to Document Editor Service",
                      "Document Editor Service: (1) Assigns server timestamp and sequence number, (2) Appends to op-log: INSERT INTO operations (op_id, doc_id, user_id, type, position, content, client_version, server_version, timestamp), (3) Publishes to Kafka 'doc.operations' topic: {doc_id, op_id, type, position, content, user_id, server_version: 43}",
                      "OT/CRDT Transformation (if concurrent ops exist - explained in next step)",
                      "Gateway broadcasts: Transformed operation to all OTHER connected clients (User B, C, D) via WebSocket: {type: 'operation', op: {type: 'insert', position: 29, content: 'R', user_id: A}}",
                      "Clients B, C, D apply: Receive operation, apply to their local state at position 29, see 'R' appear in real-time"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Conflict Resolution - Operational Transformation (OT) [from image diagram]",
                    "steps": [
                      "Scenario: User A and User B both editing at same time, document version 10, content: 'abc'",
                      "User A types at position 0: insert('A', 0) \u2192 'Aabc', sends to server",
                      "User B types at position 3: insert('D', 3) \u2192 'abcD', sends to server",
                      "Server receives both: (1) A's operation arrives first (network timing), assigned server_version: 11, (2) B's operation arrives second, assigned server_version: 12",
                      "OT Service processes B's operation: (1) Checks: B's client_version=10, but server is now at version 11 (A's op applied), (2) Transformation needed: B intended position 3 in 'abc', but A inserted at 0 \u2192 doc now 'Aabc' (4 chars), (3) Transform B's position: original position 3 + 1 (A inserted before) = new position 4, (4) Transformed operation: insert('D', 4) applies to 'Aabc' \u2192 'AabcD'",
                      "Broadcast: (1) Send A's op to B: insert('A', 0), (2) Send transformed B's op to A: insert('D', 4), (3) Both clients apply operations",
                      "Final state: Both clients converge to 'AabcD', order guaranteed by server sequencing",
                      "Key insight (from image): 'Switching happens only if keyframe is set at 0th second' - operations must be sequenced by server to ensure all clients see same order",
                      "Delete transformation example: User A: delete(2), User B: insert('X', 5), if A's delete executed first \u2192 B's position shifts: 5 \u2192 4 (one char removed before position)"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Alternative - CRDT Approach (from image diagram)",
                    "steps": [
                      "CRDT (Conflict-Free Replicated Data Types): Operations designed to be commutative (can be applied in any order)",
                      "Character-based CRDT: Each character has unique ID (site_id + sequence_number): 'A' = {site: 1, seq: 1}, 'B' = {site: 1, seq: 2}",
                      "User A inserts: {site: 1, seq: 10, char: 'X', after: {site: 1, seq: 5}}, User B inserts: {site: 2, seq: 5, char: 'Y', after: {site: 1, seq: 5}}",
                      "Both operations can be applied in ANY order: (1) A then B: ...X...Y..., (2) B then A: ...Y...X..., (3) Conflict resolution: Deterministic ordering rule (e.g., site_id tiebreaker) ensures same final order",
                      "Advantages: (1) No central server sequencing required, (2) Works offline (operations stored locally), (3) Multi-master replication (multiple servers can accept writes)",
                      "Disadvantages: (1) More complex data structure (tombstones for deletes), (2) Larger metadata overhead (each char has unique ID), (3) Memory grows with edit history",
                      "Example (from image): ABC \u2192 BCD, (1) User A sees 'ABC', inserts at 0: 'AABC' (marked inconsistent), (2) User B sees 'ABC', deletes at 0: 'BC', (3) With OT: Server sequences, both converge to 'BC', (4) With CRDT: Operations commute, both converge to 'BC' (delete wins based on ID ordering)"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Cursor & Presence Updates (from image shows real-time updates)",
                    "steps": [
                      "User A moves cursor: Client detects cursor position change: {cursor_pos: 15, selection_start: 10, selection_end: 15}",
                      "Throttling: Batch cursor updates every 100ms (reduce WebSocket traffic), if no change in 100ms \u2192 skip",
                      "Client sends: Via WebSocket: {type: 'cursor', user_id: A, cursor_pos: 15, selection: {start: 10, end: 15}, timestamp}",
                      "Gateway broadcasts: To all OTHER clients (B, C, D): {type: 'cursor_update', user: {user_id: A, name: 'Alice', color: '#FF5733', cursor_pos: 15, selection: {start: 10, end: 15}}}",
                      "Clients render: (1) Colored cursor at position 15 with label 'Alice', (2) Selection highlight from 10-15 in Alice's color (semi-transparent), (3) Update active users list showing position",
                      "Ephemeral data: Cursor positions NOT persisted in DB (recreated on reconnect), stored in Redis with TTL=5 min: HSET cursors:{doc_id} user:{user_id} '{cursor_pos, selection, timestamp}' EX 300",
                      "User disconnects: Server removes cursor: DELETE cursors:{doc_id} user:{user_id}, broadcasts: {type: 'user_left', user_id: A} \u2192 other clients remove cursor indicator"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Auto-Save & Snapshot Creation (from image shows TTL and S3)",
                    "steps": [
                      "Trigger: Every 50 operations OR every 5 minutes (whichever comes first)",
                      "Background job checks: SELECT COUNT(*) FROM operations WHERE doc_id={doc_id} AND timestamp > {last_snapshot_timestamp}, if count >= 50 \u2192 trigger snapshot",
                      "Reconciliation Service (from image): (1) Fetch latest snapshot: GET S3 docs/{doc_id}/v{last_version}.txt, (2) Fetch operations since snapshot: SELECT * FROM operations WHERE doc_id={doc_id} AND server_version > {last_version} ORDER BY server_version ASC, (3) Replay operations: Apply each operation sequentially to reconstruct current state, (4) Generate new snapshot content",
                      "Upload to S3: PUT docs/{doc_id}/v{new_version}.txt with current content (50 operations applied)",
                      "Update database: INSERT INTO snapshots (snapshot_id, doc_id, version, blob_path, operations_since_last: 50, created_at: now()), UPDATE documents SET latest_snapshot_version={new_version}, updated_at=now()",
                      "Update Redis cache: SET doc:{doc_id}:state {current_content} EX 1800 (30 min TTL)",
                      "Publish event: Kafka 'doc.snapshot_created' \u2192 {doc_id, version, operations_count: 50} for analytics"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Version History & Rollback (from image shows version API)",
                    "steps": [
                      "User requests: GET /v1/api/docs/{doc_id}/version \u2192 fetch version history",
                      "Service queries: SELECT version, created_at, operations_since_last, created_by FROM snapshots WHERE doc_id={doc_id} ORDER BY version DESC LIMIT 20",
                      "Response: [{version: 5, created_at: '2026-01-22T10:30:00Z', operations: 50, label: 'Auto-save'}, {version: 4, ...}, ...]",
                      "User selects version: GET /v1/api/docs/{doc_id}/version/3 to rollback to version 3",
                      "Service fetches: (1) Snapshot: GET S3 docs/{doc_id}/v3.txt, (2) Display read-only preview to user",
                      "User confirms restore: POST /v1/api/docs/{doc_id}/restore with {target_version: 3}",
                      "Service creates new version: (1) Fetch v3 content, (2) Upload as new snapshot: PUT S3 docs/{doc_id}/v{new_version}.txt with v3 content, (3) INSERT new snapshot record, (4) Update doc latest_snapshot_version",
                      "Notify active editors: Via WebSocket: {type: 'document_restored', version: 3, restored_by: user_B, message: 'Document restored to version 3, refresh to see changes'}",
                      "Clients refresh: Fetch latest version, reload content, discard local unsaved changes"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Offline Editing & Sync (from image shows offline capability)",
                    "steps": [
                      "User goes offline: WebSocket connection closes (network failure), client detects via error/close event",
                      "Client switches to offline mode: (1) Display banner 'Working offline, changes will sync when online', (2) Store operations locally: IndexedDB or localStorage, (3) Continue editing (optimistic updates), (4) Queue all operations: [{op_id, type, position, content, timestamp, client_version}]",
                      "Local storage: SET offline_ops:{doc_id} [{op1, op2, op3, ...}], last_known_version: 42",
                      "User comes online: Network restored, WebSocket reconnects automatically (retry with exponential backoff)",
                      "Client sends sync request: Via WebSocket: {type: 'sync_request', doc_id, last_known_version: 42, queued_operations_count: 5}",
                      "Server responds: (1) Fetch operations from server since version 42: SELECT * FROM operations WHERE doc_id={doc_id} AND server_version > 42 ORDER BY server_version, (2) Send to client: {type: 'sync_response', server_operations: [{op1, op2, ...}], current_version: 50}",
                      "Client reconciliation: (1) Apply server operations using OT (transform local queued ops against server ops), (2) Send transformed local operations to server: {type: 'operations_batch', ops: [{transformed_op1, transformed_op2, ...}]}, (3) Server validates and applies if no conflicts",
                      "Conflict resolution: If conflicts detected \u2192 display modal 'Conflicts detected during sync, review changes', allow user to choose: keep local, keep server, or merge manually"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Locking Protocol (Pessimistic - Optional, from image)",
                    "steps": [
                      "Use case: Some applications require pessimistic locking (e.g., editing a specific section/paragraph at a time)",
                      "Optimistic (Git merge): Default for Google Docs - all users edit freely, conflicts resolved via OT/CRDT",
                      "Pessimistic (Banking): Lock sections before editing to prevent conflicts",
                      "Implementation: (1) User A selects paragraph: POST /v1/api/docs/{doc_id}/lock with {section_id: 'para_5'}, (2) Server acquires lock: SETNX lock:doc:{doc_id}:para_5 {user_A} EX 300 (5 min TTL), (3) If successful: return {locked: true, expires_at}, (4) If failed: return {locked: false, locked_by: user_B}, (5) User A edits paragraph \u2192 sends operations \u2192 Server validates lock still held \u2192 applies operations",
                      "Release lock: (1) User A finishes: DELETE lock:doc:{doc_id}:para_5, (2) Auto-release: TTL expires after 5 min if user disconnects, (3) Broadcast: {type: 'lock_released', section_id: 'para_5'} \u2192 other users can now request lock",
                      "From image note: 'Pessimistic (Banking)' - used when strong locking required, not typical for docs",
                      "Trade-off: Pessimistic reduces concurrent editing flexibility but eliminates conflicts entirely"
                    ]
                  },
                  {
                    "blockTitle": "Step 11: File Replacement (from image shows DataFlow)",
                    "steps": [
                      "Scenario: User uploads a new version of document (replaces existing content)",
                      "User clicks 'Replace file': Upload new document via POST /v1/api/docs/{doc_id}/replace with file in multipart/form-data",
                      "From image DataFlow: (1) User Uploads A Document (Read-Only Mode), (2) Initiate WebSocket to Document Editor Service, (3) Frontend fetches the document snapshot from S3 (Blob Storage), (4) Opening A document Loads snapshot + replay operations from DB, (5) Download is shown in local",
                      "Upload Service: (1) Validates file format (DOCX, TXT, PDF), (2) Extracts text content (if DOCX/PDF), (3) Creates new operation: {type: 'replace_all', content: {new_content}, user_id, timestamp}",
                      "Document Editor Service: (1) Append to op-log: INSERT INTO operations (op_id, doc_id, type: 'replace_all', content, user_id, timestamp), (2) Create new snapshot immediately: PUT S3 docs/{doc_id}/v{new_version}.txt with new content, (3) Update Redis: SET doc:{doc_id}:state {new_content} EX 1800",
                      "Notify active editors: Via WebSocket: {type: 'document_replaced', message: 'Document content replaced by User X, refresh to see new version'}",
                      "Clients reload: Fetch latest snapshot, discard local changes, display new content"
                    ]
                  },
                  {
                    "blockTitle": "Step 12: Reconciliation Service (from image shows Reconciliation Consumer)",
                    "steps": [
                      "Purpose: Ensures data consistency, rebuilds canonical state from op-log, generates snapshots",
                      "Kafka consumer: Subscribes to 'doc.operations' topic with consumer group 'reconciliation-service'",
                      "Processing: (1) Receives operation event: {doc_id, op_id, type, position, content, server_version}, (2) Checks if snapshot needed: if operations_since_last_snapshot >= 50 \u2192 trigger snapshot creation, (3) Rebuilds document: Fetch last snapshot + replay all operations \u2192 validate consistency",
                      "Conflict detection: If inconsistency found (e.g., operation position > document length) \u2192 alert, log error, skip operation",
                      "From image shows: Reconciliation Consumer \u2192 DB (for operation log) \u2192 S3 (generate snapshots) \u2192 Kafka (emit reconciliation events)",
                      "Periodic full reconciliation: Nightly job (2 AM) validates ALL documents: (1) Fetch snapshot + operations, (2) Replay to verify integrity, (3) Generate new snapshot if needed, (4) Report: {total_docs, snapshots_created, errors: []}",
                      "Error handling: If op-log corrupted \u2192 restore from last known good snapshot, mark operations as 'skipped', notify admin for manual review"
                    ]
                  },
                  {
                    "blockTitle": "Step 13: Events Passing via WebSockets (from image highlights this)",
                    "steps": [
                      "All real-time updates flow through WebSockets (from image shows 'Events passing via websockets connecter')",
                      "Event types: (1) 'operation' - edit operations (insert/delete/format), (2) 'cursor' - cursor position updates, (3) 'presence' - user joined/left, (4) 'lock' - section locked/unlocked, (5) 'sync' - sync request/response for offline users, (6) 'notification' - document restored/replaced/shared",
                      "WebSocket protocol: (1) Client sends: JSON over WebSocket {type: 'operation', op: {...}}, (2) Server broadcasts: JSON to all connected clients in same doc_id room, (3) Compression: Use WebSocket compression (permessage-deflate) to reduce bandwidth",
                      "Load balancing: WebSocket LB uses sticky sessions (session affinity), ensures user's WebSocket stays on same backend instance for duration of session",
                      "Failover: If WebSocket Gateway instance dies, client auto-reconnects to different instance (retry with exponential backoff), requests sync to catch up on missed operations"
                    ]
                  },
                  {
                    "blockTitle": "Step 14: Performance Optimization & Scaling",
                    "steps": [
                      "Redis as canonical copy: Short-lived cache (TTL 30 min) of current document state, serves 90% of read requests without hitting S3/DB, evicted when TTL expires or memory pressure",
                      "WebSocket Gateway scaling: Horizontal scaling with 10K connections per instance, auto-scale based on connection count (target: 7K connections per instance for headroom)",
                      "Op-log partitioning: Partition operations table by doc_id hash, each partition handles subset of documents, enables parallel processing",
                      "Snapshot compression: gzip compress snapshots before S3 upload (reduces storage cost by 70%), decompress on read",
                      "CDN for static assets: Editor UI, fonts, icons served via CDN (CloudFront) for fast global load times",
                      "Operation batching: Client batches rapid keystrokes (e.g., typing 'hello' sends 1 operation with 'hello' instead of 5 separate chars) if typing speed > 5 chars/sec",
                      "Lazy loading: Load document sections on-demand for very large docs (>100K chars), initial load shows first 10K chars, rest loaded as user scrolls",
                      "Garbage collection: Periodic cleanup of old operations (>90 days) after snapshots created, archive to S3 Glacier for compliance"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details (from image)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Documents (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "doc_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "owner_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "title",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb ({created_at, updated_at, file_type, tags})"
                      },
                      {
                        "k": "permissions",
                        "v": "jsonb ([{user_id, role: 'view'/'edit'/'comment'}])"
                      },
                      {
                        "k": "latest_snapshot_version",
                        "v": "int (current version number)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Operations (PostgreSQL - Op-log, append-only)",
                    "pairs": [
                      {
                        "k": "op_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "doc_id",
                        "v": "uuid FK \u2192 Documents, INDEXED for queries"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid (who made the edit)"
                      },
                      {
                        "k": "type",
                        "v": "enum ('insert', 'delete', 'format', 'replace_all')"
                      },
                      {
                        "k": "position",
                        "v": "int (character position in document)"
                      },
                      {
                        "k": "content",
                        "v": "text (inserted text or deleted text)"
                      },
                      {
                        "k": "client_version",
                        "v": "int (client's version when op created)"
                      },
                      {
                        "k": "server_version",
                        "v": "int (server-assigned sequence number)"
                      },
                      {
                        "k": "timestamp",
                        "v": "timestamp (server timestamp for ordering)"
                      },
                      {
                        "k": "Composite Index",
                        "v": "INDEX on (doc_id, server_version) for range queries"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Snapshots (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "snapshot_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "doc_id",
                        "v": "uuid FK \u2192 Documents, INDEXED"
                      },
                      {
                        "k": "version",
                        "v": "int (version number, incremental)"
                      },
                      {
                        "k": "blob_path",
                        "v": "varchar(500) (S3 path: docs/{doc_id}/v{version}.txt)"
                      },
                      {
                        "k": "operations_since_last",
                        "v": "int (count of operations applied)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "created_by",
                        "v": "uuid (user_id or 'system' for auto-save)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Canonical Copy & Locks (from image shows Redis with TTL)",
                    "pairs": [
                      {
                        "k": "doc:{docId}:state",
                        "v": "STRING (current document content) EX 1800 (30 min TTL)"
                      },
                      {
                        "k": "cursors:{docId}",
                        "v": "HASH {user_id: {cursor_pos, selection, timestamp}} EX 300 (5 min ephemeral)"
                      },
                      {
                        "k": "lock:doc:{docId}:{sectionId}",
                        "v": "STRING {user_id} EX 300 (pessimistic lock, 5 min TTL)"
                      },
                      {
                        "k": "session:{sessionId}",
                        "v": "HASH {user_id, doc_id, connected_at, last_activity} EX 3600"
                      }
                    ]
                  },
                  {
                    "groupTitle": "S3 - Blob Storage (from image shows S3)",
                    "pairs": [
                      {
                        "k": "Snapshot path",
                        "v": "docs/{doc_id}/v{version}.txt (gzip compressed)"
                      },
                      {
                        "k": "Archive path",
                        "v": "archive/{doc_id}/ops_{year}_{month}.json.gz (old operations)"
                      },
                      {
                        "k": "Lifecycle policy",
                        "v": "Standard (0-30 days) \u2192 IA (30-90 days) \u2192 Glacier (>90 days)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "ot-vs-crdt-comparison",
                "title": "8. OT vs CRDT - Deep Comparison (from image diagrams)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Operational Transformation (OT)",
                    "pairs": [
                      {
                        "k": "Core idea",
                        "v": "Transform concurrent operations to account for changes made by other operations (conflict resolution via transformation)"
                      },
                      {
                        "k": "Requirements",
                        "v": "Requires central server to sequence operations, all clients must apply in same order"
                      },
                      {
                        "k": "Algorithm",
                        "v": "Transform(op1, op2) adjusts op1 based on op2's effect (e.g., position shift after insert/delete)"
                      },
                      {
                        "k": "Pros",
                        "v": "Simpler data structure (just the document text), smaller memory footprint, efficient for centralized systems"
                      },
                      {
                        "k": "Cons",
                        "v": "Complex transformation functions, requires central ordering point (single point of failure), difficult for multi-master setup"
                      },
                      {
                        "k": "Best for",
                        "v": "Client-server architecture like Google Docs (central server sequences all operations)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Conflict-Free Replicated Data Types (CRDT)",
                    "pairs": [
                      {
                        "k": "Core idea",
                        "v": "Operations designed to be commutative (can be applied in any order and still converge to same state)"
                      },
                      {
                        "k": "Requirements",
                        "v": "Each character has unique ID (site_id + sequence), operations include metadata for deterministic ordering"
                      },
                      {
                        "k": "Algorithm",
                        "v": "Operations automatically commute, no transformation needed, eventual consistency guaranteed by design"
                      },
                      {
                        "k": "Pros",
                        "v": "No central server needed (peer-to-peer), works offline (operations stored locally), multi-master replication, simpler conflict resolution"
                      },
                      {
                        "k": "Cons",
                        "v": "Larger memory overhead (metadata per character), tombstones for deletes grow over time, complex data structure (CRDT tree/list)"
                      },
                      {
                        "k": "Best for",
                        "v": "Distributed systems, offline-first apps, peer-to-peer collaboration (e.g., Figma, some note-taking apps)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Production Usage",
                    "pairs": [
                      {
                        "k": "Google Docs",
                        "v": "Uses OT (Operational Transformation) with central server sequencing"
                      },
                      {
                        "k": "Figma",
                        "v": "Uses CRDT for design collaboration (offline-first, multi-master)"
                      },
                      {
                        "k": "Notion",
                        "v": "Uses OT for real-time editing with central server"
                      },
                      {
                        "k": "Hybrid approach",
                        "v": "Some systems use OT for real-time + CRDT for offline/sync (best of both)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "collaborative-editing-problem",
                "title": "9. Collaborative Editing Problem (from image diagrams)",
                "type": "bullets",
                "items": [
                  "Problem scenario (from image): Document content 'ABC', User A and User B edit simultaneously offline/concurrently",
                  "User A operation: insert(R, 29) at position 29, document becomes 'ABC...R...' (A inserts at end)",
                  "User B operation: delete(0) at position 0, document becomes 'BC' (B deletes first character)",
                  "Challenge: How to merge these concurrent operations without conflicts? Both users see different intermediate states",
                  "Without OT/CRDT: Naive merge would lose one user's changes or create inconsistent state",
                  "With OT solution: (1) Server receives A's op first, assigns version 1, broadcasts to B, (2) Server receives B's op, transforms based on A's op: delete(0) needs no transform if A inserted at end, (3) Both clients replay operations in server order: A's insert \u2192 B's delete \u2192 converge to 'BC...R...'",
                  "With CRDT solution: (1) Each character has unique ID: 'A'={1,1}, 'B'={1,2}, 'C'={1,3}, (2) User A: insert 'R' with ID {1,29}, (3) User B: delete {1,1}, (4) Operations commute: delete {1,1} removes 'A' regardless of R's insertion, (5) Both clients converge to 'BC...R...' deterministically",
                  "From image note: 'Inconsistent' state shown during concurrent edits, final state must be 'consistent' after OT/CRDT resolution",
                  "Key guarantee: Strong eventual consistency - all clients that have seen the same set of operations will converge to identical state"
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "10. Scaling & Optimization Techniques",
                "type": "bullets",
                "items": [
                  "Technique 1: WebSocket connection pooling - Each gateway instance handles 10K concurrent connections with persistent TCP connections",
                  "Technique 2: Redis canonical copy - Cache current document state with TTL=30 min, serves 90% of reads, evicts on expiry",
                  "Technique 3: Operation log partitioning - Partition by doc_id hash, enables parallel processing and horizontal scaling",
                  "Technique 4: Snapshot compression - gzip compress before S3 upload (70% size reduction), decompress on read",
                  "Technique 5: Cursor update throttling - Batch cursor updates every 100ms to reduce WebSocket traffic by 90%",
                  "Technique 6: Operation batching - Batch rapid keystrokes into single operation (5 chars/sec threshold) reduces op-log size",
                  "Technique 7: CDN for static assets - Editor UI, fonts, icons served via CloudFront for fast global access",
                  "Technique 8: Lazy loading - Load large documents (>100K chars) in chunks, initial 10K chars + load on scroll",
                  "Technique 9: Op-log archival - Move old operations (>90 days) to S3 Glacier after snapshots created, reduces hot storage cost",
                  "Technique 10: Auto-scaling - WebSocket gateways scale based on connection count (target 7K per instance), editor service scales on CPU",
                  "Technique 11: Snapshot strategy - Create snapshot every 50 operations OR 5 minutes, balances storage vs replay time",
                  "Technique 12: Kafka for decoupling - Document operations streamed to Kafka, consumers (reconciliation, analytics) process independently"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How does Operational Transformation (OT) work to resolve conflicts in concurrent editing?",
                    "a": "OT transforms concurrent operations to account for each other's effects: Scenario: Document 'hello', version 10. User A: insert('!', 5) \u2192 'hello!', User B: delete(1) \u2192 'hllo'. Server receives A first (version 11), then B (version 12). When B's operation arrives, server sees B's client_version=10 but current server_version=11 (A's op applied). Transformation needed: B intended delete at position 1 in 'hello', but A inserted at position 5 (after), so delete position unchanged. Transform B's op: delete(1) still valid. Apply to 'hello!' \u2192 'hllo!'. Broadcast: (1) Send A's insert to B, (2) Send B's delete to A. Both clients apply operations in server order \u2192 converge to 'hllo!'. Complex case: If A inserted at position 0 instead: insert('X', 0) \u2192 'Xhello'. B's delete(1) needs transformation: original target was 'h' at position 1, but now 'h' shifted to position 2 (X inserted before). Transformed op: delete(2). Apply to 'Xhello' \u2192 'Xello'. Key rules: (1) Insert before target position \u2192 shift target right (+1), (2) Delete before target position \u2192 shift target left (-1), (3) Operations applied in server-assigned order ensures all clients converge. Why server sequencing?: Without central ordering, Client A and B might apply operations in different orders \u2192 divergent states. Server acts as single source of truth for operation sequence. Edge cases: (1) Concurrent inserts at same position \u2192 server orders arbitrarily (e.g., by timestamp or user_id), (2) Overlapping deletes \u2192 first delete wins, second becomes no-op or shifts position. Production: Google Docs uses OT with central server handling millions of concurrent operations per second, operation log persisted for replay and versioning."
                  },
                  {
                    "q": "What are CRDTs and how do they differ from Operational Transformation?",
                    "a": "CRDT (Conflict-Free Replicated Data Types) are data structures designed so operations commute (can be applied in any order and converge to same state): Character-based CRDT example: Each character has unique ID = {site_id, sequence_number}. Document 'hello': 'h'={1,1}, 'e'={1,2}, 'l'={1,3}, 'l'={1,4}, 'o'={1,5}. User A offline: insert 'X' after 'h' \u2192 creates {1,6} with position 'after {1,1}'. User B offline: delete {1,1} (deletes 'h'). When A and B sync: (1) A applies B's delete: removes 'h'={1,1}, document is 'ello' with X={1,6} positioned 'after {1,1}' \u2192 X goes at beginning \u2192 'Xello', (2) B applies A's insert: adds X={1,6} 'after {1,1}' but {1,1} deleted \u2192 deterministic rule: insert at position of deleted anchor \u2192 'Xello'. Both converge to 'Xello' regardless of operation order. Key properties: (1) Commutativity: op1(op2(doc)) = op2(op1(doc)), (2) Associativity: (op1 \u2218 op2) \u2218 op3 = op1 \u2218 (op2 \u2218 op3), (3) Idempotency: applying same operation twice has no additional effect. CRDT advantages: (1) No central server needed (peer-to-peer), (2) Works offline (store operations locally, sync later), (3) Multi-master replication (multiple servers can accept writes), (4) Simpler conflict resolution (operations designed to commute). CRDT disadvantages: (1) Larger memory: each character has metadata (site_id, sequence), document 'hello' = 5 chars but CRDT structure = 5 objects with IDs + position pointers, (2) Tombstones: deleted characters leave tombstones (invisible markers) that accumulate over time, require garbage collection, (3) Complex data structure: CRDT typically uses tree or linked list (e.g., RGA - Replicated Growable Array) instead of simple string. OT vs CRDT comparison: OT: Central server sequences operations, simpler data structure (just text), smaller memory, complex transformation functions, single point of failure. CRDT: Decentralized (no central server), complex data structure (IDs + tombstones), larger memory, simpler operations (no transformation), works offline. Production usage: Google Docs uses OT (centralized architecture, millions of users), Figma uses CRDT (offline design collaboration), Notion uses OT (real-time editing with server), Automerge library implements CRDT for distributed apps. Hybrid approach: Some systems use OT for real-time editing (low latency, central server) + CRDT for offline sync (store local changes, merge when online) to get best of both. Example scenario: Collaborative design tool (Figma): User A offline: moves object 10px right, User B offline: moves same object 5px up, CRDT: Both operations commute \u2192 final position is (+10px right, +5px up) regardless of merge order. With OT: Would need server to transform one operation based on the other \u2192 more complex, requires connectivity."
                  },
                  {
                    "q": "How do you handle offline editing and sync when the user reconnects?",
                    "a": "Offline editing with sync-on-reconnect ensures no data loss and eventual consistency: Offline flow: (1) User editing document, WebSocket connection drops (network failure), (2) Client detects: WebSocket onerror/onclose event fires, (3) Switch to offline mode: Display banner 'Working offline, changes will sync when online', enable local storage, (4) User continues editing: All operations stored locally: IndexedDB.put('offline_ops', {doc_id, ops: [{op_id, type: 'insert', position: 10, content: 'hello', timestamp}]}), also store last_known_version: 42 (last version received from server), (5) Optimistic UI: Show changes immediately in editor (user sees edits despite offline). Reconnect flow: (1) Network restored, WebSocket reconnects (auto-retry with exponential backoff: 1s, 2s, 4s, 8s, max 30s), (2) Client sends sync request: {type: 'sync_request', doc_id, last_known_version: 42, queued_ops_count: 5}, (3) Server responds: {type: 'sync_response', server_ops: [{op1, op2, op3}], current_version: 50}, (4) Server operations: ops that happened on server while client was offline (from other users), (5) Client reconciliation: (a) Apply server operations first: replay ops 43-50 from server to catch up, (b) Transform local queued operations: use OT to transform local ops against server ops (adjust positions based on server changes), (c) Apply transformed local ops: insert into local document, (d) Send to server: {type: 'operations_batch', ops: [{transformed_op1, transformed_op2}]}, (6) Server validates: Check operations don't conflict with latest state, if valid \u2192 apply and broadcast to other users, if invalid (rare) \u2192 reject and request full resync. Conflict detection: If local op position > document length after applying server ops \u2192 conflict, show modal 'Conflicts detected during sync', options: (a) Keep local changes (discard server changes in conflict area), (b) Keep server changes (discard local changes), (c) Manual merge (show diff, let user choose per-change). Example scenario: User A offline: deletes 'hello' at position 0-5, server state: 'hello world', User B (online): inserts '!!!' at position 6, server state: 'hello !!!world', User A reconnects: (1) Server sends: insert('!!!', 6) operation, (2) Client applies: 'hello' already deleted locally \u2192 transform insert position: 6 \u2192 1 (5 chars deleted before position 6), apply: '!!!world', (3) Client sends: delete(0, 5) operation, (4) Server receives: document is 'hello !!!world', apply delete \u2192 '!!!world', (5) All clients converge to '!!!world'. Edge cases: (1) Very long offline period: If offline >24 hours with 1000s of operations, skip OT transformation, fetch latest snapshot from server, display merge UI showing local changes vs server state side-by-side, (2) Document deleted: If document deleted on server while offline, on reconnect show 'Document no longer exists, save local changes as new document?', (3) Permission revoked: If edit permission removed while offline, on reconnect convert operations to 'suggested changes' instead of direct edits. Performance optimization: (1) Compress local operations: merge consecutive inserts ('h' + 'e' + 'l' + 'l' + 'o' \u2192 'hello') before sending, (2) Incremental sync: send operations in batches of 50 rather than all at once to avoid overwhelming server, (3) Background sync: use Service Worker to attempt sync even when browser tab closed. Production: Google Docs supports offline editing via Chrome extension + local storage, syncs when online with OT transformation, shows conflicts if unsolvable automatically, provides version history to rollback if needed."
                  },
                  {
                    "q": "How do you implement cursor presence and real-time cursor updates for multiple users?",
                    "a": "Cursor presence shows where each user is typing/selecting in real-time: Architecture: (1) Client tracks cursor position: Editor onChange event fires on every cursor move (arrow keys, mouse click, text selection), (2) Throttle updates: Batch cursor position changes every 100ms to avoid flooding WebSocket (user typing fast = 10 chars/sec = 100 cursor updates/sec \u2192 throttled to 10 updates/sec), (3) Send via WebSocket: {type: 'cursor', user_id: A, cursor_pos: 15, selection: {start: 10, end: 15}, timestamp}, (4) Server receives: Validates session active, extracts user info (name, color from session), (5) Broadcast to others: {type: 'cursor_update', user: {user_id: A, name: 'Alice', color: '#FF5733', cursor_pos: 15, selection: {start: 10, end: 15}}}, (6) Other clients render: Colored cursor indicator at position 15 with label 'Alice', selection highlight from 10-15 in semi-transparent color. Cursor positioning: Calculate pixel position from character position: (1) Document text: 'Hello\\nWorld' (\\n = newline), (2) User A cursor at position 8 (in 'World'), (3) Client calculates: Line 1 = 'Hello\\n' (6 chars), position 8 = line 2, column 2 (W-o), (4) Measure text: Use canvas measureText() or DOM getBoundingClientRect(), line 2 top = 20px, column 2 left = 15px, (5) Render cursor: <div style={{position: 'absolute', top: 20px, left: 15px, borderLeft: '2px solid #FF5733'}}><label>Alice</label></div>. Selection rendering: Create overlay div with semi-transparent background: {position: 'absolute', top: 20px, left: 10px (start), width: 40px (end - start), height: 20px (line height), background: 'rgba(255,87,51,0.3)'}. Multiple users: Assign each user a unique color on join: colors = ['#FF5733', '#33FF57', '#3357FF', '#FF33F5'], user_color = colors[user_id % colors.length], persist in session: Redis HSET session:{session_id} 'color' {color}, broadcast to all clients on join. Ephemeral data: Cursor positions NOT persisted in database (too frequent updates, not durable data), stored in Redis with short TTL: HSET cursors:{doc_id} user:{user_id} '{cursor_pos: 15, selection: {...}, timestamp}' EX 300 (5 min), auto-expires when user disconnects. User disconnect handling: WebSocket close event \u2192 server removes cursor: HDEL cursors:{doc_id} user:{user_id}, broadcast to other clients: {type: 'cursor_removed', user_id: A}, clients remove cursor indicator and label. Cursor transformation (OT): When operations applied, cursor positions must be adjusted: User A cursor at position 10, User B inserts 'hello' at position 5, A's cursor position transforms: 10 \u2192 15 (5 chars inserted before cursor), client automatically adjusts cursor position when receiving B's operation. Selection transformation: User A selected text 10-20, User B deletes chars 5-8 (3 chars before selection), A's selection transforms: (1) start: 10 \u2192 7 (3 chars deleted before start), (2) end: 20 \u2192 17 (3 chars deleted before end), adjusted selection: 7-17. Mobile optimization: On mobile, cursor updates less frequent (200ms throttle instead of 100ms), smaller cursor indicators (no labels, just dots), tap to see user name tooltip. Performance: 10 concurrent users \u00d7 10 cursor updates/sec = 100 WebSocket messages/sec, with throttling \u2192 10 users \u00d7 1 update/0.1sec = 10 messages/sec per document (90% reduction), enables scaling to 100+ concurrent editors per document. Example UI: Google Docs shows: colored cursors with user names, selection highlights in user's color, active users list in top-right corner with colors, hover over cursor to see user name if label hidden. Advanced features: (1) Cursor following: Click user in active list \u2192 scroll to their cursor position, (2) Cursor hide: Toggle to hide other users' cursors if distracting, (3) Collaborative editing analytics: track how long each user's cursor stays in each section (engagement metrics)."
                  },
                  {
                    "q": "How do you implement auto-save and periodic snapshots?",
                    "a": "Auto-save prevents data loss and snapshots enable fast recovery: Triggering snapshots: Two conditions (whichever comes first): (1) Operation count: Every 50 operations applied to document, (2) Time-based: Every 5 minutes since last snapshot. Background job: Runs every minute, checks all active documents: SELECT doc_id, COUNT(*) as op_count FROM operations WHERE timestamp > (SELECT max(created_at) FROM snapshots WHERE doc_id=operations.doc_id) GROUP BY doc_id HAVING COUNT(*) >= 50. Snapshot creation flow: (1) Reconciliation Service picks up: Triggered by job or Kafka event {type: 'snapshot_needed', doc_id, op_count: 52}, (2) Fetch last snapshot: SELECT blob_path, version FROM snapshots WHERE doc_id={doc_id} ORDER BY version DESC LIMIT 1 \u2192 gets S3 path docs/{doc_id}/v10.txt, (3) Download snapshot: GET S3 docs/{doc_id}/v10.txt \u2192 base content 'Hello world', (4) Fetch operations: SELECT * FROM operations WHERE doc_id={doc_id} AND server_version > 10 ORDER BY server_version ASC \u2192 52 operations, (5) Replay operations: FOR EACH op: apply to document (insert/delete/format) \u2192 final content 'Hello world! This is a test.', (6) Validate: Check document integrity (no negative positions, content length matches expected), (7) Compress: gzip compress final content (reduces size by 70%: 1000 chars \u2192 300 bytes), (8) Upload to S3: PUT docs/{doc_id}/v11.txt.gz with compressed content, (9) Update database: BEGIN TRANSACTION; INSERT INTO snapshots (snapshot_id, doc_id, version: 11, blob_path: 's3://...v11.txt.gz', operations_since_last: 52, created_at: now()); UPDATE documents SET latest_snapshot_version=11, updated_at=now(); COMMIT;, (10) Update Redis cache: SET doc:{doc_id}:state {uncompressed_content} EX 1800 (30 min), (11) Publish event: Kafka 'doc.snapshot_created' \u2192 {doc_id, version: 11, op_count: 52} for analytics. Incremental snapshots: Instead of full document, store delta: {base_version: 10, operations: [op1, op2, ...52]}, saves storage but slower to reconstruct, full snapshot every 10 versions (versions 10, 20, 30, 40) for faster recovery. Auto-save indication: Client shows: (1) 'Saving...' indicator when operations sent, (2) 'Saved' with timestamp when server acknowledges: {type: 'ack', op_id, server_version}, (3) 'All changes saved to Drive' when idle for 5 seconds, (4) 'Unsaved changes' if WebSocket disconnected (operations queued locally). Fast recovery from snapshot: User opens document: (1) Fetch latest snapshot: GET S3 docs/{doc_id}/v11.txt.gz, (2) Decompress: gunzip \u2192 'Hello world! This is a test.', (3) Fetch recent operations: SELECT * FROM operations WHERE doc_id={doc_id} AND server_version > 11 (only ops after snapshot), (4) Replay: Apply 5 operations (since last snapshot 2 min ago) \u2192 current state, (5) Load time: Snapshot fetch (50ms) + decompress (10ms) + replay 5 ops (5ms) = 65ms vs 500ms to replay 1000 ops from beginning. Garbage collection: Cleanup old operations after snapshots: (1) Archive operations: SELECT * FROM operations WHERE doc_id={doc_id} AND server_version <= {snapshot_version - 1000}, (2) Store in S3 Glacier: PUT archive/{doc_id}/ops_2026_01.json.gz with compressed ops, (3) Delete from hot DB: DELETE FROM operations WHERE doc_id={doc_id} AND server_version <= {snapshot_version - 1000}, (4) Retention: Keep operations for 90 days in hot storage, then archive, keep snapshots forever. Snapshot versioning: Each snapshot is a version: v1 (initial), v2 (after 50 ops), v3 (after 100 ops), enables rollback: user clicks 'Version history' \u2192 sees v1, v2, v3 with timestamps, can restore to any version. Concurrent editing during snapshot: Snapshot creation happens in background: (1) Operations continue to be applied during snapshot generation, (2) Snapshot captures state at specific server_version (e.g., version 62), (3) New operations (version 63+) applied after snapshot completes, no downtime. Production numbers: Google Docs generates ~100M snapshots/day across all documents, average snapshot size 50KB (compressed), stored in GCS (Google Cloud Storage) with lifecycle management (hot \u2192 cold \u2192 archive), enables sub-second document loading for 99% of users."
                  },
                  {
                    "q": "How do you handle version history and rollback to previous versions?",
                    "a": "Version history enables users to view and restore previous document states: Version creation: Automatic versioning via snapshots: (1) Every 50 operations \u2192 new snapshot = new version, (2) Manual versions: user clicks 'Name current version' \u2192 creates named snapshot with label (e.g., 'Final draft v1'), (3) Timestamps: each version stores created_at for sorting and display. Fetching version history: User clicks 'Version history' button: GET /v1/api/docs/{doc_id}/versions \u2192 (1) Query database: SELECT snapshot_id, version, created_at, operations_since_last, created_by, label FROM snapshots WHERE doc_id={doc_id} ORDER BY version DESC LIMIT 50, (2) Join with users: get creator names, (3) Response: [{version: 11, created_at: '2026-01-22T10:30:00Z', label: null, created_by: 'system', ops_count: 52}, {version: 10, created_at: '10:25:00Z', label: 'Draft 1', created_by: 'Alice', ops_count: 50}], (4) UI displays: Timeline with version numbers, timestamps, creators, operation counts. Viewing specific version: User clicks version 10: GET /v1/api/docs/{doc_id}/version/10 \u2192 (1) Fetch snapshot: GET S3 docs/{doc_id}/v10.txt.gz, (2) Decompress and return: {version: 10, content: 'Hello world', metadata: {created_at, created_by}}, (3) Display: Read-only preview with banner 'Viewing version 10 from 10:25 AM', (4) Show changes: Diff current version vs selected version (highlight additions in green, deletions in red), (5) Navigation: 'Previous version' / 'Next version' buttons to browse history. Restoring version: User clicks 'Restore this version': POST /v1/api/docs/{doc_id}/restore with {target_version: 10} \u2192 (1) Validate: Check user has edit permission, (2) Fetch target version content: GET S3 docs/{doc_id}/v10.txt.gz \u2192 'Hello world', (3) Create restore operation: {type: 'replace_all', content: {v10_content}, user_id, timestamp}, (4) Append to op-log: INSERT INTO operations (op_id, doc_id, type: 'restore', content, server_version: 63), (5) Create new snapshot: Upload to S3 docs/{doc_id}/v12.txt.gz with v10 content (restored state becomes new version 12), (6) Update database: INSERT INTO snapshots (version: 12, label: 'Restored from v10', created_by: user_id), (7) Notify active editors: Broadcast via WebSocket {type: 'document_restored', from_version: 10, to_version: 12, by_user: 'Bob'}, (8) Clients reload: Fetch latest version (v12), discard local unsaved changes (with confirmation prompt). Version comparison: Side-by-side diff: User selects two versions (v8 vs v10) \u2192 server computes diff: (1) Fetch both contents from S3, (2) Run diff algorithm (Myers diff): computes line-by-line changes, (3) Response: [{type: 'equal', text: 'Hello'}, {type: 'delete', text: 'old'}, {type: 'insert', text: 'world'}], (4) UI renders: Side-by-side view with color coding (red=deleted, green=added, white=unchanged), (5) Merge option: 'Accept left' / 'Accept right' / 'Accept both' for conflicting changes. Named versions: User can name versions for milestones: (1) Click 'Name current version' \u2192 modal prompts for name, (2) POST /v1/api/docs/{doc_id}/versions with {label: 'Final draft before review'}, (3) Creates snapshot with label: INSERT INTO snapshots (..., label: 'Final draft before review'), (4) Shows in version history with special marker (star icon). Auto-naming: System automatically names versions: (1) Major edits: 'Version from 2 hours ago (50 operations)', (2) Daily: 'January 22, 2026 10:30 AM', (3) User-triggered: 'Saved by Alice'. Retention policy: Keep all versions for: (1) 30 days: full access and restore, (2) 90 days: view-only (can't restore directly, must copy content), (3) 1 year: archived to Glacier (retrieve takes 12 hours), (4) Forever: named versions never expire (user-marked important milestones). Change attribution: Version history shows: (1) Who made changes: SELECT DISTINCT user_id FROM operations WHERE doc_id={doc_id} AND server_version BETWEEN {v8} AND {v10}, (2) Change summary: 'Alice added 500 words, Bob deleted 200 words, Carol formatted 3 paragraphs', (3) Activity timeline: Graph showing edit activity over time (operations/hour), (4) Detailed changes: Drill down to see individual operations by user. Optimizations: (1) Lazy loading: Load version list (50 most recent), load older versions on scroll (pagination), (2) Thumbnail previews: Generate image thumbnails of document at each version (first page screenshot) for visual navigation, (3) Fast diff: Use precomputed diffs between consecutive versions instead of computing on-demand, (4) Compression: Old versions compressed more aggressively (bzip2 instead of gzip) to save storage. Mobile UX: Simplified version history: show only major versions (daily snapshots), restore requires confirmation with explanation, diff view shows inline changes instead of side-by-side (screen space limited). Production example: Google Docs stores version history for all documents: millions of versions generated per day, enables rollback to any point in time within 30 days, named versions kept forever, change attribution shows exactly who made each edit with timestamps."
                  },
                  {
                    "q": "How do you implement the Reconciliation Service and what does it do?",
                    "a": "Reconciliation Service ensures data consistency by validating and rebuilding canonical document state from operation log: Purpose: (1) Validate consistency: Ensure operations applied correctly and document state matches expectations, (2) Generate snapshots: Create periodic snapshots for fast recovery, (3) Detect anomalies: Identify corrupted operations or invalid states, (4) Rebuild state: Reconstruct document from op-log if cache/snapshot lost. Architecture: Kafka consumer + background jobs: (1) Kafka consumer: Subscribes to 'doc.operations' topic with consumer group 'reconciliation-service', processes every operation in real-time, (2) Background jobs: Periodic full reconciliation runs nightly (2 AM) to validate all documents. Real-time reconciliation flow: (1) Operation published: Editor Service \u2192 Kafka 'doc.operations' topic: {doc_id, op_id, type: 'insert', position: 10, content: 'hello', user_id, server_version: 42, timestamp}, (2) Reconciliation Service consumes: Receives operation event from Kafka, (3) Validate operation: (a) Check position valid: position <= document_length (can't insert at position 1000 if doc only 100 chars), (b) Check user permissions: query DB to ensure user_id has edit permission, (c) Check sequence: server_version increments by 1 from previous (no gaps), (4) Update metrics: Increment counters {operations_processed, operations_by_doc, operations_by_user}, (5) Check snapshot trigger: SELECT COUNT(*) FROM operations WHERE doc_id={doc_id} AND timestamp > (SELECT max(created_at) FROM snapshots WHERE doc_id={doc_id}), if count >= 50 \u2192 trigger snapshot creation. Snapshot generation: (1) Fetch last snapshot: SELECT blob_path, version FROM snapshots WHERE doc_id={doc_id} ORDER BY version DESC LIMIT 1 \u2192 S3 path docs/{doc_id}/v10.txt.gz, (2) Download: GET S3 docs/{doc_id}/v10.txt.gz \u2192 base content, (3) Fetch operations: SELECT * FROM operations WHERE doc_id={doc_id} AND server_version > 10 ORDER BY server_version ASC \u2192 52 operations since v10, (4) Replay operations: Apply each operation to document: (a) insert(10, 'hello') \u2192 insert 'hello' at position 10, (b) delete(15, 3) \u2192 delete 3 chars starting at position 15, (c) Validate after each: check position valid, content length matches expected, (5) Compute checksum: SHA256 hash of final content for integrity verification, (6) Upload snapshot: PUT S3 docs/{doc_id}/v11.txt.gz with compressed content + checksum metadata, (7) Update DB: INSERT INTO snapshots (snapshot_id, doc_id, version: 11, blob_path, checksum, operations_since_last: 52, created_at), (8) Update Redis: SET doc:{doc_id}:state {content} EX 1800. Anomaly detection: (1) Invalid position: If operation position > document length \u2192 log error, skip operation, flag document for review: INSERT INTO reconciliation_errors (doc_id, op_id, error: 'Invalid position', severity: 'high'), (2) Sequence gap: If server_version jumps (42 \u2192 44, missing 43) \u2192 check if operation 43 lost, attempt recovery from Kafka offset, (3) Checksum mismatch: After snapshot creation, recompute checksum from replay vs stored checksum, if mismatch \u2192 document state corrupted \u2192 alert admin, (4) Performance anomaly: If snapshot generation takes >10 seconds \u2192 document too large or operations too complex \u2192 flag for optimization. Full reconciliation (nightly): (1) Trigger: Cron job at 2 AM (low traffic time), (2) Batch process: SELECT doc_id FROM documents WHERE updated_at > now() - INTERVAL '7 days' ORDER BY updated_at DESC \u2192 active documents, (3) FOR EACH document: (a) Fetch last snapshot, (b) Replay all operations since snapshot, (c) Compute checksum, (d) Compare with Redis cache (if exists), (e) If mismatch: Generate new snapshot, log inconsistency, (4) Report: {total_docs_checked: 10000, snapshots_created: 500, errors_detected: 2, total_time: '45 min'}, (5) Alerts: If errors_detected > 10 \u2192 PagerDuty alert to on-call engineer. State rebuild: If document state lost (Redis evicted, snapshot corrupted): (1) Service receives request: GET /v1/api/docs/{doc_id}, (2) Fetch operations from beginning: SELECT * FROM operations WHERE doc_id={doc_id} ORDER BY server_version ASC, (3) Replay all operations from empty document: start with '', apply each insert/delete sequentially, (4) Generate snapshot: Save current state to S3, (5) Cache in Redis: SET doc:{doc_id}:state {content} EX 1800, (6) Time: Replaying 10K operations takes ~5 seconds (acceptable fallback). Metrics & monitoring: (1) Grafana dashboards: operations/sec, snapshot creation rate, reconciliation errors, op-log replay time, (2) Alerts: if reconciliation_errors > 10/hour \u2192 alert, if snapshot_creation_time > 10 sec \u2192 alert (doc too large), (3) SLIs: 99.9% of operations processed within 1 sec, 99% of snapshots created within 5 sec. Scaling: (1) Kafka consumer parallelism: 10 consumer instances process 10 partitions (partition by doc_id hash), (2) Snapshot workers: 50 workers in thread pool handle snapshot generation concurrently, (3) Rate limiting: Max 100 snapshots/sec to avoid overloading S3, queue excess for later. Error recovery: (1) Corrupted operation: Skip and log, continue with next, mark document as 'inconsistent' for manual review, (2) Database failure: Kafka retains events for 7 days, replay from offset after recovery, (3) S3 failure: Retry snapshot upload with exponential backoff, temporarily serve from Redis cache only. Production: Google Docs reconciliation processes billions of operations daily, generates millions of snapshots, detects and auto-corrects inconsistencies, ensures 99.99% data integrity, enables disaster recovery (rebuild any document from op-log)."
                  },
                  {
                    "q": "How would you scale this system to handle 1 million concurrent editors?",
                    "a": "Scaling to 1M concurrent editors requires horizontal scaling at every layer: WebSocket Gateway scaling: (1) Capacity: Each instance handles 10K connections, need 1M / 10K = 100 instances, (2) Load balancing: Use TCP load balancer (ELB/ALB) with sticky sessions (IP hash), ensures user's WebSocket stays on same instance for duration, (3) Auto-scaling: Scale based on connection count, target 7K connections/instance (30% headroom), (4) Connection handling: WebSocket upgrade from HTTP, maintain persistent TCP connections, heartbeat every 30 sec to detect disconnects, (5) Memory: 10K connections \u00d7 10 KB/connection = 100 MB per instance (manageable), (6) Network: 10K connections \u00d7 10 updates/sec \u00d7 500 bytes = 50 MB/sec = 400 Mbps per instance. Document Editor Service scaling: (1) Stateless service: Each instance can process any document (no affinity), (2) Horizontal scaling: 200 instances to handle operations from 1M users, (3) Load distribution: Operations partitioned by doc_id hash, Kafka topic has 100 partitions (2 instances per partition for redundancy), (4) Operation processing: Each instance processes 5K ops/sec (1M users / 200 instances, assuming 1 operation per user per second average), (5) Cache hit rate: Redis provides 95% cache hits for document state, only 5% hit DB. Redis scaling: (1) Redis Cluster: 50 nodes (master + replicas), each node handles 20K documents, (2) Sharding: Shard by doc_id hash, hash(doc_id) % 50 = shard_id, (3) Memory: 1M active documents \u00d7 50 KB avg = 50 GB, with replication (3x) = 150 GB total, (4) Throughput: 50 nodes \u00d7 100K ops/sec = 5M ops/sec capacity (1M users \u00d7 10 ops/sec = 10M ops/sec required, so need 100 nodes), (5) Eviction policy: LRU eviction with TTL=30 min for document state, hot documents stay cached, cold documents evicted and refetched from S3. Database (PostgreSQL) scaling: (1) Read replicas: 10 replicas for read queries (fetching operations, user permissions, version history), (2) Write primary: 1 primary for writes (append operations to op-log), (3) Sharding: Partition operations table by doc_id hash (100 shards), each shard handles 1% of documents, (4) Write throughput: 1M users \u00d7 1 operation/sec = 1M ops/sec, with batching (10 ops per transaction) = 100K transactions/sec, PostgreSQL handles ~10K txn/sec per shard, need 10 shards minimum, (5) Connection pooling: Each API instance maintains 200 connections, 200 instances \u00d7 200 connections = 40K connections (use PgBouncer to pool down to 500 connections per database). Kafka scaling: (1) Cluster: 20 brokers with replication factor 3, (2) Partitions: 100 partitions for 'doc.operations' topic, enables parallel consumption, (3) Throughput: 1M ops/sec \u00d7 500 bytes/op = 500 MB/sec write, Kafka handles 1 GB/sec per broker, 20 brokers = 20 GB/sec capacity (plenty of headroom), (4) Retention: 7 days retention (enables replay for disaster recovery), (5) Consumers: 100 consumer instances (1 per partition) for Reconciliation Service, Notification Service, Analytics Service. S3 scaling: (1) Snapshots: 1M active documents \u00d7 1 snapshot/5 min = 12M snapshots/hour = 3333 snapshots/sec, (2) S3 write capacity: 5500 requests/sec per prefix (first letter of doc_id), use multiple prefixes (a/, b/, c/, ... z/) = 5500 \u00d7 26 = 143K writes/sec (sufficient), (3) Read capacity: Document opening = fetch snapshot, 1M users opening docs over 10 min = 100K opens/min = 1666 ops/sec (well within limits), (4) CDN caching: CloudFront caches frequent snapshots (popular documents), 90% cache hit rate reduces S3 reads by 10x. Network bandwidth: (1) WebSocket traffic: 1M users \u00d7 10 ops/sec \u00d7 500 bytes = 5 GB/sec = 40 Gbps, (2) Kafka traffic: 500 MB/sec write + 500 MB/sec \u00d7 3 consumers = 2 GB/sec = 16 Gbps, (3) S3 traffic: 3333 snapshots/sec \u00d7 50 KB = 166 MB/sec = 1.3 Gbps, (4) Total: ~60 Gbps, use AWS VPC with 100 Gbps capacity. Cost optimization: (1) Spot instances: Use spot instances for WebSocket Gateway (70% discount), graceful failover on interruption, (2) Reserved instances: Reserve Editor Service instances (1-year commitment for 40% discount), (3) S3 lifecycle: Move old snapshots (>90 days) to Glacier (10x cheaper), (4) Compression: gzip snapshots (70% size reduction), saves S3 storage and transfer costs, (5) Total cost: ~$500K/month for 1M concurrent users (WebSocket $200K + Databases $150K + Kafka $50K + S3 $50K + Network $50K). Monitoring & observability: (1) Metrics: operations/sec, WebSocket connections, cache hit rate, snapshot generation time, error rate, (2) Dashboards: Grafana with real-time graphs per service, (3) Alerts: PagerDuty for critical errors (DB down, Kafka lag >1 min, 5xx errors >1%), (4) Distributed tracing: Jaeger for request tracing across services (WebSocket \u2192 Editor \u2192 Kafka \u2192 DB \u2192 S3). Failure handling: (1) WebSocket instance failure: Client auto-reconnects to different instance (exponential backoff), requests sync to catch up on missed operations, (2) Editor Service failure: Kafka retains events, new instance replays from last committed offset, (3) Redis failure: Fallback to fetching snapshots from S3 + replaying recent operations from DB, (4) Database failure: Promote read replica to primary (2-3 min RTO), Kafka retains events for replay after recovery, (5) S3 failure: Serve from Redis cache (covers last 30 min of activity), queue snapshot writes for later. Production examples: Google Docs handles millions of concurrent editors using similar architecture, scales horizontally at every layer, uses custom OT algorithm optimized for millions of operations per document, maintains 99.9% uptime, handles peak loads during major events (e.g., collaborative planning for large conferences)."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "12. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Performance",
                    "pairs": [
                      {
                        "k": "Concurrent Editors",
                        "v": "Up to 50-100 users per document simultaneously"
                      },
                      {
                        "k": "Document Size",
                        "v": "1.02M characters limit (Google Docs)"
                      },
                      {
                        "k": "Update Latency",
                        "v": "<300ms for operations to propagate to all clients"
                      },
                      {
                        "k": "WebSocket Connections",
                        "v": "10K connections per gateway instance"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching & Snapshots",
                    "pairs": [
                      {
                        "k": "Redis Cache TTL",
                        "v": "30 minutes for document state (canonical copy)"
                      },
                      {
                        "k": "Cursor Cache TTL",
                        "v": "5 minutes (ephemeral presence data)"
                      },
                      {
                        "k": "Snapshot Trigger",
                        "v": "Every 50 operations OR every 5 minutes"
                      },
                      {
                        "k": "Snapshot Compression",
                        "v": "gzip reduces size by 70% (1000 chars \u2192 300 bytes)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Operation Processing",
                    "pairs": [
                      {
                        "k": "Operation Log",
                        "v": "Append-only, indexed by (doc_id, server_version)"
                      },
                      {
                        "k": "OT Transformation",
                        "v": "Position adjustment based on concurrent insert/delete operations"
                      },
                      {
                        "k": "Cursor Update Throttle",
                        "v": "100ms batching (reduces WebSocket traffic by 90%)"
                      },
                      {
                        "k": "Replay Time",
                        "v": "10K operations replay in ~5 seconds for state rebuild"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Scaling Numbers",
                    "pairs": [
                      {
                        "k": "1M Concurrent Users",
                        "v": "100 WebSocket instances + 200 Editor Service instances + 50 Redis nodes"
                      },
                      {
                        "k": "Operations/Second",
                        "v": "1M ops/sec (1M users \u00d7 1 op/sec avg), peaks at 10M ops/sec"
                      },
                      {
                        "k": "S3 Snapshots",
                        "v": "3333 snapshots/sec at scale (1M docs \u00d7 1 snapshot/5 min)"
                      },
                      {
                        "k": "Network Bandwidth",
                        "v": "~60 Gbps total (WebSocket 40 Gbps + Kafka 16 Gbps + S3 1.3 Gbps)"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Operations MUST be sequenced by central server in OT approach. Without server-assigned order, clients will apply operations in different sequences \u2192 divergent states. Server version number is the single source of truth for operation order."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'OT vs CRDT difference?'. Answer: OT requires central server sequencing, transforms concurrent ops, simpler data structure. CRDT operations commute (any order), no server needed, works offline, but larger memory overhead (IDs + tombstones)."
              },
              {
                "type": "tip",
                "text": "Snapshot strategy optimization: Create snapshot every 50 operations OR 5 minutes. Balances storage cost vs replay time. With 50 ops snapshots, max replay = 50 ops (~50ms) vs 10K ops replay = 5 seconds."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Redis as canonical copy with TTL=30 min. Serves 90% of reads without hitting S3/DB. Evicted documents refetched from S3 snapshot + replay recent ops. Hot documents stay cached."
              },
              {
                "type": "warning",
                "text": "NEVER persist cursor positions in database. Cursor updates are ephemeral (100 updates/sec/user). Store in Redis with TTL=5 min, broadcast via WebSocket, removed on disconnect."
              },
              {
                "type": "tip",
                "text": "Offline editing sync: Store operations locally (IndexedDB), on reconnect: (1) Fetch server ops since last_known_version, (2) Transform local ops using OT, (3) Apply transformed ops to server. Ensures eventual consistency."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'How does collaborative editing problem get solved?'. Walk through: User A inserts, User B deletes concurrently \u2192 Server sequences (A first) \u2192 Transforms B's position based on A's insert \u2192 Both clients replay in server order \u2192 converge to same state."
              },
              {
                "type": "warning",
                "text": "NEVER skip operation log validation. Check: (1) position <= document length, (2) user has edit permission, (3) server_version increments by 1. Invalid operations \u2192 skip + log error + flag for review. Prevents corruption."
              },
              {
                "type": "tip",
                "text": "Reconciliation Service ensures consistency: Validates operations, generates snapshots, detects anomalies, rebuilds state from op-log. Runs nightly full reconciliation, processes operations in real-time via Kafka."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: WebSocket for real-time. HTTP polling (old approach) = 1 request/sec = 1M users = 1M req/sec. WebSocket = persistent connection = only send when data changes = 100x less traffic."
              }
            ],
            "tags": [
              "system-design",
              "collaborative-editing",
              "google-docs",
              "operational-transformation",
              "OT",
              "CRDT",
              "conflict-resolution",
              "WebSocket",
              "real-time-sync",
              "version-history",
              "snapshot",
              "operation-log",
              "cursor-presence",
              "offline-editing",
              "Redis-canonical-copy",
              "S3-snapshots",
              "Kafka-events",
              "reconciliation-service"
            ]
          }
        },
        {
          "id": "11",
          "title": "System Design 11: Design Ride Sharing Application like UBER | OLA | Rapido | Lyft | HLD | LLD",
          "videoUrl": "https://youtu.be/twUz_hWw2T8",
          "description": "In this video, we dive deep into how to design a ride-sharing system like Uber or Ola \u2014 from functional requirements to high-level design (HLD), deep dive / low-level design (LLD).",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/462352yy2g4u646h4h.webp?updatedAt=1752400453888",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/UberSystemDesign34657457745734.jpg?updatedAt=1752400334650",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 15,
            "lastUpdated": "2026-01-22",
            "hero": {
              "title": "Ride Sharing Service (Uber/Ola/Lyft)",
              "oneLinerToMemorize": "Rider requests \u2192 Geo-search nearby drivers \u2192 Zookeeper lock (prevent double assignment) \u2192 Driver accepts \u2192 WebSocket location tracking \u2192 Trip completion \u2192 Payment & Rating"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Riders should be able to get a fare estimation based on start location and destination",
                  "Feature 2: Riders should be able to request for a ride based on the estimated fare",
                  "Feature 3: Riders should be able to request different categories of car (Sedan, SUV, Bike, Auto)",
                  "Feature 4: Rider can see available drivers nearby in real-time on map with their current pickup/drop-off",
                  "Feature 5: Upon request, riders should be matched with a driver who is nearby (geo-proximity matching)",
                  "Feature 6: Get real-time tracking of the driver & user location during trip",
                  "Feature 7: After trip ends, rider should be able to rate their ride post-trip and make payment",
                  "Feature 8: Drivers should be able to accept/deny ride requests and update their status (available/busy/offline)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Users & Drivers",
                        "v": "Millions of users and drivers globally"
                      },
                      {
                        "k": "Concurrent Rides",
                        "v": "Hundreds of thousands of concurrent rides at peak hours"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance & Consistency",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> consistency (user) BUT consistency >> availability (driver assignment) - prevent double assignment"
                      },
                      {
                        "k": "Ride Matching",
                        "v": "Prevent any driver from being assigned multiple rides simultaneously (strong consistency)"
                      },
                      {
                        "k": "Latency",
                        "v": "<1 sec: driver should get assigned to a particular ride request, real-time location updates every 3-5 seconds"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities (from image)",
                "type": "bullets",
                "items": [
                  "Entity 1: Rider - user_id, name, email, phone, payment_methods, current_location (lat/lon)",
                  "Entity 2: Driver - driver_id, name, vehicle_details (type, model, license_plate), rating, status (available/busy/offline), current_location",
                  "Entity 3: Location - Geospatial coordinates with timestamp for real-time tracking",
                  "Entity 4: Fare - fare_id, base_fare, distance_fare, time_fare, surge_multiplier, total_amount",
                  "Entity 5: Ride/Trip - ride_id, rider_id, driver_id, pickup_location, drop_location, status (requested/matched/started/completed/cancelled), estimated_fare, actual_fare, start_time, end_time",
                  "Entity 6: Rating - rating_id, ride_id, rider_rating (1-5 stars), driver_rating, feedback_text",
                  "Entity 7: Payment - payment_id, ride_id, amount, payment_method, status (pending/completed/failed)"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing (from image)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "User/Rider APIs",
                    "pairs": [
                      {
                        "k": "POST /v1/api/fare/estimate",
                        "v": "Get fare estimation with Request ID (different types of vehicle: Sedan, SUV, Bike, Pool)"
                      },
                      {
                        "k": "POST /v1/api/ride/request",
                        "v": "{pickup, drop, vehicleType} \u2192 Return rideId with driver details (if assigned success) OR (Matched with driver - sent message on the ride)"
                      },
                      {
                        "k": "GET /v1/api/ride/history",
                        "v": "Get ride history for user"
                      },
                      {
                        "k": "POST /v1/api/ride/{rideId}/cancel",
                        "v": "Cancel ride request"
                      },
                      {
                        "k": "POST /v1/api/ride/{rideId}/rate",
                        "v": "Rate driver after trip completion"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Driver APIs",
                    "pairs": [
                      {
                        "k": "WS /v1/driver/location",
                        "v": "WebSocket: {body: Lat, Long} - Update driver geolocation continuously every 3-5 seconds"
                      },
                      {
                        "k": "POST /v1/api/ride/rides",
                        "v": "{body: requestId, accept/deny} - Driver will accept/deny ride request, return as rideId"
                      },
                      {
                        "k": "POST /v1/api/ride/{ride_id}/start",
                        "v": "Mark trip as started when driver picks up rider"
                      },
                      {
                        "k": "POST /v1/api/ride/{ride_id}/complete",
                        "v": "Mark trip as completed when destination reached"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design (from image)",
                "type": "bullets",
                "items": [
                  "Users/Clients (riders & drivers) \u2192 LB + API Gateway: Load balancing, authentication, authorization, rate limiting, traffic distribution (90%)",
                  "Ride Service (fare calculation): Calculates estimated fare based on pickup/drop distance, surge pricing, vehicle type",
                  "Driver Matching Service: Geo-proximity search to find nearby available drivers, assigns driver to ride request",
                  "Location Map Service (GMaps, Apple Map): Provides mapping, routing, ETA calculations, distance matrix",
                  "Rating Service \u2192 Rating DB: Stores and retrieves rider/driver ratings and feedback",
                  "Payment Service \u2192 Payment DB: Processes payments via payment gateway (Stripe/Razorpay), handles refunds",
                  "Location Update Service (WebSocket server): Ingests driver location pings every 3-5 seconds, updates Redis",
                  "Redis: Stores driver availability status with TTL (ephemeral data), driver locations (geospatial index), ride request status",
                  "Zookeeper: Distributed lock for driver assignment - prevents double assignment, ephemeral nodes for driver locks",
                  "Notification Service: FCM (Firebase Cloud Messaging) + APN (Apple Push Notification) for real-time notifications",
                  "Kafka: Event streaming for trip updates, location history, analytics, notifications",
                  "Drivers DB: Persistent storage for driver profiles, vehicle details, availability history",
                  "Ride DB: Persistent storage for all ride records, trip details, routes, fare breakdowns",
                  "Trip Update Consumer: Consumes Kafka events, persists location history, generates analytics (fare bonus tracking, operations)"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level - from image)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Fare Estimation (Surge Calculator from image)",
                    "steps": [
                      "Rider enters: pickup='123 Main St, SF' and drop='456 Market St, SF' in app",
                      "Client sends: POST /v1/api/fare/estimate with {pickup_lat: 37.7749, pickup_lon: -122.4194, drop_lat: 37.7849, drop_lon: -122.4094, vehicle_type: 'sedan'}",
                      "Ride Service: (1) Queries Location Map Service (Google Maps Distance Matrix API): GET distance & duration between pickup and drop, (2) Response: {distance: 5.2 km, duration: 12 min}",
                      "Calculate base fare: (1) Base rate: $2.50 (flat charge), (2) Distance rate: 5.2 km \u00d7 $1.50/km = $7.80, (3) Time rate: 12 min \u00d7 $0.30/min = $3.60, (4) Subtotal: $2.50 + $7.80 + $3.60 = $13.90",
                      "Apply surge pricing (from image shows 'Surge Calculator'): (1) Query Redis: GET surge_multiplier:{area_hash} for pickup area, (2) If high demand (>20 requests, <5 available drivers in 2km radius) \u2192 surge_multiplier = 1.5x, (3) Final fare: $13.90 \u00d7 1.5 = $20.85",
                      "Generate fare breakdown: {base_fare: 2.50, distance_fare: 7.80, time_fare: 3.60, surge_multiplier: 1.5, estimated_total: 20.85, currency: 'USD'}",
                      "Response to client: {request_id: uuid(), estimated_fare: 20.85, breakdown: {...}, eta_min: 12, distance_km: 5.2, vehicle_options: [{type: 'sedan', fare: 20.85}, {type: 'suv', fare: 28.50}, {type: 'bike', fare: 12.00}]}",
                      "Cache estimate: SET fare_estimate:{request_id} {fare_details} EX 300 (5 min TTL - estimate valid for 5 minutes)"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Ride Request & Driver Matching (Geo-search from image)",
                    "steps": [
                      "Rider confirms: Clicks 'Request Sedan' button",
                      "Client sends: POST /v1/api/ride/request with {request_id, pickup_lat, pickup_lon, drop_lat, drop_lon, vehicle_type: 'sedan', payment_method_id}",
                      "Ride Service validates: (1) Check fare estimate exists: GET fare_estimate:{request_id} from Redis, (2) Validate payment method: query Payment Service to ensure payment_method_id valid",
                      "Create ride request: INSERT INTO ride_requests (request_id, rider_id, pickup_location, drop_location, vehicle_type, estimated_fare, status: 'PENDING', created_at: now())",
                      "Store in Redis: SET ride_request:{request_id} {details} EX 600 (10 min TTL - request expires if no driver found)",
                      "Trigger driver matching: Publish to Kafka 'ride.requested' topic: {request_id, pickup_location, vehicle_type, timestamp}",
                      "Driver Matching Service consumes: Receives ride request event from Kafka",
                      "Geo-proximity search (from image shows 'Location Map Svc'): (1) Query Redis Geospatial: GEORADIUS drivers:available {pickup_lon} {pickup_lat} 5 km WITHDIST ASC (find available drivers within 5km, sorted by distance), (2) Filter by vehicle type: SISMEMBER drivers:sedan {driver_id} for each result, (3) Result: [{driver_id: D1, distance: 0.8 km}, {driver_id: D2, distance: 1.2 km}, {driver_id: D3, distance: 2.5 km}]",
                      "From image note: 'Ride = Rs 70/km waiting: Rs 1/min, Cab = Rs 50/km' - pricing varies by vehicle type",
                      "Iterate through candidates: FOR EACH driver in sorted list (closest first): (1) Attempt to lock driver using Zookeeper (explained in next step), (2) If lock acquired \u2192 assign driver, BREAK, (3) If lock failed (driver already assigned) \u2192 try next driver, (4) If all drivers tried and none available \u2192 return 'No drivers available, please try again'"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Driver Locking with Zookeeper (CRITICAL - from image diagram)",
                    "steps": [
                      "From image: Zookeeper section shows 'Driver Lock' with ephemeral nodes driver1_001, driver1_002, driver1_003",
                      "Purpose: Prevent double assignment - ensure one driver assigned to only one ride at a time",
                      "Zookeeper structure: /locks/drivers/{driver_id}/ephemeral_node (ephemeral node created for each lock attempt)",
                      "Lock attempt: Driver Matching Service tries to lock driver D1: (1) Create ephemeral node: CREATE /locks/drivers/D1/{request_id} (ephemeral, sequence), (2) Check if this node is lowest sequence number: GET_CHILDREN /locks/drivers/D1 ORDER BY sequence, (3) If our node is first \u2192 lock acquired, (4) If another node exists with lower sequence \u2192 lock failed (driver already locked by another request)",
                      "From image quote: 'when the driver matching service want to create a lock it creates a ephemeral node. if it is successfully, means it got the lock. Incase it got the NumberAlreadyExistException, then the driver is already assigned to someone (another) or try after sometime'",
                      "Lock acquired: (1) Mark driver as BUSY in Redis: SET driver:{D1}:status 'BUSY' EX 900, (2) Remove from available drivers geospatial index: GEOREM drivers:available {D1}, (3) Update database: UPDATE drivers SET status='BUSY', current_ride_id={request_id} WHERE driver_id='D1'",
                      "Send notification to driver: Publish to Kafka 'driver.ride_offered' \u2192 Notification Service sends push notification: FCM/APN to driver's device with {request_id, rider_name, pickup_location, estimated_fare, eta_to_pickup}",
                      "Driver app displays: Modal with ride details + 'Accept' / 'Decline' buttons, timeout 30 seconds (if no response, auto-decline)",
                      "Lock TTL: Ephemeral node has session timeout 30 seconds, if Driver Matching Service crashes or driver doesn't respond \u2192 Zookeeper auto-deletes node \u2192 lock released, another service instance can retry with next driver",
                      "From image quote: 'This lock is released, server have to delete the ephemeral node manually, of zookeeper will automatically delete it, if the server gets session timeout/disconnected/dies'"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Driver Accept/Decline",
                    "steps": [
                      "Driver sees ride request: Push notification received, app shows ride details",
                      "Driver clicks 'Accept': POST /v1/api/ride/rides with {request_id, action: 'accept'}",
                      "Ride Service validates: (1) Check Zookeeper lock still held: EXISTS /locks/drivers/{driver_id}/{request_id}, (2) Check request still pending: GET ride_request:{request_id} status from Redis",
                      "Create trip: BEGIN TRANSACTION; INSERT INTO trips (trip_id, request_id, rider_id, driver_id, pickup_location, drop_location, status: 'MATCHED', matched_at: now()); UPDATE ride_requests SET status='MATCHED', matched_at=now(); UPDATE drivers SET current_trip_id={trip_id}; COMMIT;",
                      "Update Redis: (1) SET trip:{trip_id}:status 'MATCHED' EX 3600, (2) SET driver:{driver_id}:current_trip {trip_id} EX 3600, (3) DEL ride_request:{request_id} (remove pending request)",
                      "Release Zookeeper lock: DELETE /locks/drivers/{driver_id}/{request_id} (lock no longer needed, driver assigned)",
                      "Notify rider: Publish Kafka 'ride.matched' \u2192 Notification Service sends push: 'Driver {name} accepted your ride! ETA 5 min', WebSocket message to rider app: {type: 'ride_matched', driver: {name, photo, rating, vehicle_details, current_location, eta_min: 5}}",
                      "Rider app updates: Shows driver on map with real-time location, driver details card, ETA countdown, 'Contact Driver' button (call/message)",
                      "Driver decline: POST /v1/api/ride/rides with {request_id, action: 'decline'} \u2192 (1) Release Zookeeper lock, (2) Mark driver available again: SET driver:{driver_id}:status 'AVAILABLE', (3) Retry matching with next closest driver, (4) If all drivers decline \u2192 notify rider 'Unable to find driver, please try again later'"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Real-Time Location Tracking (WebSocket from image)",
                    "steps": [
                      "From image: Shows 'Location Update Svc (websocket server)' receiving location updates from drivers",
                      "Driver app: Continuous location tracking (GPS updates every 3-5 seconds)",
                      "WebSocket connection: WS /v1/driver/location with Authorization: Bearer {JWT}, authenticated on connect",
                      "Location Update Service: (1) Receives location ping via WebSocket: {driver_id, lat: 37.7849, lon: -122.4194, timestamp, accuracy: 10m, speed: 25 km/h}, (2) Validates: driver authenticated, trip in progress or driver available",
                      "Update Redis geospatial: (1) GEOADD drivers:available {lon} {lat} {driver_id} if driver status='AVAILABLE', (2) If driver on trip: SET location:{driver_id} '{lat, lon, timestamp}' EX 60 (1 min TTL), (3) Publish to Redis Pub/Sub: PUBLISH driver_location:{driver_id} '{lat, lon, timestamp}' for real-time subscribers",
                      "Persist to Kafka: Publish 'driver.location_updated' event: {driver_id, trip_id, lat, lon, timestamp, speed} \u2192 consumed by Trip Update Consumer for history tracking",
                      "Rider receives updates: (1) Rider app subscribes to WebSocket: WS /v1/trip/{trip_id}/track, (2) Location Update Service forwards driver location: {type: 'driver_location', lat, lon, timestamp, eta_min: 4}, (3) Rider app updates driver marker on map, recalculates ETA, shows 'Driver is 0.5 km away'",
                      "From image shows: 'Kafka' \u2192 'Trip Update Consumer' \u2192 'persistently update the driver location, status, rider house tracking (operations), which could driver movement, e.g ETA message'",
                      "ETA calculation: (1) Fetch current route from Location Map Service: Google Maps Directions API with current driver location \u2192 pickup \u2192 drop, (2) Response: {distance_remaining: 2.5 km, duration_remaining: 8 min, route_polyline}, (3) Update ETA: HSET trip:{trip_id}:details 'eta_min' 8 'last_updated' {timestamp}",
                      "Geofencing: When driver within 50m of pickup: (1) Detect proximity: distance(driver_location, pickup_location) < 50m, (2) Notify rider: 'Your driver has arrived!', (3) Update trip status: 'DRIVER_ARRIVED'"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Trip Start",
                    "steps": [
                      "Driver picks up rider: Driver clicks 'Start Trip' button in app",
                      "Client sends: POST /v1/api/ride/{trip_id}/start",
                      "Ride Service: (1) Validate trip status: SELECT status FROM trips WHERE trip_id={trip_id}, must be 'MATCHED' or 'DRIVER_ARRIVED', (2) Update trip: UPDATE trips SET status='IN_PROGRESS', start_time=now(), start_location={current_driver_location}, (3) Start fare meter: Calculate ongoing fare based on distance/time",
                      "Update Redis: SET trip:{trip_id}:status 'IN_PROGRESS' EX 7200 (2 hour max trip duration)",
                      "Notify rider: Push notification 'Your trip has started!', WebSocket message: {type: 'trip_started', start_time, start_location}",
                      "Real-time tracking continues: Both rider and driver see each other's location on map (from WebSocket updates)",
                      "Calculate ongoing fare: (1) Fetch current location periodically: GET location:{driver_id}, (2) Calculate distance traveled: sum of distances between consecutive location updates, (3) Calculate time elapsed: current_time - start_time, (4) Ongoing fare: base_fare + (distance \u00d7 distance_rate) + (time \u00d7 time_rate) \u00d7 surge_multiplier, (5) Update display: Rider app shows 'Current fare: $15.20' updated every 30 seconds"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Trip Completion & Payment",
                    "steps": [
                      "Driver reaches destination: Driver clicks 'Complete Trip' button",
                      "Client sends: POST /v1/api/ride/{trip_id}/complete",
                      "Ride Service: (1) Validate trip in progress: SELECT status FROM trips WHERE trip_id={trip_id} = 'IN_PROGRESS', (2) Fetch trip details: start_location, end_location, start_time, (3) Calculate final fare: (a) Total distance: query from location history OR Google Maps Directions API with actual route, (b) Total time: now() - start_time, (c) Final fare: base_fare + (distance \u00d7 rate) + (time \u00d7 rate) \u00d7 surge_multiplier",
                      "Update trip record: BEGIN TRANSACTION; UPDATE trips SET status='COMPLETED', end_time=now(), end_location={current_driver_location}, actual_fare={calculated_fare}, distance_km={total_distance}, duration_min={total_time}; UPDATE drivers SET status='AVAILABLE', current_trip_id=NULL; COMMIT;",
                      "Update Redis: (1) SET trip:{trip_id}:status 'COMPLETED' EX 86400 (keep for 24 hours), (2) DEL driver:{driver_id}:current_trip, (3) Add driver back to available pool: GEOADD drivers:available {lon} {lat} {driver_id}",
                      "Process payment: (1) Fetch payment method: SELECT payment_method_id FROM riders WHERE rider_id={rider_id}, (2) Create payment intent: POST to Payment Gateway (Stripe) with {amount: {actual_fare}, currency: 'USD', customer_id, metadata: {trip_id}}, (3) Execute payment: stripe.paymentIntents.confirm({payment_intent_id}), (4) Handle response: if success \u2192 UPDATE trips SET payment_status='COMPLETED', payment_id={stripe_payment_id}; if failure \u2192 UPDATE trips SET payment_status='FAILED', retry with exponential backoff or notify rider to update payment method",
                      "From image: 'Rating (anonymous)' - ratings stored separately from trip, rider doesn't see who rated whom",
                      "Publish events: Kafka 'trip.completed' \u2192 {trip_id, rider_id, driver_id, actual_fare, distance, duration, timestamp} for analytics, driver payouts, rider receipts",
                      "Send receipt: Email/SMS to rider with trip summary, fare breakdown, payment confirmation, receipt PDF"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Rating & Feedback (from image shows Rating Service)",
                    "steps": [
                      "Post-trip: Rider app shows rating prompt: 'How was your ride?'",
                      "Rider rates: Selects 1-5 stars for driver + optional feedback text",
                      "Client sends: POST /v1/api/ride/{trip_id}/rate with {driver_rating: 5, feedback: 'Great driver, smooth ride!'}",
                      "Rating Service: (1) Validate: Ensure rider_id matches trip, rating not already submitted, (2) Insert rating: INSERT INTO ratings (rating_id, trip_id, rider_id, driver_id, driver_rating, rider_feedback, created_at), (3) Update driver average: (a) Fetch: SELECT AVG(driver_rating) FROM ratings WHERE driver_id={driver_id}, (b) Update: UPDATE drivers SET avg_rating={new_average}, total_ratings=total_ratings+1",
                      "From image: Ratings stored with metadata: 'comment (trimmed) riderId' - anonymous to driver, driver sees rating but not rider identity",
                      "Driver also rates rider: Driver app prompts 'Rate your passenger', POST /v1/api/ride/{trip_id}/rate with {rider_rating: 5}, UPDATE riders SET avg_rating={new_average}, total_ratings=total_ratings+1",
                      "Rating factors: (1) Driver rating affects future matching (higher rated drivers prioritized), (2) Rider rating affects driver acceptance (low rated riders may have longer wait times), (3) Persistent low ratings (< 4.0) trigger account review",
                      "Analytics: Track rating trends, identify issues (if ratings drop for specific driver \u2192 trigger manager review), segment by trip type, time of day, location"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Surge Pricing Algorithm (from image shows 'Surge Calculator')",
                    "steps": [
                      "Purpose: Balance supply (available drivers) and demand (ride requests) through dynamic pricing",
                      "Real-time monitoring: (1) Background job runs every 60 seconds per geohash area (5km \u00d7 5km grid), (2) Query Redis: GEORADIUS drivers:available {area_center_lon} {area_center_lat} 5 km \u2192 count available drivers, (3) Query DB: SELECT COUNT(*) FROM ride_requests WHERE status='PENDING' AND pickup_location IN {area} AND created_at > now() - INTERVAL '10 min' \u2192 count pending requests",
                      "Calculate demand/supply ratio: (1) demand_ratio = pending_requests / available_drivers, (2) If demand_ratio < 1.5 \u2192 no surge (multiplier = 1.0), (3) If 1.5 <= demand_ratio < 3 \u2192 low surge (multiplier = 1.3x), (4) If 3 <= demand_ratio < 5 \u2192 medium surge (multiplier = 1.5x), (5) If demand_ratio >= 5 \u2192 high surge (multiplier = 2.0x or higher, max 3.0x)",
                      "Example calculation: Area = Downtown SF, Available drivers = 20, Pending requests = 80, demand_ratio = 80/20 = 4.0 \u2192 surge_multiplier = 1.8x, Rider sees: 'Fare increased due to high demand (1.8\u00d7)'",
                      "Store in Redis: SET surge_multiplier:{area_geohash} 1.8 EX 120 (2 min TTL, recalculated every 60 seconds)",
                      "Notify users: (1) Update rider app: Show heatmap with surge areas in red/orange, (2) Display message: 'High demand in your area. Fares are higher than usual.', (3) Option: 'Notify me when prices drop' \u2192 push notification when surge multiplier falls below 1.2x",
                      "Driver incentives: During high surge, notify nearby offline drivers: 'Earn 1.5\u00d7 more! Go online now.' to increase supply",
                      "Anti-gaming: (1) Cap maximum surge at 3.0\u00d7 (prevent price gouging), (2) Surge applies only to new requests (not pre-booked rides), (3) Rider must explicitly accept surge before request confirmed"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Notification System (from image shows FCM + APN)",
                    "steps": [
                      "From image: 'Notification Svc' \u2192 'FCM = Firebase Cloud Messaging' + 'APN = Apple Push Notification'",
                      "Architecture: Notification Service consumes Kafka events: 'ride.requested', 'ride.matched', 'trip.started', 'trip.completed', 'driver.ride_offered'",
                      "Device token management: (1) On app login: rider/driver registers device token, (2) Store: INSERT INTO device_tokens (user_id, device_token, platform: 'ios'/'android', active: true), (3) Update on app reinstall or token refresh",
                      "Send notification: (1) Notification Service receives Kafka event: {type: 'ride.matched', rider_id, driver_name, eta_min}, (2) Fetch device tokens: SELECT device_token, platform FROM device_tokens WHERE user_id={rider_id} AND active=true, (3) Build notification payload: {title: 'Driver Assigned!', body: '{driver_name} will arrive in {eta_min} min', data: {trip_id, driver_id, action: 'open_trip_screen'}}, (4) Send via FCM (Android): fcm.send({to: device_token, notification: {...}, data: {...}}), (5) Send via APN (iOS): apn.send({token: device_token, payload: {...}})",
                      "Notification types: (1) Ride matched \u2192 rider & driver, (2) Driver arrived \u2192 rider, (3) Trip started \u2192 rider, (4) Trip completed \u2192 rider & driver, (5) Payment processed \u2192 rider, (6) Driver declined \u2192 internal, retry matching, (7) Surge pricing changed \u2192 nearby riders",
                      "Delivery handling: (1) If FCM/APN returns invalid token \u2192 mark device_token as inactive: UPDATE device_tokens SET active=false, (2) Retry on transient failures (network timeout) with exponential backoff: 1s, 2s, 4s, max 3 retries, (3) Dead letter queue: Failed notifications after retries \u2192 stored for manual review",
                      "In-app notifications: (1) If app in foreground: Display banner notification + update UI directly, (2) If app in background: OS handles notification display, tapping opens app with deep link to trip screen",
                      "WebSocket alternative: For latency-critical updates (driver location, real-time ETA), use WebSocket instead of push notifications (sub-second latency vs 1-3 second push latency)"
                    ]
                  },
                  {
                    "blockTitle": "Step 11: Driver Availability Management",
                    "steps": [
                      "Driver goes online: (1) Driver opens app, clicks 'Go Online', (2) Request location permission (required), (3) POST /v1/api/driver/status with {status: 'AVAILABLE', current_location}",
                      "Ride Service: (1) Validate driver approved and documents verified, (2) Update database: UPDATE drivers SET status='AVAILABLE', last_online_at=now(), (3) Add to Redis geospatial index: GEOADD drivers:available {lon} {lat} {driver_id}, (4) Set status in Redis: SET driver:{driver_id}:status 'AVAILABLE' EX 300 (5 min TTL, refreshed by heartbeat)",
                      "Heartbeat mechanism: (1) Driver app sends WebSocket ping every 30 seconds: {driver_id, lat, lon, status, timestamp}, (2) Location Update Service: (a) Refresh Redis TTL: EXPIRE driver:{driver_id}:status 300, (b) Update geospatial: GEOADD drivers:available {lon} {lat} {driver_id}, (c) If heartbeat missed for 2 minutes \u2192 mark driver offline: DEL driver:{driver_id}:status, GEOREM drivers:available {driver_id}",
                      "Driver goes offline: (1) Driver clicks 'Go Offline' OR app closed, (2) POST /v1/api/driver/status with {status: 'OFFLINE'}, (3) Update DB: UPDATE drivers SET status='OFFLINE', (4) Remove from Redis: DEL driver:{driver_id}:status, GEOREM drivers:available {driver_id}, (5) Close WebSocket connection",
                      "Automatic offline: If driver doesn't accept rides for 30 min while online \u2192 prompt: 'You haven't accepted rides. Still available?' \u2192 if no response in 5 min \u2192 auto-offline",
                      "Status transitions: OFFLINE \u2192 AVAILABLE (go online) \u2192 BUSY (assigned ride) \u2192 IN_TRIP (trip started) \u2192 AVAILABLE (trip completed) \u2192 OFFLINE (go offline)"
                    ]
                  },
                  {
                    "blockTitle": "Step 12: Trip Cancellation Handling",
                    "steps": [
                      "Rider cancels before driver accepts: (1) POST /v1/api/ride/{request_id}/cancel, (2) Check status: if status='PENDING' \u2192 DELETE ride_request, refund any pre-authorization, (3) No cancellation fee",
                      "Rider cancels after driver accepts: (1) Check trip status: if status='MATCHED' and driver hasn't started \u2192 apply cancellation fee (e.g., $5), (2) UPDATE trips SET status='CANCELLED_BY_RIDER', cancelled_at=now(), cancellation_fee={fee}, (3) Charge cancellation fee: CREATE payment with amount={fee}, (4) Release driver: UPDATE drivers SET status='AVAILABLE', current_trip_id=NULL, (5) Remove Zookeeper lock if still exists, (6) Add driver back to geospatial index: GEOADD drivers:available {lon} {lat} {driver_id}, (7) Notify driver: 'Rider cancelled the trip. $3 cancellation fee credited to your account.'",
                      "Rider cancels during trip: Not allowed, must complete trip and resolve disputes through support",
                      "Driver cancels: (1) POST /v1/api/ride/{trip_id}/cancel with {reason: 'rider_not_at_pickup'/'rider_misbehaving'/'emergency'}, (2) If cancellation valid (rider no-show after 5 min wait) \u2192 no penalty, (3) If cancellation invalid (driver just doesn't want ride) \u2192 penalty: (a) Driver rating reduced, (b) Accept rate decreased (affects matching priority), (c) Multiple invalid cancellations \u2192 temporary account suspension, (4) Release driver lock, set driver AVAILABLE, retry matching rider with new driver",
                      "Grace period: Rider can cancel free within 2 minutes of request if no driver assigned yet",
                      "Refund: If rider cancels with valid reason (app error, long wait) \u2192 full refund via support ticket"
                    ]
                  },
                  {
                    "blockTitle": "Step 13: Analytics & Monitoring (Trip Update Consumer from image)",
                    "steps": [
                      "From image: 'Trip Update Consumer' consumes Kafka events \u2192 'persistently update the driver location, status, rider house tracking (operations)'",
                      "Data pipeline: Kafka 'trip.*' topics \u2192 Spark Streaming / Flink \u2192 Data Warehouse (BigQuery / Redshift) \u2192 BI Dashboards (Tableau / Looker)",
                      "Real-time metrics: (1) Active trips count per region, (2) Average wait time (request \u2192 driver assigned), (3) Average trip duration, (4) Surge multiplier heatmap, (5) Driver utilization rate (time in trip / time online), (6) Revenue per hour, (7) Cancellation rate (rider vs driver)",
                      "Operational dashboards: (1) Live map showing all active trips, drivers, pending requests, (2) Anomaly detection: spike in cancellations, sudden drop in available drivers, region with unusually long wait times, (3) Alerts: if avg_wait_time > 5 min \u2192 notify operations team, if available_drivers < 10 in high-demand area \u2192 trigger driver incentives",
                      "Driver analytics: (1) Earnings dashboard: trips completed today, total earnings, average rating, acceptance rate, (2) Heatmap: high-demand areas with expected surge times, (3) Suggestions: 'Move 2 km north for more rides'",
                      "Rider analytics: (1) Trip history, spending patterns, favorite destinations, (2) Personalized offers: 'Ride to work for 20% off tomorrow morning'",
                      "Fraud detection: (1) Detect fake GPS (driver not actually moving but location jumps), (2) Detect collusion (driver and rider same person gaming system), (3) Detect fare manipulation (driver taking longer routes intentionally), (4) ML model trained on historical patterns flags suspicious trips for review"
                    ]
                  },
                  {
                    "blockTitle": "Step 14: Scaling & Performance Optimization",
                    "steps": [
                      "Geospatial indexing: (1) Redis Geo commands (GEOADD, GEORADIUS) provide O(log(N)) search for nearby drivers, (2) For 1M active drivers, search within 5km takes ~10ms, (3) Alternative: QuadTree or R-Tree for even faster searches at scale, (4) Partitioning: Shard by geographic region (US-West, US-East, EU, APAC) to distribute load",
                      "Database sharding: (1) Shard trips table by geohash of pickup location, each shard handles trips in specific geographic area, (2) Shard drivers table by driver_id hash, (3) Cross-region queries minimized (most queries local to rider's region)",
                      "Caching strategy: (1) Driver locations: Redis with 60-second TTL (refreshed by WebSocket pings), (2) Driver availability: Redis geospatial index, evicted on offline/busy, (3) Surge multipliers: Redis with 2-minute TTL, (4) Fare estimates: Redis with 5-minute TTL, (5) Trip details: Redis with 2-hour TTL during active trip",
                      "WebSocket scaling: (1) Each WebSocket gateway instance handles 10K connections, (2) For 1M concurrent drivers/riders, need 100 instances, (3) Load balancer with sticky sessions (IP hash) ensures user stays on same instance, (4) Failover: if instance dies, client auto-reconnects to different instance (1-2 second interruption)",
                      "Kafka partitioning: (1) Partition 'driver.location_updated' by driver_id (100 partitions), enables parallel consumption, (2) Partition 'trip.*' events by trip_id, (3) Consumer groups: location-persistence (writes to DB), analytics (aggregates metrics), notifications (sends push)",
                      "Zookeeper clustering: (1) 5-node Zookeeper ensemble for high availability, (2) Quorum: need 3 nodes for write consensus, (3) Auto-failover: if leader dies, new leader elected in <1 second, (4) Session timeout: 30 seconds (balance between false positives and lock release speed)",
                      "CDN for static assets: Driver/rider photos, vehicle images, map tiles served via CloudFront, 95% cache hit rate",
                      "Rate limiting: (1) Rider: 10 ride requests per hour (prevent spam/abuse), (2) Driver: 1 status update per second (prevent API flooding), (3) Location updates: 1 per 3 seconds (balance freshness vs bandwidth)",
                      "Database optimization: (1) Index on (pickup_location, status) for finding pending requests in area, (2) Index on (driver_id, status) for driver queries, (3) Partition trips table by month for historical data, (4) Archive trips older than 2 years to cold storage (S3 Glacier)"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details (from image shows Drivers, Ride, Rating, Payment)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Riders (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "rider_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "phone",
                        "v": "varchar(20) UNIQUE"
                      },
                      {
                        "k": "payment_methods",
                        "v": "jsonb ([{type: 'card', last4, stripe_customer_id}])"
                      },
                      {
                        "k": "avg_rating",
                        "v": "decimal(3,2) (average rating from drivers)"
                      },
                      {
                        "k": "total_trips",
                        "v": "int (lifetime trip count)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Drivers (PostgreSQL - from image)",
                    "pairs": [
                      {
                        "k": "driver_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "phone",
                        "v": "varchar(20)"
                      },
                      {
                        "k": "vehicle_type",
                        "v": "enum (sedan, suv, bike, auto)"
                      },
                      {
                        "k": "vehicle_model",
                        "v": "varchar(100) (e.g., 'Toyota Camry 2020')"
                      },
                      {
                        "k": "license_plate",
                        "v": "varchar(20)"
                      },
                      {
                        "k": "status",
                        "v": "enum (AVAILABLE, BUSY, OFFLINE, IN_TRIP)"
                      },
                      {
                        "k": "current_trip_id",
                        "v": "uuid FK \u2192 Trips (nullable)"
                      },
                      {
                        "k": "avg_rating",
                        "v": "decimal(3,2)"
                      },
                      {
                        "k": "total_trips",
                        "v": "int"
                      },
                      {
                        "k": "acceptance_rate",
                        "v": "decimal(5,2) (percentage of accepted ride offers)"
                      },
                      {
                        "k": "last_online_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Trips/Rides (PostgreSQL - from image shows 'Ride')",
                    "pairs": [
                      {
                        "k": "trip_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "rider_id",
                        "v": "uuid FK \u2192 Riders"
                      },
                      {
                        "k": "driver_id",
                        "v": "uuid FK \u2192 Drivers"
                      },
                      {
                        "k": "pickup_location",
                        "v": "geography (PostGIS point)"
                      },
                      {
                        "k": "drop_location",
                        "v": "geography (PostGIS point)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, MATCHED, DRIVER_ARRIVED, IN_PROGRESS, COMPLETED, CANCELLED_BY_RIDER, CANCELLED_BY_DRIVER)"
                      },
                      {
                        "k": "vehicle_type",
                        "v": "enum (sedan, suv, bike)"
                      },
                      {
                        "k": "estimated_fare",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "actual_fare",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "surge_multiplier",
                        "v": "decimal(3,2) (e.g., 1.5 for 1.5\u00d7)"
                      },
                      {
                        "k": "distance_km",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "duration_min",
                        "v": "int"
                      },
                      {
                        "k": "requested_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "matched_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "start_time",
                        "v": "timestamp"
                      },
                      {
                        "k": "end_time",
                        "v": "timestamp"
                      },
                      {
                        "k": "payment_status",
                        "v": "enum (PENDING, COMPLETED, FAILED, REFUNDED)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (rider_id, created_at), INDEX on (driver_id, created_at), INDEX on (status, pickup_location) using GIST"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Ratings (PostgreSQL - from image shows 'Rating (anonymous)')",
                    "pairs": [
                      {
                        "k": "rating_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "trip_id",
                        "v": "uuid FK \u2192 Trips, UNIQUE (one rating per trip per user)"
                      },
                      {
                        "k": "rider_id",
                        "v": "uuid"
                      },
                      {
                        "k": "driver_id",
                        "v": "uuid"
                      },
                      {
                        "k": "driver_rating",
                        "v": "int (1-5 stars, rider rates driver)"
                      },
                      {
                        "k": "rider_rating",
                        "v": "int (1-5 stars, driver rates rider)"
                      },
                      {
                        "k": "rider_feedback",
                        "v": "text (optional comment from rider)"
                      },
                      {
                        "k": "driver_feedback",
                        "v": "text (optional comment from driver)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "Note",
                        "v": "Anonymous: drivers see rating but not rider identity, riders see rating but not driver identity in aggregated form"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Payments (PostgreSQL - from image shows 'Payment')",
                    "pairs": [
                      {
                        "k": "payment_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "trip_id",
                        "v": "uuid FK \u2192 Trips, UNIQUE"
                      },
                      {
                        "k": "amount",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "currency",
                        "v": "varchar(3) (e.g., 'USD', 'INR')"
                      },
                      {
                        "k": "payment_method",
                        "v": "enum (card, wallet, cash, upi)"
                      },
                      {
                        "k": "stripe_payment_id",
                        "v": "varchar(255) (external payment gateway reference)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, COMPLETED, FAILED, REFUNDED)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "completed_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Driver Locations & Availability",
                    "pairs": [
                      {
                        "k": "drivers:available",
                        "v": "GEOSPATIAL INDEX - GEOADD for available drivers, GEORADIUS for proximity search"
                      },
                      {
                        "k": "driver:{driverId}:status",
                        "v": "STRING (AVAILABLE/BUSY/OFFLINE) EX 300 (5 min TTL, refreshed by heartbeat)"
                      },
                      {
                        "k": "location:{driverId}",
                        "v": "STRING (JSON: {lat, lon, timestamp}) EX 60 (1 min TTL, updated every 3-5 sec)"
                      },
                      {
                        "k": "trip:{tripId}:status",
                        "v": "STRING (MATCHED/IN_PROGRESS/COMPLETED) EX 7200 (2 hour max trip)"
                      },
                      {
                        "k": "surge_multiplier:{geohash}",
                        "v": "STRING (decimal value: 1.5) EX 120 (2 min TTL, recalculated every 60 sec)"
                      },
                      {
                        "k": "ride_request:{requestId}",
                        "v": "STRING (JSON request details) EX 600 (10 min TTL, expires if no driver found)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Zookeeper - Driver Locks (from image diagram)",
                    "pairs": [
                      {
                        "k": "Lock path",
                        "v": "/locks/drivers/{driver_id}/{request_id} - ephemeral sequential node"
                      },
                      {
                        "k": "Purpose",
                        "v": "Prevent double assignment - only one request can lock a driver at a time"
                      },
                      {
                        "k": "Session timeout",
                        "v": "30 seconds - if service crashes or driver doesn't respond, lock auto-released"
                      },
                      {
                        "k": "From image",
                        "v": "Shows ephemeral nodes driver1_001, driver1_002, driver1_003 under Driver Lock"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Kafka Topics",
                    "pairs": [
                      {
                        "k": "ride.requested",
                        "v": "Ride request created, triggers driver matching"
                      },
                      {
                        "k": "driver.ride_offered",
                        "v": "Driver selected, notification sent for accept/decline"
                      },
                      {
                        "k": "ride.matched",
                        "v": "Driver accepted, trip begins"
                      },
                      {
                        "k": "trip.started",
                        "v": "Driver picked up rider, fare meter started"
                      },
                      {
                        "k": "trip.completed",
                        "v": "Trip ended, payment processed"
                      },
                      {
                        "k": "driver.location_updated",
                        "v": "Real-time location ping (100K+ messages/sec), consumed by Trip Update Consumer"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "zookeeper-deep-dive",
                "title": "8. Zookeeper Driver Locking - Deep Dive (Critical from image)",
                "type": "bullets",
                "items": [
                  "Problem: Multiple concurrent ride requests trying to assign same driver \u2192 double assignment without locking",
                  "From image diagram: Shows 'Zookeeper' with 'Driver Lock' containing ephemeral nodes driver1_001, driver1_002, driver1_003",
                  "Ephemeral node creation: When Driver Matching Service wants to lock driver D1 for request R1 \u2192 CREATE /locks/drivers/D1/request_R1 (ephemeral, sequential)",
                  "Lock acquisition check: (1) Get all children of /locks/drivers/D1, (2) Sort by sequence number, (3) If our node has lowest sequence \u2192 lock acquired, proceed with assignment, (4) If another node with lower sequence exists \u2192 lock failed, driver already locked by different request",
                  "From image quote: 'when the driver matching service want to create a lock it creates a ephemeral node. if it is successfully, means it got the lock. Incase it got the NumberAlreadyExistException, then the driver is already assigned to someone (another) or try after sometime'",
                  "Success path: Lock acquired \u2192 (1) Mark driver BUSY in Redis, (2) Remove from drivers:available geospatial index, (3) Update DB: drivers.status='BUSY', (4) Send notification to driver for accept/decline, (5) Wait for driver response (timeout 30 seconds)",
                  "Failure path: Lock not acquired \u2192 (1) Release any partial locks, (2) Move to next closest driver in candidate list, (3) Retry lock acquisition with driver D2, (4) If all drivers tried and failed \u2192 return 'No drivers available' to rider",
                  "Driver accepts: (1) Create trip record in DB, (2) Delete Zookeeper lock (no longer needed), (3) Driver assigned, lock mechanism complete",
                  "Driver declines: (1) Delete Zookeeper lock, (2) Mark driver AVAILABLE again in Redis, (3) Retry matching with next driver in list",
                  "Timeout handling: Driver doesn't respond within 30 seconds \u2192 (1) Zookeeper session timeout triggers, (2) Ephemeral node auto-deleted, (3) Driver marked AVAILABLE, (4) System retries with next driver, (5) Original driver receives late notification: 'Ride request expired'",
                  "From image quote: 'This lock is released, server have to delete the ephemeral node manually, of zookeeper will automatically delete it, if the server gets session timeout/disconnected/dies'",
                  "Crash recovery: If Driver Matching Service crashes while holding locks \u2192 (1) Zookeeper detects session loss, (2) All ephemeral nodes created by that session auto-deleted within 30 seconds, (3) Drivers unlocked automatically, (4) New service instance can retry matching",
                  "Why Zookeeper over Redis?: (1) Stronger consistency guarantees (CP system), (2) Built-in session management (ephemeral nodes), (3) Atomic lock operations with ordering, (4) Auto-cleanup on failure, (5) Redis SETNX could work but requires more manual cleanup logic",
                  "Alternative: Redis distributed lock with Redlock algorithm, but Zookeeper preferred for critical driver assignment use case",
                  "Monitoring: (1) Track lock acquisition time (<10ms typical), (2) Alert if lock acquisition fails >50% (indicates Zookeeper issues), (3) Track ephemeral node count per driver (should be 0 or 1, never >1), (4) Monitor session timeouts (too many = connectivity issues)"
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "9. Scaling & Optimization Techniques",
                "type": "bullets",
                "items": [
                  "Technique 1: Redis Geospatial indexing - GEORADIUS searches 1M drivers in <10ms, O(log(N)) complexity, shard by geographic region",
                  "Technique 2: Zookeeper driver locking - Prevents double assignment with ephemeral nodes, auto-cleanup on timeout, <10ms lock acquisition",
                  "Technique 3: WebSocket for location updates - 100K concurrent connections per instance, persistent connection vs HTTP polling (100x less traffic)",
                  "Technique 4: Kafka event streaming - 100K+ location updates/sec, decouples services (location persistence, analytics, notifications)",
                  "Technique 5: Surge pricing - Dynamic supply/demand balancing, recalculated every 60 seconds per area, stored in Redis with 2-min TTL",
                  "Technique 6: Database sharding - Shard trips by pickup location geohash, shard drivers by driver_id, enables horizontal scaling",
                  "Technique 7: Caching strategy - Driver locations (60s TTL), availability (geospatial), surge (120s TTL), fare estimates (300s TTL)",
                  "Technique 8: Push notifications - FCM/APN for real-time alerts, WebSocket for in-app updates, hybrid approach for best latency",
                  "Technique 9: Geofencing - Detect driver arrival within 50m of pickup, trigger automatic status updates, reduce manual actions",
                  "Technique 10: Rate limiting - 10 ride requests/hour per rider, 1 location update/3 sec per driver, prevents abuse and API flooding",
                  "Technique 11: CDN for assets - Driver/vehicle photos, map tiles via CloudFront, 95% cache hit rate, reduces origin load",
                  "Technique 12: Database optimization - PostGIS for geospatial queries, indexes on (status, location), partition by month for historical data"
                ]
              },
              {
                "id": "interview-qa",
                "title": "10. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you prevent double assignment of a driver to multiple ride requests?",
                    "a": "Use Zookeeper distributed locking with ephemeral nodes: Scenario: Driver D1 available at location X, simultaneously 3 ride requests R1, R2, R3 created nearby. Driver Matching Service instances (DMS1, DMS2, DMS3) each select D1 as best match for their respective requests. Lock acquisition: (1) DMS1 tries to create /locks/drivers/D1/request_R1_seq0001 (ephemeral, sequential node), Zookeeper assigns sequence 0001, (2) DMS2 tries to create /locks/drivers/D1/request_R2_seq0002, Zookeeper assigns sequence 0002, (3) DMS3 tries to create /locks/drivers/D1/request_R3_seq0003, sequence 0003. Checking lock ownership: Each DMS gets children of /locks/drivers/D1: [request_R1_seq0001, request_R2_seq0002, request_R3_seq0003]. DMS1 sees its node (seq0001) is lowest \u2192 lock acquired \u2192 proceeds with assignment. DMS2 and DMS3 see seq0001 < their sequences \u2192 lock NOT acquired \u2192 must try next driver. DMS1 proceeds: (1) Mark D1 as BUSY in Redis: SET driver:D1:status 'BUSY' EX 900, (2) Remove from available drivers: GEOREM drivers:available D1, (3) Update database: UPDATE drivers SET status='BUSY', current_ride_id={R1}, (4) Send push notification to driver for accept/decline via Kafka \u2192 Notification Service. DMS2 and DMS3 retry: (1) Delete their Zookeeper nodes (didn't get lock), (2) Select next closest driver (D2, D3) from candidate list, (3) Attempt lock acquisition for new drivers. Driver D1 responds: If accepts \u2192 DELETE /locks/drivers/D1/request_R1_seq0001 (lock released, no longer needed), CREATE trip in DB, notify rider. If declines or timeout (30 sec) \u2192 (1) Zookeeper session expires \u2192 ephemeral node auto-deleted, (2) D1 marked AVAILABLE again: SET driver:D1:status 'AVAILABLE', GEOADD drivers:available {lon} {lat} D1, (3) System can retry D1 for other requests. Why Zookeeper?: (1) Strong consistency (CP system) - guarantees only one lock holder, (2) Ephemeral nodes - auto-cleanup on failure/timeout, (3) Atomic operations - sequential node creation with ordering, (4) Session management - built-in detection of client crashes. Alternative approach: Redis distributed lock with Redlock algorithm: (1) SETNX lock:driver:D1 {request_id} EX 30, (2) If returns 1 \u2192 lock acquired, (3) If returns 0 \u2192 already locked. But Redis approach requires: (a) Manual TTL management, (b) No automatic ordering of concurrent lock attempts, (c) Risk of TTL expiring before lock released if service slow. Zookeeper provides stronger guarantees for critical use case (preventing revenue loss from double assignments, poor UX)."
                  },
                  {
                    "q": "How does real-time location tracking work with WebSockets?",
                    "a": "WebSocket enables bidirectional persistent connection for sub-second location updates: Architecture: Driver app maintains WebSocket connection to Location Update Service (WebSocket server), rider app subscribes to driver location updates via separate WebSocket. Driver side: (1) Driver goes online \u2192 app requests location permission \u2192 establishes WebSocket: WS /v1/driver/location with JWT token, (2) Location Update Service authenticates: Validates JWT, checks driver approved, creates session {session_id, driver_id, connected_at}, (3) GPS tracking starts: Android/iOS location API provides updates every 3-5 seconds with {lat, lon, accuracy, speed, timestamp}, (4) App sends via WebSocket: {driver_id, lat: 37.7849, lon: -122.4194, accuracy: 10m, speed: 25 km/h, timestamp}, (5) Location Update Service processes: (a) Validates driver in active trip or available, (b) Updates Redis geospatial: GEOADD drivers:available {lon} {lat} {driver_id} if driver available, (c) Sets location cache: SET location:{driver_id} '{lat, lon, timestamp}' EX 60 (1 min TTL), (d) Publishes to Redis Pub/Sub: PUBLISH driver_location:{driver_id} '{lat, lon, timestamp}' for real-time subscribers, (e) Sends to Kafka: 'driver.location_updated' event for persistence \u2192 Trip Update Consumer writes to location_history table. Rider side: (1) Rider matched with driver \u2192 app subscribes: WS /v1/trip/{trip_id}/track with rider auth token, (2) WebSocket Gateway authenticates: Validates rider_id owns this trip_id, (3) Gateway subscribes to Redis Pub/Sub: SUBSCRIBE driver_location:{driver_id}, (4) When driver location published \u2192 Gateway forwards to rider via WebSocket: {type: 'driver_location', lat, lon, timestamp, eta_min: 4, distance_m: 500}, (5) Rider app updates: (a) Moves driver marker on map smoothly (interpolation), (b) Recalculates ETA: distance_remaining / avg_speed, (c) Shows status: 'Driver is 500m away, arriving in 4 min'. During trip: Both rider and driver see each other's locations (bidirectional), rider sees: 'Your trip in progress', driver sees: 'Passenger onboard'. ETA calculation: (1) Every 30 seconds, Location Update Service queries Google Maps Directions API: {origin: {driver_current_location}, destination: {pickup_location}, mode: 'driving'}, (2) Response: {distance_remaining: 2500m, duration: 8 min, route_polyline}, (3) Broadcast ETA update to rider: {eta_min: 8, distance_m: 2500}. Geofencing: (1) When driver within 50m of pickup: calculate distance(driver_location, pickup_location) using Haversine formula, (2) If distance < 50m \u2192 trigger event: {type: 'driver_arrived', driver_id, trip_id}, (3) Update trip status: UPDATE trips SET status='DRIVER_ARRIVED', (4) Notify rider: Push notification + WebSocket message 'Your driver has arrived!', (5) Rider app shows: Vibrate phone, play arrival sound, display 'Driver is here' banner. Connection management: (1) Heartbeat: Client sends ping every 30 seconds, server responds with pong + {server_time}, detects stale connections, (2) Reconnect: If WebSocket closes (network loss, app backgrounded), client auto-reconnects with exponential backoff (1s, 2s, 4s, 8s, max 30s), (3) Resume: On reconnect, request last known state: {last_received_timestamp}, server sends missed updates. Bandwidth optimization: (1) Send location only if moved >10 meters (skip updates when stationary), (2) Reduce frequency if speed < 5 km/h (1 update per 10 sec vs 3 sec), (3) Compress updates: Binary protocol (Protocol Buffers) instead of JSON (50% size reduction), (4) Batch: Multiple location updates in single WebSocket frame if queued. Scaling: (1) Each WebSocket server handles 10K connections (1 connection per active driver + riders), (2) For 100K active trips (100K drivers + 100K riders = 200K connections), need 20 instances, (3) Load balancer with sticky sessions (IP hash) ensures rider stays on same instance for trip duration, (4) Redis Pub/Sub scales horizontally (multiple subscribers), (5) Kafka buffers location history for offline processing. Why WebSocket vs HTTP polling?: HTTP polling: Client sends GET /location/{driver_id} every 3 seconds \u2192 100K riders \u00d7 20 requests/min = 2M requests/min = 33K req/sec. WebSocket: Persistent connection \u2192 send update only when location changes \u2192 100K drivers \u00d7 20 updates/min = 2M updates/min but NO HTTP overhead (no headers, no handshake) \u2192 10x less bandwidth, <100ms latency vs 1-3 sec polling delay."
                  },
                  {
                    "q": "How do you calculate surge pricing dynamically?",
                    "a": "Surge pricing balances supply and demand through real-time monitoring and dynamic multipliers: Geohash grid system: Divide geographic area into 5km \u00d7 5km grid cells using geohash (6-character precision), each cell independently tracks supply/demand, example: Downtown SF = 9q8yy, Financial District = 9q8yw. Real-time monitoring (runs every 60 seconds): Background job (Surge Calculator Service) for EACH geohash: (1) Count available drivers: Redis query: GEORADIUS drivers:available {cell_center_lon} {cell_center_lat} 5 km \u2192 count results, example: Downtown SF has 15 available drivers, (2) Count pending requests: Database query: SELECT COUNT(*) FROM ride_requests WHERE status='PENDING' AND ST_DWithin(pickup_location, ST_MakePoint({cell_center_lon}, {cell_center_lat}), 5000) AND created_at > now() - INTERVAL '10 min' \u2192 example: 45 pending requests in last 10 minutes. Calculate demand/supply ratio: demand_ratio = pending_requests / available_drivers = 45 / 15 = 3.0. Determine surge multiplier based on ratio: (1) demand_ratio < 1.2 \u2192 no surge (multiplier = 1.0), plenty of drivers, (2) 1.2 <= demand_ratio < 2.0 \u2192 low surge (multiplier = 1.2\u00d7), slight imbalance, (3) 2.0 <= demand_ratio < 3.0 \u2192 medium surge (multiplier = 1.5\u00d7), moderate shortage, (4) 3.0 <= demand_ratio < 5.0 \u2192 high surge (multiplier = 1.8\u00d7), significant shortage, (5) demand_ratio >= 5.0 \u2192 very high surge (multiplier = 2.0-3.0\u00d7), severe shortage, max cap at 3.0\u00d7. Example calculation: Downtown SF at 8 PM Friday (high demand): (1) Available drivers: 15, (2) Pending requests: 45, (3) demand_ratio = 3.0 \u2192 high surge, (4) surge_multiplier = 1.8\u00d7, (5) Base fare: $15 \u2192 Surge fare: $15 \u00d7 1.8 = $27. Store in Redis: SET surge_multiplier:9q8yy 1.8 EX 120 (2 min TTL, refresh every 60 sec ensures always fresh). Rider sees surge: (1) Fare estimate API: POST /v1/api/fare/estimate \u2192 fetches surge: GET surge_multiplier:{pickup_geohash} from Redis, (2) Response: {estimated_fare: 27.00, base_fare: 15.00, surge_multiplier: 1.8, surge_notice: 'Fares are higher due to increased demand'}, (3) Rider app displays: Red/orange heatmap overlay on map showing surge areas, modal: 'High demand! Fares are 1.8\u00d7 normal. Want to wait for prices to drop?', (4) Rider must explicitly accept surge: Click 'Accept higher fare' button before ride request submitted. Driver incentives during surge: (1) Notify nearby offline drivers: Push notification 'Earn more! High demand in Downtown SF. Surge pricing active (1.8\u00d7).' with deep link to 'Go Online' screen, (2) Bonus offers: 'Complete 3 rides in surge zone in next hour \u2192 $20 bonus' to increase supply. Smoothing algorithm: Prevent sudden jumps: (1) New surge = 0.7 \u00d7 old_surge + 0.3 \u00d7 calculated_surge (exponential moving average), (2) Example: Old surge = 1.5\u00d7, Calculated = 2.0\u00d7, New surge = 0.7\u00d71.5 + 0.3\u00d72.0 = 1.05 + 0.6 = 1.65\u00d7, (3) Gradual increase feels fairer to users, prevents gaming (riders waiting for surge to drop). Historical trends: (1) ML model predicts surge: Train on historical data (time of day, day of week, weather, events, holidays), (2) Proactive driver positioning: 'High demand expected in Financial District at 5 PM. Head there now for more rides.', (3) Rider notifications: 'Avoid surge! Book your ride 30 min earlier tomorrow.'. Anti-gaming measures: (1) Surge applies only to NEW requests (pre-scheduled rides locked at original price), (2) Rider must accept surge before request (can't game by requesting then canceling), (3) Max cap 3.0\u00d7 prevents price gouging during emergencies, (4) Transparent pricing: Show exact breakdown (base + surge) not just total. Edge cases: (1) Special events: Concert ends \u2192 10K people requesting rides simultaneously \u2192 surge spikes to 3.0\u00d7 but capped, queue system activates (first-come-first-served), (2) Low supply at night: Only 5 drivers available citywide \u2192 surge high but also trigger driver incentives aggressively, (3) Weather: Rain starts \u2192 demand spikes 50% \u2192 surge increases, notify off-duty drivers. Monitoring: (1) Dashboard: Real-time heatmap of surge across city, (2) Alerts: If surge > 2.5\u00d7 for > 30 min in area \u2192 notify operations team to investigate (event? accident? driver shortage?), (3) A/B testing: Test different surge algorithms (linear vs exponential) measure impact on: (a) Driver supply response, (b) Rider conversion rate, (c) Total revenue, (d) User satisfaction. Production metrics: Uber surge pricing increases driver supply by 30-40% during high demand, typical surge duration 15-45 minutes, surge reduces rider wait time by balancing supply/demand, revenue increase during surge but must balance with user retention."
                  },
                  {
                    "q": "How do you handle geospatial queries efficiently for finding nearby drivers?",
                    "a": "Redis Geospatial commands with indexing + PostGIS for persistent storage: Redis Geospatial indexing: (1) Data structure: GEOADD drivers:available {longitude} {latitude} {driver_id} stores driver locations in sorted set with geohash encoding, (2) Under the hood: Redis uses geohash (52-bit integer encoding of lat/lon) stored in sorted set, enables range queries via ZRANGE operations, (3) Example: GEOADD drivers:available -122.4194 37.7749 driver_123 adds driver to San Francisco location. Query nearby drivers: GEORADIUS drivers:available -122.4194 37.7749 5 km WITHDIST ASC COUNT 10 \u2192 (1) Finds all drivers within 5km radius of point, (2) Returns drivers sorted by distance ascending, (3) Limits to 10 nearest drivers, (4) Includes distance in results: [(driver_123, 0.8 km), (driver_456, 1.2 km), ...]. Performance: (1) Time complexity: O(N + log(M)) where N = number of results returned, M = total items in sorted set, (2) For 1 million drivers, finding 10 nearest within 5km: ~10ms, (3) Geohash encoding enables quick pruning (only scan nearby cells), (4) Memory: Each driver entry ~100 bytes (driver_id + geohash), 1M drivers = ~100 MB. Precision levels: (1) Geohash character length determines precision: 5 chars \u2248 5km \u00d7 5km cell, 6 chars \u2248 1.2km \u00d7 600m cell, 7 chars \u2248 150m \u00d7 150m cell, (2) Use 6-char precision for driver locations (balance between accuracy and performance). Filtering by vehicle type: (1) After GEORADIUS, filter results: FOR EACH driver_id in results: SISMEMBER drivers:sedan {driver_id}, (2) Alternative: Maintain separate indexes per vehicle type: GEOADD drivers:available:sedan {lon} {lat} {driver_id}, GEOADD drivers:available:suv {lon} {lat} {driver_id}, (3) Query specific type: GEORADIUS drivers:available:sedan -122.4194 37.7749 5 km. Updating driver locations: (1) WebSocket receives location ping: {driver_id, lat, lon}, (2) Update geospatial index: GEOADD drivers:available {lon} {lat} {driver_id} (atomic operation, replaces old location), (3) Time: ~1ms per update, (4) With 100K drivers updating every 5 seconds = 20K updates/sec = 20K GEOADD operations/sec, Redis handles 100K ops/sec per instance. Removing drivers: (1) Driver goes offline or accepts ride: GEOREM drivers:available {driver_id}, (2) Atomic removal from sorted set, (3) O(log(N)) time complexity. Persistent storage with PostGIS: (1) PostgreSQL with PostGIS extension for geographic data types, (2) Schema: CREATE TABLE driver_locations (driver_id uuid PRIMARY KEY, location geography(Point, 4326), updated_at timestamp); CREATE INDEX idx_driver_location_gist ON driver_locations USING GIST (location);, (3) Query: SELECT driver_id, ST_Distance(location, ST_MakePoint(-122.4194, 37.7749)::geography) as distance FROM driver_locations WHERE ST_DWithin(location, ST_MakePoint(-122.4194, 37.7749)::geography, 5000) ORDER BY distance LIMIT 10;, (4) GIST index enables efficient spatial queries, (5) Performance: 10-50ms for 1M drivers (slower than Redis but provides persistence and complex queries). Hybrid approach (production): (1) Redis for real-time queries (sub-10ms latency, ephemeral data with 1-min TTL), (2) PostGIS for persistent storage + historical analysis, (3) Background sync: Location Update Service writes to both: Redis (immediate) + Kafka \u2192 Consumer \u2192 PostgreSQL (async, 1-5 sec lag). Geographic sharding: (1) Divide world into regions: US-West, US-East, EU, APAC, each region has own Redis instance, (2) Route requests to nearest region (latency optimization), (3) Cross-region queries rare (rider in SF won't see driver in NYC). Alternative data structures: (1) QuadTree: Recursively partition 2D space into 4 quadrants, efficient for sparse data, (2) R-Tree: Balanced tree for multidimensional data, better for overlapping regions, (3) S2 Geometry Library (used by Uber): Google's library for spherical geometry, handles edge cases (poles, antimeridian), better accuracy for long distances. Edge cases: (1) Antimeridian crossing (\u00b1180\u00b0 longitude): Geohash handles correctly, but custom logic needed for queries spanning antimeridian, (2) Poles (90\u00b0 latitude): Few drivers at poles, but geohash degenerates, use special handling or different projection, (3) Very sparse areas: If no drivers within 5km, increase radius to 10km, 20km (exponential backoff) until drivers found or max radius reached. Caching query results: (1) Frequent queries (e.g., airport, train station): Cache top 10 drivers for location: SET nearby_drivers:{geohash} '[driver_ids]' EX 10 (10 sec TTL), (2) Invalidate on driver movement, (3) Reduces load on Redis geospatial queries for hotspots. Monitoring: (1) Track GEORADIUS query time (p50, p99), (2) Alert if p99 > 50ms (indicates performance degradation), (3) Monitor memory usage (sorted set size), (4) Track cache hit rate for hotspot locations. Production scale: Uber processes millions of geospatial queries per second globally, Redis Cluster with sharding across 100+ nodes, sub-10ms latency for 99th percentile, handles 10M+ driver location updates per minute, PostGIS used for analytics and fraud detection (abnormal GPS patterns)."
                  },
                  {
                    "q": "How do you design the notification system for real-time updates to riders and drivers?",
                    "a": "Multi-channel notification system with FCM/APN for push + WebSocket for in-app: Architecture: Notification Service consumes Kafka events (ride.matched, trip.started, driver.ride_offered) \u2192 routes to appropriate channel (push notification or WebSocket). Push notifications (FCM/APN): (1) Device token registration: On app login/install \u2192 rider/driver registers device token: POST /v1/api/devices with {user_id, device_token, platform: 'ios'/'android'}, (2) Store: INSERT INTO device_tokens (user_id, device_token, platform, active: true, registered_at), (3) Update on token refresh (iOS rotates tokens periodically). Notification flow: (1) Event published: Ride Service \u2192 Kafka 'ride.matched' topic: {trip_id, rider_id, driver_id, driver_name, vehicle_model, license_plate, eta_min: 5}, (2) Notification Service consumes: Kafka consumer group 'notification-service' processes event, (3) Fetch device tokens: SELECT device_token, platform FROM device_tokens WHERE user_id={rider_id} AND active=true (user may have multiple devices: iPhone + iPad), (4) Build payload: {title: 'Driver Assigned!', body: '{driver_name} is on the way in a {vehicle_model}. ETA: 5 min', data: {trip_id, driver_id, action: 'open_trip_screen', deep_link: 'myapp://trip/{trip_id}'}}, (5) Send via FCM (Android): fcm.send({registration_ids: [device_tokens], notification: {title, body}, data: {...}, priority: 'high', time_to_live: 600}), (6) Send via APN (iOS): apn.send({device_token, payload: {aps: {alert: {title, body}, sound: 'default', badge: 1}, custom_data: {...}}}). Notification types: (1) Ride matched \u2192 rider: 'Driver {name} accepted! ETA 5 min', (2) Ride matched \u2192 driver: 'New ride request from {rider_name}. Pickup at {address}', (3) Driver arrived \u2192 rider: 'Your driver has arrived!', (4) Trip started \u2192 rider: 'Your trip has started. Enjoy your ride!', (5) Trip completed \u2192 rider & driver: 'Trip completed. Fare: $25.50', (6) Payment processed \u2192 rider: 'Payment of $25.50 charged to card ending in 1234', (7) Surge active \u2192 nearby riders: 'High demand in your area. Fares are 1.5\u00d7 higher.'. Delivery guarantees: (1) FCM/APN best-effort delivery (not guaranteed), (2) FCM retry: Up to 4 weeks for devices offline, (3) APN retry: Up to 1 day for devices offline, (4) Fallback: If push fails, send SMS for critical notifications (driver assigned, payment failed). WebSocket for in-app real-time: (1) Rider in app: Maintains WebSocket connection to API Gateway, (2) Gateway subscribes: Redis Pub/Sub channel user:{rider_id}:notifications, (3) Notification Service publishes: PUBLISH user:{rider_id}:notifications '{type: ride_matched, data: {...}}', (4) Gateway forwards to rider via WebSocket: Immediate delivery (<100ms latency), (5) Rider app handles: {type: 'ride_matched'} \u2192 navigate to trip screen, update UI, show driver card. Multi-device synchronization: (1) User has iPhone + iPad both logged in, (2) Both devices have WebSocket connections, (3) Notification sent to both: push notification + WebSocket message, (4) When one device acknowledges (user taps notification), mark as read: SET notification:{notification_id}:read true, (5) Other device receives 'notification_read' WebSocket message \u2192 dismiss duplicate. Notification preferences: (1) User settings: Allow push for {ride_updates: true, promotions: false, surge_alerts: true}, (2) Respect preferences: Notification Service checks: SELECT preferences FROM users WHERE user_id={user_id}, skip if category disabled, (3) Regulatory compliance: GDPR/opt-in requirements for marketing notifications. Rich notifications: (1) iOS actionable notifications: Buttons on notification (Accept/Decline for driver), (2) Android heads-up display: Full-screen overlay for critical notifications (driver assigned), (3) Images: Include driver photo in ride matched notification, (4) Maps: Show pickup location map thumbnail. Notification analytics: (1) Track delivery rate: sent / delivered (FCM provides delivery receipts), (2) Track open rate: notifications delivered / notifications opened (app reports when user taps), (3) Track action rate: notifications opened / action taken (e.g., accepted ride), (4) A/B test: Different notification copy, timing, images \u2192 optimize engagement. Handling failures: (1) Invalid token (user uninstalled app): FCM/APN returns 'invalid token' error \u2192 UPDATE device_tokens SET active=false WHERE device_token={token}, (2) Transient errors (network timeout): Retry with exponential backoff (1s, 2s, 4s, max 3 retries), (3) Service unavailable (FCM/APN down): Queue in Kafka (retention 7 days), replay when service restored, (4) Critical notifications (payment failed): If push fails \u2192 send SMS via Twilio as backup. Batching: (1) Bulk notifications (surge alert to 10K users in area): Batch send 500 tokens per API call, (2) FCM multicast: Single API call with array of 1000 tokens, (3) Rate limiting: FCM: 1 million messages/min, APN: no rate limit but throttle sends to avoid overwhelming, (4) Async processing: Notification Service worker pool (50 threads) sends in parallel. Priority and TTL: (1) High priority: ride_matched, driver_arrived (immediate delivery, wake device from sleep), (2) Normal priority: trip_completed, rating_reminder (can wait if device offline), (3) Time-to-live: ride_matched TTL=10 min (stale after 10 min, driver moved on), promotion TTL=24 hours. Silent notifications: (1) Background data sync: Send silent push to update trip status without alerting user, (2) iOS: content-available: 1 flag triggers background app refresh, (3) Android: data-only message (no notification shown), (4) Use case: Update driver location, sync ride history, refresh surge map. Localization: (1) Notification text in user's language: Fetch: SELECT language FROM users WHERE user_id={user_id}, (2) Translate: title_es: '\u00a1Conductor asignado!', title_hi: '\u0921\u094d\u0930\u093e\u0907\u0935\u0930 \u0928\u093f\u092f\u0941\u0915\u094d\u0924!', (3) FCM/APN support localization keys: Send key + parameters, device renders in user's language. Cost optimization: (1) FCM: Free, (2) APN: Free (but requires Apple Developer account $99/year), (3) SMS fallback: $0.01-0.05 per SMS \u2192 use sparingly for critical notifications only, (4) Reduce volume: Don't send promotional push during ride (user focused on trip), batch non-urgent notifications. Production scale: Uber sends 100M+ push notifications daily, delivery rate: 95-99% (devices reachable), open rate: 30-50% depending on notification type, latency: push delivery p50: 1-2 seconds, p99: 5-10 seconds (includes FCM/APN processing), WebSocket: <100ms in-app delivery."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "11. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Performance",
                    "pairs": [
                      {
                        "k": "Concurrent Rides",
                        "v": "100K concurrent rides at peak hours globally"
                      },
                      {
                        "k": "Driver Location Updates",
                        "v": "Every 3-5 seconds via WebSocket (100K updates/min per 1K drivers)"
                      },
                      {
                        "k": "Matching Latency",
                        "v": "<1 second for driver assignment (geo-search + lock acquisition)"
                      },
                      {
                        "k": "Geospatial Query",
                        "v": "~10ms to find 10 nearest drivers from 1M drivers (Redis GEORADIUS)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Locks & Consistency",
                    "pairs": [
                      {
                        "k": "Zookeeper Lock",
                        "v": "Ephemeral node, 30-second session timeout, prevents double assignment"
                      },
                      {
                        "k": "Driver Response Timeout",
                        "v": "30 seconds to accept/decline, auto-release lock if expired"
                      },
                      {
                        "k": "Lock Acquisition Time",
                        "v": "<10ms typical (Zookeeper sequential node creation)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Pricing & Surge",
                    "pairs": [
                      {
                        "k": "Base Fare",
                        "v": "$2.50 flat + $1.50/km + $0.30/min (varies by region)"
                      },
                      {
                        "k": "Surge Calculation",
                        "v": "Every 60 seconds per geohash area (5km \u00d7 5km grid)"
                      },
                      {
                        "k": "Surge Multiplier",
                        "v": "1.0\u00d7 (normal) to 3.0\u00d7 (max cap), based on demand/supply ratio"
                      },
                      {
                        "k": "Surge TTL",
                        "v": "2 minutes in Redis, refreshed every 60 seconds"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching Strategy",
                    "pairs": [
                      {
                        "k": "Driver Status",
                        "v": "Redis TTL 5 min (AVAILABLE/BUSY/OFFLINE), refreshed by heartbeat"
                      },
                      {
                        "k": "Driver Location",
                        "v": "Redis TTL 1 min, updated every 3-5 sec via WebSocket"
                      },
                      {
                        "k": "Fare Estimate",
                        "v": "Redis TTL 5 min (estimate valid for 5 minutes)"
                      },
                      {
                        "k": "Ride Request",
                        "v": "Redis TTL 10 min (expires if no driver found)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Notifications & Messaging",
                    "pairs": [
                      {
                        "k": "Push Notification Latency",
                        "v": "1-3 seconds (FCM/APN processing + network)"
                      },
                      {
                        "k": "WebSocket In-App Latency",
                        "v": "<100ms (direct connection, no intermediary)"
                      },
                      {
                        "k": "Notification Delivery Rate",
                        "v": "95-99% (devices reachable, valid tokens)"
                      },
                      {
                        "k": "SMS Fallback Cost",
                        "v": "$0.01-0.05 per SMS (use sparingly for critical notifications)"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: MUST use Zookeeper distributed locking for driver assignment. Without lock, multiple ride requests can assign same driver simultaneously \u2192 double booking \u2192 both riders see same driver \u2192 terrible UX + revenue loss. Ephemeral nodes ensure automatic lock release on failure."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How prevent double assignment?'. Answer: Zookeeper ephemeral sequential nodes. When request R1 tries to lock driver D1 \u2192 creates /locks/drivers/D1/request_R1_seq0001, checks if lowest sequence \u2192 lock acquired. Concurrent request R2 gets seq0002 \u2192 sees seq0001 exists \u2192 lock failed \u2192 tries next driver."
              },
              {
                "type": "tip",
                "text": "Redis Geospatial optimization: Use GEORADIUS for sub-10ms proximity search on 1M drivers. Store as sorted set with geohash encoding. Separate indexes per vehicle type (drivers:available:sedan, drivers:available:suv) for faster filtering."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: WebSocket vs HTTP polling for location tracking. Polling = 100K riders \u00d7 20 req/min = 2M req/min = 33K req/sec overhead. WebSocket = persistent connection, send only when location changes, 100x less traffic, <100ms latency."
              },
              {
                "type": "warning",
                "text": "NEVER trust Redis alone for driver availability. Redis is cache with TTL (ephemeral). Always validate driver status in DB before final assignment: SELECT status FROM drivers WHERE driver_id={id}. Redis provides speed, DB provides truth."
              },
              {
                "type": "tip",
                "text": "Surge pricing smoothing: Use exponential moving average (new_surge = 0.7\u00d7old + 0.3\u00d7calculated) prevents sudden jumps. Rider sees gradual increase (1.2\u00d7 \u2192 1.35\u00d7 \u2192 1.5\u00d7) feels fairer than instant jump (1.0\u00d7 \u2192 2.0\u00d7). Max cap at 3.0\u00d7 prevents price gouging."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'How calculate ETA dynamically?'. Answer: Every 30 sec query Google Maps Directions API with current driver location \u2192 pickup. Response: {distance_remaining: 2.5km, duration: 8 min, route_polyline}. Broadcast ETA update to rider via WebSocket."
              },
              {
                "type": "warning",
                "text": "NEVER skip geofencing. When driver within 50m of pickup: calculate distance(driver_location, pickup_location) using Haversine. If <50m \u2192 trigger 'driver_arrived' event \u2192 notify rider 'Your driver has arrived!' Push notification + vibrate + sound."
              },
              {
                "type": "tip",
                "text": "Notification fallback: If FCM/APN push fails (invalid token, device offline), send SMS for CRITICAL notifications (driver assigned, payment failed). SMS costs $0.01-0.05 but ensures delivery. Don't spam SMS for promotions."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Kafka event streaming decouples services. 'driver.location_updated' consumed by: (1) Trip Update Consumer \u2192 persists location history to DB, (2) Analytics Service \u2192 tracks driver behavior, (3) Fraud Detection \u2192 detects GPS spoofing. 100K+ events/sec throughput."
              }
            ],
            "tags": [
              "system-design",
              "ride-sharing",
              "uber",
              "ola",
              "lyft",
              "geospatial",
              "Redis-GEORADIUS",
              "Zookeeper-locking",
              "WebSocket-location-tracking",
              "surge-pricing",
              "driver-matching",
              "real-time-updates",
              "FCM-push-notifications",
              "Kafka-events",
              "double-assignment-prevention"
            ]
          }
        },
        {
          "id": "12",
          "title": "System Design 12: Design Stock Broker Trading Application like Zerodha | Grow | Upstox | HLD | LLD",
          "videoUrl": "https://youtu.be/W5FJnSywmLE",
          "description": "In this detailed video, I break down the complete system design of a real-time stock trading platform, covering everything from market data ingestion to order placement and portfolio management.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/on34onto32n4tn3to4.png?updatedAt=1754338184533",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/qj34gj4jnj43f34g45gergeg3y.jpeg?updatedAt=1754338445706",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 24,
            "lastUpdated": "2026-01-26",
            "hero": {
              "title": "Stock Trading Platform (Zerodha / Groww / Upstox)",
              "oneLinerToMemorize": "User places order \u2192 Order Svc validates \u2192 Kafka queue \u2192 Validator checks (funds/risk/balance) \u2192 Exchange Gateway \u2192 NSE/BSE matching engine \u2192 Trade executed \u2192 Kafka order_status \u2192 Portfolio Svc updates holdings \u2192 Notification sent \u2192 Real-time price via WebSocket (InfluxDB + Redis PubSub)"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Enable user registration, login, and KYC verification (Aadhaar, PAN card)",
                  "Feature 2: Support to view real-time stock prices, historical data, charts (candlestick, line graphs)",
                  "Feature 3: Allow users to verify/link their bank account for fund transfers and withdrawals",
                  "Feature 4: Provide user with a watchlist with real-time price updates for tracked stocks (WebSocket push)",
                  "Feature 5: Provide a dashboard to track current portfolio holdings, P&L (Profit & Loss), and performance metrics",
                  "Feature 6: Users can place orders: Market orders (immediate execution at current price), Limit orders (execute only at specified price or better), Stop-loss orders (trigger when price reaches threshold)",
                  "Feature 7: Order placement flow: Place buy/sell order \u2192 Limit user orders based on available funds \u2192 Order status updates (PENDING \u2192 VERIFIED \u2192 PLACED \u2192 EXECUTED/REJECTED)",
                  "Feature 8: Support batch list processing for multiple stocks (bulk operations, portfolio rebalancing)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Performance",
                    "pairs": [
                      {
                        "k": "Scale",
                        "v": "Handle high-frequency trades with millions of users, 8-10k stocks (NSE/BSE combined)"
                      },
                      {
                        "k": "Trade Volume",
                        "v": "Peak hours: 100K+ orders/minute during market volatility, normal: 10K orders/min"
                      },
                      {
                        "k": "Latency",
                        "v": "Order placement < 100ms, market data (updated stock value) < 50ms real-time push"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability & Consistency",
                    "pairs": [
                      {
                        "k": "Uptime",
                        "v": "99.99% availability during market hours (9:15 AM - 3:30 PM IST), system downtime more important than uptime"
                      },
                      {
                        "k": "Wrong balance",
                        "v": "Duplicate trade or stale price can lead to huge financial losses - consistency critical"
                      },
                      {
                        "k": "Buy-Sell balance",
                        "v": "Buy or sell orders should be highly available, no order should be lost"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity",
                "type": "bullets",
                "items": [
                  "Entity 1: User - user_id, email, name, phone, kyc_status (PENDING/VERIFIED/REJECTED), pan_card, aadhaar_number, linked_bank_account, created_at",
                  "Entity 2: Stock - stock_symbol (RELIANCE, TCS, INFY), company_name, exchange (NSE/BSE), current_price, day_high, day_low, volume, market_cap, last_updated_at",
                  "Entity 3: Order - order_id, user_id, stock_symbol, order_type (MARKET/LIMIT/STOP_LOSS), trade_type (BUY/SELL), quantity, price (limit price for LIMIT orders, null for MARKET), status (PENDING/VERIFIED/PLACED/EXECUTED/REJECTED/CANCELLED), placed_at, executed_at, exchange_order_id",
                  "Entity 4: Trade - trade_id, order_id, stock_symbol, quantity, executed_price, executed_at, exchange (NSE/BSE), commission_fee",
                  "Entity 5: Portfolio - user_id, stock_symbol, quantity (shares owned), avg_buy_price, current_value, total_investment, profit_loss (calculated), last_updated_at",
                  "Entity 6: Watchlist - user_id, stock_symbols[] (array of tracked stocks like ['RELIANCE', 'TCS', 'INFY']), created_at, updated_at",
                  "Entity 7: Funds - user_id, available_balance (cash available for trading), locked_balance (funds locked in pending orders), total_balance, bank_account_id, last_deposit_at"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Users & Authentication",
                    "pairs": [
                      {
                        "k": "POST /auth/signup",
                        "v": "User registration with email, phone, password"
                      },
                      {
                        "k": "POST /auth/login",
                        "v": "Login with credentials, returns JWT token"
                      },
                      {
                        "k": "POST /auth/verify-kyc",
                        "v": "Submit KYC documents (PAN, Aadhaar), triggers verification workflow"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Market Data",
                    "pairs": [
                      {
                        "k": "GET /api/v1/stocks",
                        "v": "List all stocks (paginated) with current prices, volume, change%"
                      },
                      {
                        "k": "GET /api/v1/stocks/{symbol}",
                        "v": "Get detailed stock info (OHLC, volume, market cap, historical data)"
                      },
                      {
                        "k": "GET /api/v1/stocks/{symbol}/history",
                        "v": "Historical price data (daily/weekly/monthly candles) for charts"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Trading",
                    "pairs": [
                      {
                        "k": "POST /api/v1/orders",
                        "v": "Place buy/sell order {stockSymbol, orderType, tradeType, quantity, price (for LIMIT)}"
                      },
                      {
                        "k": "GET /api/v1/orders",
                        "v": "List user orders with status, filters by date/status/stock"
                      },
                      {
                        "k": "DELETE /api/v1/orders/{orderId}",
                        "v": "Cancel pending order (only if status=PENDING or PLACED, not EXECUTED)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Portfolio & Funds",
                    "pairs": [
                      {
                        "k": "GET /api/v1/portfolio",
                        "v": "Get user's holdings with current value, P&L, performance"
                      },
                      {
                        "k": "GET /api/v1/portfolio/performance",
                        "v": "Portfolio performance metrics (daily/monthly returns, total P&L)"
                      },
                      {
                        "k": "POST /api/v1/funds/deposit",
                        "v": "Initiate fund deposit from linked bank account"
                      },
                      {
                        "k": "POST /api/v1/funds/withdraw",
                        "v": "Withdraw funds to bank account (only available_balance, not locked)"
                      },
                      {
                        "k": "GET /api/v1/funds/history",
                        "v": "Fund transaction history (deposits, withdrawals, order locks)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Watchlist & Batch",
                    "pairs": [
                      {
                        "k": "GET /api/v1/watchlist",
                        "v": "Get user's watchlist with real-time prices for tracked stocks"
                      },
                      {
                        "k": "POST /api/v1/watchlist",
                        "v": "Add stock to watchlist {stockSymbol: 'RELIANCE'}"
                      },
                      {
                        "k": "DELETE /api/v1/watchlist/{stockSymbol}",
                        "v": "Remove stock from watchlist"
                      },
                      {
                        "k": "POST /api/v1/batch/validate/{stockId}/symbols",
                        "v": "Batch validate multiple stock symbols (bulk operations)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Architecture: users/client \u2192 LB/API Gateway (Authentication, Authorization, Routing, Rate limiting, Round-robin) \u2192 Services \u2192 Databases",
                  "User Svc \u2192 UserDB: Registration, login, KYC verification (Aadhaar/PAN validation), session management",
                  "Price Tracker Svc \u2192 InfluxDB (time-series DB): Stores real-time stock prices, historical OHLC data, subscribes to Exchange data feed",
                  "WatchList Svc \u2192 Watch DB: User's tracked stocks, triggers WebSocket updates when price changes",
                  "Order Svc \u2192 Order DB + Kafka: Places orders, publishes to Kafka 'new_orders' topic, validates user input, tracks order status",
                  "Portfolio Svc \u2192 Trade DB: Calculates holdings, P&L, average buy price, updates on trade execution, PnL calculation logic",
                  "Payment Svc \u2192 Payment DB: Fund deposits/withdrawals via Payment Gateway (Razorpay, Stripe), bank account linking",
                  "Validator: Validates orders (sufficient funds, risk checks, duplicate order prevention) before sending to Exchange",
                  "Exchange Gateway: Communicates with NSE/BSE via FIX protocol, sends orders, receives execution confirmations, handles 400-500 symbols/legs per connection",
                  "Exchange (NSE/BSE): External matching engine, executes trades, sends order_status updates back via Kafka",
                  "Kafka: Event backbone - topics: new_orders (order placement), verified_orders (post-validation), order_status (execution updates), stock_price (real-time price feed)",
                  "WebSocket Gateway: Pushes real-time price updates to clients (watchlist, portfolio), subscribes to Redis PubSub (stock_price channel)",
                  "InfluxDB: Time-series DB for stock prices (high write throughput, efficient range queries for charts)",
                  "Redis PubSub: Real-time price updates (publish: stock_price updates, subscribe: WebSocket servers, low latency <10ms)"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Real-Time Price Updates (WebSocket + Redis PubSub + InfluxDB)",
                    "steps": [
                      "Price Tracker Svc subscribes to Exchange data feed: NSE/BSE provides real-time price stream via WebSocket/FIX protocol, receives tick-by-tick updates for 8-10k stocks, Example update: {symbol: 'RELIANCE', price: 2458.75, volume: 125000, timestamp: '2026-01-26T10:30:45.123Z'}",
                      "Write to InfluxDB (time-series DB): INSERT into stock_prices measurement: {symbol: 'RELIANCE', price: 2458.75, volume: 125000, day_high: 2465.00, day_low: 2450.00, timestamp}, InfluxDB optimized for time-series: High write throughput (100K+ writes/sec), Efficient range queries for charts (1D, 1W, 1M, 1Y historical data), Automatic downsampling (store 1-min candles forever, aggregate to 5-min/1-hour/daily)",
                      "Publish to Redis PubSub: PUBLISH stock_price '{\"symbol\": \"RELIANCE\", \"price\": 2458.75, \"change\": +0.45%}', Channel: stock_price (global channel for all stock updates), Low latency: <10ms from Exchange \u2192 Redis PubSub \u2192 WebSocket clients",
                      "WebSocket Gateway subscribes: WebSocket servers (10+ instances) subscribe to Redis PubSub: SUBSCRIBE stock_price, On message received: Parse update, broadcast to connected clients watching that stock, Clients watching RELIANCE receive: {symbol: 'RELIANCE', price: 2458.75, change: +0.45%} instantly",
                      "Client connection: User opens app \u2192 WebSocket connection established: ws://api.trading.com/prices, Client sends watchlist: {action: 'subscribe', symbols: ['RELIANCE', 'TCS', 'INFY']}, Server filters updates: Only sends RELIANCE/TCS/INFY price changes to this client (not all 8-10k stocks), Efficient: Each client receives only watched stocks, reduces bandwidth 1000\u00d7"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Order Placement Flow (Market Order)",
                    "steps": [
                      "User places MARKET BUY order: POST /api/v1/orders with {stockSymbol: 'RELIANCE', orderType: 'MARKET', tradeType: 'BUY', quantity: 10}, MARKET order: Execute immediately at current market price (best available price)",
                      "Order Svc validation: (1) Check user authentication (JWT token valid), (2) Validate stock symbol: Query stock_symbols table, if not exists \u2192 return 400 'Invalid stock symbol', (3) Check market hours: if (currentTime < 9:15 AM OR currentTime > 3:30 PM IST) \u2192 return 400 'Market closed', (4) Check quantity: Must be positive integer, (5) Generate order_id (UUID)",
                      "Check available funds: Query Funds table: SELECT available_balance FROM funds WHERE user_id={user_id}, Current price: Query Redis cache: GET stock_price:RELIANCE \u2192 2458.75, Estimated cost: quantity \u00d7 price = 10 \u00d7 2458.75 = \u20b924,587.50, Commission: \u20b920 (flat fee), Total required: \u20b924,607.50, If available_balance < \u20b924,607.50 \u2192 return 400 'Insufficient funds'",
                      "Lock funds: BEGIN TRANSACTION, UPDATE funds SET available_balance = available_balance - 24607.50, locked_balance = locked_balance + 24607.50 WHERE user_id={user_id}, COMMIT, Ensures funds can't be used for other orders (prevents overdraft)",
                      "Insert order: INSERT INTO orders (order_id, user_id, stock_symbol: 'RELIANCE', order_type: 'MARKET', trade_type: 'BUY', quantity: 10, price: null, status: 'PENDING', placed_at: now())",
                      "Publish to Kafka: Kafka.send('new_orders', {orderId, userId, stockSymbol: 'RELIANCE', orderType: 'MARKET', tradeType: 'BUY', quantity: 10}), Response to user: 200 OK {orderId: 'ORD123', status: 'PENDING', message: 'Order placed successfully'}"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Order Validation & Exchange Submission",
                    "steps": [
                      "Validator consumes Kafka 'new_orders': Validator service (5 instances, consume from 50 partitions), Receives order: {orderId: 'ORD123', ...}",
                      "Validation checks: (1) Duplicate check: SELECT COUNT(*) FROM orders WHERE user_id={user_id} AND stock_symbol='RELIANCE' AND status IN ('PENDING', 'PLACED') AND placed_at > NOW() - INTERVAL '5 seconds', if count > 1 \u2192 reject as duplicate (user double-clicked submit), (2) Risk management: Check user's total exposure: SELECT SUM(quantity \u00d7 price) FROM orders WHERE user_id={user_id} AND status IN ('PENDING', 'PLACED'), if exposure > 10\u00d7 available_balance \u2192 reject (prevent excessive risk), (3) Circuit breaker: if stock price moved >10% in last 5 min \u2192 reject (market volatility protection), (4) Compliance: Check if user is blocked/restricted for trading this stock",
                      "If validation passes: UPDATE orders SET status='VERIFIED' WHERE order_id='ORD123', Publish to Kafka 'verified_orders': {orderId: 'ORD123', ...}",
                      "Exchange Gateway consumes 'verified_orders': Exchange Gateway subscribes to Kafka, receives verified order, Formats order in FIX protocol (Financial Information eXchange): FIX message: 35=D (NewOrderSingle), 55=RELIANCE (Symbol), 54=1 (Buy), 38=10 (Quantity), 40=1 (Market order), Sends via TCP to NSE/BSE matching engine",
                      "UPDATE orders SET status='PLACED', exchange_order_id={exchange_assigned_id} WHERE order_id='ORD123'",
                      "NSE/BSE matching engine: Matches BUY order with available SELL orders, Execution at best available price (e.g., \u20b92459.00, slightly higher than cached price \u20b92458.75), Sends execution confirmation back to Exchange Gateway"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Trade Execution & Order Status Updates",
                    "steps": [
                      "Exchange sends execution confirmation: FIX message: 35=8 (ExecutionReport), 150=2 (Filled), Execution details: {orderId: exchange_order_id, executedPrice: 2459.00, executedQuantity: 10, executedAt: timestamp}",
                      "Exchange Gateway publishes to Kafka: Kafka.send('order_status', {orderId: 'ORD123', status: 'EXECUTED', executedPrice: 2459.00, executedQuantity: 10, executedAt: timestamp}), Topic: order_status (consumed by Order Tracker Svc, Notification Svc, Portfolio Svc)",
                      "Order Tracker Svc updates order: UPDATE orders SET status='EXECUTED', executed_at=now() WHERE order_id='ORD123', INSERT INTO trades (trade_id, order_id, stock_symbol: 'RELIANCE', quantity: 10, executed_price: 2459.00, executed_at: now(), exchange: 'NSE', commission_fee: 20)",
                      "Update funds (unlock and deduct actual cost): Actual cost: 10 \u00d7 2459.00 = \u20b924,590 (vs estimated \u20b924,587.50, difference due to price movement), Total debit: \u20b924,590 + \u20b920 commission = \u20b924,610, BEGIN TRANSACTION, UPDATE funds SET locked_balance = locked_balance - 24607.50 (unlock estimated), available_balance = available_balance - (24610 - 24607.50) = -2.50 (deduct difference), COMMIT, Final: User's cash balance reduced by \u20b924,610",
                      "Portfolio Svc updates holdings: Consumes Kafka 'order_status', Check if user owns stock: SELECT quantity, avg_buy_price FROM portfolio WHERE user_id={user_id} AND stock_symbol='RELIANCE', If exists: UPDATE portfolio SET quantity = quantity + 10, avg_buy_price = ((prev_quantity \u00d7 prev_avg_price) + (10 \u00d7 2459.00)) / (prev_quantity + 10) WHERE ..., Else (first purchase): INSERT INTO portfolio (user_id, stock_symbol: 'RELIANCE', quantity: 10, avg_buy_price: 2459.00, total_investment: 24590, last_updated_at: now())",
                      "Calculate P&L: Current price: GET stock_price:RELIANCE \u2192 2460.00 (from Redis), current_value: 10 \u00d7 2460.00 = \u20b924,600, profit_loss: 24600 - 24590 = +\u20b910 (unrealized gain), UPDATE portfolio SET current_value=24600, profit_loss=10"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Limit Order Flow",
                    "steps": [
                      "User places LIMIT SELL order: POST /api/v1/orders with {stockSymbol: 'TCS', orderType: 'LIMIT', tradeType: 'SELL', quantity: 5, price: 3500.00}, LIMIT order: Execute only when market price reaches \u20b93500 (or higher for SELL, lower for BUY)",
                      "Order Svc validation: (1) Check user owns stock: SELECT quantity FROM portfolio WHERE user_id={user_id} AND stock_symbol='TCS', if quantity < 5 \u2192 return 400 'Insufficient holdings', (2) Validate limit price: Must be within \u00b110% of current market price (prevent erroneous orders like SELL at \u20b91 or \u20b9100,000), (3) Lock shares: UPDATE portfolio SET quantity = quantity - 5, locked_quantity = locked_quantity + 5 (prevent selling same shares twice)",
                      "Insert LIMIT order: INSERT INTO orders (order_id, user_id, stock_symbol: 'TCS', order_type: 'LIMIT', trade_type: 'SELL', quantity: 5, price: 3500.00, status: 'PENDING', placed_at: now())",
                      "Publish to Kafka 'new_orders', Validator validates and publishes to 'verified_orders', Exchange Gateway sends to NSE/BSE",
                      "Exchange behavior: LIMIT order sits in order book until price condition met, If TCS price reaches \u20b93500.00 \u2192 order matched with BUY order at that price, If price never reaches \u20b93500 \u2192 order remains open (until cancelled or market closes), End of day: If not executed \u2192 order cancelled (day order) OR remains open (GTC - Good Till Cancelled, if specified)",
                      "Execution (when price hits \u20b93500): Exchange sends 'EXECUTED' status \u2192 Kafka 'order_status', Order Tracker Svc updates order status='EXECUTED', Portfolio Svc: UPDATE portfolio SET locked_quantity = locked_quantity - 5 (unlock shares, already removed from available quantity), Funds Svc: UPDATE funds SET available_balance = available_balance + (5 \u00d7 3500 - commission) = +\u20b917,480 (credit proceeds)"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Notification Service (Order Status Updates)",
                    "steps": [
                      "Notification Svc consumes Kafka 'order_status': Receives: {orderId: 'ORD123', status: 'EXECUTED', stockSymbol: 'RELIANCE', ...}",
                      "Determine notification channels: Check user preferences: SELECT push_enabled, email_enabled, sms_enabled FROM user_preferences WHERE user_id={user_id}, If push_enabled: Send push notification via FCM/APNS",
                      "Push notification: FCM (Firebase): POST https://fcm.googleapis.com/v1/projects/.../messages:send with {token: user_device_token, notification: {title: 'Order Executed', body: 'Your BUY order for 10 shares of RELIANCE executed at \u20b92459.00'}, data: {orderId: 'ORD123', stockSymbol: 'RELIANCE'}}, User's mobile app receives notification, tapping opens order details",
                      "In-app notification: WebSocket push: If user is online (active WebSocket connection), send: {type: 'ORDER_STATUS', orderId: 'ORD123', status: 'EXECUTED', stockSymbol: 'RELIANCE', executedPrice: 2459.00}, Real-time update in app (order status changes from 'PENDING' \u2192 'EXECUTED')",
                      "Email notification (optional): Send email: Subject: 'Order Executed - RELIANCE', Body: 'Your BUY order for 10 shares of RELIANCE was executed at \u20b92459.00. Total cost: \u20b924,610 (including \u20b920 commission).', SMTP via SendGrid/SES"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Portfolio Performance Calculation (PnL)",
                    "steps": [
                      "User views portfolio: GET /api/v1/portfolio",
                      "Portfolio Svc calculates real-time P&L: For each holding, fetch current price from Redis: GET stock_price:RELIANCE \u2192 2460.00, Holding: {stock_symbol: 'RELIANCE', quantity: 10, avg_buy_price: 2459.00, total_investment: 24590}, Current value: 10 \u00d7 2460.00 = \u20b924,600, Unrealized P&L: 24600 - 24590 = +\u20b910 (+0.04%), Day change: (current_price - prev_close) / prev_close \u00d7 100 = (2460 - 2455) / 2455 \u00d7 100 = +0.20%",
                      "Aggregate portfolio: Total investment: SUM(total_investment for all holdings) = \u20b92,45,000, Current value: SUM(current_value for all holdings) = \u20b92,48,500, Total P&L: 2,48,500 - 2,45,000 = +\u20b93,500 (+1.43%), Today's change: SUM(day change for all holdings weighted by current value)",
                      "Response: {holdings: [{stockSymbol: 'RELIANCE', quantity: 10, avgBuyPrice: 2459.00, currentPrice: 2460.00, currentValue: 24600, profitLoss: +10, profitLossPercent: +0.04%, dayChange: +0.20%}, ...], totalInvestment: 245000, currentValue: 248500, totalProfitLoss: +3500, totalProfitLossPercent: +1.43%}"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Users (UserDB)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "phone",
                        "v": "varchar(15) UNIQUE"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "password_hash",
                        "v": "varchar(255) (bcrypt)"
                      },
                      {
                        "k": "kyc_status",
                        "v": "enum (PENDING, VERIFIED, REJECTED)"
                      },
                      {
                        "k": "pan_card",
                        "v": "varchar(10) UNIQUE (required for KYC)"
                      },
                      {
                        "k": "aadhaar_number",
                        "v": "varchar(12) (encrypted, required for KYC)"
                      },
                      {
                        "k": "linked_bank_account",
                        "v": "varchar(50) (account number for fund transfers)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Stocks (Stock master data)",
                    "pairs": [
                      {
                        "k": "stock_symbol",
                        "v": "varchar(20) PRIMARY KEY (e.g., 'RELIANCE', 'TCS', 'INFY')"
                      },
                      {
                        "k": "company_name",
                        "v": "varchar(255) (Reliance Industries Limited)"
                      },
                      {
                        "k": "exchange",
                        "v": "enum (NSE, BSE)"
                      },
                      {
                        "k": "current_price",
                        "v": "decimal(10,2) (cached from Redis, updated every second)"
                      },
                      {
                        "k": "prev_close",
                        "v": "decimal(10,2) (previous day closing price)"
                      },
                      {
                        "k": "day_high",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "day_low",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "volume",
                        "v": "bigint (shares traded today)"
                      },
                      {
                        "k": "market_cap",
                        "v": "decimal(15,2) (in crores)"
                      },
                      {
                        "k": "sector",
                        "v": "varchar(100) (Technology, Finance, Energy, etc.)"
                      },
                      {
                        "k": "last_updated_at",
                        "v": "timestamptz"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Orders (Order DB)",
                    "pairs": [
                      {
                        "k": "order_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "stock_symbol",
                        "v": "varchar(20) FK \u2192 Stocks"
                      },
                      {
                        "k": "order_type",
                        "v": "enum (MARKET, LIMIT, STOP_LOSS)"
                      },
                      {
                        "k": "trade_type",
                        "v": "enum (BUY, SELL)"
                      },
                      {
                        "k": "quantity",
                        "v": "int (number of shares)"
                      },
                      {
                        "k": "price",
                        "v": "decimal(10,2) (limit price for LIMIT orders, null for MARKET)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, VERIFIED, PLACED, EXECUTED, REJECTED, CANCELLED)"
                      },
                      {
                        "k": "placed_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "executed_at",
                        "v": "timestamptz (nullable, when trade executed)"
                      },
                      {
                        "k": "exchange_order_id",
                        "v": "varchar(50) (order ID from NSE/BSE)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (user_id, placed_at DESC), INDEX on (status, placed_at), INDEX on (stock_symbol, status)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Trades (Trade history)",
                    "pairs": [
                      {
                        "k": "trade_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "order_id",
                        "v": "uuid FK \u2192 Orders"
                      },
                      {
                        "k": "stock_symbol",
                        "v": "varchar(20)"
                      },
                      {
                        "k": "quantity",
                        "v": "int"
                      },
                      {
                        "k": "executed_price",
                        "v": "decimal(10,2) (actual execution price from exchange)"
                      },
                      {
                        "k": "executed_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "exchange",
                        "v": "enum (NSE, BSE)"
                      },
                      {
                        "k": "commission_fee",
                        "v": "decimal(8,2) (brokerage commission)"
                      },
                      {
                        "k": "trade_type",
                        "v": "enum (BUY, SELL)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (order_id), INDEX on (executed_at DESC) for recent trades"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Portfolio (Holdings)",
                    "pairs": [
                      {
                        "k": "Composite PK",
                        "v": "(user_id, stock_symbol)"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "stock_symbol",
                        "v": "varchar(20) FK \u2192 Stocks"
                      },
                      {
                        "k": "quantity",
                        "v": "int (shares owned, available for selling)"
                      },
                      {
                        "k": "locked_quantity",
                        "v": "int DEFAULT 0 (shares locked in pending SELL orders)"
                      },
                      {
                        "k": "avg_buy_price",
                        "v": "decimal(10,2) (weighted average purchase price)"
                      },
                      {
                        "k": "total_investment",
                        "v": "decimal(15,2) (quantity \u00d7 avg_buy_price)"
                      },
                      {
                        "k": "current_value",
                        "v": "decimal(15,2) (quantity \u00d7 current_price, calculated)"
                      },
                      {
                        "k": "profit_loss",
                        "v": "decimal(15,2) (current_value - total_investment, calculated)"
                      },
                      {
                        "k": "last_updated_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "Calculation",
                        "v": "P&L calculated on-the-fly using current price from Redis, not stored (stale data risk)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Funds (Cash balance)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY FK \u2192 Users"
                      },
                      {
                        "k": "available_balance",
                        "v": "decimal(15,2) (cash available for trading)"
                      },
                      {
                        "k": "locked_balance",
                        "v": "decimal(15,2) (cash locked in pending BUY orders)"
                      },
                      {
                        "k": "total_balance",
                        "v": "decimal(15,2) (available_balance + locked_balance)"
                      },
                      {
                        "k": "bank_account_id",
                        "v": "varchar(50) (linked bank account for deposits/withdrawals)"
                      },
                      {
                        "k": "last_deposit_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "last_withdrawal_at",
                        "v": "timestamptz"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Watchlist",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "stock_symbols",
                        "v": "text[] (array of stock symbols: ['RELIANCE', 'TCS', 'INFY'])"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "Indexes",
                        "v": "GIN index on stock_symbols for array containment queries"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Payment Transactions (Payment DB)",
                    "pairs": [
                      {
                        "k": "transaction_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "type",
                        "v": "enum (DEPOSIT, WITHDRAWAL)"
                      },
                      {
                        "k": "amount",
                        "v": "decimal(15,2)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, COMPLETED, FAILED)"
                      },
                      {
                        "k": "payment_gateway",
                        "v": "varchar(50) (Razorpay, Stripe, UPI)"
                      },
                      {
                        "k": "gateway_transaction_id",
                        "v": "varchar(100) (external transaction ID)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "completed_at",
                        "v": "timestamptz (nullable)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "InfluxDB (Time-series stock prices)",
                    "pairs": [
                      {
                        "k": "Measurement",
                        "v": "stock_prices"
                      },
                      {
                        "k": "Tags",
                        "v": "symbol (indexed), exchange"
                      },
                      {
                        "k": "Fields",
                        "v": "price (float), volume (int), day_high (float), day_low (float)"
                      },
                      {
                        "k": "Timestamp",
                        "v": "nanosecond precision"
                      },
                      {
                        "k": "Retention",
                        "v": "1-min candles forever, 5-min/1-hour/daily aggregated via continuous queries"
                      },
                      {
                        "k": "Query example",
                        "v": "SELECT mean(price) FROM stock_prices WHERE symbol='RELIANCE' AND time > now() - 1d GROUP BY time(5m)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis Cache",
                    "pairs": [
                      {
                        "k": "stock_price:{symbol}",
                        "v": "STRING (current price) - stock_price:RELIANCE \u2192 '2458.75' (TTL 1 sec, refreshed constantly)"
                      },
                      {
                        "k": "stock_price PubSub",
                        "v": "CHANNEL - PUBLISH stock_price '{symbol, price, change}', WebSocket servers SUBSCRIBE"
                      },
                      {
                        "k": "user_session:{token}",
                        "v": "HASH (user session data) - TTL 24 hours"
                      },
                      {
                        "k": "order_lock:{orderId}",
                        "v": "STRING (distributed lock for order processing) - TTL 30 sec"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Kafka Topics",
                    "pairs": [
                      {
                        "k": "new_orders",
                        "v": "New order placements (50 partitions by user_id hash), consumed by Validator"
                      },
                      {
                        "k": "verified_orders",
                        "v": "Validated orders ready for exchange (10 partitions), consumed by Exchange Gateway"
                      },
                      {
                        "k": "order_status",
                        "v": "Order execution updates from exchange (50 partitions by order_id hash), consumed by Order Tracker, Portfolio Svc, Notification Svc"
                      },
                      {
                        "k": "stock_price",
                        "v": "Real-time price feed from exchanges (100 partitions by stock_symbol hash), consumed by Price Tracker Svc"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "8. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Redis PubSub for real-time prices - Price updates published to stock_price channel (<10ms latency), WebSocket servers subscribe and push to clients, avoids polling (100K+ concurrent connections), scales horizontally (add more WebSocket servers)",
                  "Technique 2: InfluxDB time-series DB - Optimized for stock prices (100K+ writes/sec), automatic downsampling (1-min candles \u2192 5-min \u2192 hourly \u2192 daily), efficient range queries for charts (1Y data in <100ms), retention policies (1-min data forever, aggregates auto-calculated)",
                  "Technique 3: Kafka event-driven architecture - Decouples order flow (Order Svc \u2192 Validator \u2192 Exchange Gateway \u2192 Order Tracker), async processing (user gets 200 OK immediately), enables horizontal scaling (add more consumers), guarantees order processing (at-least-once delivery)",
                  "Technique 4: Fund locking with transactions - Prevents overdraft: Lock funds on order placement (UPDATE available \u2192 locked in single transaction), Unlock on execution/cancellation, ACID guarantees (no race conditions), Isolation level: SERIALIZABLE for fund updates",
                  "Technique 5: Database sharding - Orders table sharded by user_id (user's orders on same shard, fast queries), Portfolio sharded by user_id (user's holdings together), 10 shards: each handles 100K users (total 1M users), Cross-shard: Only for admin queries (acceptable slow)",
                  "Technique 6: Read replicas for portfolio/watchlist - 1 master (writes: order execution, fund updates) + 5 read replicas (reads: portfolio view, watchlist, order history), Read/write split: Portfolio GET \u2192 replicas (95% traffic), Trade execution \u2192 master, Lag <1 sec (acceptable for portfolio view)",
                  "Technique 7: Exchange Gateway connection pooling - Maintain 400-500 persistent FIX connections to NSE/BSE, Connection per symbol group (RELIANCE, TCS, INFY on same connection), Reuse connections (avoid handshake overhead), Load balancing: Distribute orders across connections",
                  "Technique 8: Order validation pipeline - Multi-stage: (1) Duplicate check (Redis SET orderId EX 5, SETNX returns false if exists), (2) Fund check (query Funds table), (3) Risk check (total exposure limit), (4) Circuit breaker (price volatility), Parallel checks where possible (<50ms total validation)",
                  "Technique 9: WebSocket gateway auto-scaling - Auto-scale based on connections: if connections > 10K per server \u2192 add instance, Sticky sessions: User always connects to same server (maintains WebSocket state), Load balancer: Route by user_id hash (consistent routing)",
                  "Technique 10: Price caching strategy - Redis cache: Current price (TTL 1 sec, refreshed from InfluxDB/PubSub), Historical data: Cache popular queries (RELIANCE 1D chart \u2192 5 min TTL), Cache warming: Pre-load top 100 stocks on server start, Hit rate: 99% for current prices (all trades use cached price)",
                  "Technique 11: Rate limiting - Per user: 10 orders/sec (prevent fat-finger errors, accidental rapid clicks), Per API endpoint: 100 req/sec per user (portfolio, watchlist), Global: 100K orders/min (circuit breaker if exceeded, system protection)",
                  "Technique 12: Async notification with queues - Notification Svc consumes Kafka 'order_status' async, User gets trade confirmation within 1 sec (push notification), Email/SMS queued for later (non-blocking), Failed notifications \u2192 retry with exponential backoff (DLQ after 3 attempts)"
                ]
              },
              {
                "id": "interview-qa",
                "title": "9. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you ensure real-time price updates reach millions of users with <50ms latency?",
                    "a": "Real-time price distribution with WebSocket + Redis PubSub: (1) Price ingestion: Price Tracker Svc subscribes to NSE/BSE data feed (WebSocket/FIX protocol), receives tick-by-tick updates for 8-10k stocks, Example: {symbol: 'RELIANCE', price: 2458.75, volume: 125000, timestamp}. (2) Dual write: Write to InfluxDB (time-series persistence): INSERT stock_prices (symbol='RELIANCE', price=2458.75, volume=125000, timestamp), Async write, high throughput (100K+ writes/sec), Used for historical charts, not real-time push. Publish to Redis PubSub: PUBLISH stock_price '{\"symbol\": \"RELIANCE\", \"price\": 2458.75, \"change\": +0.45%}', Fan-out to all subscribers instantly (<10ms), No persistence (in-memory pub/sub). (3) WebSocket servers subscribe: 10 WebSocket gateway servers (horizontally scaled), Each server: SUBSCRIBE stock_price (Redis PubSub channel), Receives all price updates in real-time, Filters and pushes to connected clients. (4) Client connections: User opens app \u2192 WebSocket connection: ws://api.trading.com/prices, Handshake: Client sends watchlist: {action: 'subscribe', symbols: ['RELIANCE', 'TCS', 'INFY']}, Server stores subscription: userConnections[userId] = {socket, symbols: ['RELIANCE', 'TCS', 'INFY']}. (5) Selective push: Redis PubSub message received: {symbol: 'RELIANCE', price: 2458.75}, Server iterates connected users: for each user watching 'RELIANCE', send via WebSocket: socket.send({symbol: 'RELIANCE', price: 2458.75}), Only users watching RELIANCE receive update (not all 8-10k stock updates), Reduces bandwidth 1000\u00d7 (each user watches ~10 stocks, not all 8-10k). (6) Latency breakdown: Exchange \u2192 Price Tracker: 5ms (network), Price Tracker \u2192 Redis PubSub: 2ms (PUBLISH command), Redis PubSub \u2192 WebSocket server: 3ms (in-memory fan-out), WebSocket server \u2192 Client: 10ms (internet latency), Total: ~20ms (well under 50ms target). (7) Scaling: Horizontal: Add more WebSocket servers (each handles 10K connections), Load balancer: Sticky sessions (user_id hash \u2192 same server), maintains WebSocket state, Redis PubSub: Single channel, all servers subscribe (fan-out), no coordination needed. (8) Fallback: If WebSocket disconnects: Client auto-reconnects with exponential backoff (1s, 2s, 4s, max 30s), On reconnect: Re-subscribe to watchlist, fetch missed updates from REST API (GET /api/v1/stocks/{symbol}). Result: Sub-50ms real-time updates, scales to millions of concurrent users, efficient bandwidth usage (selective push), resilient (auto-reconnect on failure)."
                  },
                  {
                    "q": "How do you prevent overdraft when multiple orders are placed simultaneously?",
                    "a": "Fund locking with database transactions and pessimistic locking: (1) Problem: User has \u20b950,000 available balance, Places 2 simultaneous MARKET BUY orders: Order A: Buy 20 RELIANCE @ \u20b92459 = \u20b949,180, Order B: Buy 5 TCS @ \u20b93500 = \u20b917,500, Total required: \u20b949,180 + \u20b917,500 = \u20b966,680 > \u20b950,000 (overdraft!), Without locking: Both orders check balance (\u20b950,000 > \u20b949,180 \u2713, \u20b950,000 > \u20b917,500 \u2713), Both pass validation, both execute \u2192 overdraft by \u20b916,680. (2) Solution: Pessimistic locking with SELECT FOR UPDATE: Order A processing: BEGIN TRANSACTION, SELECT available_balance FROM funds WHERE user_id={user_id} FOR UPDATE (locks row, other transactions wait), available_balance = \u20b950,000, Check: \u20b950,000 >= \u20b949,180 \u2713 (pass), UPDATE funds SET available_balance = 50000 - 49180 = \u20b9820, locked_balance = 0 + 49180 = \u20b949,180, COMMIT (releases lock). (3) Order B processing (simultaneously): BEGIN TRANSACTION, SELECT available_balance FROM funds WHERE user_id={user_id} FOR UPDATE, Waits for Order A transaction to commit (row locked), Once Order A commits: reads available_balance = \u20b9820 (updated value), Check: \u20b9820 >= \u20b917,500 \u2717 (fail), ROLLBACK, Return error: 400 'Insufficient funds'. (4) Transaction isolation: Isolation level: SERIALIZABLE (highest level), Prevents: Dirty reads (read uncommitted data), Non-repeatable reads (data changes mid-transaction), Phantom reads (new rows appear), Guarantees: Each transaction sees consistent snapshot. (5) Alternative: Optimistic locking with version: funds table has version column (starts at 1), Order A: SELECT available_balance, version FROM funds WHERE user_id={user_id}, Read: balance=\u20b950,000, version=1, UPDATE funds SET available_balance=820, locked_balance=49180, version=2 WHERE user_id={user_id} AND version=1, If version still 1: update succeeds (1 row affected), If version changed (Order B updated first): update fails (0 rows affected), Retry with new version. (6) Optimistic vs Pessimistic: Pessimistic (SELECT FOR UPDATE): Use when contention is high (many users trading simultaneously), Blocks concurrent updates (serializes), Guarantees correctness at cost of throughput. Optimistic (version check): Use when contention is low (rare simultaneous orders from same user), Allows concurrent reads, retry on conflict, Higher throughput, but retries add latency. (7) Production choice: Use pessimistic locking for fund updates (correctness critical, can't overdraft), Use optimistic locking for portfolio updates (can tolerate retries, less critical). Result: No overdraft possible, ACID guarantees maintained, concurrent orders handled correctly, trade-off: slight latency increase for correctness (acceptable for financial transactions)."
                  },
                  {
                    "q": "Walk through complete order flow from placement to execution with all validations and state transitions.",
                    "a": "Complete order flow (MARKET BUY order): (1) User places order: POST /api/v1/orders {stockSymbol: 'RELIANCE', orderType: 'MARKET', tradeType: 'BUY', quantity: 10}. (2) Order Svc validation: Check JWT token valid (user authenticated), Validate stock_symbol: SELECT EXISTS FROM stocks WHERE stock_symbol='RELIANCE', if not exists \u2192 400 'Invalid stock', Check market hours: if currentTime NOT BETWEEN 9:15 AM AND 3:30 PM IST \u2192 400 'Market closed', Check quantity: must be positive int, multiple of lot size (1 for equity), Generate order_id: UUID. (3) Fund check: Query current price: Redis GET stock_price:RELIANCE \u2192 \u20b92458.75, Estimated cost: 10 \u00d7 2458.75 + \u20b920 commission = \u20b924,607.50, Fund locking: BEGIN TRANSACTION, SELECT available_balance FROM funds WHERE user_id={user_id} FOR UPDATE, if available_balance < 24607.50 \u2192 ROLLBACK, return 400 'Insufficient funds', UPDATE funds SET available_balance -= 24607.50, locked_balance += 24607.50, COMMIT. (4) Insert order: INSERT INTO orders (order_id, user_id, stock_symbol, order_type: 'MARKET', trade_type: 'BUY', quantity: 10, price: null, status: 'PENDING', placed_at: now()), Status: PENDING (initial state). (5) Kafka publish: Kafka.send('new_orders', {orderId, userId, stockSymbol, orderType, tradeType, quantity}), Response to user: 200 OK {orderId: 'ORD123', status: 'PENDING'}. (6) Validator consumes 'new_orders': Duplicate check: Redis SETNX order_lock:{orderId} 1 EX 30, if already exists (SETNX returns 0) \u2192 duplicate, reject, Risk check: Total exposure: SELECT SUM(quantity \u00d7 price) FROM orders WHERE user_id={user_id} AND status IN ('PENDING','PLACED'), if exposure > 10\u00d7 available_balance \u2192 reject (excessive risk), Circuit breaker: SELECT price FROM stock_prices WHERE symbol='RELIANCE' AND timestamp > NOW() - 5 MIN, if price volatility > 10% in 5 min \u2192 reject (market volatility), If all pass: UPDATE orders SET status='VERIFIED', Publish Kafka 'verified_orders': {orderId}. (7) Exchange Gateway consumes 'verified_orders': Format FIX message (Financial Information eXchange): Tag 35=D (NewOrderSingle), Tag 55='RELIANCE' (Symbol), Tag 54=1 (Side: Buy), Tag 38=10 (Quantity), Tag 40=1 (OrdType: Market), Send via TCP to NSE matching engine, UPDATE orders SET status='PLACED', exchange_order_id={assigned_by_exchange}. (8) NSE matching engine: Matches BUY order with best available SELL order(s), Execution price: \u20b92459.00 (market price at execution time, may differ from cached \u20b92458.75), Sends FIX ExecutionReport: Tag 35=8, Tag 150=2 (ExecType: Filled), Details: {executedPrice: 2459.00, executedQuantity: 10}. (9) Exchange Gateway receives execution: Publish Kafka 'order_status': {orderId, status: 'EXECUTED', executedPrice: 2459.00, executedQuantity: 10, executedAt: timestamp}. (10) Order Tracker Svc consumes 'order_status': UPDATE orders SET status='EXECUTED', executed_at=now(), INSERT INTO trades (trade_id, order_id, stock_symbol, quantity: 10, executed_price: 2459.00, executed_at: now(), exchange: 'NSE', commission_fee: 20). (11) Fund update: Actual cost: 10 \u00d7 2459 + 20 = \u20b924,610 (vs estimated \u20b924,607.50), BEGIN TRANSACTION, UPDATE funds SET locked_balance -= 24607.50 (unlock estimated), available_balance -= (24610 - 24607.50) = -2.50 (deduct difference), COMMIT. (12) Portfolio Svc consumes 'order_status': Check existing holding: SELECT quantity, avg_buy_price FROM portfolio WHERE user_id={user_id} AND stock_symbol='RELIANCE', If new holding: INSERT INTO portfolio (user_id, stock_symbol, quantity: 10, avg_buy_price: 2459.00, total_investment: 24590), If existing: UPDATE portfolio SET quantity += 10, avg_buy_price = (prev_qty \u00d7 prev_avg + 10 \u00d7 2459) / (prev_qty + 10), total_investment = quantity \u00d7 avg_buy_price. (13) Notification Svc consumes 'order_status': Push notification via FCM: {title: 'Order Executed', body: 'RELIANCE BUY 10 @ \u20b92459.00'}, WebSocket push (if user online): {type: 'ORDER_STATUS', orderId, status: 'EXECUTED'}. State transitions: PENDING (order created, funds locked) \u2192 VERIFIED (validations passed) \u2192 PLACED (sent to exchange) \u2192 EXECUTED (trade confirmed) OR REJECTED (validation failed, funds unlocked). Result: End-to-end order flow with validations, fund locking prevents overdraft, state tracking enables user visibility, Kafka decouples stages for scalability."
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Fund locking with SELECT FOR UPDATE mandatory to prevent overdraft. NEVER check balance and update in separate queries (race condition). Use single transaction with pessimistic lock. Isolation level SERIALIZABLE for fund updates. Duplicate trade or stale price = huge financial losses."
              },
              {
                "type": "interview-favorite",
                "text": "Real-time prices: Exchange \u2192 Price Tracker \u2192 InfluxDB (persist) + Redis PubSub (fan-out) \u2192 WebSocket servers (subscribe) \u2192 Clients (selective push). Sub-50ms latency. WebSocket scales horizontally (sticky sessions), Redis PubSub handles fan-out to 10+ servers instantly."
              },
              {
                "type": "tip",
                "text": "State transitions: PENDING (created, funds locked) \u2192 VERIFIED (validation passed) \u2192 PLACED (sent to exchange) \u2192 EXECUTED (trade confirmed). Track status in orders table + Kafka 'order_status' topic. Enables user visibility, retry logic, audit trail."
              },
              {
                "type": "interview-favorite",
                "text": "Order validation pipeline: (1) Duplicate check (Redis SETNX), (2) Fund check (SELECT FOR UPDATE), (3) Risk check (total exposure limit), (4) Circuit breaker (price volatility >10% in 5min). Multi-stage prevents invalid orders reaching exchange. <50ms total validation."
              },
              {
                "type": "warning",
                "text": "NEVER calculate P&L and store in DB. Current value = quantity \u00d7 current_price (from Redis). Calculate on-the-fly when user views portfolio. Storing creates stale data risk (price changes every second). Only store: quantity, avg_buy_price, total_investment (static until trade)."
              },
              {
                "type": "tip",
                "text": "InfluxDB time-series: Optimized for stock prices (100K+ writes/sec). Automatic downsampling: 1-min candles \u2192 5-min \u2192 hourly \u2192 daily (continuous queries). Retention forever for 1-min data. Efficient range queries for charts (1Y data <100ms). Don't use SQL DB for time-series."
              },
              {
                "type": "interview-favorite",
                "text": "Exchange communication: FIX protocol (Financial Information eXchange) via TCP. Maintain 400-500 persistent connections (connection pool per symbol group). Format order: Tag 35=D (NewOrderSingle), Tag 55=symbol, Tag 54=side (1=Buy,2=Sell), Tag 38=quantity, Tag 40=type (1=Market,2=Limit). Receive ExecutionReport: Tag 35=8."
              }
            ],
            "tags": [
              "system-design",
              "stock-trading",
              "zerodha",
              "groww",
              "upstox",
              "websocket-realtime",
              "redis-pubsub",
              "influxdb-timeseries",
              "kafka-event-driven",
              "fix-protocol",
              "nse-bse",
              "fund-locking",
              "pessimistic-locking",
              "order-matching"
            ]
          }
        },
        {
          "id": "13",
          "title": "System Design 13: Design Social Media like Facebook | Instagram | Feeds Generation | HLD | LLD",
          "videoUrl": "https://youtu.be/MQOz1yZaC54",
          "description": "Are you preparing for system design interviews or curious how platforms like Facebook, Instagram, and Twitter scale their feed, posts, likes, comments, and followers?",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/gfiufuytf76rt764875ryuguyt676.png?updatedAt=1756069173910",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/jhvkjhvuiy79648754786f76r875.png?updatedAt=1756069190363",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 15,
            "lastUpdated": "2025-01-20",
            "hero": {
              "title": "Social Media Platform (Facebook/Instagram)",
              "oneLinerToMemorize": "Graph database for social connections \u2192 Fanout on write for feed generation \u2192 CDN for media delivery \u2192 Redis cache for hot data"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User should be able to register and login to the application with profile management",
                  "Feature 2: Create posts (text/image/video) with multimedia content up to 500MB",
                  "Feature 3: Follow other users to build social graph (send friend requests for bidirectional connections)",
                  "Feature 4: Like and comment on posts with nested comment threads",
                  "Feature 5: View personalized feed of posts from followed users in reverse chronological order",
                  "Feature 6: Search for users, posts, hashtags with real-time suggestions",
                  "Feature 7: Receive notifications for interactions (likes, comments, follows, mentions)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Daily Active Users",
                        "v": "500M DAU (2 billion total registered users)"
                      },
                      {
                        "k": "Posts Volume",
                        "v": "100M posts per day, 1.2K posts/second average"
                      },
                      {
                        "k": "Reads/Writes Ratio",
                        "v": "100:1 (heavy read system, users scroll feeds >> create posts)"
                      },
                      {
                        "k": "Media Storage",
                        "v": "Petabytes of images/videos, 10TB uploaded daily"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance",
                    "pairs": [
                      {
                        "k": "Latency",
                        "v": "Low latency: 500ms to load feed, <200ms for likes/comments"
                      },
                      {
                        "k": "Feed Generation",
                        "v": "Fanout on write for normal users (<5K followers), pull model for celebrities"
                      },
                      {
                        "k": "Media Upload",
                        "v": "Async processing, immediate confirmation with background transcoding"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> Consistency (eventual consistency acceptable)"
                      },
                      {
                        "k": "Data Durability",
                        "v": "S3 for media (11 9's), database replication for user data"
                      },
                      {
                        "k": "Availability Target",
                        "v": "99.99% uptime (52 minutes downtime/year)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entities",
                "type": "bullets",
                "items": [
                  "Entity 1: User - Profile with user_id, username, email, bio, profile_pic_url, created_at",
                  "Entity 2: Post - Content with post_id, user_id, content (text/media URLs), created_at, visibility",
                  "Entity 3: Followers - Bidirectional social graph (follower_id, followee_id, created_at)",
                  "Entity 4: Like/Comment - Engagement data with reaction types, timestamps, metadata",
                  "Entity 5: Feeds - Precomputed feed cache for users with feed_id, user_id, post_ids[], updated_at"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "User Onboarding",
                    "pairs": [
                      {
                        "k": "POST /api/v1/users/register",
                        "v": "Create new user account with email, password, username"
                      },
                      {
                        "k": "POST /api/v1/users/login",
                        "v": "Authenticate user and return JWT token"
                      },
                      {
                        "k": "GET /api/v1/users/{user_id}/profile",
                        "v": "Fetch user profile with follower/following counts, posts count"
                      },
                      {
                        "k": "PUT /api/v1/users/{user_id}/profile",
                        "v": "Update user profile (bio, profile pic, settings)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Post Operations",
                    "pairs": [
                      {
                        "k": "POST /api/v1/posts",
                        "v": "Create new post with {content, media_urls[], visibility, mentions[]}"
                      },
                      {
                        "k": "GET /api/v1/posts/{post_id}",
                        "v": "Retrieve single post with engagement metrics (likes, comments count)"
                      },
                      {
                        "k": "PUT /api/v1/posts/{post_id}",
                        "v": "Update post content (only author can edit)"
                      },
                      {
                        "k": "DELETE /api/v1/posts/{post_id}",
                        "v": "Soft delete post, mark as deleted but keep for analytics"
                      },
                      {
                        "k": "GET /api/v1/posts/feed?limit={limit}&offset={offset}",
                        "v": "Fetch personalized feed with pagination (infinite scroll)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Interactions",
                    "pairs": [
                      {
                        "k": "POST /api/v1/posts/{post_id}/like",
                        "v": "Like a post (idempotent, second call unlikes)"
                      },
                      {
                        "k": "DELETE /api/v1/posts/{post_id}/unlike",
                        "v": "Remove like from post"
                      },
                      {
                        "k": "GET /api/v1/posts/{post_id}/comments",
                        "v": "Fetch comments with pagination, supports nested threads"
                      },
                      {
                        "k": "POST /api/v1/comments/{comment_id}",
                        "v": "Add comment to post or reply to another comment"
                      },
                      {
                        "k": "POST /api/v1/users/{user_id}/follow",
                        "v": "Follow a user (async fanout to followers' feeds)"
                      },
                      {
                        "k": "DELETE /api/v1/{id}/unfollow",
                        "v": "Unfollow a user"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Users/Clients \u2192 API Gateway & Load Balancer: Authentication, routing, rate limiting (1K req/min per user)",
                  "User Service \u2192 User DB (PostgreSQL): Manages user profiles, credentials, settings with read replicas",
                  "Content Service \u2192 Post DB (Cassandra): Stores posts partitioned by user_id, optimized for writes",
                  "Content Service \u2192 S3: Stores media files (images/videos) with CDN for fast delivery",
                  "Feed Service \u2192 Feed Cache (Redis): Precomputed feeds for users, hot data in memory",
                  "Follower Service \u2192 Follower DB (Graph DB or PostgreSQL): Manages social graph with bidirectional edges",
                  "Engagement Service \u2192 Comment DB & Like DB: Stores interactions with denormalized counts",
                  "Feed Service \u2192 Kafka: Async fanout on write to populate followers' feeds",
                  "Notification Service: Triggered by engagement events, sends push/email notifications"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: User Registration & Login",
                    "steps": [
                      "Client sends: POST /api/v1/users/register with {username: 'john_doe', email: 'john@example.com', password: 'hashed_password'}",
                      "User Service validates: Username uniqueness (DB query), email format, password strength (8+ chars, special chars)",
                      "Service creates: User record in PostgreSQL with bcrypt hashed password (cost factor 12), generates user_id (UUID)",
                      "Service provisions: Empty feed in Redis, creates default privacy settings in Settings DB",
                      "Service returns: {user_id, username, token (JWT with 7-day expiry)} for session management",
                      "Login flow: Verify password hash match, generate JWT with {user_id, username, exp}, return token"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Post Creation",
                    "steps": [
                      "Client sends: POST /api/v1/posts with {content: 'Hello World!', media_files: [File], visibility: 'public', mentions: ['@jane_doe']}",
                      "Content Service validates: JWT token, content length (<5000 chars), media size (<500MB), file types (JPEG, PNG, MP4)",
                      "Service uploads: Media to S3 with key pattern {user_id}/{year}/{month}/{uuid}.{ext}, returns S3 URLs",
                      "Service creates: Post record in Cassandra with partition key=user_id, clustering key=created_at DESC for efficient user timeline queries",
                      "Service publishes: Event to Kafka topic 'post.created' with {post_id, user_id, created_at, media_urls}",
                      "Service returns: {post_id, status: 'published', media_urls[]} immediately (202 Accepted), fanout happens async"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Media Upload & Processing",
                    "steps": [
                      "Client uploads: Media file via multipart/form-data or presigned S3 URL for large files (>100MB)",
                      "Content Service generates: Presigned PUT URL for S3 upload (valid for 15 minutes), client uploads directly to S3",
                      "S3 upload triggers: Lambda function for async processing (image resizing, video transcoding)",
                      "Image processing: Generate thumbnails (150x150, 400x400, 1080x1080) using ImageMagick, store in S3 with CDN",
                      "Video processing: Transcode to multiple resolutions (360p, 720p, 1080p) using FFmpeg, HLS format for adaptive streaming",
                      "Post Consumer Service: Polls Kafka 'post.created', extracts metadata (dimensions, duration), updates PostDB with processed URLs"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Feed Generation (Fanout on Write)",
                    "steps": [
                      "Feed Service consumes: Kafka 'post.created' event with {post_id, user_id, created_at}",
                      "Service queries: Follower DB for all followers of user_id (e.g., 500 followers)",
                      "Service updates: Each follower's feed in Redis - LPUSH feed:{follower_id} {post_id}, LTRIM to keep latest 1000 posts",
                      "Redis structure: List data structure feed:{user_id} = [post_id_1, post_id_2, ..., post_id_1000] sorted by timestamp",
                      "Celebrity handling: If user has >5K followers, skip fanout on write, use pull model (fetch posts on demand during feed load)",
                      "Hybrid model: Small users (<5K) use push (fanout on write), celebrities use pull (fanout on read), medium users (1K-5K) use hybrid"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Feed Loading (Pull Model)",
                    "steps": [
                      "Client requests: GET /api/v1/posts/feed?limit=20&offset=0 with Authorization header",
                      "Feed Service checks: Redis feed:{user_id} for cached feed",
                      "Cache hit: LRANGE feed:{user_id} 0 19 returns list of post_ids [post_123, post_456, ...]",
                      "Service fetches: Post details from PostDB using post_ids (batch query with IN clause), includes {content, media_urls, created_at, user_id}",
                      "Service enriches: Fetch engagement counts (likes, comments) from Like DB and Comment DB, fetch author details from User DB",
                      "Cache miss: For celebrities or cold feeds, query FollowDB for followed users, fetch their recent posts from PostDB sorted by timestamp, cache result",
                      "Service returns: {posts: [{post_id, author: {user_id, username, profile_pic}, content, media_urls, likes_count, comments_count, created_at}], next_offset: 20}"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Like/Comment Interaction",
                    "steps": [
                      "Client sends: POST /api/v1/posts/{post_id}/like with user_id from JWT",
                      "Engagement Service checks: Redis cache like:{user_id}:{post_id} for idempotency (prevent double-like)",
                      "Service writes: Like record to Like DB (Cassandra) with composite key (post_id, user_id, created_at)",
                      "Service increments: Like counter in Redis - INCR likes_count:{post_id}, async sync to PostDB every 5 min (write batching)",
                      "Service publishes: Event to Kafka 'post.liked' topic for notification service",
                      "Comment flow: Similar but writes to Comment DB with support for nested threads using parent_comment_id field",
                      "Denormalization: PostDB stores like_count and comment_count for fast reads, updated via background job from Like/Comment DB"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Follow/Unfollow Operation",
                    "steps": [
                      "Client sends: POST /api/v1/users/{user_id}/follow with follower_id from JWT",
                      "Follower Service validates: Not already following, not self-follow, user exists",
                      "Service creates: Follower record in Follower DB with {follower_id, followee_id, created_at}",
                      "Service updates: Follower counts - INCR followers_count:{followee_id} and INCR following_count:{follower_id} in Redis",
                      "Service triggers: Backfill job to add followee's recent posts (last 100) to follower's feed in Redis",
                      "Unfollow: DELETE from Follower DB, decrement counts, purge followee's posts from follower's feed (LREM)"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Notification Generation",
                    "steps": [
                      "Notification Service consumes: Kafka topics 'post.liked', 'post.commented', 'user.followed'",
                      "Service checks: User preferences for notification types (likes: true, comments: true, follows: true)",
                      "Service aggregates: Multiple likes within 5 min \u2192 single notification 'John and 15 others liked your post'",
                      "Service publishes: To notification topic with {user_id, type, message, actor_ids[], post_id, created_at}",
                      "Delivery: Push notification via FCM/APNS, in-app notification stored in Notification DB (PostgreSQL)",
                      "Read receipts: Mark as read updates notification status, cleanup after 30 days"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Search Functionality",
                    "steps": [
                      "Client sends: GET /api/v1/search?q=john&type=users&limit=10",
                      "Search Service routes: To Elasticsearch cluster with query DSL",
                      "User search: Match query on username, name, bio fields with fuzzy matching (edit distance 2)",
                      "Post search: Full-text search on content field with hashtag extraction, time-based boosting (recent posts ranked higher)",
                      "Index updates: Post Consumer Service indexes new posts to Elasticsearch async via Kafka",
                      "Autocomplete: Prefix query on username field with suggest API for typeahead, cached in Redis for popular searches"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: CDN & Media Delivery",
                    "steps": [
                      "Media request: Client requests https://cdn.example.com/media/{user_id}/{uuid}.jpg",
                      "CDN (CloudFront) checks: Edge location cache for media, cache hit returns immediately (<50ms)",
                      "Cache miss: CDN fetches from S3 origin, caches with TTL=30 days for images, 7 days for videos",
                      "Optimization: Serve optimized images based on device (mobile gets 400x400, desktop gets 1080x1080) using CloudFront Lambda@Edge",
                      "Video streaming: HLS manifest (m3u8) with multiple bitrates, adaptive streaming based on network speed",
                      "Purge strategy: On post deletion, invalidate CDN cache via API call, S3 object marked for deletion (lifecycle policy)"
                    ]
                  }
                ]
              },
              {
                "id": "client-components",
                "title": "7. Client-Side Components",
                "type": "bullets",
                "items": [
                  "Component 1: Feed UI - Infinite scroll with virtual scrolling for performance, lazy load images",
                  "Component 2: Post Composer - Rich text editor with @mentions autocomplete, media upload with progress bar",
                  "Component 3: Interaction Buttons - Optimistic UI updates (like button animates immediately, rollback on failure)",
                  "Component 4: Media Player - Video player with adaptive bitrate streaming, thumbnail previews",
                  "Component 5: Notification Center - Real-time updates via WebSocket or SSE, unread badge count",
                  "Component 6: Search Bar - Debounced autocomplete (300ms delay), recent searches cached locally",
                  "Component 7: Profile Manager - Edit profile, privacy settings, account management"
                ]
              },
              {
                "id": "database-schema",
                "title": "8. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Users (PostgreSQL with read replicas)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "username",
                        "v": "varchar(50) UNIQUE, INDEXED"
                      },
                      {
                        "k": "email",
                        "v": "varchar(255) UNIQUE, INDEXED"
                      },
                      {
                        "k": "password_hash",
                        "v": "varchar(255) (bcrypt with cost 12)"
                      },
                      {
                        "k": "full_name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "bio",
                        "v": "text (max 500 chars)"
                      },
                      {
                        "k": "profile_pic_url",
                        "v": "varchar(500)"
                      },
                      {
                        "k": "followers_count",
                        "v": "bigint DEFAULT 0 (denormalized for fast reads)"
                      },
                      {
                        "k": "following_count",
                        "v": "bigint DEFAULT 0"
                      },
                      {
                        "k": "posts_count",
                        "v": "bigint DEFAULT 0"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Posts (Cassandra - optimized for writes)",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid (partition key - distributes posts by user)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp (clustering key DESC - sorts posts newest first)"
                      },
                      {
                        "k": "post_id",
                        "v": "uuid (clustering key - uniqueness within user partition)"
                      },
                      {
                        "k": "content",
                        "v": "text (post caption, max 5000 chars)"
                      },
                      {
                        "k": "media_urls",
                        "v": "list<text> (array of S3 URLs for images/videos)"
                      },
                      {
                        "k": "media_metadata",
                        "v": "map<text, text> (dimensions, duration, thumbnails)"
                      },
                      {
                        "k": "visibility",
                        "v": "text (public, friends, private)"
                      },
                      {
                        "k": "likes_count",
                        "v": "bigint (denormalized, updated by background job)"
                      },
                      {
                        "k": "comments_count",
                        "v": "bigint"
                      },
                      {
                        "k": "hashtags",
                        "v": "set<text> (extracted from content)"
                      },
                      {
                        "k": "mentions",
                        "v": "set<uuid> (user_ids mentioned in post)"
                      },
                      {
                        "k": "is_deleted",
                        "v": "boolean (soft delete flag)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "PostDB_indexed (Secondary index for global queries)",
                    "pairs": [
                      {
                        "k": "post_id",
                        "v": "uuid PRIMARY KEY (allows global post lookup)"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid INDEXED"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp INDEXED (for feed queries)"
                      },
                      {
                        "k": "content",
                        "v": "text"
                      },
                      {
                        "k": "media_urls",
                        "v": "jsonb"
                      },
                      {
                        "k": "likes_count",
                        "v": "bigint"
                      },
                      {
                        "k": "comments_count",
                        "v": "bigint"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Followers (PostgreSQL or Graph DB)",
                    "pairs": [
                      {
                        "k": "follower_id",
                        "v": "uuid (who is following)"
                      },
                      {
                        "k": "followee_id",
                        "v": "uuid (who is being followed)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "status",
                        "v": "enum (pending, accepted) for friend requests"
                      },
                      {
                        "k": "PRIMARY KEY",
                        "v": "(follower_id, followee_id)"
                      },
                      {
                        "k": "INDEX",
                        "v": "on followee_id for reverse lookup (find all followers)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Likes (Cassandra - high write volume)",
                    "pairs": [
                      {
                        "k": "post_id",
                        "v": "uuid (partition key)"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid (clustering key)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp (clustering key for ordering)"
                      },
                      {
                        "k": "reaction_type",
                        "v": "text (like, love, haha, wow, sad, angry)"
                      },
                      {
                        "k": "metadata",
                        "v": "map<text, text>"
                      },
                      {
                        "k": "PRIMARY KEY",
                        "v": "(post_id, user_id) - composite for uniqueness"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Comments (PostgreSQL with nested structure)",
                    "pairs": [
                      {
                        "k": "comment_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "post_id",
                        "v": "uuid INDEXED FK \u2192 Posts"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "parent_comment_id",
                        "v": "uuid NULL (NULL for top-level, UUID for replies)"
                      },
                      {
                        "k": "content",
                        "v": "text (max 2000 chars)"
                      },
                      {
                        "k": "likes_count",
                        "v": "bigint DEFAULT 0"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp INDEXED"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "is_deleted",
                        "v": "boolean"
                      }
                    ]
                  },
                  {
                    "groupTitle": "FeedDB (Redis - precomputed feeds)",
                    "pairs": [
                      {
                        "k": "feed:{user_id}",
                        "v": "LIST of post_ids (LPUSH new posts, LTRIM to 1000)"
                      },
                      {
                        "k": "likes_count:{post_id}",
                        "v": "STRING counter (INCR on like, DECR on unlike)"
                      },
                      {
                        "k": "comments_count:{post_id}",
                        "v": "STRING counter"
                      },
                      {
                        "k": "like:{user_id}:{post_id}",
                        "v": "STRING flag (exists = liked, for idempotency)"
                      },
                      {
                        "k": "followers_count:{user_id}",
                        "v": "STRING counter (synced to PostgreSQL hourly)"
                      },
                      {
                        "k": "following_count:{user_id}",
                        "v": "STRING counter"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch Index (posts_index)",
                    "pairs": [
                      {
                        "k": "post_id",
                        "v": "keyword (unique identifier)"
                      },
                      {
                        "k": "user_id",
                        "v": "keyword"
                      },
                      {
                        "k": "username",
                        "v": "text with autocomplete analyzer"
                      },
                      {
                        "k": "content",
                        "v": "text with standard analyzer (full-text search)"
                      },
                      {
                        "k": "hashtags",
                        "v": "keyword array (exact match, aggregations)"
                      },
                      {
                        "k": "created_at",
                        "v": "date (for sorting and range filters)"
                      },
                      {
                        "k": "likes_count",
                        "v": "integer (for popularity boosting)"
                      },
                      {
                        "k": "visibility",
                        "v": "keyword (filter private posts)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "additional-mechanism",
                "title": "9. Feed Ranking & Recommendation",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Feed Ranking Algorithm",
                    "steps": [
                      "Trigger: User loads feed, service fetches candidate posts from Redis or Pull model",
                      "Scoring: Each post scored based on: (1) Recency - exponential decay (posts <1 hour = 1.0, 1 day = 0.5, 7 days = 0.1), (2) Engagement - likes_count * 0.3 + comments_count * 0.5, (3) Author affinity - close friends (frequent interactions) weighted 2x",
                      "Ranking: Sort posts by composite score = recency_score * 0.4 + engagement_score * 0.4 + affinity_score * 0.2",
                      "Diversity: Inject content from less-followed users (10% of feed) to prevent echo chambers",
                      "ML Model: Advanced systems use gradient boosted trees (XGBoost) trained on click-through rate, watch time, shares to predict engagement probability"
                    ]
                  },
                  {
                    "blockTitle": "Fanout Models Comparison",
                    "steps": [
                      "Push Model (Fanout on Write): When user posts, immediately push to all followers' feeds. Pros: Fast read (feed pre-generated), Cons: Slow write (500 followers = 500 writes), storage intensive (1000 posts * 500 followers = 500K feed entries)",
                      "Pull Model (Fanout on Read): When user loads feed, fetch posts from followed users on-demand. Pros: Fast write (single post write), no storage overhead, Cons: Slow read (query 100 followed users' posts, sort, merge)",
                      "Hybrid Model: Push for users with <5K followers, Pull for celebrities (>5K), Mixed for medium (1K-5K). Example: Taylor Swift (500M followers) uses pull, normal user uses push, verified accounts (100K followers) use hybrid",
                      "Implementation: Celebrity flag in User table, Feed Service checks before fanout, uses pull model for celebrity posts"
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "10. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Database Sharding - Posts sharded by user_id (1000 shards), Followers sharded by follower_id, enables horizontal scaling",
                  "Technique 2: Read Replicas - 5 PostgreSQL replicas for User reads (profile views), writes go to primary only, 99% queries hit replicas",
                  "Technique 3: Redis Caching - Feed cache (TTL=10 min), user profile cache (1 hour), like counts (5 min), reduces DB load by 90%",
                  "Technique 4: CDN for Media - CloudFront caches images/videos at edge locations, 95% cache hit rate, reduces S3 reads by 95%",
                  "Technique 5: Lazy Loading - Infinite scroll loads 20 posts at a time, images lazy loaded when in viewport, reduces initial page load from 5s to 500ms",
                  "Technique 6: Kafka for Async - Post creation, feed fanout, notifications all async via Kafka, decouples write from propagation",
                  "Technique 7: Connection Pooling - API servers maintain 100 DB connections each (50 servers = 5K total), reduces connection overhead from 50ms to 1ms",
                  "Technique 8: Denormalization - Like counts, follower counts stored in Post/User tables, avoids expensive COUNT queries, updated by background jobs",
                  "Technique 9: Batch Processing - Like count updates batched every 5 min (1000 likes = 1 DB write instead of 1000), reduces write load by 100x",
                  "Technique 10: Elasticsearch for Search - Async indexing via Kafka, handles 10K searches/sec, autocomplete with <50ms latency",
                  "Technique 11: Rate Limiting - 1K requests/min per user prevents abuse, 100 posts/day limit prevents spam",
                  "Technique 12: WebSocket for Real-time - Notifications delivered via WebSocket persistent connections, 1M concurrent connections per server"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "Why use fanout on write instead of fanout on read for normal users?",
                    "a": "Read-heavy optimization: Social media has 100:1 read/write ratio. Users read feeds 100x more than posting. Fanout on write: (1) Fast reads - feed pre-generated in Redis, LRANGE returns 20 posts in <10ms, (2) Predictable latency - post creation takes 200ms to fanout to 500 followers, but every feed load is <50ms, (3) Better user experience - users don't wait for feed generation, instant scroll. Trade-off: Write amplification (1 post = 500 writes) vs slow reads (query 100 users' posts, sort, merge = 500ms). For celebrities with 10M followers, fanout on write is impractical (1 post = 10M writes, takes minutes), so we use pull model where fans query celebrity's posts on-demand."
                  },
                  {
                    "q": "How do you handle the celebrity/hotspot problem in feed generation?",
                    "a": "Multi-tier approach: (1) User classification - Flagging users as celebrity (>5K followers) in User table, (2) Hybrid fanout - Push model for <5K followers (precompute feeds), Pull model for >5K (fetch on-demand), (3) Feed merging - During feed load, combine pushed posts (from normal users) + pulled posts (from celebrities) and sort by timestamp, (4) Caching - Celebrity posts cached separately in Redis with higher TTL (1 hour vs 10 min), all followers share same cache, (5) Pagination - Celebrity timelines paginated, followers see latest 100 posts max. Example: User follows 90 normal users (pushed feeds) + 10 celebrities (pulled). Feed Service: LRANGE feed:{user_id} for pushed, fetch celebrity posts from PostDB, merge and rank. Performance: Combined latency 100ms (50ms pushed + 50ms pulled) vs pure pull would be 500ms."
                  },
                  {
                    "q": "How do you ensure consistency between like counts in cache vs database?",
                    "a": "Eventual consistency with reconciliation: (1) Write path - user likes post \u2192 write to Like DB (Cassandra), increment Redis counter INCR likes_count:{post_id}, (2) Background sync - cron job runs every 5 min, queries Like DB for actual count (SELECT COUNT(*) GROUP BY post_id), updates PostDB with authoritative count, (3) Cache invalidation - if Redis count differs from DB by >10%, invalidate cache and reload from DB, (4) Conflict resolution - DB is source of truth, Redis is best-effort cache. Edge cases: Redis evicts key due to memory pressure \u2192 next read fetches from DB, subsequent reads cache again. Redis crashes \u2192 all counters lost \u2192 background job rebuilds from DB within 5 min. Accept window: Counters may be off by \u00b15% for <5 min, acceptable for social media (not financial system). Alternative: Use Redis persistence (AOF + RDB) for durability, but adds write latency 10ms \u2192 50ms."
                  },
                  {
                    "q": "What happens if a user with 1M followers posts? How do you prevent system overload?",
                    "a": "Rate limiting + async processing: (1) Classification - user_id tagged as 'celebrity' if followers_count > 5K, stored in User table, (2) Bypass fanout - celebrity posts skip push fanout entirely, stored only in PostDB, (3) Pull model - when follower loads feed, query celebrity's recent posts (last 100) from PostDB, merge with normal pushed posts, (4) Async notification - instead of 1M push notifications, send to notification aggregator, batch into 'trending' notifications, (5) Rate limiting - limit celebrity to 10 posts/hour to prevent spam. Implementation: Feed Service checks if poster.followers_count > 5K, if yes: write to PostDB only, publish 'celebrity.posted' event to Kafka, skip fanout worker. Followers see post when they load feed (pull), not pushed to their feeds. Notification Service sends to 1000 most engaged followers immediately, rest get aggregated daily digest. Prevents: 1M database writes, 1M Redis writes, 1M push notifications from overwhelming system."
                  },
                  {
                    "q": "How do you handle nested comment threads efficiently?",
                    "a": "Hierarchical storage with lazy loading: (1) Database schema - Comment table has parent_comment_id (NULL for top-level, UUID for replies), creates tree structure, (2) Loading strategy - fetch top-level comments first (WHERE parent_comment_id IS NULL LIMIT 20), load replies only when user expands thread (WHERE parent_comment_id = {comment_id}), (3) Depth limit - max 3 levels (post \u2192 comment \u2192 reply \u2192 no further nesting) to prevent UI complexity, (4) Denormalization - each comment stores reply_count for 'Show 5 replies' link without counting, (5) Sorting - top-level sorted by likes_count DESC (best first), replies sorted by created_at ASC (chronological). Query optimization: Composite index on (post_id, parent_comment_id, created_at) enables fast queries. Alternative: Nested set model or closure table for complex hierarchies, but adds write complexity. Example: Post has 10K comments \u2192 fetch top 20, user expands comment #5 \u2192 fetch replies WHERE parent_comment_id = comment_5_id LIMIT 10, lazy load 'Show more' pagination."
                  },
                  {
                    "q": "How do you implement real-time notifications without polling?",
                    "a": "WebSocket + pub/sub pattern: (1) Connection - client opens WebSocket to Notification Service on app launch, authenticated via JWT, (2) Subscription - server subscribes to Redis pub/sub channel notifications:{user_id}, (3) Event flow - when user A likes user B's post, Engagement Service publishes to Redis channel 'notifications:{user_B_id}', (4) Push - Notification Service listening on that channel receives event, sends JSON over WebSocket to user B's client, (5) Fallback - if client offline, store notification in Notification DB, deliver when client reconnects. Scalability: 1M concurrent WebSocket connections = 1000 servers (1K connections each), Redis pub/sub handles 100K messages/sec, load balanced using consistent hashing on user_id. Alternative: Server-Sent Events (SSE) for one-way streaming (simpler), or long polling (fallback for old browsers). Battery optimization: Mobile apps use FCM/APNS push notifications instead of persistent WebSocket, WebSocket only for active users in-app."
                  },
                  {
                    "q": "How do you handle media upload failures and retries?",
                    "a": "Multi-stage upload with resumability: (1) Presigned URL - client requests POST /api/v1/posts/upload/init, server generates presigned S3 PUT URL (valid 15 min), returns {upload_id, presigned_url}, (2) Chunked upload - for large files (>100MB), client uses multipart upload, splits into 5MB chunks, uploads in parallel (10 concurrent), (3) Progress tracking - each chunk upload stores progress in Redis upload:{upload_id} = {chunks_uploaded: [1,2,3], total_chunks: 20}, (4) Retry - on failure, client queries GET /api/v1/posts/upload/{upload_id}/status, server returns missing chunks, client uploads only missing parts, (5) Finalize - after all chunks uploaded, client calls POST /api/v1/posts/upload/{upload_id}/complete, server assembles chunks in S3, creates post. Timeouts: Presigned URL expires in 15 min, upload_id expires in 24 hours (Redis TTL). Network resilience: Exponential backoff (1s, 2s, 4s), circuit breaker after 3 consecutive failures. Alternative: Direct S3 multipart upload using AWS SDK, but loses control over progress tracking."
                  },
                  {
                    "q": "What's your database partitioning strategy for Posts at scale?",
                    "a": "Multi-dimensional sharding: (1) Primary partitioning - Cassandra partitions by user_id (partition key), distributes user's posts across cluster based on hash(user_id), each node handles 1000 users, (2) Clustering - within partition, sort by (created_at DESC, post_id) for efficient time-range queries, (3) Global access - separate PostDB_indexed table with post_id as primary key for direct post lookup (GET /posts/{post_id}), (4) Replication - RF=3 for high availability, QUORUM consistency for reads/writes. Query patterns: User timeline (SELECT * FROM posts WHERE user_id = {id} LIMIT 20) hits single partition = fast. Feed generation (SELECT * FROM posts WHERE user_id IN (100 followees)) fans out to 100 partitions = slow, mitigated by fanout on write. Hotspots: Celebrity users have high partition load, mitigated by pull model (followers query celebrity partition, not written to follower feeds). Scaling: Add nodes increases partition count from 1000 to 2000, Cassandra rebalances automatically using consistent hashing."
                  },
                  {
                    "q": "How do you prevent duplicate likes when user clicks multiple times?",
                    "a": "Multi-layer idempotency: (1) Client-side - debounce like button 300ms, disable button immediately on click (optimistic UI), (2) API level - check Redis cache like:{user_id}:{post_id}, if exists return 200 OK (already liked), (3) Database level - Cassandra composite primary key (post_id, user_id) enforces uniqueness, duplicate insert fails silently, (4) Transaction - atomic check-and-set in Redis: if SETNX like:{user_id}:{post_id} success \u2192 write to DB + INCR counter, if fails \u2192 already liked, (5) Race condition - if 2 parallel requests both pass Redis check (race), Cassandra constraint prevents duplicate, one request succeeds, other gets 409 Conflict. Implementation: POST /api/v1/posts/{post_id}/like checks Redis, if not exists: start transaction, write to Like DB, set Redis key (TTL=24h), increment counter, commit. Counters: Use INCR for increment, DECR for unlike, background job syncs actual count from DB every 5 min to fix any drift. Edge case: Redis eviction loses idempotency cache \u2192 user can re-like \u2192 DB constraint prevents duplicate row, counter incremented correctly."
                  },
                  {
                    "q": "How do you implement privacy controls (public vs friends-only posts)?",
                    "a": "Multi-layer visibility filtering: (1) Storage - Post table has visibility enum (public, friends, private), (2) Feed generation - during fanout on write, check relationship between poster and follower, only push to feeds if: visibility=public OR (visibility=friends AND is_mutual_follower), (3) Direct access - GET /posts/{post_id} checks: if public \u2192 allow, if friends \u2192 verify requester.user_id in poster's friends list, if private \u2192 only author can view, (4) Feed filtering - pull model queries: SELECT * FROM posts WHERE user_id IN (followees) AND (visibility = 'public' OR (visibility = 'friends' AND mutual = true)), (5) Caching - privacy checks cached in Redis friend:{user_A}:{user_B} = boolean (TTL=1 hour). Friend determination: Mutual follow check - query Followers table for bidirectional edge (A follows B AND B follows A). Performance: Privacy checks add 10ms latency, mitigated by batch checking (verify 100 posts in single Redis MGET). Alternative: Facebook-style fine-grained lists (close friends, acquaintances, custom lists), stored as relationship tags in graph database."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "12. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Volume",
                    "pairs": [
                      {
                        "k": "Daily Active Users",
                        "v": "500M DAU (2 billion total registered users)"
                      },
                      {
                        "k": "Posts Created",
                        "v": "100M posts/day = 1.2K posts/second average"
                      },
                      {
                        "k": "Feed Loads",
                        "v": "10 billion feed requests/day = 115K req/sec"
                      },
                      {
                        "k": "Media Storage",
                        "v": "Petabytes total, 10TB uploaded daily"
                      },
                      {
                        "k": "Read/Write Ratio",
                        "v": "100:1 (heavy read system)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance Metrics",
                    "pairs": [
                      {
                        "k": "Feed Load Latency",
                        "v": "p95 <500ms (includes 20 posts with media thumbnails)"
                      },
                      {
                        "k": "Like/Comment Latency",
                        "v": "p95 <200ms (Redis write + DB async)"
                      },
                      {
                        "k": "Post Creation",
                        "v": "p95 <300ms (write to DB, publish to Kafka, fanout async)"
                      },
                      {
                        "k": "Media Upload",
                        "v": "100MB video in ~30s (S3 direct upload)"
                      },
                      {
                        "k": "Search Latency",
                        "v": "p95 <100ms (Elasticsearch autocomplete)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching & Storage",
                    "pairs": [
                      {
                        "k": "Feed Cache",
                        "v": "Redis stores latest 1000 posts per user, TTL=10 min"
                      },
                      {
                        "k": "Profile Cache",
                        "v": "TTL=1 hour, reduces DB reads by 90%"
                      },
                      {
                        "k": "Like Count Cache",
                        "v": "TTL=5 min, synced to DB every 5 min"
                      },
                      {
                        "k": "CDN Cache Hit",
                        "v": "95% for images/videos, TTL=30 days"
                      },
                      {
                        "k": "DB Replication",
                        "v": "5 read replicas for PostgreSQL, 99% reads from replicas"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Fanout & Feed",
                    "pairs": [
                      {
                        "k": "Normal User Fanout",
                        "v": "<5K followers = push model (fanout on write)"
                      },
                      {
                        "k": "Celebrity Fanout",
                        "v": ">5K followers = pull model (fanout on read)"
                      },
                      {
                        "k": "Fanout Latency",
                        "v": "500 followers in 200ms (100 writes/sec per worker)"
                      },
                      {
                        "k": "Feed Size",
                        "v": "1000 posts cached per user = 1KB per post = 1MB/user"
                      },
                      {
                        "k": "Feed Refresh",
                        "v": "Pull latest 20 posts every scroll"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Database Partitioning",
                    "pairs": [
                      {
                        "k": "Posts Sharding",
                        "v": "1000 shards by user_id, 1M users per shard"
                      },
                      {
                        "k": "Cassandra Writes",
                        "v": "10K writes/sec per node, 100 nodes = 1M writes/sec"
                      },
                      {
                        "k": "PostgreSQL",
                        "v": "5 read replicas, 1 primary, handle 50K reads/sec"
                      },
                      {
                        "k": "Replication Factor",
                        "v": "RF=3 for Cassandra, tolerates 1 node failure"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Rate Limits & Quotas",
                    "pairs": [
                      {
                        "k": "API Rate Limit",
                        "v": "1000 requests/min per user"
                      },
                      {
                        "k": "Post Creation Limit",
                        "v": "100 posts/day per user (prevents spam)"
                      },
                      {
                        "k": "Media Upload Size",
                        "v": "Max 500MB per file, 10 files per post"
                      },
                      {
                        "k": "Follow Limit",
                        "v": "1000 follows/day (anti-spam)"
                      },
                      {
                        "k": "Comment Length",
                        "v": "Max 2000 characters"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Example Calculation - Feed Load",
                    "pairs": [
                      {
                        "k": "Step 1: Redis Feed Query",
                        "v": "LRANGE feed:{user_id} 0 19 = 10ms (20 post_ids)"
                      },
                      {
                        "k": "Step 2: Batch Post Fetch",
                        "v": "SELECT * WHERE post_id IN (...) = 50ms (20 posts from PostDB)"
                      },
                      {
                        "k": "Step 3: User Info Fetch",
                        "v": "SELECT * WHERE user_id IN (...) = 30ms (20 authors from cache)"
                      },
                      {
                        "k": "Step 4: Engagement Counts",
                        "v": "MGET likes:{post_id} x20 = 20ms (Redis batch)"
                      },
                      {
                        "k": "Total Feed Load",
                        "v": "110ms (well under 500ms p95 target)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cost Optimization",
                    "pairs": [
                      {
                        "k": "CDN Savings",
                        "v": "95% cache hit = 95% fewer S3 reads = $10K/month saved"
                      },
                      {
                        "k": "Redis Savings",
                        "v": "90% fewer DB queries = 10x fewer DB instances needed"
                      },
                      {
                        "k": "Denormalization",
                        "v": "Storing counts avoids COUNT(*) queries (100ms \u2192 1ms)"
                      },
                      {
                        "k": "Batch Updates",
                        "v": "Like counts batched = 100x fewer DB writes"
                      },
                      {
                        "k": "S3 Lifecycle",
                        "v": "Move old media to Glacier after 1 year = 90% storage cost reduction"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "NEVER use fanout on write for celebrities with millions of followers. 1 post = 10M writes would take minutes and overwhelm the system. Always use pull model (fanout on read) for users with >5K followers."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'Why use Redis for feed instead of database?'. Answer: (1) Speed - LRANGE from Redis <10ms vs DB query 100ms+, (2) Scalability - Redis handles 100K ops/sec, (3) Feed is ephemeral data, loss is acceptable (rebuild from DB), (4) Reduces DB load by 90%."
              },
              {
                "type": "tip",
                "text": "Key optimization: Denormalization. Store like_count, comment_count, follower_count IN the Post/User tables. COUNT(*) queries are expensive (100ms+), denormalized counts are instant (1ms). Accept eventual consistency - counts updated by background jobs every 5 min."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: 100:1 read/write ratio for social media. Users scroll feeds 100x more than posting. Optimize for READS (caching, CDN, read replicas) over writes. This is why fanout on write works - slow write (once) enables fast reads (100x)."
              },
              {
                "type": "warning",
                "text": "NEVER store media in database as BLOBs. Use S3 for object storage, store only URLs in database. BLOB storage: slow queries, expensive replication, no CDN support. S3: cheap ($0.023/GB/month), durable (11 9's), CDN-friendly."
              },
              {
                "type": "tip",
                "text": "Hybrid fanout model is critical at scale. Push model (<5K followers) enables instant feeds. Pull model (>5K) prevents write amplification. Medium users (1K-5K) use hybrid: push to active followers, pull for inactive. This handles 99% of users efficiently."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love asking: 'How to handle hot partition in Cassandra?'. Answer: Celebrity users create hotspots (1M followers all query same partition). Solution: (1) Pull model - distribute load across follower reads, not write-heavy fanout, (2) Cache celebrity posts in Redis, (3) Add read replicas for hot partitions."
              },
              {
                "type": "warning",
                "text": "NEVER synchronously process media during upload. Video transcoding takes 30s-5min. Return 202 Accepted immediately, process async via Lambda/queue. User sees 'Processing...' status, gets notification when complete. Synchronous = API timeout + poor UX."
              },
              {
                "type": "tip",
                "text": "Idempotency is critical for likes. User clicks like button 3 times due to slow network \u2192 only 1 like recorded. Use Redis SETNX + Cassandra composite key (post_id, user_id). Both layers prevent duplicates even under race conditions."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Why eventual consistency is acceptable for social media. Like counts off by 10 for 5 min? Not critical. Feed shows post 2 seconds late? Fine. Unlike banking (need strong consistency), social media prioritizes availability + low latency over perfect consistency (AP in CAP theorem)."
              }
            ],
            "tags": [
              "system-design",
              "social-media",
              "Facebook",
              "Instagram",
              "feed-generation",
              "fanout-on-write",
              "fanout-on-read",
              "Cassandra",
              "PostgreSQL",
              "Redis",
              "Kafka",
              "S3",
              "CDN",
              "graph-database",
              "eventual-consistency",
              "denormalization",
              "read-heavy",
              "celebrity-problem",
              "media-upload",
              "real-time-notifications"
            ]
          }
        },
        {
          "id": "14",
          "title": "System Design 14: Design Top K Leaderboard | Ranking System | Trending Songs | Movie | HLD | LLD",
          "videoUrl": "https://youtu.be/KOvDWEGVXec",
          "description": "In this video, we dive deep into System Design of a Leaderboard / Top-K / Trending List, exploring how to track scores, ranks, and trending topics across games, videos, or songs in real time.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/qwengoneogin24ino2n4gn2ogn24og.png?updatedAt=1757972489522",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/34otn3jn4yj35n%20yn5lyn23l5yn3.jpeg?updatedAt=1757973017991",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 18,
            "lastUpdated": "2026-01-25",
            "hero": {
              "title": "Leaderboard / Top-K / Trending List",
              "oneLinerToMemorize": "Score events \u2192 Kafka \u2192 Redis ZSET (real-time top-K) + DB (persistence) \u2192 Periodic batch jobs (regional/time-based rankings) \u2192 Hybrid: Redis for reads, DB for historical consistency"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User should be able to insert/update/delete data into our list (video/song/leaderboard/E-dynamic list)",
                  "Feature 2: User should be able to query: Top 10 player by score/rank (in case of leaderboard) based on region/group",
                  "Feature 3: Time periods should be limited by: day, week, month (leaderboard for different time windows)",
                  "Feature 4: User should be able to see all-time rankings in addition to time-bounded rankings",
                  "Feature 5: User should get real-time updates (no stale leaderboard data - <1 second latency for top-K)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Events",
                        "v": "1M req/sec - billions of songs/videos/players globally"
                      },
                      {
                        "k": "Users",
                        "v": "Hundreds of millions querying leaderboards, trending lists simultaneously"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance & Consistency",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> Consistency (eventual consistency acceptable for leaderboards)"
                      },
                      {
                        "k": "Latency",
                        "v": "100ms to get the top-K result, near real-time updates with <1 second lag (to give a soft-real time update)"
                      },
                      {
                        "k": "Accuracy",
                        "v": "Return accurate trending list/rank probabilistic (minor ranking errors tolerable, e.g., rank 100 vs 101 acceptable)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity (from image)",
                "type": "bullets",
                "items": [
                  "Entity 1: Score/View/Like - Metric being tracked (game score, video views, song plays, post likes)",
                  "Entity 2: Player/Video/Song - Entity being ranked (user in game, video on platform, song on music app)",
                  "Entity 3: TimeFrame (hour/day/week/month) - Time window for rankings (daily leaderboard, weekly trending, monthly top songs)"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing (from image)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Score Updates",
                    "pairs": [
                      {
                        "k": "POST /api/v1/scores/view",
                        "v": "Record a view/play event for video/song (increments score)"
                      },
                      {
                        "k": "GET /api/v1/leaderboards/{leaderboard_id}/top?window=daily&region=US&limit=10 (Pagination)",
                        "v": "Get top-K entries for specific leaderboard with filters (time window, region, limit)"
                      },
                      {
                        "k": "GET /leaderboards/{leaderboard_id}/rank/{user_id}?window=monthly&> (Pagination)",
                        "v": "Get user's rank in leaderboard for specific time window"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design (from image)",
                "type": "bullets",
                "items": [
                  "Clients/users \u2192 API Gateway & Load Balancer: Authentication, authorization, routing, rate limiting",
                  "Score Service: Handles score updates (views, likes, game scores), validates and publishes events",
                  "Ranking Service: Computes top-K rankings, serves read queries, manages Redis ZSET for real-time rankings",
                  "Score DB: Persistent storage for all score events (historical data, source of truth)",
                  "Kafka: Event streaming for score updates, decouples write path from ranking computation",
                  "From image shows three approaches: (1) Redis Sorted Set/ZSET, (2) TimeSeriesDB (InfluxDB), (3) Hybrid Approach"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level - from image)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Score Event Ingestion",
                    "steps": [
                      "User watches video: Client sends: POST /api/v1/scores/view with {video_id: 'V123', user_id: 'U456', timestamp: now(), metadata: {region: 'US', category: 'music'}}",
                      "Score Service validates: (1) Check video exists, (2) Check user authenticated, (3) Dedup: Check if same user viewed in last 5 min (prevent double-counting spam), Redis: GET view_dedup:{user_id}:{video_id}, if exists \u2192 ignore (already counted), else SET view_dedup:{user_id}:{video_id} 1 EX 300 (5 min TTL)",
                      "Publish to Kafka: 'score.updated' topic: {event_id: uuid(), video_id: 'V123', score_delta: +1, timestamp, region: 'US', category: 'music'}",
                      "Response: 200 OK (async processing, don't wait for leaderboard update)",
                      "Kafka partitioning: Partition by video_id hash (ensures all events for same video go to same partition \u2192 ordering preserved)"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Approach 1 - Redis Sorted Set (ZSET) [from image shows 'Redis Sorted Set/ZSET']",
                    "steps": [
                      "From image: '1. Redis Sorted Set (sorted_set), One Sorted_Set (sorted = < id, score> pairs, sorted), leaderboard:music:IN:alltime, leaderboard:music:IN:90days'",
                      "Ranking Service consumes Kafka: 'score.updated' event: {video_id: 'V123', score_delta: +1, region: 'US', timestamp}",
                      "Update Redis ZSET: (1) All-time leaderboard: ZINCRBY leaderboard:global:alltime 1 V123 (increments V123's score by 1), (2) Daily leaderboard: ZINCRBY leaderboard:global:daily:{YYYY-MM-DD} 1 V123, (3) Regional leaderboard: ZINCRBY leaderboard:US:alltime 1 V123, (4) Category leaderboard: ZINCRBY leaderboard:music:alltime 1 V123",
                      "From image note: 'One redis Set (sorted = < id, more> pairs, sorted)' - ZSET stores (member, score) pairs sorted by score",
                      "Query top-K: User requests: GET /api/v1/leaderboards/global/top?limit=10, Ranking Service: ZREVRANGE leaderboard:global:alltime 0 9 WITHSCORES (get top 10 with scores, reverse order = highest first), Response: [{video_id: 'V123', score: 1000000}, {video_id: 'V456', score: 950000}, ...], Time complexity: O(log(N) + K) where N = total items, K = limit (very fast, ~1ms for 10M items)",
                      "Get user's rank: GET /api/v1/leaderboards/global/rank/V123, Ranking Service: ZREVRANK leaderboard:global:alltime V123 (returns 0-based index, e.g., 42 \u2192 rank #43), Response: {video_id: 'V123', rank: 43, score: 500000}",
                      "From image: 'Pro: Every rank and data', 'Con: Complete expensive (no need to keep in single node + not possible for 'ideal depth')'",
                      "Memory: (1) Each entry: ~100 bytes (video_id + score + ZSET overhead), (2) 10M entries: ~1 GB per leaderboard, (3) Multiple leaderboards (global, regional, daily, weekly, monthly): 5 leaderboards \u00d7 1 GB = 5 GB (manageable in Redis)"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Time-Windowed Leaderboards (Daily/Weekly/Monthly)",
                    "steps": [
                      "From image: 'Time periods should be limited by: day, week, month (leaderboard for different time windows)'",
                      "Daily leaderboard: (1) Key format: leaderboard:global:daily:{YYYY-MM-DD}, example: leaderboard:global:daily:2026-01-25, (2) TTL: EXPIRE leaderboard:global:daily:2026-01-25 604800 (7 days = 1 week TTL, auto-delete old daily leaderboards), (3) Update: ZINCRBY leaderboard:global:daily:2026-01-25 1 V123 on every view",
                      "Weekly leaderboard: (1) Key format: leaderboard:global:weekly:{YYYY}-W{WW}, example: leaderboard:global:weekly:2026-W04 (week 4 of 2026), (2) TTL: 90 days (keep last ~13 weeks), (3) Update: Same ZINCRBY on weekly key",
                      "Monthly leaderboard: (1) Key format: leaderboard:global:monthly:{YYYY-MM}, example: leaderboard:global:monthly:2026-01, (2) TTL: 365 days (keep last 12 months), (3) Update: ZINCRBY on monthly key",
                      "All-time leaderboard: (1) Key: leaderboard:global:alltime, (2) No TTL (never expires), (3) Grows indefinitely (pagination essential)",
                      "Query with time window: User requests: GET /api/v1/leaderboards/global/top?window=daily&date=2026-01-25&limit=10, Ranking Service: today = '2026-01-25', ZREVRANGE leaderboard:global:daily:2026-01-25 0 9 WITHSCORES, returns today's top 10",
                      "Batch job for weekly aggregation (from image shows 'Periodic batch jobs'): (1) Runs every Monday at 12 AM, (2) Aggregates last 7 days: FOR date IN (last 7 days): ZUNIONSTORE leaderboard:global:weekly:{YYYY-W{WW}} 7 leaderboard:global:daily:{date} WEIGHTS 1 (union all daily ZSETs into weekly ZSET), (3) Result: Weekly ZSET contains sum of daily scores"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Regional & Category Leaderboards",
                    "steps": [
                      "From image: 'leaderboard:music:IN:alltime' - region and category specific",
                      "Regional: (1) Event includes region: {video_id, score_delta, region: 'US'}, (2) Update: ZINCRBY leaderboard:US:alltime 1 V123, (3) Query: ZREVRANGE leaderboard:US:alltime 0 9 (top 10 in US)",
                      "Category: (1) Event includes category: {video_id, category: 'music'}, (2) Update: ZINCRBY leaderboard:music:alltime 1 V123, (3) Query: ZREVRANGE leaderboard:music:alltime 0 9 (top 10 music videos)",
                      "Combined filters: (1) Regional + Category: leaderboard:music:US:alltime (top music videos in US), (2) Regional + Category + Time: leaderboard:music:US:daily:2026-01-25 (top music videos in US today)",
                      "From image note: 'littered by region' - leaderboards sharded by region for isolation",
                      "Key explosion problem: (1) Dimensions: 5 regions \u00d7 10 categories \u00d7 4 time windows (daily, weekly, monthly, alltime) = 200 leaderboards, (2) Each leaderboard: 1 GB (10M entries), (3) Total: 200 GB (Redis Cluster can handle, but expensive)",
                      "Optimization: (1) Only create leaderboard if needed (lazy initialization), (2) Expire unused leaderboards (if no updates in 30 days \u2192 delete), (3) Use separate Redis cluster per region (isolate load)"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Approach 2 - TimeSeriesDB (InfluxDB) [from image shows 'TimeseriesDB(InfluxDB)']",
                    "steps": [
                      "From image: 'TimeseriesDB(InfluxDB)' with Flink for stream processing",
                      "Use case: When you need historical trend analysis, not just current top-K (e.g., 'How did video V123 rank over the past 30 days?')",
                      "Score event ingestion: Kafka 'score.updated' \u2192 Flink stream processor \u2192 InfluxDB",
                      "Flink processing: (1) Window aggregation: Group events by (video_id, 1-minute window), count views in window, (2) Output: {video_id: 'V123', views_in_minute: 1000, timestamp: '2026-01-25T10:30:00Z'}, (3) Write to InfluxDB",
                      "InfluxDB schema: Measurement: video_scores, Tags: video_id, region, category (indexed for fast filtering), Fields: views_count, likes_count, score (numeric values), Timestamp: time (nanosecond precision)",
                      "Write: INSERT video_scores,video_id=V123,region=US views_count=1000,score=1500 1674382200000000000",
                      "Query top-K: (1) Aggregate last 24 hours: SELECT SUM(views_count) as total_views FROM video_scores WHERE time > now() - 24h GROUP BY video_id ORDER BY total_views DESC LIMIT 10, (2) Time complexity: Slower than Redis (~100ms vs 1ms), but handles complex time-based queries",
                      "From image: 'BitMap: Use Map, (littered by region), Bucket: Bitmap per K. (littered by in some cold storage), All time leaderboard in some (H or K by bits in 1BM window). only aim per tie info' - uses bitmap for space efficiency",
                      "Downsampling: (1) Keep 1-minute data for 7 days, (2) Aggregate to 1-hour data (keep for 90 days), (3) Aggregate to 1-day data (keep forever), (4) Continuous queries auto-downsample: CREATE CONTINUOUS QUERY cq_hourly ON leaderboard BEGIN SELECT SUM(views_count) INTO video_scores_hourly FROM video_scores GROUP BY time(1h), video_id END",
                      "Pros: Historical analysis (trend over time), Efficient storage (compressed time-series), Complex queries (percentiles, moving averages)",
                      "Cons: Slower queries than Redis (100ms vs 1ms), Not suitable for real-time leaderboards (<1 sec latency requirement), More complex setup (Flink + InfluxDB vs just Redis)"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Approach 3 - Hybrid (Redis + DB) [BEST PRACTICE from image]",
                    "steps": [
                      "From image: '3. Hybrid Approach - All write requests are queued in Kafka. Consumers update Redis ZSET in real-time. Write scores to durable DB (Spanner, Postgres, Cassandra). Redis serves reads, DB serves as fallback and historical source. Periodic jobs snapshot Redis \u2192 DB or vice versa for consistency.'",
                      "Architecture: Kafka (events) \u2192 Redis Consumer (real-time) + DB Consumer (persistence) \u2192 Redis (reads, hot data) + DB (writes, cold storage, source of truth)",
                      "Write path: (1) Score event \u2192 Kafka 'score.updated', (2) Redis Consumer: Consumes from Kafka, ZINCRBY leaderboard:global:alltime 1 V123 (real-time update, <100ms lag), (3) DB Consumer: Consumes from Kafka, INSERT INTO scores (video_id, score_delta, timestamp, region) VALUES ('V123', 1, now(), 'US') (durable write, eventual consistency)",
                      "Read path: (1) User queries: GET /api/v1/leaderboards/global/top?limit=10, (2) Ranking Service: Try Redis: ZREVRANGE leaderboard:global:alltime 0 9 WITHSCORES, if Redis miss (rare, maybe Redis restarted) \u2192 fallback to DB: SELECT video_id, SUM(score_delta) as total_score FROM scores WHERE leaderboard_id='global' GROUP BY video_id ORDER BY total_score DESC LIMIT 10, (3) Response: Top-10 list",
                      "From image: 'Pro: Redis instantly reflects score changes + No data loss + Kafka, Redis Cluster, and DBs can all scale independently + TTL on keys: expire after 1 day/week/month'",
                      "From image: 'Con: Complex Architecture + If Redis consumer lags, leaderboard may be stale + If data not present in redis, we need to query db'",
                      "Consistency: (1) Redis = latest (eventual consistency with <1 sec lag from Kafka), (2) DB = source of truth (all events persisted), (3) Periodic reconciliation: Nightly job compares Redis vs DB, if mismatch \u2192 rebuild Redis from DB",
                      "Rebuild Redis from DB: (1) Scenario: Redis crash, all data lost, (2) Background job: SELECT video_id, SUM(score_delta) as total_score FROM scores WHERE timestamp > now() - INTERVAL '7 days' GROUP BY video_id, (3) Populate Redis: FOR EACH row: ZADD leaderboard:global:weekly:{YYYY-W{WW}} {total_score} {video_id}, (4) Time: Rebuild 10M entries takes ~10 minutes (acceptable for disaster recovery)"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Batch Jobs for Pre-Computed Rankings (from image)",
                    "steps": [
                      "From image: 'PreComputed - All scores are stored in a SQL/NoSQL DB (Spanner, Cassandra, BigQuery). Periodic batch jobs (e.g., hourly/daily) pre-compute top-K rankings. Store results in materialized leaderboard table or cache layer.'",
                      "Use case: When real-time updates not critical (e.g., monthly top songs, annual top players)",
                      "Batch job flow: (1) Trigger: Cron job runs every day at 2 AM, (2) Query DB: SELECT video_id, SUM(views) as total_views FROM video_views WHERE timestamp >= '2026-01-01' AND timestamp < '2026-02-01' GROUP BY video_id ORDER BY total_views DESC LIMIT 1000 (top 1000 videos in January), (3) Materialize: INSERT INTO leaderboard_snapshots (leaderboard_id: 'global_monthly_2026-01', rank, video_id, score, snapshot_date) (store pre-computed rankings), (4) Cache: SET leaderboard_cache:global:monthly:2026-01 {json_top_1000} EX 86400 (cache for 24 hours)",
                      "From image: 'Cons: Batch job runs every X minutes/hours \u2192 Leaderboards can be outdated by a few minutes or more'",
                      "From image: 'Pros: + Compute Top-K for every dimension (for instant result) + Minimal search duration + Introduce caching (optimization +'",
                      "Read path: (1) User queries: GET /api/v1/leaderboards/global/top?window=monthly&date=2026-01, (2) Check cache: GET leaderboard_cache:global:monthly:2026-01, if hit \u2192 return cached top-1000 (<1ms), (3) If cache miss: Query DB: SELECT rank, video_id, score FROM leaderboard_snapshots WHERE leaderboard_id='global_monthly_2026-01' ORDER BY rank LIMIT 10, (4) Cache result",
                      "Optimization: (1) Incremental updates: Instead of full recompute, batch job only processes new scores since last run (e.g., last 1 hour), merges with existing leaderboard, (2) Parallelization: Batch job runs on Spark cluster, processes 10M videos in parallel (reduce time from 1 hour to 10 minutes)"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Probabilistic Data Structures (Top-K Approximation)",
                    "steps": [
                      "From image: 'Place to implement: Where we already know, the query before hand (means which dimension of data user is likely to view)'",
                      "Use case: When exact top-K not required, approximate ranking acceptable (e.g., 'Trending now' where rank 100 vs 101 doesn't matter)",
                      "Count-Min Sketch: (1) Data structure: Probabilistic counter (trade accuracy for space), (2) Size: 1 MB can track billions of items (vs 1 GB for exact counts in Redis), (3) Update: When view event arrives, hash video_id to multiple positions in sketch, increment counters, (4) Query: Hash video_id, read counters, take minimum \u2192 estimated count, (5) Error: Overestimation possible (e.g., actual 1000 views, estimated 1050), but never underestimation",
                      "HyperLogLog: (1) Use case: Count distinct viewers (cardinality), e.g., '10M unique viewers' (not total views), (2) Size: 12 KB can estimate billions of unique IDs with <2% error, (3) Update: PFADD video:V123:unique_viewers {user_id}, (4) Query: PFCOUNT video:V123:unique_viewers \u2192 ~10000000",
                      "Bloom Filter: (1) Use case: Check if video already in top-K (membership test), (2) Size: 1 MB for 10M items with 1% false positive rate, (3) Query: 'Is video V123 in top-100?' \u2192 Yes (definitely) or No (maybe, 1% chance of false positive)",
                      "Top-K approximation algorithm: (1) Heavy Hitters: Maintain approximate top-K using Count-Min Sketch + Min-Heap, (2) Stream processing: Process millions of events/sec, maintain top-100 in memory (heap size = 100 items), (3) Update: If new item's estimated count > heap min \u2192 replace heap min with new item, (4) Query: Return heap (approximate top-100), (5) Accuracy: 95% overlap with exact top-100 (5% may be off by a few ranks)",
                      "Trade-off: (1) Memory: 100\u00d7 less than exact (1 MB vs 100 MB), (2) Accuracy: ~95% correct for top-K (acceptable for 'Trending' lists), (3) Latency: Faster updates (no DB writes, in-memory sketch), (4) Use for: Trending topics, viral videos, real-time dashboards where approximate counts sufficient"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Handling Score Ties (Multiple Items with Same Score)",
                    "steps": [
                      "Problem: Video A and Video B both have 1000 views, how to rank them?",
                      "Approach 1 - Timestamp tiebreaker: (1) Store: ZADD leaderboard:global:alltime {score}.{timestamp} V123, example: score=1000, timestamp=1674382200 \u2192 1000.1674382200, (2) Sorting: Redis sorts by float value, earlier timestamp gets higher rank (1000.1674382100 > 1000.1674382200), (3) Query: ZREVRANGE returns items in order: [Video A (1000 views, posted earlier), Video B (1000 views, posted later)]",
                      "Approach 2 - Lexicographic tiebreaker: (1) Store: ZADD leaderboard:global:alltime 1000 V123 (score only), (2) Redis ZSET: If scores equal, sorts lexicographically by member (V123 < V456 alphabetically), (3) Result: Deterministic ordering but arbitrary (no semantic meaning)",
                      "Approach 3 - Secondary score: (1) Composite score: primary_score \u00d7 1000000 + secondary_score, example: views \u00d7 1000000 + likes, Video A: 1000 views, 50 likes \u2192 1000050000, Video B: 1000 views, 30 likes \u2192 1000030000, (2) ZADD leaderboard:global:alltime 1000050000 V123, (3) Result: Video A ranks higher (more likes as tiebreaker)",
                      "Approach 4 - Random tiebreaker: (1) Add small random value: score + random(0, 1), example: 1000 + 0.5 = 1000.5, (2) Non-deterministic: Same query may return different order for tied items, (3) Use when order doesn't matter (trending lists)"
                    ]
                  },
                  {
                    "blockTitle": "Step 10: Pagination for Large Leaderboards",
                    "steps": [
                      "Problem: Leaderboard has 10M videos, user wants to see ranks 1000-1010 (page 100)",
                      "Offset-based pagination: (1) Query: GET /api/v1/leaderboards/global/top?offset=1000&limit=10, (2) Redis: ZREVRANGE leaderboard:global:alltime 1000 1009 WITHSCORES (get items at rank 1000-1009), (3) Time complexity: O(log(N) + offset + K) where offset=1000, K=10, (4) Performance: Efficient even for large offsets (offset=1M still <100ms in Redis)",
                      "Cursor-based pagination: (1) First page: ZREVRANGE leaderboard:global:alltime 0 9 WITHSCORES \u2192 returns top 10 + cursor = last item's score, (2) Next page: ZREVRANGEBYSCORE leaderboard:global:alltime {cursor} -inf LIMIT 0 10 (get next 10 items with score < cursor), (3) Advantage: Consistent pagination even if leaderboard updates between page requests, (4) Disadvantage: Can't jump to arbitrary page (must paginate sequentially)",
                      "Rank jumping: (1) User wants to see 'My rank': GET /api/v1/leaderboards/global/rank/V123 \u2192 rank = 50000, (2) Get surrounding entries: ZREVRANGE leaderboard:global:alltime {rank-5} {rank+4} (get 5 entries before + user + 4 entries after), (3) Display: User sees their position in context",
                      "Infinite scroll: (1) Client tracks last fetched rank: offset = 0 \u2192 10 \u2192 20 \u2192 30 (as user scrolls), (2) Request next page: GET ?offset={offset}&limit=10, (3) Stop: When ZREVRANGE returns <10 items \u2192 end of leaderboard"
                    ]
                  },
                  {
                    "blockTitle": "Step 11: Decay/Time-Based Scoring (Trending Algorithm)",
                    "steps": [
                      "Problem: Old viral video has 10M views, new video has 100K views today \u2192 new video should rank higher in 'Trending'",
                      "Time-decay formula: score = raw_count / (age_in_hours + 2)^1.5 (Hacker News algorithm), Example: Video A: 10M views, 30 days old (720 hours) \u2192 score = 10000000 / (720+2)^1.5 \u2248 515, Video B: 100K views, 1 hour old \u2192 score = 100000 / (1+2)^1.5 \u2248 19245, Video B ranks higher (trending now)",
                      "Implementation: (1) Batch job runs hourly: Recalculate scores for all videos updated in last 7 days (older videos excluded from trending), (2) Formula: SELECT video_id, view_count / POW(EXTRACT(EPOCH FROM (NOW() - created_at)) / 3600 + 2, 1.5) as trending_score FROM videos WHERE created_at > NOW() - INTERVAL '7 days', (3) Update Redis: FOR EACH video: ZADD leaderboard:trending {trending_score} {video_id}, (4) Expire old entries: ZREMRANGEBYSCORE leaderboard:trending -inf {min_score_threshold} (remove videos with score < threshold)",
                      "Reddit Hot algorithm: score = log10(ups - downs) + (age_in_seconds / 45000), (1) Logarithm: Diminishing returns (10 votes vs 100 votes has less impact than 100 vs 1000), (2) Time factor: Older posts decay linearly, (3) Balances engagement and recency",
                      "Real-time decay: (1) Problem: Batch job runs hourly, but trending list stale between runs, (2) Solution: Store (raw_count, timestamp) in ZSET, compute score on read: ZREVRANGE \u2192 fetch entries, for each entry: score = calculate_decay(raw_count, timestamp), sort by score, return top-K, (3) Trade-off: More CPU on read path, but always fresh scores"
                    ]
                  },
                  {
                    "blockTitle": "Step 12: Monitoring & Alerting",
                    "steps": [
                      "Metrics: (1) Kafka lag: Consumer lag > 1 minute \u2192 alert (leaderboard stale), (2) Redis memory: Memory usage > 80% \u2192 alert (risk of eviction), (3) Query latency: p99 latency > 100ms \u2192 alert (slow queries), (4) Cache hit rate: < 90% \u2192 alert (Redis not effective)",
                      "Dashboards: (1) Real-time: Current top-10 per leaderboard (visual validation), (2) Lag: Graph of Kafka consumer lag over time, (3) Traffic: Requests/sec per leaderboard endpoint, (4) Errors: 5xx error rate, timeout rate",
                      "Alerts: (1) Redis cluster: If 1 node down \u2192 auto-failover to replica + alert, (2) Kafka: If partition offline \u2192 alert (data loss risk), (3) DB: If replication lag > 10 sec \u2192 alert (reads may be stale), (4) Batch jobs: If job fails or takes >2\u00d7 normal time \u2192 alert",
                      "Validation: (1) Consistency check: Nightly job compares Redis top-100 vs DB top-100, if mismatch > 5 items \u2192 alert (data corruption or consumer lag), (2) Sampling: Random sample 1% of scores, verify Redis matches DB",
                      "Load testing: (1) Simulate: 1M req/sec score updates (Kafka load test), 100K req/sec leaderboard queries (Redis load test), (2) Measure: Latency at percentiles (p50, p90, p99, p99.9), error rate, (3) Capacity planning: Current traffic = 100K req/sec, system handles 1M req/sec \u2192 10\u00d7 headroom"
                    ]
                  },
                  {
                    "blockTitle": "Step 13: Scaling & Performance Optimization",
                    "steps": [
                      "Redis Cluster: (1) Shard by leaderboard ID: {CRC16(leaderboard_id) mod 16384} \u2192 determines slot \u2192 determines node, (2) 10 nodes (5 masters + 5 replicas): Each node handles ~1-2 GB, distributes 10 GB total, (3) Reads: Route to replica (reduce load on master), (4) Writes: Master handles writes, replicates to replica",
                      "Kafka partitioning: (1) Partition 'score.updated' by video_id hash (100 partitions), (2) Consumer group: 100 consumers (1 per partition), parallel processing, (3) Throughput: 100 consumers \u00d7 10K events/sec = 1M events/sec",
                      "Database sharding: (1) Shard scores table by video_id hash (10 shards), (2) Each shard: 1M videos, reduces query time (scan 1M instead of 10M), (3) Cross-shard queries: Periodic batch jobs aggregate across shards",
                      "Caching: (1) Application-level cache: Cache top-100 in app memory (refresh every 10 seconds), serves 90% of queries from memory (no Redis round trip), (2) CDN: Cache API responses at edge (1 min TTL), global users get <50ms latency",
                      "Read replicas: (1) Redis: 5 replicas per master, route reads to replicas (5\u00d7 read capacity), (2) DB: 3 replicas, route leaderboard queries to replicas (primary handles writes)",
                      "Materialized views: (1) DB: CREATE MATERIALIZED VIEW top_1000_daily AS SELECT ..., refresh hourly, (2) Queries: SELECT * FROM top_1000_daily WHERE rank <= 10 (instant, no aggregation)",
                      "Approximate queries: (1) Use Count-Min Sketch for top-K approximation, (2) 100\u00d7 less memory, 95% accurate, sufficient for trending lists",
                      "Batch optimization: (1) Update Redis in batches: ZADD leaderboard:global:alltime {score1} {video1} {score2} {video2} ... (single command for 100 items), reduces round trips 100\u00d7 (100 commands \u2192 1 command)",
                      "Connection pooling: (1) Each app instance: 50 Redis connections, 50 DB connections, reuse across requests, (2) Redis pipelining: Send multiple commands in single network round trip (ZADD + ZREVRANGE \u2192 1 RTT instead of 2)"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details (from image)",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scores (Score DB - persistent storage)",
                    "pairs": [
                      {
                        "k": "score_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "entity_id",
                        "v": "varchar(100) (video_id, song_id, player_id - what's being ranked)"
                      },
                      {
                        "k": "score_delta",
                        "v": "int (increment value: +1 for view, +10 for like)"
                      },
                      {
                        "k": "leaderboard_id",
                        "v": "varchar(100) (global, regional, category-specific)"
                      },
                      {
                        "k": "region",
                        "v": "varchar(10) (US, EU, IN - for regional leaderboards)"
                      },
                      {
                        "k": "category",
                        "v": "varchar(50) (music, gaming, sports - for category leaderboards)"
                      },
                      {
                        "k": "timestamp",
                        "v": "timestamptz (when event occurred)"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb (additional context: user_id, device_type, etc.)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (leaderboard_id, timestamp) for batch aggregation, INDEX on (entity_id) for entity lookups"
                      },
                      {
                        "k": "Sharding",
                        "v": "Shard by entity_id hash (distribute load across 10 shards)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Leaderboard Snapshots (Materialized rankings)",
                    "pairs": [
                      {
                        "k": "leaderboard_id",
                        "v": "varchar(100) (e.g., 'global_daily_2026-01-25')"
                      },
                      {
                        "k": "rank",
                        "v": "int (1, 2, 3, ...)"
                      },
                      {
                        "k": "entity_id",
                        "v": "varchar(100)"
                      },
                      {
                        "k": "score",
                        "v": "bigint (total accumulated score)"
                      },
                      {
                        "k": "snapshot_date",
                        "v": "date (when snapshot was created)"
                      },
                      {
                        "k": "Composite PK",
                        "v": "(leaderboard_id, rank)"
                      },
                      {
                        "k": "Use case",
                        "v": "Pre-computed rankings for batch approach, updated by periodic jobs"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - ZSET (Sorted Sets, from image)",
                    "pairs": [
                      {
                        "k": "leaderboard:global:alltime",
                        "v": "ZSET - All-time global leaderboard, no TTL (permanent)"
                      },
                      {
                        "k": "leaderboard:global:daily:{YYYY-MM-DD}",
                        "v": "ZSET - Daily leaderboard, TTL 7 days (auto-expire old days)"
                      },
                      {
                        "k": "leaderboard:global:weekly:{YYYY}-W{WW}",
                        "v": "ZSET - Weekly leaderboard, TTL 90 days"
                      },
                      {
                        "k": "leaderboard:global:monthly:{YYYY-MM}",
                        "v": "ZSET - Monthly leaderboard, TTL 365 days"
                      },
                      {
                        "k": "leaderboard:{region}:alltime",
                        "v": "ZSET - Regional leaderboard (e.g., leaderboard:US:alltime), no TTL"
                      },
                      {
                        "k": "leaderboard:{category}:alltime",
                        "v": "ZSET - Category leaderboard (e.g., leaderboard:music:alltime), no TTL"
                      },
                      {
                        "k": "leaderboard:trending",
                        "v": "ZSET - Trending (time-decay scoring), updated hourly, TTL 24 hours"
                      },
                      {
                        "k": "Commands",
                        "v": "ZINCRBY (update score), ZREVRANGE (get top-K), ZREVRANK (get rank), ZSCORE (get score)"
                      },
                      {
                        "k": "Memory",
                        "v": "~100 bytes per entry, 10M entries = ~1 GB per leaderboard"
                      }
                    ]
                  },
                  {
                    "groupTitle": "InfluxDB - Time-Series (from image approach 2)",
                    "pairs": [
                      {
                        "k": "Measurement",
                        "v": "entity_scores (e.g., video_scores, song_scores)"
                      },
                      {
                        "k": "Tags",
                        "v": "entity_id, region, category (indexed for fast filtering)"
                      },
                      {
                        "k": "Fields",
                        "v": "views_count, likes_count, shares_count, score (numeric values)"
                      },
                      {
                        "k": "Timestamp",
                        "v": "time (nanosecond precision)"
                      },
                      {
                        "k": "Retention",
                        "v": "1-min data: 7 days, 1-hour data: 90 days, 1-day data: forever"
                      },
                      {
                        "k": "Queries",
                        "v": "SELECT SUM(views_count) FROM entity_scores WHERE time > now() - 24h GROUP BY entity_id ORDER BY SUM DESC LIMIT 10"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Kafka Topics (from image)",
                    "pairs": [
                      {
                        "k": "score.updated",
                        "v": "Score events (views, likes, game scores), partition by entity_id (100 partitions)"
                      },
                      {
                        "k": "Consumer groups",
                        "v": "redis-consumer (updates Redis ZSET), db-consumer (persists to DB), analytics-consumer (InfluxDB writes)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Aggregated DB (Spanner/Cassandra/BigQuery - from image)",
                    "pairs": [
                      {
                        "k": "Table",
                        "v": "aggregated_scores (materialized aggregates for batch approach)"
                      },
                      {
                        "k": "entity_id",
                        "v": "varchar PRIMARY KEY"
                      },
                      {
                        "k": "total_score",
                        "v": "bigint (sum of all score deltas)"
                      },
                      {
                        "k": "daily_score",
                        "v": "bigint (sum for today, reset daily)"
                      },
                      {
                        "k": "weekly_score",
                        "v": "bigint (sum for this week)"
                      },
                      {
                        "k": "monthly_score",
                        "v": "bigint (sum for this month)"
                      },
                      {
                        "k": "last_updated",
                        "v": "timestamp"
                      },
                      {
                        "k": "Use case",
                        "v": "Batch jobs aggregate scores here, materialized for fast queries"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "approach-comparison",
                "title": "8. Approach Comparison - Deep Dive (from image shows 3 approaches)",
                "type": "bullets",
                "items": [
                  "From image: Shows 3 distinct approaches with pros/cons for each",
                  "Approach 1 - Redis Sorted Set/ZSET (from image): Data structure: One ZSET per leaderboard (e.g., leaderboard:music:IN:alltime), stores (entity_id, score) pairs sorted by score. Operations: ZINCRBY (update score in O(log N)), ZREVRANGE (get top-K in O(log N + K)), ZREVRANK (get rank in O(log N)). Pros: (1) Every rank and data available instantly (<1ms queries), (2) Simple implementation (just Redis commands), (3) Atomic operations (ZINCRBY is thread-safe). Cons: (1) Complete expensive (no need to keep in single node + not possible for 'ideal depth'), (2) Memory intensive (10M entries = 1 GB per leaderboard), (3) No persistence (Redis crash = data loss without replica), (4) Hard to do historical analysis (only current state, no time-series). When to use: Real-time leaderboards (gaming, live sports), small-medium scale (<100M entities), simple time windows (daily, weekly, monthly), low latency critical (<10ms).",
                  "Approach 2 - TimeSeriesDB (InfluxDB) with Flink (from image): Data flow: Kafka \u2192 Flink (stream aggregation) \u2192 InfluxDB (time-series storage) \u2192 Query for top-K. Flink processing: Window aggregation (1-minute windows), group by entity_id, count events, output to InfluxDB. Pros: (1) Historical trend analysis (how did rank change over time), (2) Efficient storage (compressed time-series, 10\u00d7 less than raw events), (3) Complex queries (percentiles, moving averages, downsampling). Cons: (1) Slower queries (~100ms vs 1ms in Redis), (2) Complex setup (Flink + InfluxDB + Kafka), (3) Not real-time (<1 sec lag from Flink windowing), (4) Harder to get exact rank (requires aggregation query). When to use: Analytics dashboards (show trend graphs), historical leaderboards (e.g., 'Top songs each month for past year'), large-scale time-series data (billions of events), when exact real-time ranking not critical.",
                  "Approach 3 - Hybrid (Redis + DB + Kafka) [BEST PRACTICE from image]: Architecture: Kafka (events) \u2192 Redis Consumer (real-time ZSET updates) + DB Consumer (persistent writes) \u2192 Redis (reads) + DB (fallback/historical). Write path: (1) Score event \u2192 Kafka, (2) Redis Consumer: ZINCRBY (real-time, <100ms lag), (3) DB Consumer: INSERT scores (persistent, eventual consistency). Read path: (1) Try Redis ZREVRANGE (fast, <1ms), (2) If Redis miss \u2192 Query DB: SELECT ... GROUP BY ... ORDER BY ... (slower, ~100ms), (3) Rebuild Redis from DB if needed. Pros: (1) Redis instantly reflects score changes (real-time), (2) No data loss (Kafka + DB persistence), (3) All components scale independently (Kafka partitions, Redis cluster, DB sharding), (4) TTL on keys (auto-expire old leaderboards to save memory). Cons: (1) Complex architecture (Kafka + Redis + DB = 3 systems to manage), (2) If Redis consumer lags, leaderboard may be stale, (3) If data not present in Redis, we need to query DB (slower fallback). When to use: Production systems at scale (millions of users), need both real-time updates AND persistence, multi-dimensional leaderboards (regional, category, time windows), critical data that can't be lost.",
                  "From image note: 'Bitmap: Use Map, (littered by region), Bucket: Bitmap per K' - Alternative: Use bitmaps for space efficiency when tracking top-K membership (is entity in top-1000?), much smaller than storing full scores.",
                  "From image: 'Place to implement: Where we already know, the query before hand (means which dimension of data user is likely to view)' - Pre-compute only the leaderboards users actually query (don't waste resources on unused dimensions).",
                  "Decision matrix: Need <10ms latency? \u2192 Redis ZSET. Need historical analysis? \u2192 InfluxDB. Need scale + reliability? \u2192 Hybrid. Small scale (<1M entities)? \u2192 Redis. Large scale (>100M entities)? \u2192 Hybrid or InfluxDB. Exact ranking required? \u2192 Redis. Approximate OK (trending)? \u2192 Count-Min Sketch.",
                  "Production examples: Gaming leaderboards (Fortnite, PUBG): Hybrid (Redis for real-time, DB for persistence, millions of players), Music streaming (Spotify Top 50): Batch + Redis (daily batch compute, cache in Redis, not real-time), Video platform (YouTube Trending): Time-decay algorithm + Redis (hourly recompute trending scores, cache top-100), Social media (Twitter Trends): Probabilistic (Count-Min Sketch for trending topics, approximate counts acceptable)."
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "9. Scaling & Optimization Techniques",
                "type": "bullets",
                "items": [
                  "Technique 1: Redis Cluster sharding - Shard by leaderboard_id, 16384 slots distributed across nodes, 10 nodes handle 10 GB",
                  "Technique 2: Kafka partitioning - 100 partitions by entity_id hash, 100 parallel consumers, 1M events/sec throughput",
                  "Technique 3: Time-windowed leaderboards - Daily/weekly/monthly keys with TTL, auto-expire old data, saves memory",
                  "Technique 4: Batch aggregation - ZUNIONSTORE combines multiple ZSETs (daily \u2192 weekly), runs periodically (Monday 12 AM)",
                  "Technique 5: Lazy initialization - Only create leaderboard when first score arrives, don't pre-create all combinations",
                  "Technique 6: Read replicas - Route reads to Redis replicas (5\u00d7 read capacity), master handles writes only",
                  "Technique 7: Application cache - Cache top-100 in app memory (refresh every 10 sec), serve 90% from memory",
                  "Technique 8: CDN caching - Cache API responses at edge (1 min TTL), <50ms global latency",
                  "Technique 9: Probabilistic structures - Count-Min Sketch for top-K approximation, 100\u00d7 less memory, 95% accurate",
                  "Technique 10: Connection pooling - 50 Redis connections per app instance, pipelining for batch commands",
                  "Technique 11: Materialized views - Pre-compute top-1000 in DB, refresh hourly, instant queries",
                  "Technique 12: Time-decay scoring - Trending algorithm, batch recompute hourly, balances recency and engagement"
                ]
              },
              {
                "id": "interview-qa",
                "title": "10. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "Design a real-time gaming leaderboard that shows top 100 players globally and by region. How do you handle millions of score updates per second?",
                    "a": "Real-time gaming leaderboard architecture: Requirements: (1) Top 100 global, (2) Top 100 per region (US, EU, APAC), (3) Millions of score updates/sec, (4) <100ms query latency, (5) Handle score ties. Architecture: Kafka (score events) \u2192 Redis Consumer (real-time ZSET) + DB Consumer (persistence) \u2192 Redis Cluster (reads) + PostgreSQL (backup). Score update flow: (1) Player scores 1000 points in game \u2192 Game server sends: POST /api/v1/scores with {player_id: 'P123', score_delta: 1000, region: 'US', match_id: 'M456', timestamp}, (2) Score Service validates: Check player exists, check match active, check for duplicate (Redis dedup with 5-min TTL), (3) Publish to Kafka: 'score.updated' topic with {player_id, score_delta: 1000, region: 'US', timestamp}, partition by player_id hash \u2192 ensures ordering per player. Redis Consumer (real-time updates): (1) Consumes from Kafka (100 consumer instances, 1 per partition), (2) Updates Redis ZSETs: ZINCRBY leaderboard:global:alltime 1000 P123 (global leaderboard), ZINCRBY leaderboard:US:alltime 1000 P123 (regional leaderboard), ZINCRBY leaderboard:global:daily:{YYYY-MM-DD} 1000 P123 (daily leaderboard), (3) Lag: <100ms from event to Redis update (Kafka consumer lag monitoring). DB Consumer (persistence): (1) Separate consumer group writes to PostgreSQL, (2) INSERT INTO scores (player_id, score_delta, region, timestamp, match_id), (3) Eventual consistency (lag ~1 second, acceptable for backup/analytics). Query flow: User requests top 100: GET /api/v1/leaderboards/global/top?limit=100. Ranking Service: (1) Check Redis: ZREVRANGE leaderboard:global:alltime 0 99 WITHSCORES (get top 100 with scores), Time: <1ms (Redis in-memory lookup), (2) Enrich data: For each player_id, fetch player details: MGET player:P123 player:P456 ... (batch fetch from Redis player cache), If cache miss \u2192 query DB: SELECT username, avatar_url FROM players WHERE player_id IN (...), (3) Response: [{rank: 1, player_id: 'P123', username: 'ProGamer', score: 1500000}, {rank: 2, ...}, ...], (4) Total latency: <10ms (1ms ZREVRANGE + 5ms MGET + 2ms serialization). Regional leaderboard: GET /api/v1/leaderboards/US/top?limit=100, Same flow but query: ZREVRANGE leaderboard:US:alltime 0 99. Handle score ties: (1) Timestamp tiebreaker: When scores equal, player who reached score first ranks higher, (2) Implementation: Store score as float: score + (timestamp / 1e12), Example: Player A: 1000 points at t=1674382200 \u2192 1000.000001674382200, Player B: 1000 points at t=1674382300 \u2192 1000.000001674382300, Player A ranks higher (earlier timestamp), (3) ZADD leaderboard:global:alltime {score_with_timestamp} P123. Scaling for millions of updates/sec: (1) Kafka: 100 partitions \u00d7 10K events/sec/partition = 1M events/sec, add more partitions to scale beyond, (2) Redis Cluster: 10 master nodes (each handles 100K ZINCRBY/sec) = 1M ZINCRBY/sec, shard by leaderboard_id: hash(leaderboard:global:alltime) mod 10 \u2192 node 5, (3) Redis Consumer: 100 instances (1 per Kafka partition), auto-scale based on Kafka lag (if lag > 1 min \u2192 add more consumers). Redis optimization: (1) Pipelining: Batch updates: PIPELINE; ZINCRBY leaderboard:global:alltime 1000 P123; ZINCRBY leaderboard:US:alltime 1000 P123; EXEC; (send 2 commands in 1 network round trip), (2) Lua script: EVALSHA to update multiple ZSETs atomically (prevents partial updates if one fails), (3) Memory: Top 100 leaderboard = 100 entries \u00d7 100 bytes = 10 KB (tiny), Global leaderboard (10M players) = 1 GB (manageable). Disaster recovery: (1) Redis crash: All ZSET data lost (in-memory only), (2) Rebuild from DB: Background job queries: SELECT player_id, SUM(score_delta) as total_score FROM scores GROUP BY player_id, Populate Redis: FOR EACH player: ZADD leaderboard:global:alltime {total_score} {player_id}, Time: Rebuild 10M players takes ~10 minutes (acceptable), (3) Redis persistence: Enable RDB snapshots (every 5 min) or AOF (append-only file) for faster recovery. Advanced features: (1) Player's rank: GET /api/v1/leaderboards/global/rank/P123 \u2192 ZREVRANK leaderboard:global:alltime P123 returns rank (0-indexed), Example: rank=42 \u2192 display as #43, (2) Percentile: Player in top 1%? \u2192 total_players = ZCARD leaderboard:global:alltime, percentile = (rank / total_players) \u00d7 100, (3) Surrounding players: ZREVRANGE leaderboard:global:alltime {rank-5} {rank+4} WITHSCORES (show 5 above + player + 4 below). Monitoring: (1) Kafka consumer lag: Alert if lag > 1 min (leaderboard stale), (2) Redis memory: Alert if > 80% (risk of eviction), (3) Query latency: p99 < 10ms (SLA), (4) Consistency check: Hourly job compares Redis top-100 vs DB top-100, alert if mismatch. Production example: Fortnite uses similar architecture, 250M+ players globally, millions of concurrent matches, real-time leaderboards updated every few seconds, Redis Cluster (100+ nodes), Kafka (1000+ partitions), sub-10ms query latency for top 100."
                  },
                  {
                    "q": "How would you design a YouTube Trending page that shows top 50 trending videos? Explain the time-decay algorithm.",
                    "a": "YouTube Trending page design with time-decay scoring: Requirements: (1) Top 50 trending videos (updated hourly), (2) Balance recency and engagement (new viral video ranks higher than old popular video), (3) Regional trending (US, UK, India, etc.), (4) Category trending (Music, Gaming, News). Time-decay algorithm (Hacker News style): Formula: trending_score = engagement_score / (age_in_hours + 2)^gravity, Parameters: (1) engagement_score: Weighted sum of interactions, engagement_score = views + (likes \u00d7 10) + (comments \u00d7 20) + (shares \u00d7 30), Weights: Shares > Comments > Likes > Views (stronger engagement signal weighted higher), (2) age_in_hours: Time since video published, age_in_hours = (NOW() - published_at) / 3600 (convert seconds to hours), (3) gravity: Decay rate (typically 1.5-2.0), Higher gravity = faster decay (older content drops faster), YouTube likely uses gravity \u2248 1.8. Example calculation: Video A (old viral): Published 7 days ago (168 hours), 10M views, 500K likes, 50K comments, 10K shares, engagement_score = 10000000 + (500000\u00d710) + (50000\u00d720) + (10000\u00d730) = 16300000, trending_score = 16300000 / (168+2)^1.8 = 16300000 / 1173 \u2248 13895. Video B (new rising): Published 3 hours ago, 500K views, 50K likes, 5K comments, 1K shares, engagement_score = 500000 + (50000\u00d710) + (5000\u00d720) + (1000\u00d730) = 1130000, trending_score = 1130000 / (3+2)^1.8 = 1130000 / 11.8 \u2248 95763, Video B ranks HIGHER (trending now despite lower absolute engagement). Architecture: Batch computation + Redis caching. Batch job (hourly): (1) Trigger: Cron runs every hour at :00 (12:00, 13:00, 14:00), (2) Query recent videos: SELECT video_id, views, likes, comments, shares, published_at FROM videos WHERE published_at > NOW() - INTERVAL '7 days' (only videos from last 7 days eligible for trending), (3) Calculate trending score: FOR EACH video: age_in_hours = (NOW() - published_at) / 3600, engagement_score = views + (likes\u00d710) + (comments\u00d720) + (shares\u00d730), trending_score = engagement_score / POW(age_in_hours + 2, 1.8), (4) Sort and store top 1000: ORDER BY trending_score DESC LIMIT 1000, (5) Write to Redis: DEL leaderboard:trending (clear old), ZADD leaderboard:trending {trending_score} {video_id} (add all 1000 videos), EXPIRE leaderboard:trending 7200 (2-hour TTL, in case batch job fails next run). Regional trending: (1) Separate batch job per region, (2) Filter: WHERE region IN ('US', 'CA', 'MX') for North America trending, (3) Store: ZADD leaderboard:trending:US {trending_score} {video_id}. Category trending: (1) Filter: WHERE category='Music', (2) Store: ZADD leaderboard:trending:Music {trending_score} {video_id}. Query flow: User opens Trending page: GET /api/v1/trending?region=US&limit=50. Ranking Service: (1) Check Redis: ZREVRANGE leaderboard:trending:US 0 49 WITHSCORES (get top 50), Cache hit (99% of requests): Return from Redis (<1ms), Cache miss (rare, during batch job or Redis restart): Fallback to DB query (recalculate trending scores on-the-fly, slower ~500ms), (2) Enrich video details: For each video_id, fetch metadata: MGET video:V123 video:V456 ... (title, thumbnail, channel), (3) Response: [{rank: 1, video_id: 'V123', title: 'New Music Video', trending_score: 95763, views: 500K, ...}]. Real-time vs batch trade-off: (1) Batch (hourly update): Simpler implementation, less compute (recalculate once per hour, not on every query), Staleness: Trending list can be up to 1 hour old (acceptable for most users), (2) Real-time (per-query calculation): Always fresh scores, High compute cost (recalculate for 1M videos on every query \u2192 100ms query time), YouTube uses batch approach (hourly or even daily updates for trending). Optimization: (1) Incremental computation: Instead of recalculating all videos every hour, only update videos with new engagement (events in last hour), Kafka stream: Consume view/like/comment events, update trending score in real-time (hybrid), (2) Materialized view: CREATE MATERIALIZED VIEW trending_videos AS SELECT video_id, ..., REFRESH MATERIALIZED VIEW CONCURRENTLY (non-blocking), Query trending view instead of raw videos table (10\u00d7 faster). Alternative: Reddit Hot algorithm: Formula: hot_score = log10(engagement) + (age_in_seconds / 45000), Logarithm: Diminishing returns (100 vs 1000 engagement has less impact than 10 vs 100), Linear time decay: Older posts decay steadily, Use case: When you want engagement to matter more than recency (Reddit front page stays stable longer than YouTube trending). Monitoring: (1) Batch job duration: Alert if job takes >10 min (normally 5 min), (2) Trending consistency: Compare manual check vs automated trending (validate viral videos appear), (3) Regional coverage: Ensure all regions have trending data (no empty leaderboards). Advanced: Personalized trending: (1) User's watch history: SELECT categories_watched, channels_subscribed FROM user_behavior WHERE user_id={user_id}, (2) Boost relevant categories: If user watches Music videos 80% of time, boost Music videos in trending, Formula: personalized_score = trending_score \u00d7 (1 + category_affinity), (3) ML model: Train model to predict 'will user click this trending video', Features: trending_score, user_watch_history, video_category, time_of_day. Production: YouTube Trending page updated daily or hourly (not real-time), uses combination of engagement signals + time decay, regional and category-specific trending, reviewed by human moderators (prevent manipulation, ensure quality), ML-based personalization for logged-in users."
                  },
                  {
                    "q": "Compare Redis ZSET vs database-based leaderboard approaches. When would you choose one over the other?",
                    "a": "Redis ZSET vs Database leaderboard comparison: Redis ZSET approach: Data structure: Sorted set (ZSET) stores (member, score) pairs, automatically sorted by score. Operations: (1) ZADD leaderboard {score} {member}: Add/update entry O(log N), (2) ZINCRBY leaderboard {increment} {member}: Increment score O(log N), (3) ZREVRANGE leaderboard 0 9: Get top-10 O(log N + K), (4) ZREVRANK leaderboard {member}: Get rank O(log N), (5) ZSCORE leaderboard {member}: Get score O(1). Example: ZADD leaderboard 1000 player1, ZINCRBY leaderboard 500 player1 (now 1500), ZREVRANGE leaderboard 0 2 WITHSCORES \u2192 [(player1, 1500), (player2, 1200), (player3, 1000)]. Pros: (1) Blazing fast: All operations <1ms (in-memory), top-100 query in ~1ms even with 10M players, (2) Simple: Just Redis commands, no SQL, no indexes, no query optimization, (3) Atomic: ZINCRBY is thread-safe (no race conditions), (4) Built-in ranking: ZREVRANK gives rank without scanning, (5) Auto-sorted: No manual sorting needed. Cons: (1) Memory only: No persistence by default (RDB/AOF mitigates but not 100% durable), (2) Single machine limit: One ZSET must fit in RAM of single node (can't shard a single ZSET), For 100M players \u00d7 100 bytes = 10 GB (expensive Redis server needed), (3) No time-series: Only current state, can't query 'What was the leaderboard last week?', (4) Limited queries: Can't do complex filters (e.g., 'Top players from California who started in 2024'), (5) Cost: Redis memory expensive (RAM > disk storage by 10-100\u00d7). Database approach (PostgreSQL/MySQL): Schema: CREATE TABLE leaderboard (player_id varchar PRIMARY KEY, score bigint, updated_at timestamp); CREATE INDEX idx_score ON leaderboard(score DESC); Query top-10: SELECT player_id, score FROM leaderboard ORDER BY score DESC LIMIT 10; Update score: UPDATE leaderboard SET score = score + {increment} WHERE player_id = {player_id}; Get rank: SELECT COUNT(*) FROM leaderboard WHERE score > (SELECT score FROM leaderboard WHERE player_id = {player_id}); (counts how many players have higher score). Pros: (1) Persistent: Data on disk, survives crashes (ACID transactions), (2) Scalable storage: Disk cheaper than RAM (10 GB disk << 10 GB RAM), can store 1B+ players, (3) Time-series: Add timestamp column, query historical leaderboards ('Top players on 2024-01-01'), (4) Complex queries: Filter by region, category, date range, etc., (5) Mature ecosystem: Backups, replication, monitoring well-established. Cons: (1) Slower: Top-10 query ~50-100ms (disk I/O, index scan), 100\u00d7 slower than Redis, (2) Rank calculation expensive: Get rank query scans all players with score > player's score \u2192 O(N) time for large N, For 10M players, rank query takes ~500ms (unacceptable for real-time leaderboards), (3) Concurrent updates: UPDATE locks row, high contention for popular players (score updated 1000s of times/sec), (4) Index overhead: Score index needs rebalancing on every UPDATE (slower writes). Hybrid approach (Redis + DB): Best of both worlds. Architecture: Writes: Score event \u2192 Kafka \u2192 (1) Redis Consumer: ZINCRBY leaderboard {increment} {player} (real-time), (2) DB Consumer: INSERT INTO score_events (player_id, score_delta, timestamp) (persistent log). Reads: (1) Try Redis: ZREVRANGE leaderboard 0 9 (fast, <1ms), (2) If Redis miss: Query DB: SELECT player_id, SUM(score_delta) FROM score_events GROUP BY player_id ORDER BY SUM DESC LIMIT 10 (slow, ~100ms), rebuild Redis from DB result. Consistency: Redis reflects latest scores (<1 sec lag from Kafka), DB is source of truth (eventual consistency, ~1-5 sec lag), Periodic reconciliation: Nightly job compares Redis vs DB, rebuilds Redis if mismatch. Disaster recovery: Redis crash \u2192 rebuild from DB (takes ~10 min for 10M players, acceptable). Decision matrix: Choose Redis ZSET when: (1) Need low latency (<10ms for top-K queries), (2) Leaderboard fits in memory (<100M players \u2192 <10 GB), (3) Real-time updates critical (gaming, live sports), (4) Simple use case (global top-K, no complex filters), (5) Can tolerate data loss risk (use RDB/AOF for mitigation). Choose Database when: (1) Need persistence (can't lose leaderboard data), (2) Large scale (>100M players, >10 GB data), (3) Historical queries needed ('Top players last month'), (4) Complex filters (region, category, time ranges), (5) Lower budget (disk storage 10\u00d7 cheaper than RAM). Choose Hybrid when: (1) Need BOTH low latency AND persistence, (2) Production system with millions of users, (3) Can manage complexity (Kafka + Redis + DB), (4) Multi-dimensional leaderboards (regional, category, time windows). Materialized views (advanced DB approach): CREATE MATERIALIZED VIEW top_1000_players AS SELECT player_id, score FROM leaderboard ORDER BY score DESC LIMIT 1000; REFRESH MATERIALIZED VIEW top_1000_players; (refresh hourly). Query: SELECT * FROM top_1000_players LIMIT 10; (~1ms, pre-computed). Trade-off: Freshness vs performance (view can be 1 hour stale). Sharding (scale DB approach): Shard by player_id hash, not by score (score-based sharding causes hotspots \u2192 top players all on same shard). Query top-K: Scatter-gather across all shards \u2192 merge results \u2192 return top-10. Expensive but scales to billions of players. Production examples: Gaming (Fortnite, PUBG): Hybrid (Redis for real-time, DB for persistence), Music (Spotify Top 50): Batch DB + Redis cache (hourly batch, cache top-1000), Social media (Twitter Trends): Redis ZSET (short-lived trending, OK to lose data), Finance (Stock tickers): Database (need ACID transactions, audit trail). Summary: Redis = fast but volatile, Database = slow but durable, Hybrid = best of both but complex. Choose based on latency requirements, data size, durability needs, and budget."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "11. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Performance",
                    "pairs": [
                      {
                        "k": "Events/sec",
                        "v": "1M req/sec - billions of score updates globally"
                      },
                      {
                        "k": "Query Latency",
                        "v": "100ms to get top-K (Redis <1ms, DB ~100ms, InfluxDB ~100ms)"
                      },
                      {
                        "k": "Update Lag",
                        "v": "<1 second for real-time leaderboard updates (Redis ZSET via Kafka)"
                      },
                      {
                        "k": "Batch Frequency",
                        "v": "Hourly or daily for trending algorithms (time-decay recomputation)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis ZSET (from image Approach 1)",
                    "pairs": [
                      {
                        "k": "Memory per entry",
                        "v": "~100 bytes (member + score + ZSET overhead)"
                      },
                      {
                        "k": "10M entries",
                        "v": "~1 GB per leaderboard ZSET"
                      },
                      {
                        "k": "Operations",
                        "v": "ZINCRBY O(log N), ZREVRANGE O(log N + K), ZREVRANK O(log N)"
                      },
                      {
                        "k": "Query time",
                        "v": "<1ms for top-100 even with 10M entries"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Time Windows",
                    "pairs": [
                      {
                        "k": "Daily leaderboard TTL",
                        "v": "7 days (auto-expire old days)"
                      },
                      {
                        "k": "Weekly leaderboard TTL",
                        "v": "90 days (keep ~13 weeks)"
                      },
                      {
                        "k": "Monthly leaderboard TTL",
                        "v": "365 days (keep 12 months)"
                      },
                      {
                        "k": "All-time leaderboard",
                        "v": "No TTL (permanent, grows indefinitely)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Hybrid Approach (from image Approach 3)",
                    "pairs": [
                      {
                        "k": "Kafka consumer lag",
                        "v": "<100ms (Redis consumer), ~1 sec (DB consumer)"
                      },
                      {
                        "k": "Kafka partitions",
                        "v": "100 partitions by entity_id hash, 100 parallel consumers"
                      },
                      {
                        "k": "Redis rebuild time",
                        "v": "~10 minutes to rebuild 10M entries from DB after crash"
                      },
                      {
                        "k": "Consistency check",
                        "v": "Nightly job compares Redis vs DB top-100, alerts on mismatch"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Redis ZSET is fast (<1ms) but volatile (in-memory). For production leaderboards, use HYBRID approach: Redis for real-time reads + Kafka + DB for persistence. Without persistence, Redis crash = all leaderboard data lost."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'Redis vs Database for leaderboards?'. Answer: Redis ZSET = <1ms queries but no persistence. Database = persistent but slow (~100ms). Hybrid = Redis (reads) + Kafka (events) + DB (writes) = best of both, industry standard for production."
              },
              {
                "type": "tip",
                "text": "Time-decay algorithm for Trending: trending_score = engagement / (age_in_hours + 2)^1.8. New viral video (3 hours old, 500K views) ranks higher than old popular video (7 days old, 10M views). Batch recompute hourly to balance recency and engagement."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Handle score ties with timestamp tiebreaker. Store score as float: score + (timestamp / 1e12). Example: Player A reaches 1000 points at t=100 \u2192 1000.0000000001, Player B reaches 1000 at t=200 \u2192 1000.0000000002. Player A ranks higher (earlier timestamp)."
              },
              {
                "type": "warning",
                "text": "NEVER create all leaderboard combinations upfront. With 5 regions \u00d7 10 categories \u00d7 4 time windows = 200 leaderboards \u00d7 1 GB = 200 GB Redis memory (expensive!). Use lazy initialization: create leaderboard only when first score arrives. Expire unused leaderboards after 30 days."
              },
              {
                "type": "tip",
                "text": "Kafka partitioning: Partition 'score.updated' by entity_id hash (not by timestamp). Ensures all events for same entity go to same partition \u2192 ordering preserved \u2192 Redis consumer processes scores in order \u2192 no race conditions."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love: 'How handle millions of score updates/sec?'. Answer: Kafka (1M events/sec = 100 partitions \u00d7 10K/sec) \u2192 100 Redis consumers (1 per partition) \u2192 Each does ZINCRBY (O(log N) = <1ms) \u2192 Total: 1M ZINCRBY/sec with 100 consumers. Scale by adding partitions."
              },
              {
                "type": "warning",
                "text": "NEVER query rank with COUNT(*) in database: SELECT COUNT(*) FROM leaderboard WHERE score > {player_score}. For 10M players, this scans millions of rows \u2192 500ms query time (too slow). Use Redis ZREVRANK instead \u2192 <1ms, O(log N)."
              },
              {
                "type": "tip",
                "text": "Batch jobs optimization: Use ZUNIONSTORE to combine daily ZSETs into weekly: ZUNIONSTORE leaderboard:weekly:2026-W04 7 leaderboard:daily:2026-01-19 leaderboard:daily:2026-01-20 ... WEIGHTS 1. Aggregates 7 days in single Redis command (atomic)."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: From image shows 3 approaches with pros/cons. Approach 1 (Redis ZSET): Fast but no persistence. Approach 2 (InfluxDB): Historical analysis but slow queries. Approach 3 (Hybrid): Best practice for production - Redis + Kafka + DB = real-time + durable + scalable."
              }
            ],
            "tags": [
              "system-design",
              "leaderboard",
              "top-k",
              "trending",
              "Redis-ZSET",
              "sorted-sets",
              "Kafka-streaming",
              "time-decay-algorithm",
              "hybrid-approach",
              "InfluxDB-time-series",
              "batch-jobs",
              "real-time-ranking"
            ]
          }
        },
        {
          "id": "15",
          "title": "System Design 15: Design Scalable Notifications System | SMS | OTP | Email & Push | HLD | LLD",
          "videoUrl": "https://youtu.be/Yv9q8d11Y7k",
          "description": "In this video, we design a complete Notification System from scratch, explaining how large-scale companies like Netflix, Walmart, and Amazon send millions of notifications per minute reliably using Kafka, Outbox pattern, Redis, and microservices architecture.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/ergbqgb3ogb3ogbogb34ogvoigb34o.png?updatedAt=1760949574807",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/efbwwegwgrkgnelgnlgnelgreglg.png?updatedAt=1760949557207",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 22,
            "lastUpdated": "2026-01-26",
            "hero": {
              "title": "Notification System (Multi-Channel Delivery)",
              "oneLinerToMemorize": "Client triggers notification \u2192 API Gateway \u2192 Notification Svc validates template & user preferences \u2192 Kafka queue (prioritized) \u2192 Notification Queue consumers \u2192 Provider routing (Email/SMS/Push/In-app) \u2192 External services (Twilio/SendGrid/FCM/APNS) \u2192 Delivery status \u2192 Rate limiting \u2192 Reporting"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Support multiple delivery channels (email, SMS, push notifications, in-app notifications)",
                  "Feature 2: Create, update, and manage notification templates with variables/placeholders",
                  "Feature 3: Support user preferences per channel and notification type (promotional, transactional, alerts)",
                  "Feature 4: Support immediate and scheduled notification delivery (send now or schedule for future)",
                  "Feature 5: Track delivery status for each notification (PENDING, SCHEDULED, SENT, DELIVERED, FAILED, CANCELLED)",
                  "Feature 6: Support bulk notifications (campaigns, announcements) with rate limiting",
                  "Feature 7: Provide reporting and analytics (delivery rates, open rates, click rates, failures)",
                  "Feature 8: Retry mechanism for failed deliveries with exponential backoff"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Performance",
                    "pairs": [
                      {
                        "k": "Scale",
                        "v": "1M+ notifications/min during peak (Black Friday, breaking news alerts)"
                      },
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> Consistency (eventual consistency for delivery status acceptable)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Latency & Reliability",
                    "pairs": [
                      {
                        "k": "Real-time delivery",
                        "v": "High-priority notifications (OTP) delivered within 5-10s delay for transactional notifications acceptable"
                      },
                      {
                        "k": "Reliability",
                        "v": "At-least-once delivery guarantee (no notification should be lost), retry failed deliveries"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity",
                "type": "bullets",
                "items": [
                  "Entity 1: User + Client - user_id, email, phone, device_tokens[] (FCM/APNS), preferences {channels, types, do_not_disturb}",
                  "Entity 2: NotificationPreferences - user_id, channels {email: true, sms: false, push: true, inapp: true}, types {promotional: false, transactional: true, alerts: true}, do_not_disturb: false",
                  "Entity 3: NotificationContent - notification_id, user_id (or recipient_id), external_user_id, template_id, channel (EMAIL/SMS/PUSH/INAPP), payload {variables}, status (PENDING/SCHEDULED/SENT/DELIVERED/FAILED/CANCELLED), priority (high/normal/low), scheduled_at, created_at, metadata",
                  "Entity 4: Template - template_id, name, channel (EMAIL/SMS/PUSH/INAPP), subject (for email), body (with {{variable}} placeholders), variables[] (list of expected variables like {name, order_id}), version, is_active, created_at",
                  "Entity 5: DeliveryStatus - delivery_id, notification_id, channel, provider (Twilio/SendGrid/FCM/APNS), provider_message_id, status (SENT/DELIVERED/FAILED), sent_at, delivered_at, error_message, retry_count",
                  "Entity 6: Notifications_outbox - outbox_id, notification_id, event_type, payload, published (boolean), created_at (for transactional outbox pattern)",
                  "Entity 7: Notifications table - notification_id, user_id (or recipient_id for position), external_user_id, template_id, channel, payload, status, priority, scheduled_at, metadata"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Notification Operations",
                    "pairs": [
                      {
                        "k": "POST /api/v1/notifications",
                        "v": "Send notification with {templateId, recipientId, variables, channels[], priority, scheduledAt}"
                      },
                      {
                        "k": "GET /api/v1/notifications/{notificationId}/status",
                        "v": "Get delivery status for specific notification (PENDING/SENT/DELIVERED/FAILED)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Template Management",
                    "pairs": [
                      {
                        "k": "POST /api/v1/templates",
                        "v": "Create notification template with placeholders"
                      },
                      {
                        "k": "GET /api/v1/templates",
                        "v": "List all templates (paginated)"
                      },
                      {
                        "k": "GET /api/v1/templates/{templateId}/{version}",
                        "v": "Get specific template version"
                      },
                      {
                        "k": "CRUD",
                        "v": "Create, Read, Update, Delete templates"
                      }
                    ]
                  },
                  {
                    "groupTitle": "User Preferences",
                    "pairs": [
                      {
                        "k": "PUT /api/v1/preferences",
                        "v": "Update user notification preferences {channels: {email: bool, sms: bool}, types: {promotional: bool}}"
                      },
                      {
                        "k": "..CRUD",
                        "v": "Create, Read, Update, Delete user preferences"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design (HLD)",
                "type": "bullets",
                "items": [
                  "Architecture: clients (Amazon/Uber) \u2192 API Gateway & Load Balancer \u2192 Services \u2192 Databases",
                  "Template Svc \u2192 Template DB: CRUD operations on notification templates, version management",
                  "User Preference Svc \u2192 UserPref DB: Manage user preferences (channels, types, DND), cache in Redis",
                  "Notification Svc \u2192 Notification Provider (Email/InApp/SMS/Push): Core orchestrator, validates template & preferences, publishes to Kafka",
                  "Notification Evt (MQ): Message Queue (Kafka) for async notification processing, prioritized queues (critical, standard, promotional, bulk)",
                  "Reporting Svc: Tracks delivery metrics, analytics (sent, delivered, failed, open rates, click rates)"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level - LLD)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Notification Creation & Template Rendering",
                    "steps": [
                      "Client triggers notification: POST /api/v1/notifications with {templateId: 'order_confirmation', recipientId: 'user123', variables: {name: 'Alice', order_id: 'ORD456'}, channels: ['email', 'push'], priority: 'high'}",
                      "API Gateway validation: (1) Authentication & Authorization (JWT token, API key), (2) Rate limiting (100 req/min per client), (3) Route to Notification Svc",
                      "Notification Svc: (1) Fetch template: Template Svc read: SELECT * FROM templates WHERE template_id='order_confirmation' AND is_active=true, Template: {template_id, name: 'Order Confirmation', channel: 'EMAIL', subject: 'Order {{order_id}} confirmed', body: 'Hi {{name}}, your order {{order_id}} has been confirmed...', variables: ['name', 'order_id']}, (2) Validate variables: Check all required variables present ({name: 'Alice', order_id: 'ORD456'}), if missing \u2192 return 400 'Missing variable: name'",
                      "Fetch user preferences: User Preference Svc: Check Redis cache: GET user_pref:user123 \u2192 {channels: {email: true, sms: false, push: true}, types: {promotional: false, transactional: true}}, if cache miss \u2192 query UserPref DB \u2192 cache result (24 hour TTL), Check opt-in: if channels.email=false \u2192 skip email channel, if types.promotional=false AND notification type='promotional' \u2192 skip entirely",
                      "Render template: Replace placeholders: subject: 'Order ORD456 confirmed', body: 'Hi Alice, your order ORD456 has been confirmed...', Result: {subject, body, channel: 'EMAIL'}",
                      "Generate notification_id (UUID): notification_id = 'NOTIF789'"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Transactional Outbox Pattern & Kafka Publishing",
                    "steps": [
                      "Transactional write (Outbox pattern - critical for reliability): BEGIN TRANSACTION, (1) INSERT INTO notifications (notification_id: 'NOTIF789', user_id: 'user123', template_id, channel: 'EMAIL', payload: {subject, body, to: 'alice@example.com'}, status: 'PENDING', priority: 'high', created_at: now()), (2) INSERT INTO notifications_outbox (outbox_id, notification_id: 'NOTIF789', event_type: 'notification.created', payload: {notification_id, channel, priority, recipientId}, published: false), COMMIT (atomic - both inserts succeed or both fail)",
                      "Why Outbox pattern: Prevents lost notifications, Problem: If publish to Kafka succeeds but DB write fails \u2192 notification lost (no record), If DB write succeeds but Kafka publish fails \u2192 notification stuck (never sent), Solution: Outbox ensures if DB commit succeeded, message will EVENTUALLY be published (CDC polling)",
                      "CDC/Publisher service polls outbox: SELECT * FROM notifications_outbox WHERE published=false ORDER BY created_at LIMIT 1000 (batch processing), For each unpublished record: (1) Publish to Kafka: Determine topic by priority: high \u2192 'notifications.critical.email', normal \u2192 'notifications.standard.email', low \u2192 'notifications.promotional.email', Kafka.send(topic: 'notifications.critical.email', key: notification_id, value: payload), (2) Mark published: UPDATE notifications_outbox SET published=true, published_at=now() WHERE outbox_id={id}, (3) If Kafka fails: published stays false, retry on next poll (at-least-once delivery)",
                      "Kafka topics hierarchy (prioritized queues): notifications.critical.{channel} - OTP, security alerts (processed immediately, dedicated consumers), notifications.standard.{channel} - order confirmations, shipping updates (5 min SLA), notifications.promotional.{channel} - marketing emails, newsletters (30 min SLA), notifications.bulk.{channel} - batch campaigns (low priority), notifications.retry - failed deliveries with exponential backoff"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Notification Queue Processing & Provider Routing",
                    "steps": [
                      "Notification Queue consumers (Dlvy Cons): Consumer groups subscribe to Kafka topics, Email consumers: 20 instances consume 'notifications.*.email' (critical: 10, standard: 20, promotional: 5), SMS consumers: 15 instances for 'notifications.*.sms', Push consumers: 10 instances for 'notifications.*.push'",
                      "Consumer receives message: Consumes from 'notifications.critical.email', Message: {notification_id: 'NOTIF789', channel: 'EMAIL', payload: {subject, body, to: 'alice@example.com'}, priority: 'high'}",
                      "Idempotency check (prevent duplicates): Check delivery log: SELECT COUNT(*) FROM delivery_status WHERE notification_id='NOTIF789' AND channel='EMAIL', if exists \u2192 skip (already processed), Kafka may deliver duplicate messages (at-least-once), idempotency prevents sending duplicate emails",
                      "Rate limiting (ABC - provider limits): Token bucket algorithm with Redis, Bucket key: rate_limit:email:sendgrid, Capacity: 100 tokens (emails), Refill: 100 tokens/sec (SendGrid limit), Atomic check: Lua script: check tokens >= 1 \u2192 DECR token count \u2192 proceed, else wait, Prevents overwhelming provider (Twilio 100 SMS/sec, SendGrid 50 email/sec, FCM 10K push/sec), Backpressure: If rate limited \u2192 consumer slows down, Kafka buffers messages (millions of messages capacity)",
                      "Provider routing: Based on channel: EMAIL \u2192 Email Svc Provider (SendGrid/AmazonSES), SMS \u2192 SMS Svc Provider (Twilio/MSG91), Push \u2192 inAPP Svc Provider (FCM for Android, APNS for iOS), OTP \u2192 OTP Svc (dedicated, high priority)"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Email Delivery (SendGrid/AmazonSES)",
                    "steps": [
                      "Email Svc Provider: Render final email: HTML body with CSS styling, Plain text fallback, Attachments (if any), Unsubscribe link (for promotional emails, CAN-SPAM compliance)",
                      "Send via SendGrid: POST https://api.sendgrid.com/v3/mail/send with {personalizations: [{to: [{email: 'alice@example.com'}]}], from: {email: 'noreply@company.com', name: 'Company Name'}, subject: 'Order ORD456 confirmed', content: [{type: 'text/html', value: '<html>...'}]}, SendGrid response: 202 Accepted {id: 'msg_abc123'} (async, doesn't mean delivered yet)",
                      "Update status: UPDATE notifications SET status='SENT', sent_at=now() WHERE notification_id='NOTIF789', INSERT INTO delivery_status (delivery_id, notification_id: 'NOTIF789', channel: 'EMAIL', provider: 'SendGrid', provider_message_id: 'msg_abc123', status: 'SENT', sent_at: now())",
                      "Webhooks (delivery confirmation): SendGrid sends webhooks: POST /webhooks/sendgrid with {event: 'delivered', sg_message_id: 'msg_abc123', timestamp}, Event types: delivered (email reached inbox), opened (user opened email, tracked via 1px image), clicked (user clicked link), bounced (email bounced, invalid address), spam (marked as spam), Update: UPDATE delivery_status SET status='DELIVERED', delivered_at=now() WHERE provider_message_id='msg_abc123'"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: SMS Delivery (Twilio/MSG91)",
                    "steps": [
                      "SMS Svc Provider: Format message (160 chars limit for single SMS, longer messages split), Add opt-out instructions: 'Reply STOP to unsubscribe' (TCPA compliance)",
                      "Send via Twilio: POST https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json with {To: '+919876543210', From: '+12345678901', Body: 'Hi Alice, your order ORD456 has been confirmed. Track: https://track.link/ORD456'}, Twilio response: {sid: 'SM1234abcd', status: 'queued'} (Twilio queues message for delivery)",
                      "Update status: UPDATE notifications SET status='SENT', INSERT INTO delivery_status (notification_id: 'NOTIF789', channel: 'SMS', provider: 'Twilio', provider_message_id: 'SM1234abcd', status: 'SENT', sent_at: now())",
                      "Twilio webhooks: POST /webhooks/twilio with {MessageSid: 'SM1234abcd', MessageStatus: 'delivered'}, Statuses: sent (to carrier), delivered (to phone), failed (delivery failed), Update: UPDATE delivery_status SET status='DELIVERED' WHERE provider_message_id='SM1234abcd'"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Push Notification (FCM/APNS)",
                    "steps": [
                      "inAPP Svc Provider (FCM for Android, APNS for iOS): Fetch device tokens: SELECT device_tokens FROM users WHERE user_id='user123', device_tokens: ['fcm_token_android_123', 'apns_token_ios_456'] (user has multiple devices: phone, tablet)",
                      "FCM (Firebase Cloud Messaging - Android/Web): POST https://fcm.googleapis.com/v1/projects/{project_id}/messages:send with {message: {token: 'fcm_token_android_123', notification: {title: 'Order Confirmed', body: 'Your order ORD456 has been confirmed'}, data: {order_id: 'ORD456', deep_link: 'app://orders/ORD456'}, android: {priority: 'high', notification: {sound: 'default', click_action: 'OPEN_ORDER'}}}}, FCM response: {name: 'projects/.../messages/0:1234567890'} (success)",
                      "APNS (Apple Push Notification Service - iOS): POST https://api.push.apple.com/3/device/{apns_token_ios_456} with HTTP/2 headers: {apns-topic: 'com.company.app', apns-priority: 10}, Body: {aps: {alert: {title: 'Order Confirmed', body: 'Your order ORD456 has been confirmed'}, badge: 1, sound: 'default'}, order_id: 'ORD456'}, APNS response: 200 OK (success) or 410 Gone (token invalid, device uninstalled app)",
                      "Multi-device fanout: Send to all 3 tokens (Android phone, iOS tablet, web browser), User receives notification on all devices, INSERT INTO delivery_status for each device (3 records: FCM Android, APNS iOS, FCM Web)",
                      "Handling failures: Invalid token (device uninstalled app): FCM returns error: 'invalid-registration-token', Remove token: UPDATE users SET device_tokens = array_remove(device_tokens, 'fcm_token_android_123'), Retry: Don't retry for invalid tokens (permanent failure)"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: OTP Delivery (High Priority)",
                    "steps": [
                      "OTP Svc (dedicated for time-sensitive notifications): Generate OTP: 6-digit code (123456), Store in Redis: SET otp:user123:login '123456' EX 300 (5 min TTL, expires after 5 minutes)",
                      "Create notification: POST /api/v1/notifications with {templateId: 'otp_login', recipientId: 'user123', variables: {otp: '123456'}, channels: ['sms'], priority: 'critical'}, Priority: 'critical' (highest priority, bypasses normal queue)",
                      "Fast-track delivery: Publish to 'notifications.critical.sms' (dedicated consumer group: 20 workers vs 10 for standard), No rate limiting for OTP (critical path, can't wait), Delivery target: <10 seconds (user waiting on login screen)",
                      "Track delivery: User enters OTP within 5 minutes, Verify: GET otp:user123:login from Redis, if matches \u2192 login success, if expired (TTL reached) \u2192 prompt resend OTP"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Retry Mechanism (Failed Deliveries)",
                    "steps": [
                      "Failure scenarios: Provider timeout (Twilio didn't respond in 10 sec), Rate limit 429 (hit provider limit, too many requests), Network error (connection refused), Invalid recipient (email bounced, phone number invalid)",
                      "Retry strategy: If temporary failure (timeout, rate limit, network error): Exponential backoff: 1st retry after 1 min, 2nd retry after 5 min, 3rd retry after 15 min, Max 3 attempts over 30 minutes, Publish to 'notifications.retry' Kafka topic with retry_count",
                      "Retry consumer: Consumes 'notifications.retry', Checks retry_count: if retry_count < 3 \u2192 attempt redelivery, if retry_count >= 3 \u2192 move to DLQ (Dead Letter Queue), DLQ: 'notifications.dlq' for manual investigation, Alert ops team for persistent failures",
                      "Permanent failures (don't retry): Invalid recipient (550 User not found for email, invalid phone number for SMS), Unsubscribed user (user opted out, honor preference), Token expired (APNS 410 Gone, device uninstalled app), Mark: UPDATE delivery_status SET status='FAILED', error_message='Invalid email address'"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Notifications (main table)",
                    "pairs": [
                      {
                        "k": "notification_id",
                        "v": "uuid PRIMARY KEY (unique identifier across system)"
                      },
                      {
                        "k": "user_id / recipient_id",
                        "v": "uuid (recipient, for position tracking)"
                      },
                      {
                        "k": "external_user_id",
                        "v": "varchar(255) (client's user ID, e.g., Amazon customer ID)"
                      },
                      {
                        "k": "template_id",
                        "v": "uuid FK \u2192 Templates"
                      },
                      {
                        "k": "channel",
                        "v": "enum (EMAIL, SMS, PUSH, INAPP)"
                      },
                      {
                        "k": "payload",
                        "v": "jsonb (rendered content: {subject, body, to, variables})"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, SCHEDULED, SENT, DELIVERED, FAILED, CANCELLED)"
                      },
                      {
                        "k": "priority",
                        "v": "enum (critical/high, normal, low)"
                      },
                      {
                        "k": "scheduled_at",
                        "v": "timestamptz (nullable, for scheduled notifications)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb (additional data: campaign_id, tags)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (user_id, created_at), INDEX on (status, scheduled_at), INDEX on (template_id)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Notifications_outbox (transactional outbox pattern)",
                    "pairs": [
                      {
                        "k": "outbox_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "notification_id",
                        "v": "uuid FK \u2192 Notifications"
                      },
                      {
                        "k": "event_type",
                        "v": "varchar(50) (e.g., 'notification.created')"
                      },
                      {
                        "k": "payload",
                        "v": "jsonb (notification details for Kafka)"
                      },
                      {
                        "k": "published",
                        "v": "boolean DEFAULT false (CDC polling checks this)"
                      },
                      {
                        "k": "published_at",
                        "v": "timestamptz (nullable, when published to Kafka)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "Purpose",
                        "v": "Ensures at-least-once delivery - if DB commit succeeds, message will eventually be published to Kafka"
                      },
                      {
                        "k": "CDC/Publisher",
                        "v": "Polls WHERE published=false, publishes to Kafka, marks published=true"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Templates",
                    "pairs": [
                      {
                        "k": "Composite PK",
                        "v": "(template_id, version) - immutable versioning"
                      },
                      {
                        "k": "template_id",
                        "v": "uuid (same ID across versions)"
                      },
                      {
                        "k": "version",
                        "v": "int (starts at 1, increments on update)"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255) (e.g., 'Order Confirmation Email')"
                      },
                      {
                        "k": "channel",
                        "v": "enum (EMAIL, SMS, PUSH, INAPP)"
                      },
                      {
                        "k": "subject",
                        "v": "text (for EMAIL, can contain {{variables}})"
                      },
                      {
                        "k": "body",
                        "v": "text (template content with {{variable}} placeholders)"
                      },
                      {
                        "k": "variables",
                        "v": "jsonb (array of required variables: ['name', 'order_id'])"
                      },
                      {
                        "k": "is_active",
                        "v": "boolean (only active templates can be used)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "Purpose",
                        "v": "Immutable versions - on update, create new version (audit trail, rollback capability, A/B testing)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "User_preferences (UserPref DB)",
                    "pairs": [
                      {
                        "k": "Composite PK",
                        "v": "(client_id, external_user_id) - client's user identifier"
                      },
                      {
                        "k": "client_id",
                        "v": "varchar(100) (e.g., 'amazon', 'uber')"
                      },
                      {
                        "k": "external_user_id",
                        "v": "varchar(255) (client's user ID)"
                      },
                      {
                        "k": "channels",
                        "v": "jsonb ({email: true, sms: false, push: true, inapp: true})"
                      },
                      {
                        "k": "types",
                        "v": "jsonb ({promotional: false, transactional: true, alerts: true})"
                      },
                      {
                        "k": "do_not_disturb",
                        "v": "boolean DEFAULT false (DND mode, block all notifications)"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "Enforcement",
                        "v": "Check before sending - if email:false, skip email channel; if promotional:false, skip promo notifications"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Delivery_status (delivery tracking)",
                    "pairs": [
                      {
                        "k": "delivery_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "notification_id",
                        "v": "uuid FK \u2192 Notifications (INDEXED for quick lookup)"
                      },
                      {
                        "k": "channel",
                        "v": "enum (EMAIL, SMS, PUSH, INAPP)"
                      },
                      {
                        "k": "provider",
                        "v": "varchar(50) (SendGrid, Twilio, FCM, APNS)"
                      },
                      {
                        "k": "provider_message_id",
                        "v": "varchar(255) (external ID from provider, e.g., Twilio SID)"
                      },
                      {
                        "k": "provider_response",
                        "v": "jsonb (full response from provider for debugging)"
                      },
                      {
                        "k": "status",
                        "v": "enum (SENT, DELIVERED, FAILED, BOUNCED, OPENED, CLICKED)"
                      },
                      {
                        "k": "sent_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "delivered_at",
                        "v": "timestamptz (nullable)"
                      },
                      {
                        "k": "error_message",
                        "v": "text (nullable, failure reason)"
                      },
                      {
                        "k": "retry_count",
                        "v": "int DEFAULT 0 (number of retry attempts)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (notification_id), INDEX on (provider_message_id) for webhook lookups"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis Cache",
                    "pairs": [
                      {
                        "k": "user_pref:{userId}",
                        "v": "STRING (JSON) - cached user preferences, TTL 24 hours (86400 sec)"
                      },
                      {
                        "k": "template:{templateId}:{version}",
                        "v": "STRING (JSON) - cached templates, TTL 1 hour (3600 sec), 95% hit rate"
                      },
                      {
                        "k": "rate_limit:{channel}:{provider}",
                        "v": "INT - token bucket for rate limiting (e.g., rate_limit:email:sendgrid \u2192 100 tokens)"
                      },
                      {
                        "k": "otp:{userId}:{purpose}",
                        "v": "STRING - OTP codes, TTL 300 sec (5 min expiry)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Kafka Topics (prioritized queues)",
                    "pairs": [
                      {
                        "k": "notifications.critical.{channel}",
                        "v": "OTP, security alerts, fraud alerts - processed immediately, dedicated consumers (10-20 workers)"
                      },
                      {
                        "k": "notifications.standard.{channel}",
                        "v": "Order confirmations, shipping updates, receipts - 5 min SLA, 20-30 workers"
                      },
                      {
                        "k": "notifications.promotional.{channel}",
                        "v": "Marketing emails, newsletters, campaigns - 30 min SLA, 5-10 workers (rate limited)"
                      },
                      {
                        "k": "notifications.bulk.{channel}",
                        "v": "Batch campaigns (100K+ recipients) - low priority, rate limited"
                      },
                      {
                        "k": "notifications.retry",
                        "v": "Failed notifications with exponential backoff (1min, 5min, 15min)"
                      },
                      {
                        "k": "user.preference",
                        "v": "User preference update events - consumed by User Pref Consumer, updates cache"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "8. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Transactional Outbox pattern - Ensures at-least-once delivery (DB + Kafka write atomic), prevents lost notifications, CDC polls outbox \u2192 publishes to Kafka \u2192 marks published",
                  "Technique 2: Prioritized Kafka topics - 4 priority levels (critical/standard/promotional/bulk), critical: OTP processed immediately (<10s), promotional: 30 min SLA, worker allocation: 20/30/10/5 for crit/std/promo/bulk",
                  "Technique 3: Rate limiting with token bucket - Redis atomic Lua script (check tokens >= 1 \u2192 DECR \u2192 proceed), provider limits: Twilio 100 SMS/sec, SendGrid 50 email/sec, prevents overwhelming providers, backpressure to Kafka",
                  "Technique 4: Template caching - Redis cache (template:{id}:{version}, 1 hour TTL), 95% hit rate, immutable versioning (create new version on update, never modify existing), enables A/B testing + rollback",
                  "Technique 5: User preference caching - Redis cache (user_pref:{userId}, 24 hour TTL), check before sending (if email:false \u2192 skip email), Kafka consumer updates cache on preference change",
                  "Technique 6: Provider failover - Multi-provider strategy (primary: SendGrid, fallback: AmazonSES for email), if SendGrid fails/rate limited \u2192 route to AmazonSES, circuit breaker pattern (if failure rate >50% \u2192 open circuit, use fallback)",
                  "Technique 7: Idempotent consumers - Check delivery_status before sending (if exists \u2192 skip), Kafka at-least-once delivery may send duplicates, prevents duplicate emails/SMS to users",
                  "Technique 8: Database partitioning - Notifications table partitioned by created_at (monthly: notifications_2026_01, notifications_2026_02), delivery_status partitioned by sent_at, old partitions archived to S3 (6 month retention)",
                  "Technique 9: Batch processing for bulk notifications - Bulk campaigns (100K+ recipients): Batch insert to notifications table (1000 records at a time), SendGrid bulk API (1000 emails per call vs 1 email per call), reduces API calls 1000\u00d7",
                  "Technique 10: Kafka consumer auto-scaling - Auto-scale based on lag (if lag > 1000 messages \u2192 add consumer instances), 100 partitions enables 100 parallel consumers, throughput scales linearly",
                  "Technique 11: Webhook processing - Async webhook handling (SendGrid/Twilio webhooks \u2192 update delivery_status), don't block provider response (respond 200 OK immediately, process later), batched DB updates (update 100 records at once)",
                  "Technique 12: Reporting aggregation - Real-time: Redis counters (INCR sent_count:{date}, HINCRBY stats:{template_id} delivered 1), Batch: Hourly aggregation (Spark/Flink): SELECT template_id, COUNT(*) FROM delivery_status GROUP BY template_id, Store in InfluxDB/BigQuery for dashboards"
                ]
              },
              {
                "id": "interview-qa",
                "title": "9. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you ensure no notification is lost and implement at-least-once delivery guarantee?",
                    "a": "At-least-once delivery with Transactional Outbox pattern: (1) Problem with dual writes: Naive approach: INSERT notification to DB, then publish to Kafka, Issues: If Kafka publish succeeds but DB write fails \u2192 notification lost (no record in DB), If DB write succeeds but Kafka publish fails \u2192 notification stuck (never sent), Network failures, service crashes can cause inconsistency. (2) Outbox pattern solution: Single transaction: BEGIN TRANSACTION, INSERT INTO notifications (notification_id, user_id, template_id, channel, payload, status: 'PENDING', priority, created_at), INSERT INTO notifications_outbox (outbox_id, notification_id, event_type: 'notification.created', payload, published: false, created_at), COMMIT (atomic - both succeed or both fail). (3) CDC/Publisher service: Polls outbox table: SELECT * FROM notifications_outbox WHERE published=false ORDER BY created_at LIMIT 1000 (batch processing), For each unpublished record: Determine Kafka topic by priority + channel: high + email \u2192 'notifications.critical.email', normal + sms \u2192 'notifications.standard.sms', Publish: Kafka.send(topic, key: notification_id, value: payload), If successful: UPDATE notifications_outbox SET published=true, published_at=now() WHERE outbox_id={id}, If Kafka fails: published stays false, retry on next poll (every 100ms), Guarantees: If DB COMMIT succeeded \u2192 message in outbox \u2192 will be published eventually (retries until success). (4) At-least-once semantics: Outbox \u2192 Kafka: at-least-once (may publish same message twice if CDC crashes after Kafka success but before marking published), Kafka \u2192 Consumer: at-least-once (Kafka may redeliver messages on consumer restart), Idempotency: Consumer checks delivery_status: SELECT COUNT(*) WHERE notification_id={id} AND channel={channel}, if exists \u2192 skip (already sent), prevents duplicate emails/SMS to users. (5) Exactly-once = at-least-once + idempotency: Outbox ensures message never lost, Idempotent consumers ensure message processed only once, Result: Notification sent exactly once to user, even with failures/retries. (6) Failure handling: CDC Publisher crashes: Restarts, polls outbox again, republishes unsent messages (published=false), Kafka partition leader fails: Kafka auto-elects new leader, CDC Publisher retries automatically, Consumer crashes: Kafka redelivers message to another consumer (consumer group), idempotency check prevents duplicate send. (7) Production example: User places order \u2192 Order Service calls Notification API, Outbox ensures order confirmation email created (DB persisted), CDC Publisher sends to Kafka within 100ms, Email consumer delivers via SendGrid within 5 sec, Even if any component crashes: notification eventually sent (retries until success). Result: Zero notification loss, at-least-once delivery guaranteed, idempotent consumers prevent duplicates, resilient to failures (services, network, Kafka, providers)."
                  },
                  {
                    "q": "How do you handle rate limiting when sending millions of notifications to prevent overwhelming external providers?",
                    "a": "Rate limiting with Token Bucket algorithm and Redis: (1) Provider limits: Twilio SMS: 100 messages/sec per account, SendGrid Email: 50 emails/sec (free tier), 10K/sec (paid), FCM Push: 10K messages/sec, APNS: No documented limit but best practice <5K/sec. (2) Token Bucket algorithm: Concept: Bucket holds tokens (e.g., 100 tokens), each send consumes 1 token, tokens refill at fixed rate (100 tokens/sec), if bucket empty \u2192 wait until refill. (3) Redis implementation: Bucket key: rate_limit:email:sendgrid, Capacity: 100 tokens (max burst), Refill rate: 100 tokens/sec (matches SendGrid limit), Atomic Lua script (ensures thread-safety): local key = KEYS[1], local capacity = tonumber(ARGV[1]), local refill_rate = tonumber(ARGV[2]), local tokens = redis.call('GET', key) or capacity, local now = redis.call('TIME'), if tokens >= 1 then redis.call('DECR', key), return 1 (proceed), else return 0 (rate limited, wait), end, redis.call('EXPIRE', key, 10) (reset bucket after 10 sec idle). (4) Consumer flow: Consumer receives notification from Kafka, Before sending: Check rate limit: result = redis.eval(lua_script, 'rate_limit:email:sendgrid', 100, 100), if result = 1 (tokens available): Send email via SendGrid, else (rate limited): Sleep 100ms, retry (busy wait), OR push back to Kafka (better - allows other consumers to process other messages). (5) Backpressure to Kafka: If all consumers rate limited (provider at max capacity), Consumers slow down (sleep/retry), Kafka buffers messages (millions of messages capacity), Message lag increases (visible in monitoring), Auto-scaling: If lag > 1000 \u2192 add more consumers (but still subject to provider rate limit), If provider limit reached: Can't send faster (bottleneck), queue grows, eventual delivery (when capacity available). (6) Multi-provider strategy: Primary: SendGrid (50 emails/sec), Fallback: AmazonSES (50 emails/sec), Total capacity: 100 emails/sec (both providers), Load balancing: Round-robin or weighted (SendGrid 70%, SES 30%), If SendGrid rate limited: Route to SES (circuit breaker pattern). (7) Priority handling: Critical notifications (OTP): Bypass rate limiter (can't wait, time-sensitive), OR dedicated rate bucket (critical_rate_limit:sms:twilio with higher limit), Standard/Promotional: Subject to rate limit (acceptable delay). (8) Batching optimization: SendGrid bulk API: Send 1000 emails in single API call (vs 1 email per call), Effective rate: 50 API calls/sec \u00d7 1000 emails/call = 50K emails/sec, Reduces API overhead, increases throughput 1000\u00d7. (9) Monitoring: Track: Tokens available (should not hit 0 frequently), Rate limit events (how often consumers wait), Provider response times (detect slowdowns), Alert: If rate limit hit > 10% of time \u2192 increase capacity (upgrade provider tier, add fallback). Result: Providers never overwhelmed (respects rate limits), backpressure prevents system overload, multi-provider increases capacity, batching optimizes throughput, critical notifications prioritized."
                  },
                  {
                    "q": "Walk through complete notification flow from creation to delivery with all state transitions and failure handling.",
                    "a": "Complete notification flow (Order Confirmation Email): (1) Trigger: User places order, Order Service calls: POST /api/v1/notifications {templateId: 'order_confirmation', recipientId: 'user123', variables: {name: 'Alice', order_id: 'ORD456', amount: '$49.99'}, channels: ['email', 'push'], priority: 'normal'}. (2) Notification Svc validation: Authenticate request (API key/JWT), Fetch template: SELECT * FROM templates WHERE template_id='order_confirmation' AND is_active=true, template: {subject: 'Order {{order_id}} confirmed', body: 'Hi {{name}}, your order {{order_id}} for {{amount}} has been confirmed...'}, Validate variables: Check {name, order_id, amount} present, if missing \u2192 400 error, Fetch user preferences: Redis GET user_pref:user123 \u2192 {channels: {email: true, push: true}, types: {transactional: true}}, Check: email enabled \u2713, transactional enabled \u2713 (order confirmation is transactional). (3) Render template: Replace: subject: 'Order ORD456 confirmed', body: 'Hi Alice, your order ORD456 for $49.99 has been confirmed...', Generate notification_id: 'NOTIF789'. (4) Transactional write (Outbox pattern): BEGIN TRANSACTION, INSERT notifications: (notification_id: 'NOTIF789', user_id: 'user123', template_id, channel: 'EMAIL', payload: {subject, body, to: 'alice@example.com'}, status: 'PENDING', priority: 'normal', created_at: now()), INSERT notifications_outbox: (outbox_id, notification_id: 'NOTIF789', event_type: 'notification.created', payload, published: false), COMMIT, Response: 200 OK {notificationId: 'NOTIF789', status: 'PENDING'}. (5) CDC/Publisher polling: SELECT * FROM notifications_outbox WHERE published=false (polls every 100ms), Finds: {outbox_id, notification_id: 'NOTIF789', payload, published: false}, Publish: Kafka.send('notifications.standard.email', key: 'NOTIF789', value: payload), Update: notifications_outbox SET published=true, published_at=now(). (6) Email Consumer: Subscribes to 'notifications.standard.email', Receives: {notification_id: 'NOTIF789', channel: 'EMAIL', payload: {subject, body, to}}, Idempotency: SELECT COUNT(*) FROM delivery_status WHERE notification_id='NOTIF789' AND channel='EMAIL', if 0 \u2192 proceed (first time), Rate limit: Check Redis token bucket: rate_limit:email:sendgrid, if tokens >= 1 \u2192 DECR, proceed, Send: POST SendGrid API {to: 'alice@example.com', subject, content: body}, Response: 202 Accepted {id: 'msg_sg123'}. (7) Update status: UPDATE notifications SET status='SENT', sent_at=now(), INSERT delivery_status: (delivery_id, notification_id: 'NOTIF789', channel: 'EMAIL', provider: 'SendGrid', provider_message_id: 'msg_sg123', status: 'SENT', sent_at: now()). (8) SendGrid webhook (async): Minutes later: POST /webhooks/sendgrid {event: 'delivered', sg_message_id: 'msg_sg123', email: 'alice@example.com', timestamp}, Webhook handler: UPDATE delivery_status SET status='DELIVERED', delivered_at=now() WHERE provider_message_id='msg_sg123', UPDATE notifications SET status='DELIVERED'. (9) Push notification (parallel): Same flow for channel='PUSH', Consumer: Push consumer group, Send: FCM.send({token: user_device_token, notification: {title: 'Order Confirmed', body: 'Your order ORD456...'}}), Multi-channel: Email + Push sent independently (both succeed or fail independently). (10) Failure scenario (temporary): SendGrid timeout (10 sec no response), Consumer: Catch exception, Publish to 'notifications.retry': {notification_id: 'NOTIF789', channel: 'EMAIL', retry_count: 1, scheduled_at: now() + 1 min}, Retry consumer: Waits 1 min, reattempts send, If fails again: retry_count: 2, scheduled_at: now() + 5 min (exponential backoff), Max 3 retries: If all fail \u2192 move to DLQ (notifications.dlq), alert ops team. (11) Failure scenario (permanent): Email bounced (invalid address): SendGrid webhook: {event: 'bounce', reason: 'Invalid email'}, UPDATE delivery_status SET status='FAILED', error_message='Email bounced: invalid address', UPDATE notifications SET status='FAILED', Don't retry (permanent failure), Optionally: Notify sender (Order Service) that email failed. (12) State transitions: PENDING (created, in outbox) \u2192 published to Kafka, SENT (delivered to provider, awaiting confirmation), DELIVERED (provider confirmed delivery to recipient), FAILED (permanent failure, don't retry), CANCELLED (user cancelled notification before sending). Result: Reliable delivery with state tracking, retry for temporary failures, idempotency prevents duplicates, webhooks confirm final delivery, multi-channel support, complete audit trail."
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Transactional Outbox pattern mandatory for reliable notifications. NEVER publish to Kafka without DB write in same transaction. If Kafka succeeds but DB fails \u2192 notification lost. Outbox guarantees: if DB commit succeeded, message will EVENTUALLY be published (CDC polling + retry)."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How prevent notification loss?'. Answer: Outbox pattern (single transaction: INSERT notifications + INSERT outbox \u2192 COMMIT). CDC polls outbox (published=false) \u2192 publishes to Kafka \u2192 marks published=true. At-least-once delivery + idempotent consumers = exactly-once to user."
              },
              {
                "type": "tip",
                "text": "Prioritized Kafka topics: notifications.critical.{channel} (OTP, <10s), notifications.standard.{channel} (orders, 5min), notifications.promotional.{channel} (marketing, 30min), notifications.bulk (campaigns). Worker allocation: 20/30/10/5. Critical bypasses rate limiting."
              },
              {
                "type": "interview-favorite",
                "text": "Rate limiting: Token bucket with Redis Lua script (atomic check + DECR). Provider limits: Twilio 100/sec, SendGrid 50/sec, FCM 10K/sec. Backpressure: consumers slow \u2192 Kafka buffers. Multi-provider failover: primary SendGrid \u2192 fallback AmazonSES if rate limited (circuit breaker)."
              },
              {
                "type": "warning",
                "text": "NEVER skip user preference checks. Must honor opt-outs (email:false \u2192 skip email), DND mode (do_not_disturb:true \u2192 skip all), notification types (promotional:false \u2192 skip marketing). Regulatory compliance: GDPR, CAN-SPAM (unsubscribe link), TCPA (SMS opt-in). Cache preferences in Redis (24h TTL)."
              },
              {
                "type": "tip",
                "text": "Template versioning: Immutable - create new version on update (never modify existing). Benefits: (1) Audit trail (know exact content sent), (2) A/B testing (route 50% to v1, 50% to v2), (3) Rollback (reactivate old version). Cache: template:{id}:{version} in Redis (1h TTL, 95% hit rate)."
              },
              {
                "type": "interview-favorite",
                "text": "Idempotent consumers: Check delivery_status (if exists \u2192 skip). Kafka at-least-once may deliver duplicates (consumer restart, network retry). Without idempotency: user receives 5 duplicate emails (bad UX). SELECT COUNT WHERE notification_id + channel (composite uniqueness check)."
              }
            ],
            "tags": [
              "system-design",
              "notification-system",
              "multi-channel",
              "kafka-outbox-pattern",
              "sendgrid",
              "twilio",
              "fcm",
              "apns",
              "rate-limiting",
              "token-bucket",
              "at-least-once-delivery",
              "idempotency",
              "template-versioning",
              "user-preferences"
            ]
          }
        },
        {
          "id": "16",
          "title": "System Design 16: Design Distributed Logging System | Splunk | Logstash | HLD | LLD",
          "videoUrl": "https://youtu.be/cqs5t7zqzCk",
          "description": "In this video, I explain how to design a complete Distributed Logging Platform \u2014 similar to Splunk, OpenObserve, ELK, Datadog, and Grafana Loki. We deep-dive into how logs are collected, processed, enriched, stored, indexed, searched, and monitored in a large-scale distributed system.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/bwouebowbgoqbogweigwoieo.png?updatedAt=1764402032243",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/591859853_25724875320429770_5245894370665601546_n.jpg?updatedAt=1764409028874",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 12,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "Distributed Logging Platform (Splunk/OpenObserve)",
              "oneLinerToMemorize": "Multi-source ingestion \u2192 Kafka buffering \u2192 Apache Flink processing \u2192 Hot/Warm/Cold storage tiering with Elasticsearch"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Ingest logs from multiple sources (FluentBit, OTEL, custom agents) in real-time and batch",
                  "Feature 2: Support both real-time streaming and batch log ingestion with different protocols",
                  "Feature 3: Validate, parse, and normalize logs into a standard structure (JSON/structured format)",
                  "Feature 4: User should be able to see logs in the dashboard in almost near real-time (<5 seconds)",
                  "Feature 5: Advanced search with filters, regex, time-range queries on indexed logs",
                  "Feature 6: Tail logs in real-time with live streaming capabilities",
                  "Feature 7: Generate alerts based on log patterns, thresholds, and anomalies"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Log Volume",
                        "v": "Millions of events per hour, petabytes of log data annually"
                      },
                      {
                        "k": "Ingestion Rate",
                        "v": "100K-1M logs/second sustained throughput"
                      },
                      {
                        "k": "Sources",
                        "v": "Thousands of distributed services/servers"
                      },
                      {
                        "k": "Retention",
                        "v": "Hot: 14 days, Warm: 45 days, Cold: unlimited (S3)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance",
                    "pairs": [
                      {
                        "k": "Latency",
                        "v": "Near real-time ingestion (<5 seconds end-to-end), search <200ms for hot data"
                      },
                      {
                        "k": "Query Performance",
                        "v": "Sub-second for recent logs (14 days), seconds for historical queries"
                      },
                      {
                        "k": "Throughput",
                        "v": "High concurrent writes and reads without degradation"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability",
                    "pairs": [
                      {
                        "k": "Availability",
                        "v": "System should be always available for ingestion - 99.9% uptime"
                      },
                      {
                        "k": "Consistency",
                        "v": "Eventual consistency acceptable (AP in CAP theorem)"
                      },
                      {
                        "k": "Durability",
                        "v": "No data loss - replicated storage with S3 cold backup"
                      },
                      {
                        "k": "Data Lifecycle",
                        "v": "Automated tiering: Hot \u2192 Warm \u2192 Cold with retention policies"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity",
                "type": "bullets",
                "items": [
                  "Entity 1: LogEvent - The actual log entry with timestamp, message, severity, metadata",
                  "Entity 2: IngestionJob - Tracks batch/streaming ingestion tasks from sources",
                  "Entity 3: Source/Agent - Log producers (servers, apps, containers) with unique IDs",
                  "Entity 4: Client/User - Organizations/teams with access control to their log namespaces",
                  "Entity 5: AlertRule - Configured patterns/thresholds that trigger notifications",
                  "Entity 6: SearchQuery - User queries with filters, time ranges, and pagination"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Ingestion APIs (Splunk Universal Forwarder/FluentBit)",
                    "pairs": [
                      {
                        "k": "POST /v1/logs/ingest",
                        "v": "Single log ingestion with JSON payload {timestamp, level, message, metadata}"
                      },
                      {
                        "k": "POST /v1/logs/batch",
                        "v": "Batch ingestion for offline/bulk uploads (up to 10K logs per request)"
                      },
                      {
                        "k": "POST /agents/register",
                        "v": "Register new log source/agent with credentials and metadata"
                      },
                      {
                        "k": "POST /agents/{agentId}/config",
                        "v": "Update agent configuration (sampling rate, filters, buffer size)"
                      },
                      {
                        "k": "POST /agents/login/logs",
                        "v": "Agent authentication and log submission endpoint"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Search APIs (microservices)",
                    "pairs": [
                      {
                        "k": "POST /logs/search (Pagination)",
                        "v": "Search logs with query={text search + regex + filters + time-range}, returns paginated results"
                      },
                      {
                        "k": "POST /logs/tail",
                        "v": "Real-time log tailing with WebSocket/SSE for live streaming"
                      },
                      {
                        "k": "GET /logs/{logId}/status",
                        "v": "Get processing status of specific log entry"
                      },
                      {
                        "k": "WS /logs/tail/*",
                        "v": "WebSocket endpoint for real-time log streaming with filters"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Alert & Management APIs",
                    "pairs": [
                      {
                        "k": "POST /alerts/create",
                        "v": "Create alert rule with conditions and notification channels"
                      },
                      {
                        "k": "GET /alerts/{alertId}",
                        "v": "Retrieve alert configuration and trigger history"
                      },
                      {
                        "k": "POST /agents/{agentId}/status/{requestId}",
                        "v": "Health check and status reporting from agents"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Clients/Agents (FluentBit/OTEL/Custom) \u2192 LB & API Gateway: Entry point with authentication, rate limiting",
                  "Client Onboarding Service \u2192 Client DB: Manages client registration, tokens, namespaces",
                  "File Ingestion Service \u2192 Kafka: Accepts logs via HTTP/gRPC, publishes to Kafka topics (raw_logs)",
                  "Agent Based Service \u2192 Kafka: Handles agent-specific protocols and batching",
                  "Search Service \u2192 Elasticsearch: Queries indexed logs with filtering and aggregation",
                  "Kafka (Buffer) \u2192 Apache Flink: Stream processing for validation, enrichment, deduplication",
                  "Apache Flink \u2192 Log DB (Cassandra/CockroachDB): Writes processed logs to hot storage with clustering by timestamp",
                  "Apache Flink \u2192 Elasticsearch: Real-time indexing for fast search (14 days retention)",
                  "Cron Service \u2192 S3: Archives logs older than 45 days to cold storage, deletes from hot DB"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Client Onboarding",
                    "steps": [
                      "Client sends: POST /agents/register with {client_name, email, metadata}",
                      "Client Onboarding Service validates: Email format, uniqueness, rate limits",
                      "Service creates: Client record in PostgreSQL with {client_id, token (JWT), token_ttl, env, metadata}",
                      "Service provisions: Dedicated namespace/index in Elasticsearch (client_<id>_logs)",
                      "Service returns: {client_id, token, namespace} for agent configuration"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Log Ingestion (Real-time)",
                    "steps": [
                      "Agent sends: POST /v1/logs/ingest with headers {Authorization: Bearer <token>} and body {timestamp, level: 'ERROR', message, service_name, host, trace_id}",
                      "LB & API Gateway validates: Token (JWT signature), rate limit (10K logs/min per client), payload size (<1MB)",
                      "File Ingestion Service performs: Schema validation (required fields present), timestamp normalization (ISO 8601)",
                      "Service enriches: Adds {ingestion_time, client_id, pod_name, namespace} metadata",
                      "Service publishes: To Kafka topic 'raw_logs' with key=client_id for partitioning, returns 202 Accepted immediately"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Stream Processing (Apache Flink)",
                    "steps": [
                      "Flink consumes: From Kafka topic 'raw_logs' with consumer group 'log_processor'",
                      "Flink performs validation: Checks required fields {timestamp, message}, drops malformed logs to DLQ (dead letter queue)",
                      "Flink enriches: Adds geo-location from IP, service tags from registry lookup",
                      "Flink deduplicates: Using 5-minute tumbling window + message hash to detect duplicates",
                      "Flink routes: To multiple sinks - (1) Cassandra for durable storage, (2) Elasticsearch for indexing, (3) Alert topic if matches alert rules"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Storage in Log DB (Cassandra)",
                    "steps": [
                      "Flink writes: Batch of 1000 logs to Cassandra with clustering key (timestamp DESC)",
                      "Cassandra stores: In table logs_by_service with partition key=(service_name, log_date), clustering=(timestamp, log_id)",
                      "Cassandra replication: RF=3 (replication factor) across multiple datacenters for durability",
                      "TTL policy: Automatically deletes logs older than 14 days (hot storage retention)",
                      "Write optimization: Uses write-ahead log (WAL) + memtable for fast writes (10K writes/sec per node)"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Indexing in Elasticsearch",
                    "steps": [
                      "Flink bulk indexes: Batches of 5000 logs to Elasticsearch using _bulk API",
                      "Elasticsearch creates: Documents in index 'logs_<date>' with fields {log_id, timestamp, log_level, message, service_name, host, env, trace_id}",
                      "Elasticsearch indexes: Full-text on 'message', keyword on 'service_name', 'log_level', range on 'timestamp'",
                      "Index lifecycle: ILM (Index Lifecycle Management) rolls over daily, keeps 14 days in hot tier",
                      "Search optimization: Uses inverted index for text search, doc values for aggregations"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Log Search (Real-time)",
                    "steps": [
                      "User sends: POST /logs/search with {query: 'error AND service:payment', time_range: {start: '2025-01-19T00:00:00Z', end: '2025-01-20T00:00:00Z'}, filters: {log_level: ['ERROR', 'FATAL']}, page: 1, size: 100}",
                      "Search Service builds: Elasticsearch DSL query with bool query (must: text match, filter: term + range)",
                      "Elasticsearch executes: Query on hot indices (last 14 days), uses query cache for repeated queries",
                      "Service fetches: Results with highlighting on matched terms, total count for pagination",
                      "Service returns: {logs: [{log_id, timestamp, message, ...}], total: 15234, page: 1, size: 100} in <200ms"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Real-time Log Tailing",
                    "steps": [
                      "User opens: WebSocket connection to WS /logs/tail/* with query parameters {service: 'payment', level: 'ERROR'}",
                      "Search Service subscribes: To Kafka topic 'alert_topic' with filter matching user's criteria",
                      "Kafka streams: New matching logs to WebSocket connection in real-time",
                      "Service sends: Each log event as JSON message over WebSocket with <1s latency",
                      "Connection handling: Auto-reconnect on disconnect, buffer last 100 messages for reconnect recovery"
                    ]
                  },
                  {
                    "blockTitle": "Step 8: Alerting (Alert Service)",
                    "steps": [
                      "Alert Service polls: AlertRule DB for active rules every 10 seconds",
                      "Service subscribes: To Kafka 'alert_topic' which receives logs matching alert patterns from Flink",
                      "Service evaluates: Conditions like 'ERROR count > 100 in 5 minutes' using sliding window aggregation",
                      "Service triggers: Notification to configured channels - Kafka \u2192 Notification Service \u2192 Email/SMS/Pager",
                      "Service deduplicates: Alerts using cooldown period (don't re-alert for same condition within 10 min)"
                    ]
                  },
                  {
                    "blockTitle": "Step 9: Cold Storage Archival (Cron Service)",
                    "steps": [
                      "Cron Service runs: Daily job at 2 AM UTC to archive logs older than 45 days",
                      "Service queries: Cassandra for logs_by_traceid WHERE timestamp < (now - 45 days)",
                      "Service exports: Logs to S3 in Parquet format with compression (Snappy) for efficient storage",
                      "S3 organization: Partitioned by s3://logs-archive/{year}/{month}/{day}/{service_name}/logs.parquet",
                      "Service deletes: Archived logs from Cassandra to free up disk space, keeps S3 copy indefinitely",
                      "Recovery: Historical queries (>45 days) route to S3 using Athena/Presto for querying"
                    ]
                  }
                ]
              },
              {
                "id": "client-components",
                "title": "7. Agent/Client-Side Components",
                "type": "bullets",
                "items": [
                  "Component 1: Log Collector (FluentBit/OTEL) - Installed on servers/containers to collect logs from files, syslog, stdout",
                  "Component 2: Parser - Transforms logs from various formats (JSON, syslog, custom) to standard schema",
                  "Component 3: Buffer - Local disk/memory buffer to handle network failures, retry with exponential backoff",
                  "Component 4: Batch Aggregator - Batches logs (100 logs or 10 seconds window) to reduce API calls",
                  "Component 5: Authentication Module - Manages JWT token refresh, API key rotation",
                  "Component 6: Sampling - Reduces log volume by sampling (e.g., send 10% of DEBUG logs, 100% of ERROR logs)"
                ]
              },
              {
                "id": "database-schema",
                "title": "8. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "ClientDB (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "client_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "client_name",
                        "v": "varchar(255) UNIQUE"
                      },
                      {
                        "k": "token",
                        "v": "text (JWT token for authentication)"
                      },
                      {
                        "k": "token_ttl",
                        "v": "timestamp (token expiration time)"
                      },
                      {
                        "k": "env",
                        "v": "varchar(50) (production, staging, dev)"
                      },
                      {
                        "k": "pref",
                        "v": "jsonb (preferences: retention, sampling rate)"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb (custom client metadata)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "logs_by_service (Cassandra - Hot Storage 14 days)",
                    "pairs": [
                      {
                        "k": "service_name",
                        "v": "text (partition key - distributes data)"
                      },
                      {
                        "k": "log_date",
                        "v": "date (partition key - enables time-based partitioning)"
                      },
                      {
                        "k": "timestamp",
                        "v": "timestamp (clustering key DESC - sorts logs newest first)"
                      },
                      {
                        "k": "log_id",
                        "v": "uuid (clustering key - uniqueness)"
                      },
                      {
                        "k": "log_level",
                        "v": "text (DEBUG, INFO, WARN, ERROR, FATAL)"
                      },
                      {
                        "k": "message",
                        "v": "text (actual log message)"
                      },
                      {
                        "k": "host",
                        "v": "text (hostname or pod name)"
                      },
                      {
                        "k": "trace_id",
                        "v": "text (distributed tracing correlation)"
                      },
                      {
                        "k": "metadata",
                        "v": "map<text, text> (additional fields)"
                      },
                      {
                        "k": "TTL",
                        "v": "14 days (automatic expiration)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "logs_by_traceid (Cassandra - For trace correlation)",
                    "pairs": [
                      {
                        "k": "trace_id",
                        "v": "text PRIMARY KEY"
                      },
                      {
                        "k": "timestamp",
                        "v": "timestamp (clustering DESC)"
                      },
                      {
                        "k": "log_id",
                        "v": "uuid"
                      },
                      {
                        "k": "service_name",
                        "v": "text"
                      },
                      {
                        "k": "log_level",
                        "v": "text"
                      },
                      {
                        "k": "message",
                        "v": "text"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch Index (logs_<date>)",
                    "pairs": [
                      {
                        "k": "log_id",
                        "v": "keyword (unique identifier)"
                      },
                      {
                        "k": "timestamp",
                        "v": "date (indexed for range queries)"
                      },
                      {
                        "k": "log_level",
                        "v": "keyword (exact match filtering)"
                      },
                      {
                        "k": "message",
                        "v": "text (full-text search with analyzer)"
                      },
                      {
                        "k": "service_name",
                        "v": "keyword (aggregations, filtering)"
                      },
                      {
                        "k": "host",
                        "v": "keyword"
                      },
                      {
                        "k": "env",
                        "v": "keyword (production, staging)"
                      },
                      {
                        "k": "namespace",
                        "v": "keyword (Kubernetes namespace)"
                      },
                      {
                        "k": "pod_name",
                        "v": "keyword"
                      },
                      {
                        "k": "trace_id",
                        "v": "keyword (distributed tracing)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "AlertRule (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "alert_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "client_id",
                        "v": "uuid FK \u2192 ClientDB"
                      },
                      {
                        "k": "rule_name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "condition",
                        "v": "jsonb ({field: 'log_level', operator: '=', value: 'ERROR', count: 100, window: '5m'})"
                      },
                      {
                        "k": "notification_channels",
                        "v": "jsonb (['email', 'slack', 'pagerduty'])"
                      },
                      {
                        "k": "is_active",
                        "v": "boolean"
                      },
                      {
                        "k": "cooldown_period",
                        "v": "interval (10 minutes - prevent alert spam)"
                      },
                      {
                        "k": "last_triggered",
                        "v": "timestamp"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "additional-mechanism",
                "title": "9. Data Lifecycle & Tiering Mechanism",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Hot-Warm-Cold Storage Tiering",
                    "steps": [
                      "Hot Tier (0-14 days): Logs stored in Cassandra + Elasticsearch for fast reads/writes. SSD storage, high IOPS. Used for real-time search and dashboards.",
                      "Warm Tier (14-45 days): Logs remain in Cassandra but removed from Elasticsearch. Slower queries via direct DB access. HDD storage for cost optimization.",
                      "Cold Tier (>45 days): Logs archived to S3 in Parquet format. Queried via Athena/Presto for historical analysis. Glacier storage for ultra-low-cost retention.",
                      "Transition Logic: Cron job runs daily, checks log age, moves data between tiers automatically based on timestamp.",
                      "Cost Optimization: Hot=$0.10/GB/month, Warm=$0.05/GB/month, Cold=$0.01/GB/month - saves 90% on storage for old logs"
                    ]
                  },
                  {
                    "blockTitle": "Retention Policy Enforcement",
                    "steps": [
                      "Trigger: Configured per client in ClientDB preferences (e.g., retain_days=90)",
                      "Processing: Cron service queries logs older than retention period",
                      "Deletion: Soft delete (mark as deleted) for 7 days, then hard delete from all tiers",
                      "Compliance: Supports GDPR/data deletion requests by purging specific user logs across all tiers",
                      "Audit: Logs deletion events to audit trail for compliance verification"
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "10. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Kafka Partitioning - Topic 'raw_logs' partitioned by client_id (100 partitions) for parallel processing, each partition handles ~1K msg/sec",
                  "Technique 2: Elasticsearch Sharding - Daily indices with 10 primary shards + 1 replica, auto-scales based on data volume",
                  "Technique 3: Cassandra Clustering - Partition by (service_name, log_date) enables distributed queries, each node handles 10K writes/sec",
                  "Technique 4: Apache Flink Parallelism - Runs with parallelism=32, each task processes subset of Kafka partitions for horizontal scaling",
                  "Technique 5: Index Lifecycle Management (ILM) - Automatically rolls over Elasticsearch indices daily, moves to warm nodes after 7 days",
                  "Technique 6: Batch Processing - Agents batch 100 logs or 10 seconds worth before sending, reduces API calls by 100x",
                  "Technique 7: Compression - Logs compressed with Snappy (3:1 ratio) in Kafka and S3, saves storage and network bandwidth",
                  "Technique 8: Query Cache - Elasticsearch caches frequent queries (60s TTL), improves dashboard load time by 80%",
                  "Technique 9: Connection Pooling - API Gateway maintains 1000 persistent connections to backend services, reduces handshake overhead",
                  "Technique 10: Rate Limiting - Token bucket algorithm limits to 10K logs/min per client, prevents single client from overwhelming system",
                  "Technique 11: Sampling - Agents configured to sample DEBUG logs (10%) but send all ERROR/FATAL (100%), reduces volume by 60%",
                  "Technique 12: Dead Letter Queue (DLQ) - Malformed logs sent to Kafka DLQ topic for manual review, prevents processing pipeline blockage"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "Why use Kafka as a buffer instead of writing directly to the database?",
                    "a": "Kafka provides critical decoupling and resilience: (1) Back-pressure handling - if DB is slow/down, Kafka buffers millions of logs without data loss, (2) Replay capability - can reprocess logs if processing logic changes or fails, (3) Multiple consumers - same log stream feeds Elasticsearch, Cassandra, and Alert service independently, (4) Peak load smoothing - ingestion spikes (10x normal) absorbed by Kafka, downstream processes at steady rate. Example: Black Friday traffic spike - Kafka handles 1M logs/sec burst, Flink processes at 100K/sec, no data loss."
                  },
                  {
                    "q": "How do you handle out-of-order logs with different timestamps?",
                    "a": "Multi-layer approach: (1) Flink Watermarks - uses event time processing with 5-minute watermark delay, allows late-arriving logs to be included in correct time window, (2) Cassandra Clustering - sorts by timestamp within partition, maintains chronological order for queries, (3) Elasticsearch @timestamp - indexed for range queries, UI shows logs in order even if processed out-of-order, (4) Grace Period - Flink accepts logs up to 10 minutes late, drops extremely late logs to DLQ. Trade-off: 5-min watermark delays alerting but ensures 99.9% logs in correct window."
                  },
                  {
                    "q": "What happens if Elasticsearch goes down? Can users still search logs?",
                    "a": "Graceful degradation strategy: (1) Fallback to Cassandra - Search service automatically routes queries to Cassandra, slower (1-2s vs 200ms) but functional, (2) Elasticsearch recovery - Flink queues bulk index requests in Kafka topic 'es_retry', replays when ES recovers, (3) Cache layer - Redis caches recent common queries for 60s, serves during ES downtime, (4) Status notification - UI shows 'Limited search mode' banner, sets user expectations. Cassandra can handle point queries (by service+time) but lacks full-text search capability. Typical ES downtime: <5 minutes with auto-restart."
                  },
                  {
                    "q": "How do you ensure no log data is lost during ingestion?",
                    "a": "Multiple durability guarantees: (1) Kafka persistence - replication factor=3, min.insync.replicas=2, acks=all ensures log written to 2+ brokers before acknowledging, (2) Agent buffering - FluentBit buffers 10MB locally on disk, retries failed sends with exponential backoff (max 24 hours), (3) Flink checkpointing - every 60s, saves processing state to durable storage (S3), can recover exactly-once on failure, (4) Cassandra replication - RF=3 with QUORUM writes, tolerates 1 node failure, (5) S3 archival - immutable cold storage with 11 9's durability. End-to-end guarantee: At-least-once delivery (may have duplicates, handled by deduplication), zero data loss."
                  },
                  {
                    "q": "How does the deduplication mechanism work in Flink?",
                    "a": "Window-based deduplication: (1) Tumbling Window - 5-minute windows, groups logs by time slice, (2) Hash Generation - creates hash from (timestamp_minute, service_name, message_substring[0:100]), (3) State Backend - Flink maintains RocksDB state with hash set of seen hashes within window, (4) Duplicate Detection - if hash exists in state, drop log, else add to state and emit downstream, (5) State Expiration - window closes after 5 min, state cleared to prevent unbounded growth. Handles: duplicate sends from agent retries, load balancer routing same log to multiple endpoints. Trade-off: only deduplicates within 5-min window, cross-window duplicates not detected."
                  },
                  {
                    "q": "What's your data retention and archival strategy?",
                    "a": "Three-tier lifecycle: (1) Hot (0-14 days) - Cassandra + Elasticsearch, SSD storage, <200ms queries, costs $0.10/GB/month, (2) Warm (14-45 days) - Cassandra only (ES deleted), HDD storage, 1-2s queries, costs $0.05/GB/month, (3) Cold (>45 days) - S3 Parquet files, Glacier storage, 10-30s queries via Athena, costs $0.01/GB/month. Cron job runs daily: moves ES indices to warm tier (ILM policy), exports Cassandra to S3, deletes old Cassandra data. Example: 1TB logs/day = $3000/month hot storage vs $300/month blended with tiering. Compression: Snappy compression 3:1, Parquet columnar format enables efficient queries on cold data."
                  },
                  {
                    "q": "How do you handle log search across multiple time ranges efficiently?",
                    "a": "Query routing strategy: (1) Time-based routing - if query range within last 14 days, query Elasticsearch (fast), if 14-45 days, query Cassandra (medium), if >45 days, query S3 via Athena (slow), (2) Parallel execution - for cross-tier queries (e.g., last 60 days), fan out to ES + Cassandra + S3 in parallel, merge results, (3) Elasticsearch query optimization - use date histogram aggregations for time-series, filter context instead of query context for non-scoring filters, (4) Cassandra partition pruning - WHERE clause includes partition key (service_name, log_date) to read minimal partitions, (5) S3 partitioning - Parquet files partitioned by year/month/day, Athena only scans relevant partitions. Example: Query 'last 30 days' = ES (14 days) + Cassandra (16 days) in <500ms vs single tier would be 2-3s."
                  },
                  {
                    "q": "How do you implement real-time log tailing efficiently?",
                    "a": "WebSocket + Kafka Consumer approach: (1) Connection - client opens WebSocket to /logs/tail with filters (service, level), (2) Consumer Group - backend creates dedicated Kafka consumer in unique group (per WebSocket connection), subscribes to 'alert_topic', (3) Filtering - Kafka consumer uses Consumer Interceptor to filter messages matching client's criteria before sending over WebSocket, (4) Backpressure - if client slow to consume, buffer last 100 messages in-memory, drop oldest if buffer full, (5) Heartbeat - ping/pong every 30s to detect disconnects, (6) Reconnect - on disconnect, client stores last received log timestamp, reconnects with 'since' parameter to resume. Scalability: Kafka supports 10K+ consumer groups, each WebSocket is independent, no shared state. Alternative: SSE (Server-Sent Events) for one-way streaming, simpler but no bidirectional communication."
                  },
                  {
                    "q": "What's your alerting strategy to avoid alert fatigue?",
                    "a": "Multi-level alert suppression: (1) Aggregation - alert on 'ERROR count >100 in 5 min' not 'every ERROR', reduces noise by 100x, (2) Cooldown Period - once alert fires, don't re-alert for same condition for 10 minutes (configurable), (3) Severity Levels - P0 (critical, page immediately), P1 (high, email+Slack), P2 (medium, Slack only), P3 (low, daily digest), (4) Deduplication - alerts with same fingerprint (service+error_type) grouped, send summary not individual alerts, (5) Escalation - if P1 alert not acknowledged in 15 min, escalate to P0 and page on-call, (6) Rate Limiting - max 10 alerts per service per hour, prevent alert storms. Example: Payment service 1000 errors \u2192 single alert 'Payment ERROR spike: 1000 in 5 min' with link to dashboard, not 1000 individual alerts."
                  },
                  {
                    "q": "How do you handle schema evolution when log formats change?",
                    "a": "Backward-compatible schema design: (1) Schema Registry - maintain version history in registry, agents send schema_version with logs, (2) Multi-version Support - Flink has parsers for v1, v2, v3 schemas, routes to appropriate parser based on version, (3) Elasticsearch Mapping - dynamic mapping enabled, new fields auto-detected and indexed with default types, (4) Default Values - missing fields populated with defaults during processing (null for optional, 'UNKNOWN' for required), (5) Migration Path - during schema change, run both old and new parsers in parallel for 7 days, validate outputs match before deprecating old version, (6) Deprecated Field Handling - old fields kept for 90 days with 'deprecated' flag, gives clients time to upgrade agents. Example: Add 'request_id' field \u2192 Flink auto-maps to ES, old logs without field show null, queries work across old+new logs."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "12. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Ingestion & Throughput",
                    "pairs": [
                      {
                        "k": "Ingestion Rate",
                        "v": "100K-1M logs/second (peak), 10K-100K/sec (average)"
                      },
                      {
                        "k": "Batch Size",
                        "v": "100 logs per batch OR 10 seconds, whichever first"
                      },
                      {
                        "k": "Agent Buffer",
                        "v": "10MB local disk buffer per agent, 24 hour retry window"
                      },
                      {
                        "k": "Kafka Throughput",
                        "v": "100K messages/sec per partition, 100 partitions = 10M/sec"
                      },
                      {
                        "k": "Flink Parallelism",
                        "v": "32 parallel tasks, processes 100K events/sec per task"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Storage & Retention",
                    "pairs": [
                      {
                        "k": "Hot Storage",
                        "v": "14 days in Cassandra + Elasticsearch (SSD, <200ms queries)"
                      },
                      {
                        "k": "Warm Storage",
                        "v": "14-45 days in Cassandra only (HDD, 1-2s queries)"
                      },
                      {
                        "k": "Cold Storage",
                        "v": ">45 days in S3 Parquet (Glacier, 10-30s queries via Athena)"
                      },
                      {
                        "k": "Compression Ratio",
                        "v": "3:1 with Snappy (e.g., 1TB raw \u2192 330GB compressed)"
                      },
                      {
                        "k": "Cassandra Write Speed",
                        "v": "10K writes/sec per node, 100 nodes = 1M writes/sec"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Search & Query Performance",
                    "pairs": [
                      {
                        "k": "Elasticsearch Query",
                        "v": "<200ms for recent logs (14 days), uses query cache"
                      },
                      {
                        "k": "Cassandra Query",
                        "v": "1-2 seconds for warm tier (14-45 days)"
                      },
                      {
                        "k": "S3/Athena Query",
                        "v": "10-30 seconds for cold tier historical analysis"
                      },
                      {
                        "k": "Real-time Tail Latency",
                        "v": "<1 second from log generation to WebSocket delivery"
                      },
                      {
                        "k": "Index Size",
                        "v": "50GB per daily index, 10 shards, 1 replica"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability & Availability",
                    "pairs": [
                      {
                        "k": "Kafka Replication",
                        "v": "RF=3, min.insync.replicas=2, acks=all"
                      },
                      {
                        "k": "Cassandra Replication",
                        "v": "RF=3, QUORUM writes/reads, tolerates 1 node failure"
                      },
                      {
                        "k": "Flink Checkpoints",
                        "v": "Every 60 seconds, state saved to S3 for recovery"
                      },
                      {
                        "k": "System Availability",
                        "v": "99.9% SLA (8.7 hours downtime/year max)"
                      },
                      {
                        "k": "Watermark Delay",
                        "v": "5 minutes (handles late-arriving logs, 99.9% coverage)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Example Calculation - End-to-End Latency",
                    "pairs": [
                      {
                        "k": "Agent \u2192 API Gateway",
                        "v": "50ms (network + TLS handshake + auth)"
                      },
                      {
                        "k": "API \u2192 Kafka Publish",
                        "v": "100ms (validation + Kafka acks=all)"
                      },
                      {
                        "k": "Kafka \u2192 Flink Processing",
                        "v": "500ms (consumer lag + windowing + enrichment)"
                      },
                      {
                        "k": "Flink \u2192 Elasticsearch Index",
                        "v": "200ms (bulk API with 5K batch)"
                      },
                      {
                        "k": "Total Latency (p95)",
                        "v": "850ms (typically <5 seconds SLA met)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cost Optimization",
                    "pairs": [
                      {
                        "k": "Hot Storage Cost",
                        "v": "$0.10/GB/month (SSD, Cassandra + ES)"
                      },
                      {
                        "k": "Warm Storage Cost",
                        "v": "$0.05/GB/month (HDD, Cassandra only)"
                      },
                      {
                        "k": "Cold Storage Cost",
                        "v": "$0.01/GB/month (S3 Glacier)"
                      },
                      {
                        "k": "Example Savings",
                        "v": "1TB/day = $3000/mo hot vs $300/mo blended (90% savings)"
                      },
                      {
                        "k": "Sampling Impact",
                        "v": "Sample DEBUG 10%, send ERROR 100% = 60% volume reduction"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "NEVER write logs directly to Elasticsearch during ingestion. Always use Kafka as buffer first. This is critical for: (1) preventing data loss during ES downtime, (2) enabling replay on processing errors, (3) supporting multiple downstream consumers."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'Why Cassandra over other databases?'. Answer: (1) Write-optimized - 10K writes/sec per node, perfect for log ingestion, (2) Time-series model - clustering by timestamp enables efficient range queries, (3) TTL support - auto-deletes old logs without manual cleanup, (4) Linear scalability - add nodes to scale horizontally."
              },
              {
                "type": "tip",
                "text": "Key optimization: Hot-Warm-Cold tiering saves 90% on storage costs. Recent logs (14 days) need fast access (SSD), old logs (>45 days) rarely queried (S3 Glacier). Users don't notice 10s delay on historical queries but save thousands in monthly costs."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Apache Flink for stream processing shows understanding of event-time processing, watermarks, and exactly-once semantics. Don't just say 'Kafka consumer' - Flink provides windowing, stateful operations, and fault tolerance that plain consumers lack."
              },
              {
                "type": "warning",
                "text": "NEVER synchronously wait for Elasticsearch indexing during ingestion. Return 202 Accepted immediately after Kafka publish. This prevents ingestion slowdown when ES is under load and improves p95 latency from 2s to 200ms."
              },
              {
                "type": "tip",
                "text": "Deduplication window trade-off: 5-minute window catches 99% duplicates from agent retries while keeping Flink state size manageable (<1GB per task). Longer windows (1 hour) catch more duplicates but increase state size 12x and checkpoint time from 5s to 60s."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love asking: 'How do you handle log spikes during incidents?'. Answer: (1) Kafka absorbs burst (buffer 1M logs), (2) Sampling increases automatically (DEBUG\u21921%, INFO\u219210%), (3) Rate limiting per client prevents one service overwhelming system, (4) Alert service uses top-K to show 'top 10 error types' not all errors."
              },
              {
                "type": "tip",
                "text": "Elasticsearch ILM (Index Lifecycle Management) is NOT optional for production. Without it, indices grow unbounded, queries slow down, and cluster runs out of disk. ILM auto-rolls over daily, moves to warm tier, and deletes old indices - completely automated."
              },
              {
                "type": "warning",
                "text": "NEVER use SELECT * or full table scans in Cassandra. Always include partition key (service_name, log_date) in WHERE clause. Without it, query hits all nodes, takes minutes instead of milliseconds, and can crash the cluster under load."
              },
              {
                "type": "interview-favorite",
                "text": "Must explain: Why eventual consistency is acceptable for logs. Unlike payments (need strong consistency), logs don't require immediate global consistency. It's fine if log appears in Elasticsearch 1 second after Cassandra - users care about near real-time (<5s), not strict ordering."
              }
            ],
            "tags": [
              "system-design",
              "distributed-logging",
              "observability",
              "Splunk",
              "OpenObserve",
              "Kafka",
              "Apache-Flink",
              "Elasticsearch",
              "Cassandra",
              "stream-processing",
              "time-series",
              "hot-warm-cold-storage",
              "log-aggregation",
              "real-time-analytics",
              "data-lifecycle",
              "FluentBit",
              "OTEL",
              "alerting",
              "event-time-processing",
              "watermarks",
              "exactly-once-semantics"
            ]
          }
        },
        {
          "id": "17",
          "title": "System Design 17: Design Payment Gateway | Payment System | Stripe | Paypal | Razorpay | HLD | LLD",
          "videoUrl": "https://youtu.be/7EnqCwQOddY",
          "description": "In this video, we deep dive into the complete system design of an online Payment Gateway and explain how real-world digital payments actually work.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/qeowjfngon4ogn3og4n3ogn43ogn43ogno.png",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/wergejngjerngoewrnfqengorwqngon.png",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 12,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "Payment Gateway System",
              "oneLinerToMemorize": "PCI-DSS compliant orchestration layer \u2192 Tokenization + Idempotency \u2192 Multi-processor routing with reconciliation"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Initiate payment requests from merchants/clients",
                  "Feature 2: Create temporary checkout sessions with payment details",
                  "Feature 3: Secure PCI-DSS compliant card data handling via tokenization",
                  "Feature 4: Transaction status tracking (pending, processing, success, failed)",
                  "Feature 5: Support for partial payments and returns/refunds",
                  "Feature 6: Webhook notifications for payment status updates"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Transaction Volume",
                        "v": "10,000+ TPS (Transactions Per Second)"
                      },
                      {
                        "k": "Merchants",
                        "v": "Millions of merchant accounts"
                      },
                      {
                        "k": "Daily Transactions",
                        "v": "Hundreds of millions per day"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance",
                    "pairs": [
                      {
                        "k": "Latency",
                        "v": "< 200ms for payment authorization response"
                      },
                      {
                        "k": "Throughput",
                        "v": "High concurrent payment processing"
                      },
                      {
                        "k": "Availability",
                        "v": "99.99% uptime (52 minutes downtime/year max)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability & Security",
                    "pairs": [
                      {
                        "k": "Consistency",
                        "v": "Strong consistency for payment transactions (CP in CAP)"
                      },
                      {
                        "k": "Security",
                        "v": "End-to-end encryption, PCI-DSS Level 1 compliance"
                      },
                      {
                        "k": "Idempotency",
                        "v": "Prevent duplicate charges on retry"
                      },
                      {
                        "k": "Reconciliation",
                        "v": "Daily settlement matching with processors"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity",
                "type": "bullets",
                "items": [
                  "Entity 1: Merchant/Clients - Business accounts initiating payments with API keys",
                  "Entity 2: Transaction - Core payment record with amount, status, timestamps",
                  "Entity 3: PaymentMethod - Card details, tokenized references, billing info",
                  "Entity 4: Customer/Users - End users making payments",
                  "Entity 5: WebhookEvent - Async notifications for status changes",
                  "Entity 6: PaymentSession - Temporary checkout context with expiry",
                  "Entity 7: PaymentIntent - User's intention to pay with status tracking"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Payment Operations",
                    "pairs": [
                      {
                        "k": "POST /v1/payment-intents",
                        "v": "Create payment intent with amount, currency, merchant details"
                      },
                      {
                        "k": "POST /v1/payment-sessions",
                        "v": "Create checkout session with cart details and return URLs"
                      },
                      {
                        "k": "POST /v1/checkout/{pay}",
                        "v": "Process checkout with payment method and session ID"
                      },
                      {
                        "k": "GET /v1/payments/{payment_id}",
                        "v": "Retrieve payment status and details"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Management Operations",
                    "pairs": [
                      {
                        "k": "POST /v1/refunds",
                        "v": "Initiate refund for completed payment"
                      },
                      {
                        "k": "POST /v1/webhooks",
                        "v": "Register webhook endpoint for notifications"
                      },
                      {
                        "k": "GET /v1/reconciliation/report",
                        "v": "Retrieve settlement and reconciliation reports"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Clients/Merchants \u2192 API Gateway + Load Balancer: Entry point with rate limiting and authentication",
                  "PaymentIntent Service \u2192 PaymentIntent DB: Manages payment intentions and queues to Payment Intent Queue",
                  "PaymentSession Service \u2192 Redis: Creates and validates checkout sessions with expiry",
                  "Checkout Backend Service \u2192 PostgreSQL: Validates session, intent, merchant match; manages state transitions",
                  "Payment Processor Service \u2192 External Processors (Visa, Razorpay, Mastercard): Routes to appropriate processor",
                  "Orchestrator \u2192 Multiple Services: Coordinates payment flow and processor selection",
                  "Reconciliation Service \u2192 Merchant Register + Kafka: Daily settlement matching and discrepancy detection",
                  "Tokenization Service \u2192 HSM + Card Vault: PCI-DSS compliant token generation and card data storage"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Payment Intent Creation",
                    "steps": [
                      "Client sends: POST /v1/payment-intents with {amount, currency, merchant_id, metadata}",
                      "PaymentIntent Service validates: merchant exists, amount > 0, currency valid",
                      "Service creates: PaymentIntent record in PostgreSQL with status='requires_payment_method'",
                      "Service publishes: Intent created event to Payment Intent Queue",
                      "Service returns: {intent_id, client_secret, status, amount, currency}"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Checkout Session Creation",
                    "steps": [
                      "Client sends: POST /v1/payment-sessions with {intent_id, success_url, cancel_url}",
                      "PaymentSession Service retrieves: Intent details from PaymentIntent DB",
                      "Service creates: Session object in Redis with TTL (30 mins) containing {session_id, intent_id, merchant_id, expiry}",
                      "Service generates: Checkout URL - https://pay.gateway.com/checkout/sess_{32-char-id}",
                      "Service returns: {session_id, checkout_url, expires_at}"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Tokenization (PCI-DSS Zone)",
                    "steps": [
                      "User submits: Card details {card_number: '4111 1111 1111 1111', expiry_month: '12', cvv: '123', billing_address}",
                      "Tokenization Service sends: Card data to HSM (Hardware Security Module) for encryption",
                      "HSM generates: Token like 'tok_1A2B3C' and stores encrypted card in Card Vault",
                      "Service stores: Mapping {token \u2192 hash of card data} in secure database",
                      "Service returns: {card_token: 'tok_1A2B3C', last4: '1111', brand: 'visa'} - NO raw card data leaves PCI zone"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Checkout Processing",
                    "steps": [
                      "Client calls: POST /v1/checkout/{pay} with {session_id, card_token, save_card: true/false}",
                      "Checkout Backend Service validates: (1) Session exists in Redis and not expired, (2) Intent valid and matches merchant, (3) Session state is ACTIVE",
                      "Service updates: Intent status to 'processing' in PostgreSQL with idempotency key check",
                      "Service calls: Orchestrator with {intent_id, card_token, amount, merchant_id}",
                      "Orchestrator selects: Appropriate processor based on card brand, amount, merchant config"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Payment Processing",
                    "steps": [
                      "Orchestrator routes: Request to Payment Processor Service (e.g., Visa Connector)",
                      "Processor Service calls: External processor API with detokenized card data from HSM",
                      "External processor responds: Authorization response with {transaction_id, status, auth_code}",
                      "Processor Service stores: Response in database and publishes to Kafka topic 'payment.processor.callback'",
                      "Response flows back: Through Orchestrator \u2192 Checkout Backend \u2192 Update intent status"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Status Update & Webhook",
                    "steps": [
                      "Checkout Backend updates: Transaction status to 'succeeded' or 'failed' in PostgreSQL",
                      "Backend publishes: Event to Kafka topic for async processing",
                      "Webhook Service consumes: Event and retrieves merchant webhook URL from config",
                      "Service sends: POST to merchant webhook with {event: 'payment.succeeded', payment_id, amount, status, metadata}",
                      "Service implements: Retry logic with exponential backoff (3 attempts: 0s, 30s, 5min)"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Reconciliation (Daily)",
                    "steps": [
                      "Reconciliation Service polls: Checkout Backend DB for status (+1) transactions",
                      "Service fetches: Processor settlement files via connector APIs",
                      "Service matches: Internal transaction records with processor records by transaction_id",
                      "Service detects: Discrepancies (missing, amount mismatch) and logs to Merchant Register DB",
                      "Service generates: Daily reconciliation report with {matched, unmatched, discrepancies}",
                      "Service triggers: Alerts for unreconciled transactions > threshold"
                    ]
                  }
                ]
              },
              {
                "id": "client-components",
                "title": "7. Client-Side Components",
                "type": "bullets",
                "items": [
                  "Component 1: Checkout UI - Embedded iframe or redirect to hosted payment page",
                  "Component 2: Card Input Form - PCI-compliant form with client-side validation",
                  "Component 3: Payment Status Poller - Polls GET /v1/payments/{id} for status updates",
                  "Component 4: Webhook Handler - Merchant backend endpoint to receive async notifications",
                  "Component 5: 3D Secure Handler - Redirects for additional authentication when required",
                  "Component 6: Error Display - User-friendly error messages and retry options"
                ]
              },
              {
                "id": "database-schema",
                "title": "8. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "PaymentIntent (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "intent_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "merchant_id",
                        "v": "uuid FK \u2192 Merchants, INDEXED"
                      },
                      {
                        "k": "amount",
                        "v": "bigint (cents, e.g., 10000 for $100.00)"
                      },
                      {
                        "k": "currency",
                        "v": "varchar(3) (ISO 4217: USD, EUR, INR)"
                      },
                      {
                        "k": "status",
                        "v": "enum (requires_payment_method, processing, succeeded, failed, canceled)"
                      },
                      {
                        "k": "idempotency_key",
                        "v": "varchar(255) UNIQUE - prevents duplicate processing"
                      },
                      {
                        "k": "payment_method_type",
                        "v": "varchar(50) (card, upi, wallet)"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb - merchant custom data"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "CheckoutSession (Redis)",
                    "pairs": [
                      {
                        "k": "session_id",
                        "v": "string PRIMARY KEY (sess_{32-chars})"
                      },
                      {
                        "k": "intent_id",
                        "v": "uuid - reference to PaymentIntent"
                      },
                      {
                        "k": "merchant_id",
                        "v": "uuid"
                      },
                      {
                        "k": "status",
                        "v": "string (ACTIVE, USED, EXPIRED)"
                      },
                      {
                        "k": "success_url",
                        "v": "string - redirect on success"
                      },
                      {
                        "k": "cancel_url",
                        "v": "string - redirect on cancel"
                      },
                      {
                        "k": "expires_at",
                        "v": "timestamp (TTL: 30 minutes)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Transaction (PostgreSQL)",
                    "pairs": [
                      {
                        "k": "txn_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "intent_id",
                        "v": "uuid FK \u2192 PaymentIntent"
                      },
                      {
                        "k": "processor",
                        "v": "varchar(50) (visa, razorpay, mastercard)"
                      },
                      {
                        "k": "processor_txn_id",
                        "v": "varchar(255) - external processor reference"
                      },
                      {
                        "k": "status",
                        "v": "enum (pending, authorized, captured, failed, refunded)"
                      },
                      {
                        "k": "amount",
                        "v": "bigint"
                      },
                      {
                        "k": "auth_code",
                        "v": "varchar(20) - processor authorization code"
                      },
                      {
                        "k": "error_code",
                        "v": "varchar(50) - failure reason code"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamp INDEXED for reconciliation"
                      }
                    ]
                  },
                  {
                    "groupTitle": "TokenizedCard (Card Vault - Encrypted DB)",
                    "pairs": [
                      {
                        "k": "token",
                        "v": "varchar(50) PRIMARY KEY (tok_xxxxx)"
                      },
                      {
                        "k": "card_hash",
                        "v": "varchar(64) - SHA-256 hash for deduplication"
                      },
                      {
                        "k": "encrypted_card_data",
                        "v": "bytea - AES-256 encrypted card number"
                      },
                      {
                        "k": "last4",
                        "v": "varchar(4)"
                      },
                      {
                        "k": "brand",
                        "v": "varchar(20) (visa, mastercard, amex)"
                      },
                      {
                        "k": "expiry_month",
                        "v": "smallint"
                      },
                      {
                        "k": "expiry_year",
                        "v": "smallint"
                      },
                      {
                        "k": "customer_id",
                        "v": "uuid FK \u2192 Customer (if saved)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "MerchantRegister (PostgreSQL - Reconciliation)",
                    "pairs": [
                      {
                        "k": "record_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "merchant_id",
                        "v": "uuid INDEXED"
                      },
                      {
                        "k": "txn_id",
                        "v": "uuid FK \u2192 Transaction"
                      },
                      {
                        "k": "processor_txn_id",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "internal_amount",
                        "v": "bigint"
                      },
                      {
                        "k": "processor_amount",
                        "v": "bigint"
                      },
                      {
                        "k": "reconciliation_status",
                        "v": "enum (matched, unmatched, discrepancy)"
                      },
                      {
                        "k": "settlement_date",
                        "v": "date INDEXED"
                      },
                      {
                        "k": "config",
                        "v": "jsonb - processor specific config"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "additional-mechanism",
                "title": "9. Idempotency & Retry Mechanism",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Idempotency Implementation",
                    "steps": [
                      "Trigger: Client sends Idempotency-Key header with request (e.g., UUID)",
                      "Processing: Backend checks if key exists in idempotency cache (Redis TTL: 24h)",
                      "Cache Hit: Return cached response immediately without re-processing payment",
                      "Cache Miss: Process payment normally, store {key \u2192 response} in Redis before returning",
                      "Resolution: Prevents duplicate charges on network failures/retries - critical for payment integrity"
                    ]
                  },
                  {
                    "blockTitle": "Webhook Retry Logic",
                    "steps": [
                      "Trigger: Payment status change event published to Kafka",
                      "Initial Attempt: Webhook service sends POST to merchant URL with 5s timeout",
                      "Retry Schedule: If failed, retry at [30s, 5min, 1hour] with exponential backoff",
                      "Handling: After 3 failures, mark webhook as 'failed' and send alert to merchant",
                      "Offline: Merchants can query GET /v1/payments/{id} as fallback for status"
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "10. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Database Sharding - Partition by merchant_id for horizontal scaling (handle 10K+ merchants per shard)",
                  "Technique 2: Redis Caching - Session data cached with 30min TTL reduces DB reads by 85%",
                  "Technique 3: Kafka Event Streaming - Async webhook delivery decouples payment flow from notification delivery",
                  "Technique 4: Read Replicas - Payment status queries routed to read replicas, writes to primary (95% read traffic)",
                  "Technique 5: Connection Pooling - Processor connector services maintain persistent connections to reduce latency",
                  "Technique 6: Rate Limiting - API Gateway enforces 1000 req/min per merchant to prevent abuse",
                  "Technique 7: Circuit Breaker - Processor failures trigger fallback to backup processor or graceful degradation",
                  "Technique 8: HSM Scaling - Multiple HSM instances for tokenization with load balancing (5000 tokenizations/sec)",
                  "Technique 9: Dead Letter Queue - Failed Kafka messages routed to DLQ for manual investigation",
                  "Technique 10: Database Indexing - Composite index on (merchant_id, created_at, status) for fast reconciliation queries"
                ]
              },
              {
                "id": "interview-qa",
                "title": "11. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "Why use tokenization instead of storing card numbers?",
                    "a": "PCI-DSS compliance requirement. Storing raw card data requires Level 1 PCI compliance which is extremely expensive and complex. Tokenization: (1) Reduces PCI scope - only tokenization service needs full compliance, (2) Security - tokens are useless if leaked, real card data stays in HSM-protected vault, (3) Flexibility - same token works across processors. Tokens are one-way mapped, cannot be reversed without HSM access."
                  },
                  {
                    "q": "How do you handle concurrent payment attempts with the same card?",
                    "a": "Use database-level locking with idempotency keys. Flow: (1) Client generates unique idempotency key (UUID), (2) Backend checks UNIQUE constraint on idempotency_key column, (3) First request acquires row lock and processes, (4) Concurrent requests with same key either wait or get cached response, (5) After processing, response cached in Redis for 24h. This prevents double-charging even if user clicks 'Pay' button multiple times."
                  },
                  {
                    "q": "What happens if payment processor API fails midway?",
                    "a": "Multi-layer failure handling: (1) Detection - timeout (30s) or HTTP 5xx triggers failure, (2) Circuit Breaker - after 5 consecutive failures, route to backup processor for 1 minute, (3) Transaction State - mark as 'pending_verification' instead of 'failed', (4) Reconciliation - daily job queries processor for transaction status, (5) Fallback - if processor down, queue payment in Kafka for later processing, (6) User Experience - show 'Payment pending, will update via email' instead of hard failure. Recovery SLA: 99% resolved within 1 hour via automated reconciliation."
                  },
                  {
                    "q": "How to ensure payment data durability and prevent loss?",
                    "a": "Multiple durability layers: (1) Database - PostgreSQL with synchronous replication to 2 standby replicas (RPO=0), (2) WAL Archival - continuous archiving to S3 with point-in-time recovery, (3) Kafka - payment events stored with replication factor=3, retention=7 days, (4) Processor Records - external processors maintain independent records for reconciliation, (5) Backup - daily encrypted snapshots to cold storage (30 day retention). Even if entire primary datacenter fails, zero transaction data loss guaranteed."
                  },
                  {
                    "q": "How does the reconciliation process work?",
                    "a": "Daily batch reconciliation: (1) Fetch - pull previous day's transactions from internal DB (status=succeeded), (2) Download - fetch settlement files from each processor (Visa, Mastercard, Razorpay), (3) Match - join on processor_txn_id, (4) Compare - check amount, status, timestamp differences, (5) Detect - flag discrepancies: missing in processor file, amount mismatch (>1%), status conflict, (6) Alert - notify finance team for manual review, (7) Resolve - automatic retry for missing transactions after 24h. Typical match rate: 99.8%, discrepancies resolved within 48h."
                  },
                  {
                    "q": "What if the database goes down during payment processing?",
                    "a": "High availability setup: (1) Primary-Standby - PostgreSQL with streaming replication, automatic failover via Patroni in <30s, (2) Health Checks - load balancer pings DB every 2s, routes to standby if primary unhealthy, (3) In-Flight Transactions - payments in 'processing' state are idempotent, can be retried safely, (4) Kafka Buffer - if DB fully down, queue payment requests in Kafka (retention=7 days) for processing when DB recovers, (5) Graceful Degradation - return 'Payment queued, will process shortly' instead of hard error. RTO: 30 seconds, RPO: 0 seconds (no data loss)."
                  },
                  {
                    "q": "How do you handle quota/rate limits per merchant?",
                    "a": "Multi-tier rate limiting: (1) API Gateway - token bucket algorithm, 1000 req/min per merchant API key stored in Redis, (2) Circuit Breaker - if merchant hits limit, return 429 with Retry-After header, (3) Quota Enforcement - monthly transaction volume limits checked before processing (stored in merchant config table), (4) Soft Limits - warning at 80% quota via email, hard stop at 100%, (5) Sliding Window - use Redis sorted sets with timestamp scoring for accurate window-based counting. Implementation: Zadd merchant:{id}:requests {timestamp} {request_id}, Zremrangebyscore to remove old entries, Zcard to count current window."
                  },
                  {
                    "q": "What's the optimal session timeout and why?",
                    "a": "30 minutes with reasoning: (1) User Behavior - 95% of checkouts complete within 5 minutes, 30 min covers edge cases (phone interruptions, OTP delays), (2) Security - limits window for session hijacking attacks, (3) Memory - Redis eviction policy (LRU) prevents unbounded growth, (4) UX Trade-off - too short (5 min) = user frustration on slow forms, too long (2 hours) = stale cart data, security risk. Implementation: Redis TTL set on session creation, auto-extends by 10 min on each activity (view payment page, enter card), maximum 30 min from first creation. Alternative: Shorter timeout (10 min) for high-value transactions >$1000."
                  },
                  {
                    "q": "How do you prevent replay attacks on payment APIs?",
                    "a": "Multiple layers: (1) Idempotency Keys - each payment request must include unique key, stored with TTL=24h, replayed requests return cached response without charging, (2) Timestamp Validation - request must include timestamp, reject if >5 minutes old, (3) HMAC Signature - client signs request with secret key, server validates signature to ensure integrity, (4) Nonce - one-time random value included in signature, stored in Redis (TTL=10 min) to prevent reuse, (5) HTTPS - TLS 1.3 prevents MITM attacks. Full signature scheme: HMAC-SHA256(api_key + timestamp + nonce + request_body) sent in X-Signature header."
                  },
                  {
                    "q": "How do you handle refunds and partial refunds?",
                    "a": "Refund workflow: (1) Validation - check original payment status=succeeded, refund amount \u2264 remaining balance, (2) Idempotency - refund requests also use idempotency keys to prevent double-refunds, (3) Processor Call - call processor's refund API with original transaction_id, (4) State Update - create Refund record linked to Transaction, update remaining_refundable_amount, (5) Settlement - refunds processed in next day's settlement cycle, not instant, (6) Partial Support - track refund history: original_amount=100, refunded=[30, 20] = remaining=50. Typical refund processing time: 5-10 business days (processor dependent)."
                  }
                ]
              },
              {
                "id": "key-numbers",
                "title": "12. Key Numbers to Remember",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Performance Metrics",
                    "pairs": [
                      {
                        "k": "Payment Authorization",
                        "v": "< 200ms p95 latency (API Gateway \u2192 Processor \u2192 Response)"
                      },
                      {
                        "k": "Throughput",
                        "v": "10,000+ TPS sustained, 50,000 TPS peak with auto-scaling"
                      },
                      {
                        "k": "Session TTL",
                        "v": "30 minutes (covers 95% checkout completion time)"
                      },
                      {
                        "k": "Idempotency Cache",
                        "v": "24 hour TTL in Redis (prevents duplicate charges)"
                      },
                      {
                        "k": "Webhook Timeout",
                        "v": "5 seconds per attempt, 3 retries [30s, 5min, 1h]"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Availability & Reliability",
                    "pairs": [
                      {
                        "k": "System Uptime",
                        "v": "99.99% SLA (52 minutes downtime/year maximum)"
                      },
                      {
                        "k": "Database Failover",
                        "v": "< 30 seconds RTO, 0 seconds RPO (no data loss)"
                      },
                      {
                        "k": "Reconciliation Match",
                        "v": "99.8% automatic match rate, 0.2% manual review"
                      },
                      {
                        "k": "Circuit Breaker",
                        "v": "Trips after 5 failures, recovers after 1 min"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Capacity Planning",
                    "pairs": [
                      {
                        "k": "Merchants per Shard",
                        "v": "10,000 merchants (sharding by merchant_id)"
                      },
                      {
                        "k": "Rate Limit",
                        "v": "1,000 requests/min per merchant API key"
                      },
                      {
                        "k": "HSM Throughput",
                        "v": "5,000 tokenizations/sec (scales horizontally)"
                      },
                      {
                        "k": "Kafka Retention",
                        "v": "7 days for payment events (reprocessing window)"
                      },
                      {
                        "k": "DB Connections",
                        "v": "Connection pool size: 100 per service instance"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Example Calculation - Payment Flow",
                    "pairs": [
                      {
                        "k": "Step 1: API Gateway",
                        "v": "20ms (TLS termination, auth, routing)"
                      },
                      {
                        "k": "Step 2: Checkout Backend",
                        "v": "50ms (validation, DB write, orchestrator call)"
                      },
                      {
                        "k": "Step 3: Processor Call",
                        "v": "100ms (external API, network latency)"
                      },
                      {
                        "k": "Step 4: Response",
                        "v": "30ms (status update, webhook queue, response)"
                      },
                      {
                        "k": "Total Latency",
                        "v": "200ms end-to-end (p95 target met)"
                      }
                    ]
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "NEVER store raw card numbers in application databases. Always use tokenization via HSM. This is critical for PCI-DSS compliance and can result in massive fines ($5k-$100k/month) if violated."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How do you prevent duplicate charges?'. Answer: Idempotency keys (client-generated UUID) + database UNIQUE constraint + Redis caching (24h TTL). Same key = return cached response, no re-processing."
              },
              {
                "type": "tip",
                "text": "Key optimization: Use Redis for session storage instead of database. Sessions are temporary (30 min TTL), high read/write frequency (1M+ sessions/hour). Redis TTL auto-cleanup prevents manual garbage collection."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention: Strong consistency for transactions (PostgreSQL with synchronous replication) vs Eventual consistency for webhooks (Kafka + async retry). This shows understanding of CAP theorem trade-offs in payment systems."
              },
              {
                "type": "warning",
                "text": "NEVER return detailed error messages to clients about processor failures (e.g., 'Visa API timeout'). Always return generic 'Payment failed, please try again' to prevent information leakage and security vulnerabilities."
              },
              {
                "type": "tip",
                "text": "Reconciliation is NOT optional. Financial regulations require daily settlement matching. Implement automated reconciliation + alert system for discrepancies >0.1% to prevent revenue leakage."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers love asking: 'How do you scale to 10,000 TPS?'. Answer: (1) Horizontal scaling - stateless services behind load balancer, (2) Database sharding by merchant_id, (3) Redis for sessions/idempotency, (4) Kafka for async processing, (5) Connection pooling to processors."
              },
              {
                "type": "tip",
                "text": "Circuit breaker pattern is critical for payment processors. If Visa API fails, don't hammer it with retries. After 5 failures \u2192 open circuit \u2192 route to backup processor or queue for later. Prevents cascade failures."
              }
            ],
            "tags": [
              "system-design",
              "payment-gateway",
              "fintech",
              "PCI-DSS",
              "tokenization",
              "PostgreSQL",
              "Redis",
              "Kafka",
              "idempotency",
              "webhooks",
              "reconciliation",
              "high-availability",
              "distributed-systems",
              "strong-consistency",
              "HSM",
              "circuit-breaker",
              "rate-limiting",
              "microservices"
            ]
          }
        },
        {
          "id": "18",
          "title": "System Design 18: Design Cloud Storage Platform | Google Drive | Dropbox | HLD | LLD",
          "videoUrl": "https://youtu.be/Dnp_-xZjNRo",
          "description": "In this video, we deep dive into the system design of a distributed storage system like Google Drive or Dropbox.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/qwornfnwrof4ogo3ngo43ngo3gno3ngi34g.png?updatedAt=1768506789520",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/616889478_26113765334874098_4968132749202016461_n.jpg?updatedAt=1768506884752",
          "refresher": {
            "label": "Interview Cheat Sheet ( Bonus Tips )",
            "estimatedMinutes": 18,
            "lastUpdated": "2026-01-20",
            "hero": {
              "title": "Distributed Storage (Google Drive / Dropbox)",
              "oneLinerToMemorize": "Initiate upload \u2192 chunk & hash \u2192 pre-signed URLs \u2192 parallel chunk upload \u2192 commit \u2192 metadata versioning \u2192 sync events to other devices (eventual)"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Upload/download files (large files supported) and create folders",
                  "Resumable uploads (resume after disconnect)",
                  "Share files/folders with permissions",
                  "Version history (restore older versions)",
                  "Cross-device sync (push/pull updates)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Durability",
                    "pairs": [
                      {
                        "k": "Scale",
                        "v": "Billions of files; large total storage; multi-region"
                      },
                      {
                        "k": "Durability",
                        "v": "No data loss for stored blobs (multi-AZ/replication)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Consistency",
                    "pairs": [
                      {
                        "k": "Metadata",
                        "v": "Strong consistency for file/folder metadata operations"
                      },
                      {
                        "k": "Sync",
                        "v": "Eventual consistency is acceptable for cross-device synchronization"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entities",
                "title": "3. Core Entities",
                "type": "bullets",
                "items": [
                  "User",
                  "Folder (folder_id, parent_folder_id, owner_id, name)",
                  "File (file_id, parent_folder_id, owner_id, name, size_bytes, current_version_id)",
                  "FileVersion (version_id, file_id, created_at, created_by, size_bytes)",
                  "Chunk (chunk_id, object_key, size_bytes, checksum)",
                  "FileVersionChunk (version_id, chunk_index, chunk_id)"
                ]
              },
              {
                "id": "upload-flow",
                "title": "4. Upload Flow (resumable, matches diagram)",
                "type": "bullets",
                "items": [
                  "Initiate: client calls /files/upload/init with fileName, fileSize, parentFolderId",
                  "Client chunks file locally and computes checksum (e.g., SHA-256) per chunk",
                  "Client requests signed URL for chunk N \u2192 service checks if already uploaded and returns URL",
                  "Client uploads chunks in parallel; on reconnect, server returns existingChunks so client resumes",
                  "Commit: /files/upload/commit persists metadata + version + chunk map and emits sync event to Kafka"
                ]
              },
              {
                "id": "deep-dive-dedup",
                "title": "5. Deep Dive: Deduplication & Validation",
                "type": "bullets",
                "items": [
                  "Dedup is typically chunk-level using content hash; identical chunks can be reused across files/versions",
                  "Blob store is not the metadata source of truth; metadata DB + version mapping is authoritative",
                  "Validations can include checksum verification, size validation, malware scan (async), and quota checks"
                ]
              },
              {
                "id": "sync-engine",
                "title": "6. Deep Dive: Sync Engine",
                "type": "bullets",
                "items": [
                  "Client watcher/local index detects filesystem changes and uploads deltas",
                  "Server publishes change events; other devices receive via push (fanout) or pull (polling)",
                  "Conflicts resolved via versioning + merge rules (app-specific; for binary files, last-writer-wins is common)"
                ]
              }
            ]
          }
        },
        {
          "id": "19",
          "title": "System Design 19: Design of Online Learning Platform | Udemy | Coursera | HLD | LLD",
          "videoUrl": "https://youtu.be/PUf-Q7lNtv8",
          "description": "In this video, we design a large-scale online learning & teaching platform like Udemy or Coursera from scratch.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/bihbiub389h493b4g3u4b.png?updatedAt=1770152347854",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/626354092_26288470950736868_6848330558593996237_n.jpg?updatedAt=1770152835086",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 20,
            "lastUpdated": "2026-01-26",
            "hero": {
              "title": "Online Learning Platform (Udemy / Coursera / EdTech)",
              "oneLinerToMemorize": "Instructor uploads video \u2192 S3 presigned URL \u2192 FFmpeg transcode (1080p/720p/480p/360p) \u2192 HLS 6-sec segments \u2192 CloudFront CDN (<2sec startup) \u2192 Student watches \u2192 Progress batched 30sec \u2192 Kafka consumer upserts (last_position_sec resume, max_position_sec completion) \u2192 Stripe payment with idempotency \u2192 Certificate on 100% completion"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User accounts with roles (student/instructor/admin), instructor verification required before teaching",
                  "Feature 2: Instructors create courses with videos, quizzes, assignments, category, difficulty, rating, price, resources",
                  "Feature 3: Students enroll in courses (free or paid), track progress through video completion and quiz scores",
                  "Feature 4: Assessment system with quizzes (auto-grade MCQ), assignments (manual grade essays), pass/fail status",
                  "Feature 5: Progress tracking and course completion status with resume functionality (last watched position)",
                  "Feature 6: Review and rating system for courses (1-5 stars, written reviews with helpful votes)",
                  "Feature 7: Instructor onboarding & verification (identity check, payout setup via Stripe Connect)",
                  "Feature 8: Support large files (videos up to 5GB per file, resources up to 100MB per file)"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Courses & Students",
                        "v": "100K+ courses and millions of enrolled students globally"
                      },
                      {
                        "k": "Video Files",
                        "v": "Billions of video segments stored in S3, petabytes of content"
                      },
                      {
                        "k": "Traffic",
                        "v": "Millions of video requests/day, thousands of enrollments/hour"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance",
                    "pairs": [
                      {
                        "k": "Video Latency",
                        "v": "<2sec startup time, adaptive bitrate streaming"
                      },
                      {
                        "k": "API Response",
                        "v": "<200ms for course listing, <100ms for enrollment check"
                      },
                      {
                        "k": "Throughput",
                        "v": "High bandwidth for 1080p streaming (5 Mbps per concurrent user)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability",
                    "pairs": [
                      {
                        "k": "Availability",
                        "v": "99.9% uptime (CAP: Availability >> Consistency for video streaming)"
                      },
                      {
                        "k": "Payment Consistency",
                        "v": "Highly consistent for payments (ACID transactions, no double charging)"
                      },
                      {
                        "k": "Durability",
                        "v": "Video durability 11 9's via S3 cross-region replication"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity",
                "type": "bullets",
                "items": [
                  "Entity 1: User/Instructor - userID, name, email, role (student/instructor/admin), instructor_profile {bio, expertise, verification_status, stripe_account_id}, status, metadata",
                  "Entity 2: Course - courseID, instructorID, title, description, category, difficulty (BEGINNER/INTERMEDIATE/ADVANCED from image), rating (DECIMAL), price, students_enrolled, published_status, thumbnail_url, created_at",
                  "Entity 3: Enrollment - enrollmentID, userID, courseID (FK), owner_instructor_id, enrolled_at (DATETIME/TIMESTAMP from image), completion_status (DECIMAL 5,2% from image), payment_id, metadata",
                  "Entity 4: Progress - user_id, course_id, lesson_id, video_id (if lesson is video from image), last_position_sec (resume from image), max_position_sec (how watched point from image), status (NOT_STARTED/IN_PROGRESS/COMPLETED), completed_at, metadata",
                  "Entity 5: Quiz - quiz_id, lesson_id, title, pass_score (70%), time_limit_sec, attempts_allowed, Questions: question_id (FK from image), question, type (MCQ_SINGLE/MULTI_SELECT/FILL/SELECT/TRUE_FALSE/TEXT from image), options (JSON from image), correct_answer, points, metadata",
                  "Entity 6: Payment - payment_id, user_id, course_id, amount (DECIMAL), status (PENDING/COMPLETED/FAILED/REFUNDED), stripe_payment_id, payment_method, created_at, metadata",
                  "Entity 7: Review - review_id, user_id, course_id, rating (1-5 INT), comment (TEXT), helpful_votes (INT), created_at",
                  "Entity 8: Course_stats (denormalized from image) - course_id, avg_rating, total_reviews, enrolled, completion_rate (%) - updated async via Kafka for fast queries"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Courses",
                    "pairs": [
                      {
                        "k": "GET /courses?search={}category={}price={}rating={}level={}sort=",
                        "v": "Search/filter courses by category, price range, rating, difficulty level, sort by popularity/rating/newest"
                      },
                      {
                        "k": "GET /courses/{courseId}",
                        "v": "Get course details including curriculum, instructor info, reviews, enrollment count"
                      },
                      {
                        "k": "POST /instructor/courses",
                        "v": "Instructor creates new course with title, description, category, price, difficulty"
                      },
                      {
                        "k": "POST /instructor/courses/{courseId}/publish",
                        "v": "Publish course (makes visible to students after admin approval if first course)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Enrollments",
                    "pairs": [
                      {
                        "k": "POST /courses/{courseId}/enroll (free enroll)",
                        "v": "Enroll in free course, creates enrollment record, grants access to content"
                      },
                      {
                        "k": "GET /me/enrollments?courseId=...",
                        "v": "Get user's enrolled courses with progress percentage, completion status, last accessed"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Progress",
                    "pairs": [
                      {
                        "k": "POST /progress/events (batched events)",
                        "v": "Track video watch events (batched every 30 sec), quiz completion, assignment submission"
                      },
                      {
                        "k": "GET /me/courses/{courseId}/progress",
                        "v": "Get user's progress for specific course (percentage, last watched position, completed lessons)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "From image HLD: users/students \u2192 LB & API Gateway \u2192 Services (User Svc, Comment & Review Svc, Course Search Svc, Video Playback Svc, User Progress Svc, Enrollment Svc, Payment Svc) \u2192 Databases",
                  "From image HLD: instructor \u2192 LB & API Gateway \u2192 Catalog Svc, Media Uploader Svc \u2192 Course DB, S3 Blob storage",
                  "LB & API Gateway: Load balancing, authentication (JWT), authorization (role-based), routing, rate limiting (100 req/min per user)",
                  "User Svc \u2192 UserDB: User profiles, authentication, instructor verification status, Stripe account linkage",
                  "Comment & Review Svc \u2192 Course DB: Course reviews (1-5 stars), ratings aggregation, comments on lectures, helpful votes",
                  "Course Search Svc \u2192 Elastic Search: Full-text search on title/description/tags, filtering by category/price/rating/difficulty, autocomplete suggestions",
                  "Video Playback Svc \u2192 Blob (S3) \u2192 CDN: Streams video content via CloudFront, generates signed URLs (24-hour expiry), adaptive bitrate HLS streaming",
                  "User Progress Svc \u2192 Kafka \u2192 Progress DB: Tracks video watch time (last_position_sec, max_position_sec from image), quiz scores, assignment completion, batched events every 30 sec",
                  "Enrollment Svc \u2192 Course Enrollments DB: Manages course enrollments, access control checks, completion_status updates (DECIMAL 5,2% from image)",
                  "Payment Svc \u2192 Payment DB \u2192 Stripe: Handles course purchase payments, refunds (30-day window), instructor payouts (70/30 revenue split), idempotency via enrollment_request_id",
                  "Catalog Svc (instructor): Course creation, curriculum management (sections/lessons), content organization, publishing workflow",
                  "Media Uploader Svc \u2192 S3: Uploads videos via presigned URLs, triggers transcoding pipeline, generates thumbnails at 0s/5s/10s",
                  "From image LLD: CDN (CloudFront) \u2192 S3 for fast global video delivery (<2sec startup from image)",
                  "From image LLD: Kafka \u2192 user_tracking_consumer_svc \u2192 Progress DB for event-driven progress tracking with async updates"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Video Upload & Transcoding",
                    "steps": [
                      "Instructor uploads video: POST /instructor/courses/{courseId}/lessons/{lessonId}/video",
                      "Media Uploader generates presigned S3 URL: presignedURL = s3.generatePresignedUrl('putObject', {Bucket: 'course-videos-raw', Key: 'courses/C123/lessons/L456/original.mp4', Expires: 3600}), Response: {uploadURL: presignedURL, videoId: 'V789'}",
                      "Client uploads directly to S3 (bypasses server, faster): PUT {presignedURL} with video file, chunked upload for large files (5GB max), resumable if network fails",
                      "S3 event triggers Lambda: S3 \u2192 Lambda on object creation \u2192 Lambda publishes Kafka 'video.uploaded': {videoId, courseId, lessonId, s3Key, fileSize: 2GB}",
                      "Video Processing Worker (Kafka consumer): (1) Download original.mp4 from S3, (2) Transcode to 4 resolutions: FFmpeg: 1080p (1920\u00d71080, 5 Mbps), 720p (1280\u00d7720, 2.5 Mbps), 480p (854\u00d7480, 1 Mbps), 360p (640\u00d7360, 500 Kbps), (3) HLS packaging: ffmpeg -i 1080p.mp4 -c copy -f hls -hls_time 6 -hls_segment_filename '1080p_segment_%03d.ts' 1080p.m3u8, generates master.m3u8 + variant playlists + .ts segments (6-sec each), (4) Thumbnail extraction: Extract frames at 0s, 5s, 10s (JPEG), (5) Upload all to S3: s3://course-videos-cdn/.../master.m3u8, (6) UPDATE lessons SET video_url='https://cdn.example.com/.../master.m3u8', thumbnail_url, processing_status='COMPLETED', variants={1080p,720p,480p,360p}, duration_sec=1200",
                      "S3 shows 'video file chunks in CloudMedia (thumbnail)', processing time ~10-15 min for 2GB video (async, instructor notified when complete)"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Video Streaming & CDN (<2sec startup from image)",
                    "steps": [
                      "Student watches video: GET /courses/C123/lessons/L456/video",
                      "Video Playback Service: (1) Check enrollment: SELECT COUNT(*) FROM enrollments WHERE user_id={user_id} AND course_id='C123', if 0 \u2192 return 403 'Enroll first', (2) Fetch video URL: SELECT video_url FROM lessons WHERE lesson_id='L456' \u2192 'https://cdn.example.com/.../master.m3u8', (3) Generate CloudFront signed URL (expires 24 hours, prevents unauthorized sharing): signedURL = cloudfront.getSignedUrl({url: video_url, expires: now() + 86400}), (4) Response: {videoURL: signedURL, thumbnailURL, duration: 1200, variants: [1080p, 720p, 480p, 360p]}",
                      "Client-side playback: (1) Video player (Video.js, Plyr) loads HLS manifest: fetch(signedURL) \u2192 master.m3u8, (2) Adaptive bitrate: Player measures bandwidth (download speed of first segment), Fast connection (10 Mbps) \u2192 selects 1080p, Slow connection (1 Mbps) \u2192 selects 480p, switches mid-stream if bandwidth changes (seamless, no buffering), (3) Segment loading: Player fetches .ts segments sequentially: segment_0.ts (6 sec), segment_1.ts, ..., buffers 30 sec ahead for smooth playback",
                      "CDN optimization with CloudFront 200+ edge locations, user routed to nearest edge (<50ms latency), Cache TTL: segments 30 days (immutable), manifests 1 hour, Cache hit rate: 95%+ for popular courses",
                      "Startup time breakdown: (1) DNS lookup 50ms + TCP/TLS handshake 100ms + Manifest download 200ms + First segment download 500ms (6 sec \u2248 2 MB at 480p) = ~850ms total (well under <2sec target from image), (2) Optimization: Preload first segment while showing intro, HTTP/2 connection reuse"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Progress Tracking",
                    "steps": [
                      "Shows 'progress_level: course_id, video_id (if lesson is video), max_position_sec (how watched point), last_position_sec (resume), status, metadata'",
                      "Video watch events: (1) Client sends heartbeat every 10 seconds: {courseId: 'C123', lessonId: 'L456', videoId: 'V789', position: 120 (seconds), action: 'WATCHING', timestamp}, (2) Batching: Client batches 3 events (30 sec total) before sending: POST /progress/events with {events: [{position: 120}, {position: 130}, {position: 140}]}, reduces API calls 3\u00d7 (from 1 event/10sec to 1 batch/30sec)",
                      "User Progress Service: (1) Receives batch, publishes to Kafka: FOR EACH event: kafka.send('video.progress', {userId, courseId, lessonId, videoId, position, timestamp}), (2) Response: 200 OK immediately (doesn't wait for DB write, async processing)",
                      "Progress Consumer (Kafka consumer 'user_tracking_consumer_svc'): (1) Consumes 'video.progress' events (10 consumer instances, 100 partitions by user_id hash), (2) Upsert progress: INSERT INTO progress (user_id, course_id, lesson_id, video_id, last_position_sec: 140, max_position_sec: 140, updated_at: now()) ON CONFLICT (user_id, course_id, lesson_id) DO UPDATE SET last_position_sec=140, max_position_sec=GREATEST(progress.max_position_sec, 140), updated_at=now(), Explanation: last_position_sec=140 (resume point, always latest), max_position_sec=GREATEST(existing, 140) (farthest watched, never decreases if user rewinds), (3) Completion check: if max_position_sec >= duration_sec * 0.95 (watched 95%+): UPDATE progress SET status='COMPLETED', completed_at=now(), trigger enrollment completion_status update",
                      "Resume functionality: (1) User reopens lesson: GET /me/courses/C123/lessons/L456/progress \u2192 {lastPosition: 140, duration: 1200, percentWatched: 11.7%, status: 'IN_PROGRESS'}, (2) Client seeks video to 140 seconds (resume where left off)",
                      "Kafka integration enables event-driven, async updates (doesn't block video playback), idempotent (safe to reprocess events), scales to millions of events/day"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Payment & Enrollment",
                    "steps": [
                      "Student enrolls in paid course: POST /courses/C123/enroll (price $49.99)",
                      "Payment flow: (1) Payment Service creates Stripe payment intent: POST https://api.stripe.com/v1/payment_intents with {amount: 4999 (cents), currency: 'usd', metadata: {courseId: 'C123', userId: 'U456', enrollmentRequestId: 'REQ123'}}, enrollmentRequestId: Client-generated UUID (idempotency key prevents double charging), Stripe response: {id: 'pi_abc123', client_secret: '...', status: 'requires_payment_method'}, (2) INSERT INTO payments (payment_id, user_id, course_id, amount: 49.99, status: 'PENDING', stripe_payment_id: 'pi_abc123', enrollment_request_id: 'REQ123', created_at: now()), (3) Response: {paymentIntentId, clientSecret}",
                      "Client-side: (1) Load Stripe.js, mount card element, (2) User enters card details, (3) stripe.confirmCardPayment(clientSecret, {payment_method: {card: cardElement}}) \u2192 Stripe processes (2-5 sec)",
                      "Payment Gateway shows 'stripe atomic db transaction' for consistency",
                      "Stripe webhook: (1) POST /webhooks/stripe with {type: 'payment_intent.succeeded', data: {object: {id: 'pi_abc123', status: 'succeeded'}}}, (2) Payment Service: Verify signature (prevents fake webhooks), UPDATE payments SET status='COMPLETED', completed_at=now() WHERE stripe_payment_id='pi_abc123', Publish Kafka: 'payment.completed' \u2192 {paymentId, userId, courseId, amount: 49.99}",
                      "Enrollment creation (Kafka consumer): (1) Consumes 'payment.completed', (2) Idempotency check (CRITICAL): SELECT COUNT(*) FROM enrollments WHERE user_id='U456' AND course_id='C123', if count > 0: Log('Already enrolled') and RETURN (prevents double enrollment on duplicate webhook), (3) INSERT INTO enrollments (enrollment_id, user_id, course_id, enrolled_at: now(), payment_id, completion_status: 0.0), (4) UPDATE courses SET students_enrolled = students_enrolled + 1, (5) Send email: 'Welcome to the course!'",
                      "Idempotency scenarios: (1) User clicks 'Enroll' twice: Same enrollmentRequestId \u2192 Stripe returns existing payment_intent (no duplicate charge), (2) Webhook sent twice: Consumer checks DB before enrollment (skips if exists), (3) Webhook lost: Polling backup queries Stripe API every 5 min for PENDING payments"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Quiz System",
                    "steps": [
                      "'quizzes: question_id (FK), question, type (MCQ_SINGLE/MULTI_SELECT/FILL/SELECT/TRUE_FALSE/TEXT), quiz_id, options (JSON), pass_score, metadata'",
                      "Quiz structure: (1) Quiz: quiz_id, lesson_id, title: 'Module 1 Assessment', pass_score: 70%, time_limit: 600 sec (10 min), attempts_allowed: 3, (2) Questions: question_id, quiz_id, question: 'What is CAP theorem?', type: 'MCQ_SINGLE', options: [{id: 'A', text: 'Consistency, Availability, Partition tolerance'}, {id: 'B', text: 'Concurrency, Atomicity, Performance'}], correct_answer: 'A', points: 10",
                      "Student takes quiz: (1) Start: POST /courses/C123/lessons/L456/quiz/start \u2192 INSERT INTO quiz_attempts (attempt_id, user_id, quiz_id, started_at, status: 'IN_PROGRESS'), (2) Fetch questions: GET .../quiz/questions \u2192 {questions: [...], timeLimit: 600, totalPoints: 100}, (3) Submit: POST .../quiz/submit with {answers: [{question_id: 'Q1', answer: 'A'}, {question_id: 'Q2', answer: ['A', 'C']}]}, (4) Auto-grading (MCQ): FOR EACH answer: if (answer === correct_answer) score += points, total_score = SUM(scores), percentage = (total_score / total_points) \u00d7 100, passed = percentage >= pass_score, (5) UPDATE quiz_attempts SET score=85, percentage=85.0, status='COMPLETED', passed=true, completed_at=now(), (6) If passed: UPDATE progress SET status='COMPLETED' for quiz lesson",
                      "Retake policy: (1) If failed: attempts_left = attempts_allowed - attempts_used, if attempts_left > 0 \u2192 allow retake, (2) Cooldown: Must wait 24 hours between attempts (prevent brute force), (3) If attempts exhausted \u2192 return 403 'No attempts remaining, contact instructor'"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Courses (Course DB from image)",
                    "pairs": [
                      {
                        "k": "course_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "instructor_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "title",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "description",
                        "v": "text"
                      },
                      {
                        "k": "category",
                        "v": "varchar(100) (Business, IT, Design, Marketing, etc.)"
                      },
                      {
                        "k": "difficulty",
                        "v": "enum (BEGINNER, INTERMEDIATE, ADVANCED)"
                      },
                      {
                        "k": "rating",
                        "v": "decimal(3,2) (0.00-5.00, denormalized from reviews)"
                      },
                      {
                        "k": "price",
                        "v": "decimal(10,2) (0 for free courses)"
                      },
                      {
                        "k": "students_enrolled",
                        "v": "int DEFAULT 0 (denormalized counter, updated on enrollment)"
                      },
                      {
                        "k": "published_status",
                        "v": "enum (DRAFT, PENDING_APPROVAL, PUBLISHED, ARCHIVED)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (instructor_id, created_at), INDEX on (category, rating), INDEX on (published_status)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Enrollments (Course Enrollments DB from image)",
                    "pairs": [
                      {
                        "k": "enrollment_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "course_id",
                        "v": "uuid FK \u2192 Courses')"
                      },
                      {
                        "k": "owner_instructor_id",
                        "v": "uuid (denormalized from courses.instructor_id)"
                      },
                      {
                        "k": "enrolled_at",
                        "v": "timestamptz%TIMESTAMP/DATETIME')"
                      },
                      {
                        "k": "completion_status",
                        "v": "decimal(5,2) (0.00-100.00%, percentage of course completed)"
                      },
                      {
                        "k": "last_accessed_at",
                        "v": "timestamptz (when user last opened course)"
                      },
                      {
                        "k": "payment_id",
                        "v": "uuid FK \u2192 Payments (nullable, null for free courses)"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb"
                      },
                      {
                        "k": "Unique constraint",
                        "v": "UNIQUE (user_id, course_id) - user can't enroll twice"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (user_id, enrolled_at), INDEX on (course_id, enrolled_at)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Progress (Progress DB detailed schema)",
                    "pairs": [
                      {
                        "k": "Composite PK",
                        "v": "(user_id, course_id, lesson_id)"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "course_id",
                        "v": "uuid FK \u2192 Courses"
                      },
                      {
                        "k": "lesson_id",
                        "v": "uuid FK \u2192 Lessons"
                      },
                      {
                        "k": "video_id",
                        "v": "uuid (if lesson is video)"
                      },
                      {
                        "k": "last_position_sec",
                        "v": "int (last watched position in seconds for resume)"
                      },
                      {
                        "k": "max_position_sec",
                        "v": "int (farthest watched position, for completion check)"
                      },
                      {
                        "k": "status",
                        "v": "enum (NOT_STARTED, IN_PROGRESS, COMPLETED)"
                      },
                      {
                        "k": "completed_at",
                        "v": "timestamptz (nullable, when lesson marked complete)"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb (quiz scores, assignment submissions)"
                      },
                      {
                        "k": "Sharding",
                        "v": "Shard by user_id (user's progress on same shard for fast queries)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Quizzes (Quiz DB from image)",
                    "pairs": [
                      {
                        "k": "quiz_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "lesson_id",
                        "v": "uuid FK \u2192 Lessons"
                      },
                      {
                        "k": "pass_score",
                        "v": "decimal(5,2) (70.00 = 70% to pass)"
                      },
                      {
                        "k": "time_limit_sec",
                        "v": "int (nullable, time limit in seconds)"
                      },
                      {
                        "k": "attempts_allowed",
                        "v": "int DEFAULT 3"
                      },
                      {
                        "k": "Questions",
                        "v": "question_id uuid PK'), quiz_id uuid FK, question text, type enum (MCQ_SINGLE/MULTI_SELECT/FILL/SELECT/TRUE_FALSE/TEXT)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Payments (Payment DB from image)",
                    "pairs": [
                      {
                        "k": "payment_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "course_id",
                        "v": "uuid FK \u2192 Courses"
                      },
                      {
                        "k": "amount",
                        "v": "decimal(10,2)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, COMPLETED, FAILED, REFUNDED)"
                      },
                      {
                        "k": "stripe_payment_id",
                        "v": "varchar(255) (Stripe payment intent ID)"
                      },
                      {
                        "k": "enrollment_request_id",
                        "v": "varchar(255) (idempotency key, client-generated UUID)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "metadata",
                        "v": "jsonb"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Course_stats (Denormalized from image)",
                    "pairs": [
                      {
                        "k": "course_id",
                        "v": "uuid PRIMARY KEY FK \u2192 Courses"
                      },
                      {
                        "k": "avg_rating",
                        "v": "decimal(3,2)"
                      },
                      {
                        "k": "total_reviews",
                        "v": "int"
                      },
                      {
                        "k": "enrolled",
                        "v": "int (total enrollments)"
                      },
                      {
                        "k": "completion_rate",
                        "v": "decimal(5,2) (percentage who completed)"
                      },
                      {
                        "k": "updated_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "Purpose",
                        "v": "Materialized view updated async via Kafka for fast course listing queries"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "8. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: CDN video delivery - CloudFront 200+ edge locations, 95% cache hit rate, <2sec startup time (HLS adaptive streaming), reduces S3 egress cost 95%",
                  "Technique 2: Async video processing - S3 event \u2192 Lambda \u2192 Kafka \u2192 Worker pool (Spot instances 70% cheaper), transcoding 10-15 min (4 resolutions), instructor notified when complete",
                  "Technique 3: Progress event batching - Client batches 3 events every 30 sec (vs 1 event/10sec), reduces API calls 3\u00d7, Kafka handles millions events/day with 100 partitions",
                  "Technique 4: Database read replicas - 1 master (writes: enrollments, payments) + 5 replicas (reads: course listing, progress queries), read/write split scales to millions of queries",
                  "Technique 5: Denormalized stats - course_stats table (avg_rating, total_reviews, students_enrolled) updated async via Kafka, fast course listing queries avoid COUNT aggregations",
                  "Technique 6: Elasticsearch for search - Full-text search on title/description/tags, aggregations for category/price filters, autocomplete with ngram tokenizer, handles complex queries <100ms",
                  "Technique 7: Redis caching - Course details (1 hour TTL), enrollment check (24 hours), user preferences (24 hours), 90% cache hit rate reduces DB load 10\u00d7",
                  "Technique 8: Payment idempotency - enrollment_request_id (client-generated UUID), Stripe deduplicates, Kafka consumer checks DB before enrollment, prevents double enrollment on retry/duplicate webhook",
                  "Technique 9: HLS adaptive bitrate - 4 resolutions (1080p/720p/480p/360p), 6-sec segments, player switches mid-stream based on bandwidth, users on slow network get 360p (less bandwidth)",
                  "Technique 10: Database sharding - Progress table sharded by user_id (user's progress on same shard), Payments sharded by created_at (time-based for historical queries), scales to billions of records",
                  "Technique 11: S3 lifecycle policies - Raw videos archived to Glacier after 90 days ($0.004/GB vs $0.023/GB), transcoded videos stay in S3 (frequently accessed), saves 80% storage cost for old content",
                  "Technique 12: Kafka consumer auto-scaling - Auto-scale based on lag (if lag > 1000 messages \u2192 add instances), 100 partitions enables 100 parallel consumers, throughput scales linearly"
                ]
              },
              {
                "id": "interview-qa",
                "title": "9. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you achieve <2 second video startup time with adaptive bitrate streaming?",
                    "a": "Video streaming architecture with <2sec startup: (1) Transcoding pipeline: FFmpeg transcodes to 4 resolutions (1080p 5Mbps, 720p 2.5Mbps, 480p 1Mbps, 360p 500Kbps), HLS packaging creates master.m3u8 + variant playlists + 6-sec .ts segments, (2) CDN delivery: CloudFront 200+ edge locations, user routed to nearest (<50ms latency), cache hit rate 95% (segments cached 30 days, manifests 1 hour), (3) Adaptive bitrate: Player loads master.m3u8, measures bandwidth via first segment download, selects resolution (20 Mbps \u2192 1080p, 1 Mbps \u2192 480p), switches mid-stream if bandwidth changes (seamless, no buffering), (4) Startup optimization: DNS prefetch (saves 50ms), HTTP/2 connection reuse (saves 100ms per segment), preload first segment while showing intro, reduced segment size 4-sec for first segment (faster initial load), Breakdown: DNS 50ms + TLS 100ms + Manifest 200ms + First segment 500ms = 850ms total (under <2sec target). Result: Fast startup globally, adaptive to network conditions, scalable to millions of concurrent viewers."
                  },
                  {
                    "q": "How do you track progress for millions of students watching videos and taking quizzes?",
                    "a": "Event-driven progress tracking via Kafka: (1) Client batching: Video player sends heartbeat every 10 sec with position, batches 3 events (30 sec) before POST /progress/events, reduces API calls 3\u00d7 (1M students \u00d7 6 events/min = 6M \u2192 2M API calls/min), (2) Kafka publishing: Progress Service publishes each event to 'video.progress' topic (100 partitions by user_id hash), responds 200 OK immediately (async, doesn't block video), (3) Consumer processing: 10 consumer instances consume from 100 partitions (10K events/sec per consumer = 100K events/sec total capacity), Upsert: INSERT ... ON CONFLICT DO UPDATE SET last_position_sec=140 (resume point), max_position_sec=GREATEST(existing, 140) (farthest watched, never decreases on rewind), (4) Completion: if max_position_sec >= duration \u00d7 0.95 \u2192 UPDATE status='COMPLETED', (5) Idempotency: Kafka consumer may reprocess events (restart, lag), upsert is idempotent (same event processed twice \u2192 same DB state), (6) Scaling: Kafka partitions scale to millions events/day, DB sharded by user_id (user's progress on same shard), consumer auto-scales based on lag. Quiz progress: Submit \u2192 auto-grade MCQ \u2192 if passed UPDATE progress status='COMPLETED' for quiz lesson. Result: Handles millions of events/day, low latency (<1 sec DB write), resume functionality works reliably, idempotent (safe to retry)."
                  },
                  {
                    "q": "How do you prevent double enrollment when payment succeeds but webhook is duplicated or lost?",
                    "a": "Payment idempotency with multiple safeguards: (1) Client-side idempotency: Client generates enrollmentRequestId (UUID), includes in Stripe payment intent metadata, if user clicks 'Enroll' twice \u2192 same ID \u2192 Stripe returns existing payment_intent (no duplicate charge), (2) Webhook deduplication: Stripe sends 'payment_intent.succeeded' webhook, Payment Service: Verifies signature (prevents fake webhooks), Updates DB: payment status='COMPLETED', Publishes Kafka 'payment.completed', If webhook sent twice (network retry): Both events reach Kafka (duplicate), (3) Enrollment consumer idempotency: Consumes 'payment.completed', Checks DB: SELECT COUNT(*) WHERE user_id AND course_id, If count > 0 \u2192 already enrolled \u2192 SKIP (no duplicate enrollment), If count = 0 \u2192 INSERT enrollment (first time), UNIQUE constraint on (user_id, course_id) prevents race conditions, (4) Webhook lost scenario: Payment succeeds (Stripe status='succeeded') but webhook delayed/lost, Polling backup: Payment Service polls Stripe API every 5 min for PENDING payments: GET /v1/payment_intents/{id}, If status='succeeded' but local='PENDING' \u2192 manually update DB + publish Kafka (same flow as webhook), Catches stuck payments within 5 min, (5) Retry safety: All paths converge: DB check prevents duplicate enrollment, Stripe idempotency prevents duplicate charge, Kafka consumer handles out-of-order/duplicate events. Result: Exactly-once enrollment even with webhook failures/duplicates, payment consistency maintained, no double charging."
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Video <2sec startup requires HLS adaptive bitrate + CDN. MUST transcode to 4 resolutions (1080p/720p/480p/360p), 6-sec segments, CloudFront 200+ edges. Without CDN = 200-500ms S3 latency globally (fails requirement)."
              },
              {
                "type": "interview-favorite",
                "text": "Progress tracking: Client batches 3 events/30sec \u2192 Kafka 'video.progress' (100 partitions) \u2192 Consumer upserts (last_position_sec for resume, max_position_sec for completion check at 95%). Handles millions events/day, idempotent, async (doesn't block video playback)."
              },
              {
                "type": "tip",
                "text": "Two position fields critical - last_position_sec (resume playback here) vs max_position_sec (farthest watched, for completion). User rewinds \u2192 last decreases but max stays (prevents marking incomplete on rewind)."
              },
              {
                "type": "interview-favorite",
                "text": "Payment idempotency prevents double enrollment: (1) Client enrollment_request_id, (2) Stripe deduplicates payment_intents, (3) Kafka consumer checks DB (if enrolled \u2192 skip), (4) UNIQUE constraint (user_id, course_id), (5) Polling backup if webhook lost. All paths converge to exactly-once enrollment."
              },
              {
                "type": "warning",
                "text": "NEVER sync transcode during upload. 2GB video = 10-15 min processing (FFmpeg 4 resolutions + HLS packaging + thumbnails). MUST async: S3 event \u2192 Lambda \u2192 Kafka 'video.uploaded' \u2192 Worker pool (Spot instances 70% cheaper). Instructor gets immediate upload confirmation, notified when complete."
              },
              {
                "type": "tip",
                "text": "Kafka integration critical for scale. Shows 'user_tracking_consumer_svc' consuming events, updating Progress DB async. Event-driven architecture decouples video playback from progress writes, enables horizontal scaling (add more consumers as needed)."
              },
              {
                "type": "interview-favorite",
                "text": "HLS adaptive bitrate: master.m3u8 lists 4 variant playlists, player measures bandwidth via first segment, selects resolution (fast \u2192 1080p, slow \u2192 360p), switches mid-stream seamlessly. Users on slow network get lower quality (less bandwidth) instead of buffering. CDN caches segments 30 days (95% hit rate)."
              }
            ],
            "tags": [
              "system-design",
              "edtech",
              "udemy",
              "coursera",
              "online-learning",
              "video-streaming",
              "hls-adaptive-bitrate",
              "cdn-cloudfront",
              "kafka-event-driven",
              "progress-tracking",
              "stripe-payment",
              "idempotency",
              "ffmpeg-transcoding",
              "elasticsearch-search"
            ]
          }
        },
        {
          "id": "20",
          "title": "System Design 20: Design an Email Delivery System | Gmail | Outlook | HLD | LLD",
          "videoUrl": "https://youtu.be/87GhPP3jMp0",
          "description": "In this video, we deep dive into how Gmail / Outlook email systems are designed at scale.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/no3jn5oyn5ony4o5n47n4k5noyn3gn3o4ngn3lg3n4.png?updatedAt=1773518074836",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/651403131_26656907153893244_2590581715252335028_n%20(1).jpg?updatedAt=1773517813074",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 32,
            "lastUpdated": "2026-01-26",
            "hero": {
              "title": "Email Delivery System (Gmail / Outlook)",
              "oneLinerToMemorize": "User composes email \u2192 Compose Svc validates \u2192 Mail Send Svc \u2192 Outbox Delivery Orchestrator (SLA-based priority) \u2192 Kafka queues \u2192 SMTP relay to receiver \u2192 Receiver SMTP validates (SPF/DKIM/DMARC) \u2192 Inbox \u2192 Mailbox DB \u2192 Search indexed via Elastic Search"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: User should be able to create a mailing account with unique email address",
                  "Feature 2: Users can compose and send emails to one or more recipients (To, Cc, Bcc)",
                  "Feature 3: Users can view their Inbox, Sent, Drafts, Spam, trash folders and create custom labels/folders",
                  "Feature 4: User should be able to search content across email subject, body, sender, attachments",
                  "Feature 5: Users should be able to send attachments (images, docs, PDFs up to 25MB per email)",
                  "Feature 6: Emails in the same conversation/replies are grouped together as threads",
                  "Feature 7: Support spam filtering, policy checks (PASS/FAIL/UNKNOWN), virus scanning",
                  "Feature 8: Reliable email delivery with retry mechanisms, bounce handling, delivery status notifications"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale",
                    "pairs": [
                      {
                        "k": "Users",
                        "v": "1.8B+ users with billions of emails sent/received daily"
                      },
                      {
                        "k": "Storage",
                        "v": "Petabytes of email data, attachments stored in S3/blob storage"
                      },
                      {
                        "k": "Traffic",
                        "v": "Millions of emails per minute during peak hours"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Performance & Reliability",
                    "pairs": [
                      {
                        "k": "CAP Theorem",
                        "v": "Availability >> Consistency, but highly consistent for same user account (no duplicate emails with same email_id)"
                      },
                      {
                        "k": "Durability",
                        "v": "Emails must be durable (11 9's via S3), no data loss on server failure"
                      },
                      {
                        "k": "Reliability",
                        "v": "Email delivery guaranteed with retry mechanisms, DLQ for failed deliveries"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity",
                "type": "bullets",
                "items": [
                  "Entity 1: Users - user_id, email_address (unique), password_hash, name, quota (15GB default), created_at",
                  "Entity 2: Message/Email - email_id (unique across system), message_id (RFC 5322), thread_id (conversation grouping), from_email, to_emails[], cc_emails[], bcc_emails[], subject, body (HTML/plain text), has_attachment, url_attachment, sent_at, metadata",
                  "Entity 3: Thread/Conversation - thread_id, subject (first email subject), participant_emails[], email_ids[] (ordered by sent_at), last_updated_at, message_count",
                  "Entity 4: Email Metadata - labels[] (Inbox/Sent/Drafts/Spam/Trash/Custom), is_read, is_starred, importance (high/normal/low)",
                  "Entity 5: Mailbox_items (Inbox schema) - user_id, email_id, message_id, thread_id, from_email, to_emails, subject, snippet (first 100 chars of body), has_attachment, url_attachment (S3 URLs)",
                  "Entity 6: Outbox (pending sends) - user_id, email_id, message_id, thread_id, from_email, to_emails, subject, body, has_attachment, url_attachment, scheduled_at (for retry/delayed send)",
                  "Entity 7: Attachment - attachment_id, email_id, filename, size_bytes, content_type, s3_url (blob storage), virus_scan_status"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Account & Email Operations",
                    "pairs": [
                      {
                        "k": "POST /v1/accounts/register",
                        "v": "Create new email account with unique email address (check availability)"
                      },
                      {
                        "k": "POST /v1/emails/send",
                        "v": "Send email with Body: {to: [], cc: [], bcc: [], subject, body, attachmentId, storageUrl}"
                      },
                      {
                        "k": "GET /v1/emails/{emailId_UUID}",
                        "v": "Get full email object including body, attachments, metadata (Response includes full Email object + attachments)"
                      },
                      {
                        "k": "GET /v1/search?q='meeting tomorrow'&userId={XXXX1234}",
                        "v": "Search emails by subject/body/sender, returns matching email IDs + snippets"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Attachments",
                    "pairs": [
                      {
                        "k": "POST /v1/attachments/upload (multipart form)",
                        "v": "Upload attachment to S3, returns {attachmentId, storageUrl} for use in send email"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design",
                "type": "bullets",
                "items": [
                  "Architecture: clients/users/webclient \u2192 Load Balancer & API Gateway \u2192 Services (User Svc, Compose Svc, Mail Send Svc, Search Svc) \u2192 Databases",
                  "User Svc \u2192 User DB: Account creation, authentication, quota management (15GB default Gmail)",
                  "Compose Svc \u2192 Drafts DB: Save drafts, validate recipients, attachment handling (upload to S3)",
                  "Mail Send Svc \u2192 SMTP relay: Validates sender, anti-spam checks, routes to external SMTP servers (Gmail \u2192 Yahoo, Outlook \u2192 Gmail)",
                  "Search Svc \u2192 Mailbox DB + Elastic Search: Full-text search across subject/body/sender, indexed for fast queries",
                  "Mailbox DB: Stores Mailbox_items (Inbox) and Outbox (pending sends) with email metadata",
                  "Outside Internet: Receiver SMTP servers (Yahoo SMTP, Outlook SMTP) receive emails via SMTP protocol",
                  "Inbound SMTP Service: Receives emails from external senders (Outlook/Yahoo SMTP \u2192 Gmail Inbox)"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Email Composition & Draft Saving",
                    "steps": [
                      "User composes email in web client: To: alice@yahoo.com, Cc: bob@outlook.com, Subject: 'Meeting Tomorrow', Body: 'Hi Alice...', Attachment: presentation.pdf (5MB)",
                      "Attachment upload (if present): POST /v1/attachments/upload with multipart/form-data {file: presentation.pdf}, Compose Service: Upload to S3: s3://email-attachments/{user_id}/{email_id}/presentation.pdf, Virus scan: ClamAV scans file, if infected \u2192 reject, else mark clean, Response: {attachmentId: 'ATT123', storageUrl: 's3://...', size: 5MB, contentType: 'application/pdf'}",
                      "Save draft (auto-save every 30 sec): POST /v1/emails/draft with {to: ['alice@yahoo.com'], cc: ['bob@outlook.com'], subject, body, attachmentId: 'ATT123'}, Compose Service: Generate email_id (UUID), message_id (RFC 5322: <{email_id}@gmail.com>), INSERT INTO drafts (email_id, user_id, to_emails, subject, body, has_attachment, url_attachment: 's3://...', updated_at: now()), Response: {emailId: 'E123', status: 'DRAFT'}",
                      "Draft retrieval: GET /v1/emails/E123 \u2192 returns full email object for editing"
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Send Email Flow (Outbox & Delivery Orchestrator)",
                    "steps": [
                      "User clicks Send: POST /v1/emails/send with {emailId: 'E123'} (from draft) OR new email payload",
                      "Mail Send Service validation: (1) Check sender quota: SELECT used_quota, quota_limit FROM users WHERE user_id={user_id}, if used_quota >= quota_limit \u2192 return 403 'Quota exceeded', (2) Validate recipients: Check valid email format, resolve domain (DNS MX record lookup), anti-spoofing checks (sender domain verification), (3) Spam/policy check: Content analysis (suspicious keywords, excessive links), Rate limiting (max 500 emails/day for free users), CAPTCHA if suspicious pattern detected",
                      "Insert into Outbox: INSERT INTO outbox (user_id, email_id, message_id, thread_id, from_email, to_emails: ['alice@yahoo.com', 'bob@outlook.com'], subject, body, has_attachment, url_attachment, scheduled_at: now()) - Outbox table acts as durable queue",
                      "Publish to Kafka: Kafka.send('email.outbound', {emailId: 'E123', toEmails: ['alice@yahoo.com', 'bob@outlook.com'], priority: 'NORMAL', createdAt: timestamp}) - Topic: email.outbound with 100 partitions",
                      "Response to user: 200 OK {emailId: 'E123', status: 'SENDING'} - User sees email in Sent folder immediately (optimistic UI), actual delivery happens async"
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Outbox Delivery Orchestrator (SLA-based Priority Processing)",
                    "steps": [
                      "Outbox Delivery Orchestrator consumes Kafka 'email.outbound' topic (100 consumer instances, one per partition)",
                      "SLA-based priority routing (6 scenarios): Scenario 1: Sending email to 'elixir (first time)' with no auto-suggestion \u2192 Validation DB checks recipient validity, no previous interaction \u2192 NORMAL priority queue, Scenario 2: Sending within same org (alice@gmail.com \u2192 bob@gmail.com) \u2192 auto-suggestion available from contacts \u2192 HIGH priority (faster delivery within same domain), Scenario 3: Sending to known contact (within google contacts) \u2192 auto-suggestion enabled \u2192 HIGH priority, Scenario 4: Sending email with critical subject keywords ('urgent', 'immediate', 'asap') \u2192 auto-suggestion might trigger \u2192 elevated priority, Scenario 5: Sending to external domain (first contact at new company) \u2192 no auto-suggestion \u2192 NORMAL priority, Scenario 6: Bulk/newsletter emails \u2192 LOW priority (rate limited, sent during off-peak)",
                      "Priority determination logic: IF (same_domain OR in_contacts): priority = 'HIGH', ELSE IF (subject contains urgent keywords): priority = 'ELEVATED', ELSE IF (bulk_send OR newsletter): priority = 'LOW', ELSE: priority = 'NORMAL'",
                      "Route to appropriate SMTP queue: Kafka topics: email.smtp.high (processed immediately, <1 min delivery), email.smtp.normal (processed within 5 min), email.smtp.low (processed within 30 min, rate limited)",
                      "Consistent hashing: Hash email by sending_user_id to maintain order (emails from same user processed by same consumer, preserves send order)"
                    ]
                  },
                  {
                    "blockTitle": "Step 4: SMTP Delivery & External Server Communication",
                    "steps": [
                      "SMTP Worker consumes from priority queues (High: 50 workers, Normal: 100 workers, Low: 20 workers)",
                      "Steps to Connect SMTP Server (detailed from image): 1. (Checks/Pings) DNS Request: dig MX outlook.com \u2192 MX records: outlook-com.olc.protection.outlook.com (priority 5), 2. MX Receiver Response: Returns SMTP server IP addresses, 3. SMTP connection: TCP connection to receiver SMTP (port 25/587/465), TLS handshake for encryption, 4. SMTP handshake: EHLO gmail.com (identify sending server), Receiver responds: 250-outlook.com Hello [IP], 5. (email) MAIL FROM command: MAIL FROM:<sender@gmail.com>, Receiver validates sender domain, 6. (email) RCPT TO command: RCPT TO:<alice@outlook.com>, Receiver checks if recipient exists (local delivery table), 7. (email) DATA command: Sends email headers + body, Subject:, From:, To:, Date:, Message-ID:, MIME headers for attachments, 8. (email) Email body: Plain text and/or HTML multipart, Base64 encoded attachments (if any), 9. (email) '.' (period): Signals end of email data, 10. (email) QUIT: Terminate connection, 11. (Checks) SPF record check: Receiver queries DNS TXT record for sender domain, spf.gmail.com TXT: 'v=spf1 ip4:74.125.0.0/16 ~all', Validates sending IP is authorized, 12. (Checks) DKIM signature: Email headers include DKIM-Signature, Receiver fetches public key from DNS: default._domainkey.gmail.com, Verifies signature matches email content (prevents tampering), 13. (Checks) DMARC check: Receiver checks DMARC policy at _dmarc.gmail.com, Policy: p=reject (strict) or p=quarantine or p=none, If SPF/DKIM fail + DMARC=reject \u2192 email rejected, 14. (Status) Delivery confirmation: 250 OK Message accepted for delivery, SMTP worker receives confirmation",
                      "Update outbox status: UPDATE outbox SET status='SENT', sent_at=now() WHERE email_id='E123', DELETE FROM outbox WHERE email_id='E123' (remove from pending queue)",
                      "Insert into Sent folder: INSERT INTO mailbox_items (user_id, email_id, folder: 'SENT', from_email, to_emails, subject, snippet, sent_at) - User can see email in Sent folder"
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Receiving Email (Inbound SMTP Service)",
                    "steps": [
                      "External sender (Outlook/Yahoo SMTP) delivers to Gmail: SMTP connection to Gmail's MX servers (gmail-smtp-in.l.google.com)",
                      "Inbound SMTP Service receives: (1) MAIL FROM: <sender@outlook.com>, (2) RCPT TO: <recipient@gmail.com> \u2192 Check local cache: Does user exist?, (3) DATA \u2192 Receive full email content",
                      "Spam/Malware filtering: Spam/Malware Filtering Svc (consumes from Kafka): Content analysis: Bayesian filter (spam probability scoring), Sender reputation check (IP blacklist, domain reputation), Policy check: SPF/DKIM/DMARC validation (PASS/FAIL/UNKNOWN based on image), Link analysis: Check URLs against phishing database, Attachment scan: ClamAV virus scan, If spam score > 0.8 \u2192 mark as SPAM folder, If virus detected \u2192 quarantine/reject",
                      "Policy Check results: validation.spf (PASS/FAIL/UNKNOWN), validation.dkim (PASS/FAIL/UNKNOWN), spam_check (clean: PASS/FAIL/UNKNOWN), reputation_score",
                      "Insert into Mailbox: Kafka.send('email.inbound', {emailId, recipientEmail: 'recipient@gmail.com', ...}), Mailbox Receiver DB consumer: INSERT INTO mailbox_items (user_id, email_id, message_id, thread_id (if reply), from_email: 'sender@outlook.com', to_emails, subject, snippet, has_attachment, folder: 'INBOX' OR 'SPAM', is_read: false, received_at: now())",
                      "Thread detection: Check if email is reply (In-Reply-To header, References header match existing message_id), If match: UPDATE thread SET email_ids = array_append(email_ids, 'E456'), last_updated_at=now(), Else: Create new thread: INSERT INTO threads (thread_id, subject, participant_emails, email_ids: ['E456'])"
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Search Indexing (Elastic Search)",
                    "steps": [
                      "Elastic Search Svc (asynchronous indexing): Consumes Kafka 'email.indexed' events (published on email insert to mailbox_items)",
                      "Index email content: POST /emails/_doc/{email_id} to Elasticsearch with {user_id, email_id, from_email, to_emails[], subject, body (full text), snippet, has_attachment, labels[], sent_at}, Fields indexed: subject (analyzed with ngram for autocomplete), body (full-text search), from_email (keyword), to_emails (keyword array), Analyzer: Standard analyzer + stemming (meeting \u2192 meet, meetings)",
                      "Search query: User searches 'meeting tomorrow', Query: GET /v1/search?q=meeting tomorrow&userId=U123, Elastic Search query: {query: {bool: {must: [{match: {subject: 'meeting tomorrow'}}, {match: {body: 'meeting tomorrow'}}], filter: [{term: {user_id: 'U123'}}]}}, sort: [{sent_at: 'desc'}]}, Response: {emailIds: ['E123', 'E456'], total: 2, results: [{emailId, subject, snippet, from, sentAt}]}",
                      "Autocomplete: As user types 'meet...', Elasticsearch completion suggester returns: ['meeting', 'meetings', 'meet and greet']"
                    ]
                  },
                  {
                    "blockTitle": "Step 7: Notification Service (Push/Email alerts)",
                    "steps": [
                      "Notification Svc consumes Kafka 'email.received' events",
                      "Determine notification preference: Check user settings (push enabled? email forwarding?), Importance: If from VIP sender OR subject contains important keywords \u2192 push notification, Else: silent notification (badge count update)",
                      "Push notification: FCM (Firebase Cloud Messaging) for mobile: POST https://fcm.googleapis.com/v1/.../messages:send with {token: device_token, notification: {title: 'New email from alice@yahoo.com', body: 'Meeting Tomorrow - Hi Bob...'}, data: {emailId: 'E456', threadId}}, User's device receives push, tapping opens email in app",
                      "Email forwarding (if configured): User has rule: 'Forward emails from boss@company.com to personal@gmail.com', Notification Svc triggers: POST /v1/emails/send (internal send) with modified headers (X-Forwarded-From)"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Mailbox_items (Inbox/Sent/Spam/Trash)",
                    "pairs": [
                      {
                        "k": "Composite PK",
                        "v": "(user_id, email_id) - ensures no duplicate emails for user"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "email_id",
                        "v": "uuid (globally unique email identifier)"
                      },
                      {
                        "k": "message_id",
                        "v": "varchar(255) (RFC 5322 Message-ID: <uuid@gmail.com>)"
                      },
                      {
                        "k": "thread_id",
                        "v": "uuid FK \u2192 Threads (conversation grouping)"
                      },
                      {
                        "k": "from_email",
                        "v": "varchar(255) (sender email address)"
                      },
                      {
                        "k": "to_emails",
                        "v": "text[] (array of recipient emails)"
                      },
                      {
                        "k": "cc_emails",
                        "v": "text[] (nullable, carbon copy)"
                      },
                      {
                        "k": "bcc_emails",
                        "v": "text[] (nullable, blind carbon copy - not visible to other recipients)"
                      },
                      {
                        "k": "subject",
                        "v": "text (email subject line)"
                      },
                      {
                        "k": "snippet",
                        "v": "text (first 100 chars of body for preview)"
                      },
                      {
                        "k": "has_attachment",
                        "v": "boolean (true if attachments present)"
                      },
                      {
                        "k": "url_attachment",
                        "v": "text (S3 URLs to attachments, comma-separated)"
                      },
                      {
                        "k": "folder",
                        "v": "varchar(50) (INBOX, SENT, DRAFTS, SPAM, TRASH, or custom label)"
                      },
                      {
                        "k": "is_read",
                        "v": "boolean DEFAULT false"
                      },
                      {
                        "k": "is_starred",
                        "v": "boolean DEFAULT false"
                      },
                      {
                        "k": "received_at",
                        "v": "timestamptz (when email arrived in mailbox)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (user_id, folder, received_at DESC) for folder listing, INDEX on (user_id, thread_id) for conversation view"
                      },
                      {
                        "k": "Sharding",
                        "v": "Shard by user_id (user's emails on same shard for fast queries)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Outbox (pending email sends)",
                    "pairs": [
                      {
                        "k": "Composite PK",
                        "v": "(user_id, email_id)"
                      },
                      {
                        "k": "user_id",
                        "v": "uuid FK \u2192 Users"
                      },
                      {
                        "k": "email_id",
                        "v": "uuid"
                      },
                      {
                        "k": "message_id",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "thread_id",
                        "v": "uuid (if reply, links to existing thread)"
                      },
                      {
                        "k": "from_email",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "to_emails",
                        "v": "text[]"
                      },
                      {
                        "k": "subject",
                        "v": "text"
                      },
                      {
                        "k": "body",
                        "v": "text (full email body, HTML or plain text)"
                      },
                      {
                        "k": "has_attachment",
                        "v": "boolean"
                      },
                      {
                        "k": "url_attachment",
                        "v": "text (S3 URLs)"
                      },
                      {
                        "k": "scheduled_at",
                        "v": "timestamptz (when to send, for retry scheduling)"
                      },
                      {
                        "k": "retry_count",
                        "v": "int DEFAULT 0 (number of send attempts)"
                      },
                      {
                        "k": "status",
                        "v": "enum (PENDING, SENDING, SENT, FAILED)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "Purpose",
                        "v": "Durable queue for outbound emails, ensures no email lost on server crash"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Threads (conversation grouping)",
                    "pairs": [
                      {
                        "k": "thread_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "subject",
                        "v": "text (subject of first email in thread)"
                      },
                      {
                        "k": "participant_emails",
                        "v": "text[] (all unique emails in thread - from, to, cc)"
                      },
                      {
                        "k": "email_ids",
                        "v": "uuid[] (ordered array of email IDs in thread, oldest to newest)"
                      },
                      {
                        "k": "last_updated_at",
                        "v": "timestamptz (when last email added)"
                      },
                      {
                        "k": "message_count",
                        "v": "int (number of emails in thread)"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on participant_emails (GIN index for array containment queries)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Users",
                    "pairs": [
                      {
                        "k": "user_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "email_address",
                        "v": "varchar(255) UNIQUE (user@gmail.com)"
                      },
                      {
                        "k": "password_hash",
                        "v": "varchar(255) (bcrypt hashed)"
                      },
                      {
                        "k": "name",
                        "v": "varchar(255)"
                      },
                      {
                        "k": "quota_limit",
                        "v": "bigint (15GB = 15 \u00d7 1024\u00b3 bytes for free Gmail)"
                      },
                      {
                        "k": "quota_used",
                        "v": "bigint (bytes used, updated on email/attachment storage)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "last_login_at",
                        "v": "timestamptz"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Attachments",
                    "pairs": [
                      {
                        "k": "attachment_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "email_id",
                        "v": "uuid FK \u2192 Mailbox_items"
                      },
                      {
                        "k": "filename",
                        "v": "varchar(255) (presentation.pdf)"
                      },
                      {
                        "k": "size_bytes",
                        "v": "bigint (5MB = 5 \u00d7 1024\u00b2)"
                      },
                      {
                        "k": "content_type",
                        "v": "varchar(100) (application/pdf, image/jpeg)"
                      },
                      {
                        "k": "s3_url",
                        "v": "text (s3://email-attachments/{user_id}/{email_id}/{filename})"
                      },
                      {
                        "k": "virus_scan_status",
                        "v": "enum (PENDING, CLEAN, INFECTED)"
                      },
                      {
                        "k": "uploaded_at",
                        "v": "timestamptz"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Kafka Topics",
                    "pairs": [
                      {
                        "k": "email.outbound",
                        "v": "Outbound emails to be sent (100 partitions by user_id hash), consumed by Outbox Delivery Orchestrator"
                      },
                      {
                        "k": "email.smtp.high",
                        "v": "High priority SMTP delivery queue (same domain, known contacts), 50 SMTP workers"
                      },
                      {
                        "k": "email.smtp.normal",
                        "v": "Normal priority SMTP queue (external first-time contacts), 100 workers"
                      },
                      {
                        "k": "email.smtp.low",
                        "v": "Low priority SMTP queue (bulk/newsletters), 20 workers, rate limited"
                      },
                      {
                        "k": "email.inbound",
                        "v": "Received emails from external SMTP, consumed by Mailbox Receiver DB service"
                      },
                      {
                        "k": "email.indexed",
                        "v": "Emails to be indexed in Elasticsearch, consumed by Elastic Search Svc"
                      },
                      {
                        "k": "email.received",
                        "v": "New email notification events, consumed by Notification Svc for push/email alerts"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Elasticsearch Index",
                    "pairs": [
                      {
                        "k": "Index name",
                        "v": "emails"
                      },
                      {
                        "k": "Fields",
                        "v": "user_id (keyword), email_id (keyword), from_email (keyword), to_emails (keyword array), subject (text with ngram), body (text with standard analyzer + stemming), snippet (text), has_attachment (boolean), folder (keyword), sent_at (date)"
                      },
                      {
                        "k": "Sharding",
                        "v": "10 shards per index, route by user_id hash"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "8. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Outbox pattern - Durable queue for outbound emails (INSERT into outbox \u2192 Kafka publish \u2192 SMTP send \u2192 DELETE from outbox), prevents email loss on server crash, enables retry with exponential backoff",
                  "Technique 2: SLA-based priority queues - 3 Kafka topics (high/normal/low priority), high priority for same domain (gmail\u2192gmail) and known contacts (<1 min delivery), normal for external (5 min), low for bulk (30 min, rate limited), worker allocation: 50/100/20 for high/normal/low",
                  "Technique 3: Consistent hashing - Hash emails by sending_user_id to same Kafka partition, ensures send order preserved (user sends Email A then Email B \u2192 A delivered before B), prevents out-of-order delivery",
                  "Technique 4: Database sharding - Mailbox_items sharded by user_id (user's emails on same shard), enables horizontal scaling to billions of emails, shard routing: hash(user_id) mod N",
                  "Technique 5: Elasticsearch for search - Full-text search on subject/body with stemming (meeting \u2192 meet), ngram for autocomplete, aggregations for faceted filters (folder, has_attachment, date range), 10 shards per index, <100ms query latency",
                  "Technique 6: S3 for attachments - Store attachments in blob storage (not DB), presigned URLs for direct upload (bypasses server), lifecycle policies (archive to Glacier after 1 year), saves 90% cost for old attachments, virus scan before allowing download",
                  "Technique 7: Kafka event-driven architecture - Decouples services (Mail Send \u2192 SMTP Workers \u2192 Mailbox Receiver \u2192 Search Indexer \u2192 Notification), async processing (user gets 200 OK immediately, actual delivery happens later), enables horizontal scaling (add more consumers)",
                  "Technique 8: SMTP connection pooling - Maintain persistent connections to frequently contacted domains (gmail.com \u2192 outlook.com reuses TCP connection), reduces handshake overhead (TLS setup ~200ms), connection pool size: 100 per SMTP worker",
                  "Technique 9: Spam filtering pipeline - Multi-stage: (1) IP reputation check (blacklist lookup <10ms), (2) SPF/DKIM/DMARC validation (DNS queries ~50ms), (3) Content analysis (Bayesian filter ~100ms), (4) Link/attachment scan (~200ms), total <400ms, parallel processing where possible",
                  "Technique 10: Thread detection caching - Cache recent threads in Redis (key: message_id, value: thread_id), TTL 7 days, avoids DB query on every reply, 95% cache hit rate for active conversations",
                  "Technique 11: Read replica scaling - 1 master (writes: new emails, outbox updates) + 10 read replicas (reads: inbox listing, search results, thread views), read/write split, replicas lag <1 sec (acceptable for email viewing)",
                  "Technique 12: Rate limiting - Per user: 500 emails/day for free accounts, 2000/day for paid, per IP: 100 requests/min to prevent spam bots, per domain: Gradual ramp-up when sending to new domain (first 10 emails slow, then increase if no bounces)"
                ]
              },
              {
                "id": "interview-qa",
                "title": "9. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "How do you ensure reliable email delivery with retry mechanisms and handle failures?",
                    "a": "Reliable email delivery with Outbox pattern and retry logic: (1) Outbox pattern (durable queue): When user sends email, INSERT INTO outbox table (durable storage), Publish to Kafka 'email.outbound', SMTP worker processes, If successful: DELETE FROM outbox, If failed: Keep in outbox for retry, Guarantees: Email survives server crash (persisted in DB), At-least-once delivery (retry until success or max attempts). (2) Retry strategy: Exponential backoff: 1st retry after 1 min, 2nd after 5 min, 3rd after 15 min, max 10 attempts over 24 hours, Retry conditions: Temporary failures (4xx SMTP codes like 421 Service unavailable, 450 Mailbox busy), Network timeouts, Receiver server down. (3) Failure handling: Permanent failures (5xx codes like 550 User not found, 552 Mailbox full): Stop retrying, Mark status='FAILED', Send bounce notification to sender: 'Delivery failed: User alice@yahoo.com does not exist', INSERT into sender's inbox as system message. (4) Dead Letter Queue (DLQ): After max retries (10 attempts, 24 hours), Move to DLQ Kafka topic: 'email.failed', Manual investigation by ops team, Possible issues: Misconfigured recipient server, Persistent network partition, Invalid recipient that passed initial validation. (5) Delivery confirmation: SMTP 250 OK response \u2192 email accepted by receiver, UPDATE outbox SET status='SENT', sent_at=now(), DELETE FROM outbox, Receiver may still reject later (after accepting via SMTP) due to spam filters, Track final delivery via DSN (Delivery Status Notification) if supported. (6) Idempotency: Same email_id sent multiple times (due to retries) \u2192 receiver deduplicates by message_id header: Message-ID: <{email_id}@gmail.com>, Prevents duplicate emails in user's inbox. Result: Guaranteed delivery with at-least-once semantics, graceful failure handling with user notification, durable queue survives failures, exponential backoff prevents overwhelming receiver."
                  },
                  {
                    "q": "How does the Outbox Delivery Orchestrator implement SLA-based priority routing?",
                    "a": "SLA-based priority routing with 6 scenarios: (1) Priority determination: HIGH priority (target <1 min delivery): Same domain (gmail.com \u2192 gmail.com, internal routing), Known contacts (recipient in sender's contacts, auto-suggestion enabled), VIP senders (emails from boss@company.com), NORMAL priority (target 5 min): External first-time contact (gmail.com \u2192 yahoo.com, no prior interaction), Subject doesn't contain urgent keywords, LOW priority (target 30 min): Bulk sends (>100 recipients), Newsletter emails (detected by unsubscribe link, bulk headers), Marketing campaigns (tagged by sender). (2) Implementation: Outbox Delivery Orchestrator consumes 'email.outbound', Determines priority: IF (recipient_domain == sender_domain): priority = 'HIGH' // Same domain, ELSE IF (recipient IN sender_contacts): priority = 'HIGH' // Known contact, ELSE IF (subject CONTAINS ['urgent', 'asap', 'immediate']): priority = 'ELEVATED', ELSE IF (recipient_count > 100 OR 'List-Unsubscribe' header present): priority = 'LOW', ELSE: priority = 'NORMAL', Routes to Kafka topic: email.smtp.{priority}, Kafka.send('email.smtp.high', emailPayload) or 'email.smtp.normal' or 'email.smtp.low'. (3) Worker allocation: HIGH: 50 SMTP workers (dedicated, always available for fast delivery), NORMAL: 100 workers (majority of traffic, balanced capacity), LOW: 20 workers (rate limited, processes during off-peak to avoid impacting high/normal). (4) Auto-suggestion correlation: Scenarios 1-6 from image all reference auto-suggestion, Auto-suggestion = recipient appears in sender's contact list or has prior email interaction, Enables HIGH priority routing (faster delivery for known contacts improves user experience). (5) Rate limiting per priority: HIGH: No rate limit (immediate processing), NORMAL: 1000 emails/min per domain (gradual ramp-up for new domains), LOW: 100 emails/min per domain (prevents spam, spreads load). (6) Monitoring: Track SLA compliance: % of HIGH priority emails delivered <1 min (target 99%), % of NORMAL delivered <5 min (target 95%), % of LOW delivered <30 min (target 90%), Alert if SLA violated (queue backlog, worker shortage). Result: Fast delivery for important emails (same domain, known contacts), efficient resource allocation (most workers on normal priority), abuse prevention (bulk emails rate limited), SLA-driven user experience."
                  },
                  {
                    "q": "Walk through the complete SMTP delivery process with SPF, DKIM, and DMARC validation.",
                    "a": "Complete SMTP delivery with authentication: (1) DNS MX lookup: SMTP worker needs to send to alice@outlook.com, Query DNS: dig MX outlook.com, Response: outlook-com.olc.protection.outlook.com (priority 5) with IP addresses, Select lowest priority MX server (priority 5 = highest priority in MX records, lower number = higher priority). (2) TCP connection: Connect to receiver SMTP server: TCP connection to outlook-com.olc.protection.outlook.com:25 (SMTP), Or port 587 (submission), port 465 (SMTPS with TLS), TLS handshake: STARTTLS command \u2192 upgrade to encrypted connection, Prevents email interception (MITM attacks). (3) SMTP handshake: Client (Gmail SMTP worker): EHLO gmail.com, Server (Outlook): 250-outlook.com Hello [74.125.0.1], Server lists supported extensions: 250-STARTTLS, 250-AUTH PLAIN LOGIN, 250-SIZE 35000000 (max 35MB email). (4) Sender verification: Client: MAIL FROM:<sender@gmail.com>, Server: 250 OK (accepts sender, may check later via SPF). (5) Recipient verification: Client: RCPT TO:<alice@outlook.com>, Server checks local delivery table: SELECT user_id FROM users WHERE email='alice@outlook.com', If exists: 250 OK, If not exists: 550 User not found (reject, sender gets bounce). (6) Email transmission: Client: DATA, Server: 354 Start mail input; end with <CRLF>.<CRLF>, Client sends headers + body: From: sender@gmail.com, To: alice@outlook.com, Subject: Meeting Tomorrow, Date: Mon, 26 Jan 2026 10:30:00 -0800, Message-ID: <E123@gmail.com>, DKIM-Signature: v=1; a=rsa-sha256; d=gmail.com; s=default; h=from:to:subject; bh=base64hash; b=signaturebase64, Content-Type: multipart/mixed (if attachments), Blank line, Email body (plain text or HTML), Attachments (Base64 encoded), Client: . (period on line by itself = end of message). (7) SPF validation (Sender Policy Framework): Server queries DNS: dig TXT gmail.com \u2192 spf.gmail.com, Response: v=spf1 ip4:74.125.0.0/16 ip4:74.125.224.0/19 ~all, Server checks: Is sending IP (74.125.0.1) in allowed ranges?, Result: PASS (IP authorized) or FAIL (unauthorized IP, likely spoofed), If FAIL: May reject email or mark as spam. (8) DKIM validation (DomainKeys Identified Mail): Server extracts DKIM-Signature header from email: d=gmail.com (signing domain), s=default (selector, identifies which key to use), h=from:to:subject (signed headers), bh=hash of body, b=signature of headers, Server fetches public key: dig TXT default._domainkey.gmail.com, Response: Public key (RSA or Ed25519), Server verifies: Hash email body \u2192 compare with bh, Sign headers using public key \u2192 compare with b, Result: PASS (signature valid, email not tampered) or FAIL (signature invalid, email modified). (9) DMARC validation (Domain-based Message Authentication): Server queries: dig TXT _dmarc.gmail.com, Response: v=DMARC1; p=reject; rua=mailto:dmarc@gmail.com (policy=reject strict, report aggregate to dmarc@gmail.com), Server checks alignment: Does 'From' header domain (gmail.com) match SPF/DKIM domain?, SPF: MAIL FROM domain = gmail.com? (aligned), DKIM: d= domain = gmail.com? (aligned), If SPF PASS AND aligned: DMARC PASS, If DKIM PASS AND aligned: DMARC PASS, If both FAIL + policy=reject: Reject email (550 DMARC policy violation), If both FAIL + policy=quarantine: Accept but mark as spam, If both FAIL + policy=none: Accept, log only. (10) Final acceptance: If all checks pass (SPF, DKIM, DMARC, spam filters), Server: 250 OK Message accepted for delivery, Client: QUIT, Server: 221 Goodbye (close connection). (11) Post-delivery: Server inserts email into alice@outlook.com's mailbox, Sends DMARC aggregate report to gmail.com (daily summary of auth results), Gmail tracks delivery confirmation, updates outbox status='SENT'. Result: Authenticated email delivery with anti-spoofing protection, SPF prevents IP spoofing, DKIM prevents email tampering, DMARC enforces policy, ensures legitimate emails delivered, spam/phishing blocked."
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Outbox pattern mandatory for reliable email delivery. NEVER publish to Kafka without persisting to Outbox DB first. If Kafka publish succeeds but server crashes before DB commit \u2192 email lost. Outbox guarantees at-least-once delivery (email survives crashes, retries until success)."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers ALWAYS ask: 'How ensure email delivery?'. Answer: Outbox pattern (durable DB queue) + Kafka + SMTP workers with exponential backoff retry (1min, 5min, 15min, max 10 attempts 24hr). Failures \u2192 bounce notification. DLQ for manual investigation. Idempotency via message_id header."
              },
              {
                "type": "tip",
                "text": "SLA-based priority routing: HIGH (same domain, known contacts, <1min, 50 workers), NORMAL (external first-time, 5min, 100 workers), LOW (bulk/newsletters, 30min, 20 workers). Auto-suggestion correlates with known contacts \u2192 HIGH priority for better UX."
              },
              {
                "type": "interview-favorite",
                "text": "SMTP authentication triple-check: SPF (validates sending IP authorized for domain, DNS TXT spf.gmail.com), DKIM (validates email not tampered, signature verification with public key from DNS), DMARC (enforces policy: reject/quarantine/none, requires SPF or DKIM alignment)."
              },
              {
                "type": "warning",
                "text": "NEVER store email body in Kafka messages. Email body can be large (25MB with attachments). Kafka message size limit 1MB. Store in DB, pass only email_id in Kafka. SMTP worker fetches full email from DB when processing. Keeps Kafka lightweight, prevents broker overload."
              },
              {
                "type": "tip",
                "text": "Thread detection: Check In-Reply-To and References headers for existing message_id. If match \u2192 UPDATE thread (append email_id to array). Else \u2192 CREATE new thread. Cache recent threads in Redis (message_id \u2192 thread_id, 7 day TTL, 95% hit rate) avoids DB query on every reply."
              },
              {
                "type": "interview-favorite",
                "text": "Database sharding by user_id: User's emails on same shard (fast folder listing, thread view). Partition key: hash(user_id) mod N. Cross-shard query only for global search (uses Elasticsearch). Enables horizontal scaling to billions of users, each shard handles millions."
              }
            ],
            "tags": [
              "system-design",
              "email-system",
              "gmail",
              "outlook",
              "smtp-protocol",
              "outbox-pattern",
              "kafka-queues",
              "spf-dkim-dmarc",
              "email-authentication",
              "spam-filtering",
              "priority-routing",
              "elasticsearch-search",
              "reliable-delivery"
            ]
          }
        },
        {
          "id": "21",
          "title": "System Design 21: Design a Distributed Rate Limiter | Token Bucket | Leaky Bucket | Sliding Window | HLD | LLD",
          "videoUrl": "https://youtu.be/SfV42UuZEBY",
          "description": "In this video, we cover the complete end-to-end design of a production-grade Rate Limiting system \u2014 not just theory, but real implementation decisions that senior engineers and interviewers expect you to know.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/ibqrhgbqirug2i4ubti3bt894t3048t30t349t3ti34bt3jtb3itbi.png?updatedAt=1778168045811",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/ratelimiter.png?updatedAt=1778167592395",
          "refresher": {
            "label": "Pre-Interview Memory Refresher",
            "estimatedMinutes": 20,
            "lastUpdated": "2026-01-26",
            "hero": {
              "title": "Rate Limiter System",
              "oneLinerToMemorize": "Client request \u2192 API Gateway checks auth \u2192 Rate Limiter queries Redis (token bucket/sliding window) \u2192 if allowed (tokens available) \u2192 forward to Backend \u2192 if exceeded \u2192 429 Too Many Requests with retry-after header \u2192 Policy updates propagated via write-through cache"
            },
            "sections": [
              {
                "id": "functional-requirements",
                "title": "1. Functional Requirements",
                "type": "bullets",
                "items": [
                  "Feature 1: Limit number of requests a client (by User ID / API Key / IP) can make within a time window",
                  "Feature 2: Rate limit rules must be configurable at runtime (update limits without redeployment)",
                  "Feature 3: When the limit is exceeded, return HTTP 429 Too Many Requests with retry-after header",
                  "Feature 4: Support different rate limits based on client tier (Free: 100 req/min, Premium: 1000 req/min)",
                  "Feature 5: Support different limiters per client tier (Free/Premium) with granular control"
                ]
              },
              {
                "id": "non-functional-requirements",
                "title": "2. Non-Functional Requirements",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Scale & Performance",
                    "pairs": [
                      {
                        "k": "Scale",
                        "v": "1 Million requests per second (rps) - high-traffic APIs"
                      },
                      {
                        "k": "CAP",
                        "v": "Availability >> Consistency (eventual consistency acceptable, slight over-limit tolerable)"
                      },
                      {
                        "k": "Latency",
                        "v": "Ultra low <5ms per request added latency (rate limit check must be fast, not bottleneck)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-entity",
                "title": "3. Core Entity",
                "type": "bullets",
                "items": [
                  "Entity 1: RateLimitPolicy/Rules - rule_id, subject_type (USER/API_KEY/IP), subject_value (specific user ID or global), limit (100 requests), window (60 seconds), endpoint (*/api/login for specific endpoint), created_at",
                  "Entity 2: Client - client_id, tier (FREE/PREMIUM), api_key, quota (rate limit), created_at",
                  "Entity 3: RequestCounter - Tracks request count per client per window, stored in Redis for fast access"
                ]
              },
              {
                "id": "api-designing",
                "title": "4. API Designing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Admin Operations (Create rules)",
                    "pairs": [
                      {
                        "k": "POST /v1/admin/rules",
                        "v": "Create rate limit rule with {subjectType, subjectValue, limit, window, endpoint}"
                      },
                      {
                        "k": "GET /v1/admin/rules?clientId={xyz} (CRUD)",
                        "v": "Get rate limit rules for specific client"
                      },
                      {
                        "k": "Admin: GET /v1/admin/rate-limit/metrics?resource={url}",
                        "v": "Get rate limiting metrics for monitoring"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "high-level-design",
                "title": "5. High Level Design (HLD)",
                "type": "bullets",
                "items": [
                  "Architecture: client \u2192 Load Balancer \u2192 Rate Limiter (with API Gateway) \u2192 Backend Svc (protected resources)",
                  "Load Balancer: Distributes traffic across multiple Rate Limiter instances, health checks",
                  "Rate Limiter + API Gateway: Core component, checks auth, queries Redis for rate limit state, enforces limits before forwarding",
                  "Backend Svc: Protected API services (multiple instances), only receives allowed requests",
                  "Algorithms supported: Fixed Window Counter, Sliding Window Log, Sliding Window Counter, Token Bucket (most common), Leaky Bucket"
                ]
              },
              {
                "id": "deep-dive-lld",
                "title": "6. Deep Dive Design (Low Level - LLD)",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Algorithm 1: Fixed Window Counter",
                    "steps": [
                      "How it works: Divide time into fixed windows (e.g., every 60s), count requests per window, reset counter at window boundary",
                      "Example: Window: 10:00:00-10:00:59, limit: 100 requests, at 10:00:10 \u2192 50 requests \u2192 allow, at 10:00:58 \u2192 99 requests \u2192 allow, at 10:01:00 \u2192 reset counter to 0 (new window starts)",
                      "Implementation: Redis key: rate_limit:{user_id}:{window_start_timestamp}, Value: request count (integer), Increment: INCR rate_limit:user123:1706270400 (window starting at Unix timestamp 1706270400), Set TTL: EXPIRE rate_limit:user123:1706270400 60 (auto-delete after window expires), Check: current_count = GET rate_limit:user123:1706270400, if current_count < limit \u2192 allow, else \u2192 reject 429",
                      "Pros: Simple + easy to implement, low memory (one counter per window), cons: boundary problem (spike at window edges: 99 requests at 10:00:59 + 100 requests at 10:01:00 = 199 requests in 2 seconds, violates spirit of 100/min limit)"
                    ]
                  },
                  {
                    "blockTitle": "Algorithm 2: Sliding Window Log",
                    "steps": [
                      "How it works: Store timestamp of every request, count logs in last N seconds (true sliding window)",
                      "Example: Current time: 10:00:45, window: 60s (last minute), stored timestamps: [10:00:05, 10:00:12, 10:00:30, ...], filter: only timestamps >= 10:00:45 - 60s = 09:59:45, count: 85 requests in last 60s, allow only if count < 100",
                      "Implementation: Redis sorted set (ZSET), Key: rate_limit_log:user123, Members: request_id (or UUID), Scores: timestamp (Unix time in seconds), Add request: ZADD rate_limit_log:user123 {current_timestamp} {request_id}, Remove old: ZREMRANGEBYSCORE rate_limit_log:user123 0 {current_timestamp - window_size} (cleanup logs older than window), Count: ZCOUNT rate_limit_log:user123 {current_timestamp - 60} {current_timestamp} \u2192 returns count in last 60 sec, if count < limit \u2192 ZADD (allow + store), else \u2192 reject 429",
                      "Pros: Highly accurate (true rolling window, no boundary problem), cons: expensive memory (stores every request timestamp, 100 req/min \u00d7 millions users = massive storage), cleanup overhead"
                    ]
                  },
                  {
                    "blockTitle": "Algorithm 3: Sliding Window Counter",
                    "steps": [
                      "How it works: Hybrid of fixed window + sliding window, calculate weighted count from current window and previous window depending on how much current window has elapsed",
                      "Example: Current time: 10:00:45, Previous window (09:59:00-09:59:59): 80 requests, Current window (10:00:00-10:00:59): 30 requests, Elapsed in current window: 45 seconds (75% of 60s window), Weighted count: (previous_window_count \u00d7 (1 - elapsed%)) + current_window_count = (80 \u00d7 (1 - 0.75)) + 30 = (80 \u00d7 0.25) + 30 = 20 + 30 = 50 requests, if 50 < 100 \u2192 allow",
                      "Implementation: Two Redis keys: rate_limit:user123:{current_window}, rate_limit:user123:{previous_window}, Calculate: elapsed_pct = (current_time % window_size) / window_size, weighted_count = (prev_count \u00d7 (1 - elapsed_pct)) + curr_count, if weighted_count < limit \u2192 INCR current window, else \u2192 reject",
                      "Pros: More accurate than fixed window (smooths boundary spikes), slightly more complex than fixed window, cons: not always some as typical API rate limiting expectation, approximation (not 100% accurate like sliding log)"
                    ]
                  },
                  {
                    "blockTitle": "Algorithm 4: Token Bucket (MOST COMMON - recommended)",
                    "steps": [
                      "How it works: Bucket contains tokens (capacity), tokens refill at steady rate (refill_rate tokens/sec), each request consumes one token, if tokens available \u2192 allow + consume, else \u2192 reject",
                      "Example: Capacity: 100 tokens, Refill rate: 10 tokens/sec (600 tokens/min = 100 req/10sec avg), Scenario: Burst: 100 requests in 1 sec (consumes all 100 tokens) \u2192 all allowed (burst supported), After 10 sec: bucket refilled to 100 tokens (10 tokens/sec \u00d7 10 sec), Steady: 10 req/sec sustained \u2192 allowed (matches refill rate)",
                      "Implementation (Redis Lua script for atomicity): local key = KEYS[1], local capacity = tonumber(ARGV[1]) -- 100, local refill_rate = tonumber(ARGV[2]) -- 10 tokens/sec, local now = tonumber(ARGV[3]) -- current timestamp, local tokens = tonumber(redis.call('HGET', key, 'tokens')) or capacity, local last_refill = tonumber(redis.call('HGET', key, 'last_refill')) or now, local elapsed = now - last_refill, local refilled = math.min(capacity, tokens + (elapsed * refill_rate)), if refilled >= 1 then redis.call('HSET', key, 'tokens', refilled - 1), redis.call('HSET', key, 'last_refill', now), redis.call('EXPIRE', key, 3600), return 1 -- allow, else return 0 -- reject",
                      "Pros: Supports bursts naturally (bucket capacity = burst size), smooth refill model (no sudden resets), widely used in real systems (AWS API Gateway, Stripe API), cons: Slightly more complex state management (track tokens + last_refill time)"
                    ]
                  },
                  {
                    "blockTitle": "Algorithm 5: Leaky Bucket",
                    "steps": [
                      "How it works: Requests enter bucket/queue, they leak out at fixed rate (processed), if bucket full \u2192 reject new requests",
                      "Example: Bucket capacity: 100 requests, Leak rate: 10 req/sec, Scenario: 50 requests arrive instantly \u2192 all queued in bucket, Leak: processes 10 req/sec (fixed rate), After 5 sec: all 50 processed, 100 requests arrive instantly \u2192 100 queued (bucket full), 101st request \u2192 rejected (bucket full)",
                      "Implementation: Redis list (queue), LPUSH rate_limit_queue:user123 {request_id} (add to queue), LLEN rate_limit_queue:user123 \u2192 queue_size, if queue_size < capacity \u2192 allow (queue request), else \u2192 reject, Background processor: pops from queue at fixed rate (10/sec), RPOP rate_limit_queue:user123 \u2192 process request",
                      "Pros: Smooth output traffic (fixed processing rate), good for shaping traffic (prevent downstream overload), cons: Can delay/drop requests (not always ideal for synchronous APIs), more complex (requires queue + background processor), some OS typical API rate limiting expectation"
                    ]
                  },
                  {
                    "blockTitle": "Production Implementation (Token Bucket with Redis + Policy DB)",
                    "steps": [
                      "Architecture: Client \u2192 Load Balancer \u2192 API Gateway (Rate Limiter logic) \u2192 Rate Limiter/Policy Svc \u2192 Policy DB (rules) + Redis (token state)",
                      "Request flow: (1) Client: GET /api/users (with API key or user session), (2) API Gateway: Auth check (validate API key/JWT), Extract subject: user_id='user123' OR api_key='key_abc', Call Rate Limiter: check_rate_limit(user_id, endpoint='/api/users'), (3) Rate Limiter fetches policy: Policy Cache (Redis): GET policy:user123 \u2192 {limit: 100, window: 60, refill_rate: 10}, if cache miss \u2192 query Policy DB: SELECT limit, window FROM rate_limit_rules WHERE subject_value='user123' AND endpoint='/api/users', cache result: SET policy:user123 {limit, window, refill_rate} EX 3600 (1 hour TTL), (4) Check token bucket (Redis Lua script): EVAL lua_script 1 'rate_limit:user123' 100 10 {current_timestamp}, Returns: 1 (allow) or 0 (reject), (5) If allowed (1): Forward to Backend Svc, return response to client, If rejected (0): Return 429 Too Many Requests with headers: X-RateLimit-Limit: 100, X-RateLimit-Remaining: 0, X-RateLimit-Reset: 1712340788 (Unix timestamp when limit resets), Retry-After: 30 (seconds until retry allowed), Body: {error: 'rate_limit_exceeded', message: 'You have exceeded 100 requests/min. Retry after 30 seconds.'}",
                      "Policy updates (write-through cache): Admin updates rule: PUT /v1/admin/rules/{ruleId} {limit: 200}, Policy Svc: UPDATE rate_limit_rules SET limit=200, Invalidate cache: DEL policy:user123 (next request fetches new limit), OR write-through: SET policy:user123 {limit: 200, ...} (immediate update), Propagation: All Rate Limiter instances fetch new policy within 1 hour (cache TTL) OR immediately (if write-through + Redis pub/sub notification)"
                    ]
                  },
                  {
                    "blockTitle": "Multi-Tier Rate Limiting (Free vs Premium)",
                    "steps": [
                      "Tier-based rules: Free tier: limit=100 req/min, window=60s, endpoints=['*'] (all endpoints), Premium tier: limit=1000 req/min, window=60s, endpoints=['*'], Enterprise tier: limit=10000 req/min, custom limits per endpoint",
                      "Rule applies to all premium users but quota is tracked + scoped across entire tenant: subject_type='TIER', subject_value='PREMIUM' (shared quota for all premium users globally?), NO - typically per-user: subject_type='USER', subject_value='user123', lookup tier: SELECT tier FROM clients WHERE user_id='user123' \u2192 'PREMIUM', fetch rule: SELECT limit FROM rate_limit_rules WHERE tier='PREMIUM' \u2192 1000 req/min",
                      "Multi-level limiting: Per-user: 100 req/min (individual user limit), Per-IP: 1000 req/min (prevent single IP abuse), Per-API-key: 10000 req/min (API key quota), Per-endpoint: /api/login \u2192 10 req/min (prevent brute force), Global: 1M req/sec (cluster capacity), Check all levels: if ANY limit exceeded \u2192 reject 429, enforce_by field indicates what the limit is scoped by (USER, IP, TENANT_ID, API_KEY) - tenant_id means the quota is shared across the entire tenant"
                    ]
                  }
                ]
              },
              {
                "id": "database-schema",
                "title": "7. Database Schema Details",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Rate_limit_rules (Policy DB)",
                    "pairs": [
                      {
                        "k": "rule_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "subject_type",
                        "v": "enum (USER, API_KEY, IP, TIER, GLOBAL) - what the limit applies to"
                      },
                      {
                        "k": "subject_value",
                        "v": "varchar(255) - specific value (user_id='user123', api_key='key_abc', tier='PREMIUM', global='*')"
                      },
                      {
                        "k": "endpoint",
                        "v": "varchar(500) - API endpoint pattern ('*' for all, '/api/login' for specific, '/api/*' for prefix)"
                      },
                      {
                        "k": "request_limit",
                        "v": "int - number of requests allowed (100, 1000)"
                      },
                      {
                        "k": "window_sec",
                        "v": "int - time window in seconds (60 for 1 min, 3600 for 1 hour)"
                      },
                      {
                        "k": "refill_rate",
                        "v": "decimal(10,2) - for token bucket (10.0 tokens/sec)"
                      },
                      {
                        "k": "enforce_by",
                        "v": "varchar(50) - USER, IP, TENANT_ID (quota scope: per-user or shared across tenant)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      },
                      {
                        "k": "Indexes",
                        "v": "INDEX on (subject_type, subject_value, endpoint) for fast rule lookup"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Clients",
                    "pairs": [
                      {
                        "k": "client_id",
                        "v": "uuid PRIMARY KEY"
                      },
                      {
                        "k": "api_key",
                        "v": "varchar(255) UNIQUE (hashed for security)"
                      },
                      {
                        "k": "tier",
                        "v": "enum (FREE, PREMIUM, ENTERPRISE)"
                      },
                      {
                        "k": "quota",
                        "v": "int - rate limit (100 for FREE, 1000 for PREMIUM)"
                      },
                      {
                        "k": "created_at",
                        "v": "timestamptz"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Token State (Token Bucket)",
                    "pairs": [
                      {
                        "k": "rate_limit:{subject}:{endpoint}",
                        "v": "HASH - {tokens: 95, last_refill: 1706270445} (token count + last refill timestamp)"
                      },
                      {
                        "k": "Example",
                        "v": "rate_limit:user123:/api/users \u2192 {tokens: 95, last_refill: 1706270445}"
                      },
                      {
                        "k": "TTL",
                        "v": "3600 sec (1 hour) - auto-expire inactive buckets to save memory"
                      },
                      {
                        "k": "Lua script",
                        "v": "Atomic check + decrement tokens, calculate refill based on elapsed time"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Policy Cache",
                    "pairs": [
                      {
                        "k": "policy:{subject}:{endpoint}",
                        "v": "STRING (JSON) - {limit: 100, window: 60, refill_rate: 10, enforce_by: 'USER'}"
                      },
                      {
                        "k": "Example",
                        "v": "policy:user123:/api/users \u2192 {limit: 100, window: 60, refill_rate: 10}"
                      },
                      {
                        "k": "TTL",
                        "v": "3600 sec (1 hour) - cache policy rules, revalidate hourly or on invalidation"
                      },
                      {
                        "k": "Write-through",
                        "v": "On policy update \u2192 update DB + SET cache (immediate propagation)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Redis - Sliding Window Log (ZSET)",
                    "pairs": [
                      {
                        "k": "rate_limit_log:{subject}",
                        "v": "ZSET - members: request_id (UUID), scores: timestamp (Unix time)"
                      },
                      {
                        "k": "Operations",
                        "v": "ZADD (add request), ZREMRANGEBYSCORE (cleanup old), ZCOUNT (count in window)"
                      },
                      {
                        "k": "Memory",
                        "v": "High - stores every request (not recommended for high-traffic APIs)"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-optimization",
                "title": "8. Scaling & Optimization",
                "type": "bullets",
                "items": [
                  "Technique 1: Redis Lua scripts for atomicity - Single atomic operation (check + decrement tokens), prevents race conditions (multiple requests checking simultaneously), faster than multiple Redis roundtrips",
                  "Technique 2: Policy caching - Cache rules in Redis (1 hour TTL), 99% cache hit rate (rules rarely change), reduces Policy DB load 100\u00d7, write-through on updates (immediate propagation)",
                  "Technique 3: Token Bucket algorithm - Supports bursts naturally (capacity = burst size), smooth refill (no boundary problem), production-proven (AWS, Stripe, GitHub all use token bucket)",
                  "Technique 4: Distributed rate limiting - Redis cluster (master + replicas), consistent hashing (route user123 to same shard), synchronization: Redis atomic operations ensure consistency across API Gateway instances",
                  "Technique 5: Multi-tier rate limiting - Check multiple levels (per-user, per-IP, per-endpoint, global), enforce strictest limit (if any exceeded \u2192 reject), tiered quotas (FREE: 100, PREMIUM: 1000, ENTERPRISE: 10K)",
                  "Technique 6: Horizontal scaling of API Gateway - Stateless API Gateway instances (10-100+), Redis shared state (all instances check same Redis), load balancer distributes requests, auto-scaling based on CPU/latency",
                  "Technique 7: Redis optimization - TTL for inactive buckets (auto-expire after 1 hour idle, saves memory), Pipelining (batch multiple Redis commands), Connection pooling (reuse connections, avoid handshake overhead)",
                  "Technique 8: Graceful degradation - If Redis down: Fail open (allow all requests, log error) OR fail closed (reject all with 503, safer but impacts availability), Circuit breaker (if Redis fails 10\u00d7 \u2192 open circuit for 30 sec, skip Redis checks)",
                  "Technique 9: Rate limit headers - X-RateLimit-Limit: 100 (total quota), X-RateLimit-Remaining: 45 (remaining requests), X-RateLimit-Reset: 1712340788 (Unix timestamp when resets), Retry-After: 30 (seconds to wait), helps clients implement backoff",
                  "Technique 10: Monitoring & alerting - Track: Request count, rejection rate (% of 429 responses), Redis latency (p50, p99), policy cache hit rate, Alert: if rejection rate > 5% (potential attack or wrong limit), if Redis latency > 10ms (performance degradation)",
                  "Technique 11: Dynamic limit adjustment - Auto-scale limits based on load (if cluster at 80% capacity \u2192 reduce limits 20%), DDoS protection (if spike detected \u2192 temporary stricter limits), allowlist (trusted IPs bypass limits)",
                  "Technique 12: Write-through cache updates - On policy change: UPDATE DB + SET Redis cache (both in transaction), publish Redis pub/sub: PUBLISH policy_update {rule_id}, API Gateway instances subscribe, invalidate local cache, ensures <1 sec propagation (vs 1 hour with TTL)"
                ]
              },
              {
                "id": "interview-qa",
                "title": "9. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "Compare the 5 rate limiting algorithms (Fixed Window, Sliding Window Log, Sliding Window Counter, Token Bucket, Leaky Bucket). Which would you choose for a production API and why?",
                    "a": "Comparison of 5 rate limiting algorithms: (1) Fixed Window Counter: Pros: Simple (single counter per window), low memory (one int per user), fast (single INCR), Cons: Boundary problem (spike at edges: 100 req at 10:00:59 + 100 req at 10:01:00 = 200 req in 2 sec), Implementation: Redis INCR + EXPIRE, Use case: Simple internal APIs, not user-facing. (2) Sliding Window Log: Pros: Highly accurate (true rolling window, no boundary issue), perfect precision, Cons: Expensive memory (stores every request timestamp: 100 req/min \u00d7 1M users = 100M timestamps), cleanup overhead (ZREMRANGEBYSCORE on each request), Implementation: Redis ZSET (ZADD, ZCOUNT, ZREMRANGEBYSCORE), Use case: Low-traffic critical APIs where accuracy crucial (fraud detection). (3) Sliding Window Counter: Pros: More accurate than fixed window (smooths boundaries), lower memory than log (2 counters vs all timestamps), Cons: Approximation (not 100% accurate), complexity (weighted calculation), Implementation: Two counters (current + previous window), weighted sum, Use case: Good middle ground, not widely adopted (token bucket preferred). (4) Token Bucket (RECOMMENDED): Pros: Supports bursts naturally (capacity = burst size, e.g., 100 token bucket allows 100 instant requests then refills), smooth refill (10 tokens/sec = steady 600/min), production-proven (AWS API Gateway, Stripe, GitHub), handles spiky traffic well, Cons: Slightly complex (track tokens + last_refill), Implementation: Redis HASH (tokens, last_refill) + Lua script (atomic check + refill calculation), Use case: Production APIs (most common choice). (5) Leaky Bucket: Pros: Smooth output traffic (fixed processing rate), good for traffic shaping (prevent downstream overload), Cons: Can delay/drop requests (queuing), requires background processor (leak at fixed rate), not typical for sync APIs, Implementation: Redis LIST (queue) + background worker, Use case: Traffic shaping, not typical API rate limiting. Production choice: Token Bucket. Reasoning: (1) Supports bursts: Users expect to make 100 requests instantly if quota available (e.g., batch operations), token bucket allows this, fixed/sliding window would reject after hitting limit in burst, (2) Smooth refill: No sudden resets at window boundaries, users can make requests continuously at refill rate (10/sec), (3) Industry standard: AWS, Stripe, GitHub all use token bucket, proven at scale (billions of requests/day), (4) Good UX: Burst + refill feels natural to users, (5) Reasonable complexity: Redis Lua script handles atomicity, not much more complex than fixed window. Implementation example (Token Bucket): Capacity: 100 tokens, Refill: 10 tokens/sec, Scenario: User makes 100 requests in 1 sec (burst) \u2192 all allowed (consumes all 100 tokens), After 5 sec: bucket refilled to 50 tokens (10 tokens/sec \u00d7 5 sec), User makes 50 requests \u2192 all allowed, User makes 51st request \u2192 rejected (bucket empty), After 1 sec: 10 tokens refilled, user can make 10 more requests. Result: Flexible (supports bursts), fair (smooth refill), production-ready (proven algorithm)."
                  },
                  {
                    "q": "How do you implement distributed rate limiting across multiple API Gateway instances using Redis? Handle race conditions and ensure consistency.",
                    "a": "Distributed rate limiting with Redis: (1) Challenge: Multiple API Gateway instances (10-100+), each handling 10K req/sec, all must enforce same limit (100 req/min per user), without coordination \u2192 race conditions (2 instances allow request simultaneously \u2192 user gets 101 requests in window). (2) Redis as shared state: All API Gateway instances connect to same Redis cluster, Token state stored in Redis: rate_limit:user123 \u2192 {tokens: 95, last_refill: 1706270445}, Every rate limit check queries Redis (centralized truth). (3) Race condition problem: Request 1 (Instance A): GET tokens \u2192 1 token remaining, allow request, Request 2 (Instance B): Simultaneously GET tokens \u2192 also sees 1 token, also allows, DECR tokens (both execute), Result: tokens = -1 (over-limit, race condition!). (4) Solution: Redis Lua script (atomic execution): lua_script = local key = KEYS[1], local capacity = tonumber(ARGV[1]), local refill_rate = tonumber(ARGV[2]), local now = tonumber(ARGV[3]), local bucket = redis.call('HMGET', key, 'tokens', 'last_refill'), local tokens = tonumber(bucket[1]) or capacity, local last_refill = tonumber(bucket[2]) or now, local elapsed = now - last_refill, local refilled = math.min(capacity, tokens + (elapsed * refill_rate)), if refilled >= 1 then redis.call('HMSET', key, 'tokens', refilled - 1, 'last_refill', now), redis.call('EXPIRE', key, 3600), return 1 -- allow, else return 0 -- reject, end, API Gateway call: result = redis.eval(lua_script, 1, 'rate_limit:user123', 100, 10, current_timestamp), Atomicity: Lua script runs as single atomic operation in Redis, no other command can interleave, guarantees: only one instance decrements token, race condition prevented. (5) Consistency guarantees: Redis single-threaded execution: Commands processed serially (one at a time), Lua scripts: Execute atomically (all-or-nothing), even across Redis Cluster, Result: Distributed consistency without locks, CAP tradeoff: CP within Redis (consistent), AP across system (if Redis down, fail open for availability). (6) Redis Cluster (horizontal scaling): Multiple Redis shards (master + replicas), Consistent hashing: hash(user_id) \u2192 shard (user123 always routes to same shard), Read from replicas: GET operations can use replicas (reduce master load), Write to master: EVAL (Lua script) must go to master (consistency), Replication lag: <1ms (acceptable slight over-limit during lag). (7) Performance optimization: Connection pooling: Each API Gateway maintains 50 Redis connections (reuse, avoid handshake), Pipelining: Batch multiple rate limit checks (if checking 10 users \u2192 send 10 EVAL in one round-trip), Redis latency: p99 <5ms (fast enough for rate limiting, doesn't bottleneck API), Local cache: Cache policy rules (limit, refill_rate) locally (avoid Redis query for policy, only check token state). (8) Failure handling: Redis unavailable: Circuit breaker: After 10 consecutive Redis failures \u2192 open circuit for 30 sec, During open circuit: Fail open (allow all requests, log error) OR fail closed (reject with 503), Auto-recovery: After 30 sec \u2192 half-open (try one request), if succeeds \u2192 close circuit (resume normal), Monitoring: Alert if Redis errors > 1% (investigate), track latency (if p99 > 10ms \u2192 scaling issue). (9) Multi-region: Redis in each region: us-east-1, eu-west-1, asia-south-1 (low latency), Global rate limit: Aggregate across regions (complex, eventual consistency), OR per-region limit: 100 req/min per region (simpler, slightly relaxed global limit). Result: Distributed rate limiting with strong consistency (Redis atomic Lua), scales horizontally (Redis Cluster + multiple API Gateways), low latency (<5ms), handles failures gracefully (circuit breaker)."
                  },
                  {
                    "q": "Walk through complete request flow: client makes request \u2192 rate limiter checks \u2192 either allowed or rejected. Include policy lookup, token bucket check, and 429 response with headers.",
                    "a": "Complete rate limiting flow (Token Bucket): (1) Client request: GET /api/users (with header: Authorization: Bearer {jwt_token}), Load Balancer: Routes to API Gateway instance (round-robin). (2) API Gateway authentication: Extract JWT: Parse token, validate signature, extract user_id: 'user123', (or extract api_key from header: X-API-Key). (3) Rate Limiter: Identify subject: user_id='user123', endpoint='/api/users', Fetch policy (with caching): Check Redis cache: policy:user123:/api/users, if cache hit: policy = {limit: 100, window: 60, refill_rate: 10, enforce_by: 'USER'}, if cache miss: Query Policy DB: SELECT limit, window, refill_rate, enforce_by FROM rate_limit_rules WHERE (subject_type='USER' AND subject_value='user123' AND endpoint='/api/users') OR (subject_type='USER' AND subject_value='user123' AND endpoint='*') OR (subject_type='TIER' AND subject_value=(SELECT tier FROM clients WHERE user_id='user123') AND endpoint='*'), Fetch first matching rule (specific endpoint > wildcard, user > tier), Cache: SET policy:user123:/api/users {limit: 100, window: 60, refill_rate: 10} EX 3600. (4) Token Bucket check (Redis Lua script): Prepare: key = 'rate_limit:user123:/api/users', capacity = 100, refill_rate = 10, now = current_unix_timestamp, Execute: result = redis.eval(lua_script, 1, key, capacity, refill_rate, now), Lua script logic: Fetch bucket: tokens, last_refill = redis.call('HMGET', key, 'tokens', 'last_refill'), Calculate refill: elapsed = now - last_refill (e.g., 5 seconds since last request), refilled = min(capacity, tokens + elapsed \u00d7 refill_rate) = min(100, 90 + 5\u00d710) = min(100, 140) = 100 tokens (capped at capacity), Check: if refilled >= 1 (100 >= 1 \u2192 true), Consume: tokens = 100 - 1 = 99, Update: redis.call('HMSET', key, 'tokens', 99, 'last_refill', now), Return: 1 (allow). (5) Request allowed: result = 1 (allow), Calculate remaining: remaining = 99 tokens, Calculate reset time: reset = now + (capacity - remaining) / refill_rate = now + (100 - 99) / 10 = now + 0.1 sec (when bucket fully refilled), Forward request: Proxy to Backend Svc: GET /api/users, Add headers: X-RateLimit-Limit: 100, X-RateLimit-Remaining: 99, X-RateLimit-Reset: {reset_timestamp}, Backend response: {users: [...]}, Forward to client: 200 OK with data + rate limit headers. (6) Request rejected (if no tokens): Scenario: User already made 100 requests in last 10 sec, tokens = 0, Lua script: refilled = min(100, 0 + 0\u00d710) = 0 (no refill yet, too soon), Check: refilled >= 1 \u2192 false, Return: 0 (reject), (7) Return 429 Too Many Requests: Status: 429 Too Many Requests, Headers: X-RateLimit-Limit: 100, X-RateLimit-Remaining: 0, X-RateLimit-Reset: 1712340788 (Unix timestamp when bucket refills to 1 token), Retry-After: 1 (seconds until retry allowed, ceil((1 - 0) / 10) = 1 sec), Body (JSON): {error: 'rate_limit_exceeded', message: 'You have exceeded 100 requests per minute. Retry after 1 second.', limit: 100, remaining: 0, reset_at: '2024-04-05T10:01:28Z'}, Client behavior: Receive 429, parse Retry-After: 1, wait 1 second, retry request \u2192 likely succeeds (1 token refilled). (8) Monitoring: Log rate limit events: {user_id, endpoint, allowed: false, tokens_remaining: 0, timestamp}, Metrics: Increment counter: rate_limit_rejections.incr(labels={endpoint='/api/users', tier='FREE'}), Track: Rejection rate (% of requests rejected), per-endpoint breakdown, Dashboard: Alert if rejection rate > 5% (potential attack or misconfigured limit). Result: Complete flow with policy caching (fast lookup), atomic token check (race-free), proper 429 response (helps client implement backoff), monitoring (visibility into rate limiting)."
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "CRITICAL: Use Redis Lua scripts for atomic rate limit checks. NEVER use separate GET + DECR (race condition: 2 instances both GET \u2192 both see 1 token \u2192 both DECR \u2192 over-limit). Lua script executes atomically in Redis (single-threaded), prevents races across distributed API Gateways."
              },
              {
                "type": "interview-favorite",
                "text": "Token Bucket is production standard (AWS, Stripe, GitHub). Supports bursts (capacity=100 allows 100 instant requests), smooth refill (10 tokens/sec), no boundary problem. Implementation: Redis HASH {tokens, last_refill} + Lua script (atomic check + refill calculation based on elapsed time)."
              },
              {
                "type": "tip",
                "text": "Fixed Window has boundary problem: 100 req at 10:00:59 + 100 req at 10:01:00 = 200 req in 2 sec (violates 100/min spirit). Token Bucket avoids this with continuous refill. Sliding Window Log is accurate but expensive (stores every timestamp, high memory)."
              },
              {
                "type": "interview-favorite",
                "text": "429 response must include headers: X-RateLimit-Limit (quota), X-RateLimit-Remaining (left), X-RateLimit-Reset (Unix timestamp when resets), Retry-After (seconds to wait). Helps clients implement exponential backoff, avoid hammering API."
              },
              {
                "type": "warning",
                "text": "NEVER store rate limit state locally in API Gateway memory (lost on restart, no consistency across instances). MUST use Redis (centralized state, atomic operations). Redis down \u2192 circuit breaker: fail open (allow all, log) OR fail closed (reject with 503, safer)."
              },
              {
                "type": "tip",
                "text": "Cache policy rules in Redis (1 hour TTL, 99% hit rate). Write-through on updates: UPDATE DB + SET cache + PUBLISH Redis pub/sub \u2192 API Gateways invalidate local cache within <1 sec. Avoids stale limits for 1 hour (TTL-only approach)."
              },
              {
                "type": "interview-favorite",
                "text": "Multi-tier limiting: Check per-user (100/min), per-IP (1000/min), per-endpoint (login: 10/min), global (1M/sec cluster capacity). Enforce strictest limit (if ANY exceeded \u2192 429). Prevents abuse at multiple levels (user quota + IP-based DDoS protection + endpoint brute-force prevention)."
              }
            ],
            "tags": [
              "system-design",
              "rate-limiter",
              "rate-limiting",
              "token-bucket",
              "sliding-window",
              "fixed-window",
              "redis-lua-script",
              "api-gateway",
              "distributed-systems",
              "429-too-many-requests",
              "ddos-protection",
              "throttling"
            ]
          },
          "toolId": "rate-limiter",
          "codeSnippets": [
            {
              "title": "Complete Implementation \u2014 Strategy Pattern",
              "language": "java",
              "description": "All 5 rate limiting algorithms implemented using the Strategy Pattern. Swap algorithms via config without changing application code.",
              "code": "import java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class RateLimiterStrategyDemo {\n\n    // =========================\n    // Strategy Interface\n    // =========================\n    interface RateLimiterStrategy {\n        boolean allowRequest(String userId);\n        String getName();\n    }\n\n    // =========================\n    // 1. Fixed Window Counter\n    // =========================\n    static class FixedWindowRateLimiter implements RateLimiterStrategy {\n        private final int maxRequests;\n        private final long windowSizeMillis;\n        private final Map<String, Window> userWindows = new ConcurrentHashMap<>();\n\n        static class Window {\n            long windowStartTime;\n            int requestCount;\n\n            Window(long windowStartTime) {\n                this.windowStartTime = windowStartTime;\n                this.requestCount = 0;\n            }\n        }\n\n        public FixedWindowRateLimiter(int maxRequests, long windowSizeMillis) {\n            this.maxRequests = maxRequests;\n            this.windowSizeMillis = windowSizeMillis;\n        }\n\n        @Override\n        public synchronized boolean allowRequest(String userId) {\n            long now = System.currentTimeMillis();\n\n            Window window = userWindows.getOrDefault(userId, new Window(now));\n\n            if (now - window.windowStartTime >= windowSizeMillis) {\n                window.windowStartTime = now;\n                window.requestCount = 0;\n            }\n\n            if (window.requestCount < maxRequests) {\n                window.requestCount++;\n                userWindows.put(userId, window);\n                return true;\n            }\n\n            userWindows.put(userId, window);\n            return false;\n        }\n\n        @Override\n        public String getName() {\n            return \"Fixed Window Counter\";\n        }\n    }\n\n    // =========================\n    // 2. Sliding Window Log\n    // =========================\n    static class SlidingWindowLogRateLimiter implements RateLimiterStrategy {\n        private final int maxRequests;\n        private final long windowSizeMillis;\n        private final Map<String, Deque<Long>> userRequestLogs = new ConcurrentHashMap<>();\n\n        public SlidingWindowLogRateLimiter(int maxRequests, long windowSizeMillis) {\n            this.maxRequests = maxRequests;\n            this.windowSizeMillis = windowSizeMillis;\n        }\n\n        @Override\n        public synchronized boolean allowRequest(String userId) {\n            long now = System.currentTimeMillis();\n\n            Deque<Long> requestLog = userRequestLogs.getOrDefault(userId, new LinkedList<>());\n\n            while (!requestLog.isEmpty() && now - requestLog.peekFirst() >= windowSizeMillis) {\n                requestLog.pollFirst();\n            }\n\n            if (requestLog.size() < maxRequests) {\n                requestLog.offerLast(now);\n                userRequestLogs.put(userId, requestLog);\n                return true;\n            }\n\n            userRequestLogs.put(userId, requestLog);\n            return false;\n        }\n\n        @Override\n        public String getName() {\n            return \"Sliding Window Log\";\n        }\n    }\n\n    // =========================\n    // 3. Sliding Window Counter\n    // =========================\n    static class SlidingWindowCounterRateLimiter implements RateLimiterStrategy {\n        private final int maxRequests;\n        private final long windowSizeMillis;\n        private final Map<String, SlidingWindow> userWindows = new ConcurrentHashMap<>();\n\n        static class SlidingWindow {\n            long currentWindowStart;\n            int currentCount;\n            int previousCount;\n\n            SlidingWindow(long currentWindowStart) {\n                this.currentWindowStart = currentWindowStart;\n                this.currentCount = 0;\n                this.previousCount = 0;\n            }\n        }\n\n        public SlidingWindowCounterRateLimiter(int maxRequests, long windowSizeMillis) {\n            this.maxRequests = maxRequests;\n            this.windowSizeMillis = windowSizeMillis;\n        }\n\n        @Override\n        public synchronized boolean allowRequest(String userId) {\n            long now = System.currentTimeMillis();\n            long currentWindowStart = now - (now % windowSizeMillis);\n\n            SlidingWindow window = userWindows.getOrDefault(\n                    userId,\n                    new SlidingWindow(currentWindowStart)\n            );\n\n            if (window.currentWindowStart != currentWindowStart) {\n                long windowsPassed =\n                        (currentWindowStart - window.currentWindowStart) / windowSizeMillis;\n\n                if (windowsPassed == 1) {\n                    window.previousCount = window.currentCount;\n                } else {\n                    window.previousCount = 0;\n                }\n\n                window.currentCount = 0;\n                window.currentWindowStart = currentWindowStart;\n            }\n\n            long elapsedTimeInCurrentWindow = now - window.currentWindowStart;\n            double previousWindowWeight =\n                    (double) (windowSizeMillis - elapsedTimeInCurrentWindow) / windowSizeMillis;\n\n            double estimatedRequestCount =\n                    window.currentCount + (window.previousCount * previousWindowWeight);\n\n            if (estimatedRequestCount < maxRequests) {\n                window.currentCount++;\n                userWindows.put(userId, window);\n                return true;\n            }\n\n            userWindows.put(userId, window);\n            return false;\n        }\n\n        @Override\n        public String getName() {\n            return \"Sliding Window Counter\";\n        }\n    }\n\n    // =========================\n    // 4. Token Bucket\n    // =========================\n    static class TokenBucketRateLimiter implements RateLimiterStrategy {\n        private final int bucketCapacity;\n        private final double refillRatePerSecond;\n        private final Map<String, Bucket> userBuckets = new ConcurrentHashMap<>();\n\n        static class Bucket {\n            double tokens;\n            long lastRefillTimestamp;\n\n            Bucket(double tokens, long lastRefillTimestamp) {\n                this.tokens = tokens;\n                this.lastRefillTimestamp = lastRefillTimestamp;\n            }\n        }\n\n        public TokenBucketRateLimiter(int bucketCapacity, double refillRatePerSecond) {\n            this.bucketCapacity = bucketCapacity;\n            this.refillRatePerSecond = refillRatePerSecond;\n        }\n\n        @Override\n        public synchronized boolean allowRequest(String userId) {\n            long now = System.currentTimeMillis();\n\n            Bucket bucket = userBuckets.getOrDefault(\n                    userId,\n                    new Bucket(bucketCapacity, now)\n            );\n\n            double elapsedSeconds = (now - bucket.lastRefillTimestamp) / 1000.0;\n            double tokensToAdd = elapsedSeconds * refillRatePerSecond;\n\n            bucket.tokens = Math.min(bucketCapacity, bucket.tokens + tokensToAdd);\n            bucket.lastRefillTimestamp = now;\n\n            if (bucket.tokens >= 1) {\n                bucket.tokens -= 1;\n                userBuckets.put(userId, bucket);\n                return true;\n            }\n\n            userBuckets.put(userId, bucket);\n            return false;\n        }\n\n        @Override\n        public String getName() {\n            return \"Token Bucket\";\n        }\n    }\n\n    // =========================\n    // 5. Leaky Bucket\n    // =========================\n    static class LeakyBucketRateLimiter implements RateLimiterStrategy {\n        private final int bucketCapacity;\n        private final double leakRatePerSecond;\n        private final Map<String, LeakyBucket> userBuckets = new ConcurrentHashMap<>();\n\n        static class LeakyBucket {\n            double currentWaterLevel;\n            long lastLeakTimestamp;\n\n            LeakyBucket(long lastLeakTimestamp) {\n                this.currentWaterLevel = 0;\n                this.lastLeakTimestamp = lastLeakTimestamp;\n            }\n        }\n\n        public LeakyBucketRateLimiter(int bucketCapacity, double leakRatePerSecond) {\n            this.bucketCapacity = bucketCapacity;\n            this.leakRatePerSecond = leakRatePerSecond;\n        }\n\n        @Override\n        public synchronized boolean allowRequest(String userId) {\n            long now = System.currentTimeMillis();\n\n            LeakyBucket bucket = userBuckets.getOrDefault(\n                    userId,\n                    new LeakyBucket(now)\n            );\n\n            double elapsedSeconds = (now - bucket.lastLeakTimestamp) / 1000.0;\n            double leakedAmount = elapsedSeconds * leakRatePerSecond;\n\n            bucket.currentWaterLevel =\n                    Math.max(0, bucket.currentWaterLevel - leakedAmount);\n\n            bucket.lastLeakTimestamp = now;\n\n            if (bucket.currentWaterLevel < bucketCapacity) {\n                bucket.currentWaterLevel++;\n                userBuckets.put(userId, bucket);\n                return true;\n            }\n\n            userBuckets.put(userId, bucket);\n            return false;\n        }\n\n        @Override\n        public String getName() {\n            return \"Leaky Bucket\";\n        }\n    }\n\n    // =========================\n    // Enum for Strategy Type\n    // =========================\n    enum RateLimiterType {\n        FIXED_WINDOW,\n        SLIDING_WINDOW_LOG,\n        SLIDING_WINDOW_COUNTER,\n        TOKEN_BUCKET,\n        LEAKY_BUCKET\n    }\n\n    // =========================\n    // Factory / Strategy Selector\n    // =========================\n    static class RateLimiterFactory {\n\n        public static RateLimiterStrategy getRateLimiter(RateLimiterType type) {\n            switch (type) {\n                case FIXED_WINDOW:\n                    return new FixedWindowRateLimiter(5, 10_000);\n\n                case SLIDING_WINDOW_LOG:\n                    return new SlidingWindowLogRateLimiter(5, 10_000);\n\n                case SLIDING_WINDOW_COUNTER:\n                    return new SlidingWindowCounterRateLimiter(5, 10_000);\n\n                case TOKEN_BUCKET:\n                    return new TokenBucketRateLimiter(5, 1);\n\n                case LEAKY_BUCKET:\n                    return new LeakyBucketRateLimiter(5, 1);\n\n                default:\n                    throw new IllegalArgumentException(\"Unsupported rate limiter type\");\n            }\n        }\n    }\n\n    // =========================\n    // Service using Strategy\n    // =========================\n    static class ApiGateway {\n        private final RateLimiterStrategy rateLimiterStrategy;\n\n        public ApiGateway(RateLimiterStrategy rateLimiterStrategy) {\n            this.rateLimiterStrategy = rateLimiterStrategy;\n        }\n\n        public void handleRequest(String userId, int requestNumber) {\n            boolean allowed = rateLimiterStrategy.allowRequest(userId);\n\n            if (allowed) {\n                System.out.println(\n                        \"Request \" + requestNumber + \" allowed using \"\n                                + rateLimiterStrategy.getName()\n                );\n            } else {\n                System.out.println(\n                        \"Request \" + requestNumber + \" rejected using \"\n                                + rateLimiterStrategy.getName()\n                                + \" | HTTP 429 Too Many Requests\"\n                );\n            }\n        }\n    }\n\n    // =========================\n    // Main Method\n    // =========================\n    public static void main(String[] args) throws InterruptedException {\n\n        /*\n         * Imagine this value comes from config:\n         *\n         * application.yml\n         * rate-limiter:\n         *   algorithm: TOKEN_BUCKET\n         */\n        RateLimiterType selectedAlgorithm = RateLimiterType.TOKEN_BUCKET;\n\n        RateLimiterStrategy strategy =\n                RateLimiterFactory.getRateLimiter(selectedAlgorithm);\n\n        ApiGateway apiGateway = new ApiGateway(strategy);\n\n        String userId = \"user-123\";\n\n        System.out.println(\"Selected Algorithm: \" + strategy.getName());\n        System.out.println(\"----------------------------------\");\n\n        for (int i = 1; i <= 10; i++) {\n            apiGateway.handleRequest(userId, i);\n            Thread.sleep(300);\n        }\n    }\n}\n"
            }
          ]
        }
      ]
    },
    "systemdesignfundamentals": {
      "title": "System Design Fundamentals: Complete Beginner's Course",
      "description": "Learn system design fundamentals from the ground up, including scalability, load balancing, caching, databases, distributed systems, and reliable software architecture. This free beginner-friendly course helps software engineers design scalable applications and prepare for system design interviews.",
      "videos": [
        {
          "id": "01",
          "title": "System Design Fundamentals 1: ✅ Introduction to System Design | Complete Beginner's Guide",
          "videoUrl": "https://youtu.be/Vwe59JJLSTE",
          "description": "In this video, we will deeply understand what System Design actually is, why it is important for software engineers, and how companies like Google, Microsoft, Amazon, Netflix, Uber, WhatsApp and Facebook design scalable distributed systems.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/fiq3rbfi3bf3tgh438tgh3itb43it4b3.jpg",
          "refresher": {
            "label": "System Design Fundamentals - Beginner's Guide",
            "estimatedMinutes": 18,
            "lastUpdated": "2026-01-26",
            "hero": {
              "title": "Introduction to System Design | Complete Beginner's Guide",
              "oneLinerToMemorize": "System Design = Blueprint for building scalable, reliable systems. Combines: Architecture (HLD) + Database design + Caching + Queues + Load balancing + API design + Monitoring. Ask: What scale? What latency? What consistency needed? Then design accordingly."
            },
            "sections": [
              {
                "id": "why-system-design-matters",
                "title": "1. Why System Design Matters",
                "type": "bullets",
                "items": [
                  "Real-world applications: Single server works for 100 users, but breaks at 10,000 users (performance crashes, database overload, memory exhaustion). System Design solves: How scale to millions? How keep latency <100ms? How survive server failures?",
                  "Interview preparation: Top tech companies (Google, Amazon, Microsoft, Meta) use system design interviews as gatekeepers, Tests: Can you think like an architect? Can you make trade-offs? Can you explain decisions? Knowledge of scale, databases, caching, load balancing separates mid-level engineers from architects.",
                  "Career growth: Junior developer \u2192 Senior engineer \u2192 Architect, System design is the bridge. Understanding how systems work at scale makes you 10\u00d7 more effective. You start thinking about: throughput (requests/sec), latency (response time), availability (uptime %), consistency (data accuracy).",
                  "Building production systems: Your first job might be: 'Implement login API', but in production that means: How many concurrent users? What if server crashes? How store passwords securely? How replicate data? System design answers these questions.",
                  "Innovation foundation: Understanding system design lets you evaluate technologies. Why use Redis vs Memcached? When use Kafka vs RabbitMQ? Why NoSQL instead of SQL? These are system design questions."
                ]
              },
              {
                "id": "what-is-system-design",
                "title": "2. What IS System Design?",
                "type": "bullets",
                "items": [
                  "Definition: System Design = Art of building large-scale applications that are scalable, reliable, and efficient. Not about coding, but about architecture and trade-offs.",
                  "High-Level Design (HLD): Boxes and lines diagram showing: users \u2192 load balancer \u2192 servers \u2192 database \u2192 cache. Answering: What are major components? How do they talk? Where are bottlenecks?",
                  "Low-Level Design (LLD): Diving deep into one component. Example: How does caching work? What's the eviction policy? When to invalidate? What about cache stampede?",
                  "Scalability: Can handle 100\u00d7 more users without breaking? Vertical scaling (bigger server: add RAM/CPU, easy but limited), Horizontal scaling (more servers: add instances, harder but unlimited). Good system design leans on horizontal scaling.",
                  "Trade-offs (CAP Theorem): Every system must choose 2 of 3: Consistency (all users see same data), Availability (system always up), Partition tolerance (handles network failures). Example: SQL databases choose CA (consistent + available), NoSQL databases choose AP (available + partition-tolerant).",
                  "Reliability: System works even when things fail (disk crashes, network down, server overloads). Achieved through: redundancy (backups), replication (copies), monitoring (detect issues early).",
                  "Performance: How fast is the system? Measured by: Latency (how long one request takes: <100ms good), Throughput (how many requests/sec: 1000 req/sec good), Response time distribution (p50, p95, p99 percentiles matter more than average)"
                ]
              },
              {
                "id": "key-components",
                "title": "3. Key Components of Any System",
                "type": "bullets",
                "items": [
                  "Component 1: API Gateway - Entry point for all client requests, handles: Authentication (verify user), Rate limiting (prevent abuse: 100 req/min), Routing (forward to correct service), Load balancing (distribute across servers)",
                  "Component 2: Database - Store persistent data, Types: SQL (structured: Postgres, MySQL), NoSQL (unstructured: MongoDB, Cassandra), Data warehouse (analytics: BigQuery, Snowflake), Time-series (metrics: InfluxDB, Prometheus)",
                  "Component 3: Cache - Store frequently accessed data in memory (Redis, Memcached), Speed: Memory access <1ms vs disk access 10ms (10\u00d7 faster), Trade-off: Consistency (cache can be stale), Memory cost (expensive)",
                  "Component 4: Message Queue - Decouple services (async communication), Examples: Kafka (100K msg/sec, distributed), RabbitMQ (reliability-focused), SQS (AWS managed), Use case: Order placed \u2192 send to queue \u2192 Notification Svc processes async",
                  "Component 5: Load Balancer - Distribute traffic across servers, Algorithms: Round-robin (1st req\u2192server1, 2nd\u2192server2), Least connections (send to least busy), Hash (user123 always\u2192server1, sticky sessions), Benefits: No single point of failure, scale horizontally",
                  "Component 6: CDN (Content Delivery Network) - Cache static content geographically closer to users, Benefits: 50ms latency vs 200ms without CDN, Reduces origin server load, Examples: CloudFlare, Akamai, AWS CloudFront",
                  "Component 7: Search Engine - Index and search across millions of documents fast, Examples: Elasticsearch (full-text search <100ms), Apache Solr, AWS OpenSearch, Use case: Google-like search, autocomplete",
                  "Component 8: Monitoring & Logging - Track system health in real-time, Metrics: CPU, memory, request rate, error rate, latency, Dashboards: Grafana (visualize), Alerts: PagerDuty (notify on issues), Log aggregation: ELK stack (Elasticsearch + Logstash + Kibana)",
                  "Component 9: Authentication & Authorization - Verify user identity + permissions, Auth methods: OAuth 2.0 (sign in with Google), JWT tokens (stateless), Session (store in database), HTTPS (encrypt in transit)"
                ]
              },
              {
                "id": "core-design-concepts",
                "title": "4. Core Design Concepts & Patterns",
                "type": "bullets",
                "items": [
                  "Concept 1: Scalability - Make system handle more load, Vertical (add RAM/CPU to one server, max 256GB RAM, expensive), Horizontal (add more servers, unlimited, cheaper per server), Sharding (split data across servers by user_id: shard 1 = users 1-100K, shard 2 = 100K-200K)",
                  "Concept 2: Availability - System stays up (99.9% = 43 min downtime/month, 99.99% = 4 min/month), Redundancy (backup server if primary fails), Replication (data copied to multiple locations), Health checks (detect failures, auto-failover)",
                  "Concept 3: Consistency - All users see same data, Strong consistency (all read latest, expensive), Eventual consistency (reads might see old data briefly, cheap), Causal consistency (reads see causally related writes)",
                  "Concept 4: Latency vs Throughput - Latency: how long one request takes (ms), Throughput: how many requests/sec, Trade-off: Batch processing increases throughput but latency (write 1000 requests at once\u2192lower latency/request but higher waiting time)",
                  "Concept 5: Caching Strategy - Where to cache: Client-side (browser cache images), CDN (cache static files globally), Database cache (query result cache), Application cache (Redis), When to invalidate: TTL (time-to-live, auto-expire), Write-through (update cache when DB updates), Lazy loading (load on first request)",
                  "Concept 6: Database Indexing - Speed up queries (full table scan slow, index fast), B-tree index (most common: ORDER BY user_id), Hash index (exact match lookups: WHERE id=123), Full-text index (search keywords), Trade-off: Faster reads, slower writes (must update index)",
                  "Concept 7: Denormalization - Duplicate data to avoid joins (fast reads), SQL: Normalize to avoid redundancy (saves space, complex queries), NoSQL: Denormalize for speed (more space, simple queries)",
                  "Concept 8: Microservices vs Monolith - Monolith (single codebase, all features, hard to scale individual parts), Microservices (separate services, easy to scale, complex to coordinate), Example: Uber = User Service + Order Service + Payment Service + Notification Service"
                ]
              },
              {
                "id": "lifecycle",
                "title": "5. System Design Interview Format",
                "type": "bullets",
                "items": [
                  "Step 1 (5 min): Clarify requirements, Functional: What should system do? (Place order, search, chat), Non-functional: Scale (1M users?), Latency (<100ms?), Consistency (real-time?), Availability (99.9% uptime?), Ask: Geo-distributed? Mobile app? Peak vs average load?",
                  "Step 2 (10 min): Estimate scale, Back-of-envelope calculation: 1M users \u2192 daily active users = 100K, peak requests/sec = 100K users \u00d7 1 req/min = 1666 req/sec, storage: 100K users \u00d7 1MB profile = 100GB, Cache: top 1% of data = 1GB, Design for 10\u00d7 peak",
                  "Step 3 (10 min): High-Level Design (HLD), Draw boxes: Clients \u2192 Load Balancer \u2192 10 servers \u2192 Database \u2192 Cache, Explain each box: What does it do? Why include it? Bottlenecks?",
                  "Step 4 (15 min): Deep Dive, Pick one component (API design, database schema, caching strategy, payment flow), Go deep: Tables? Indexes? Query patterns? Failure scenarios? Recovery?",
                  "Step 5 (5 min): Scaling, How handle 10M users? Sharding strategy? Database replication? CDN? Rate limiting? Monitoring?",
                  "Tips: Start simple (1 server, 1 DB), then scale, Ask clarifying questions (don't assume), Draw diagrams (visualize), Explain trade-offs (why Redis not Memcached?), Handle failures (what if server crashes?), Monitor (metrics, alerts)"
                ]
              },
              {
                "id": "what-we-cover",
                "title": "6. What We'll Cover in This Playlist",
                "type": "bullets",
                "items": [
                  "Fundamentals (this series): Why system design? Core components? Trade-offs? CAP theorem? Scalability patterns?",
                  "Real-world systems (next series): Design Uber, Instagram, Gmail, Stock Trading, Notification System, Rate Limiter, URL Shortener, Leaderboard, etc.",
                  "Database deep dives: SQL (ACID, transactions, indexes, sharding), NoSQL (eventual consistency, horizontal scaling), When use each",
                  "Caching strategies: Redis, Memcached, cache invalidation patterns (TTL, write-through, lazy loading), cache stampede, hot key",
                  "Message queues & async processing: Kafka, RabbitMQ, event-driven architecture, exactly-once delivery, retry mechanisms",
                  "API design: REST vs GraphQL, versioning, rate limiting, authentication, pagination, error handling",
                  "Distributed systems: CAP theorem, Consistency models, Consensus algorithms (Raft, Paxos), Distributed transactions",
                  "Microservices: Service discovery, Load balancing, Circuit breakers, Monitoring, Distributed tracing",
                  "Interview prep: Real questions from Google, Amazon, Microsoft, Meta, practice designing systems in 45 min, common mistakes"
                ]
              },
              {
                "id": "mindset",
                "title": "7. System Design Mindset",
                "type": "bullets",
                "items": [
                  "Start with requirements: Before designing, understand what you're building. 1000 users vs 1M users = completely different design.",
                  "Think in scale: Always assume scale, How would this break at 10M users? Design for 10\u00d7 your target.",
                  "Make trade-offs: No perfect system, Fast storage (RAM) vs cheap storage (disk), Consistency vs availability, Complexity vs simplicity. Discuss trade-offs in interview.",
                  "Ask about failures: What if database dies? Cache fails? Network goes down? Design for failures, not just happy path.",
                  "Measure everything: Add monitoring, metrics, alerts. You can't optimize what you don't measure. Latency, throughput, error rate, cache hit rate.",
                  "Iterate: Design \u2192 implement \u2192 measure \u2192 identify bottleneck \u2192 redesign. Never stop optimizing.",
                  "Know your tools: Understand what Redis vs Memcached do. When use Postgres vs MongoDB. Why Kafka for streaming. This comes from experience + reading.",
                  "Communication: Explain your thinking. Why chose this DB? What's the bottleneck? How scale? Good communication = 50% of system design interview."
                ]
              },
              {
                "id": "key-metrics",
                "title": "8. Key Metrics & Terminology",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Performance Metrics",
                    "pairs": [
                      {
                        "k": "Latency",
                        "v": "Time for one request to complete (ms). Goal: <100ms for web, <5ms for internal APIs. Measure p50 (median), p95, p99 (tail latency matters more than average)."
                      },
                      {
                        "k": "Throughput",
                        "v": "Requests per second system can handle. Goal: 1000+ rps. Formula: Users \u00d7 Requests/user/day \u00f7 Seconds/day"
                      },
                      {
                        "k": "Bandwidth",
                        "v": "Data transferred per second (Mbps). 1 Gbps = 125 MB/s. Critical for media (video = 5 Mbps per stream)"
                      },
                      {
                        "k": "Storage",
                        "v": "Disk space needed. Example: 1M users \u00d7 1MB profile = 1TB. Plan for 10\u00d7 growth = 10TB needed"
                      },
                      {
                        "k": "Memory",
                        "v": "RAM for caching. Hot 1% of data often sufficient (Pareto principle). 100M records \u00d7 1KB = 100GB, cache top 1% = 1GB"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability Metrics",
                    "pairs": [
                      {
                        "k": "Availability",
                        "v": "% of time system is up. 99.9% = 43 min/month downtime (3 nines), 99.99% = 4 min/month (4 nines), 99.999% = 26 sec/month (5 nines, very expensive)"
                      },
                      {
                        "k": "Fault tolerance",
                        "v": "System works even with component failures. Achieved through redundancy (2+ copies) and replication (data spread across servers)"
                      },
                      {
                        "k": "Durability",
                        "v": "Data survives failures. AWS S3: 11 nines durability (1 failure per 100 trillion objects). Backup policies critical."
                      },
                      {
                        "k": "MTTR",
                        "v": "Mean Time To Recovery - how fast recover from failure. Goal: <5 min. Faster MTTR = higher availability"
                      },
                      {
                        "k": "RTO/RPO",
                        "v": "Recovery Time Objective (how long down?) vs Recovery Point Objective (how much data lost?). Trade-off: faster RTO = more backups = higher cost"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Scale Terminology",
                    "pairs": [
                      {
                        "k": "Million (M)",
                        "v": "10^6 = 1,000,000. 1M users reasonable for single datacenter"
                      },
                      {
                        "k": "Billion (B)",
                        "v": "10^9 = 1,000,000,000. Requires sharding + multiple datacenters"
                      },
                      {
                        "k": "Kilobyte (KB)",
                        "v": "10^3 bytes. 1 KB = small text file"
                      },
                      {
                        "k": "Megabyte (MB)",
                        "v": "10^6 bytes. 1 MB = profile picture, 5 MB = video thumbnail"
                      },
                      {
                        "k": "Gigabyte (GB)",
                        "v": "10^9 bytes. 1 GB = 1000 emails, 1 hour video in HD"
                      },
                      {
                        "k": "Terabyte (TB)",
                        "v": "10^12 bytes. 1 TB = 1000 hours of 4K video"
                      },
                      {
                        "k": "Second (s)",
                        "v": "1 sec = 1000 ms. Network round-trip: 100 ms. Disk I/O: 1-10 ms"
                      },
                      {
                        "k": "Microsecond (\u03bcs)",
                        "v": "10^-6 seconds = 0.001 ms. L1 cache access: 1 \u03bcs"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "design-patterns",
                "title": "9. Common System Design Patterns",
                "type": "bullets",
                "items": [
                  "Pattern 1: Caching - Store frequently accessed data in memory (Redis), Invalidate stale data (TTL or event-based), Trade-off: Consistency vs Speed. Example: Cache user profile (changes rarely) for 1 hour TTL.",
                  "Pattern 2: Database Replication - Keep data copies on multiple servers, Write to master, read from replicas, Benefits: Availability (if master fails, promote replica), Load distribution (reads scale horizontally), Downside: Replication lag (replicas slightly behind master).",
                  "Pattern 3: Sharding - Split data across multiple databases, Shard key: user_id (users 1-100K on shard 1, 100K-200K on shard 2), Benefits: Horizontal scaling (unlimited data size), Fast queries (smaller dataset per shard), Downside: Cross-shard queries slow, resharding painful.",
                  "Pattern 4: Load Balancing - Distribute requests across multiple servers, Algorithms: Round-robin, least connections, hash (sticky sessions), Benefits: No bottleneck (scale horizontally), Auto-failover (if server down, route elsewhere), Downside: Single point of failure (LB itself must be redundant).",
                  "Pattern 5: Message Queue - Decouple services (async communication), Producer: Order Service \u2192 Kafka, Consumer: Notification Service reads from Kafka, Benefits: Loose coupling (services independent), Scalability (process at own pace), Durability (messages persisted), Downside: Complexity (eventual consistency).",
                  "Pattern 6: Circuit Breaker - Prevent cascading failures, States: Closed (normal), Open (failing, reject requests), Half-open (probe if recovered), Example: Payment gateway down \u2192 open circuit \u2192 reject payments \u2192 alert team. Downside: Clients get errors (but controlled).",
                  "Pattern 7: Rate Limiting - Prevent abuse (prevent DDoS), Client limits: 100 req/min per user, IP limits: 1000 req/min, Benefits: Fair resource sharing, prevent abuse, Downside: Rejects legitimate traffic if limit too strict.",
                  "Pattern 8: Bulkhead - Isolate failures (one failing component doesn't crash system), Example: Separate thread pools for critical vs non-critical requests, Downside: Resource complexity.",
                  "Pattern 9: Eventual Consistency - Accept temporary inconsistency for speed, Example: User updates profile \u2192 written to cache first \u2192 synced to DB later, Downside: Might read stale data briefly.",
                  "Pattern 10: Event-Driven Architecture - React to events (order placed \u2192 notifications, recommendations), Benefits: Scalable (process events at own pace), Decoupled, Downside: Complexity (tracking state across services)."
                ]
              },
              {
                "id": "common-mistakes",
                "title": "10. Common System Design Mistakes",
                "type": "bullets",
                "items": [
                  "Mistake 1: Not asking clarifying questions - Assume scale wrongly, design for 1M users when it's actually 100K (over-engineered) or vice versa (under-engineered). FIX: Ask: Scale? SLA? Peak load? Read-heavy or write-heavy?",
                  "Mistake 2: Premature optimization - Add caching, sharding, microservices from start when not needed. FIX: Start simple (1 server, 1 DB), measure bottleneck, then optimize.",
                  "Mistake 3: Ignoring failures - Design happy path only (server up, network good). FIX: Ask: What if server crashes? Network fails? Disk corrupts? Design for failures.",
                  "Mistake 4: Not tracking metrics - Can't optimize what you don't measure. FIX: Add monitoring (CPU, memory, request rate, latency, error rate, cache hit rate). Set up alerts.",
                  "Mistake 5: Overcomplicating - Use too many technologies (microservices + Kafka + Redis + ElasticSearch) when simpler solution works. FIX: Start simple, add complexity only when needed (measured bottleneck).",
                  "Mistake 6: Not discussing trade-offs - Saying 'we'll use this technology' without explaining why. FIX: Explain: Why Redis over Memcached? Why Postgres over MongoDB? What are trade-offs?",
                  "Mistake 7: Inconsistent sharding strategy - Shard by user_id but later need to query by country (cross-shard queries slow). FIX: Plan sharding upfront, choose key carefully.",
                  "Mistake 8: No backup/recovery plan - Database crashes, no backups, data lost. FIX: Regular backups, test recovery, document RTO/RPO.",
                  "Mistake 9: Underestimating operational complexity - Writing code is 20%, operating in production is 80% (monitoring, debugging, scaling, deploying). FIX: Plan for ops (DevOps, automation, runbooks).",
                  "Mistake 10: Not iterating - Design once, never revisit. FIX: Continuous improvement (measure \u2192 identify bottleneck \u2192 redesign \u2192 repeat)."
                ]
              },
              {
                "id": "quick-reference",
                "title": "11. Quick Reference Guide - Which Technology When?",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "When to use each Database",
                    "pairs": [
                      {
                        "k": "SQL (Postgres, MySQL)",
                        "v": "Structured data, ACID transactions, complex queries (JOINs), financial systems, relational data. Scaling: Replication (read replicas), sharding (hard)"
                      },
                      {
                        "k": "NoSQL Document (MongoDB)",
                        "v": "Unstructured/semi-structured data, fast writes, flexible schema, user profiles, logs. Scaling: Replication, sharding (easy)"
                      },
                      {
                        "k": "NoSQL KeyValue (Redis, Memcached)",
                        "v": "Cache layer (NOT primary DB), session storage, real-time leaderboards, counters. Fast (<1ms) but limited storage (~100GB)"
                      },
                      {
                        "k": "Column Store (BigQuery, Redshift)",
                        "v": "Analytics, data warehouse, aggregate queries (GROUP BY, SUM), time-series data. Not good for transactional (single row queries slow)"
                      },
                      {
                        "k": "Search Engine (Elasticsearch)",
                        "v": "Full-text search, autocomplete, log analysis, filtering. Not good for transactional (eventual consistency)"
                      },
                      {
                        "k": "Time-Series (InfluxDB, Prometheus)",
                        "v": "Metrics, monitoring, stock prices, real-time sensor data. Optimized for append-only writes + range queries"
                      }
                    ]
                  },
                  {
                    "groupTitle": "When to use each Messaging System",
                    "pairs": [
                      {
                        "k": "Kafka",
                        "v": "High throughput (100K msg/sec), durability (replicated), distributed, log replay. For: streaming, events, analytics. Downside: complex"
                      },
                      {
                        "k": "RabbitMQ",
                        "v": "Reliability-focused, routing (exchanges), AMQP protocol. For: traditional message queue (job scheduling). Downside: lower throughput than Kafka"
                      },
                      {
                        "k": "SQS (AWS)",
                        "v": "Managed, simple, FIFO mode available. For: lightweight queuing, serverless. Downside: not suitable for streaming"
                      },
                      {
                        "k": "Pub/Sub (Redis)",
                        "v": "Real-time messaging, low latency (<5ms), simple pub/sub. For: notifications, chat. Downside: no persistence (messages lost if no subscribers)"
                      }
                    ]
                  },
                  {
                    "groupTitle": "When to use each Caching Strategy",
                    "pairs": [
                      {
                        "k": "TTL (Time-To-Live)",
                        "v": "Simple, cache auto-expires after N seconds. Good for: user profiles, product info (changes infrequently). Downside: stale data possible"
                      },
                      {
                        "k": "Write-Through",
                        "v": "Update cache when DB updates. Always consistent. For: critical data (user balance). Downside: writes slower (2 updates: cache + DB)"
                      },
                      {
                        "k": "Lazy Loading",
                        "v": "Load to cache on first request. For: rarely accessed data (cold path). Downside: first user experiences slow load"
                      },
                      {
                        "k": "Event-Driven Invalidation",
                        "v": "Invalidate cache on specific events (user updates profile \u2192 clear cache:user123). For: accuracy important. Downside: complexity"
                      }
                    ]
                  }
                ]
              },
              {
                "id": "interview-tips",
                "title": "12. System Design Interview Preparation",
                "type": "bullets",
                "items": [
                  "Tip 1: Clarify scope (5 min) - Ask questions: How many users? Peak load? Geographic distribution? Read/write ratio? Ask before designing, not mid-design.",
                  "Tip 2: Do back-of-envelope math (5 min) - Estimate scale: 1M users \u2192 100K daily active \u2192 1 req/min \u2192 1600 req/sec peak. Storage: 100K users \u00d7 1MB = 100GB. Interviewer impressed if you do math.",
                  "Tip 3: Start simple (5 min) - Draw: 1 client \u2192 1 server \u2192 1 DB. Then scale. Don't jump to microservices + Kafka immediately (overengineered).",
                  "Tip 4: Think aloud (entire interview) - Explain your reasoning: 'I'm using Redis because it's fast (<1ms) and we need to cache hot data. But risk is if Redis crashes we lose cache, so we need monitoring.' Interviewer wants to see thought process.",
                  "Tip 5: Draw diagrams (entire interview) - Visual communication > verbal. Use boxes for services, arrows for data flow. Update diagram as you discuss.",
                  "Tip 6: Discuss trade-offs (entire interview) - Not just 'we use Postgres', say: 'We use Postgres for ACID guarantees + complex queries (JOINs), but it's harder to scale horizontally than MongoDB. For scale, we'll shard by user_id.'",
                  "Tip 7: Deep dive on one area (15 min) - Pick: API design, database schema, caching strategy, payment flow. Go deep: What queries? What indexes? Failure scenarios? Recovery?",
                  "Tip 8: Handle failures (5 min) - Ask: What if DB dies? Cache fails? Server overloads? Design redundancy (backups, replicas, failover).",
                  "Tip 9: Monitoring + alerting (3 min) - Mention: Metrics (latency, throughput, error rate), dashboards (Grafana), alerts (PagerDuty). Shows production mindset.",
                  "Tip 10: Practice explains, not memorization - Don't memorize solutions (won't work for new problems). Understand concepts (scalability, trade-offs, patterns). Practice: Design Instagram, Uber, Twitter in 45 min. Get feedback.",
                  "Tip 11: Admit unknowns - If asked about something unknown: 'I'm not familiar with that, but I'd research + learn.' Better than guessing wrong.",
                  "Tip 12: Answer in 45 min - HLD (10 min) + deep dive (20 min) + scaling (10 min) + Q&A (5 min). Leave time for follow-up questions (interviewer will drill deeper)."
                ]
              },
              {
                "id": "learning-path",
                "title": "13. Learning Path - How to Master System Design",
                "type": "bullets",
                "items": [
                  "Phase 1: Learn fundamentals (1-2 weeks) - Understand core concepts (scalability, CAP, trade-offs), common components (DB, cache, queue, LB), basic patterns (caching, replication, sharding). This playlist.",
                  "Phase 2: Study real systems (2-3 weeks) - Design Instagram, Uber, Gmail, Stock Trading. For each: clarify requirements \u2192 estimate scale \u2192 HLD \u2192 deep dive \u2192 scaling. Identify patterns (how many use sharding? caching?).",
                  "Phase 3: Deep dive on weak areas (2-3 weeks) - If struggle with databases, deep dive into SQL vs NoSQL, transactions, indexing, replication. If weak on distributed systems, learn CAP, consensus algorithms, distributed transactions.",
                  "Phase 4: Interview prep (1-2 weeks) - Do mock interviews (45 min, timed). Record yourself, review (am I explaining? do I ask questions? do I handle failures?). Get feedback from peers/mentors.",
                  "Phase 5: Stay current (ongoing) - Follow engineering blogs (High Scalability, Uber Eng, Netflix Eng), read code (GitHub), attend meetups. Learn new technologies (why did company X switch from Y to Z?).",
                  "Learning resources: Books (Designing Data-Intensive Applications by Martin Kleppmann, gold standard), YouTube (this playlist!), websites (High Scalability blog), practice (design on paper/whiteboard, present to others)."
                ]
              },
              {
                "id": "interview-qa",
                "title": "14. Fundamental Questions & Answers",
                "type": "qa",
                "items": [
                  {
                    "q": "What is System Design and why is it important for software engineers?",
                    "a": "System Design = Process of designing scalable, reliable, efficient systems that can handle real-world scale and failures. It's the blueprint for building production applications. Why important: (1) Interviews: Top tech companies use system design interviews to identify architects (not just coders). (2) Career growth: Junior dev \u2192 Senior engineer \u2192 Architect. System design is required for leveling up. (3) Production reality: Your code must work for 100 users AND 10M users. Same code doesn't work at both scales (must think about database, caching, load balancing). (4) Decision-making: Should we use Postgres or MongoDB? Redis or Memcached? When use microservices? System design teaches you to make informed trade-off decisions. (5) Problem-solving: Learn patterns (caching, replication, sharding) applicable to many problems (not just individual system). Example: Designed Uber? Then designing Airbnb is 70% same patterns (replication, sharding, caching, real-time updates). Without system design knowledge, you're solving same problem repeatedly."
                  },
                  {
                    "q": "Explain CAP Theorem and give real-world examples of which 2 each system chooses.",
                    "a": "CAP Theorem: Any distributed system can guarantee only 2 of 3 properties: Consistency (all users see same data), Availability (system always up), Partition tolerance (survives network failures). Why only 2? If network partition occurs (nodes disconnected): must choose Consistency (stop serving to ensure consistency) OR Availability (serve stale data to keep serving). Can't have both during partition. Examples: (1) SQL Databases (Postgres, MySQL): Choose Consistency + Availability (CA). Design: Single master (all writes go here), replicas (read copies). If master-replica network fails: stop accepting writes (sacrifice availability to ensure consistency). Downside: entire system can't write during partition. (2) NoSQL Databases (MongoDB, Cassandra): Choose Availability + Partition-tolerance (AP). Design: Every replica can accept writes independently, sync later. Benefit: system keeps serving even if some nodes down. Downside: reads might return stale data (eventual consistency). (3) Real-world: Uber during surge: Availability is critical (users must book rides, losing orders = revenue loss). So choose AP (stale price OK temporarily, but must be available). Amazon during peak: Availability critical (Black Friday, must sell, stale inventory OK temporarily). Consistency critical for: Banking (double-charge is disaster), stock trading (must see accurate balance). Choose consistency. Rule of thumb: Choose CP (consistency + partition) if data accuracy critical (financial), AP (availability + partition) if serving users critical (social media)."
                  },
                  {
                    "q": "Back-of-envelope calculation: Design a system for 1M users. How many requests/sec? Storage needed?",
                    "a": "Calculation: (1) Daily Active Users (DAU): Assume 10% of 1M users = 100K DAU (100K users use app daily), (2) Requests per user per day: Assume user opens app once/day, views 10 posts, comments on 2 \u2192 ~15 requests/day, (3) Requests per second: 100K users \u00d7 15 requests/day \u00f7 86400 seconds = ~17 requests/sec average, (4) Peak load: Assume peak is 10\u00d7 average (everyone checking during lunch hour) = 170 requests/sec, (5) Design for 10\u00d7 growth = 1700 requests/sec capacity, (6) Storage: 1M users \u00d7 1MB per profile = 1TB, 100K DAU \u00d7 50 posts/day \u00d7 1KB post = 5GB posts/day, 365 days = 1.8TB/year \u2192 plan for 10TB storage, (7) Bandwidth: 100K users \u00d7 50 KB data/request = 5GB/sec peak (doesn't sound right, let me recalculate) \u2192 170 req/sec \u00d7 50KB average response = 8.5 MB/sec = ~70 Mbps, (8) Cache: Hot 1% of data (active users, trending posts) = 1TB \u00d7 1% = 10GB memory (affordable), (9) Conclusion: 1700 req/sec, 10TB storage, 70 Mbps bandwidth, 10GB cache. For 1700 req/sec, need ~5-10 servers (assume 200 req/sec per server), 1 database server initially (scale later with replicas/sharding). Interviewer impressed if you: do math, state assumptions ('assume 10% DAU'), explain bottleneck ('storage is OK, but bandwidth is tight if we had video'), propose solutions ('cache hot data, compress images')."
                  }
                ]
              }
            ],
            "callouts": [
              {
                "type": "interview-favorite",
                "text": "System design IS about trade-offs. No perfect system. Don't say 'we'll use this' without explaining why + trade-offs. Example: 'We use Postgres for ACID guarantees (good for financial correctness) and complex queries, but it's harder to scale horizontally than MongoDB. We'll shard by user_id if scale demands it.'"
              },
              {
                "type": "tip",
                "text": "Always start simple (1 server, 1 DB), then scale. Premature optimization is enemy #1. Start with HLD (boxes + lines), then deep dive on 1 component. If interview ends early, ask 'what if we need to handle 10\u00d7 load?' and continue scaling."
              },
              {
                "type": "warning",
                "text": "Never ignore failures in design. If you say 'we use database' without mentioning backups/replication, interviewer will ask 'what if database crashes?'. Design redundancy (backups, replicas), monitoring (alerts), and recovery strategy (RTO/RPO) from start."
              },
              {
                "type": "interview-favorite",
                "text": "Ask clarifying questions FIRST (5 min). Better to ask 'how many users?' and get 1M vs 100K (completely different designs) than assume wrong. Questions show: you think like architect, you don't over/under-engineer."
              },
              {
                "type": "tip",
                "text": "Caching golden rule: Cache fast but stale data, DB has slow but fresh data. Cache miss strategy matters. TTL (time-to-live) simplest (cache expires after 1 hour). Event-driven invalidation (cache cleared on updates) more accurate but complex."
              },
              {
                "type": "interview-favorite",
                "text": "Sharding is game-changer for scale. 1 database \u2192 10M rows slow. Shard by user_id (shard 1: users 1-100K) \u2192 100K rows fast. But cross-shard queries hard. Choose shard key carefully (read requirements carefully: if query by country needed, shard by country NOT user_id)."
              },
              {
                "type": "warning",
                "text": "Microservices NOT default for scale. Don't say 'we'll use microservices' unless you explain why (need independent scaling? different tech stacks?). Monolith is simpler, easier to debug, deploy faster. Microservices add complexity (service discovery, distributed tracing, eventual consistency)."
              }
            ],
            "tags": [
              "system-design",
              "fundamentals",
              "beginners-guide",
              "scalability",
              "reliability",
              "cap-theorem",
              "databases",
              "caching",
              "load-balancing",
              "distributed-systems",
              "interview-prep",
              "architecture"
            ]
          }
        },
        {
          "id": "02",
          "title": "System Design Fundamentals 2: ✅ What is Client Server Architecture | Complete Beginner's Guide",
          "videoUrl": "https://youtu.be/hu7-yvfRHN0",
          "description": "Want to learn about the Client Service model / Architecture? In this video, we will deeply understand one of the most basic and important concepts in system design: Client Server Architecture. Before learning advanced system design topics like Load Balancer, API Gateway, Microservices, Caching, Database Sharding, Kafka, Distributed Systems, and High Availability, you must first understand how a client communicates with a server.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/35jy25oy2oyn23g35235h235h.webp",
          "refresher": {
            "label": "System Design Fundamentals Refresher",
            "estimatedMinutes": 12,
            "lastUpdated": "2026-06-22",
            "hero": {
              "title": "Client–Server Architecture",
              "oneLinerToMemorize": "A client requests a service or resource, the server processes the request, and returns a response over a network."
            },
            "sections": [
              {
                "id": "what-is-client-server",
                "title": "1. What Is Client–Server Architecture?",
                "type": "bullets",
                "items": [
                  "Client–server architecture is a distributed computing model in which clients initiate requests and servers provide services, data, or computation.",
                  "The client is usually the user-facing application, such as a browser, mobile app, desktop app, smart TV, or another backend service.",
                  "The server listens for incoming requests, validates them, executes business logic, interacts with storage or other services, and returns a response.",
                  "The client and server are roles, not fixed machine types. The same machine or application can act as a client in one interaction and a server in another.",
                  "Communication usually happens through a network protocol such as HTTP, HTTPS, WebSocket, TCP, UDP, or gRPC."
                ]
              },
              {
                "id": "core-components",
                "title": "2. Core Components",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Client",
                    "pairs": [
                      {
                        "k": "Responsibility",
                        "v": "Collect user input, send requests, display responses, and manage local UI state."
                      },
                      {
                        "k": "Examples",
                        "v": "Web browser, mobile application, desktop software, IoT device, CLI tool, or another server."
                      },
                      {
                        "k": "Typical concerns",
                        "v": "Rendering, input validation, caching, authentication tokens, retries, and user experience."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Server",
                    "pairs": [
                      {
                        "k": "Responsibility",
                        "v": "Receive requests, authenticate users, execute business logic, access data, and return responses."
                      },
                      {
                        "k": "Examples",
                        "v": "Web server, application server, API server, authentication server, database server, or file server."
                      },
                      {
                        "k": "Typical concerns",
                        "v": "Concurrency, security, scalability, availability, data consistency, logging, and failure handling."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Network",
                    "pairs": [
                      {
                        "k": "Responsibility",
                        "v": "Transport requests and responses between the client and server."
                      },
                      {
                        "k": "Important factors",
                        "v": "Latency, bandwidth, packet loss, connection setup, encryption, and timeouts."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "request-response-flow",
                "title": "3. Request–Response Flow",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: User Action",
                    "steps": [
                      "The user performs an action, such as opening a page, searching for a product, or submitting a form.",
                      "The client converts that action into a network request."
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Server Discovery and Connection",
                    "steps": [
                      "The client resolves the server domain name through DNS.",
                      "A connection is established using a transport protocol, commonly TCP or QUIC.",
                      "For HTTPS, a TLS handshake establishes encryption and verifies the server identity."
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Request Processing",
                    "steps": [
                      "The server receives the request and validates its format.",
                      "Authentication and authorization checks are performed when required.",
                      "Business logic executes and may call databases, caches, queues, file storage, or downstream services."
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Response",
                    "steps": [
                      "The server returns a status code, headers, and response data.",
                      "The client parses the response and updates the user interface.",
                      "Errors may trigger retries, fallback behavior, or an error message."
                    ]
                  }
                ]
              },
              {
                "id": "simple-example",
                "title": "4. Simple Example: Opening a Website",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Browser to Website",
                    "steps": [
                      "You enter example.com in the browser.",
                      "The browser uses DNS to find the server IP address.",
                      "The browser establishes a secure connection with the server.",
                      "The browser sends an HTTP GET request for the webpage.",
                      "The server processes the request and returns HTML.",
                      "The browser parses the HTML and may request CSS, JavaScript, fonts, images, and API data.",
                      "The browser renders the final page."
                    ]
                  }
                ]
              },
              {
                "id": "client-server-roles",
                "title": "5. Client and Server Are Contextual Roles",
                "type": "bullets",
                "items": [
                  "A browser is a client when it calls a backend API.",
                  "A backend server becomes a client when it calls a payment gateway, database, authentication service, or another microservice.",
                  "A database is a server when it receives queries from an application.",
                  "A microservice can be both a server and a client during the same user request.",
                  "The terms client and server describe who initiates the request and who provides the service."
                ]
              },
              {
                "id": "types-of-clients",
                "title": "6. Thin Client vs Thick Client",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Thin Client",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Most processing and data storage happen on the server."
                      },
                      {
                        "k": "Advantages",
                        "v": "Easy deployment, centralized updates, less powerful client hardware required."
                      },
                      {
                        "k": "Disadvantages",
                        "v": "Strong network dependency and higher server load."
                      },
                      {
                        "k": "Examples",
                        "v": "Traditional server-rendered web applications and browser-based enterprise tools."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Thick Client",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "A significant amount of processing and state management happens on the client."
                      },
                      {
                        "k": "Advantages",
                        "v": "Faster local interactions, offline capability, and reduced server computation."
                      },
                      {
                        "k": "Disadvantages",
                        "v": "Complex updates, device compatibility issues, and greater client-side security exposure."
                      },
                      {
                        "k": "Examples",
                        "v": "Desktop applications, mobile applications, games, and offline-first software."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "architecture-tiers",
                "title": "7. One-Tier, Two-Tier, Three-Tier, and N-Tier",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "One-Tier Architecture",
                    "pairs": [
                      {
                        "k": "Structure",
                        "v": "UI, business logic, and data exist in one application or machine."
                      },
                      {
                        "k": "Example",
                        "v": "A local desktop application using an embedded database."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Two-Tier Architecture",
                    "pairs": [
                      {
                        "k": "Structure",
                        "v": "The client communicates directly with a server, often a database server."
                      },
                      {
                        "k": "Example",
                        "v": "A desktop application directly querying a company database."
                      },
                      {
                        "k": "Limitation",
                        "v": "Tight coupling, security exposure, and poor scalability for large systems."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Three-Tier Architecture",
                    "pairs": [
                      {
                        "k": "Presentation Tier",
                        "v": "The user interface running in the browser or application."
                      },
                      {
                        "k": "Application Tier",
                        "v": "The backend API and business logic."
                      },
                      {
                        "k": "Data Tier",
                        "v": "Databases, caches, and storage systems."
                      }
                    ]
                  },
                  {
                    "groupTitle": "N-Tier Architecture",
                    "pairs": [
                      {
                        "k": "Structure",
                        "v": "The system is divided into multiple layers or services such as gateway, authentication, business services, cache, queue, and storage."
                      },
                      {
                        "k": "Benefit",
                        "v": "Independent scaling, separation of concerns, and better maintainability."
                      },
                      {
                        "k": "Cost",
                        "v": "More network calls, operational complexity, monitoring needs, and failure points."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "stateful-stateless",
                "title": "8. Stateful vs Stateless Servers",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Stateless Server",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Each request contains all information required to process it. The server does not depend on local session state from a previous request."
                      },
                      {
                        "k": "Advantages",
                        "v": "Easy horizontal scaling, simple load balancing, better fault tolerance."
                      },
                      {
                        "k": "Example",
                        "v": "An API that authenticates every request using a token."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Stateful Server",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "The server retains session or connection state across multiple interactions."
                      },
                      {
                        "k": "Advantages",
                        "v": "Useful for real-time connections, gaming sessions, transactions, and workflows requiring continuity."
                      },
                      {
                        "k": "Challenges",
                        "v": "Harder scaling, sticky sessions, state replication, and more complex failover."
                      },
                      {
                        "k": "Example",
                        "v": "A WebSocket server maintaining active client connections."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "communication-patterns",
                "title": "9. Common Communication Patterns",
                "type": "bullets",
                "items": [
                  "Request–Response: The client sends a request and waits for a response. Common with HTTP APIs.",
                  "Polling: The client repeatedly asks the server whether new data is available.",
                  "Long Polling: The server keeps the request open until data becomes available or the request times out.",
                  "Server-Sent Events: The server continuously pushes one-way updates to the client over HTTP.",
                  "WebSocket: A persistent bidirectional connection allows both client and server to send messages at any time.",
                  "Asynchronous Messaging: A client or service publishes work to a queue, allowing processing to happen later."
                ]
              },
              {
                "id": "scaling",
                "title": "10. How Client–Server Systems Scale",
                "type": "bullets",
                "items": [
                  "Vertical Scaling: Add more CPU, memory, or storage to one server. Simple but limited.",
                  "Horizontal Scaling: Add more server instances and distribute requests among them.",
                  "Load Balancer: Routes requests across healthy server instances.",
                  "Stateless Application Servers: Allow any request to be handled by any server.",
                  "Caching: Stores frequently accessed data closer to the client or application.",
                  "Database Replication: Uses read replicas to distribute read traffic and improve availability.",
                  "Database Sharding: Splits data across multiple database servers.",
                  "CDN: Serves static and cacheable content from edge locations closer to users.",
                  "Message Queue: Moves slow or non-critical processing out of the synchronous request path.",
                  "Auto Scaling: Adds or removes server capacity based on traffic and resource usage."
                ]
              },
              {
                "id": "failure-handling",
                "title": "11. Failures and Reliability",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Common Failures",
                    "pairs": [
                      {
                        "k": "Network Timeout",
                        "v": "The request or response takes longer than the configured limit."
                      },
                      {
                        "k": "Server Unavailable",
                        "v": "The server crashes, is overloaded, or cannot be reached."
                      },
                      {
                        "k": "Partial Failure",
                        "v": "The server is running, but a database or downstream service has failed."
                      },
                      {
                        "k": "Duplicate Request",
                        "v": "A retry causes the same operation to be submitted more than once."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability Techniques",
                    "pairs": [
                      {
                        "k": "Timeout",
                        "v": "Stop waiting indefinitely for a response."
                      },
                      {
                        "k": "Retry",
                        "v": "Repeat transient failures using limited attempts and exponential backoff."
                      },
                      {
                        "k": "Idempotency",
                        "v": "Ensure repeating the same request does not create unintended duplicate effects."
                      },
                      {
                        "k": "Circuit Breaker",
                        "v": "Temporarily stop calls to a failing dependency."
                      },
                      {
                        "k": "Health Check",
                        "v": "Allow load balancers and orchestrators to remove unhealthy instances."
                      },
                      {
                        "k": "Replication and Failover",
                        "v": "Maintain redundant instances so traffic can move to a healthy system."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "security",
                "title": "12. Security Basics",
                "type": "bullets",
                "items": [
                  "Use HTTPS to encrypt communication and verify the server identity.",
                  "Authenticate the client using sessions, tokens, API keys, certificates, or another identity mechanism.",
                  "Authorize every sensitive operation on the server; never trust client-side checks alone.",
                  "Validate and sanitize all client input.",
                  "Apply rate limiting to reduce abuse and denial-of-service risk.",
                  "Store secrets on the server, not in browser or mobile application code.",
                  "Use secure cookies, short-lived tokens, token rotation, and proper logout or revocation mechanisms.",
                  "Log security-relevant events without exposing passwords, tokens, or sensitive data."
                ]
              },
              {
                "id": "advantages-disadvantages",
                "title": "13. Advantages and Limitations",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Advantages",
                    "pairs": [
                      {
                        "k": "Centralized Control",
                        "v": "Business rules, security policies, and data can be managed centrally."
                      },
                      {
                        "k": "Resource Sharing",
                        "v": "Many clients can use the same services and data."
                      },
                      {
                        "k": "Maintainability",
                        "v": "Server-side changes can often be deployed without updating every client."
                      },
                      {
                        "k": "Scalability",
                        "v": "Servers can be replicated and placed behind load balancers."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Limitations",
                    "pairs": [
                      {
                        "k": "Network Dependency",
                        "v": "Performance and availability depend on network connectivity."
                      },
                      {
                        "k": "Server Bottleneck",
                        "v": "A server can become overloaded if capacity is insufficient."
                      },
                      {
                        "k": "Single Point of Failure",
                        "v": "A single server can bring down the system unless redundancy is added."
                      },
                      {
                        "k": "Operational Complexity",
                        "v": "Distributed systems require deployment, monitoring, scaling, and failure management."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "client-server-vs-p2p",
                "title": "14. Client–Server vs Peer-to-Peer",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Client–Server",
                    "pairs": [
                      {
                        "k": "Control",
                        "v": "Centralized or logically centralized."
                      },
                      {
                        "k": "Roles",
                        "v": "Clients request services; servers provide them."
                      },
                      {
                        "k": "Strength",
                        "v": "Simpler governance, security, data management, and updates."
                      },
                      {
                        "k": "Weakness",
                        "v": "Server infrastructure can become a bottleneck or failure point."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Peer-to-Peer",
                    "pairs": [
                      {
                        "k": "Control",
                        "v": "Decentralized."
                      },
                      {
                        "k": "Roles",
                        "v": "Each peer can both request and provide resources."
                      },
                      {
                        "k": "Strength",
                        "v": "Can distribute load and avoid dependence on one central server."
                      },
                      {
                        "k": "Weakness",
                        "v": "Harder security, coordination, consistency, discovery, and governance."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "interview-qa",
                "title": "15. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "What is client–server architecture?",
                    "a": "It is a distributed model where a client initiates a request for a resource or service and a server processes that request and returns a response over a network."
                  },
                  {
                    "q": "Can a server also be a client?",
                    "a": "Yes. A backend server acts as a client when it calls a database, another microservice, a payment gateway, or any external API."
                  },
                  {
                    "q": "What is the difference between a client and a server?",
                    "a": "The client normally initiates communication and consumes a service. The server listens for requests and provides the service. These are contextual roles, not necessarily different hardware."
                  },
                  {
                    "q": "Why are stateless servers easier to scale?",
                    "a": "Because any instance can process any request. A load balancer does not need to send a user back to the same server, and failed instances can be replaced without losing local session state."
                  },
                  {
                    "q": "What happens when a server receives too many requests?",
                    "a": "Latency increases, queues grow, resources become exhausted, and requests may fail. Common solutions include horizontal scaling, load balancing, caching, rate limiting, backpressure, and asynchronous processing."
                  },
                  {
                    "q": "What is the role of a load balancer?",
                    "a": "It distributes incoming requests across healthy server instances, improves availability, and prevents a single server from receiving all traffic."
                  },
                  {
                    "q": "What is a three-tier architecture?",
                    "a": "It separates the presentation layer, application or business-logic layer, and data layer. This improves separation of concerns and allows each tier to scale independently."
                  },
                  {
                    "q": "How does the client know where the server is?",
                    "a": "Usually through a domain name. DNS resolves that domain into an IP address, after which the client establishes a connection to the server."
                  },
                  {
                    "q": "How do you handle duplicate requests caused by retries?",
                    "a": "Use idempotent operations or an idempotency key so the server can recognize repeated requests and avoid applying the same side effect more than once."
                  },
                  {
                    "q": "Is client–server architecture always synchronous?",
                    "a": "No. It commonly uses synchronous request–response, but it can also use queues, callbacks, events, WebSockets, server-sent events, and other asynchronous patterns."
                  }
                ]
              },
              {
                "id": "summary",
                "title": "16. Final Revision Summary",
                "type": "bullets",
                "items": [
                  "The client initiates a request; the server provides a service or resource.",
                  "Client and server describe roles, not necessarily separate physical machines.",
                  "A normal request may involve DNS, connection establishment, TLS, an application protocol, business logic, storage, and a response.",
                  "Three-tier architecture separates presentation, application logic, and data.",
                  "Stateless servers are easier to scale horizontally.",
                  "Load balancers, caching, replication, queues, and CDNs help systems scale.",
                  "Timeouts, retries, idempotency, circuit breakers, health checks, and failover improve reliability.",
                  "Never trust the client for authorization or sensitive business rules."
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "Never trust client-side validation or authorization. A client can be modified, automated, or bypassed, so the server must enforce every security-sensitive rule."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers often ask whether a server can also be a client. The correct answer is yes: roles depend on the direction of a specific interaction."
              },
              {
                "type": "tip",
                "text": "For horizontal scaling, prefer stateless application servers and keep shared state in databases, distributed caches, or dedicated session stores."
              },
              {
                "type": "interview-favorite",
                "text": "Be able to explain the full path: user action → DNS → connection → TLS → request → server logic → database/cache → response → UI update."
              }
            ],
            "tags": [
              "system-design",
              "client-server-architecture",
              "client-server-model",
              "distributed-systems",
              "networking",
              "http",
              "load-balancing",
              "scalability",
              "backend-fundamentals",
              "system-design-fundamentals"
            ]
          }
        },
        {
          "id": "03",
          "title": "System Design Fundamentals 3: ✅ What is DNS? Domain Name System Explained with Real-Life Example.",
          "videoUrl": "https://youtu.be/xqEBTX4MDz4",
          "description": "In this video, we will understand DNS, also known as the Domain Name System, in a simple and practical way from a system design perspective.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/qrg35ghhn3oiny3t48t3t43ntg34ngo43n.jpg",
          "refresher": {
            "label": "System Design Fundamentals Refresher",
            "estimatedMinutes": 14,
            "lastUpdated": "2026-06-22",
            "hero": {
              "title": "DNS — Domain Name System",
              "oneLinerToMemorize": "DNS translates human-readable domain names into IP addresses so clients can locate and connect to servers."
            },
            "sections": [
              {
                "id": "what-is-dns",
                "title": "1. What Is DNS?",
                "type": "bullets",
                "items": [
                  "DNS stands for Domain Name System.",
                  "It is a distributed and hierarchical naming system used to translate domain names such as example.com into IP addresses such as 93.184.216.34.",
                  "Humans prefer memorable names, while network devices communicate using IP addresses.",
                  "DNS acts like the internet's contact directory: you provide a name, and DNS returns the location associated with that name.",
                  "DNS does more than return IP addresses. It also supports email routing, aliases, service discovery, verification records, and other metadata."
                ]
              },
              {
                "id": "real-life-example",
                "title": "2. Real-Life Example: A Phone Contact List",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Name to Number",
                    "steps": [
                      "You remember a person's name, not their phone number.",
                      "You search the name in your contact list.",
                      "Your phone finds the saved number.",
                      "The call is then made using that number.",
                      "Similarly, you enter a domain name, DNS finds its IP address, and the client connects to that address."
                    ]
                  },
                  {
                    "blockTitle": "Important Difference",
                    "steps": [
                      "A personal contact list is usually stored in one place.",
                      "DNS is globally distributed across many servers.",
                      "DNS also uses caching so the full lookup does not happen for every request."
                    ]
                  }
                ]
              },
              {
                "id": "why-dns-is-needed",
                "title": "3. Why Do We Need DNS?",
                "type": "bullets",
                "items": [
                  "Memorability: Domain names are easier to remember than IP addresses.",
                  "Flexibility: A server's IP address can change without changing the public domain name.",
                  "Load Distribution: One domain can return multiple IP addresses.",
                  "Geographic Routing: Users can be directed to nearby or region-specific servers.",
                  "Failover: DNS records can be updated to move traffic away from unavailable infrastructure.",
                  "Service Discovery: Applications can discover mail servers and other services through DNS records.",
                  "Decoupling: Users depend on a stable name rather than the physical location of a server."
                ]
              },
              {
                "id": "dns-components",
                "title": "4. Main DNS Components",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "DNS Stub Resolver",
                    "pairs": [
                      {
                        "k": "Where it runs",
                        "v": "On the user's operating system or device."
                      },
                      {
                        "k": "Responsibility",
                        "v": "Accepts DNS requests from applications and sends them to a recursive resolver."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Recursive DNS Resolver",
                    "pairs": [
                      {
                        "k": "Responsibility",
                        "v": "Finds the final DNS answer on behalf of the client."
                      },
                      {
                        "k": "Examples",
                        "v": "Resolvers operated by an ISP, company network, public DNS provider, or local router."
                      },
                      {
                        "k": "Optimization",
                        "v": "Caches previous answers to avoid repeating the complete lookup."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Root Name Server",
                    "pairs": [
                      {
                        "k": "Responsibility",
                        "v": "Directs the resolver to the appropriate top-level domain name servers."
                      },
                      {
                        "k": "Example",
                        "v": "For example.com, the root server points toward the .com name servers."
                      }
                    ]
                  },
                  {
                    "groupTitle": "TLD Name Server",
                    "pairs": [
                      {
                        "k": "TLD meaning",
                        "v": "Top-Level Domain, such as .com, .org, .in, or .net."
                      },
                      {
                        "k": "Responsibility",
                        "v": "Directs the resolver to the authoritative name server for the requested domain."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Authoritative Name Server",
                    "pairs": [
                      {
                        "k": "Responsibility",
                        "v": "Stores or serves the official DNS records for a domain."
                      },
                      {
                        "k": "Final answer",
                        "v": "Returns the requested record, such as an A, AAAA, CNAME, or MX record."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "dns-resolution-flow",
                "title": "5. Complete DNS Resolution Flow",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Browser and Operating-System Cache",
                    "steps": [
                      "The browser checks whether it already knows the domain's IP address.",
                      "If not found, the operating system checks its DNS cache and hosts file.",
                      "A cached result can complete the lookup immediately."
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Recursive Resolver",
                    "steps": [
                      "The device sends the DNS query to its configured recursive resolver.",
                      "The resolver checks its own cache.",
                      "If the answer is cached and still valid, it returns the answer immediately."
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Root Name Server",
                    "steps": [
                      "If the resolver has no cached answer, it queries a root name server.",
                      "The root server does not usually return the final IP address.",
                      "It returns a referral to the relevant TLD name servers."
                    ]
                  },
                  {
                    "blockTitle": "Step 4: TLD Name Server",
                    "steps": [
                      "The resolver queries the appropriate TLD name server.",
                      "The TLD server returns the location of the domain's authoritative name server."
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Authoritative Name Server",
                    "steps": [
                      "The resolver asks the authoritative server for the requested record.",
                      "The authoritative server returns the final record and its TTL."
                    ]
                  },
                  {
                    "blockTitle": "Step 6: Response to Client",
                    "steps": [
                      "The recursive resolver caches the answer.",
                      "The resolver returns the result to the client.",
                      "The client can now connect to the returned IP address using the required application protocol."
                    ]
                  }
                ]
              },
              {
                "id": "recursive-iterative",
                "title": "6. Recursive vs Iterative Queries",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Recursive Query",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "The requester expects the DNS server to return the final answer or an error."
                      },
                      {
                        "k": "Typical usage",
                        "v": "A client asks a recursive resolver to resolve a domain completely."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Iterative Query",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "The DNS server returns the best information it currently has, often a referral to another server."
                      },
                      {
                        "k": "Typical usage",
                        "v": "A recursive resolver follows referrals from root and TLD servers."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "dns-record-types",
                "title": "7. Important DNS Record Types",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Address Records",
                    "pairs": [
                      {
                        "k": "A",
                        "v": "Maps a domain name to an IPv4 address."
                      },
                      {
                        "k": "AAAA",
                        "v": "Maps a domain name to an IPv6 address."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Alias and Delegation Records",
                    "pairs": [
                      {
                        "k": "CNAME",
                        "v": "Maps one hostname to another hostname. The target must then be resolved."
                      },
                      {
                        "k": "NS",
                        "v": "Identifies the authoritative name servers for a DNS zone."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Mail and Service Records",
                    "pairs": [
                      {
                        "k": "MX",
                        "v": "Specifies the mail servers responsible for receiving email for a domain."
                      },
                      {
                        "k": "SRV",
                        "v": "Describes the hostname and port for a specific service."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Verification and Metadata",
                    "pairs": [
                      {
                        "k": "TXT",
                        "v": "Stores text data used for verification, email security policies, and configuration."
                      },
                      {
                        "k": "SOA",
                        "v": "Contains administrative information about a DNS zone, including its primary server and timing values."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reverse Lookup",
                    "pairs": [
                      {
                        "k": "PTR",
                        "v": "Maps an IP address back to a domain name."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "domain-hierarchy",
                "title": "8. DNS Hierarchy and Domain Structure",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Example: api.shop.example.com",
                    "pairs": [
                      {
                        "k": ".",
                        "v": "The DNS root. It is usually omitted when writing domain names."
                      },
                      {
                        "k": "com",
                        "v": "The top-level domain."
                      },
                      {
                        "k": "example",
                        "v": "The registered domain or second-level domain."
                      },
                      {
                        "k": "shop",
                        "v": "A subdomain."
                      },
                      {
                        "k": "api",
                        "v": "A hostname or deeper subdomain."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "dns-zone",
                "title": "9. Domain, Zone, and Delegation",
                "type": "bullets",
                "items": [
                  "A domain is a portion of the DNS namespace.",
                  "A DNS zone is the portion of that namespace managed by a particular administrative authority.",
                  "A zone contains DNS records for the names under its control.",
                  "A parent zone can delegate responsibility for a subdomain to different authoritative name servers.",
                  "Delegation is performed using NS records, usually with supporting glue records when required.",
                  "A domain and a zone are related, but they are not always identical because subdomains can be delegated separately."
                ]
              },
              {
                "id": "ttl-caching",
                "title": "10. DNS Caching and TTL",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "TTL",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Time To Live: the duration for which a DNS answer may be cached."
                      },
                      {
                        "k": "High TTL",
                        "v": "Reduces DNS queries and improves cache effectiveness, but changes take longer to reach users."
                      },
                      {
                        "k": "Low TTL",
                        "v": "Allows faster changes and failover, but increases DNS query volume."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Caching Locations",
                    "pairs": [
                      {
                        "k": "Browser Cache",
                        "v": "The browser may store recent DNS results."
                      },
                      {
                        "k": "Operating-System Cache",
                        "v": "The local operating system may cache DNS records."
                      },
                      {
                        "k": "Router or Network Cache",
                        "v": "A local network device may cache DNS answers."
                      },
                      {
                        "k": "Recursive Resolver Cache",
                        "v": "The resolver caches records for many users."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "dns-propagation",
                "title": "11. What Does DNS Propagation Mean?",
                "type": "bullets",
                "items": [
                  "DNS propagation is the informal term used for the time required before users consistently receive newly changed DNS records.",
                  "DNS does not copy an update instantly to every resolver in the world.",
                  "Resolvers continue serving older cached records until their TTL expires.",
                  "Different users may temporarily receive different answers depending on the state of their resolver cache.",
                  "The authoritative record may already be updated even while some recursive resolvers still return the older value.",
                  "Lowering TTL before a planned migration can reduce the transition period, but the lower TTL must be applied before old cached records expire."
                ]
              },
              {
                "id": "dns-transport",
                "title": "12. How DNS Is Transported",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Traditional DNS",
                    "pairs": [
                      {
                        "k": "UDP",
                        "v": "Commonly used for normal DNS queries because it has low overhead."
                      },
                      {
                        "k": "TCP",
                        "v": "Used when responses are too large, for zone transfers, and in other cases requiring a reliable stream."
                      },
                      {
                        "k": "Port",
                        "v": "Traditional DNS commonly uses port 53."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Encrypted DNS",
                    "pairs": [
                      {
                        "k": "DNS over HTTPS",
                        "v": "Sends DNS queries through HTTPS."
                      },
                      {
                        "k": "DNS over TLS",
                        "v": "Encrypts DNS traffic using TLS."
                      },
                      {
                        "k": "Benefit",
                        "v": "Protects DNS queries from straightforward observation or modification between the client and resolver."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "dns-load-balancing",
                "title": "13. DNS in System Design",
                "type": "bullets",
                "items": [
                  "Round-Robin DNS: Return multiple IP addresses and rotate their order.",
                  "Weighted Routing: Send different percentages of traffic to different endpoints.",
                  "Latency-Based Routing: Direct users to a region expected to provide lower latency.",
                  "Geo-Based Routing: Return different endpoints based on the user's geographic location.",
                  "Failover Routing: Return a backup endpoint when the primary endpoint is considered unhealthy.",
                  "CDN Integration: Resolve a domain to an edge location near the user.",
                  "Service Discovery: Internal systems may use DNS to locate service instances.",
                  "DNS-based traffic management is useful, but it is affected by caching and is not a replacement for a request-level load balancer."
                ]
              },
              {
                "id": "dns-failures",
                "title": "14. Common DNS Failures",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Failure Types",
                    "pairs": [
                      {
                        "k": "NXDOMAIN",
                        "v": "The requested domain name does not exist."
                      },
                      {
                        "k": "SERVFAIL",
                        "v": "The DNS server could not complete the query, often because of an upstream or validation failure."
                      },
                      {
                        "k": "Timeout",
                        "v": "The DNS server did not respond within the expected time."
                      },
                      {
                        "k": "Stale Cache",
                        "v": "A resolver temporarily returns an older record."
                      },
                      {
                        "k": "Misconfiguration",
                        "v": "Incorrect NS, CNAME, A, AAAA, MX, or other records prevent proper resolution."
                      },
                      {
                        "k": "Delegation Error",
                        "v": "The parent zone points to incorrect or unreachable authoritative servers."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reliability Techniques",
                    "pairs": [
                      {
                        "k": "Multiple Authoritative Servers",
                        "v": "Use redundant authoritative name servers across different networks or regions."
                      },
                      {
                        "k": "Anycast",
                        "v": "Advertise the same DNS service IP from multiple locations so traffic reaches a nearby healthy site."
                      },
                      {
                        "k": "Monitoring",
                        "v": "Continuously check DNS resolution from multiple locations."
                      },
                      {
                        "k": "Controlled TTL",
                        "v": "Choose TTL values based on stability, traffic, and failover requirements."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "dns-security",
                "title": "15. DNS Security",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Common Threats",
                    "pairs": [
                      {
                        "k": "DNS Spoofing",
                        "v": "An attacker attempts to provide a false DNS answer."
                      },
                      {
                        "k": "Cache Poisoning",
                        "v": "A resolver is tricked into caching an incorrect DNS record."
                      },
                      {
                        "k": "DNS Hijacking",
                        "v": "DNS settings or responses are altered to redirect users."
                      },
                      {
                        "k": "DNS Amplification",
                        "v": "Open resolvers are abused to generate large volumes of traffic toward a victim."
                      },
                      {
                        "k": "Domain Takeover",
                        "v": "An expired, abandoned, or incorrectly configured DNS target is claimed by an attacker."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Protection",
                    "pairs": [
                      {
                        "k": "DNSSEC",
                        "v": "Adds cryptographic signatures so resolvers can verify that DNS data is authentic and has not been modified."
                      },
                      {
                        "k": "Encrypted DNS",
                        "v": "Protects traffic between the client and recursive resolver."
                      },
                      {
                        "k": "Registrar Security",
                        "v": "Use strong authentication, domain locks, and controlled access to DNS settings."
                      },
                      {
                        "k": "Restricted Recursion",
                        "v": "Do not expose unrestricted recursive resolvers to the public."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "what-dns-does-not-do",
                "title": "16. What DNS Does Not Do",
                "type": "bullets",
                "items": [
                  "DNS does not establish the application connection. It only helps locate the destination.",
                  "DNS does not guarantee that the returned server is healthy unless a separate health-aware routing system is used.",
                  "DNS does not encrypt normal application traffic. HTTPS or another secure protocol is still required.",
                  "DNS does not replace a load balancer. DNS typically decides where a client should connect, while a load balancer can route individual requests or connections.",
                  "DNS does not always return one fixed IP address. The answer may vary by location, policy, health, or time."
                ]
              },
              {
                "id": "interview-qa",
                "title": "17. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "What is DNS?",
                    "a": "DNS is a distributed hierarchical naming system that translates domain names into IP addresses and stores other records used for routing and service discovery."
                  },
                  {
                    "q": "What happens when you enter a URL in a browser?",
                    "a": "Before connecting to the server, the browser and operating system check local caches. If needed, a recursive resolver queries root, TLD, and authoritative name servers to obtain the required DNS record. The browser then connects to the returned address."
                  },
                  {
                    "q": "What is the difference between a recursive resolver and an authoritative name server?",
                    "a": "A recursive resolver finds answers on behalf of clients and caches them. An authoritative server serves the official DNS records for a domain or zone."
                  },
                  {
                    "q": "What is TTL?",
                    "a": "TTL is the amount of time a DNS answer may be cached. A higher TTL improves caching but slows record changes, while a lower TTL allows faster changes but increases query traffic."
                  },
                  {
                    "q": "What is the difference between A and CNAME records?",
                    "a": "An A record maps a hostname directly to an IPv4 address. A CNAME maps one hostname to another hostname, which must then be resolved."
                  },
                  {
                    "q": "Why can DNS changes take time?",
                    "a": "Recursive resolvers may continue returning older cached records until their TTL expires. There is no instant global refresh of every cache."
                  },
                  {
                    "q": "Does DNS use TCP or UDP?",
                    "a": "It uses both. UDP is common for normal queries, while TCP is used for cases such as larger responses, retries after truncation, and zone transfers."
                  },
                  {
                    "q": "What is DNSSEC?",
                    "a": "DNSSEC uses digital signatures to let validating resolvers verify that DNS data is authentic and has not been modified. It does not encrypt the DNS query."
                  },
                  {
                    "q": "Can DNS be used for load balancing?",
                    "a": "Yes. A domain can return multiple or policy-based endpoints. However, DNS caching limits request-level control, so DNS traffic management and load balancers often work together."
                  },
                  {
                    "q": "What happens if DNS fails?",
                    "a": "The client may be unable to locate the service even if the application servers are healthy. Cached records may allow existing clients to continue temporarily, depending on TTL and cache behavior."
                  }
                ]
              },
              {
                "id": "final-summary",
                "title": "18. Final Revision Summary",
                "type": "bullets",
                "items": [
                  "DNS converts domain names into IP addresses and other service-related records.",
                  "The main lookup path is client cache → recursive resolver → root → TLD → authoritative server.",
                  "Recursive resolvers do the lookup work for clients and cache answers.",
                  "Authoritative servers provide the official records for a domain.",
                  "A and AAAA map names to IP addresses; CNAME creates an alias; MX handles mail routing; NS identifies authoritative servers.",
                  "TTL controls how long DNS answers may be cached.",
                  "DNS propagation delays are mainly caused by caches retaining older records.",
                  "DNS commonly uses UDP and also uses TCP when required.",
                  "DNS can support geographic routing, failover, CDN routing, and service discovery.",
                  "DNSSEC verifies authenticity, while encrypted DNS protects queries between clients and resolvers.",
                  "DNS helps locate a server; it does not replace HTTPS, application protocols, or request-level load balancing."
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "Do not say that DNS directly connects the browser to the server. DNS only resolves the name; the client establishes the actual network connection afterward."
              },
              {
                "type": "interview-favorite",
                "text": "Be ready to explain the lookup sequence: browser cache → OS cache → recursive resolver → root → TLD → authoritative server."
              },
              {
                "type": "tip",
                "text": "A high TTL improves cache efficiency but makes migrations slower. A low TTL improves change speed but increases DNS traffic."
              },
              {
                "type": "interview-favorite",
                "text": "DNSSEC provides authenticity and integrity, not confidentiality. DNS over HTTPS or DNS over TLS provides transport encryption."
              },
              {
                "type": "warning",
                "text": "DNS-based load balancing is affected by resolver and client caching. It cannot control every individual request like an application load balancer."
              }
            ],
            "tags": [
              "system-design",
              "dns",
              "domain-name-system",
              "networking",
              "dns-resolution",
              "dns-records",
              "recursive-resolver",
              "authoritative-server",
              "caching",
              "ttl",
              "dnssec",
              "system-design-fundamentals"
            ]
          }
        },
        {
          "id": "04",
          "title": "System Design Fundamentals 4: ✅ OSI Model Explained for System Design | Networking Made Easy",
          "videoUrl": "https://youtu.be/66RgQUTekfs",
          "description": "The OSI model is one of the most asked topics in system design interviews — but most tutorials make it confusing. In this video, I explain all 7 layers of the OSI model using a simple real-life example that you'll never forget.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/regrgh53h45h355yy3y.webp",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/sdfsdfwrg34g34g43oswef4fg2g.png",
          "refresher": {
            "label": "System Design Fundamentals Refresher",
            "estimatedMinutes": 16,
            "lastUpdated": "2026-06-22",
            "hero": {
              "title": "OSI Model Explained for System Design",
              "oneLinerToMemorize": "The OSI model divides network communication into seven layers, where each layer performs a specific responsibility and serves the layer above it."
            },
            "sections": [
              {
                "id": "what-is-osi",
                "title": "1. What Is the OSI Model?",
                "type": "bullets",
                "items": [
                  "OSI stands for Open Systems Interconnection.",
                  "It is a conceptual framework that divides network communication into seven layers.",
                  "Each layer has a specific responsibility and interacts mainly with the layer directly above and below it.",
                  "The model helps engineers understand, design, and troubleshoot network communication.",
                  "The OSI model is not a protocol itself. It is a reference model used to organize networking concepts.",
                  "Real-world internet communication more commonly follows the TCP/IP model, but the OSI model remains extremely useful for learning and system-design discussions."
                ]
              },
              {
                "id": "why-osi-matters",
                "title": "2. Why the OSI Model Matters in System Design",
                "type": "bullets",
                "items": [
                  "Separation of Concerns: Each layer handles a different networking responsibility.",
                  "Troubleshooting: Engineers can identify whether a problem exists in routing, transport, encryption, or the application layer.",
                  "Technology Selection: It helps compare protocols and infrastructure at the correct layer.",
                  "Scalability Discussions: Load balancers, proxies, gateways, and firewalls behave differently depending on the layer at which they operate.",
                  "Security Design: Different attacks and protections apply at different layers.",
                  "Interview Communication: The model gives engineers a shared vocabulary for explaining network flows."
                ]
              },
              {
                "id": "seven-layers-overview",
                "title": "3. The Seven OSI Layers",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Layer 7 — Application",
                    "pairs": [
                      {
                        "k": "Purpose",
                        "v": "Provides network services directly to user-facing applications."
                      },
                      {
                        "k": "Examples",
                        "v": "HTTP, HTTPS, DNS, SMTP, FTP, WebSocket, gRPC."
                      },
                      {
                        "k": "System-design relevance",
                        "v": "APIs, application gateways, reverse proxies, authentication, caching, and business-level communication."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 6 — Presentation",
                    "pairs": [
                      {
                        "k": "Purpose",
                        "v": "Transforms data into a format understood by the application."
                      },
                      {
                        "k": "Responsibilities",
                        "v": "Serialization, encoding, compression, encryption, and decryption."
                      },
                      {
                        "k": "Examples",
                        "v": "JSON, XML, Protocol Buffers, UTF-8, JPEG, TLS-related data transformation."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 5 — Session",
                    "pairs": [
                      {
                        "k": "Purpose",
                        "v": "Establishes, manages, and terminates communication sessions."
                      },
                      {
                        "k": "Responsibilities",
                        "v": "Session control, checkpoints, reconnection, dialogue management."
                      },
                      {
                        "k": "Examples",
                        "v": "Persistent sessions, RPC sessions, authentication sessions, long-lived application conversations."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 4 — Transport",
                    "pairs": [
                      {
                        "k": "Purpose",
                        "v": "Provides end-to-end data delivery between processes."
                      },
                      {
                        "k": "Responsibilities",
                        "v": "Segmentation, ports, reliability, ordering, flow control, retransmission."
                      },
                      {
                        "k": "Examples",
                        "v": "TCP, UDP, QUIC."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 3 — Network",
                    "pairs": [
                      {
                        "k": "Purpose",
                        "v": "Moves packets between networks using logical addressing and routing."
                      },
                      {
                        "k": "Responsibilities",
                        "v": "IP addressing, routing, path selection, packet forwarding."
                      },
                      {
                        "k": "Examples",
                        "v": "IPv4, IPv6, ICMP, routers."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 2 — Data Link",
                    "pairs": [
                      {
                        "k": "Purpose",
                        "v": "Transfers frames between devices on the same local network."
                      },
                      {
                        "k": "Responsibilities",
                        "v": "MAC addressing, framing, local delivery, error detection, switching."
                      },
                      {
                        "k": "Examples",
                        "v": "Ethernet, Wi-Fi, ARP, switches, VLANs."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 1 — Physical",
                    "pairs": [
                      {
                        "k": "Purpose",
                        "v": "Transmits raw bits through a physical medium."
                      },
                      {
                        "k": "Responsibilities",
                        "v": "Electrical, optical, or radio signals; cables; frequencies; connectors; transmission rates."
                      },
                      {
                        "k": "Examples",
                        "v": "Fiber-optic cable, copper cable, radio waves, network adapters, hubs."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "mnemonic",
                "title": "4. Easy Way to Remember the Layers",
                "type": "bullets",
                "items": [
                  "Top to bottom: Application, Presentation, Session, Transport, Network, Data Link, Physical.",
                  "Common mnemonic: All People Seem To Need Data Processing.",
                  "Bottom to top: Physical, Data Link, Network, Transport, Session, Presentation, Application.",
                  "The top layers are closer to applications and user data.",
                  "The lower layers are closer to network hardware and transmission."
                ]
              },
              {
                "id": "encapsulation",
                "title": "5. Encapsulation and Decapsulation",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Sender: Encapsulation",
                    "steps": [
                      "The application creates data, such as an HTTP request.",
                      "The presentation layer may serialize, encode, compress, or encrypt the data.",
                      "The session layer manages the logical communication session.",
                      "The transport layer adds TCP or UDP information, including source and destination ports.",
                      "The network layer adds source and destination IP addresses.",
                      "The data-link layer adds source and destination MAC addresses and creates a frame.",
                      "The physical layer converts the frame into electrical, optical, or radio signals."
                    ]
                  },
                  {
                    "blockTitle": "Receiver: Decapsulation",
                    "steps": [
                      "The physical layer receives raw signals and reconstructs bits.",
                      "The data-link layer validates and removes the frame information.",
                      "The network layer checks the destination IP address.",
                      "The transport layer reassembles data and delivers it to the correct process using the port number.",
                      "The upper layers decode, decrypt, and interpret the data.",
                      "The application receives the final request or response."
                    ]
                  }
                ]
              },
              {
                "id": "pdu-names",
                "title": "6. Data Unit at Each Layer",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Protocol Data Units",
                    "pairs": [
                      {
                        "k": "Layers 7, 6, and 5",
                        "v": "Data or message."
                      },
                      {
                        "k": "Layer 4",
                        "v": "TCP segment or UDP datagram."
                      },
                      {
                        "k": "Layer 3",
                        "v": "Packet."
                      },
                      {
                        "k": "Layer 2",
                        "v": "Frame."
                      },
                      {
                        "k": "Layer 1",
                        "v": "Bits."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "real-life-flow",
                "title": "7. Real-Life Example: Opening a Website",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Application to Physical",
                    "steps": [
                      "Layer 7: The browser creates an HTTP request.",
                      "Layer 6: The request may be encoded and encrypted using TLS.",
                      "Layer 5: The communication session is maintained.",
                      "Layer 4: TCP or QUIC handles end-to-end delivery and ports.",
                      "Layer 3: IP routes packets toward the server.",
                      "Layer 2: Ethernet or Wi-Fi delivers frames across each local network segment.",
                      "Layer 1: Signals travel through cable, fiber, or radio."
                    ]
                  },
                  {
                    "blockTitle": "At the Server",
                    "steps": [
                      "The server receives the bits and processes them upward through the layers.",
                      "The application server interprets the HTTP request.",
                      "The server creates a response.",
                      "The response travels back down the server's stack and up the client's stack."
                    ]
                  }
                ]
              },
              {
                "id": "layer-7-detail",
                "title": "8. Layer 7 — Application Layer",
                "type": "bullets",
                "items": [
                  "The application layer defines how software applications exchange meaningful messages.",
                  "HTTP is used for web pages and APIs.",
                  "DNS resolves domain names.",
                  "SMTP is used for sending email.",
                  "WebSocket supports persistent bidirectional messaging.",
                  "gRPC commonly uses HTTP/2 and Protocol Buffers for service-to-service communication.",
                  "Layer 7 load balancers can inspect paths, headers, cookies, hostnames, and application content.",
                  "API gateways generally operate primarily at Layer 7."
                ]
              },
              {
                "id": "layer-6-detail",
                "title": "9. Layer 6 — Presentation Layer",
                "type": "bullets",
                "items": [
                  "This layer ensures data is represented in a format both sides understand.",
                  "Serialization converts application objects into transferable formats such as JSON or Protocol Buffers.",
                  "Encoding defines how characters and binary data are represented.",
                  "Compression reduces payload size.",
                  "Encryption protects data from being read by unauthorized parties.",
                  "In real systems, presentation-layer responsibilities are often handled by application libraries rather than a separate standalone component."
                ]
              },
              {
                "id": "layer-5-detail",
                "title": "10. Layer 5 — Session Layer",
                "type": "bullets",
                "items": [
                  "The session layer manages the lifecycle of logical conversations.",
                  "It may establish, maintain, resume, synchronize, and terminate sessions.",
                  "Examples include user login sessions, persistent application sessions, RPC conversations, and reconnection checkpoints.",
                  "Modern networking stacks do not always expose a separate session layer.",
                  "Its responsibilities are often distributed across application protocols, libraries, and transport mechanisms."
                ]
              },
              {
                "id": "layer-4-detail",
                "title": "11. Layer 4 — Transport Layer",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "TCP",
                    "pairs": [
                      {
                        "k": "Type",
                        "v": "Connection-oriented and reliable."
                      },
                      {
                        "k": "Features",
                        "v": "Ordering, retransmission, flow control, congestion control, and duplicate detection."
                      },
                      {
                        "k": "Use cases",
                        "v": "Traditional HTTP, HTTPS, databases, file transfers, and most reliable request-response systems."
                      }
                    ]
                  },
                  {
                    "groupTitle": "UDP",
                    "pairs": [
                      {
                        "k": "Type",
                        "v": "Connectionless with minimal transport overhead."
                      },
                      {
                        "k": "Features",
                        "v": "No built-in delivery guarantee, ordering, or retransmission."
                      },
                      {
                        "k": "Use cases",
                        "v": "DNS, streaming, online gaming, voice calls, telemetry, and low-latency communication."
                      }
                    ]
                  },
                  {
                    "groupTitle": "QUIC",
                    "pairs": [
                      {
                        "k": "Type",
                        "v": "A modern encrypted transport protocol built over UDP."
                      },
                      {
                        "k": "Features",
                        "v": "Reduced connection setup, multiplexed streams, encryption, and improved behavior during packet loss."
                      },
                      {
                        "k": "Use cases",
                        "v": "HTTP/3 and modern internet applications."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "ports",
                "title": "12. Ports and Process-to-Process Delivery",
                "type": "bullets",
                "items": [
                  "An IP address identifies a machine or network interface.",
                  "A port identifies a process or service on that machine.",
                  "The combination of IP address and port identifies a network endpoint.",
                  "Examples include port 80 for HTTP, 443 for HTTPS, and 53 for DNS.",
                  "A server listens on a known port.",
                  "A client usually uses a temporary ephemeral source port.",
                  "Transport-layer headers contain source and destination port numbers."
                ]
              },
              {
                "id": "layer-3-detail",
                "title": "13. Layer 3 — Network Layer",
                "type": "bullets",
                "items": [
                  "The network layer is responsible for communication across different networks.",
                  "IP addresses provide logical addressing.",
                  "Routers inspect destination IP addresses and forward packets toward the next hop.",
                  "Routing protocols help routers learn available paths.",
                  "Packets can travel through multiple routers before reaching the destination.",
                  "ICMP supports network diagnostics and error reporting.",
                  "Layer 3 load balancers make routing decisions using IP addresses and related network information."
                ]
              },
              {
                "id": "layer-2-detail",
                "title": "14. Layer 2 — Data-Link Layer",
                "type": "bullets",
                "items": [
                  "The data-link layer handles communication within the same local network segment.",
                  "It packages packets into frames.",
                  "MAC addresses identify network interfaces within the local network.",
                  "Switches forward frames based on MAC addresses.",
                  "ARP is used in IPv4 networks to discover the MAC address associated with a local IP address.",
                  "VLANs logically separate Layer 2 networks.",
                  "A frame is generally recreated at each routed hop because the next local link has different Layer 2 addressing."
                ]
              },
              {
                "id": "layer-1-detail",
                "title": "15. Layer 1 — Physical Layer",
                "type": "bullets",
                "items": [
                  "The physical layer carries raw bits over a transmission medium.",
                  "It defines cables, connectors, voltages, radio frequencies, signal timing, and transmission rates.",
                  "Examples include Ethernet cables, fiber-optic links, Wi-Fi radio, and cellular signals.",
                  "Physical-layer problems include damaged cables, weak wireless signals, faulty ports, interference, and disconnected equipment."
                ]
              },
              {
                "id": "devices-by-layer",
                "title": "16. Common Devices and Components by Layer",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Layer Mapping",
                    "pairs": [
                      {
                        "k": "Layer 7",
                        "v": "API gateway, reverse proxy, web application firewall, Layer 7 load balancer."
                      },
                      {
                        "k": "Layer 4",
                        "v": "Transport load balancer, connection proxy, stateful firewall."
                      },
                      {
                        "k": "Layer 3",
                        "v": "Router, Layer 3 switch, IP firewall."
                      },
                      {
                        "k": "Layer 2",
                        "v": "Ethernet switch, bridge, wireless access point."
                      },
                      {
                        "k": "Layer 1",
                        "v": "Cable, repeater, hub, transceiver, network interface hardware."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "l4-vs-l7-load-balancer",
                "title": "17. Layer 4 vs Layer 7 Load Balancing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Layer 4 Load Balancer",
                    "pairs": [
                      {
                        "k": "Decision information",
                        "v": "Source and destination IP addresses, ports, and transport protocol."
                      },
                      {
                        "k": "Advantages",
                        "v": "Fast, protocol-agnostic, and lower processing overhead."
                      },
                      {
                        "k": "Limitations",
                        "v": "Cannot make routing decisions using HTTP paths, headers, or cookies."
                      },
                      {
                        "k": "Example",
                        "v": "Forward TCP connections on port 443 across backend servers."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 7 Load Balancer",
                    "pairs": [
                      {
                        "k": "Decision information",
                        "v": "URL path, hostname, HTTP headers, cookies, method, and other application-level information."
                      },
                      {
                        "k": "Advantages",
                        "v": "Content-based routing, TLS termination, authentication integration, and advanced traffic policies."
                      },
                      {
                        "k": "Limitations",
                        "v": "More processing overhead and application-protocol awareness."
                      },
                      {
                        "k": "Example",
                        "v": "Route /payments to the payment service and /orders to the order service."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "osi-vs-tcpip",
                "title": "18. OSI Model vs TCP/IP Model",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "OSI Model",
                    "pairs": [
                      {
                        "k": "Layers",
                        "v": "Seven."
                      },
                      {
                        "k": "Nature",
                        "v": "Conceptual reference model."
                      },
                      {
                        "k": "Strength",
                        "v": "Clear separation of networking responsibilities."
                      }
                    ]
                  },
                  {
                    "groupTitle": "TCP/IP Model",
                    "pairs": [
                      {
                        "k": "Layers",
                        "v": "Commonly represented as four or five layers."
                      },
                      {
                        "k": "Nature",
                        "v": "Practical model based on protocols used by the internet."
                      },
                      {
                        "k": "Mapping",
                        "v": "OSI layers 5, 6, and 7 are generally grouped into the application layer."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Typical Mapping",
                    "pairs": [
                      {
                        "k": "TCP/IP Application",
                        "v": "OSI Application, Presentation, and Session."
                      },
                      {
                        "k": "TCP/IP Transport",
                        "v": "OSI Transport."
                      },
                      {
                        "k": "TCP/IP Internet",
                        "v": "OSI Network."
                      },
                      {
                        "k": "TCP/IP Network Access",
                        "v": "OSI Data Link and Physical."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "troubleshooting",
                "title": "19. Troubleshooting by OSI Layer",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Common Problems",
                    "pairs": [
                      {
                        "k": "Layer 7",
                        "v": "Incorrect API path, HTTP 500 error, authentication failure, malformed request."
                      },
                      {
                        "k": "Layer 6",
                        "v": "Serialization failure, unsupported encoding, certificate or encryption issue."
                      },
                      {
                        "k": "Layer 5",
                        "v": "Expired session, failed reconnection, broken session state."
                      },
                      {
                        "k": "Layer 4",
                        "v": "Port blocked, connection timeout, TCP reset, packet loss affecting transport."
                      },
                      {
                        "k": "Layer 3",
                        "v": "Incorrect IP address, missing route, routing loop, unreachable network."
                      },
                      {
                        "k": "Layer 2",
                        "v": "MAC or ARP issue, VLAN misconfiguration, switch-port problem."
                      },
                      {
                        "k": "Layer 1",
                        "v": "Disconnected cable, weak wireless signal, damaged hardware, no electrical or optical signal."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "security-by-layer",
                "title": "20. Security Across the Layers",
                "type": "bullets",
                "items": [
                  "Layer 7: Protect APIs using authentication, authorization, input validation, rate limiting, and web application firewalls.",
                  "Layer 6: Use strong encryption, secure serialization, and correct certificate validation.",
                  "Layer 5: Protect session identifiers and prevent session hijacking.",
                  "Layer 4: Control ports, limit connections, and protect against SYN floods.",
                  "Layer 3: Use network segmentation, IP filtering, secure routing, and DDoS protection.",
                  "Layer 2: Prevent ARP spoofing, MAC flooding, and unauthorized VLAN access.",
                  "Layer 1: Restrict physical access and protect network hardware."
                ]
              },
              {
                "id": "common-confusions",
                "title": "21. Common Misunderstandings",
                "type": "bullets",
                "items": [
                  "HTTPS is not a completely separate transport protocol. It is HTTP protected by TLS, usually carried over TCP, or over QUIC with HTTP/3.",
                  "A port does not identify a machine; it identifies a process or service on a machine.",
                  "An IP address is not the same as a MAC address. IP supports logical routing across networks; MAC is mainly used for local-link delivery.",
                  "A Layer 7 load balancer is not automatically better than Layer 4. The correct choice depends on routing needs, protocol support, and performance.",
                  "The OSI model is conceptual. Real systems do not always implement every layer as a separate component.",
                  "WebSocket starts with an HTTP-based handshake but then maintains a persistent bidirectional application connection."
                ]
              },
              {
                "id": "interview-qa",
                "title": "22. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "What is the OSI model?",
                    "a": "It is a seven-layer conceptual framework that separates network communication into application, presentation, session, transport, network, data-link, and physical responsibilities."
                  },
                  {
                    "q": "Why is the OSI model useful in system design?",
                    "a": "It helps engineers identify where protocols and infrastructure operate, compare Layer 4 and Layer 7 components, troubleshoot failures, and discuss networking with a shared vocabulary."
                  },
                  {
                    "q": "What happens to data as it moves down the OSI stack?",
                    "a": "Each layer adds its own control information. This process is called encapsulation. At the receiving side, each layer removes and processes the corresponding information through decapsulation."
                  },
                  {
                    "q": "What is the difference between Layer 3 and Layer 4?",
                    "a": "Layer 3 handles IP addressing and routing between networks. Layer 4 handles process-to-process delivery using ports and may provide reliability, ordering, and flow control."
                  },
                  {
                    "q": "What is the difference between a Layer 4 and Layer 7 load balancer?",
                    "a": "A Layer 4 load balancer routes using IP addresses, ports, and transport information. A Layer 7 load balancer understands application protocols and can route using paths, hostnames, headers, and cookies."
                  },
                  {
                    "q": "At which layer does HTTP operate?",
                    "a": "HTTP operates at the application layer, Layer 7."
                  },
                  {
                    "q": "At which layer does TLS operate?",
                    "a": "TLS is commonly associated with the presentation layer because it encrypts and transforms application data, although real implementations span boundaries and do not map perfectly to the OSI model."
                  },
                  {
                    "q": "Why are ports needed?",
                    "a": "An IP address identifies a host or interface, while a port identifies the application process or service that should receive the data."
                  },
                  {
                    "q": "Does every network request pass through all seven layers?",
                    "a": "Conceptually, yes, but real-world implementations may combine or blur layers. The OSI model explains responsibilities rather than requiring seven separate software components."
                  },
                  {
                    "q": "Which model is actually used by the internet?",
                    "a": "The internet is based primarily on the TCP/IP protocol suite. The OSI model is mainly used as a conceptual and educational reference."
                  }
                ]
              },
              {
                "id": "final-summary",
                "title": "23. Final Revision Summary",
                "type": "bullets",
                "items": [
                  "The OSI model divides networking into seven layers.",
                  "Layers 7 to 5 focus on applications, data representation, and sessions.",
                  "Layer 4 provides process-to-process transport using ports.",
                  "Layer 3 provides IP addressing and routing.",
                  "Layer 2 provides local-link delivery using frames and MAC addresses.",
                  "Layer 1 transmits raw bits as physical signals.",
                  "Encapsulation adds headers while moving downward; decapsulation removes them while moving upward.",
                  "Layer 4 load balancers route using connection information.",
                  "Layer 7 load balancers route using application-level information.",
                  "The OSI model is conceptual, while TCP/IP is the practical protocol model used by the internet.",
                  "In system design, the OSI model is most useful for protocol selection, load balancing, security, and troubleshooting."
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "Do not treat the OSI model as a literal seven-component implementation. It is a conceptual model, and real protocols often cross or combine layers."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers often ask the difference between Layer 4 and Layer 7 load balancing. Explain connection-level routing versus application-aware routing."
              },
              {
                "type": "tip",
                "text": "Memorize the full request path: HTTP data → TLS or serialization → session → TCP or QUIC → IP → Ethernet or Wi-Fi → physical signal."
              },
              {
                "type": "interview-favorite",
                "text": "Be able to distinguish IP address, MAC address, and port: host routing, local-link delivery, and process identification."
              },
              {
                "type": "warning",
                "text": "Do not say TLS maps perfectly to only one OSI layer. It is commonly placed near Layer 6 for teaching, but real protocol stacks do not follow the model rigidly."
              }
            ],
            "tags": [
              "system-design",
              "osi-model",
              "networking",
              "application-layer",
              "transport-layer",
              "network-layer",
              "data-link-layer",
              "tcp",
              "udp",
              "ip",
              "load-balancing",
              "system-design-fundamentals"
            ]
          },
          "toolId": "networking-protocols"
        },
        {
          "id": "05",
          "title": "System Design Fundamentals 5: ✅ TCP, UPD, TLS, HTTP, HTTPS, WebSocket, gRPC | OSI Layers Explained",
          "videoUrl": "https://youtu.be/VtbyAw4SslI",
          "description": "In this video from the **System Design Fundamentals** series, we will understand the most important networking protocols used in system design by connecting OSI Layer 3, Layer 4, and Layer 7",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/orjgneojnrgoerngerngewrngorng.png",
          "refresher": {
            "label": "System Design Fundamentals Refresher",
            "estimatedMinutes": 20,
            "lastUpdated": "2026-06-22",
            "hero": {
              "title": "TCP, UDP, QUIC, TLS, HTTP, HTTPS, WebSocket, gRPC, IPv4 and IPv6",
              "oneLinerToMemorize": "Layer 7 defines how applications communicate, Layer 4 controls end-to-end transport, and Layer 3 provides addressing and routing across networks."
            },
            "sections": [
              {
                "id": "big-picture",
                "title": "1. The Big Picture",
                "type": "bullets",
                "items": [
                  "Layer 7 defines the application communication model, such as request-response, streaming, or bidirectional messaging.",
                  "Layer 4 determines how data is transported between processes, including reliability, ordering, latency, and congestion control.",
                  "Layer 3 determines how packets are addressed and routed between networks.",
                  "A protocol stack combines multiple layers. For example, HTTPS commonly means HTTP at Layer 7, TLS for encryption, TCP or QUIC at Layer 4, and IP at Layer 3.",
                  "Choosing a protocol is not about finding the universally best option. The correct choice depends on latency, reliability, browser support, streaming needs, infrastructure, and operational complexity."
                ]
              },
              {
                "id": "protocol-stack-example",
                "title": "2. Example Protocol Stacks",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Traditional HTTPS",
                    "pairs": [
                      {
                        "k": "Layer 7",
                        "v": "HTTP/1.1 or HTTP/2."
                      },
                      {
                        "k": "Security",
                        "v": "TLS."
                      },
                      {
                        "k": "Layer 4",
                        "v": "TCP."
                      },
                      {
                        "k": "Layer 3",
                        "v": "IPv4 or IPv6."
                      }
                    ]
                  },
                  {
                    "groupTitle": "HTTP/3",
                    "pairs": [
                      {
                        "k": "Layer 7",
                        "v": "HTTP/3."
                      },
                      {
                        "k": "Security",
                        "v": "TLS 1.3 integrated into QUIC."
                      },
                      {
                        "k": "Layer 4",
                        "v": "QUIC over UDP."
                      },
                      {
                        "k": "Layer 3",
                        "v": "IPv4 or IPv6."
                      }
                    ]
                  },
                  {
                    "groupTitle": "WebSocket",
                    "pairs": [
                      {
                        "k": "Layer 7",
                        "v": "WebSocket protocol."
                      },
                      {
                        "k": "Handshake",
                        "v": "Starts with an HTTP-based upgrade request."
                      },
                      {
                        "k": "Transport",
                        "v": "Usually TCP, commonly protected using TLS as WSS."
                      }
                    ]
                  },
                  {
                    "groupTitle": "gRPC",
                    "pairs": [
                      {
                        "k": "Layer 7",
                        "v": "gRPC."
                      },
                      {
                        "k": "Serialization",
                        "v": "Protocol Buffers by default."
                      },
                      {
                        "k": "Transport",
                        "v": "Commonly HTTP/2 over TLS and TCP."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "layer-7-overview",
                "title": "3. Layer 7 Overview",
                "type": "bullets",
                "items": [
                  "Layer 7 protocols define the meaning and structure of application messages.",
                  "They determine whether communication is request-response, full-duplex, unary, streaming, text-based, or binary.",
                  "HTTP and HTTPS are the default choices for public web APIs.",
                  "WebSocket is useful for long-lived bidirectional communication.",
                  "gRPC is useful for strongly typed, efficient service-to-service communication.",
                  "Protocol choice affects client compatibility, observability, caching, load balancing, and debugging."
                ]
              },
              {
                "id": "http",
                "title": "4. HTTP",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "HTTP Basics",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Hypertext Transfer Protocol."
                      },
                      {
                        "k": "Communication model",
                        "v": "Primarily request-response."
                      },
                      {
                        "k": "Typical usage",
                        "v": "Web pages, REST APIs, file delivery, service endpoints."
                      },
                      {
                        "k": "Message structure",
                        "v": "Method, path, headers, and optional body."
                      },
                      {
                        "k": "Common methods",
                        "v": "GET, POST, PUT, PATCH, DELETE."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Advantages",
                    "pairs": [
                      {
                        "k": "Compatibility",
                        "v": "Supported by browsers, servers, proxies, gateways, and almost every programming language."
                      },
                      {
                        "k": "Observability",
                        "v": "Easy to inspect, log, debug, and monitor."
                      },
                      {
                        "k": "Infrastructure",
                        "v": "Works well with CDNs, caches, reverse proxies, API gateways, and load balancers."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Limitations",
                    "pairs": [
                      {
                        "k": "Request-driven",
                        "v": "The server normally cannot send arbitrary messages unless the client initiates or maintains a suitable stream."
                      },
                      {
                        "k": "Overhead",
                        "v": "Headers and text-based payloads can be inefficient compared with compact binary protocols."
                      },
                      {
                        "k": "Real-time communication",
                        "v": "Repeated polling can waste bandwidth and increase latency."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "https",
                "title": "5. HTTPS",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "HTTPS Basics",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "HTTP protected using TLS."
                      },
                      {
                        "k": "Confidentiality",
                        "v": "Encrypts traffic so intermediaries cannot easily read the content."
                      },
                      {
                        "k": "Integrity",
                        "v": "Helps detect modification of data in transit."
                      },
                      {
                        "k": "Authentication",
                        "v": "The client verifies the server's identity using certificates."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Important Clarification",
                    "pairs": [
                      {
                        "k": "HTTP vs HTTPS",
                        "v": "HTTPS is not a completely different application protocol. It is HTTP carried through a secure TLS channel."
                      },
                      {
                        "k": "Port",
                        "v": "HTTPS commonly uses port 443."
                      },
                      {
                        "k": "Use",
                        "v": "Use HTTPS for virtually all production web traffic."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "tls",
                "title": "6. TLS",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "TLS Purpose",
                    "steps": [
                      "Encrypt application data.",
                      "Verify server identity using a certificate.",
                      "Protect message integrity.",
                      "Negotiate cryptographic algorithms and session keys."
                    ]
                  },
                  {
                    "blockTitle": "Simplified TLS Handshake",
                    "steps": [
                      "The client sends supported TLS versions, algorithms, and connection parameters.",
                      "The server chooses compatible parameters and sends its certificate.",
                      "The client validates the certificate chain and domain name.",
                      "Both sides derive shared session keys.",
                      "Application data is then encrypted using symmetric encryption."
                    ]
                  },
                  {
                    "blockTitle": "TLS in System Design",
                    "steps": [
                      "TLS may terminate at a CDN, load balancer, reverse proxy, API gateway, or application server.",
                      "Traffic can be re-encrypted between internal components.",
                      "Mutual TLS can authenticate both client and server.",
                      "TLS adds setup cost, but connection reuse and modern protocols reduce repeated overhead."
                    ]
                  }
                ]
              },
              {
                "id": "websocket",
                "title": "7. WebSocket",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "WebSocket Basics",
                    "pairs": [
                      {
                        "k": "Communication model",
                        "v": "Persistent, full-duplex communication."
                      },
                      {
                        "k": "Connection",
                        "v": "One long-lived connection allows both client and server to send messages at any time."
                      },
                      {
                        "k": "Handshake",
                        "v": "Usually begins as an HTTP request that asks to upgrade the connection."
                      },
                      {
                        "k": "Secure form",
                        "v": "WSS uses WebSocket over TLS."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Good Use Cases",
                    "pairs": [
                      {
                        "k": "Chat",
                        "v": "Low-latency messages in both directions."
                      },
                      {
                        "k": "Collaboration",
                        "v": "Live document editing, cursors, and presence updates."
                      },
                      {
                        "k": "Live dashboards",
                        "v": "Continuous delivery of changing data."
                      },
                      {
                        "k": "Gaming",
                        "v": "Frequent bidirectional application events."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Trade-offs",
                    "pairs": [
                      {
                        "k": "Scaling",
                        "v": "Servers must maintain many open connections."
                      },
                      {
                        "k": "State",
                        "v": "Connection state complicates load balancing and failover."
                      },
                      {
                        "k": "Backpressure",
                        "v": "Slow clients can create buffering and memory problems."
                      },
                      {
                        "k": "Delivery guarantees",
                        "v": "The application still needs message IDs, acknowledgements, replay, or persistence when required."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "grpc",
                "title": "8. gRPC",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "gRPC Basics",
                    "pairs": [
                      {
                        "k": "Communication style",
                        "v": "Remote Procedure Call."
                      },
                      {
                        "k": "Transport",
                        "v": "Commonly HTTP/2."
                      },
                      {
                        "k": "Serialization",
                        "v": "Protocol Buffers by default."
                      },
                      {
                        "k": "Contract",
                        "v": "Services and message types are defined in a .proto file."
                      }
                    ]
                  },
                  {
                    "groupTitle": "RPC Modes",
                    "pairs": [
                      {
                        "k": "Unary",
                        "v": "One request and one response."
                      },
                      {
                        "k": "Server streaming",
                        "v": "One request followed by a stream of responses."
                      },
                      {
                        "k": "Client streaming",
                        "v": "A stream of requests followed by one response."
                      },
                      {
                        "k": "Bidirectional streaming",
                        "v": "Both sides send streams independently."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Advantages",
                    "pairs": [
                      {
                        "k": "Efficiency",
                        "v": "Compact binary messages and HTTP/2 multiplexing."
                      },
                      {
                        "k": "Strong typing",
                        "v": "Generated clients and servers reduce contract mismatch."
                      },
                      {
                        "k": "Internal services",
                        "v": "Works well for controlled service-to-service environments."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Limitations",
                    "pairs": [
                      {
                        "k": "Browser support",
                        "v": "Direct browser support is more limited than normal HTTP APIs and may require gRPC-Web or a proxy."
                      },
                      {
                        "k": "Debugging",
                        "v": "Binary payloads are less human-readable."
                      },
                      {
                        "k": "Public APIs",
                        "v": "REST or JSON APIs are often easier for external consumers."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "layer7-comparison",
                "title": "9. HTTP vs HTTPS vs WebSocket vs gRPC",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "HTTP",
                    "pairs": [
                      {
                        "k": "Best for",
                        "v": "Standard web communication and public APIs."
                      },
                      {
                        "k": "Pattern",
                        "v": "Request-response."
                      },
                      {
                        "k": "Payload",
                        "v": "Commonly JSON, text, HTML, or files."
                      }
                    ]
                  },
                  {
                    "groupTitle": "HTTPS",
                    "pairs": [
                      {
                        "k": "Best for",
                        "v": "Secure web communication."
                      },
                      {
                        "k": "Pattern",
                        "v": "Same application behavior as HTTP, protected by TLS."
                      },
                      {
                        "k": "Default choice",
                        "v": "Use instead of plain HTTP for production systems."
                      }
                    ]
                  },
                  {
                    "groupTitle": "WebSocket",
                    "pairs": [
                      {
                        "k": "Best for",
                        "v": "Long-lived bidirectional real-time communication."
                      },
                      {
                        "k": "Pattern",
                        "v": "Full-duplex messaging."
                      },
                      {
                        "k": "Main cost",
                        "v": "Connection state and operational complexity."
                      }
                    ]
                  },
                  {
                    "groupTitle": "gRPC",
                    "pairs": [
                      {
                        "k": "Best for",
                        "v": "High-performance internal service communication."
                      },
                      {
                        "k": "Pattern",
                        "v": "Unary and streaming RPC."
                      },
                      {
                        "k": "Main cost",
                        "v": "Lower human readability and weaker universal browser compatibility."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "layer7-decision-guide",
                "title": "10. When to Use Each Layer 7 Protocol",
                "type": "bullets",
                "items": [
                  "Use HTTPS with REST or normal HTTP APIs for browsers, mobile apps, third-party integrations, and public APIs.",
                  "Use WebSocket when both client and server must send frequent low-latency updates over a persistent connection.",
                  "Use gRPC for internal microservices when strong contracts, binary efficiency, and streaming are valuable.",
                  "Use normal HTTP polling when updates are infrequent and simplicity matters more than real-time latency.",
                  "Do not use WebSocket merely because the application feels modern. Persistent connections add real operational cost.",
                  "Do not use gRPC automatically for every internal API. Teams must support its tooling, observability, proxies, and debugging model."
                ]
              },
              {
                "id": "layer4-overview",
                "title": "11. Layer 4 Overview",
                "type": "bullets",
                "items": [
                  "Layer 4 provides process-to-process communication using port numbers.",
                  "It controls how application data is segmented and transported.",
                  "The main design questions are reliability, ordering, latency, congestion control, connection setup, and multiplexing.",
                  "TCP prioritizes reliable ordered delivery.",
                  "UDP prioritizes minimal overhead and application control.",
                  "QUIC adds modern reliability, multiplexing, encryption, and faster connection establishment over UDP."
                ]
              },
              {
                "id": "tcp",
                "title": "12. TCP",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "TCP Characteristics",
                    "pairs": [
                      {
                        "k": "Connection",
                        "v": "Connection-oriented."
                      },
                      {
                        "k": "Reliability",
                        "v": "Retransmits lost data."
                      },
                      {
                        "k": "Ordering",
                        "v": "Delivers bytes to the application in order."
                      },
                      {
                        "k": "Flow control",
                        "v": "Prevents a fast sender from overwhelming a slow receiver."
                      },
                      {
                        "k": "Congestion control",
                        "v": "Adjusts transmission based on network conditions."
                      },
                      {
                        "k": "Data model",
                        "v": "A continuous byte stream rather than independent application messages."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Good Use Cases",
                    "pairs": [
                      {
                        "k": "Web traffic",
                        "v": "HTTP/1.1 and HTTP/2."
                      },
                      {
                        "k": "Databases",
                        "v": "Reliable queries and responses."
                      },
                      {
                        "k": "File transfer",
                        "v": "Complete and ordered delivery is important."
                      },
                      {
                        "k": "Messaging",
                        "v": "Systems where data loss is unacceptable."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "tcp-handshake",
                "title": "13. TCP Connection Lifecycle",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Three-Way Handshake",
                    "steps": [
                      "Client sends SYN to request a connection.",
                      "Server replies with SYN-ACK.",
                      "Client sends ACK.",
                      "The connection is established and application data can flow."
                    ]
                  },
                  {
                    "blockTitle": "Data Transfer",
                    "steps": [
                      "TCP numbers bytes using sequence numbers.",
                      "The receiver acknowledges received data.",
                      "Lost segments are retransmitted.",
                      "Flow and congestion-control algorithms regulate sending speed."
                    ]
                  },
                  {
                    "blockTitle": "Connection Closure",
                    "steps": [
                      "Each direction is closed independently using FIN and ACK messages.",
                      "A connection can also be terminated abruptly using RST."
                    ]
                  }
                ]
              },
              {
                "id": "udp",
                "title": "14. UDP",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "UDP Characteristics",
                    "pairs": [
                      {
                        "k": "Connection",
                        "v": "Connectionless."
                      },
                      {
                        "k": "Reliability",
                        "v": "No built-in retransmission or delivery guarantee."
                      },
                      {
                        "k": "Ordering",
                        "v": "No built-in ordering guarantee."
                      },
                      {
                        "k": "Data model",
                        "v": "Message-oriented datagrams."
                      },
                      {
                        "k": "Overhead",
                        "v": "Smaller protocol overhead than TCP."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Good Use Cases",
                    "pairs": [
                      {
                        "k": "DNS",
                        "v": "Small, fast queries where retry can happen at the application level."
                      },
                      {
                        "k": "Voice and video",
                        "v": "Late data may be less useful than missing data."
                      },
                      {
                        "k": "Gaming",
                        "v": "Fresh state may matter more than retransmitting old state."
                      },
                      {
                        "k": "Telemetry",
                        "v": "Occasional loss may be acceptable."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Important Clarification",
                    "pairs": [
                      {
                        "k": "UDP is not automatically faster",
                        "v": "It has less built-in work, but the application may need to implement reliability, ordering, congestion handling, and security."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "quic",
                "title": "15. QUIC",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "QUIC Characteristics",
                    "pairs": [
                      {
                        "k": "Foundation",
                        "v": "Runs over UDP."
                      },
                      {
                        "k": "Reliability",
                        "v": "Provides reliable delivery where required."
                      },
                      {
                        "k": "Encryption",
                        "v": "Integrates TLS 1.3."
                      },
                      {
                        "k": "Multiplexing",
                        "v": "Supports multiple independent streams within one connection."
                      },
                      {
                        "k": "Connection setup",
                        "v": "Reduces handshake latency compared with separate TCP and TLS setup."
                      },
                      {
                        "k": "Connection migration",
                        "v": "Can survive certain network changes, such as switching from Wi-Fi to mobile data."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Why QUIC Helps",
                    "pairs": [
                      {
                        "k": "Head-of-line blocking",
                        "v": "Packet loss in one stream does not block delivery of unrelated streams at the transport layer."
                      },
                      {
                        "k": "User-space implementation",
                        "v": "Protocol updates can often be deployed faster than kernel-level TCP changes."
                      },
                      {
                        "k": "HTTP/3",
                        "v": "HTTP/3 uses QUIC as its transport."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Trade-offs",
                    "pairs": [
                      {
                        "k": "CPU cost",
                        "v": "Encryption and user-space processing can increase CPU usage."
                      },
                      {
                        "k": "Network support",
                        "v": "Some networks and middleboxes handle UDP poorly."
                      },
                      {
                        "k": "Operational maturity",
                        "v": "Debugging and monitoring may be less familiar than TCP."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "layer4-comparison",
                "title": "16. TCP vs UDP vs QUIC",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "TCP",
                    "pairs": [
                      {
                        "k": "Reliability",
                        "v": "Built in."
                      },
                      {
                        "k": "Ordering",
                        "v": "Built in."
                      },
                      {
                        "k": "Connection setup",
                        "v": "Requires a handshake."
                      },
                      {
                        "k": "Best for",
                        "v": "Reliable general-purpose communication."
                      }
                    ]
                  },
                  {
                    "groupTitle": "UDP",
                    "pairs": [
                      {
                        "k": "Reliability",
                        "v": "Not built in."
                      },
                      {
                        "k": "Ordering",
                        "v": "Not built in."
                      },
                      {
                        "k": "Connection setup",
                        "v": "No transport handshake."
                      },
                      {
                        "k": "Best for",
                        "v": "Low-latency messaging where the application controls trade-offs."
                      }
                    ]
                  },
                  {
                    "groupTitle": "QUIC",
                    "pairs": [
                      {
                        "k": "Reliability",
                        "v": "Built in where required."
                      },
                      {
                        "k": "Ordering",
                        "v": "Per-stream ordering."
                      },
                      {
                        "k": "Connection setup",
                        "v": "Designed for low-latency secure establishment."
                      },
                      {
                        "k": "Best for",
                        "v": "Modern secure internet transport and HTTP/3."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "layer4-decision-guide",
                "title": "17. When to Use TCP, UDP, or QUIC",
                "type": "bullets",
                "items": [
                  "Use TCP when data must arrive completely and in order, and broad compatibility matters.",
                  "Use UDP when the application can tolerate loss or needs custom control over retransmission and timing.",
                  "Use QUIC when modern low-latency secure transport, multiplexed streams, and connection migration are valuable.",
                  "Do not choose UDP only to avoid TCP overhead unless the application team is ready to handle missing transport features.",
                  "Do not assume TCP guarantees application-level success. It only confirms delivery to the remote networking stack, not completion of the business operation.",
                  "Use idempotency and application acknowledgements when business-level delivery guarantees matter."
                ]
              },
              {
                "id": "layer3-overview",
                "title": "18. Layer 3 Overview",
                "type": "bullets",
                "items": [
                  "Layer 3 provides logical addressing and routing between networks.",
                  "IP addresses identify network interfaces and allow routers to forward packets.",
                  "IPv4 uses 32-bit addresses.",
                  "IPv6 uses 128-bit addresses.",
                  "Routers inspect the destination IP address and forward packets toward the next hop.",
                  "Layer 3 does not identify the destination application process; Layer 4 ports do that."
                ]
              },
              {
                "id": "ipv4",
                "title": "19. IPv4",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "IPv4 Basics",
                    "pairs": [
                      {
                        "k": "Address size",
                        "v": "32 bits."
                      },
                      {
                        "k": "Notation",
                        "v": "Four decimal octets, such as 192.168.1.10."
                      },
                      {
                        "k": "Address capacity",
                        "v": "Approximately 4.3 billion theoretical addresses."
                      },
                      {
                        "k": "Deployment",
                        "v": "Still widely used across the internet."
                      }
                    ]
                  },
                  {
                    "groupTitle": "IPv4 Limitations",
                    "pairs": [
                      {
                        "k": "Address exhaustion",
                        "v": "The available public address space is limited."
                      },
                      {
                        "k": "NAT dependency",
                        "v": "Network Address Translation is widely used to share public addresses."
                      },
                      {
                        "k": "Operational complexity",
                        "v": "NAT complicates inbound connections, peer-to-peer communication, and end-to-end visibility."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "ipv6",
                "title": "20. IPv6",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "IPv6 Basics",
                    "pairs": [
                      {
                        "k": "Address size",
                        "v": "128 bits."
                      },
                      {
                        "k": "Notation",
                        "v": "Hexadecimal groups separated by colons, such as 2001:db8::1."
                      },
                      {
                        "k": "Address capacity",
                        "v": "Extremely large address space."
                      },
                      {
                        "k": "Configuration",
                        "v": "Supports mechanisms such as stateless address autoconfiguration."
                      }
                    ]
                  },
                  {
                    "groupTitle": "IPv6 Advantages",
                    "pairs": [
                      {
                        "k": "Address space",
                        "v": "Greatly reduces pressure caused by address exhaustion."
                      },
                      {
                        "k": "End-to-end addressing",
                        "v": "Reduces the technical need for widespread address-sharing NAT."
                      },
                      {
                        "k": "Simplified header",
                        "v": "Uses a streamlined base header with optional extension headers."
                      },
                      {
                        "k": "Modern networking",
                        "v": "Designed for long-term internet growth."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "ipv4-vs-ipv6",
                "title": "21. IPv4 vs IPv6",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Addressing",
                    "pairs": [
                      {
                        "k": "IPv4",
                        "v": "32-bit address."
                      },
                      {
                        "k": "IPv6",
                        "v": "128-bit address."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Notation",
                    "pairs": [
                      {
                        "k": "IPv4",
                        "v": "Dotted-decimal notation."
                      },
                      {
                        "k": "IPv6",
                        "v": "Colon-separated hexadecimal notation."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Address Availability",
                    "pairs": [
                      {
                        "k": "IPv4",
                        "v": "Limited and heavily dependent on address reuse and NAT."
                      },
                      {
                        "k": "IPv6",
                        "v": "Extremely large address space."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Broadcast",
                    "pairs": [
                      {
                        "k": "IPv4",
                        "v": "Supports broadcast."
                      },
                      {
                        "k": "IPv6",
                        "v": "Uses multicast and anycast instead of traditional broadcast."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Compatibility",
                    "pairs": [
                      {
                        "k": "IPv4",
                        "v": "Universally mature and widely deployed."
                      },
                      {
                        "k": "IPv6",
                        "v": "Increasingly deployed, but transition mechanisms are still required."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "dual-stack",
                "title": "22. IPv4 and IPv6 Coexistence",
                "type": "bullets",
                "items": [
                  "IPv4 and IPv6 are not directly interchangeable.",
                  "Dual-stack systems support both IPv4 and IPv6.",
                  "DNS can return A records for IPv4 and AAAA records for IPv6.",
                  "Clients may attempt one or both address families and select the connection that succeeds quickly.",
                  "Tunneling and translation mechanisms can bridge environments during migration.",
                  "For modern public systems, supporting both address families is often safer than assuming universal IPv6-only connectivity."
                ]
              },
              {
                "id": "end-to-end-flow",
                "title": "23. End-to-End Example: Calling a Secure API",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Resolve the Destination",
                    "steps": [
                      "The client resolves the API domain using DNS.",
                      "DNS returns an IPv4 address, IPv6 address, or both."
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Select Layer 3 Connectivity",
                    "steps": [
                      "The client selects a reachable destination address.",
                      "IP routers forward packets across networks."
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Establish Layer 4 Transport",
                    "steps": [
                      "For HTTP/1.1 or HTTP/2, the client commonly establishes a TCP connection.",
                      "For HTTP/3, the client establishes a QUIC connection over UDP."
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Establish Security",
                    "steps": [
                      "TLS verifies the server identity and establishes encryption keys.",
                      "With QUIC, TLS 1.3 is integrated into the transport handshake."
                    ]
                  },
                  {
                    "blockTitle": "Step 5: Exchange Layer 7 Messages",
                    "steps": [
                      "The client sends an HTTP request or gRPC call.",
                      "The server processes the request and returns a response.",
                      "The connection may be reused for additional requests."
                    ]
                  }
                ]
              },
              {
                "id": "system-design-decisions",
                "title": "24. System Design Decision Guide",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Public REST API",
                    "pairs": [
                      {
                        "k": "Recommended",
                        "v": "HTTPS with JSON over HTTP/1.1 or HTTP/2."
                      },
                      {
                        "k": "Why",
                        "v": "Maximum compatibility, simple debugging, and mature infrastructure."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Internal Microservices",
                    "pairs": [
                      {
                        "k": "Recommended",
                        "v": "HTTPS APIs or gRPC depending on performance and contract needs."
                      },
                      {
                        "k": "Why",
                        "v": "gRPC offers strong typing and streaming, while HTTP APIs remain simpler to operate."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Real-Time Chat",
                    "pairs": [
                      {
                        "k": "Recommended",
                        "v": "WebSocket, often with a message broker behind the connection servers."
                      },
                      {
                        "k": "Why",
                        "v": "Frequent bidirectional messages over a persistent connection."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Live One-Way Updates",
                    "pairs": [
                      {
                        "k": "Recommended",
                        "v": "Server-Sent Events or streaming HTTP may be simpler than WebSocket."
                      },
                      {
                        "k": "Why",
                        "v": "The server primarily pushes data and the client sends few messages."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Voice, Video, or Gaming",
                    "pairs": [
                      {
                        "k": "Recommended",
                        "v": "UDP-based protocols or QUIC depending on requirements."
                      },
                      {
                        "k": "Why",
                        "v": "Low latency may matter more than retransmitting stale data."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Modern Web Delivery",
                    "pairs": [
                      {
                        "k": "Recommended",
                        "v": "HTTP/2 over TCP or HTTP/3 over QUIC."
                      },
                      {
                        "k": "Why",
                        "v": "Connection reuse, multiplexing, encryption, and improved page performance."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "common-misconceptions",
                "title": "25. Common Misconceptions",
                "type": "bullets",
                "items": [
                  "HTTPS is HTTP secured by TLS; it is not unrelated to HTTP.",
                  "WebSocket does not replace HTTP for every API. It is designed for persistent bidirectional messaging.",
                  "gRPC is not automatically faster in every system. Serialization, payload size, network latency, service logic, and infrastructure all matter.",
                  "UDP does not guarantee lower end-to-end latency if the application must rebuild TCP-like features badly.",
                  "TCP guarantees ordered byte delivery, not successful completion of a payment, order, or business transaction.",
                  "QUIC uses UDP as a foundation but implements substantial transport functionality above it.",
                  "IPv6 does not automatically make an application faster, although routing and network conditions may differ.",
                  "An IP address identifies a network interface, while a port identifies the receiving process."
                ]
              },
              {
                "id": "failure-handling",
                "title": "26. Failure Handling Across the Stack",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Layer 7",
                    "pairs": [
                      {
                        "k": "Techniques",
                        "v": "Timeouts, idempotency keys, retries, authentication, application acknowledgements, and error codes."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 4",
                    "pairs": [
                      {
                        "k": "Techniques",
                        "v": "Retransmission, congestion control, connection health checks, keepalive, and reconnect logic."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 3",
                    "pairs": [
                      {
                        "k": "Techniques",
                        "v": "Redundant routes, multiple regions, dual-stack support, anycast, and failover endpoints."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Critical Rule",
                    "pairs": [
                      {
                        "k": "Retries",
                        "v": "Retry only transient failures and use exponential backoff with jitter."
                      },
                      {
                        "k": "Side effects",
                        "v": "Use idempotency for operations such as payments, order creation, and resource provisioning."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "interview-qa",
                "title": "27. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "What is the difference between HTTP and HTTPS?",
                    "a": "HTTPS is HTTP carried over a TLS-protected connection. TLS provides encryption, integrity, and server authentication."
                  },
                  {
                    "q": "When should you use WebSocket instead of HTTP?",
                    "a": "Use WebSocket when the system needs frequent low-latency bidirectional communication over a long-lived connection. Use ordinary HTTP when request-response is sufficient."
                  },
                  {
                    "q": "When should you use gRPC?",
                    "a": "Use gRPC primarily for controlled service-to-service communication where strong contracts, generated code, compact binary payloads, and streaming are valuable."
                  },
                  {
                    "q": "What is the difference between TCP and UDP?",
                    "a": "TCP provides reliable ordered byte-stream delivery with congestion and flow control. UDP sends independent datagrams without built-in reliability or ordering."
                  },
                  {
                    "q": "Why is QUIC built over UDP?",
                    "a": "UDP provides a deployable foundation in user space, allowing QUIC to implement modern connection setup, encryption, streams, congestion control, and migration without depending on operating-system TCP changes."
                  },
                  {
                    "q": "What is head-of-line blocking?",
                    "a": "It occurs when missing data prevents later data from being delivered. TCP exposes one ordered byte stream, so packet loss can delay all application streams sharing that connection. QUIC isolates ordering by stream."
                  },
                  {
                    "q": "Does TCP guarantee that an API request was processed?",
                    "a": "No. TCP confirms byte delivery behavior, not business completion. The server may fail after receiving data, so applications need responses, acknowledgements, idempotency, and reconciliation."
                  },
                  {
                    "q": "What is the difference between IPv4 and IPv6?",
                    "a": "IPv4 uses 32-bit addresses and has limited address space. IPv6 uses 128-bit addresses, provides a vastly larger address space, and removes the technical need for widespread address-sharing NAT."
                  },
                  {
                    "q": "Can HTTP use UDP?",
                    "a": "HTTP/3 runs over QUIC, and QUIC runs over UDP. Traditional HTTP/1.1 and HTTP/2 commonly use TCP."
                  },
                  {
                    "q": "How do you choose between REST and gRPC?",
                    "a": "Choose REST or normal HTTPS APIs for broad compatibility, public consumption, and easy debugging. Choose gRPC for efficient internal communication with strong contracts and streaming."
                  },
                  {
                    "q": "How do you scale WebSocket servers?",
                    "a": "Use multiple connection servers, load balancing, connection-aware routing when necessary, a shared pub-sub or message broker, distributed presence state, backpressure handling, and reconnection logic."
                  },
                  {
                    "q": "Why is IPv6 adoption not instantaneous?",
                    "a": "Networks, clients, providers, software, security policies, and operational tooling must all support it. Dual-stack deployment is commonly used during the transition."
                  }
                ]
              },
              {
                "id": "final-summary",
                "title": "28. Final Revision Summary",
                "type": "bullets",
                "items": [
                  "Layer 7 defines application communication.",
                  "HTTP is the standard request-response protocol for the web.",
                  "HTTPS is HTTP protected using TLS.",
                  "WebSocket provides persistent bidirectional communication.",
                  "gRPC provides strongly typed unary and streaming RPC, commonly over HTTP/2.",
                  "TLS provides confidentiality, integrity, and identity verification.",
                  "TCP provides reliable ordered byte-stream delivery.",
                  "UDP provides lightweight datagram delivery without built-in reliability.",
                  "QUIC provides secure multiplexed transport over UDP and powers HTTP/3.",
                  "IPv4 uses 32-bit addresses; IPv6 uses 128-bit addresses.",
                  "Choose protocols based on communication pattern, latency, reliability, compatibility, and operational cost.",
                  "Transport delivery is not the same as successful business processing."
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "Do not say HTTPS is a completely separate protocol from HTTP. HTTPS is HTTP protected by TLS."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers often ask when to use HTTP, WebSocket, or gRPC. Answer using communication pattern, client compatibility, payload efficiency, and operational complexity."
              },
              {
                "type": "warning",
                "text": "Do not claim UDP is always faster than TCP. UDP provides fewer built-in guarantees, but rebuilding reliability and congestion control at the application layer can be difficult."
              },
              {
                "type": "interview-favorite",
                "text": "Must mention that TCP guarantees byte delivery behavior, not completion of the business operation. Application-level acknowledgement and idempotency are still required."
              },
              {
                "type": "tip",
                "text": "Memorize the stack: HTTP or gRPC → TLS → TCP, or HTTP/3 → QUIC over UDP → IPv4 or IPv6."
              },
              {
                "type": "warning",
                "text": "The correct term is UDP, not UPD."
              }
            ],
            "tags": [
              "system-design",
              "networking",
              "osi-model",
              "http",
              "https",
              "tls",
              "websocket",
              "grpc",
              "tcp",
              "udp",
              "quic",
              "ipv4",
              "ipv6",
              "layer-7",
              "layer-4",
              "layer-3",
              "system-design-fundamentals"
            ]
          },
          "toolId": "networking-protocols"
        },
        {
          "id": "06",
          "title": "System Design Fundamentals 6: ✅ Vertical Scaling vs Horizontal Scaling Explained",
          "videoUrl": "https://youtu.be/_DkLKAfWm6s",
          "description": "In this video, we will understand one of the most important concepts in System Design — Scaling. When your application starts getting more users, more requests, and more traffic, a single server may not be enough. That is when scaling becomes important.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/PopularQuestionsifb43itb3974th39gb3igb434t3.png",
          "toolId": "scaling",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/scaling234t87y3248ty32iu4yt238t9y34b8.png",
          "refresher": {
            "label": "System Design Fundamentals Refresher",
            "estimatedMinutes": 18,
            "lastUpdated": "2026-07-03",
            "hero": {
              "title": "Fundamentals of Scaling: Vertical Scaling and Horizontal Scaling",
              "oneLinerToMemorize": "Vertical scaling makes one machine stronger; horizontal scaling adds more machines and distributes the load."
            },
            "sections": [
              {
                "id": "what-is-scaling",
                "title": "1. What Is Scaling?",
                "type": "bullets",
                "items": [
                  "Scaling means increasing a system's ability to handle more users, requests, data, traffic, or background work.",
                  "A system usually needs scaling when CPU, memory, disk, network bandwidth, database capacity, or request latency becomes a bottleneck.",
                  "Scaling is not only about adding servers. It also includes better architecture, caching, load balancing, database optimization, queues, partitioning, and reducing unnecessary work.",
                  "The two basic scaling strategies are vertical scaling and horizontal scaling.",
                  "Vertical scaling means increasing the power of an existing machine.",
                  "Horizontal scaling means adding more machines and distributing traffic or data across them."
                ]
              },
              {
                "id": "why-scaling-matters",
                "title": "2. Why Scaling Matters in System Design",
                "type": "bullets",
                "items": [
                  "Performance: The system should respond quickly even as traffic increases.",
                  "Availability: The system should keep working even if one server fails.",
                  "Reliability: The system should handle load spikes without collapsing.",
                  "Cost Control: The system should add capacity efficiently instead of wasting resources.",
                  "User Experience: Slow pages, failed requests, and timeouts directly hurt users.",
                  "Business Growth: A system that cannot scale becomes a business bottleneck.",
                  "Interview Relevance: Scaling trade-offs are one of the most common system design discussion areas."
                ]
              },
              {
                "id": "vertical-scaling",
                "title": "3. Vertical Scaling",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Definition",
                    "pairs": [
                      { "k": "Meaning", "v": "Increase the capacity of a single machine by adding more CPU, RAM, storage, IOPS, or network bandwidth." },
                      { "k": "Common phrase", "v": "Scale up." },
                      { "k": "Example", "v": "Move from a 4-core 16 GB RAM server to a 32-core 128 GB RAM server." }
                    ]
                  },
                  {
                    "groupTitle": "Where It Is Used",
                    "pairs": [
                      { "k": "Application server", "v": "Increase CPU and memory to handle more requests on the same instance." },
                      { "k": "Database server", "v": "Increase RAM, CPU, storage speed, or IOPS to handle more queries." },
                      { "k": "Cache server", "v": "Increase memory to store more cached data." },
                      { "k": "Analytics workload", "v": "Use a larger machine for heavy computation." }
                    ]
                  }
                ]
              },
              {
                "id": "vertical-pros",
                "title": "4. Pros of Vertical Scaling",
                "type": "bullets",
                "items": [
                  "Simple to understand and implement.",
                  "Usually requires fewer architectural changes.",
                  "No need to split traffic across many servers in the beginning.",
                  "Good for early-stage systems where traffic is still moderate.",
                  "Useful for databases because a bigger machine can immediately improve query performance.",
                  "Lower operational complexity compared with distributed systems.",
                  "Fewer problems with distributed coordination, data partitioning, replication, and consistency.",
                  "Can be faster to apply during an urgent capacity issue if a larger instance is available."
                ]
              },
              {
                "id": "vertical-cons",
                "title": "5. Cons of Vertical Scaling",
                "type": "bullets",
                "items": [
                  "Hard physical or cloud-provider limits exist. A single machine can only become so powerful.",
                  "Cost grows sharply at the high end. Very large machines are often disproportionately expensive.",
                  "Still creates a single point of failure if there is only one machine.",
                  "Upgrades may require downtime, restart, migration, or failover.",
                  "Does not naturally improve availability.",
                  "A single machine can still be overwhelmed by sudden traffic spikes.",
                  "Hardware failure affects the whole workload if redundancy is not added.",
                  "Eventually, vertical scaling hits a ceiling and the system must move toward horizontal scaling or workload reduction."
                ]
              },
              {
                "id": "horizontal-scaling",
                "title": "6. Horizontal Scaling",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Definition",
                    "pairs": [
                      { "k": "Meaning", "v": "Increase capacity by adding more machines or instances and distributing work across them." },
                      { "k": "Common phrase", "v": "Scale out." },
                      { "k": "Example", "v": "Run 10 application servers behind a load balancer instead of one large application server." }
                    ]
                  },
                  {
                    "groupTitle": "Where It Is Used",
                    "pairs": [
                      { "k": "Application servers", "v": "Multiple stateless servers handle requests behind a load balancer." },
                      { "k": "Databases", "v": "Read replicas, sharding, partitioning, or distributed databases spread query and data load." },
                      { "k": "Caches", "v": "Distributed cache clusters store data across nodes." },
                      { "k": "Workers", "v": "Multiple background workers consume jobs from a queue." },
                      { "k": "Storage", "v": "Distributed object storage spreads data across many machines." }
                    ]
                  }
                ]
              },
              {
                "id": "horizontal-pros",
                "title": "7. Pros of Horizontal Scaling",
                "type": "bullets",
                "items": [
                  "Can support much larger scale than a single machine.",
                  "Improves availability when traffic can move away from failed instances.",
                  "Capacity can be added incrementally by adding more nodes.",
                  "Works well with stateless application servers.",
                  "Supports auto scaling based on traffic, CPU, memory, queue length, or custom metrics.",
                  "Can use commodity machines instead of one extremely expensive machine.",
                  "Better fault isolation if the system is designed correctly.",
                  "Allows regional distribution for lower latency and disaster recovery.",
                  "Works naturally with queues and worker pools because jobs can be processed in parallel."
                ]
              },
              {
                "id": "horizontal-cons",
                "title": "8. Cons of Horizontal Scaling",
                "type": "bullets",
                "items": [
                  "Adds distributed-system complexity.",
                  "Requires load balancing, service discovery, health checks, monitoring, and deployment automation.",
                  "State management becomes harder because requests may go to different servers.",
                  "Session handling requires stateless tokens, shared session storage, sticky sessions, or another coordination mechanism.",
                  "Databases are harder to horizontally scale than stateless application servers.",
                  "Data consistency becomes more difficult with replication and sharding.",
                  "Debugging is harder because requests may pass through many machines and services.",
                  "Network failures, partial failures, retries, duplicate requests, and race conditions become common design concerns.",
                  "Operational cost can increase if many underutilized instances are running."
                ]
              },
              {
                "id": "vertical-vs-horizontal",
                "title": "9. Vertical Scaling vs Horizontal Scaling",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Core Difference",
                    "pairs": [
                      { "k": "Vertical scaling", "v": "Make one server more powerful." },
                      { "k": "Horizontal scaling", "v": "Add more servers and distribute work." }
                    ]
                  },
                  {
                    "groupTitle": "Complexity",
                    "pairs": [
                      { "k": "Vertical scaling", "v": "Simpler architecture." },
                      { "k": "Horizontal scaling", "v": "More complex distributed architecture." }
                    ]
                  },
                  {
                    "groupTitle": "Availability",
                    "pairs": [
                      { "k": "Vertical scaling", "v": "Does not solve single-machine failure by itself." },
                      { "k": "Horizontal scaling", "v": "Can improve availability when multiple healthy nodes exist." }
                    ]
                  },
                  {
                    "groupTitle": "Limit",
                    "pairs": [
                      { "k": "Vertical scaling", "v": "Limited by maximum machine size." },
                      { "k": "Horizontal scaling", "v": "Can scale much further but needs better design." }
                    ]
                  },
                  {
                    "groupTitle": "Cost Pattern",
                    "pairs": [
                      { "k": "Vertical scaling", "v": "Simple at first, expensive at high-end machine sizes." },
                      { "k": "Horizontal scaling", "v": "Can be cost-efficient at scale, but operational complexity costs more." }
                    ]
                  }
                ]
              },
              {
                "id": "when-to-use-vertical",
                "title": "10. When to Use Vertical Scaling",
                "type": "bullets",
                "items": [
                  "Use vertical scaling when the system is still simple and traffic is moderate.",
                  "Use it when the immediate bottleneck is CPU, memory, disk IOPS, or storage on a single server.",
                  "Use it for early-stage products where engineering time is more valuable than perfect architecture.",
                  "Use it for monoliths before the system genuinely needs distributed complexity.",
                  "Use it for databases when adding RAM, CPU, or faster storage can significantly improve performance.",
                  "Use it as a short-term fix during traffic growth, but do not pretend it is an infinite long-term plan.",
                  "Use it when downtime tolerance, cost, and provider limits are acceptable."
                ]
              },
              {
                "id": "when-to-use-horizontal",
                "title": "11. When to Use Horizontal Scaling",
                "type": "bullets",
                "items": [
                  "Use horizontal scaling when traffic exceeds what one machine can safely handle.",
                  "Use it when high availability is required.",
                  "Use it when traffic is spiky and auto scaling can add or remove instances.",
                  "Use it for stateless application servers behind a load balancer.",
                  "Use it for background processing where many workers can process independent jobs.",
                  "Use it when the system must serve users from multiple regions.",
                  "Use it when read traffic can be spread across replicas.",
                  "Use it when data volume requires partitioning or sharding.",
                  "Use it when a single-machine failure cannot be allowed to take down the system."
                ]
              },
              {
                "id": "application-server-scaling",
                "title": "12. Scaling Application Servers",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Step 1: Start Simple",
                    "steps": [
                      "Run one application server.",
                      "Measure CPU, memory, latency, error rate, and request throughput.",
                      "Optimize inefficient code, database queries, and caching before blindly adding infrastructure."
                    ]
                  },
                  {
                    "blockTitle": "Step 2: Scale Vertically",
                    "steps": [
                      "Increase CPU and memory if the app server is resource-bound.",
                      "Use a larger instance if traffic growth is moderate.",
                      "This is usually the fastest first move."
                    ]
                  },
                  {
                    "blockTitle": "Step 3: Make the App Stateless",
                    "steps": [
                      "Move sessions to signed tokens, Redis, database, or a dedicated session store.",
                      "Do not store user-specific critical state only in local memory.",
                      "Store uploaded files in shared object storage rather than local disk."
                    ]
                  },
                  {
                    "blockTitle": "Step 4: Scale Horizontally",
                    "steps": [
                      "Place multiple application servers behind a load balancer.",
                      "Add health checks so unhealthy servers stop receiving traffic.",
                      "Use auto scaling if traffic changes significantly over time.",
                      "Use centralized logging and tracing to debug requests across instances."
                    ]
                  }
                ]
              },
              {
                "id": "database-scaling",
                "title": "13. Scaling Databases",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Vertical Database Scaling",
                    "pairs": [
                      { "k": "How", "v": "Add more CPU, RAM, faster disks, more IOPS, or better network capacity." },
                      { "k": "Good for", "v": "Improving query performance without redesigning data access." },
                      { "k": "Limit", "v": "A single primary database eventually becomes a bottleneck." }
                    ]
                  },
                  {
                    "groupTitle": "Read Replicas",
                    "pairs": [
                      { "k": "How", "v": "Replicate data from a primary database to read-only copies." },
                      { "k": "Good for", "v": "Scaling read-heavy workloads." },
                      { "k": "Trade-off", "v": "Replication lag can cause stale reads." }
                    ]
                  },
                  {
                    "groupTitle": "Partitioning",
                    "pairs": [
                      { "k": "How", "v": "Split a large table into smaller logical or physical parts." },
                      { "k": "Good for", "v": "Improving manageability and query performance for large datasets." }
                    ]
                  },
                  {
                    "groupTitle": "Sharding",
                    "pairs": [
                      { "k": "How", "v": "Split data across multiple database servers using a shard key." },
                      { "k": "Good for", "v": "Scaling write and storage capacity beyond one database node." },
                      { "k": "Trade-off", "v": "Cross-shard queries, transactions, rebalancing, and hot shards become hard problems." }
                    ]
                  }
                ]
              },
              {
                "id": "load-balancing",
                "title": "14. Load Balancing in Horizontal Scaling",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Purpose",
                    "pairs": [
                      { "k": "Traffic distribution", "v": "Spread incoming requests across multiple healthy servers." },
                      { "k": "High availability", "v": "Stop sending traffic to unhealthy instances." },
                      { "k": "Scalability", "v": "Allow capacity to increase by adding more backend instances." }
                    ]
                  },
                  {
                    "groupTitle": "Common Algorithms",
                    "pairs": [
                      { "k": "Round robin", "v": "Send requests to servers in sequence." },
                      { "k": "Least connections", "v": "Send traffic to the server with fewer active connections." },
                      { "k": "Weighted routing", "v": "Send more traffic to stronger or preferred servers." },
                      { "k": "Consistent hashing", "v": "Route related keys to stable backend nodes, useful for caches and stateful routing." }
                    ]
                  },
                  {
                    "groupTitle": "Important Requirement",
                    "pairs": [
                      { "k": "Health checks", "v": "A load balancer must know whether a backend is healthy before routing traffic to it." }
                    ]
                  }
                ]
              },
              {
                "id": "state-management",
                "title": "15. State Management While Scaling Horizontally",
                "type": "bullets",
                "items": [
                  "Stateless servers are easier to horizontally scale because any server can handle any request.",
                  "Avoid storing critical user session data only in local memory.",
                  "Use signed tokens when the session state can safely live on the client side.",
                  "Use Redis, a database, or a distributed session store for server-side session state.",
                  "Sticky sessions can send a user back to the same server, but they reduce flexibility and complicate failover.",
                  "For WebSocket systems, connection state is unavoidable, so use connection servers, pub-sub, distributed presence tracking, and reconnection logic.",
                  "For background jobs, store job state in a durable queue or database so workers can fail and recover safely."
                ]
              },
              {
                "id": "caching",
                "title": "16. Caching as a Scaling Strategy",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Why Caching Helps",
                    "pairs": [
                      { "k": "Latency", "v": "Cached responses are usually faster to serve." },
                      { "k": "Database load", "v": "Repeated reads can be served from cache instead of the database." },
                      { "k": "Cost", "v": "Serving cached data can be cheaper than recomputing or repeatedly querying storage." }
                    ]
                  },
                  {
                    "groupTitle": "Cache Locations",
                    "pairs": [
                      { "k": "Browser cache", "v": "Stores static or cacheable responses on the client." },
                      { "k": "CDN", "v": "Caches static and sometimes dynamic content near users." },
                      { "k": "Application cache", "v": "Stores frequently used data in memory." },
                      { "k": "Distributed cache", "v": "Uses systems like Redis or Memcached across multiple servers." }
                    ]
                  },
                  {
                    "groupTitle": "Caching Risks",
                    "pairs": [
                      { "k": "Stale data", "v": "Users may see old information." },
                      { "k": "Invalidation", "v": "Correctly removing or updating cached data is hard." },
                      { "k": "Cache stampede", "v": "Many requests hit the database at the same time when a hot cache key expires." }
                    ]
                  }
                ]
              },
              {
                "id": "queues-workers",
                "title": "17. Queues and Worker Scaling",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "How It Works",
                    "steps": [
                      "The application accepts a request.",
                      "Slow or non-critical work is placed into a message queue.",
                      "Multiple worker instances consume jobs from the queue.",
                      "The number of workers can be scaled horizontally based on queue length and processing time."
                    ]
                  },
                  {
                    "blockTitle": "Good Use Cases",
                    "steps": [
                      "Email sending.",
                      "Video processing.",
                      "Image resizing.",
                      "Notification delivery.",
                      "Report generation.",
                      "Payment reconciliation.",
                      "Data import and export."
                    ]
                  },
                  {
                    "blockTitle": "Important Design Rules",
                    "steps": [
                      "Jobs should be idempotent where possible.",
                      "Failed jobs should be retried with backoff.",
                      "Poison messages should move to a dead-letter queue.",
                      "Worker progress should be observable through logs, metrics, and tracing."
                    ]
                  }
                ]
              },
              {
                "id": "autoscaling",
                "title": "18. Auto Scaling",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Definition",
                    "pairs": [
                      { "k": "Meaning", "v": "Automatically add or remove capacity based on demand." },
                      { "k": "Scale out", "v": "Add more instances." },
                      { "k": "Scale in", "v": "Remove unnecessary instances." }
                    ]
                  },
                  {
                    "groupTitle": "Common Metrics",
                    "pairs": [
                      { "k": "CPU usage", "v": "Useful when CPU is the main bottleneck." },
                      { "k": "Memory usage", "v": "Useful for memory-heavy workloads." },
                      { "k": "Request rate", "v": "Scale based on traffic volume." },
                      { "k": "Latency", "v": "Scale when user-facing performance degrades." },
                      { "k": "Queue length", "v": "Useful for worker fleets." }
                    ]
                  },
                  {
                    "groupTitle": "Risks",
                    "pairs": [
                      { "k": "Slow reaction", "v": "Scaling may take time, so sudden spikes can still cause issues." },
                      { "k": "Wrong metric", "v": "Scaling on the wrong signal can waste money or fail to fix the bottleneck." },
                      { "k": "Flapping", "v": "Instances repeatedly scale up and down if thresholds are poorly configured." }
                    ]
                  }
                ]
              },
              {
                "id": "bottlenecks",
                "title": "19. How to Identify Scaling Bottlenecks",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Common Bottlenecks",
                    "pairs": [
                      { "k": "CPU-bound", "v": "High CPU usage, slow computation, heavy serialization, encryption, or compression." },
                      { "k": "Memory-bound", "v": "High memory usage, garbage collection pressure, memory leaks, or large working sets." },
                      { "k": "Database-bound", "v": "Slow queries, lock contention, missing indexes, too many connections, or high IOPS." },
                      { "k": "Network-bound", "v": "High bandwidth usage, packet loss, connection exhaustion, or cross-region latency." },
                      { "k": "Disk-bound", "v": "Slow reads/writes, low IOPS, high disk queue depth." },
                      { "k": "Dependency-bound", "v": "A downstream service is slow, unreliable, or rate limited." }
                    ]
                  },
                  {
                    "groupTitle": "Signals to Measure",
                    "pairs": [
                      { "k": "Latency", "v": "p50, p95, p99 response times." },
                      { "k": "Throughput", "v": "Requests per second or jobs per second." },
                      { "k": "Errors", "v": "Timeouts, 5xx responses, failed jobs, dropped messages." },
                      { "k": "Saturation", "v": "CPU, memory, disk, network, DB connections, thread pools." },
                      { "k": "Queueing", "v": "Queue length and wait time." }
                    ]
                  }
                ]
              },
              {
                "id": "scaling-patterns",
                "title": "20. Common Scaling Patterns",
                "type": "bullets",
                "items": [
                  "Stateless app servers behind a load balancer.",
                  "Read replicas for read-heavy databases.",
                  "Database sharding for massive write or storage scale.",
                  "Distributed cache for hot data.",
                  "CDN for static and cacheable content.",
                  "Message queues for asynchronous workloads.",
                  "Worker pools for parallel processing.",
                  "Rate limiting to protect systems from abusive or accidental overload.",
                  "Backpressure to slow producers when consumers cannot keep up.",
                  "Circuit breakers to avoid repeatedly calling a failing dependency.",
                  "Bulkheads to isolate failures between parts of the system.",
                  "Multi-region deployment for latency reduction and disaster recovery."
                ]
              },
              {
                "id": "cost-tradeoffs",
                "title": "21. Cost Trade-Offs",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Vertical Scaling Cost",
                    "pairs": [
                      { "k": "Early stage", "v": "Often cheaper because it avoids architectural complexity." },
                      { "k": "High end", "v": "Large machines become expensive and limited." },
                      { "k": "Operational cost", "v": "Lower engineering and operational complexity." }
                    ]
                  },
                  {
                    "groupTitle": "Horizontal Scaling Cost",
                    "pairs": [
                      { "k": "Infrastructure", "v": "Many smaller instances may be cheaper and more flexible." },
                      { "k": "Operations", "v": "Requires automation, monitoring, incident response, distributed debugging, and reliability engineering." },
                      { "k": "Waste risk", "v": "Poor auto scaling or overprovisioning can waste money." }
                    ]
                  }
                ]
              },
              {
                "id": "common-failures",
                "title": "22. Common Scaling Failures",
                "type": "bullets",
                "items": [
                  "Scaling the application layer while the database remains the real bottleneck.",
                  "Adding more servers without a proper load balancer.",
                  "Keeping session state in local memory and then wondering why users randomly get logged out.",
                  "Using sticky sessions as a permanent shortcut instead of designing proper state management.",
                  "Adding cache without a clear invalidation strategy.",
                  "Adding replicas but forgetting replication lag.",
                  "Sharding too early and creating unnecessary complexity.",
                  "Ignoring p95 and p99 latency while only looking at average latency.",
                  "Retrying aggressively and making an overloaded system even worse.",
                  "Scaling on CPU when the actual bottleneck is database connections, locks, or network calls."
                ]
              },
              {
                "id": "practical-growth-path",
                "title": "23. Practical Growth Path",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Stage 1: Single Server",
                    "steps": [
                      "Use one application server and one database.",
                      "Keep the architecture simple.",
                      "Add monitoring from the beginning."
                    ]
                  },
                  {
                    "blockTitle": "Stage 2: Scale Up",
                    "steps": [
                      "Use a larger server when resource limits are reached.",
                      "Add indexes and optimize slow queries.",
                      "Add basic caching where it clearly helps."
                    ]
                  },
                  {
                    "blockTitle": "Stage 3: Scale Out Application Layer",
                    "steps": [
                      "Make application servers stateless.",
                      "Add a load balancer.",
                      "Run multiple application instances.",
                      "Centralize logs, metrics, and traces."
                    ]
                  },
                  {
                    "blockTitle": "Stage 4: Scale Data and Background Work",
                    "steps": [
                      "Add read replicas for read-heavy workloads.",
                      "Move slow work to queues and worker pools.",
                      "Add distributed caching.",
                      "Partition or shard only when required."
                    ]
                  },
                  {
                    "blockTitle": "Stage 5: High Availability and Global Scale",
                    "steps": [
                      "Use multiple availability zones.",
                      "Add disaster recovery plans.",
                      "Deploy across regions if latency or resilience requires it.",
                      "Use CDN, traffic management, and regional failover."
                    ]
                  }
                ]
              },
              {
                "id": "interview-qa",
                "title": "24. Common Interview Questions",
                "type": "qa",
                "items": [
                  { "q": "What is vertical scaling?", "a": "Vertical scaling means increasing the capacity of a single machine by adding more CPU, memory, storage, IOPS, or network bandwidth." },
                  { "q": "What is horizontal scaling?", "a": "Horizontal scaling means adding more machines or instances and distributing traffic, data, or jobs across them." },
                  { "q": "Which is better: vertical or horizontal scaling?", "a": "Neither is universally better. Vertical scaling is simpler and useful early, but has hard limits. Horizontal scaling supports larger scale and availability, but adds distributed-system complexity." },
                  { "q": "Why are stateless servers easier to scale horizontally?", "a": "Because any server can handle any request. The load balancer does not need to route a user to the same server, and failed instances can be replaced more easily." },
                  { "q": "What problem does a load balancer solve?", "a": "It distributes incoming traffic across multiple healthy backend servers and stops routing traffic to unhealthy instances." },
                  { "q": "How do you scale a read-heavy database?", "a": "Use query optimization, indexes, caching, and read replicas. Read replicas distribute read traffic, but replication lag can cause stale reads." },
                  { "q": "How do you scale a write-heavy database?", "a": "First optimize schema, indexes, and queries. Then consider batching, queues, partitioning, and sharding. Sharding increases write capacity but complicates transactions and queries." },
                  { "q": "What is the biggest risk of horizontal scaling?", "a": "Distributed-system complexity: state management, consistency, network failures, partial failures, retries, monitoring, and debugging all become harder." },
                  { "q": "When should you use queues for scaling?", "a": "Use queues when work is slow, bursty, retryable, or does not need to complete inside the user-facing request path." },
                  { "q": "What is auto scaling?", "a": "Auto scaling automatically adds or removes capacity based on demand, such as CPU, memory, request rate, latency, or queue length." },
                  { "q": "Why can retries make scaling problems worse?", "a": "Aggressive retries increase traffic during failure or overload, creating retry storms. Use timeouts, exponential backoff, jitter, and idempotency." },
                  { "q": "What should you check before scaling?", "a": "Measure the real bottleneck. Check CPU, memory, disk, network, database queries, locks, connection pools, latency percentiles, errors, and queue lengths." }
                ]
              },
              {
                "id": "final-summary",
                "title": "25. Final Revision Summary",
                "type": "bullets",
                "items": [
                  "Scaling increases a system's capacity to handle more traffic, data, or work.",
                  "Vertical scaling means making one machine stronger.",
                  "Horizontal scaling means adding more machines and distributing load.",
                  "Vertical scaling is simpler but limited.",
                  "Horizontal scaling is more powerful but more complex.",
                  "Stateless application servers are easy to scale horizontally.",
                  "Databases are harder to scale horizontally because of consistency, transactions, and data distribution.",
                  "Load balancers are central to horizontal scaling.",
                  "Caching reduces repeated expensive work.",
                  "Queues help move slow work out of the request path.",
                  "Auto scaling helps match capacity to demand, but only when the right metric is used.",
                  "Measure bottlenecks before scaling. Blind scaling is expensive and often useless."
                ]
              }
            ],
            "callouts": [
              { "type": "warning", "text": "Do not scale blindly. First identify the real bottleneck. Adding more app servers will not fix a slow database." },
              { "type": "interview-favorite", "text": "Interviewers often ask vertical vs horizontal scaling. Answer with simplicity, limits, availability, cost, and distributed-system complexity." },
              { "type": "tip", "text": "For application servers, the clean path is: make them stateless → put them behind a load balancer → add health checks → auto scale." },
              { "type": "warning", "text": "Sticky sessions are usually a shortcut, not a clean long-term scaling strategy. Prefer stateless servers or shared session storage." },
              { "type": "interview-favorite", "text": "Must mention that scaling the wrong layer is useless. Always talk about bottleneck measurement: CPU, memory, database, network, disk, latency, and queue length." },
              { "type": "tip", "text": "Use queues when work is slow, bursty, retryable, or not required immediately in the user-facing request." }
            ],
            "tags": [
              "system-design",
              "scaling",
              "vertical-scaling",
              "horizontal-scaling",
              "load-balancing",
              "stateless-server",
              "database-scaling",
              "caching",
              "queues",
              "autoscaling",
              "bottlenecks",
              "high-availability",
              "system-design-fundamentals"
            ]
          }
        },
        {
          "id": "07",
          "title": "System Design Fundamentals 7: ✅ Load Balancer Explained | Round Robin, Least Connections, IP Hash",
          "videoUrl": "https://youtu.be/pd6C69lYHmc",
          "description": "In this video, we will understand what a Load Balancer is, why it is used in system design, and how it distributes traffic across multiple backend servers. A Load Balancer is one of the most important components in scalable and highly available systems. It helps distribute incoming user requests across multiple servers so that no single server becomes overloaded. It also helps improve performance, fault tolerance, and availability.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/wrgreg3rgewheh45252354364.png?updatedAt=1783609480559",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/rgwrgq34gq34tq34g3qv3qwvdvawvwddwvwv.png?updatedAt=1783609596405",
          "toolId": "load-balancer",
          "refresher": {
            "label": "System Design Fundamentals Refresher",
            "estimatedMinutes": 22,
            "lastUpdated": "2026-07-10",
            "hero": {
              "title": "Load Balancer",
              "oneLinerToMemorize": "A load balancer distributes incoming traffic across multiple healthy servers to improve scalability, availability, and performance."
            },
            "sections": [
              {
                "id": "what-is-load-balancer",
                "title": "1. What Is a Load Balancer?",
                "type": "bullets",
                "items": [
                  "A load balancer sits between clients and backend servers and distributes incoming traffic across healthy servers.",
                  "It prevents one server from being overloaded while other servers remain idle.",
                  "It enables horizontal scaling because clients use one entry point while backend capacity can grow behind it.",
                  "It improves availability by removing unhealthy servers from rotation.",
                  "Depending on the type, it can also handle TLS termination, routing rules, sticky sessions, rate limiting, logging, and traffic shaping."
                ]
              },
              {
                "id": "why-needed",
                "title": "2. Why Do We Need a Load Balancer?",
                "type": "bullets",
                "items": [
                  "Traffic distribution across multiple servers.",
                  "Horizontal scaling without exposing every backend to clients.",
                  "High availability by routing around failed servers.",
                  "Better performance by reducing hot spots.",
                  "Zero-downtime deployment by draining and replacing instances.",
                  "Stable client-facing endpoint even when backend servers change.",
                  "Controlled traffic shifting for canary, blue-green, migrations, and failover."
                ]
              },
              {
                "id": "basic-flow",
                "title": "3. Basic Request Flow",
                "type": "steps",
                "blocks": [
                  {
                    "blockTitle": "Flow",
                    "steps": [
                      "Client sends request to api.example.com.",
                      "DNS resolves the domain to the load balancer endpoint.",
                      "Client connects to the load balancer.",
                      "The load balancer checks healthy backend servers.",
                      "It selects a backend using an algorithm.",
                      "The backend processes the request.",
                      "The response returns to the client, usually through the load balancer."
                    ]
                  }
                ]
              },
              {
                "id": "types",
                "title": "4. Types of Load Balancers",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Layer 4 Load Balancer",
                    "pairs": [
                      {
                        "k": "OSI layer",
                        "v": "Transport layer."
                      },
                      {
                        "k": "Routing data",
                        "v": "Source/destination IP, port, protocol, and connection information."
                      },
                      {
                        "k": "Works with",
                        "v": "TCP, UDP, TLS passthrough, generic transport traffic."
                      },
                      {
                        "k": "Pros",
                        "v": "Fast, low overhead, protocol-agnostic."
                      },
                      {
                        "k": "Cons",
                        "v": "Cannot route by HTTP path, headers, cookies, or application content."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 7 Load Balancer",
                    "pairs": [
                      {
                        "k": "OSI layer",
                        "v": "Application layer."
                      },
                      {
                        "k": "Routing data",
                        "v": "Host, URL path, HTTP method, headers, cookies, query params, and application metadata."
                      },
                      {
                        "k": "Works with",
                        "v": "HTTP, HTTPS, WebSocket, and gRPC when supported."
                      },
                      {
                        "k": "Pros",
                        "v": "Application-aware routing, TLS termination, WAF, rate limiting, better observability."
                      },
                      {
                        "k": "Cons",
                        "v": "More CPU and processing overhead."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Global Load Balancer",
                    "pairs": [
                      {
                        "k": "Scope",
                        "v": "Routes traffic across regions, data centers, or edge locations."
                      },
                      {
                        "k": "Routing data",
                        "v": "Geography, latency, health, capacity, weights, or policy."
                      },
                      {
                        "k": "Use case",
                        "v": "Multi-region apps, disaster recovery, CDN/edge routing."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "l4-vs-l7",
                "title": "5. Layer 4 vs Layer 7",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Layer 4",
                    "pairs": [
                      {
                        "k": "Decision level",
                        "v": "Connection/transport level."
                      },
                      {
                        "k": "Can inspect",
                        "v": "IP, port, protocol, TCP/UDP connection state."
                      },
                      {
                        "k": "Best for",
                        "v": "High-throughput systems where application inspection is unnecessary."
                      },
                      {
                        "k": "Example",
                        "v": "Forward TCP connections on port 443 to backend servers."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Layer 7",
                    "pairs": [
                      {
                        "k": "Decision level",
                        "v": "Application/request level."
                      },
                      {
                        "k": "Can inspect",
                        "v": "HTTP host, path, headers, method, cookies, and body depending on configuration."
                      },
                      {
                        "k": "Best for",
                        "v": "Microservices, API routing, canary release, authentication, rate limiting, and content-based routing."
                      },
                      {
                        "k": "Example",
                        "v": "Route /payments to payment service and /orders to order service."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "core-components",
                "title": "6. Core Components",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Frontend Listener",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "The public IP/port/protocol where the load balancer accepts traffic."
                      },
                      {
                        "k": "Example",
                        "v": "HTTPS listener on port 443."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Backend Pool",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "A group of servers that can receive traffic."
                      },
                      {
                        "k": "Example",
                        "v": "app-1, app-2, app-3."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Health Check",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Periodic check to decide whether a backend should receive traffic."
                      },
                      {
                        "k": "Example",
                        "v": "GET /health returns 200 OK."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Routing Rule",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Rule that decides where traffic should go."
                      },
                      {
                        "k": "Example",
                        "v": "Host-based or path-based routing."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Algorithm",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Strategy used to pick one backend from the healthy pool."
                      },
                      {
                        "k": "Example",
                        "v": "Round robin, least connections, weighted routing, consistent hashing."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "algorithms-overview",
                "title": "7. Load Balancing Algorithms Overview",
                "type": "bullets",
                "items": [
                  "Round Robin: fixed rotation across servers.",
                  "Weighted Round Robin: more traffic to servers with higher configured weight.",
                  "Least Connections: pick server with fewest active connections.",
                  "Weighted Least Connections: connection count adjusted by server capacity.",
                  "Least Response Time: prefer fastest observed backend, often with connection count.",
                  "Random: pick a backend randomly.",
                  "Power of Two Choices: sample two random servers and choose the better one.",
                  "IP Hash: route same client IP to same backend.",
                  "URL Hash: route same URL/path/key to same backend.",
                  "Consistent Hashing: map keys and servers on a ring to reduce remapping.",
                  "Weighted Routing: route by configured percentages.",
                  "Priority/Failover: use backup only when primary is unhealthy.",
                  "Geo/Latency Routing: route users to nearby or lower-latency regions."
                ]
              },
              {
                "id": "round-robin",
                "title": "8. Round Robin",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Requests go to backends one by one in circular order."
                      },
                      {
                        "k": "Pros",
                        "v": "Simple, predictable, good when servers and request costs are similar."
                      },
                      {
                        "k": "Cons",
                        "v": "Not load-aware; heavy requests, long-lived connections, or slow servers cause imbalance."
                      },
                      {
                        "k": "Use when",
                        "v": "Homogeneous servers and roughly uniform short requests."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "weighted-round-robin",
                "title": "9. Weighted Round Robin",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Servers receive traffic according to configured weights."
                      },
                      {
                        "k": "Pros",
                        "v": "Handles servers with different capacities; useful for gradual migrations."
                      },
                      {
                        "k": "Cons",
                        "v": "Weights are static; no live awareness of CPU, memory, active connections, or latency."
                      },
                      {
                        "k": "Use when",
                        "v": "Servers have known capacity differences and traffic is predictable."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "least-connections",
                "title": "10. Least Connections",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Choose the backend with the fewest active connections."
                      },
                      {
                        "k": "Pros",
                        "v": "Better for long-lived connections and variable request durations."
                      },
                      {
                        "k": "Cons",
                        "v": "Connection count is not true load; one connection can be heavier than many light ones."
                      },
                      {
                        "k": "Use when",
                        "v": "Requests/connections have uneven duration, such as WebSocket or long polling."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "weighted-least-connections",
                "title": "11. Weighted Least Connections",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Uses active connections plus server weight/capacity."
                      },
                      {
                        "k": "Pros",
                        "v": "Good for mixed-size server fleets."
                      },
                      {
                        "k": "Cons",
                        "v": "Still needs correct weights and does not fully know CPU, memory, DB pressure, or downstream bottlenecks."
                      },
                      {
                        "k": "Use when",
                        "v": "Server capacities differ and connection duration varies."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "least-response-time",
                "title": "12. Least Response Time",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Prefers backends with lower observed latency, sometimes combined with active connections."
                      },
                      {
                        "k": "Pros",
                        "v": "Performance-aware and can avoid slow servers."
                      },
                      {
                        "k": "Cons",
                        "v": "Latency measurements can be noisy; can create feedback loops by overloading the fastest server."
                      },
                      {
                        "k": "Use when",
                        "v": "Backend latency is a strong signal of actual load."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "random",
                "title": "13. Random",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Randomly selects a healthy backend."
                      },
                      {
                        "k": "Pros",
                        "v": "Very simple, low overhead, acceptable at high traffic volume with similar servers."
                      },
                      {
                        "k": "Cons",
                        "v": "Short-term distribution can be uneven; no load awareness."
                      },
                      {
                        "k": "Use when",
                        "v": "Simplicity matters and imbalance risk is acceptable."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "power-two",
                "title": "14. Power of Two Choices",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Pick two random healthy servers and choose the better one using load/connection/latency metric."
                      },
                      {
                        "k": "Pros",
                        "v": "Much better than pure random with low overhead; good for large fleets."
                      },
                      {
                        "k": "Cons",
                        "v": "Approximate; still needs a metric for the sampled servers."
                      },
                      {
                        "k": "Use when",
                        "v": "Large backend pools need efficient balancing."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "ip-hash",
                "title": "15. IP Hash",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Hash client IP and map it to a backend."
                      },
                      {
                        "k": "Pros",
                        "v": "Simple session affinity without cookies."
                      },
                      {
                        "k": "Cons",
                        "v": "NAT can overload one backend; mobile IP changes break affinity; adding/removing servers can remap clients."
                      },
                      {
                        "k": "Use when",
                        "v": "Simple client affinity is needed and NAT risk is acceptable."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "url-hash",
                "title": "16. URL Hash",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Hash URL/path/key and route matching values to the same backend."
                      },
                      {
                        "k": "Pros",
                        "v": "Good for cache locality and deterministic resource routing."
                      },
                      {
                        "k": "Cons",
                        "v": "Hot URLs/keys can overload one server; remapping occurs if backend set changes."
                      },
                      {
                        "k": "Use when",
                        "v": "Same resource should repeatedly hit the same cache/backend."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "consistent-hashing",
                "title": "17. Consistent Hashing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Place keys and servers on a hash ring; a key maps to the next server on the ring."
                      },
                      {
                        "k": "Pros",
                        "v": "Adding/removing servers remaps only a fraction of keys; strong cache locality."
                      },
                      {
                        "k": "Cons",
                        "v": "More complex; hot keys still hurt; virtual nodes are usually needed for balance."
                      },
                      {
                        "k": "Use when",
                        "v": "Distributed caches, storage, or key-based routing need low remapping."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "weighted-routing",
                "title": "18. Weighted Routing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Split traffic by configured percentages or weights."
                      },
                      {
                        "k": "Pros",
                        "v": "Great for canary releases, blue-green deployments, migrations, and capacity control."
                      },
                      {
                        "k": "Cons",
                        "v": "Manual percentages are not automatically load-aware."
                      },
                      {
                        "k": "Use when",
                        "v": "You need controlled traffic shifting."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "priority-failover",
                "title": "19. Priority / Failover Routing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Use primary backends first; use secondary backends only when primary fails."
                      },
                      {
                        "k": "Pros",
                        "v": "Good for active-passive disaster recovery."
                      },
                      {
                        "k": "Cons",
                        "v": "Backup may be cold; failback and data consistency can be hard."
                      },
                      {
                        "k": "Use when",
                        "v": "You need backup infrastructure for outages."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "geo-latency",
                "title": "20. Geo / Latency-Based Routing",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Details",
                    "pairs": [
                      {
                        "k": "How it works",
                        "v": "Route users based on location or expected latency."
                      },
                      {
                        "k": "Pros",
                        "v": "Lower latency and regional resilience."
                      },
                      {
                        "k": "Cons",
                        "v": "DNS caching and data locality can complicate routing."
                      },
                      {
                        "k": "Use when",
                        "v": "Multi-region systems serve geographically distributed users."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "health-checks",
                "title": "21. Health Checks",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Purpose",
                    "pairs": [
                      {
                        "k": "Detect failure",
                        "v": "Stop routing traffic to unhealthy instances."
                      },
                      {
                        "k": "Detect recovery",
                        "v": "Add an instance back after it becomes healthy."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Types",
                    "pairs": [
                      {
                        "k": "TCP check",
                        "v": "Can a TCP connection be opened?"
                      },
                      {
                        "k": "HTTP check",
                        "v": "Does /health return a healthy status?"
                      },
                      {
                        "k": "Deep check",
                        "v": "Does the app plus critical dependencies work?"
                      }
                    ]
                  },
                  {
                    "groupTitle": "Warning",
                    "pairs": [
                      {
                        "k": "Bad health checks",
                        "v": "A shallow check may miss broken dependencies; an overly deep check may remove all servers during a shared database outage."
                      },
                      {
                        "k": "Readiness vs liveness",
                        "v": "Readiness decides traffic eligibility; liveness decides restart eligibility."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "sticky-sessions",
                "title": "22. Sticky Sessions / Session Affinity",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Meaning",
                    "pairs": [
                      {
                        "k": "Definition",
                        "v": "Send the same client to the same backend across multiple requests."
                      },
                      {
                        "k": "Methods",
                        "v": "Cookie-based affinity, IP hash, or custom key."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Pros",
                    "pairs": [
                      {
                        "k": "Local state",
                        "v": "Can support legacy apps storing session in memory."
                      },
                      {
                        "k": "Cache locality",
                        "v": "Repeated user requests may hit warm local cache."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Cons",
                    "pairs": [
                      {
                        "k": "Failover problem",
                        "v": "If the backend dies, local session state may be lost."
                      },
                      {
                        "k": "Uneven load",
                        "v": "Sticky users can create imbalance."
                      },
                      {
                        "k": "Design warning",
                        "v": "For scalable web APIs, stateless servers or shared session storage are cleaner."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "tls",
                "title": "23. TLS Termination vs TLS Passthrough",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "TLS Termination",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Load balancer decrypts HTTPS and forwards plain or re-encrypted traffic to backends."
                      },
                      {
                        "k": "Pros",
                        "v": "Central certificate management, Layer 7 routing, WAF, logging, path/header inspection."
                      },
                      {
                        "k": "Cons",
                        "v": "Requires trust or re-encryption inside the network."
                      }
                    ]
                  },
                  {
                    "groupTitle": "TLS Passthrough",
                    "pairs": [
                      {
                        "k": "Meaning",
                        "v": "Load balancer forwards encrypted traffic without decrypting it."
                      },
                      {
                        "k": "Pros",
                        "v": "End-to-end encryption to backend."
                      },
                      {
                        "k": "Cons",
                        "v": "No HTTP path/header inspection, so Layer 7 routing is limited."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "reverse-proxy-api-gateway",
                "title": "24. Load Balancer vs Reverse Proxy vs API Gateway",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Load Balancer",
                    "pairs": [
                      {
                        "k": "Primary job",
                        "v": "Distribute traffic across backend servers."
                      },
                      {
                        "k": "Focus",
                        "v": "Scalability, availability, health checks, routing."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reverse Proxy",
                    "pairs": [
                      {
                        "k": "Primary job",
                        "v": "Accept client requests and forward them while hiding backend details."
                      },
                      {
                        "k": "Focus",
                        "v": "Proxying, TLS termination, caching, compression, routing, rewriting."
                      }
                    ]
                  },
                  {
                    "groupTitle": "API Gateway",
                    "pairs": [
                      {
                        "k": "Primary job",
                        "v": "Manage API traffic and cross-cutting API concerns."
                      },
                      {
                        "k": "Focus",
                        "v": "Auth, authorization, rate limits, quotas, validation, transformation, versioning, analytics."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Reality",
                    "pairs": [
                      {
                        "k": "Overlap",
                        "v": "Many real products do all three; the difference is the primary responsibility."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "dns-vs-lb",
                "title": "25. DNS Load Balancing vs Dedicated Load Balancer",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "DNS Load Balancing",
                    "pairs": [
                      {
                        "k": "How",
                        "v": "DNS returns one or more IP addresses."
                      },
                      {
                        "k": "Pros",
                        "v": "Simple and useful for global/regional traffic steering."
                      },
                      {
                        "k": "Cons",
                        "v": "DNS caching reduces control; no true per-request routing."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Dedicated Load Balancer",
                    "pairs": [
                      {
                        "k": "How",
                        "v": "Client connects to LB; LB routes each connection/request to a backend."
                      },
                      {
                        "k": "Pros",
                        "v": "Health checks, per-request routing, TLS termination, sticky sessions, observability."
                      },
                      {
                        "k": "Cons",
                        "v": "The LB layer itself must be highly available and scaled."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "single-point-failure",
                "title": "26. Is the Load Balancer a Single Point of Failure?",
                "type": "bullets",
                "items": [
                  "A single load balancer instance is a single point of failure.",
                  "Production systems run multiple load balancer instances.",
                  "Common setups include active-active, active-passive, cloud-managed load balancers, virtual IPs, Anycast, or DNS failover.",
                  "Backend health is not enough; load balancer infrastructure must also be monitored.",
                  "Cloud load balancers are usually managed across multiple availability zones behind one stable endpoint."
                ]
              },
              {
                "id": "scaling-lb",
                "title": "27. How Load Balancers Scale",
                "type": "bullets",
                "items": [
                  "Scale vertically by adding CPU, memory, bandwidth, and connection capacity.",
                  "Scale horizontally by running multiple load balancer instances.",
                  "Use Anycast or DNS to distribute clients across load balancer nodes or regions.",
                  "Use cloud-managed load balancing when possible to reduce operational burden.",
                  "Use connection reuse and efficient routing algorithms to reduce overhead.",
                  "Track request rate, active connections, latency, bandwidth, error rate, and backend health."
                ]
              },
              {
                "id": "websocket-grpc",
                "title": "28. Load Balancing WebSocket and gRPC",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "WebSocket",
                    "pairs": [
                      {
                        "k": "Challenge",
                        "v": "Connections are long-lived, so request count is not enough."
                      },
                      {
                        "k": "Approach",
                        "v": "Use least connections, multiple connection servers, pub-sub/message broker, distributed presence, and reconnection logic."
                      }
                    ]
                  },
                  {
                    "groupTitle": "gRPC",
                    "pairs": [
                      {
                        "k": "Challenge",
                        "v": "HTTP/2 can multiplex many streams over few long-lived connections, causing uneven backend usage."
                      },
                      {
                        "k": "Approach",
                        "v": "Use gRPC-aware proxies, service mesh, client-side load balancing, or connection management."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "deployment-patterns",
                "title": "29. Deployment Patterns",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Rolling Deployment",
                    "pairs": [
                      {
                        "k": "How",
                        "v": "Drain some servers, update them, health-check them, then re-add them."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Blue-Green",
                    "pairs": [
                      {
                        "k": "How",
                        "v": "Run old and new environments side by side and switch traffic."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Canary",
                    "pairs": [
                      {
                        "k": "How",
                        "v": "Send a small percentage to the new version and increase if metrics are healthy."
                      }
                    ]
                  },
                  {
                    "groupTitle": "A/B Testing",
                    "pairs": [
                      {
                        "k": "How",
                        "v": "Route different user groups to different versions for comparison."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "failure-handling",
                "title": "30. Failure Handling",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Backend failure",
                    "pairs": [
                      {
                        "k": "Detect",
                        "v": "Health checks, latency, error rate, failed connections."
                      },
                      {
                        "k": "Action",
                        "v": "Remove backend from rotation."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Slow backend",
                    "pairs": [
                      {
                        "k": "Problem",
                        "v": "Server is alive but too slow."
                      },
                      {
                        "k": "Action",
                        "v": "Latency-aware routing, outlier detection, timeouts, deadlines."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Overload",
                    "pairs": [
                      {
                        "k": "Signals",
                        "v": "High latency, high errors, connection exhaustion, queue growth."
                      },
                      {
                        "k": "Action",
                        "v": "Rate limiting, autoscaling, backpressure, circuit breakers, caching, traffic shedding."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Load balancer failure",
                    "pairs": [
                      {
                        "k": "Action",
                        "v": "Fail over to another LB instance or managed endpoint."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "security",
                "title": "31. Security Features",
                "type": "bullets",
                "items": [
                  "TLS termination and certificate management.",
                  "TLS passthrough for end-to-end encryption.",
                  "Web Application Firewall integration.",
                  "Rate limiting and request-size limits.",
                  "IP allowlists and blocklists.",
                  "DDoS protection.",
                  "Header validation and normalization.",
                  "Authentication integration in Layer 7 gateways.",
                  "Access logs for auditing and investigation."
                ]
              },
              {
                "id": "observability",
                "title": "32. Observability and Metrics",
                "type": "keyValueGroups",
                "groups": [
                  {
                    "groupTitle": "Metrics",
                    "pairs": [
                      {
                        "k": "Request rate",
                        "v": "Requests per second."
                      },
                      {
                        "k": "Active connections",
                        "v": "Open client and backend connections."
                      },
                      {
                        "k": "Latency",
                        "v": "Total latency and backend latency."
                      },
                      {
                        "k": "Errors",
                        "v": "4xx, 5xx, timeouts, connection failures."
                      },
                      {
                        "k": "Backend health",
                        "v": "Healthy/unhealthy backend count."
                      },
                      {
                        "k": "Bandwidth",
                        "v": "Inbound and outbound traffic."
                      }
                    ]
                  },
                  {
                    "groupTitle": "Debugging",
                    "pairs": [
                      {
                        "k": "Access logs",
                        "v": "Show path, status, latency, backend, user agent."
                      },
                      {
                        "k": "Trace IDs",
                        "v": "Connect LB logs with application logs."
                      },
                      {
                        "k": "Dashboards",
                        "v": "Show traffic, latency, errors, saturation, and backend health."
                      }
                    ]
                  }
                ]
              },
              {
                "id": "misconceptions",
                "title": "33. Common Misconceptions",
                "type": "bullets",
                "items": [
                  "A load balancer does not fix a database bottleneck.",
                  "Round robin balances request count, not actual CPU or memory usage.",
                  "A server can pass a health check but still be too slow for real users.",
                  "DNS load balancing is not the same as per-request load balancing.",
                  "Sticky sessions are not a replacement for stateless design.",
                  "Layer 7 is not always better than Layer 4; it is more powerful but heavier.",
                  "The load balancer itself must be scaled and made highly available.",
                  "Bad health checks can either hide failures or remove too many healthy servers."
                ]
              },
              {
                "id": "which-algorithm",
                "title": "34. Which Algorithm Should You Use?",
                "type": "bullets",
                "items": [
                  "Use Round Robin for similar servers and similar short requests.",
                  "Use Weighted Round Robin when server capacities differ.",
                  "Use Least Connections for long-lived or uneven connections.",
                  "Use Weighted Least Connections for mixed server capacity plus uneven connection duration.",
                  "Use Least Response Time when latency is a reliable load signal.",
                  "Use Power of Two Choices for large fleets needing good balance with low overhead.",
                  "Use IP Hash for simple client affinity, but beware NAT and mobile IP changes.",
                  "Use URL Hash for cache locality by resource.",
                  "Use Consistent Hashing for distributed caches/storage/key-based routing.",
                  "Use Weighted Routing for canary, blue-green, and migration.",
                  "Use Geo/Latency Routing for multi-region systems."
                ]
              },
              {
                "id": "interview-qa",
                "title": "35. Common Interview Questions",
                "type": "qa",
                "items": [
                  {
                    "q": "What is a load balancer?",
                    "a": "A component that distributes incoming traffic across multiple healthy backend servers to improve scalability, availability, and performance."
                  },
                  {
                    "q": "Why do we need a load balancer?",
                    "a": "To support horizontal scaling, avoid overloaded servers, route around failures, and provide a stable entry point for clients."
                  },
                  {
                    "q": "Layer 4 vs Layer 7 load balancing?",
                    "a": "Layer 4 routes using IP, port, and transport data. Layer 7 routes using application data like host, path, headers, cookies, and methods."
                  },
                  {
                    "q": "What is round robin?",
                    "a": "A simple algorithm that sends requests to backends in fixed rotation. It is not load-aware."
                  },
                  {
                    "q": "Why can round robin create uneven load?",
                    "a": "Because request costs differ. One request can be much heavier or longer than another."
                  },
                  {
                    "q": "What is least connections?",
                    "a": "An algorithm that sends traffic to the backend with the fewest active connections."
                  },
                  {
                    "q": "What is consistent hashing?",
                    "a": "A key-based routing technique that minimizes remapping when backend servers are added or removed."
                  },
                  {
                    "q": "What are sticky sessions?",
                    "a": "A routing method that sends the same client to the same backend across requests."
                  },
                  {
                    "q": "Why are sticky sessions risky?",
                    "a": "They hurt failover and can create uneven load. Stateless servers or shared session storage are usually cleaner."
                  },
                  {
                    "q": "How do health checks work?",
                    "a": "The load balancer periodically checks backends and removes unhealthy instances from rotation until they recover."
                  },
                  {
                    "q": "Is the load balancer a single point of failure?",
                    "a": "A single LB instance is. Production systems use multiple LBs, active-active/active-passive setups, Anycast, DNS failover, or cloud-managed LBs."
                  },
                  {
                    "q": "What is TLS termination?",
                    "a": "The LB decrypts HTTPS traffic, enabling certificate management, HTTP inspection, Layer 7 routing, WAF, and logging."
                  },
                  {
                    "q": "DNS load balancing vs dedicated load balancer?",
                    "a": "DNS chooses endpoints during name resolution and is affected by caching. A dedicated LB routes actual connections or requests with health checks and richer logic."
                  },
                  {
                    "q": "How do you load balance WebSocket?",
                    "a": "Use connection-aware balancing, multiple connection servers, pub-sub/message broker, distributed presence, and reconnect logic."
                  },
                  {
                    "q": "How do you load balance gRPC?",
                    "a": "Use gRPC-aware proxies, service mesh, client-side load balancing, or connection management because HTTP/2 multiplexing can create uneven distribution."
                  },
                  {
                    "q": "Can a load balancer fix a database bottleneck?",
                    "a": "No. If the database is saturated, adding more app servers behind a LB can make the database problem worse."
                  }
                ]
              },
              {
                "id": "final-summary",
                "title": "36. Final Revision Summary",
                "type": "bullets",
                "items": [
                  "A load balancer distributes traffic across healthy backend servers.",
                  "It enables horizontal scaling and improves availability.",
                  "Layer 4 routing uses IP, port, and protocol.",
                  "Layer 7 routing uses application data like host, path, headers, and cookies.",
                  "Round robin is simple but not load-aware.",
                  "Weighted round robin handles server capacity differences using static weights.",
                  "Least connections is better for long-lived or uneven connections.",
                  "Least response time uses latency but can be noisy.",
                  "Consistent hashing reduces remapping when servers change.",
                  "Health checks are mandatory.",
                  "Sticky sessions are useful but usually not ideal long-term.",
                  "TLS termination enables Layer 7 inspection and central certificate management.",
                  "The LB itself must be redundant and scalable.",
                  "DNS load balancing and request-level load balancing are different.",
                  "Always measure the real bottleneck; a LB cannot fix every scaling problem."
                ]
              }
            ],
            "callouts": [
              {
                "type": "warning",
                "text": "Do not say a load balancer automatically makes a system scalable. If the database is the bottleneck, adding app servers will not fix it."
              },
              {
                "type": "interview-favorite",
                "text": "Interviewers often ask Layer 4 vs Layer 7. Answer with IP/port/protocol versus host/path/header/cookie routing."
              },
              {
                "type": "tip",
                "text": "For stateless web APIs, a common setup is DNS → load balancer → stateless app servers → shared database/cache."
              },
              {
                "type": "warning",
                "text": "Round robin distributes request count, not actual server load."
              },
              {
                "type": "interview-favorite",
                "text": "Always mention health checks. Without them, a load balancer can keep sending traffic to broken servers."
              },
              {
                "type": "tip",
                "text": "For WebSocket and long-lived connections, least connections is usually more sensible than simple round robin."
              },
              {
                "type": "warning",
                "text": "Sticky sessions are a workaround for stateful backends. Prefer stateless services or shared session storage when possible."
              }
            ],
            "tags": [
              "system-design",
              "load-balancer",
              "load-balancing",
              "horizontal-scaling",
              "high-availability",
              "round-robin",
              "weighted-round-robin",
              "least-connections",
              "consistent-hashing",
              "layer-4",
              "layer-7",
              "health-checks",
              "sticky-sessions",
              "tls-termination",
              "reverse-proxy",
              "api-gateway",
              "system-design-fundamentals"
            ]
          }
        },
        {
          "id": "08",
          "title": "🧠 System Design Fundamentals 8: ✅ SQL, NoSQL and Database Types Explained",
          "videoUrl": "https://youtu.be/pqD3TJcKkYM",
          "description": "Confused between SQL and NoSQL? Wondering what different types of databases exist and why companies use MySQL, PostgreSQL, MongoDB, Redis, Cassandra, or Neo4j? In this beginner-friendly System Design Fundamentals lecture, we'll build a strong foundation by understanding how databases are classified, the difference between Structured, Semi-Structured, and Unstructured Data, and the complete ecosystem of modern databases.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/hu3b4ytu3487t3847h3ngi34ug.jpg",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/hb4uy837fg84gbgfkj34bgh34bg.png"
        },
        {
          "id": "09",
          "title": "🧠 System Design Fundamentals 9: ✅ How to Choose the Right Database?",
          "videoUrl": "https://youtu.be/JjhJ5-RBIG0",
          "description": "How do you choose the right database for your application? In this System Design Fundamentals video, I explain a practical framework for database selection, including when to use SQL vs NoSQL databases, how workload and data models affect your decision, and what factors matter when designing scalable systems.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/q4bt3b4iuty348t73hg34gi43g.jpg",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/buywe8f7gw8efgw8yfebewuyfbe.png"
        },
        {
          "id": "10",
          "title": "🧠 System Design Fundamentals 10:✅ Database Optimization | Indexing Partitioning Replication Sharding",
          "videoUrl": "https://youtu.be/VHzUcMI5vkQ",
          "description": "How do you optimize and scale a database when your application grows from a few users to millions of requests? In this video, we’ll understand the complete journey of Database Optimization and Database Scaling — starting from query optimization and indexing, then moving toward vertical scaling, database replication, partitioning, and finally sharding.",
          "thumbnail": "https://ik.imagekit.io/interviewwithbunny/b34ijtb3i4ubtiu34t3tb43gt4b.png",
          "designImageUrl": "https://ik.imagekit.io/interviewwithbunny/b34bk43b6k3b4k63k4jb6k34k63jk4kj.png"
        }
      ]
    }
  }
}
