Complete System Design Course: Scalable Architectures & Key Concepts
System Design: From Foundation to Scalable Architectures
This course provides a thorough introduction to system design, explaining the core concepts that allow applications to scale from serving 10 users to 10 million users simultaneously. The content covers the entire spectrum, from fundamental components to advanced architectural patterns.
1. Foundation & Core Components
What is System Design?
System design is the discipline of creating architectures that can handle massive user loads. It involves combining components (servers, databases, caches) to achieve a common goal of serving millions of users reliably and efficiently. For a broader overview of these principles, see System Design Basics: Scalability, Cloud Hosting & API Explained.
The Alien Bank Analogy
To understand system design concepts intuitively, the course uses a story of building a bank:
| Issue | Solution | System Design Concept | |-------|----------|----------------------| | Process is slow (10 mins/customer) | Improve cashier skills | Optimize code quality, LLD | | Customer count increasing | Add cash counting machine, forms | Vertical scaling (upgrade server) | | Customers still waiting for single counter | Add more cash counters | Horizontal scaling (add more servers) | | Data discrepancy between counters | Introduce centralized database | Centralized database architecture | | Underutilized counters | Add a middleman to distribute customers | Load balancer |
This analogy is further explained in Scalable System Design Explained Using a Restaurant Analogy.
Data-Intensive vs. Compute-Intensive Applications
- Data-Intensive: Focus on moving data quickly (e.g., Instagram feeds, WhatsApp messages). Worries include read speed, storage safety, and handling high concurrent users. Solutions involve caching, replication, and sharding.
- Compute-Intensive: Focus on heavy calculations (e.g., image processing, ML model training, simulations). Worries include computation speed, parallel processing, and cost reduction. Solutions involve optimizing CPU/GPU usage.
- Key Trick: If time is lost in data movement, it's data-intensive. If lost in computation, it's compute-intensive.
2. Communication & APIs
DNS (Domain Name System)
DNS is a hierarchical system that translates human-readable domain names into IP addresses. It involves:
- DNS Resolver: Provided by your ISP, initiates the process.
- Root Servers: 13 logical servers that know the IP addresses of Top-Level Domain (TLD) servers.
- TLD Server: Handles specific TLDs like ".com", knows the IP of the authoritative name server.
- Authoritative Name Server: Stores the actual IP address of the domain (e.g., telesco.com).
Caching: Results are cached at the browser, OS, and DNS resolver levels to speed up subsequent requests.
Types of APIs
| API Type | Description | Use Case | |----------|-------------|----------| | REST | Stateless, uses JSON, most common | General web and mobile apps | | SOAP | Uses XML, legacy systems | Older enterprise applications | | GraphQL | Single endpoint, client defines query structure | Flexible data fetching | | gRPC | Uses Protocol Buffers, fast and small | Internal microservice communication | | WebSockets | Full-duplex, real-time communication | Chat, live notifications, gaming |
REST API Deep Dive
- Methods: GET (retrieve), POST (create, returns 201), PUT (full update), PATCH (partial update), DELETE (remove).
- Path Conventions: Use plural nouns (e.g.,
/users,/blogs). - Nested Resources: Use nesting for clear relationships (e.g.,
/blogs/{id}/comments), query parameters for complex filters or pagination. - Response Codes:
- 200 OK, 201 Created, 204 No Content
- 301 Permanent Redirect, 302 Temporary Redirect
- 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found
- 500 Internal Server Error
- Best Practice: Always wrap responses in an object (e.g.,
{"users": [...]}) for future extensibility.
3. Data Storage
SQL (Relational Databases)
- Structure: Tables, rows, columns, relationships via foreign keys.
- Key Constraints: UNIQUE, NOT NULL, PRIMARY KEY, FOREIGN KEY, CHECK, DEFAULT.
- Relationships:
- One-to-Many: A user can have multiple orders.
- Many-to-Many: Students and courses (requires a junction table).
- One-to-One: A content table linking to separate video/audio/blog tables.
- Use Case: Best for consistent, structured data with complex relationships (e.g., payment systems, transactions).
NoSQL Databases
- Why NoSQL?: Easy to scale horizontally, schema-less (handles unstructured data), and faster for certain operations due to single document structure.
- Types:
- Key-Value (e.g., Redis): Simple data retrieval by a unique key. Good for caching, sessions, cookies.
- Columnar DB (e.g., BigQuery, Redshift): Reads data column-wise instead of row-wise. Excellent for analytical queries and aggregations.
- Graph DB (e.g., Neo4j): Nodes and edges with properties. Ideal for social networks, recommendation engines, and analyzing relationships.
- Document DB (e.g., MongoDB, CouchDB): Stores JSON-like documents. Flexible and great for logging, user profiles, and content management.
- Rule of Thumb: Use NoSQL when availability and scalability are more important than strict consistency, and when data is unstructured.
4. Performance Optimization & Scalability
Caching
- What it is: A temporary, high-speed storage layer for frequently accessed data.
- Why it Matters: Reduces database load and response latency. Cache must be smaller than the database for efficiency.
- Strategies (Read/Write):
- Read-Through Cache: Reads from cache, cache fetches from DB on miss.
- Write-Through Cache: Writes go to cache first, then to DB (ensure data consistency).
- Write-Around Cache: Writes go directly to DB, cache only populated on read (good for Twitter/X type workloads).
- Write-Back Cache: Writes go to cache, asynchronously synced to DB (fast, but risk of data loss). Used in high-write scenarios like delivery status updates.
- Eviction Policies: LRU (Least Recently Used), MRU (Most Recently Used), LFU (Least Frequently Used), FIFO, LIFO.
Load Balancers
- Purpose: Distribute incoming traffic across multiple servers to prevent overload and increase availability.
- Key Responsibilities:
- Choosing a Server (Algorithms):
- Round Robin: Simple, equal distribution. Best for identical servers.
- Geo-based: Routes users to the nearest server, reducing latency.
- Least Connections: Sends requests to the server with the fewest active connections. Good for sticky sessions.
- Least Time: Routes based on server response time. Ideal for real-time apps (trading, search).
- IP Hash: Ensures a user always hits the same server. Used for session persistence but makes scaling difficult.
- Weighted Round Robin: Servers with higher capacity get more requests.
- Health Checks:
- Passive: Observes standard server responses to determine health.
- Active: Sends dedicated health check requests.
- Parameters: Interval, timeout, unhealthy threshold.
- Choosing a Server (Algorithms):
Replication
- Purpose: Keep copies of data for fault tolerance, high availability, and improved read throughput.
- Algorithms:
- Single-Leader: One leader handles writes, then replicates to followers.
- Async: Leader responds to user immediately, replicates later (faster, eventual consistency).
- Sync: Leader waits for all followers to confirm (stronger consistency, slower). Async is generally preferred.
- Multi-Leader: Multiple leaders accept writes, each replicates to its followers and to other leaders. Resolves conflicts via Last-Write-Wins, Replica ID, or user intervention.
- Leaderless: Any node can accept reads/writes. Uses Quorum (e.g., wait for more than half of N nodes to respond) to confirm writes. Used in DynamoDB, Cassandra.
- Single-Leader: One leader handles writes, then replicates to followers.
Partitioning (Sharding)
- Why Partition?: When data is too large for a single node, or query performance degrades with indexes.
- Goal: Data must be completely combined to form the full dataset (no data loss) and should be evenly distributed to avoid hotspots.
- Methods:
- Key Range: Divide by a range of keys (e.g., users 1-50000 on node 1, 50001-100000 on node 2). Can lead to hotspots if key distribution is not uniform.
- Hash of Key: Use a hash function to distribute keys, more even distribution but still can cause hotspots for popular keys.
- Partitioning by Secondary Index (Local): Each partition has its own secondary indexes. Requires scanning all partitions for a query.
- Partitioning by Secondary Index (Global): A single global index knows which data is on which partition. Faster reads, but writes become more complex.
5. Consistency, Availability, and Fault Tolerance
CAP Theorem
CAP theorem states that a distributed data store can only provide two of the following three guarantees simultaneously:
- C (Consistency): Every read returns the most recent write.
- A (Availability): Every request receives a (non-error) response, without guarantee it contains the latest write.
- P (Partition Tolerance): The system continues to operate even if network partitions occur between nodes.
Combinations:
- CA: Only possible with a single-node system (no P).
- CP: Consistency over Availability. System will stop serving to ensure data consistency during a partition.
- AP: Availability over Consistency. System will serve potentially stale data to remain available.
Real-World Examples:
- Instagram (AP): A slight delay in seeing a new post is acceptable; availability is paramount.
- Banking (CP): A transaction must be consistent; it's okay to wait for confirmation.
Message Queues
- Purpose: Decouple synchronous tasks from asynchronous ones. A producer puts a message into a queue, and a consumer processes it independently.
- Model: Pub/Sub (Publisher/Subscriber).
- Ordering:
- FIFO (First-In, First-Out): Strict ordering, but a single failed message can block the queue.
- Unordered: Processing continues even if one message fails.
- Priority Queue: Higher priority messages are processed first.
- Delivery:
- Pull: Consumer requests messages from the queue when ready.
- Push: Queue sends messages directly to the consumer.
- DLQ (Dead Letter Queue): Holds messages that could not be processed successfully after a set number of retries, preventing them from blocking the main queue.
- Use Cases: Email/SMS notifications, order processing, analytics logs. Avoid for real-time systems requiring immediate synchronous acknowledgments.
Fault Tolerance
Faults are categorized as:
- Hardware: Random, unpredictable (disk failure, server crash). Mitigated by replication and redundancy.
- Software: Deterministic, caused by bad code, configuration errors, or unhandled edge cases. Mitigated by thorough testing and logging.
- Human: The most unpredictable. Mitigated by code reviews, good practices, and automation.
6. Monitoring & Observability
API Monitoring
- Throughput: Requests per second. Set alerts when approaching limits.
- Error Codes: Track 4xx and 5xx errors. Log details for root cause analysis.
- Health Checks: Continuously monitor component status.
- Latency: Use percentiles (P50, P90, P99) instead of averages to understand majority user experience. A large gap between P50 and P99 indicates high latency tail events.
Machine Monitoring
- CPU Usage: Percentage of CPU in use.
- Memory Usage: Percentage of RAM being consumed.
- Disk I/O & Network: Input/output operations on disk and network bandwidth.
7. Practical Project: Designing a Video Streaming Platform
This final section demonstrates how to apply the learned concepts.
- Requirement: Stream a video (e.g., 50 GB, 4K, 60 FPS, 20 minutes) to 100,000 users.
- Solution:
- Segmentation: Divide the video into 1200 segments (1 second each).
- Resolutions: Create multiple quality levels (4K, 1080p, 720p, etc.) for each segment.
- Adaptive Bitrate Streaming: A client-side algorithm automatically selects the highest possible quality segment based on current network bandwidth.
- Architecture:
- Source Video → Transformation Service (creates segments)
- Segments → Priority Queue
- Priority Queue → Worker 1 (to 1080p), Worker 2 (to 720p), etc.
- Workers → Priority Queue 2
- Priority Queue 2 → Distributed CDN (servers in India, US, etc.) → End User
- Maths:
- Segment Size (4K): 50 GB / 1200 ≈ 42.6 MB
- Segment Size (480p): 5 GB / 1200 ≈ 4.17 MB
- Total for 100 users: ~8.7 GB. For 100,000 users, load balancing and CDN are required.
- Iterate: This design is a starting point. Adding caching at the CDN or browser level, and potentially different cache strategies, would be the next iteration for improvement.
Final Advice for Interviews
- Think out loud: Explain your reasoning and trade-offs.
- Use components for the right reasons: Adding more components increases cost and complexity.
- Start simple: Begin with a basic architecture and add complexity as requirements grow.
For a deeper exploration of these patterns, see the Comprehensive System Design Series: From Monolith to Microservices and Beyond.
Welcome back aliens. My name is Vin Reddy, and in this video we'll talk about system design. I know you are
waiting for a long time for a system design course on Telesco, and finally we are here. Now, in this course we are
going to understand different aspect of system design. Now, why it is important? When you go for the interviews, most of
the companies will ask you for system design knowledge, and the question will be based on that. Now, of course they
will not be saying this is a system design round, but they will expect from you to understand different concepts
here, and how will you solve a complex problem. And let me give you a statement which highlights this, and I have it
here. Anyone can write a code that works. A system design is what makes it work for million people at once, and
that's the power of system design. See, when you write a code, of course when you learn a language, you learn a
framework, you can build application. That framework can I mean, the entire project will work. The moment you deploy
it, the moment it is in production, and when you have users, not just 10 users, of course it will work for 10 users, and
it should work for 10 users, but the moment you have 10 million users accessing that at the same time, will it
work? That's the question. When you talk about all your favorite applications, it can be WhatsApp, YouTube, Instagram, or
Amazon for that matter, there are not few users who are accessing that at one time. There are millions of users, but
still if you can see, they work. So, how do we do it? And that's where system design plays a huge role. Now, to
understand this, we have we will be going through these concepts. So, in this particular video, we are going to
start with the basics of system design, what is system design, what are different components, data versus
compute intensive, and functional and non-functional, and all this comes under foundation. Now, once that is done,
we'll talk about how the communication happens between different systems. So, we'll focus on DNS, API, and of course
REST, which you might be knowing if you have worked with Spring Boot or Fast or Django and any web framework. Then we'll
move towards the data. Of course, when you have a lot of users, how will you store data? Not just their personal
data, but their activities. Uh and in that we have different options like SQL databases, no SQL databases, when to use
cache, and when to use SQL and no non-SQL. Then, let's say your users are growing day by day. How are you are
going to manage your application? And that's where scaling and distribution comes into picture.
In which we'll talk about load balancers, replication, partitioning, and the CAP theorem. That this will be
an interesting the CAP theorem. And then, we'll talk about operations and reliability. And the most important part
there becomes a fault-tolerant. What if something goes wrong? Does the application do have something to do
that? In which we'll talk about message queues and monitoring as well. And of course, we'll cover different examples
here to understand different concepts. And the entire section or this part will be taken by an amazing trainer, Akshay,
who is very good in explaining stuff. And I hope you will enjoy the entire part. And do share your thoughts in the
comment section. Don't keep it blank. You what you like, what you don't like. If you have any questions, do point it
there. And we'll try to answer them. And if you enjoy this type of videos from us, let me know in the comments as well.
That is our motivation. And I hope you're motivated. Let's get started. Akshay is all set.
>> [music] >> Hello everyone. Welcome back to Teleschool. My name is Akshay Agarwal.
And today, we are going to start the system design course. In this course, we are covering each and every topic which
includes in system design. So, let's start with understanding why do we need system design at all? In systems, we
have a lot of systems around us. If we talk about human body only, we have digestive system. And also, we have a
system. Along with this we have a lot of systems, but these kind of systems are pre-built. It means we have not created
them. We just discovered them with time and we got to know about various components which are required for us to
breathe or to digest our food. So, in these systems as they are not built by us, we can't actually change anything.
Maybe through surgery we can change some of the other component, but not much. But the kind of system we talk about in
IT industry are the systems which are built by human or engineers like us only.
So, in that there is a possibility that we can pick our systems based on our requirement and also based on our
requirement we can pick the components which are required in our system and we can basically improve our system based
on the request we are getting, based on the load our system is managing, based on the kind of traffic which we are
getting in our system. So, in system design if we go through the words we have two words here, right? One is
system and one is design. What is a system? If we go through the definition, system basically means there are a lot
of components. There can be more than one components in that system. So, let me write components. And those
components are working eventually to solve a particular use case or maybe multiple use cases, but the common
purpose of the application remains same. So, that means if we write system is equal to components plus common goal,
this sums up our definition of system. Now, when we talk about design, there are various components which we are
going to learn and there are various common goals based on the requirement analysis of different applications which
we are going to cover in this particular series. But in order to start, let's not dig too much into the technical details
in this video. Let's start from a very basic. Let's say we have a requirement of creating our own application or our
own bank. Let's say we have a bank named Alien Bank because we are in Telesco, why not? And in this bank, we are not
dealing about each and every operation. Currently, we have a single operation, which is we have a cash counter, and we
have a person sitting on a cash counter. Let's name it as cashier. Okay, so here we are ready with our cashier, who is on
our cash counter, and the requirement or the flow is pretty simple. For the flow, let's understand the concept of our
bank. So, we have multiple, let's say, customers are here, and each customer will come to our counter, either
withdraw or deposit some amount, get the receipt from the cashier, and then move forward. So, let's write it down as
customer flow cycle. So, in our customer flow cycle, there are four steps, basically. Customer first will visit the
counter, will come to the counter, then the customer will either withdraw or deposit some amount, take the receipt
from the cashier, and then the transaction is done. The purpose of our application is done. So, these are the
core functionality of our system. It is very basic, very easy. You might have seen this already in some of the banks.
So, we are building the same architecture right now. The architecture basically looks like this. Now, in order
to understand system design, what it really is, and how we are going to improve the working of our Alien Bank,
let's face some issues. We are going to face five issues, and based on the issues and solutions, we are going to
understand how system design looks like. So, here comes our first issue, which is the process is really slow. By slow,
what we meant is, let's say, our cashier takes 10 minutes per customer. So, that means we are serving only six customers
in 1-hour span. So, this is a huge issue, because if the number of customers will increase to a limit, and
also we have rush hour, in those cases, our system will break, and we will be not able to serve the customers rightly.
So, in order to improve this, let's first focus on our cashier. So, let's understand our system from the cashier's
point of view. So, basically, our cashier is taking time to understand the requirement. By requirement, I mean
whether the customer needs to deposit the amount or customer needs to withdraw the amount. They have to make this
interaction, which is time-consuming, right? Now, once the requirement is locked, whether the customer is going to
deposit the amount or withdraw the amount, the cashier manually have to count the cash. And then our cashier
will prepare a receipt, which will be again given to our customer. So, these are the time-consuming tasks for our
cashier. How can we improve the cashier time? So, in order to improve the working of our cashier, we have three
major issues. First is they are not able to understand the requirement. Next is they are counting the cash manually,
which is taking time. And again, they have to prepare the receipt. So, these are the issues which our cashier is
feeling, and we are trying to solve these issues. Maybe we can train our cashier in order to count the cash very
rapidly. Okay. Also, our cashier is also responsible for maintaining the receipt. So, we will improve the typing speed of
our cashier in order to make the receipt very fast. So, the second thing is train to type fast. So, with this, what we are
doing, we are not adding any resources to our cashier. Simply, we are making our cashier super fast, right? We are
improving the skills of our cashier, which is required for them to maintain this job. And let's say through this
training, the cashier is really improved now. He is or he or she is now taking, let's
say, 5 minutes in order to fulfill one customer's need. So, this is a huge improvement. By this, we are able to
improve the process by 50%. So, let's say our second issue is that the customers which our cashier used to
attend are increasing very fast, because this is our alien bank, and it is working very fastly, right? Growing very
fastly. So, now the issue two is our customers count is increasing. This is our issue two. In this case, what we
know so far? We know that our cashier we can't improve our cashier performance because it is now at its peak. So we
have to basically handle some or the other components to our cashier in order to make the work very fluently. So maybe
the desk on which our cashier is sitting, we will increase the size of desk. So next thing which we can do is
we will provide the cash counting machine to our cashier so that the manual cash counting time will get
reduced, right? So we'll provide cash counting machine, right? So with this also the time will
get reduced and our cashier will be able to attend more customers. One more thing we can do that is instead of coming to
counter and getting the requirement at the real time, we can give a form in advance to all customers so that they
can fill it and our interaction time will get reduced, right? So our cashier will now don't have to communicate each
and every request to our customers. Uh they can simply see the form and based on the form they will fill out the
thing. And now our cashier have cash counting device also, so the process will be fast. Let's say through these
processes, through enhancing our desk or cash counter, the timing got reduced from 5 minutes per customer to 3 minutes
per customer. This is also a huge impactful thing. So now our cash counter is capable of attending more customers
in lesser time. So that means even if the customer count is increasing, we will be able to process more customers
and enroll more customers into our Alien bank. So this issue is also kind of solved. Now let's move to the third
issue. The issue three is our customers are still waiting to get their application resolved, right? Currently,
what do we have? We have our cashier who is upgraded to its maximum level. So, we can't upgrade the cashier more. Also,
the desk on which it is sitting, the cash counter, is also upgraded to its maximum limit. So, we can't upgrade our
cash counter more. So, these two things are pretty solid right now. We can't improve it now. Now, the thing is, so if
we have 10 customers in place, that means our 10th customer will have to wait for 27 minutes in order to process
the three-minute work. So, that is a huge drawback of our system right now. And why that drawback is coming? Because
we have only single cash counter in our system. So, in order to make the process more feasible, now we have reached that
point that we have to add more cash counters into our system. So, let's add them. So, now we have our
cash counter one and cash counter two. Now, at this point, we have added one more counter, and things are pretty
solid right now. So, our customers will eventually have two counters to go to, and both of those counters will be able
to solve our issues. It feels good, right? Now, we have two counters to process. So, our customers will not have
to wait for longer time. They can either go to counter one, or they can go to counter two. But, let's say if first
customer is going to counter one, and the counter one is able to process the information, and they are maintaining
their own data. And similarly, our second counter is also maintaining its own data. And they are not synced. So,
this creates two issue, right? First issue is, if in our system, the counter two doesn't know anything about the
customer, that will create an issue because they have a valid account in our bank, and they should be able to proceed
their information through it. Now, the second issue is this only. That from the first account, they can also withdraw
the amount, and they will not update the second counter. So, that means they can again withdraw the amount from cash
counter two also. This will create an issue of data discrepancy. That means our data is not synced. So, the fourth
issue here is we need to have a centralized database or central data from which the cash counter one and cash
counter two will go and validate the data and go and retrieve the data. If the account is there for any customer
and the amount is also there, so the customer one will also insert the cash into the same account. And if the
counter two is getting withdrawal request, they will also get the amount out of the same account. So, in order to
fix this, we need to have a centralized data. That means our counter will look like this. This is our counter, let's
say two. This is our counter one. And they will eventually connect to a centralized database. And as it was
before, our customers are standing in a queue waiting for their turn to come in order to get the request. Now, after
solving all these case also, we have a new issue in hand. Which is, let's say the gate of our bank is here. This is
our alien bank. So, since the gate is here, most of our customers are going to the cash counter one only, where cash
counter two is sitting idle. Or maybe processing lesser request, far lesser request than the cash counter one. This
will again end up adding the wait time to our customers. And again, we will be end up handling lesser customers than
our potential. So, let's write that down. This is our issue five, which is underutilized counter. Now, in order to
solve this issue, we need to have a middleman. Which will stand apart from our counters. Let's say this is our
counter two. Again, this is our counter one. And here are our customers. And now our customers will not go to the counter
directly. They will first go to this middleman. Then the middleman will check how much load each counter is handling.
Suppose there are 10 customers at counter one and only five customers at counter two, then our middleman can
decide that next customer should go to the counter two. And the count will increase to six,
right? And the next customer will again go to counter two, and the next customer is again going to counter two only.
When the limit reach, let's say we have 11 customers here and 11 customers here. And if then any new customer arrive,
they will again ask man where to go. Let's say on some fine day our counter one is not working. That means our
middleman will know about this and will eventually send all the customers to counter two until the counter one is
getting fixed. So, let's end this example here at this particular point and see what have we learned. Through
this story, through this single story and five issues, I have tried to capture multiple concepts of system design. So,
the first concept which we discussed is designing a system, that is for sure. Ultimately, what design we do have here?
We have one cashier and the other cashier. Each cashier is having their own counter. So, this is our counter
two, maybe this is our counter one. We have a number of customers waiting for us. Also, we have a middleman now. With
that, we also have a common database, okay? So, this database is nothing but centralized database. The customers
which are coming to our bank is nothing but requests. And if they are standing in a queue, we can say they are creating
a traffic, okay? So, these are the traffic which we are going to handle. Now, all the requests or all the traffic
is coming through a load balancer, the middleman. So, this is nothing but our load balancer. So, from the load
balancers, all the requests are coming to either of the cash counter. So, we can say that our cash counter is nothing
but servers. And the person sitting there doing the work is nothing but our application code. Now, let's see the
system again through the system design point of view. So, the first issue which we have was our process is slow. So, in
order to solve that, we improved our code quality, right? So, first thing which we do in our systems also, we
improve our code quality. So, this is where our LLD will come and this is where our DSA will come. Suppose a
particular loop is taking certain time, we can reduce that loop or we can change that loop to some for each function or
some other way in order to process that information fast. So, while solving the issue two, we increase the size of our
table, we added some functionality, we added this cash counter. That means we upgraded our server. So, in order to
upgrade our server, this term is called vertical scaling. In vertical scaling, we don't add multiple servers, we
basically update our current server only. And again, the current server updation have a particular limit. After
that limit, you can't upgrade your system. So, then comes the issue third, which is our customers need to wait for
the single server to respond. So, in this case, we added more servers. So, in this scaling, we added more server that
is called horizontal scaling. So, after having multiple servers, we need to maintain the data at the common point.
So, that's why we added databases. And in order to add a common database, we basically can move to distributed
database architecture or can move to a centralized approach of handling that database. So, this is nothing but
centralized database. So, after having centralized database, again the issue comes where we don't know how to handle
multiple requests. That means our requests are again going to one counter only or one server only. Now that we
know they are going to one server only and the other server is underutilized. So, what we did, we added a middleman.
Our middleman is no one but our load balancer. Our load balancer basically do two things. They will check whether our
servers are healthy or not and based on that, they will distribute the request equally into multiple servers. So, these
are all the concepts which we discovered through this particular example. So, if we think about the applications we are
using in our day-to-day life, we are using WhatsApp, we are using maybe Snapchat, we are using Instagram, we are
using YouTube, we are using LinkedIn, we are using Netflix, Amazon, maybe Flipkart. All of the applications, if we
think about it, are showing us the data either in the form of images or in videos, in audios or in text. So, we can
say through a large lens, the data consist of these four kind of information. And all of the applications
which we are using in our day-to-day life consist of these data's only. So, we can say that in the core of every
system, we have data and we are then finding ways to make it available to the users in the way they are expecting it.
So, in the center, as we discussed, we have data. Data can be of various form. It can be image, video, audio or text.
Now, if we think about data, it is not like we are seeing the data once only. We have to see the data and also while
storing also, we need to store the large amount of data. Now, in order to store the data, we do have databases. So,
databases are basically our first component of system design. What is functionality of databases? Pretty
simple. We need to store our data and whenever we want to retrieve some data, we need to get the data from our
databases only. They are represented by this cylindrical figure. Databases can be of multiple ways. It can be
categorized into no sequel or sequel. Now, there are various kind of databases in both of these categories. In sequel
also, we have various databases from which we can select which database we are going to use. And similarly, in no
sequel also, we have various kinds of database from which we can pick which database we need for our application
right now. But, the core remains seems that we are saving our data in some or the other database. We will discuss
about sequel and no sequel databases in the coming videos. That's why I'm not going to deep into this at this moment.
Let's think about the scenario where you and every user on the planet knows the queries of database, knows how the
tables are structured if we are talking about sequel, or how the documents or nodes are connected if we are talking
about no sequel. If we talk about both of these scenarios, if user, the end user, already know the database query
languages and know how to deal with different kind of data, in that case, we might not need any application if you
think about it. We will simply go to our database, or the database will be accessible throughout every application.
Suppose we are seeing the LinkedIn, but on LinkedIn we will see an interface on which we will write our query, and the
data will be fetched to us. That will be an hectic task. But, if users know about that, and if users start exploring that,
that will be easier, right? In that case, we don't need any application layer in between us, and in that case,
we don't need any client-side application also between us. We simply will see a database, query it, and get
the results, right? In that case, what will happen? Each query have to be managed through the users only. And
these queries, when I talk about large systems, let's say we are talking about LinkedIn, there can be multiple tables
through which our data is curating and coming to us. Every user can't do it, right? That's why we have different
layer in front of our database, which is our application code. So, in application code, what we do is we hide the
complexity of dealing with databases, different tables, different documents, and we give the endpoints to our
application or to the world so that they can access the kind of information they are looking for. For that, we create
APIs or we can say we create endpoints. There can be multiple endpoints based on different features, based on different
operations. So, through endpoints, we communicate to our application, which will again communicate to our databases
to get the data we are trying to get. There can be various operations, like we can insert some data, we can update some
data, we can retrieve some data, we can delete some data based on our request. So, the thing becomes little easier to
us. Now, we don't have to deal with all the database tables, but now we have assigned endpoints through which we can
do the work which we want to do. And endpoints are pretty simple. So, in order to get some data, we have a
get endpoint. Let's say https, and here comes our Teleschool. So, this URL or this endpoint can be used to fetch all
the students which are there in our Teleschool environment. But then, what we are doing? By introducing application
code, we are moving our logic and data separately. That means we need some kind of connections between these two. In
which we can connect through IP addresses, we can connect through some HTTP protocols, or we can connect
through some TCP protocol. There are many ways of connecting the database from the application code and getting
the data which we want. So, in order to show the APIs or in order to interact with the APIs, we have to know what is
JSON and how we are going to send the data to our APIs and how we are going to fetch the data from our API.
In this level also, if we know how to handle JSON, if we know how to handle various operations through which
endpoint, it will make our life little easier than accessing the databases, right? So, in that case, we don't need a
front end if we know how to hit the endpoints through which we'll get the data. But, for our normal users, for
other users of any applications, this is also a tedious task. So, in order to make this easier, we have a different
layer between our system that is our client application. Client applications are nothing but the applications through
which we are interacting to the database, through which we are getting those databases. So, our client
applications, be it a mobile application, be it any web application, be it any ATM machine for that matter,
they are connected with the application code, and further, the data is being consumed by us. So, again, in order to
connect the application layer and the client code, we again need some kind of communication between them. We either
can go to the same HTTP or TCP or maybe IP. Now, we have our application ready in our hand and we can use it. So, if I
draw it like we used to draw it, this is represented by our client, either a desktop or a mobile device. Now, they
are sending queries to our application code, and our application code is further getting the data from database.
Now, for our client to communicate with our application code, we need to put our application code on any server. And now,
if we think about it, our application code is already exposing the APIs to our client. That means, it can also expose
the APIs, or any other application can also use our application codes through our APIs and do the work which is
needed. So, it can be connected to any other server also, which will contain their application code. And this will
communicate with our data through our application code. For every query which we are making, we are going to our
database, and from that we are getting the data which we are trying to get. So, if we think about it, most of the
communications will start from the client side, and they will come to our application code through which we will
get the data from the database. This is pretty much the easy application or the applications which we create on our
college projects look like. Either a to-do application or a simple chatting application will look like this. Now, in
the current scenario, what we are doing with this kind of architecture is every client is going to ask application code
for some or the other information, and to fetch the data, our application code needs to get the data from the database
only. We don't have any other option. In order to make the files accessible to our server faster, because the
database's calls are heavy, which will take a lot of time, let's say, if a database query is finding the columns
from 12 tables, it is going to take time, right? Now, in order to make the frequently used data accessible faster,
we have a different layer, which is called cache. When we are going to put any data into our cache, and when we are
supposed to remove the data. There are different strategies we are going to cover that also in the upcoming videos,
but for now, let's see, this is our cache, and all the frequently used data will be stored here. So, that we don't
have to call to our database, and they can be easily accessible through our cache only, which will make our process
slightly faster. So, this is our fourth component of our system, which is cache. Now, let's move forward. Let's say our
application is big, is having multiple servers. In that case, we may have some client or this client, either the mobile
application or the web view, and we are getting the data from multiple servers. Let's say this is server one or this is
server two. Now, when we have two servers or multiple servers, our client have to decide where to go. From where
do we fetch the information? Now, in order to take this control, we have a load balancer in the middle, which will
take care of each and every request and which will take care of each and every connections of the server. So, our load
balancers are responsible for handling two tasks. First, they will check whether the server is healthy or not.
And second one, they will divide the request for each servers equally. This is what our fifth component looks like.
Now, let's come to our next component, which is messaging queue. So, in order to understand message queue, let's first
understand that in order to process one operation, there can be multiple task into that operation, right? Let's say we
are ordering something. So, when we are ordering, we are going to do multiple tasks, right? We are first creating an
inventory update. Next is we are going to process the order also. We are going to talk to the vendor who is going to
take the delivery. So, we are going to handle delivery. And then, we are also trying to send you the information, the
notifications that your order is placed successfully. Don't worry about it. So, that means we are going to handle the
mail service or the SMS service also. Now, in order to handle multiple services like this, we can't wait for
every application to respond back. There are two kind of applications basically. First is, let's say this is an
application and this is another application. Let's say this is SMS application and this is our order
application or microservice for that matter. Now, whenever an order is placed, it is going to speak to the SMS
service or SMS application to send the message to the user that your order is placed successfully. Now, there can be
two ways of handling this. First way is, whenever the SMS is sent, they will notify the order service or the order
application back that we have sent your message done. Or there can be different way of handling it that from our order
service, we simply say that okay, this is our order information. Please send the message to this number and we are
done. We will not wait for the response. We will move forward with our application and with different orders.
So, the kind of requests in which we are waiting for any confirmation or any feedback from the other service are
called synchronous calls. And the kind of service in which we will simply put down the service and we are not waiting
for any confirmation, we will move forward to do our task. Those are called asynchronous services. Don't worry about
this if you don't understand it now. We are going to dissect each and every component in details in the further
coming videos. So, for asynchronous kind of services, what we can do, we can simply push our notification or push our
request to another service and move forward. So, in those kinds of services, we have a message queue or message
broker in middle. What it will do, we are having our order service here. It will put multiple request or can put a
request time to time into this message queue. And then all the requests which our order is processing through message
queue, they will not take care of it anymore. Our message queues will take care of it. They will connect with our
SMS service or SMS applications and send the request one by one. And they will take care of these all operations. So,
our message queue is like a broker who will take the request from one application, store inside it. They will
take care of these applications. They will take care of the retries or if any request is failing, they will take care
of it. Maybe they will communicate to our order application in the end or in the end of each day, we will send some
notification, some batch processes to our order application in order to show that which requests are fulfilled and
which are not fulfilled. So, in short, our messages queues are doing two work here. They are storing each and every
request which are coming from various other services or various other applications for that matter. They are
storing it and they are taking care of those requests. They are sending those requests finally to SMS application or
service and they are taking charge that okay, my first service or my first request is being processed. Nice. Now, I
will send the next request. Now, I will send the next request and further on. If it is failed, there are various
mechanisms how messages queues work and how they bisect and dissect each and every request and make sure that they
will provide the latest status of each request. And in those kinds of environment where we have multiple
microservices, in those cases message queues plays a vital role in between them. And with that, let's move to our
final component which is monitoring and logs. In monitoring and logs, what do we do? As we know now, we have different
components in our architecture, we have a client system here. We may have a mobile application again
which is connecting through an API to our load balancer which is again further connected to
either server one or server two. Our servers are the area in which we have our application code and then they
may be connected to single database or multiple databases and those databases can also communicate with each other in
order to maintain the data. And also, let's say our application is also using some third-party application for which
we are using message queues. Our message queues are denoted by this. Now, this is our architecture. We have so many
components. So, it is very important for us to monitor those components. To monitor
whether our load balancer is working finely or not. To manage whether our application code does have any error or
not. And if there is an error, we need to know how to solve it. We need to know the complete stack trace of that
particular error. We need to know how to reproduce those errors on our local system in order to fix them. And also,
while working with the applications, we know that for every each and now, we have multiple features which are being
attached to our applications. So, we also need to monitor while adding one more feature to our application, whether
any other feature is impacted or not, or it is failing or not. So, in order to do that, we have a lot of monitoring tools
and also logs to check our system. There is a complete testing department in our team also, who takes care of these
things. This is one of the vital role in our application. So, these are most of the common components which are used in
system design. Whenever we think about any application or designing any application, we suddenly jump to the
tools which are used in that application. We start creating diagrams that this is our client, this is our
database, this is how our data is going to flow, this is how we add cache maybe. So, these all things we start doing
without noticing a single thing, that is, how is our application going to work?
What is this application? What are the limitations of this application? In system design, there is a smart way of
doing that. First question you need to ask yourself before diving into the solutions is, what is this application?
What is the type of this application? Is it a data intensive application or a compute intensive application? With this
common differentiation, you will be able to save a lot of cost to your system design, as well as you will be able to
handle the task properly into your system. Let's say, we have two applications. In both of these
application, there are the same number of users. With that, the network on which both of the applications are
working are also pretty much same. Now, let's put a scenario that our system or the application which we are working on
is facing latency. Now, what is latency? Latency means our system is taking time to respond. There are various ways of
handling this issue, of fixing the latency issue. We can add multiple databases, we can add different caching
layers, we can update the caching mechanism, or we can update or the CPU powers. Now, on which vector
we are going to do this changes defines how well we are going to solve this issue. So, now there are two type of
applications as I discussed. One is data intensive application and one is compute intensive application. Let's understand
them one by one. The basic difference on which data intensive application is different from a compute intensive
application is that in data intensive application our core focus is on data. Now, for this data we need to have
multiple operations, right? We need to store the data, plus we need to gather the data, we need to move the data time
to time. That means we need to keep updating the data because new data is entering. And also, while new data is
entering we can also, right, we need to add a lot of data. We can take example of any application
which can be Instagram because on Instagram we see a lot of content and we post a lot of content. So, that is a
data intensive application where the primary focus is on gathering the data, is on showing the data, is on the data
you are posting. But, the focus is not on how the data will be calculated. The calculation part is pretty solid. We
just need to move the data from our databases to the client applications and we are sorted. So, in these kind of
systems the data mainly resides in the database. So, first issue can be the database response is low. Another is,
since we are using network calls here and there, that means our network calls are also bulky or we need to optimize
them. The third can be our server itself that our server configuration is pretty low, that's why we are not able to get
the data faster. So, all in all, in data-intensive applications, we are not blaming the client to upgrade their CPU,
we are blaming our circle, our database calls, our network calls, our server configuration in order to get the data
fast. Most of the applications in which you work on are data-intensive application. That's why we use terms
like cache enhancement, shredding, and through all of those things we will be able to solve this issue. Now, let me
also write some common examples of this. So, the common examples here are Instagram feed where we see a lot of
data as we discussed earlier. In WhatsApp messages also, there is a high amount of data because we are not only
taking care of one chat, we are taking care of entire chat. The count of chats or the count of messages WhatsApp deal
with is around 2 to 10 million messages per day. So, this is also a heavy data-intensive application. Now, there
are bank transactions, analytic dashboard, and log process system as well. In all these application, the
primary goal is to serve the data from database to our client system faster. There is not much calculation required
for them to show on the client machine. Now, based on the data-intensive application, what are the worries of a
data-intensive application? The first worry is how fast can we read the data? The second point is how safely are we
going to store the data? The third point, as we all know, a lot of users are using our application, so how many
users can use the same data simultaneously? The next and one of the most important worry of these kind of
application is what if any server dies? What if any server breaks? What if any network call breaks? What if any
database machine breaks down? So, in those cases, how we are going to handle the data? So, these are the most common
worries of the data intensive applications. And now, in order to solve them, we don't go to our client system.
We talk about the components on which these processes lies. And those are our databases, caching, replication, maybe
data sharding, or it can be solved through data consistency. So, these are all the components or the terms we use
in order to improve our data intensive application. Don't worry about these terms if you don't know them yet. We are
going to cover each and every component in details in the coming videos. So, I hope data intensive applications are
pretty clear up to now. But, for the sake of example, we are also going to take one more example. Now, think about
Instagram feed. We are not solving Instagram feed issue through any mathematical equations. We just have to
gather a lot of data and show you that data. So, what we don't do here, we don't do any complex maths. What we do
then? We normally fetch the posts. After fetching the post, we sort them through time or relevance. We show a lot of
images or videos. And according to those content, we normally do some operations, maybe like or commenting or sharing. So,
I'm writing that. So, this is all the operations loosely we have on the Instagram feed. Now, what is the common
challenge which Instagram is facing daily? That is we we have a lot of content. We have millions of users and
million of content which we have to handle. So, let me write that down. In other words, we can also say that
million of users are reading and writing at the same time. So, with this challenge, Instagram still needs to
scale, right? How does Instagram scale? If we think about this, Instagram adds multiple databases in order to store the
data. And also, Instagram uses aggressive caching in order to store or get the data fast. Now, we also know
that Instagram heavily depends on the images and the video content form. So, in order to handle them, we can't simply
store those kind of data into our databases directly. For that, in order to fetch them faster, we need to have
certain CDN in place. And in databases, we will only store the location of that particular file which we are going to
fetch through the CDN. Now, think about this. In order to fix the latency issue on Instagram or in order to help
Instagram scale, we hardly touch the CPU or we hardly touch any GPU. We only touch the data or the components which
are associated with them. Because the issue here is not the CPU, but the data movements between the components. Now,
this is all about data-intensive applications. Let's now focus on the compute-intensive applications. Now, the
compute-intensive application is exactly opposite of a data-intensive application. Here, the amount of data
which we are retrieving from databases are pretty much low, but the calculations we need to perform are
really heavy. So, instead of putting our focus on database caching, replication, sharding maybe, we move our focus from
there to CPU and GPUs. So, let me write down the challenge now. So, we can safely say that here our systems are
compute-bound or we can say CPU or GPU-bound. Let me take some examples of this. The most common example can be
image processing, video rendering. The machine learning model training can also be a good example of this. Also, we can
talk about simulation and cryptography. Now, in all these examples, the data which we are getting from the database
are in a smaller level only, but the kind of processing we need to do in order to show these data to user is
heavy. That's why they come under the category of compute intensive applications. Now, let's discuss about
the worries of a computer intensive application. The first worry is pretty simple, that is how fast can we do the
calculation or how fast can we compute? The second worry can be can we do something some processes in parallel in
order to show the data faster to the user. You may have seen a lot of systems in which in parallel the image is being
processed and you are able to move further with the application. Let the image process and the meanwhile you are
moving to the other features of the application. The next issue can be how can we reduce the computational cost?
And also as a system designer, we can also think about can we use GPU instead of CPU? So, these are the common worries
of a computer intensive application where the data is not much, but the processing is the king. Let me also take
a good example of a computer intensive application. So, in order to create a simulator machine, it is very important
for the simulator to work exactly how the actual machine works. So, think about a scenario that our country is
hiring a lot of pilots every year and we can't spend too much on the actual aircrafts when the pilot doesn't know
how to ride it or how to fly it. In order to train them, we need to give them simulators machines and on which
they train themself first and then sit on the actual plane. So, now in simulation machine, our common worries
or the task which we are going to have is can we do anything in parallel again? Can we use GPU instead of CPU? Again,
how can we reduce the cost of our simulator? Also, we need to find a way to gather a lot of good algorithms in
order to do the task frequently. Now, at this point I think we are very clear on how our data intensive applications are
different from computer intensive applications and why we need to put our focus on computation on different
algorithms on GPU or CPU in order to make a computer intensive application works because the data is very low and
in data intensive application if we start focusing on GPU and CPU there will not be any big changes because the issue
lies in the database network calls caching algorithms or maybe server. So in order to fix them we need to put our
effort into the right category into the right components so that our application works seamlessly. And why this is
important? Because if we put our efforts into the wrong components our components are not free they are going to cost us
money and if we put the money in the wrong place and not getting any help in order to solve our issue it is going to
be a bad system design for the entire game. Now when we talk about data intensive or computer intensive
applications we often think that these two are very separate applications. But in today's time we live in the
environment of multiple applications and each application performs multiple tasks. So that means our data
intensiveness or the computer intensiveness of the application can also depend on the feature we are
working in. For example we have YouTube right? YouTube is data intensive for serving the videos. There are a lot of
videos in YouTube environment and you get the videos and we can say that it is computer intensive when it comes to the
recommendation. Because in order to get the recommendations we have to get what you are watching right now. So I think
it is pretty sorted now that in order to fix a particular issue you need to first think whether the issue which we are
facing is computer intensive or data intensive and then you get into the components and fix that. There is a
trick to understand whether the feature is data intensive or compute intensive. That is, if the time is lost in data
movement, probably it is data intensive application. Or if the time is lost in the computation, it is probably a
compute intensive feature or the task. This one distinctions save you a lot of money, time, and complexity. In order to
discuss our requirements, we divide the requirements into two categories. One is functional requirement and the another
one is non-functional requirement. So, let's go to our screen and let's discuss this. Functional requirement says,
"Please define all the points which you are building, all the features which you are building, all the working which you
are building." That means, if we define every feature that comes under the functional requirement. So, let's
understand functional requirement by taking an example of any e-commerce application, let's say Amazon. So, what
can be the functional requirements of Amazon application? That means, we are defining each and every feature which we
see on Amazon. So, let's say on Amazon application, a user can register themself. User should also be able to
log in. So, let's write that. After login, user should be able to see the products. Also, user can search the
products or filter them. Let's write the searching and filtering into two parts. So, search the products and user
can also filter the products. After filtering, searching, user should be able to add any product to the cart.
After adding any product to the cart, user should be able to get some promotions or get some discounts, right?
So, they should be able to apply the coupon codes to get the discount. And once all this done, user should be able
to place the order. After placing the order, user should be able to do the payments, maybe. And once the item is
placed, once the order is placed, user should be able to track that order. So, all of these points are the functional
requirements of Amazon or any e-commerce application for that matter. In which we define all the functionalities, all the
actions which user can take, all the working of the application is defined under functional requirement. So, if we
have to write this, what is functional requirement again? Functional requirements means all the features
which you are going to have in your application are the functional requirements. Now, let's move forward to
the non-functional requirements. For non-functional requirements also, let's start with the example of Amazon only.
Let's say the first non-functional requirement is that on our Amazon site, we have
approximately 1 million users. So, that means our system should be able to handle 1 million daily active users.
Now, in order to give our users a good experience, we can say that the response time should be less. But, in
non-functional requirements, we have to define how less. So, let's say for Amazon application,
the response time should be less than 200 milliseconds for each request. Now, our application is built and is running,
that means our application can't handle failure. But, that is not possible, right? So, we should define up to how
much is our availability of our system. That means for how much time our system will be available. That are defined
under SLAs and SLOs. So, we have to define them. That for 1 year, our system downtime should be this much amount
only. And for 99.9% time, our system should be up. That is defined under availability. After
availability, we don't want our data to be flowing easily, so we want to encrypt our data. And let's say on any sale day,
let's say Black Friday sale or Diwali sale or Holi sale, in those times our normal users can go up to 10x. So, our
system should be able to handle that much traffic also. So, let's write that down. With this, our system should be
also able to handle the faults. Because there are going to be faults in the system. No application is perfect so
far. Every system have their own faults, have their own downtime. But we need to see how can we track them and how can we
fix them as fast as possible. So, that is we need to make our system fault tolerant. So, with this example of
Amazon, we get to know that our functional requirement are the features of the system that we can say what our
system is going to do. And with non-functional requirements, we define how our system is going to perform. So,
these are the specific non-functional requirements for Amazon. If we have to write it in a generic way, there are
some generic terms which we can write. So, we can talk about scalability. Let's say our current configuration of server
is reaching to a point where it is not able to handle the kind of traffic it is getting. So, now in order to scale, will
you scale it horizontally or vertically? That will be taken care under non-functional requirements. Apart from
that, as we have discussed, availability of every system will be defined under the non-functional. As we have already
discussed, that means our system uptime will be calculated here. Which is defined under SLAs and SLOs.
So, in these agreements, it is already defined that, let's say we are using Amazon server in order to deploy our
application, then Amazon will give us some contract that we will be down up to this time. And apart from that, you will
not see any issues. Now, in those cases, if we see any issues, let's say for a day, Amazon says that we will be down
maybe for 5 minutes maximum. But then, if Amazon fails to keep that promise or that agreement, the companies which are
using the Amazon service can sue them. Because the Amazon services have given us the SLA's in writing that the
services will not be down after certain point. So, availability plays a huge concern there. Next, we have
reliability. In reliability, we can say that our system should be reliable. That means there should be no data loss in
our system. After reliability, comes the performance. Under performance, we are going to define how our system is going
to work, how much latency can we feel in the system. What is our P95? What is our P90? What is our P99? These all things
are defined under performance. So, all the words which I have said in this particular video, we are going to cover
each and every topic in details in the coming videos. So, don't worry about this if you don't get it now. After
performance, we can jump to security. In security, we can have features like authentication, authorization, and
encryption. With security, we are also dealing with a lot of code. That means we can say our code should be in perfect
modules so that each code or each chunk of code can use different APIs or different modules in order to work in
efficient way. After this, we have last but not the least requirement, that is observability. Even if our code is
pushed to production, there are a lot of use cases which are handled after that. Because we don't know the exact behavior
of users, that how they are going to use it. So, we need to keep track of all the monitoring tools, all the tools which
are going to track the user behavior on our application. So that we can improve our system and also we will be able to
track if there are any bugs and solve them. So, let me write monitoring and logging. So, this is all about
functional and non-functional requirements. I hope you all get it. But in order to completely get it, we all go
through a lot of applications on a day-to-day basis, right? We may be scrolling through Instagram, maybe
watching YouTube, maybe watching Netflix, or Amazon Prime, or purchasing anything from Flipkart, Amazon, or any
other application e-commerce platform, or we may be scrolling through our WhatsApp messages. Now, I want you,
while using your application, just sit back for a few minutes and think about all the functional features which you
are working, or all the functional requirements which you are using, and all the non-functional requirements
which the app must have configured or thought of while creating that applications. Also, comment down the
application on which you worked on, and write down three to four functional and non-functional requirements. That will
help you and me also in order to understand that we are learning on a similar pace.
How does our browser know which webpage to hit? Do they know by domain name, or do they get these things through IP
addresses? Let's find out. So, whenever on our browser or on our machine, whenever we
write any address, let's say telesco.com, it is going to redirect us to the telesco page, which is our
homepage. Maybe some banners here, maybe some courses section here. It is like this only. Now, how do our
browser figure out which page to show? Because, as humans, we communicate through names, but systems don't
communicate through names. In order to establish the communication between one system to another system, we need IP
addresses. So, that means there should be some place in between or some system in between our browser or our OS through
which we are going to get the correct IP addresses of each and every site. So, we will send telesco.com, and in return we
will get some IP addresses. So, this can be the IP address of telesco. I'm not exactly sure about the IP address, but
yeah. Like this, our DNS works. So, DNS basically stands for domain name system. Domain means any specific URL or
any specific domain. Domain is this only, telesco.com. There can be multiple domains, telesco.net, google.com,
facebook.com, whatsapp.com, .web I think. But yeah, you get the point. These all things are domain. And what is
subdomain? If I write any subdomain, let's say docs.telesco.com or any other subdomain, let's say
course. So, they both are subdomains of telesco.com. Where telesco.com acts as an umbrella and under which we can
create multiple subdomains if and when required. So, these are the domain and subdomains. But mind it, the machines,
the computers doesn't communicate with each other through these domain names. So, they need IP addresses. Let's figure
out ways on which our system can handle the IP addresses. Let's say we are building a browser. Why do we need DNS?
Why do we need another system for maintaining the domains? We can basically install or store all the
domain name list on our browser only. Now, is this even possible? Let's say if we have only two or three websites,
maybe 10 websites, we can store their names, even 100 also. But the current number of domains which exist in our
world is more than 350 million. And as a browser, browser doesn't know which sites I'm going to use, whether I'm
going to use Stack Overflow one day and Gmail the other. So, we can't store all the 350 plus domains into our browser.
That will crash our system. So, two reasons because of which we can't store the entire domain list on our browser
are first thing is it will get heavier. And to store such amount of information, we need a high storage browser. So, that
will also impact the performance of the system. Now, the other point is what if the IP changes after some time? What if,
let's say, I am running any site, teleschool.com, I have purchased my domain from maybe GoDaddy, and tomorrow
I decide to discontinue my service from GoDaddy, and I'll move to Google Domains. So, I'll purchase or renew the
domain from there. In that case, my IP can be changed. And if it restores, I need to reassure that in all the
browsers where teleschool.com is running, I need to update them. I need to send them some update. So, through
this we can estimate that it can't be stored in one browser. And also, it can't be stored in a single server,
also, because it will also get heavier. There are more than 350 million domains. That means, if we are going to store it
in a tabular manner, so the here will be the domain name, and the corresponding IP addresses accordingly. So, here we
will have any domain name such as google.com, and here we will have the IP address, 1.2.3.4,
for example. So, by this, we will create the entire list. This is not possible for a single
machine to handle, because if that machine breaks, then the entire system, the entire internet will break. Anyone
in the world will not be able to use any internet services or any web pages for that matter. No APIs will hit, because
it all depends on the DNS. So, one thing is sure, we need a certain kind of system in order to fulfill this
requirement. That's why we have one of the most beautifully designed system, DNS. Let's start from the high level.
So, this is our machine, which is going to use some internet service provider, and under this service provider, we have
something called as DNS resolver. So, this DNS resolver is responsible for us to getting the correct IP of any
websites or any domain which we are trying to process. In order to understand this clearly, let me take
this and put it in a side, so that I can draw more things onto our screen. So, the first call it is going to make is to
our root server. Now, there are 13 root servers in this entire world. That means there
are 13 companies who own 13 web servers in this entire world. They are not physically 13, but they are categorized
into 13. So, that means out of these 13, we are going to hit one of them and get the response. These root servers are
typically named from A to M like this. Again, from this A to M, they are owned from various companies. So, you can see
the complete list of the root servers with the companies which own that root servers on the screen. You don't have to
remember all of them. You just have to see and understand that these are the service providers of the root servers
from which we are going to get some information in order to process our domain. Now, let's come back to screen.
As discussed, we were searching for telesco.com. Also, I can remove this now. Now, when we are searching for
telesco.com, the root server doesn't know anything about telesco.com, but it knows about the TLDs which are top level
domains. What is top level domain? If I talk about .com, this is our top level domain. .com,
.net, .gov, .in, .uk, .us, .education maybe .edu, we use them. These all domains are the top level domains of any
website. So, that means if we need to find out where telesco.com is, we first need to know which server is handling
all the .coms. So, in order to do that, our root server will give the response that okay, you can go to one of the TLDs
which is handling the top level domain for .com and the root server will return the IP address of that. Maybe this is
the IP address and from now, we know that we are going to hit any TLD, top-level domain for a .com. Now, from
this TLD, we are again not going to get the complete IP address of the site. We are going to get the proper server or
the name server or the authoritative servers which are managing the all the IP addresses for our domains. Let's say
this is the IP address of our authoritative name server. And from this, we are going to make another call
which are our authoritative name servers. And from here, we are going to actually get where our telesco.com
resides, the IP address, which was 19283841. And once this is captured through our
browser, the IP, then it is going to make the call to telesco.com server and it is going to get the web page which is
desired for. I get it this can be a little confusing at start, so let me reiterate it for once.
So, whenever we click on any site, whenever we want to find any site, let it be telesco.com, it is first going to
our service provider, internet service provider. Also, you can see uh it can go through router and router can
also have DNS resolver. So, from internet service provider which have DNS resolver, this component is going to
resolve all our queries for us and ultimately will give us a perfectly fine working IP address for telesco.com or
any other site for that matter. So, the first step is it will go to the root servers. What is root servers? There are
13 companies who own these root servers who doesn't know the IP address of your site directly, but knows which TLD to
follow. So, from root server, it is going to check what is your TLD, top-level domain. And in this case,
it is .com. Now, as it is dot com it is going to get the IP address of the TLD which is handling the dot com domain. So
after this first call now we are going to call the TLD which we get from the root server and from this also we are
not going to get the complete IP address of telesco. We are going to get on which name server we are going to hit to get
the complete IP address of telesco.com. So from here we are going to get the authoritative name servers IP and
accordingly we are going to hit our third and final call to our authoritative name servers. Our
authoritative name servers are mostly can be configured in GoDaddy or Hostinger or any other service providers
in your current application. And from these authoritative name servers we are going to get the IP address of
telesco.com. Now in order to click one side we have to do these three operations. This can be heavy, right? So
this is only done first time and the next time when you click on anything it basically takes the value from cache.
Now cache can be stored in three locations here or more than three if you think about it, but three are definite
locations. The first is the DNS resolver can have the cache to have all the common IP addresses which we are going
to take or the all the common domains which we are trying to hit. It can store it here. Also on our system we have two
things. One is OS and one is browser. In both of these areas we can basically add that domain and the IP address in order
to get the details fast. So browser also maintain that cache OS level also we can basically maintain the cache and on our
DNS resolver site also a cache is maintained in order to get the details fast. Now when we are at this topic let
me tell you about one more thing, that whenever we create domain name systems like this dogs or subdomains, these all
entirely creates a zone in the authoritative name server. That means telesco.com including all the
subdomains. So, one zone can contain multiple domain list, which will include only the domains specifically to that
particular main domain. Okay? And through this it is easier to get to the main domain. So, in this case, whenever
we are hitting the root server, we are getting the IP of TLD, again hitting the TLD, getting the IP of authoritative
name server. And in authoritative name servers, we can have different zones attached to different domains.
And in this zone, if it is telesco zone, in that case, all the domains which are associated with telesco.com will be
here. So, if instead of telesco.com, if we are hitting courses.telesco.com, and from this, the authoritative name
server will get that this is our telesco zone. And in telesco zone, we will get the courses.telesco.com
IP address also. So, yeah, this is it for DNS for now. I hope you understand it. If you get it, let me know in the
comments. If you have any queries also, let me know in the comments. Have you ever wondered, if you see Zomato, if you
see Uber, if you see Ola, if you see Rapido, and all these applications, also Swiggy, they all have an inbuilt map.
But, did they create their own map in their applications, or do they use the existing Google Maps in order to do
that? Now, that is possible only through APIs. So, Google Maps present some APIs to other applications which they can
use. Now, in order to understand APIs in detail, let's dig into this video and go to our screen. If we think about API,
the API basically stands for application programming interface. Now, what does this mean? Suppose this is our
application. Any application will work. Let's say this is a movie application.
In which we have all the movies, plus we have all the ratings, all the reviews of the movie. Like IMDb or something, okay?
Like Rotten Tomatoes or something. So, we are already capturing all the details which includes a movie like latest
movie, latest hits of 2000, 2010, 2020, everything. Now, we already have this amount of data which is stored in our
database and our application is already working on that. Let's say now in any other corner of the world, someone is
trying to create one application based on the data which is already captured in this app. Now, they have two options.
Either they can create their own engine like this, their own application where they will again curate all the ratings,
curate all the reviews, and curate all the data. But now, where are they going to curate this from?
For this, every application leaves some end points which can be used by another application. Now, if
for our application or for this developer application, if they want to use rating of some movie application,
they can directly call the APIs of this application and through which they will get the proper information which is
required for them. Now, like this, this developer or this application don't have to create their own application for the
task which has already been done. So, they can use the application data directly. But again, can't we directly
give access to our database? This can be dangerous, right? Because then this application again have to handle a lot
of scenarios based on the database queries, plus they can also delete some important data in this database which
will affect the other applications which are using the same content. Also, there are some added functionalities. Let's
say we are giving an option to write a review again. So, in this we have already handled that case through some
or the other API. So, instead of creating a new endpoint in this application, we can directly access the
endpoint of the other application, which is going to take our review also. So, in this case, this application saves
a lot of time because they are using another application, the APIs of another application through which they are going
to do the same work. Now, in this case, if this application wants to add some data into their database also, they can
easily do this. So, this is one use case of creating an API or using APIs of other application. Also, there can be a
case where you're creating an application and in the backend you are exposing the APIs which can be used by
your front end. So, front end can be a web application or even a mobile application. Can be both also. Let's say
we are creating some backend and from for that application we are also creating front end maybe using React or
something and backend using Swift or something. In both of these cases, these languages, the React and Swift, both are
capable of handling APIs. So, they don't care about which language did the backend use in order to provide them the
API. They will directly call the APIs and use the information which they are trying to use. So, this is another
scenario where there are no two applications, there is only single application which is divided into front
end and backend part and using the data which backend is providing. Again, in this case also, you can think that they
will use the database directly, but it is restricted and prohibited for the security of data which we have already
discussed in the first case. So, with this we understand the core functionality or usage of the APIs.
Number one is if you want to give access to some other application, and number two is if you want to give access of the
endpoints or the APIs to our front end. Now, this both can also happen at the same time because it is up to us how do
we want to expose our APIs. Some applications or some backend basically restrict the APIs to be used through
their front end applications only. And if you want to make them open, you can make them open so that other application
can also use the same API. Now, let's move forward to the type of APIs. The first type of API is called as REST API.
If you are a developer, you already are using this. Also, if you are creating projects, you might already have created
them or use them in your application. Now, REST basically stands for representational state transfer. REST is
one of those APIs which you will see in most of the application nowadays because of its easiness, its structure, and its
ability to easily maintain a large amount of data with low volume. So, that's why we use REST. Mostly, in
REST we use JSON, which is JavaScript Object Notation, which is a way or a format through which
we will send or retrieve the data. It is a easy to use format, that's why we are sticking with this for this today's
video, and we are going to cover this in details in a minute. Also, we have some other types, that is SOAP. SOAP
basically stands for Simple Object Access Protocol. This is also one way of handling the data across applications,
across different platforms, and SOAP basically use XML formats in order to transfer the data.
XMLs are replaced by JSON because XMLs were really bulky in nature because in order to create every object or every
array, we have to mention the attributes again and again, which is solved by JSON. So, that's why we prefer JSON over
XML any day in today's time. But, the SOAP or the XML was famous back then. So, that means there are a lot of
applications which are still using XMLs. Why? Because there are legacy applications. They are built way earlier
when JSON was introduced and still are using XML. So, in legacy system, if the APIs are created and exposed using XML,
that means in order to make our system work, we also need to know about XMLs so that we
can integrate those APIs into our system. Also, you can use some middleware application where the data
will be taken in the form of XML and will be converted into JSON and your application can use that data
afterwards. So, that's why if you are working with a legacy system, it is preferred to understand the XMLs also so
that you can convert them in JSON or you can work them without converting them as well. So, after SOAP, let's jump to the
third one, which is GraphQL. So, GraphQL is also used widely across many applications, many platforms. So, the
basic thing in GraphQL is GraphQL basically says that I'll provide a single endpoint, only a single endpoint,
and based on that, you can send whichever query you want. So, in FE, from FE side, we will not hit multiple
endpoints, but we will send a request as a query. That's why the GraphQL stands for graph query language. So, GraphQL
also have a query language that is similar to SQL. In SQL also, we have multiple queries from which we can do
our things. In this also, only one point is exposed as I told earlier and using that only
the front end now can send multiple queries and access that particular endpoint and get the data which they
want. So, this is the core concept of GraphQL. So, as I said, GraphQL is also present in our today's time also. So,
there can be a chance that you learning GraphQL and working on it. The other type of API is gRPC. Here, the RPC
stands for remote procedural calls and for G some may say that G stands for Google because it is created by Google,
but it is gRCP remote procedural calls only. So, instead of using XML and JSON, we have a
new format here which is called protocol buffers. It is very much efficient than JSON and XML combined. So, that means
the size of protocol buffers are usually very small than XML or JSON. So, that will enable the data to transfer faster.
So, because of this only Google created this and uses this. So, this is small in size and faster to
transfer. In today's applications where we have microservices, right? So, in microservices, this is our entire
application and let's say our applications is divided into multiple applications which is combining the data
of their endpoints through each other. Let's say these are the connections and through this they are going to provide
the actual API to the outer world. Now, for this communication between these microservices, we often use gRPC. Why?
Because internally at least the communication should be faster to provide the relevant information in a
low latency. This will also help us having multiple microservices and having various kind of communications between
them before giving the actual data to the other application or the front end. Now, let's jump to the last one in our
list today, which is WebSockets. WebSockets are also used in today's time and today's application for various
purposes. For every notifications, if we see, WebSockets are used. In order to maintain a chatting stream, WebSockets
are used. Wherever you need some real-time connections, WebSockets are used. So, its architecture is slightly
different from the other services. In other services, front end or the other application will place some request to
the application and then they will provide the response. But, in WebSockets, whenever front end creates a
request, a channel is created. Now, once the channel is established, then the conversation can be lead through back
end also. Whereas, in other forms, only front end or the other application, which is trying to access the
information, they send the request and get the request. But, in this case, since the back end also can initiate the
conversation, that means it can also send some or the other information by itself. Those information can be the
chat or the notifications, which is very helpful for front end to get. Because, if this channel is not there, the front
end have to constantly ask the back end whether there are some new notifications or not or whether the chat is there or
not. But, once the channel is maintained through WebSockets, it is easy to navigate message from back end to front
end and front end to back end as well. With this also, if you have played quizzes on our Teleschool platform only,
Teleschool channel, in which you may see an application which Naveen sir presents in order to share the quiz with you all,
you use that application in order to give the responses. Now, those responses or the marks are calculated based on the
response time and the correctness of the answer. Now, those connections are also maintained through WebSockets. That's
why you see many notifications or some GIF or something something. So, this is about web socket and with this I would
like to conclude this session because it is going to be a long session and it is better to understand these things in
parts. So, these are the most used five type of APIs which we have. Now, in order to understand them briefly in the
next video, I'm going to talk about RESTful APIs in which we will discuss the various aspects or the parts of the
APIs and also see what are the recommendations and usage of those APIs.
Now that we already know about RESTful APIs and all the other different type of APIs, it's time to deep dive into one of
them. And for today's video, I have selected REST APIs to deep dive into. Why? Because it is the most used APIs
across the world. It doesn't matter if you are starting a career or you are already seasoned, you have to work with
RESTful APIs for sure. So, let's move to the screen now. RESTful APIs are also known as REST APIs. As we know, it
stands for Representational State Transfer. What does it mean? It means that REST is also one format through
which we will transfer our data from one end to another end. What are these ends? We have discussed this already. Either
we are giving some data from front end to back end or vice versa. It is also possible that one application needs to
get some data from some other application. So, in that case also, APIs are used and REST specifically is used
to transfer some data from one end to another end. So, basically, it is a way of how our applications are going to
communicate with each other. Now, before starting, let me tell you that in REST, we normally use JSON. JSON is a standard
way of sharing the data. In JSON, we can have array, we can have object and with these two combinations,
we can share our data in the form of key and value. So, let me create a simple JSON. Let's say this is a start of an
object and here we have a key, let's say name. For value, I'm writing my name. With comma, we separate different
values. So, after comma, I have to write some other key. So, let it be age. And with age, I can write any numeric value
as well. So, as we can see, this is string value and this is a numeric value. There are more values who are
accepted as a form of value, which is We have already seen string and then number. We can also use boolean, even a
nested object, arrays, and a null. Null can also be used because if we don't have any value for that particular field
directly, it is easy to say it is a null value. And the application which is using that value can take care of it.
So, this is all about the JSON which we need to know. We need to know that it is in the form of key-value pair and what
are the values which we can use. Now, let's move forward to the next part, which is endpoint. In order to
understand endpoint, we have to get two things. One is method and the other thing is path. So, in order to access
any data, we have to have some URL on which we will hit with that particular method and based on the method selection
and the path, we are going to either get or pass some data to our server. Let me start this with an example. Suppose we
are using HTTP as uh any site, my site .com. So, this is our main site and after that, we can put a slash and use a
different URL. Let's say we are separating all the APIs in the API subdomain. So, APIs is there and then we
can also segregate the APIs using version. So, this is version one and here we are going to write resource.
Now, resource can be anything. If I talk about any e-commerce application, it can be a product, it can be coupon, can be
user, can be anything, basically. A resource is an entity through which we are navigating and getting and passing
the data to the database. So, this is the URL. The URL is called the path through which we are going to either get
or pass some data. And with this, we need to have a method. So, for method, we can have a get basically. So, get
method with this path combinedly creates an endpoint and through which now we can get some data. We expect this path to
retrieve some data because we are using get. Now, there can be multiple ways of creating this URL. So, normally, we hide
the domain part of it. We don't use them in the explanations. We only use the resource part. Also, we can remove the
version also. With that, API can also be removed in order to understand the resource part directly.
So, from now on, if I create any path, let's say users, it is expected to have all this domain name API versioning if
it is there beforehand. So, I'm not writing that again and again. Cool. So, with that, if I write users here and use
get method, now I'm expecting some response from our server. I'm expecting either a list or a response from our
server. We'll come back to responses in a while, but this is how we can get some user information from our server. Now,
let's move to the second part. In this, we don't want all the users, we want a specific users. So, to find a specific
user, we have to provide them an ID. Normally, we provide them in the URL itself. So, it is get users and here we
can include the ID. So, in real time, it can be used as users/1. So, if we write one, the user with the
ID one will be retrieved. Now, with this, we can also add any user. So, the third part is
adding a user. So, again, the URL will be same users and now we are adding the user, so we are using the method post
here. So, in this with post, we are going to give some body, and in body, we will have some JSON which will store the
user information which we are trying to create. And based on our request, our user will be created in our database.
Now, once we have all the user details in our table, we may choose to update any user information, correct? So, for
that, we use another method which is put. Again, the URL will be same, users. And with this users, we will send the ID
of the user of which information we are trying to update. So, with put, we also have another method which is patch,
which can also be used in a similar way to update the user information. Now, what is the difference between put and
patch? It is simply that if we use put, our entire user object will be replaced with the new data. So, this is going to
replace the user data. And if we don't want to update all the fields, we want to update a specific field or particular
field, suppose username, name, age, or anything, so we can use patch. It can be used to partially update the user
record. Let's understand this with an example. Let's say we have any user information which have ID and have a
name and have a username, maybe age. This is all. So, these are all the values which are
user currently have. Let's we put some values. So, if our user table or user entity have these all four values, if we
use put to update a particular change only, let's say I'm going to change my username from Akshay to AK something.
So, now this change will only be done if we again send the entire body into the put request. If we only send username
like this, then all the other values of our entity will be updated with the default value. So, if we send this kind
of data in a put request, it is going to replace our entire entity. So, that means the name, the age will be replaced
by the default or null value. So, it is not recommended to use put entirely if you want to update your entity
partially. In those cases, always try to use patch. In patch, we're going to again update the particular information.
So, this is perfectly valid for a patch request. So, with this, let's move to the next part. The next kind of method
which we use is delete. In delete also, normally we pass the ID like this. So, if we write users and with {slash} we
write one, so that means the user with the ID one will be deleted. Now, this is the usual stuff. You will get this
information anywhere. Now, let me talk about something which is nested data. Let's understand nested data with an
example. Suppose you're creating a blog site. So, in blog site, we normally have our users who are going to read our
blogs. Then, we have blog, the actual content on the site. And on blog, the user can comment. So, we have clearly
three entities: users, blogs, and then comments. So, these three entities are interrelated. So, a user is going to
give comment on a particular blog. Users can't comment if there is no blog, correct? So, this is the way. Now, in
order to create APIs for this, we can create a users API for all the users' activity. So, all the users' activity
means we can use it for get, put, push, delete, and patch also. Now, this is simple because a single entity is there,
users, and all the operations are on that users only. Now, let's say we want to create an API on which user can add a
comment. Okay? So, for that, what we can do, we can either go from the blogs perspective. So, every blog have a
particular comment section, right? So, blogs, then here we can write ID. So, this ID is basically belong to the blog,
and with blog, we can write comments. And if we write get here, what it is going to do, it is going to give us all
the comments which are associated with the blogs with ID this. Correct? So, this is a nested relationship where
we're trying to access comments, but we are going via blogs. We can also do like this. Suppose we are on the user's page,
and we want to see all the comments that user have created. So, we can again create a get query
via users, where this is the user ID here, and we can see comments. So, we are dealing with two different entities
by creating the relationships between them. So, by this, we are going to get all the comments which are created by
the user with this ID. So, we can go to comments via blogs, as well, and via users, as well. So, this is nested.
Again, if you want to update any comment, delete any comment, we can use simply the comments path
where the user can enter the ID here of the comment. And they can use it to update or delete
that particular comment. So, here you can also ask me that why are we using this kind of nesting in URL? Why are we
not sending the data in the request? So, that is also possible. We can do like this. We can
go to this comments, and we can pass the request body, in which we'll pass the post ID to get all the comments
associated with this post. This is also possible, but this is not recommended. As there is a direct relationship
between two entities, we don't normally go for this body approach. We go for the URL approach because it is easy to
understand also. So, when do we go for the body approach? So, the body approach is used when we are trying to filter any
data. So, we understood two concepts here. First is the nesting data versus filtering. So, when to use which one? If
you have a clear relationship between two entities and it is clean, so you can go for nesting. And also in filtering,
apart from sending the data like this in post ID, you can also use the URLs like this, comments, and then
in query parameter, you use post ID. Now, this is again not recommended because there is a direct relationship
between comments and post. So, it should be a nested type of URL. So, if there are clear relationship, we can use
nesting. And if the relationship is complex, we can go for filtering. Also, the filtering will be used whenever we
are trying to do pagination or we are trying to filter particular records based on multiple ways like we can go
for any e-commerce page where we are seeing different products and we can filter our product based on our color,
price range, and all these things. So, those things normally go for query parameters as a URL, right? And we don't
go for nesting there because it will be complex to handle a long path with nesting data. So, the crux is if the
relationship is pretty straightforward and we will go with respect to the other entity, we'll go for nesting kind of
URLs. And if there are complex relationships or the filtering is used, we'll go for the query parameters. Now,
let's move to the next part. How do we pass the actual data to the endpoint? So, there are three ways. Either we can
use path, so we can integrate the IDs in the path, or we can use some query parameter, or we can use body. I have
given you glimpse of these all three in the previous section, but now let's talk about this specifically. So for path, it
is very specific that we will only pass the recommended IDs or maybe slugs or maybe that particular unique identifier
only. So this is unique identifier. It can be ID or slug. Slug is basically a short form of that particular post. If
you are using any blog site, so let's say the blog site the blog is about what is Java and in slug you can see
something like blog /what is Java like this. So this is our slug. Again, this slug will be unique throughout our
blogs. So the name of the URL I have also written blogs, it is wrong. Normally we address the URL in the form
of plurals. So it is going to access in multiple areas, multiple scenarios. So normally we don't go for blog, we go for
blogs like this. Cool. Now for query parameters, whenever we are filtering or maybe sorting, we can go for query
parameter. Suppose we are again scrolling through our blogs and in blogs we can write like sort ASC or we can
assign some numbers also. So ascending means sort ascending maybe by date. So whichever date they are created, they
will be sorted by that. Or maybe we can use DESC for decreasing order. So that the latest post will be pop up first.
With this again we can use the query parameter for filtering. So maybe we can use blogs again here and with that we'll
pass query parameter. So this is Q for maybe query search or search step. So anyone with is searching for Java and it
will give us a list of blogs for Java or which have Java in their name or their content or their category. So, this is
all about query parameters. Now, whenever we pass any information in query parameter and path, those
informations are going to be exposed, right? So, that means we can't pass any sensitive information into query
parameters or path. So, for that we have body. So, let's say we have a login path, and now we can pass username and
password in the body, which is not exposed at all. So, it is safe. So, the rules are pretty
clear when we use path, query params, and body. Now, with this let's see a full request in action. The first thing
is we will have our method. So, this is maybe a post method, and we are using some API of version three, maybe.
For version three, we are using users, and then we can also pass the metadata. Suppose content type we can pass, which
is application/json here, because we are passing the information in the form of JSON. We can
also use XML, or if you have latest information, we also have a new format in town, which is
tone. So, we can also use various kind of information, but because currently we are using JSON, so we can pass it like
this, and with this we have our body. In body, we can pass anything, maybe some data we are trying to add, and then
maybe username. So, like this we can pass the entire request or the data to our server. So, this is what? This is
our method. This is the path. This again is metadata, and this is our body. Now, once we have understood the request
correctly, it is time to understand the responses. So, in order to understand the response, first we need to
understand the response code. So, there are specific codes assigned to a specific type of response, which if the
user gives will clearly understand what they want to tell us. So, let me start with those status codes and then we will
move forward. So, these are all the response codes which we have to know. 200 stands for okay. So, this means
everything is working as expected. Don't worry about it. Then we have 201. 201 is basically used when there is a new
entity created in the database. So, this is created. Then we have 204. 204 is again saying that everything is working
fine, but I don't have anything to provide to you. Let's say we are deleting any entity, so it is
successfully deleted, but they don't have any response body to give to us. So, that means there is no content to
give to us, but the request is working fine. Then we have 301. 301 is basically used for redirects and 301 indicates
that it is a permanent redirect. And if the redirect is not permanent, then we use 302. That is temporary redirect.
Then we will come to the 400 series. So, all the 400 series basically says that there is some issue with the request we
are having. So, the 400 stands for bad request. That means some or the other data is not correctly mentioned in the
body. Either they have some validation issue, either either they are missing when they are required. Either they have
some other issues, but request which we are getting, the body doesn't have the correct information. So, in that case,
we normally give 400 bad request as a response. Then we have 401. 401 is given when someone is trying to access some
information for which they are not basically authorized. So, 401 is unauthorized. With that we have 403,
which is used for forbidden. Suppose on an LMS, you have not enrolled for a course and you want to access a
particular video or lesson part of that course which is not publicly accessible. So, in that case, you will receive 403
forbidden because you are not allowed to see that particular resource. With this, we have 404 page not found. This is
basically used whenever the URL is incorrect or trying to access some or the other resource which is not there
into our system. And at last, we have 500. So, 500 is the response which is given to a front end when there is
something wrong with the back end. So, this is related to back end. Some or the other error is there in the back end.
Maybe some syntax error, maybe some other error, some logic is missing, some data is missing, something is wrong with
back end. So, this is internal server error. There are many errors which we don't want users to see. So, those all
are handled by 500 because we don't want the user to understand what is the issue we are facing with our database
connection or anything. So, we will create logs into our system and through which we'll resolve the 500 error
issues. So, these are all the most commonly used response codes which we have to understand. So, now whenever we
are creating any APIs, if the response is completely fine, we will provide 200. If we are creating any entity, we will
provide 201. If we are deleting or performing some operations where we don't have to give any content or any
data or any body in the response, we will use 204. Then, if the permanent redirection is there, we will use 301.
If the redirection is for temporary basis, we will use 302. For any bad request in the body, we will use 400.
For authorization, we will use 401. For forbidden or the content which requires some special access, we will
give 403. 404 we will give when they are trying to access any resource which is not there and 500 we will give when
there is any issue in the internal server. So with this, let's move to the next part which is response body. So
normally in response body, what is allowed first we start with that. So we can return an array, right? Also, we can
return an object. With this also we can return data as nested objects. Now in most of the applications whenever you
are going through any YouTube tutorial or any course also sometimes when you are dealing with multiple responses,
let's say the responses for this is normally an array, right? But it is not recommended to handle the response as
array. It should always be wrapped in the form of an object. And if you want, you can use any other key for this users
and in the value you will give these responses. Now these can also be string or an object. Normally an object only of
all the users, but it should be handled like this. So this is the correct or I would say recommended way. So every
response body should be an object. And even if we're trying to give an array, we have to wrap it up into an object and
give us in the key value form. So this is a good practice and through this we'll achieve a more long-lasting code.
Why? Let's say today we are passing the user details only and tomorrow we understand that okay, we have to add
more fields. So we don't have to change much on the front end side or the receiving side, but we will simply
update our response, change the object here and maybe add some other field user
count and pass that information maybe 29. And with this we can end our object here. So we can pass as many
informations as we want, but if we have used a single array as a response, then we have to struggle
with it and we have to change the entire architecture of our response. So, it is always recommended to pass the response
always in the form of an object. So, let me write returning as array not recommended. Let me know in the comments
if you already knew about this or you are hearing this for the first time. So, with this, let me create a map of
request type and responses. So, let's say here we have request. Here are the responses
which we are going to receive. So, if the request type is get and we are trying to access many records, so maybe
we are trying to access users, it is going to give an array of records, right? So, it is normally going to give
200 as a response code and going to give an array. Now, we understand that we have to not give array directly, so it
will be wrapped in the form of an object. Now, the next request is also a get request where we want to access a
particular object. Let's say one. Now, you can think that we will give the user information directly, but again, it is
not recommended to give that directly. We will use the nested object for the same reason. So, in this object, we will
use the user as key and pass on the user information into this object. So, this is the actual user object, but we are
wrapping it into an another object because it will give us flexibility to pass some more information if we want
to. So, let me clear this up for now. So, normally, we are going to get 200 response with this kind of nested
objects. Also, it is possible that this ID is not there in our DB. In that case, we can also give 404 response. Now, if
we are adding anything, we will use post with the same URL, and normally it is going to give 201 response, which means
the entity is created. And also, we can use 200 response if you want. But, if we are using 201, normally we give the ID
or the new object as response. Now, here with post request, we are also giving the body, so it can also have 400
issues, right? 400 bad request issue. And also, if the format doesn't sync in, it can also give 500 issue. If the data
is coming properly and there is some issue in the back end, it can also give 500 easily. So, these are most common
response codes which you can have while working with post. Now, let's talk about delete. In delete also, we will pass
this URL with some ID, and this can also give 200 as a response. And also, if it is
deleted correctly and we don't want to give anybody in the response, we can go for 204. If this entity is not present,
maybe the ID they are using is one and we don't have any record with the ID one, we can have 404 or we can have 500
if there are any dependencies and which is not handled correctly by our back end. With this, you can also
see 401 here if you are unau- prized person to delete that particular record or you are not allowed to delete that
particular entity, you can also get 401. So, these are the most common request responses, and with that we have also
discussed what can be there in the response body. So, with this we have covered the entire request section and
the response part of the REST APIs, and I hope the information which you get from this video is going to help you in
your entire development career. Whenever you are dealing with REST APIs. In order to build any application, we have client
on this side, right? Who is going to interact with either a web application or a mobile device to interact with our
application. Now, with this we will have our server in this side. So, this is maybe our server. And if we keep the
model like this only, that means every time we restart our server, all the data which user is trying to access will be
lost. Because when server restarts, it clears all the data. But, the thing is we need to store that data somewhere,
right? So, for that we use database in order to store the information which the user may require next time when they log
in or see our application. Now, one thing is very clear that most of the application can't live without
databases. Now, in order to understand how do we store data, we have two approaches. First is the SQL approach,
and the other one is no SQL approach. So, SQL basically consists of RDBMS, which is relational database model, in
which we will have tables, rows, columns, and we will connect those tables based on different joins and
everything. And in no SQL, we have other methods. See, saying SQL and no SQL is like saying Java and no Java. That means
there are various options in no SQL. We can have our data as in the form of key value. Also, we can have column. We can
also have the data in the form of nodes, maybe. And we can store them in the form of graphs. Also, we can store the data
in the form of document. And here, if we talk about SQL, we have our tables in place, who is going to hold our data in
the form of entities. And there will be different relationships between different tables. Also, in order to do
in SQL manner, we have various softwares like post dress or we can go for MySQL. Right? Like this, we can have multiple
options here. For here also, we have multiple options like Cassandra or we can go for MongoDB. And based on this
selection, where we are going to do it SQL way or no SQL way, we are going to build the entire foundation of our work.
Like how our data, our important data is going to store in our database. So, in today's video, let's focus on SQL. And
in next video, we will focus the ways of no SQL. So, let's start. So, SQL basically stands for structured query
language. And SQL comes under the category of relational DB. Now, in relational DB, we are going to have
multiple entities and we are going to establish different type of relationships between those entities, if
required. So, we are going to store our data in the form of tables. So, we can loosely say that every table
is loosely an entity. It can either be a user entity or it can be comments or it can be post, if we are
building anything like blog site or something or it can be videos, if we are building YouTube. So, like this, we
segregate our different entities and then we will build the relationship between them. Now, if I draw one of
those tables, let's say we are building a user table. A user table can have multiple fields. That is equivalent to a
user entity can have multiple properties. For example, a user can have their unique ID. A user can also have
their first name and L name and maybe we also have their phone number. So, this is how a table is
formed, where we have multiple attributes of a table. These attributes are stored in columns and when we have a
complete record, let's say in this table, we can add a new record where I can add any record, let's say the ID is
one, the name is Akshay, last name is Agarwal, and phone number is, let's say, 987654
321. So, this is my number, this is a complete record. Now, this record is called as a row.
So, by getting any row from our table, we can get the entire information of that particular entity or of that
particular user. Normally, for the name of entities, we don't go for singular words because it will contain multiple
records. So, we will go for plural things, users. So, this is the standard, this is a constraint, this is not a
particular rule, but it is safer to go into plural things. So, for comments, we can go for comments, for post, we will
go for posts, for videos, also, we will go for videos. So, all these things are in plural. Why? Because a single table
will contain multiple records. It will be easier for us to fetch the data with that plural name because then we will
know that multiple entries are there and it is a convention to use plural things. So, with this, let's move to our next
topic, which is constraints. If we are building any application, we can have multiple users who are coming with
different backgrounds, different geography, and everything. So, it is our responsibility to make them enter the
data correctly. We will use validations in form, and in database, we can handle them with the use of constraints.
Because every wrong data and every faulty data is of no use. So, for that, let's dive into the constraints. So, the
first constraint is unique. Unique basically stands for a unique value throughout the table. For example, in
the user field, let's create the user table again. So, this was our user table, let's add one more column or one
more attribute, which is username. Now, we don't want two users to share the same username. This is a common thing
which we can see on Instagram also. If you're trying to create a new username and if that username is already taken,
you may get some suggestions with some extra characters or you can change the name entirely. Similarly, for Gmail
also, if the username is taken, they suggest you some more usernames with the extra characters. So, that means we can
say that the username is unique. Every user will have their unique usernames. So, in order to maintain that username
uniqueness, what we will do, either while creating the table or while adding the username field like we did, what we
do, we add the constraint unique to that particular username. So, that when any user comes to this entry and if they
write ABC, no other user can write ABC. If they want to use the same username again, they will not be added into our
databases. Why? Because this username field is unique. The next constraint is not null. So, not null basically
represent a value which can't be null. Null is the default value for any entity, right? So, whenever we want a
field to have a particular value either given by a system or server or given by our user, we want to have that value.
So, we can't have null in that. If we see in users table, the first name can easily come under the not null category.
So, we don't want our first name to be null because we are going to address our user with their first name. So, this is
a required field. The next constraint is primary key. So, our primary key acts as a unique identifier for that particular
entity. In our whole user table, there is no record which can share that particular value. So, if we find any
record with ID, let's say ID is equal to three, so only one record will come out of this. Okay? It can't give two records
because the ID cannot be shared between two records. So, it will give a unique record out of our users table. So, our
primary key basically helps us to track down to that particular record. And also, it helps us when we deal with
multiple tables because there we might need some ID to put into some other table as a foreign key. So, in that
concept also, primary key is used. With this, we can have a fourth option here, which is check. Check is basically used
in order to maintain the authenticity of any record. Let's say we are using a password. So, while creating a password
or while having a password, we might want our user to use certain constraints like the length should be greater than
eight or maybe it should have any numeric values or it can have one or two special characters also. So, all these
rules which we apply are applied in the DB for that particular column through the check constraint. Also, the other
example of this can be a mobile number or a phone number. So, a phone number will only contain numeric values. It
can't have any characters apart from that. So, this is also a check which we can apply for our phone number field.
Also, the checks can be applied to any field. Let's say for first name, we can't have any name whose length is less
than maybe two because A B C D, this can't be stored as a name. The shortest name I know is Om, right? It is also two
characters long. So, we want to have that check. So, all the checks are covered with the check constraint. Now,
with this, let's move to the other constraint, which is foreign key. So, let's
understand foreign key through an example. Let's say we are creating a blog site where we have users table
again. So, the users table is basically the authors table. Or let me rename it to author then.
So this contains the list of all the authors who are going to publish some or the other blogs. So there we have this
ID and then from this ID, let's say we only have two fields, first name and last name. We can have multiple
fields if you want. For the sake of this example, I'm creating this smaller. So let's say we have one and the author is
Akshat Agarwal. Let's say I have two authors. Okay. Now these two authors are not going to write
only single post. They are going to write multiple posts. So we will have to create a separate table for posts and in
that post we will have some ID. I'll have data or maybe content is the better word. So I'll change the name data to
content. And that content is provided by some or the other author. So here we will have author ID also. So now let's
say our post, first post, have some data about HTML and who wrote that post? Maybe Akshat. So for Akshat, I'm not
going to write the entire details about the author again into this table. So for that, what I will use, I'll use the
common identifier which is our ID. So our ID is our primary key here and which will be used as the foreign key here. So
this is our foreign key and we will simply write one. With this particular information, we are going to get that
okay, this author ID is one that means it is written by Akshat. Similarly, if we have multiple fields like this, let's
say post two is on AI maybe and it is created by Gaurav. So we don't have to write the entire
fields or entire information about Gaurav. Only the primary key will do. So from this two, we are going to again
recognize that it is written by our second author who is Gaurav. So this is the role of our foreign key to establish
the connections between two entities. With this, we also have a next constraint, which is default. So,
default basically stands for the area or the field where we can't have null. So, null is not an option. And we don't want
our user also to have that entry as a mandatory field. We can fill in for them. Let's say we are building anything
for subscription. And if user have not paid anything to get a subscription, they might be having free as the value
because they are currently using our free subscription. So, this is one case. The other case can be, let's say you are
into our learn.telesco.com, right? So, this is our LMS and we are creating them. So, in this, every new user or the
more number of users are going to be students only. And only few users are going to be managers or trainers or
maybe admin. So, those are few categories and most of them are going to be students. So, the default value of
role for every user here is student. Student/learner. So, either student or learner, we can
have as a default role. And with time, let's say one or the other student or learner got hired into Telesco only to
maintain the notes or maybe to teach, then we will update their role to trainer. This is how the system works
with default. So, these are all the constraints which we have in our databases. Now comes the next part of
our relational databases. One of the most important part is join. Join is basically used to associate two tables.
That means to manage relationships between multiple tables. And why do we do that? To basically maintain
relationships between different entities. So, let's see how many joins options do we have. The first and the
most common join option is one to many. So, we have a users table. We have ID here, and we have F name only.
Again, I'm going to use the same entry, one and two. And here, I can write any name. So, here are the two names, Akshay
and Gaurav. Two entries basically for this users table. And again, we are creating a blog site. So, that means
both Akshay can write multiple blogs, and Gaurav can also write multiple blogs. So, we will create another table
to maintain our blogs, which will have ID, the content, and the author ID. So, maybe the first blog, which is on HTML
again, is written by Akshay. The second blog is on AI, which is written by Gaurav. And then, there is third blog,
which is on system design, which is created by Akshay. So, here we have one to many relationships.
Because for a single record here, we have multiple options or multiple rows associated with that particular row. So,
this is one to many relationship. With this, if we reverse this, we can also have many to one relationship. So, what
we will do? Here, we were talking about users to blogs relationship. And here, we are talking about blogs relationship
with users. So, here we can safely say that multiple blogs are associated with the same user. So, here we can have
multiple blogs associated with the single user. So, this is many to one relationship. Now, let's move forward
for our third relationship, which is many to many. So, in order to understand many to many, let's again go to our LMS.
So, in LMS, we can have students. And we have courses here, right? Students also, we have ID maybe, and
only F name is here right now. And in course, we have ID, and then we can have the course name here. Now,
let's fill in some entries here. So, here we have students as Akshaya and Gaurav. And our courses are Master Java,
Master AI. Now, Akshaya as a student can enroll into multiple courses. So, we can sense that from students, we can have
multiple relationship for courses. And also, one course can have multiple students. And it is also possible that
in Master Java, we have both Akshaya and Gaurav. So, that means our students can enroll into same course. So,
whenever we see a situation like this where we can have many students or many records from single table associated
with the single record of another table, and vice versa is also true, we are going to get a many-to-many
relationship. So, in order to maintain multiple relationships, we have to have a
junction table in between. So, this is our students-courses table. And here, we will have a unique
ID of this particular record. Maybe this is required, maybe this is not, based on your queries and use cases. Here, we
will have students ID. And here, we will have course ID. And now, while entering the data, we can simply say the record
one, we have Akshaya enrolled in Master Java. Now, record number two, we have Akshaya
again enrolled in Master AI. Then, for the third record, we have Gaurav enrolled in Master Java. And the fourth
record, Gaurav enrolled in Master AI as well. So, with this junction table, we are going to figure out how many
students are enrolled in how many courses, and how many courses have how many students. I hope this makes sense.
This is a little confusing if you are starting, but this is how we maintain many-to-many relationships. With this,
we can have one more relationship, which is one-to-one. Suppose we are building a platform like YouTube or blog site which
also have podcast, which also have maybe video section. So, we have three kind of content, right? The first content is
text for blogs. The second content is audio, and the third kind of content can be video.
Now, the processing of all these contents are different. We can handle these multiple options by two ways.
Either create a common table named as contents. So, in content now we have three types. For video, we are
going to store MP4. For audio, we're going to store MP3 maybe. And for text, we're going to use the text which is
there in database. We can use varchar, long text, short text, maybe text. Now, there are three kind of content. We can
basically divide them by using a field which is type. So, in type we will have options like text, audio, or video. So,
here the first record can be on again HTML, which is a blog type text type. So, we will type one here or text
directly here. For the next content, it can be anything. Let's say the content is on improve focus, and in order to
make that content, we don't have a video. So, we have something like audio here. And for the third thing, we have
any video, let's say system design. And for this video, we have the type as
video. Now, if you have worked into systems with these type of content, the processing of each type is very
different. And if we curate all these data into a single table, it is going to be a really heavy one. Now, what are the
drawbacks of having a heavy table? That is, whenever we are trying to filter, we have to basically filter through each
and every content type and each and every record as well. So, instead of having all these kind of data into
single table, what we can do is we can segregate the data already into multiple tables and have those IDs as a foreign
key. So, the other approach is we will create a simple contents table where we will
have ID here again and maybe the content name. And then we will not have the entire
video or audio. What we will have, we will have the ID of that particular content. So, that means we are going to
create multiple tables, one for videos and one for audios and one for maybe blogs. They all will have a common ID.
With common ID, I mean their primary key and we will use the ID from videos, from audios and from blogs into this
particular table. So, we will not have all the data here. Here we will only have content ID maybe. And with this, we
can also have content type. So, this type will determine where we need to follow. So, this content table will only
contain all the fields which user can search. They can search for name, they can search for type, they can search for
maybe some keywords, some slugs. So, all these details will be there in our content field. And the actual data, the
actual content will not be in the contents field but in their respective tables. So, all the blogs will contain
all the blogs, all the audios will contain all the audio files, all the videos will contain all the video files.
So, now if we have one and type as video, we know that one is going to fetch some data
from this videos table where the ID is equal to one. So, this is one to one relationship.
Where we know that we are using a different entity to our entity and the relation between us is one to one.
Whenever we write one here, we are not going to fetch any other data and also we are not going to use the one
again in this table. So, this is one-to-one relationship. So, we have understood that in relational DB, we
have entities, right? And we join multiple entities through relations. That means every entity will have
particular set of properties, and then they will be joined, and we will get the data accordingly. Let's understand the
same thing with the example of posts and comments. So, suppose we are building a blog site, and in blog site, we have
some post, and then we have some comments. The post table will have some ID, unique identifier, then some content
associated with the post. Now, content can be anything, right? It can be images, it can again be text, or
sometimes it can be video, also. So, these all are the category of post. Now, we have different type of posts, but we
have to somehow pass those IDs and manage three tables for image, text, and videos. We can manage them in the same
table, as well, but that will increase our latency, because different kind of formats are curated into a single table.
That will increase the size, right? With content, we can also have content type, through which we will basically apply
some filters, if we want to. Now, if we talk about it, we already have four tables. One for images, one for videos,
one for text, and one for content. Now, we will have comments, as well. So, in order to have comments, we will have
some comment ID, and then we will have comment. Now, comment can also be a sub comment of a parent comment. So, that
means we can have a parent kind of comment ID, as well. Now, in order to handle comments, we have to handle
users, also. Now, in order to handle user, we need one more table. We will have ID, maybe name here. So, our number
again got increased to six. Now, in with this PID, we will also have user ID who is commenting on our post. So, in order
to maintain a simple post and comment section, we will have to have four to five to six tables, right? And
then we have to maintain the relationships as well. So, that means even for a small blog site, we will have
to maintain so much data. And when the size of the blogs increase, with that I mean this will get hard to scale. Now,
why I'm saying it is hard to scale? First, understand how can we scale any database? We have two options only,
right? First is by scaling them vertically, and the second option is by scaling them horizontally. In vertical
scaling, what do we do? We add the capacity to a single server. So, that means if we are using 4 GB data, we will
add more space into our server to have more data. We can only expand this kind of vertical scaling to a extent. After
that, we will have to use horizontal scaling. That means having different servers across different places. So, in
this case, we will have to maintain relationships between the different entities
in all of these databases. And this will be a tedious task. So, in order to avoid this, we can use NoSQL.
So, we can say the first advantage of using NoSQL databases is it is easy to scale both vertically and horizontally.
Because we will have to maintain a single document, and we can replicate it on different servers or even split them
into different partitions and pass it to different servers. We will understand about that also in a bit. Now, what is
the next issue? The next issue is if we are maintaining a table, we can't change the table again and again. We
have some fixed columns, right? We have some fixed attributes or columns. We can't add or remove different column
types in order to maintain different kind of data. Now, let's say we are going to handle the blog site, but this
time we are going for NoSQL database. In this, what we can do now, we can create again and content entity and we can
basically use any kind of content which we want. So, we'll create the first object and maybe we'll write something.
Let's say user ID is one, then we will write the content. Here, the content is an image, so let me pass some URL. https
maybe something something.jpg. Now, when we are adding next content, let's we can add it directly into the
same content document. So, what we'll do, we'll again use some user ID. Maybe this time it is two and in form of
content, now we have some textual data and we can have some text. Also, if you want to use it in a more refined way, if
you want to add some rows, we can use that, right? We can use user ID as three, then maybe heading as anything,
let's say Java, and then maybe we can have some description. This is description. And like this, we can have
as many fields as we want. So, that means in a single entity content, we can have different fields and also we can
have different number of fields in the same document. And if we go about relational database, we can't do that
there, right? So, in these kind of scenarios where the document structure is not fixed, this is basically called
schema-less, we need to go for NoSQL options because in SQL we don't have this option. So, that comes to our
second advantage of using NoSQL, that is we can handle schema-less objects as well. So, now let's discuss about third
point. So, in NoSQL up to now we have seen it is easy to scale, then it is schema-less and what makes NoSQL more
feasible is when we don't have to handle a lot of linking between different entities. That is we don't have to
maintain a lot of relationships between different entities and also every entity in a NoSQL database stands on its own.
Think of this as a key-value pairs. Let's say a course ID. ID is the key here and in the course content we can
have anything. We can have only name or we can have entire object. Or in third place we can have entire course with
lesson. Of course there are going to be nested lessons in a course structure. Again with lessons in fourth place we
can have course which will contain some lessons as a nested object and those lessons can also contain some comments.
Now all these things are possible in NoSQL which were not there in SQL. If we want to have similar structure in SQL we
have to maintain different entities and we have to maintain different relationships between them. Here we
don't have that struggle. So that makes our NoSQL work easy. So this is why we use NoSQL over SQL and with time it is
gaining popularity. For example in Netflix we use Apache Cassandra in order to maintain user activities. In Amazon
they use DynamoDB in order to scale their application. In Facebook or Meta they use HBase in order to maintain user
messaging and large-scale storage. In Uber they use MongoDB in order to handle flexible and real-time data. And with
that Twitter or X use Redis for caching and timeline purposes. So the conclusion is that NoSQL is also being used in
variety of systems nowadays. Let's distinguish it for a bit now. So when we should go for SQL and when we should go
for NoSQL. Whenever we are handling payments, we don't have so much variety there. So, payments can be easily
managed through SQL. So, with payments, we can also think about transactions or any other feature which needs
consistency where the data is not changing, the schema is not changing, and also we need to handle those
relationships perfectly. In all those areas where we need consistency, we can easily pick SQL. Now, when do we pick
NoSQL? When we are talking about scaling and also we can talk about speed because as in NoSQL, we have a single document
to search from whether in SQL, we have different entities or tables who are joined through one or more
relationships. So, we have to traverse multiple tables in a SQL database and in NoSQL, we don't have to do that. So, by
that nature, the speed of NoSQL database is faster than the speed of SQL one. And also, one of the core reasons to use
NoSQL is unstructured data. So, in other words, whenever the consistency is on more
priority than availability, we can easily pick SQL. And when availability and scalability is on more priority than
strict consistency, we can choose NoSQL. With this, I hope you all get why we use NoSQL databases also with SQL and in
what areas will we pick SQL over NoSQL and vice versa. Now that we have understood what NoSQL database is and
why do we use that, let's see some examples and application areas of NoSQL database. Now, the thing is in SQL, we
have only considered all the databases which will have tables, similar relationships, and in NoSQL, there can
be various ways in which we can store data because it is like saying Java and NoJava. In Java, we have Java and in no
Java, we can have many applications, right? So, like that, let's start by our first example, which is key-value pair.
Now, if you are a developer or developing front-end, back-end, or anything, you must have used cache. We
use the format of key and value whenever we are trying to insert some value in cache or maybe cookies or in data
storage. We use this kind of data. So, if we talk about key, the only feature which a key should have is that a key
should be unique. And for value, we can use any kind of value. We can use basically JSON or a string or maybe a
number. Maybe if you want to add blob, we can use that. We can also store it as byte array or any array for that matter.
We can also obviously have objects as well and we can also have the combinations of them. Like a list of
object can be stored in an array or an object can also have nested objects. And with that, obviously, it will be having
some strings, numbers, blob, byte array, or any of that values. Now, the two important points here are we don't have
to follow any structure because the NoSQL database follows schema-less structure. So, we don't have to follow
any structure. With that, we also don't have to maintain any relationships. Suppose, if you want to add post and
comments, we can simply use any key. Let's say post is the key here, post one maybe. And in order to have that object,
we can have any ID, post ID, or any content. So, these all things are key basically of this object. Post ID can be
a number. Post content can be anything. And with that, we can have comments and we can have array of objects in
comments. We can have multiple comments field also. Let's say it can have a comment ID and
the actual content of the comment. Like this, it can have multiple objects and this all can be done without any
relationships. So, this is a good example of key-value pair, which we normally use in our cache, cookies, or
any other places where we don't need to maintain any structure. Now, let's move to the second one, columnar DB. So, the
thing is whenever we are having any table, let's say we have student table, which have typical ID, a unique
identifier, the name, and then the marks. Maybe total marks of all the students. So, ID is one, name is Akshat,
and marks is maybe 90. With that, let's have one more record. The ID is two, name is Gaurav, and the marks maybe is
95. Now, what we have to do is we have to get the average marks of class. So, the students is basically maintaining a
class, we are assuming that, and all the marks, if we sum them, we will get the total marks, and if we divide them with
the total number of students, we will get the aggregated marks. Now, in order to do that, if we do it in a SQL manner,
the SQL manner basically reads the data from left to right. So, even if we don't require any name in our data, we have to
still read our table from left to right for each and every row. This is a heavy task, right? Because we don't need those
values. Here, we have only three columns. What if we have 17 columns? Then also we have to read all the rows
from left to right, and which is not mandatory. So, in order to fix this issue, we have
columnar DB. What columnar DB does is it gives us the flexibility to read the data column-wise. So, instead of reading
it row-wise, now we can read the data column-wise. That means if you want to deal with only marks, we can read only
marks data column-wise. And then we can aggregate all the marks. And if we want some other column, those can be also
read by column-wise. And how we can link them? We can link them with the common IDs. So, the common IDs will be there
whenever we are reading column-wise, and then it will be easier and faster through our unique identifier that is ID
here. Where we are reading the data, but column-wise instead of row-wise. The common examples of columnar DB are
Google BigQuery, Amazon Redshift, and Snowflake. So, all in all, if we have to read a lot
of data, we can go for columnar DB. So, it is good for analysis. And imagine writing the data into columnar DB, that
is going to take again a lot of time because we have to write also column-wise. So, that means we have to
first maintain an ID, and then the name will be written, and then the marks will be written, and then the other values if
we have them. Those will be also written. So, in order to read the value, it is useful. And when it comes to
write, it is going to take more time than a SQL database. The third kind of NoSQL database is graph DB. Imagine, we
have all seen those movies where detectives basically pinpoint many photos and join them with some thread or
something, right? So, like this only, we have graph DB where we have different nodes, and they are joined by the
threads which are called edge. So, every edge basically represent a common relationships between two nodes. Two
nodes are basically the entities, and edge signifies the relationship between them. Now, what is different in graph DB
is both nodes and edges can have properties. Suppose we are creating any node which is student, maybe, and which
contain all the student information. Now, we have another node for course, maybe, and we can name the relationship
anything. Maybe it is enrolled. Now, the enrolled can also have properties in it. It can contain year, it can contain
marks. It can contain year of passing maybe. And all of these things the edge or the relationship can also have. So,
this is what makes our system unique. Graph DBs are often used by data scientists to find patterns between
different scenarios. It works basically in the same level which we see in any detective movie as well. So, if you want
to work with graph DB, we can learn Gremlin or we can go for SparkQL. Also, we can use Cypher in
order to work with graph DB. As I said, it is used in huge companies whenever we are dealing with a huge amount of data
in order to face some patterns or basically analyze the customer behavior on our site. So, as we can see the two
entities are going to have some properties and also the relationship between them will also have some
properties. Also, two entities can have different relationships. And also it can be linked via different entities as
well. So, student basically studies in this college. We can write all the parameters of the studies edge also. And
college provides the course. So, in this provide relationship also we can have multiple properties. So, which makes our
graph DB complex. And also since we are dealing with multiple entities with properties and also multiple
relationship with different properties, it is going to be a bit slow. With this, let's move to our final and the most
famous NoSQL database which is document DB. So, in document DB we will have some document and in that document we can
write JSON and can store data with any length. We don't have to follow any structure, we don't have to follow any
schema. So, that is flexible in nature. Also, it is schema-less. So, similarly, document DB also don't have to follow
any relationship. At this point, if we think about this, the graph QL is kind of an exception on the borderline there
because we do have relationships between them and with relationships, we also have properties of those relationships,
which makes it entirely unique and different from no sequel and sequel both. But on a large level, it comes
under the category of no sequel only. The common examples of document DB is CouchDB and also MongoDB is widely used.
So, that is also an example of our document DB only. Our document DB is highly used in logging the systems
because we need flexible log structures. So, it is highly used in logging, logging the error, maybe logging the
warn messages, logging any kind of stuff. Also, it can be used to handle profile section because the profile
section can also contain different values for different users. Suppose some have given all the values, some have
given less values. So, that can be easily maintained through profile. And with that, if we talk about content like
content on any application, let's say Instagram, we have different kind of content. We can have image, we can have
video, or we can have some text. If we talk about Facebook here. Now, these different kind of posts or content don't
have to go to different tables. In document DB, we can basically create a single document and curate all of these
content into the same document, obviously with different attributes and values. So, again, as it is a no sequel
DB, it is going to be complex and the retrieval of any information will be faster. Easily, any no sequel databases
can be used for any kind of analysis. So, this is all about the types of no sequel databases. After understanding
about databases, one thing is very sure that each and every important information needs to be stored in
databases and will be retrieved from there itself. That means if we have any user here who is trying to access, let's
say telesco.com only from their mobile application, the request eventually go to our backend and from backend if we
are showing any information, it will be retrieved from the database and will be shown to our user. Now, the thing is for
every information we need to hit our database. So, even if we have a homepage which have,
let's say, three most popular courses aligned like this. This is our course one, course two, and course three. Maybe
the details of each course are visible. One, a thumbnail is visible. Then, with that the name is visible. Maybe the
price is also visible. And with price, the discounted price is also visible. Also, let's say we are showing some of
the important sections. So, yeah. These are all the details of every course which is visible on our homepage. Now,
this maybe is coming from any DNS because that's where we are saving any static files, remember? The name will be
stored in a table. Let's say the table name is course or courses. For price, we may have the prices in the courses table
itself, but according to area, according to the currency, the price keeps on changing. So, we will create a separate
tables for prices. So, the price is coming from price. Then, the discount price also based on the locality. So,
these both details are coming from the same place, prices table. And then, the important section. Important section may
have some criteria or some course content which we need to show. So, this maybe it is coming from courses section
only, but courses section normally have the course main course detail and then accordingly we can have different
sections. So this is maybe courses table and we can create some other table which will have all the information for
courses. We can name it as course info. So here also the things are coming from course info table.
So that means in order to show these three courses we are hitting 1 2 3 and 4 resources, three tables and one DNS. And
that means that for every user who is trying to access our site telesco.com, it is going to hit all of these four
resources again and again. So if the count increases to 10,000 maybe daily active users that means we are
going to hit the database and the DNS 10,000 times for each and every user. Also don't forget that the courses are
three. Maybe in future we will increase the number to six. So that means 10,000
requests multiplied by six that is 60,000 requests. In short, for every view we are hitting six courses
and from those six courses we are hitting these four sections. So for every user we are hitting 24 requests.
This is totally going to break our system because 24 requests means it will add latency to our home page. And in
these fast times where nobody wants to wait for any resources, these latency are eventually going to affect our
business. Also the more the latency the poor the performance. So this is clearly not a
good sign. We can't keep hitting our databases again and again in order to fetch the same information for each
user. We need some other mechanism. So this is where cache comes into the picture.
Now let's understand what cache is. Cache is nothing but a memory storage, which is away from databases and it can
either be in our backend or into our front end in order to load the resources faster. Now, how does it work? Let's
take the same example. Our user is trying to access telesco.com and is hitting backend
to get the information and backend eventually is hitting database to get the information. Now, once we understand
that these six courses are going to be static on our home page, right? So, that means we don't want to fetch this
information again and again from our databases. So, what will we do? We will eventually get that particular
information and put it onto our backend either or we can also put the same information on our frontend storage as
well. So, when the user again comes on telesco.com, we don't have to hit the entire database again and again. We have
that information handy on our backend or frontend or in both the areas. So, whenever we hit our home page again, we
can get those information directly through our cache, which is stored either on our frontend or on our
backend. Here, we are saying that our user is accessing mobile device. Maybe it is accessing our site through any web
application. So, there also we can handle cache, okay? These caches, which are on client
side, are normally referred as client side cache. And the data, which we stored in backend systems, are referred
as server side cache. In databases also, there is some mechanisms where they can also store the information ahead of
time, that is known as well. And we can get the most requested information from this data only. We don't need to hit our
database tables. Also, we can have cache in DB as well, which is known as database cache. Up to this point, we
have understand why do we need cache from a very high level point. Now, let's understand how can we implement the
caches in the real system. In order to understand them, first we have to make sure that we know some of the terms.
The first term which we need to know is cache hit. Now, what is cache hit? Cache hit basically means let's say we have
any request which is coming from our front end to back end. Let's say we have cache installed here and here is our
final DB. Now, whenever we are hitting the request and we found the data in the cache, that is known as a cache hit
scenario where the data is present in the cache and we don't need to hit our database. But, if the data which we are
trying to get is not in cache and we have to hit our database, that is known as cache miss. So, what is cache miss
here? Cache miss is again we are trying to access some data from our back end and we don't find the
data in our cache. Instead, we have to get to our database to fetch that particular data. So, from where we are
getting the data in cache miss? We are getting it from database. And from where we are getting the data in cache hit
situation? We are getting it from the cache itself. So, whenever we hit a cache hit kind of situation, our
responses will be faster. And similarly, in cache miss, our responses are going to be slower. With this, the next term
which we need to know is TTL. TTL basically means time to live. Time to live basically specifies for how long
the data is going to be in the cache. In other words, we can also say after how much time our data is going to expire
from cache. Up to now, I hope we have understood what cache really is. So, how do we store data in cache? We
store the data in cache in normally a key value form. And we get these data by using the keys, obviously, and by value,
we also have TTL with them. In order to check whether the time is expired for that particular key-value
pair or not. If it is expired, this key-value pair will be eliminated from our cache so that it makes room for
having more data. So, at this point, we understand that the cache works faster than our databases. Then, why don't we
get all the data from our databases and put it in cache so that every request which we make will be faster. If you
understand software development, you understand that I'm asking a silly question. But, if you are new, just hear
me out. Now, this is those kind of question where the answer lies in the question itself. We are saying that
cache is faster. Now, let's understand why it is faster. Because we are only collecting the information which is
frequently used. Okay, so that's why the size of our cache is very smaller than our DB. Because DB have all the
information which our site needs. So, that means the size of cache is limited. And we have to keep refreshing the data
in cache so that it performs well. Why do we need to refresh? Let's say today we have some campaign of maybe
selling our most sellable course, maybe Java we are selling on the let's go. We are saying mastering Java. So, you can
master Java. This is our most prominent course today. But, tomorrow we change our focus from Java to maybe AI. Let's
say most students are not interested to learn Java at that time. But, are interested to learn AI. At that time, we
have to replace the content of Java to AI. So that we can get this information
faster. Why it is faster? Because it is easy to find something in a limited space than in the entire database. That
is why cache is faster. And the second point is if let's say somehow we managed to create a large size of cache itself
and we managed to save our entire database into cache, in that case, every search query or everything which we are
trying to find is going to take a longer time into our cache itself. So, it defeats the whole purpose of having a
cache. That is to reduce the latency and to get the performance better. So, with this two points are very clear. First is
the size of our cache needs to be way smaller than the size of our DB so that we can get the responses very faster.
And now that we have size constraint, we need to keep updating the cache data in order to get the values which are
relevant to our users. Now, I think we are very sure what cache is, why the size is limited, why do we need to
refresh the content again and again. Now, it's time to go a bit deeper. Let's go into the strategies through which we
can update the cache data. The first strategy is read through cache. As the name suggests, read through cache
strategy is designed to handle the reads from the cache only, not the rights. So, let me create a diagram. Let's say this
is our server. Here we have our back end and our cache and with this we have our actual database here. So, read through
the query suggests that every time we have a request of getting any information, we are only going to read
through cache. We are not going to hit database. We are only going to read the information from cache. And if and only
if the data is not present in our cache, then only we will need to hit our database.
Again, for right, we are not going to write any value in our cache. We are directly going to write our data into
our database. So, in order to read, we are going to hit our cache, and in order to write, we are going to hit our
databases. Here, clearly, we can have two cases. The first case is cache hit. In case of cache hit, that
means we are trying to get some information, the back end is going to hit our cache, and cache have that
value, right? Cache returns that value to our back end system. So, this is good. This means that we are going to
have a faster retrieval. Then, we have our case two for cache miss. What happens here is our back end is again
going to ask our cache whether they have the information or not. And in this case, as this is cache miss, that means
we don't have that information into our cache. So, here, cache itself is going to ask to our database
and fetch that particular data. So, database is going to give that information to our cache, and then cache
will return the data again to BE. So, clearly, it adds a next level step to get the data from database, which is
going to make this request call slower. So, that means whenever we are reading any data, we are not going to go to
database directly. Our cache will be connecting to database and getting the value which is required for our back
end. And from cache, we are going to give that value again to back end in order to process it. So, here, if you
see what we are doing, we are not adding any recently added data directly to our cache. We are adding data directly to
our databases, and only the data which is getting retrieved from our users are going to be in our cache. Let's say,
from our back end, we are making an order placement, and that data we are not seeing that again. So, that means
that data is never going to come to our cache because no one is accessing that. Only the data which user is accessing
are going to come to our cache, which makes our cache always ready with the information which user wants. So, this
is all about RTC. Let's move to the second strategy, which is WTC. WTC stands for write through cache. As in
RTC, the strategy is only for reading, here this strategy is only for writing. So, that means we have again our client
application. Here we have our backend and our cache with our database. Now, whenever we are going to write any data,
we're going to write it into cache and then eventually from cache, the data is going to be there into our databases.
So, this means whenever we are writing any data, we're going to write it into our cache first and from cache, the data
is going to be written into databases finally. So, by doing this, our cache data is always going to be updated with
the latest value. Where it can be used? It can be used if we talk about stock values, where we always want to see the
latest data. And with this, it can be used to any other application where we want to see the latest data first. With
this, let's move to the third strategy, which is WAC. WAC basically stands for write around cache. So, up to now, we
have only talked about either read or write. With WAC, we're going to talk about read and write both into one
strategy. So, read around cache. So, when it comes to write, we are going to write the data from any query from our
client, it is going to call our backend services and from backend, we are directly going to write the data into
our databases. No cache is in between. So, that means all the writes will be directly dumped into our database. And
we haven't updated any cache here. So, this is about write. And with read, what we are doing, whenever we are reading
any data, we are going to read it always from cache. If the data is there in our cache, that is a cache hit situation,
then we're going to get this information directly. So, this is good. If we get any cache miss kind of situation, where
the data is not there in our cache, the cache is going to call database and update the value in the cache first and
then will give the value to our backend. So, this is how WAC works. You may think that it is a combination of RTC and WDC,
which is correct. So, in RTC and WDC, we are only talking about read and write respectively. But, in WAC, we are
talking about the entire flow of data. So, it is heavily used in applications like Twitter. Now, it is called X. So,
here the scenario is we have a lot of users who are going to create tweets. But, we can't directly create those
tweets and save them in our cache, right? And whenever the users are trying to access any tweet, at this point it is
decided to keep this tweet in cache memory because now it can be used by some other users as well. So, we don't
decide the importance of any tweet at the time of its creation. We decide the importance at the time of reading those
tweets, right? So, with WAC, we also have one more strategy, which is WBC. WBC basically stands for write back
cache, which basically means whenever we are trying to write any data, it is going to be written into our cache and
at this point our write operation is completed. We are going to write this data into our database, but this is an
async process. So, that means while writing our data, we don't care about consistency much, but we care about
speed. We want to get the update faster. So, whenever we write any data into our cache, it is going to give us the okay
response, maybe. And after that, the data will be dropped into our databases eventually. Now, as
this is async process, it is very possible that we may get some or other error here.
So, we have to handle this kind of scenario or strategy very carefully. And now, let's talk about read. While
reading the data, again, we are going to read the entire data from our cache itself, right? That is the whole point
of having a cache. So, as most of our data is directly written into cache, it is most likely to have the most updated
values. So, normally, it is going to give cache hit. And if we get cache miss kind of situation, so we are again going
to call our database, update our cache, and from cache, we are again going to read the value from our application. So,
as you can see the WAC and WBC are pretty common, but here we are using async to write the data. That means we
are trading off between speed and consistency. So, even if our data is not consistent, we are making sure that the
speed of application remains fast. Now, WBCs are basically used where we have a high write traffic. The most common
example of this can be any food delivery application. Let's say Swiggy or Zomato. So, in Zomato or Swiggy kind of
application, what normally happens is at a particular time, we may be handling multiple orders with multiple status.
And the status keeps on changing. So, instead of saving all the data changes into our database, we choose to save
them into cache. Because we know that the status of this particular order will change very fast. So, we use WAL here
because we need to update the information fast and also the user who have created the order are also going to
see the latest information through this cache only. And in the back process or in the backend or async manner, we are
going to keep updating our databases. So, this way it is going to handle million of request and status updates at
a particular time. So, up to now we have understood that how do we handle the data into our cache? But, we have only
added the data. We haven't deleted the data. So, now let's understand the cache eviction policies through which we are
going to remove the data which is not used by our cache. Here I'm writing policies here. We can easily use
strategies. I just wanted to sound little different. So, that's why I added policies.
Um just for fun, you may say. So, the first policy basically is LRU. LRU basically stands for least recent used.
So, as the name suggest, least recent used. That means the data which is not used
recently will be evicted from our cache. Suppose Apple launches a new iPhone which is 17 Pro
Max updated something something very acronym synonyms of Pro Max Ultra something. And now everyone is keep
looking for 17 Pro. No one is in today's time looking for 11. So, that means 11 is a stale data which was used earlier
by our cache maybe. But, in today's time we don't need to store that information into our cache because a very few clicks
were there who were still searching for 11. Now, with this let's talk about the second policy which is MRU, the exact
opposite of LRU. There we were saying least here we will say most recent used. So here we are
going to delete the data which is most recently used. This sound a bit strange, right? Why do we delete something which
is most recently used? But if you think about it, let's say you are surfing through an e-commerce
application and you already apply a particular coupon. So in that case we don't need that particular coupon into
our cache, right? So we will directly remove that. Also, if you think about it, we have streaming applications. For
example, let's say this YouTube only. So in YouTube's the streaming normally happens in sections. When you are
watching any video, let's say you have watched the video till here. At this point this data gets loaded.
And you may see a white line moving forward which denotes that the data up to that point is loaded right now. Let's
say currently we are at this point and this data is already loaded. Now at this point we don't need the previous video
information. This can easily be deleted from our cache at least. Because in this case the chances of me
going back into the video is very slow. So that's where we remove the most recently used data. So I hope you are
getting all the points. The next part, the next eviction policy is LFU. LFU basically stands for least frequently
used. So LFU basically means the data which is not frequently used will be evicted from our cache. What does that
mean? Let's say today I opened any e-com application and search for plant. Also, I am a gadget guy. I basically purchase
a lot of gadgets. So suppose I have a history of seeing any secondary screen also. With that I also purchase a lot of
clothes. So clothes section is also there. Now, between these three, I keep on seeing about the secondary screen and
I keep on seeing the clothes in one or the other manner, but the plant search I only did once. Maybe I saw some YouTube
video or liked some plant into some Instagram story of others. Only at that time I thought of purchasing a plant and
after that I didn't think about it again. So, that means the plant is the least
frequently used terms for my history of that e-commerce applications. So, now whenever we are going to evict any data,
it is going to be this. So, this is another strategy where we remove the least frequently used data from our
cache. After LFU, we have two standard ways of handling cache data or removing the cache data. The first strategy is
FIFO. We all know about it. FIFO basically stands for first in, first out. Suppose we have
assigned 180 MB to our cache and we don't want to exceed this limit. Now, whenever we are trying to add any
value and and if we have reached our limit, first data which we are going to remove is the first data which we
entered into the cache without thinking about anything, without thinking about frequency, without thinking about how
recently they are being used. We will simply remove the first added data from our cache. And with this we have the
last eviction policy which is LIFO. LIFO basically stands for last in, first out. You may think of a stack kind of
situation here, right? Where the last object which is inserted will be evicted first.
So, this is how LIFO works. So, in this what we will do, again we have 180 MB memory and here we I have written bytes.
Here it is bits. Don't worry about that. It doesn't matter. Now, in that 180 MB memory, I don't want to store any other
information. So, what information will I remove? I'll remove the most recent information which I added. Okay? So,
maybe this information which is added first will be there for the longest time, and
we will keep on removing this or this or this information in order to have more space for the added information. So,
this is about LIFO and FIFO. I have not added any application areas for LIFO and FIFO both. I want you to explore this a
bit and tell me in the comments. And with this, we have understood about caches, their eviction policies, and how
do we add the data into our cache. I hope you all get it. Up to now, we have understood how our request basically
flows through different components of our system and ultimately provide some information to our user. Right? We have
seen who our customer is. They're going to interact with either some web application or maybe some mobile
application. From where the request will flow to a server where our back-end code is, and
through the server, we will hit some or the other database in order to retrieve that information and provide it back to
the user. With this, we have also discussed about DNS through which our clients are going to get the address of
our server. And then, the cycle repeats. So, this is all we know up to now. This all looks very good, works very
efficiently up to a single point. Why? Because here we are using only single server, and a single server can scale
only up to certain limits. Suppose we are already using 128 GB of our server, and somehow our processes
are consuming this much resources. That means we are getting more requests than we were expecting. Maybe our app is
tracking attraction now from across the globe. And at that time, we have no choice but to scale our system. In order
to scale the system, this is the first step which you can think of. So, instead of having a single server,
we need to have multiple servers. Let's name them server one and server two. So, we have two servers. That means our
client need to give the call to either server one or server two. But, how our client is going to decide where to give
information to? This is where we need to discuss one more component which is load balancer. Now, let's make the diagram
again with load balancer. Now, what happens? Our client or the user is going to access the same information from any
mobile application or website, and they are going to hit DNS in order to get the IP address of server. Up to now, things
are not changing. Everything is pretty straightforward. But, now our DNS is not going to give the IP address of the
actual server, but it is going to give the IP address of load balancer. And then, the load balancer is connected to
multiple servers. Suppose this is server one, this is server two, this is server
three. And then, the load balancer basically decides to which server our request should go to, whether it should
go to S1, S2, or S3. And if it goes to S1, S2, and S3, they are basically going to connect to a database. Maybe they are
connected to same database, or there can be a possibility that S3 is managing the data in some other database, and through
migration, replication, or partitioning, we are handling the data between those databases as well. So, this is what the
actual picture of a microservice applications looks like. Where the load balancer is going to play a huge role in
order to pass those requests from client to server. Now, if we talk about load balancer in brief, our load balancer
mainly is going to perform two tasks. The first task is how do it give responses to the perfect server from
which it can get the response faster, right? So, the first thing is choosing the server. Also, if we are giving the
responsibility of choosing the server to load balancer, that means load balancer should know how our servers are
performing in the current situation. So that no server will be overheated and no server will be underutilized. So, the
second thing which load balancers are going to do is maintaining the health checks of our
server. So that whenever any new request comes to load balancer, they are the one who is going to decide whichever server
is working in a fine condition plus can handle that request pretty well. So now, let's discuss these both points
one by one. So, in order to choose a server, we need some algorithms, right? So, there are various algorithms and
hybrids also from which our load server decides where the request should go. So, let's discuss those algorithms. The
first algorithm here is round robin. Round robin is the most simplistic approach in order to pick the web server
for the next request. So, in round robin, what do we do? Suppose this is our load balancer and we have our server
one here, server two here, and server three here. Load balancer is now going to pick server one for the first
request. For second request, it is going to pick server two. For third request, it is going to pick server three. And
where will the fourth request go? It will again go to server one. Then five for server two and six for server three.
And this is how every request will keep generating and going to the next server which is in the list. So, there is no
much calculation needed here. We only need to know that we are following the sequence of S1, S2, and S3. And if the
current request is being processed by S2, the next request is going to be on S3. This is how round robin works. So,
that means round robin send request one by one, right? Apart from this, it doesn't check anything, which makes the
round robin very easy to implement. That means if you are using round robin algorithm, each server are going to
handle almost equal number of requests. So, we can say the distribution of requests are equal. And as in this
algorithm, we are not managing any state of any server, basically, we'll manage only a single counter from which we are
going to decide where the next request is going to be. And also, we need to manage where the current request is. So,
with simple implementation, we can easily set up round robin into a load balancer. But, there are some demerits
to it. What if server one, server two, server three doesn't have equal configurations? What if the RAM
configuration between them is different? What if the storage is different? What if the CPU power is different? Let's say
for S1, RAM is 4GB. For S2, it is 8GB. For S3, it is 16GB.
Again, the storage, let's say for server one, we are using maybe 100GB storage. For server two, we are using 200GB
storage. And for server three, we are using 500GB of storage. Also, the CPU, let's say it is 16 core, it is 32, it is
64 core. So, the configurations are entirely different. And what we are doing in round robin, we are not
checking any of these matrixes, but we are simply following the next counter. So, that means even if with uneven
configurations, the load on server one, server two, and server three are going to be same. And
this is unfair because the server three may do all the task very frequently and can sit idle. Whereas server one will
struggle to complete the task and it will be overloaded, maybe heat up, maybe stop working. So, this is the demerits
of using round robin. Now, let's jump to the second algorithm, which is geo-based, which basically states that
suppose this is our load balancer and we have our server one again and server two again. This is in India and this is in
US. So, if the majority of users are from India and some users are from US, that means they need to hit their
respective servers in order to process the information. That way, they will get the response faster
because if I'm sitting in India and I'm requesting any information from the Indian server, it will be faster to
process those information in the Indian server as well with respect to processing the same information through
US servers because the connections are going to take time and which will increase the latency of the application,
which the user doesn't want, right? So, this is what our second algorithm implies. It is based on geo addresses of
the particular user. So, here also we are not maintaining any matrices for server specifications. We are only
maintaining two matrices. The number one matrix is where the server is and the second matrix is from where are we
getting the request or we can say where the user is. Now, this algorithm is used by many applications across the world
whichever have the global appearance. Whenever we are trying to hit google.com or facebook.com or any other global
applications, our request basically moves to the server whichever is closest to us. Maybe we are residing in this
area. Here we have some server and also here we have some server. Maybe this is US server, this is Indian server.
So, our server needs to be situated near us so that we get the response faster and all the requests are maintained
through the same server. So, whenever I'm trying to access any information, they are going to be addressed through
the same server. So, that means our session and other informations are also will be in sync for this particular
user. In this algorithm, there are some disadvantages as well, right? Because now our load server needs to manage more
information. Now, it needs to know from where the requests are coming and it needs to know
where each and every server is located. So, this is a lot of information to be seen by load balancer. And now as our
servers are distributed across the globe, we also need to maintain databases accordingly. So, every region
may have their own databases for faster retrieval. So, that will include the computation power of replication and
partitioning, which we will be covering in further series. Also, let's say if we are using Indian server or this
server particularly, maybe there are two servers in India. This is India B server, this is India A server. For some
reason, this server breaks down. Then we need to reroute our request to this particular server, which is again
needs all the information which we are accessing through this server. That means the data or the application on
which we are working on should transfer the data immediately to this server as well. Even if the server A doesn't
crash, then we need all the information which is being tracked by server A. But this is a necessity in geo-based
location algorithm. Now, the third point here is if any user is using VPN or other proxy servers to change their
location, that means VPN or proxies can mislead the traffic. With this, let's move to our next algorithm, which is
least connection algorithm. So, in this, what happens is we have our load balancer in place.
And suppose we have two servers, server one and server two. And server one and server two at current times are managing
multiple requests, right? Suppose S1 is maintaining 30 requests or 30 connections at this
particular time and S2 is maintaining 50 connections. Now, whenever we get a new request, it
is going to be addressed by S1. Why? Because S1 currently is having least connections.
So, this is how our least connection algorithm works. Whichever server have the least connections, our next request
is going to that particular server. In this algorithm also, we are not considering any of the matrices of
server. We are also not maintaining any of the requests or response time. We are only maintaining the number of
connections. Let's say if these 30 requests are heavy request and these 50 are lightweight, then also the next
request is going to be taken care by S1, which is wrong and it is going to take time, but this is how our least
connections work. Then why do we use this? Basically, the least connection algos works very well when there are
sticky sessions. Now, what is sticky session? Where we have to maintain a proper channel for a particular
communication. Let's say we are using web sockets for maintaining any kind of application. Let's say we are
maintaining a chat application or a video streaming application or maybe we are handling some gaming kind of setup.
In all of these cases, we needs the request and response to be faster, so we will pass our request to those servers
who are having least connections currently so that we can get the response faster. Now, if we talk to
disadvantages, the first disadvantage is this only heavy and lightweight. Maybe these calls are for AI calls which are
going to take time and these calls are basically chat calls or maybe some web CTR calls or maybe some REST API calls
which are lightweighted normally. So, they are working faster then also we will send the next request to 30 until
the number of connections drops from 50 to maybe 29. Then only we will consider S2. So, this is how we are going to
work. Now, if we talk about disadvantages of least connections, the first we already discussed. The second
disadvantage is that our load balancer basically needs to maintain the number of connections for each web server. So,
if S1 is maintaining 50 connections at that time and it give response to one of them, that means currently it is
maintaining 49 connections. It needs to be updated to load balancer. So, our node balancer will keep updating the
number of connections again and again which is of course a tedious task. The third disadvantage is we are only
considering least number of connections. What if our server one is of very low configuration than server two
and it is maintaining eight connections at that time or request at that time and this is maintaining 16.
It is easy for S2 to maintain 16 connections or maybe 100 connections and it is hard to maintain 10 connections
for S1. Then also we will send the next request to S1 because we are not checking anything apart from number of
connections. So, this is how our least connection algorithm is going to work. After least connections come least time.
So, it is our fourth algorithm least time algo. Suppose load balancer is maintaining two web servers S1
and S2. The response time of S1 is nearly about 50 ms. The average response time, basically. It is handling some
thousand requests, and for server two, it is currently 10 ms. So, in this scenario, the next request is going to
be handled by server two. Because the average response time of server two is lesser than the average response time of
server one. Now, if we talk about response time, if any server is having less response time, that means the
configurations of those servers are high. Right? That's why the response time is low. So, this algorithm
basically takes care of the actual matrices of the server by taking the response time into measure. Now, the
least time algorithm is used in those applications where we can't handle more latency. That can be our real-time
applications, like the trading app or even our search engines. In both of these areas, we want the responses to be
very fast, and that's why we use least time algorithms there. And now, if we talk about least time algo's
disadvantage, that is our load balancer needs to calculate the average response time. So, with each request they are
sending to server, also the average response time will be calculated. And based on this, our load balancer needs
to manage where the next request is going to be. So, calculating these things can be really complex because if
we are calculating average time, that means we need to know number of maybe requests, and also the response time of
each and every response. Then only our load balancer is going to calculate the average response time. So, this is for
sure complex. Now, in this scenario, if our web servers maintain any kind of spike, it is going to affect the
calculations of our load balancer. So, we need to be very careful there. Now, let's talk about the fifth algorithm,
which is IP hash. Here, as the name suggests, the IP of every request is going to be processed by our load
balancer and will be given a unique number. And according to those numbers, our
servers are going to decide. Let's say our server one is maintaining the hash values from one to 10. So, that means
all the IPs which results between this range are going to be addressed by S1 and maybe from 11 to 20 are addressed by
S2. So, this is how IP hash algorithm works. Every request is associated with some of
the other IP. They are going to process through a hash function, and suppose the result is five, then it is going to be
addressed by S1 because S1 is maintaining one to 10. As we all know, the IP of any user normally doesn't
change. That means this user for at least one session is going to be addressed through the same server. And
it will help. Basically, if we are maintaining any session data, it is going to help because we don't need to
replicate this data to other servers. We are only handling these requests through the same server. So, it is very easy.
Now, if the connection breaks in between the session, then we are in problem. In that case, this next request will go to
S2, and we need all the session data here. And if that data is not maintained, our user may need to address
or add the same data again into server two, which is a loss case, which is not recommended at all. So, for this to work
efficiently, our server basically needs to be healthy enough to maintain a proper session. Plus, even if the
session breaks due to any reason, due to any server crash, it is very important to replicate those data to other server
as well. The major disadvantage of this is, as we are basically focused on IP functions and hash functions, so scaling
is challenging here. Let's say we are adding S3 here, then we again needs to reconfigure our hash function. So, that
it consider S3 as well. And also, we need to reassign these values also. Maybe next time it is only handling 1 to
8, and maybe it is handling uh 9 to 12 maybe, and this one is handling
12 to 20. So, whenever any server is down or we are adding any new server, basically the function will get affected
and it will reassign all the values to the entire server. This is hard to scale in this manner. And that's why it is not
recommended for most of the microservices architecture we have today. It is basically used where the
data was used in the old kind of applications where the applications were monolithic and mostly were legacy apps.
Like in today's scenario, if you want to maintain a session of a particular user, we don't only stick for that particular
session. We use many other tools like JWT token we use, right? So, this is how we are going to maintain an entire
connection through a server and a client. We don't only rely on giving the request to the same server again and
again. Even if the server changes, it is going to recognize us through our JWT token. And this is going to work
excellently with our distributed systems, microservices, and also if we use Redis.
In all these scenarios, we now don't need IP hash functions. We don't need our request to be handled through same
server. Even if the server changes, our request or our session should be consistent. Also, there is another
algorithm which is extension of round robin, which is called weighted round robin. So, in which the round robin, as
it was discussed, is working same, but we added weight. So, that means let's say this is S1, this is S2, and
this is S3. Let's say the RAM capacity of S1 is 8 GB, for S2 it is 16, and for S3 it is 32 GB. Now, what we are doing,
we are adding a weight to our request. So, more the configuration, more is going to be the weight. So, if
our load balancer basically keeps track of this, so it knows that the S3 may have weight three, S2 may have weight
two, and S1 may have weight one. So, whenever a new request arrives at load balancer, it is going to be addressed by
the one which have the most weight. So, here the weight of S3 is maximum, so it is going to be addressed by our S3
server. And then, based on the weight and round robin fashion, our request are going to be answered. So, with weighted
round robin algorithm also added in the list, we have now covered all the algorithms, major algorithms basically,
which we needs to know when we talk about load balancers. Also, in real base, no one algorithm basically works.
We always work with hybrid approach. And load balancer basically balances multiple algorithms. It is very possible
that we are using maybe IP hash with the combination of round robin. And also, other combinations are also possible.
Maybe we are combining two, maybe we are combining all six at the same time. Like here, we were talking about least time
algorithm and least connection algorithm. We can combine these two algorithms in order to get a better
algorithm. So, in order to fulfill our requirement, we normally don't rely on a single algorithm, we rely on multiple
hybrid algorithms. So, after discussing all the algorithms, we know now how the load balancer basically chooses a server
for the next request. The second thing which load balancer does is doing the health checks. Let's understand how load
balancer basically manages the health checks. Here the thing is we have two approaches how a load balancer can check
the health checks of entire servers. Let's say we have a load balancer here again and we have server one
and server two. The first way how we can check any performance is by simply seeing how server one and server two are
performing. We are not adding any new request in server one or server two. We are just observing how they are
responding to the actual requests. This is the passive way of checking the health. By this, we don't add any new
request to the servers. We don't basically increase the load of any server, but we simply observe. By we, I
mean load balancer. The load balancer simply observe how the server one is responding to request and maintain that
track. And let's say at a particular point, if server one breaks or stops responding, at that time the load
balancer knows about this by again tracking the actual request. It doesn't add any new request to server one. So,
this is what the passive way is. Now, there is another way which is active way. In active way, what a load balancer
does is it adds additional request to our servers. And based on the responses on those requests, it checks the actual
health of the server. So, these are the two ways of checking the health of servers. One by simply observing the
request and second by actively adding the request to the servers. It is very important to check the health of every
server. Why? Because it gives us the pattern to take decisions. Like when to scale or when to scale down. Both of
these cases are very usual by working on any application on a day-to-day basis. Let's suppose if we are maintaining our
application only for India region, in that case also we know that in day time the requests are going to be heavy.
Maybe in evening time it will be heavier. And in night time it is going to be little or lesser in count. So, at
that time we can maybe scale down our server in the night time in order to save some cost. So, let's see what are
the other parameters of health checks. The first parameter of health check is interval. This means how often do we
check our server? For example, it can be maybe 5 seconds. So, if the case is active, every 5 seconds I'll monitor all
the requests I've sent to the server and see how the performance is going. And also, in the passive case we'll see the
responses and calculate the matrices every 5 seconds. The second params can be time out. So, time out basically
means for how long do we wait to get the response? Suppose there is any server who normally takes from maybe 50
milliseconds to the maximum of maybe 5 seconds in order to process any request from a
long time. And now suddenly it is taking 10 seconds. So, this should be an alarming case and we should immediately
check what went wrong. Okay, with that particular web server. What is that particular request which is taking 10
seconds? Is there any issue with the code? Is there any issue with the request? Does Do we change any
configuration? Or does our server is managing some other task because of which our this request is being
affected? We need to know the answers of all of these questions in order to make a better decision. We also need to know
about the threshold. Threshold can be like healthy and unhealthy both, right? In unhealthy, we basically take care of
the failed calls. So, we will basically maybe set an alarm after 20 failed calls. Okay? We can configure them based
on a particular region, based on a particular area of our code, maybe a route, particular subdomain of a route,
or maybe randomly. At a particular time, if 20 calls fail, that is an alarming situation for us. In normal scenario,
many application may give a faulty response based on various factors. It happens on a day-to-day basis, but after
that n threshold, after that count of threshold, we should start investigating the cause. And similarly, we have our
healthy threshold, where we should put our marker maybe after every some calls. So, maybe after every 100 calls, 100
positive calls, we need to check is everything fine with those calls. The response was expected, and the system is
working the way it should work. So, these are the three health parameters of our web servers which our load balancer
needs to check end over time in order to make sure that our web servers are working fine. And with this, we now
understand the two major factors of load balancer, how it gives request to the other server, and plus, how do it check
the health of every server. Now that we have understood load balancer, it is very important to know that in terms of
scaling, we also need to scale our databases, right? So, in databases, what normally happens is, suppose we have 100
GB of data, and we have a storage of maybe 200 GB. This is still fine, right? Maybe we have 1 TB of data, maybe 100 TB
of data, it doesn't matter. The thing which matter is this data will eventually get full.
And there will come a stage where you can't expand your databases vertically. You have to add more databases into our
system whenever it comes to scaling. Also, now that we know about load balancers, we
know that our server can be placed into different regions of our world, right? This can be placed
into US region, this can be placed into India region only. And what we were doing is we were adding both of these
data into single database. This is effective if the user count is one, the request we are getting are minimal. But
if the user increases, the traffic increases, the data increases, it will be hard to cater both of these servers
from the single database. This is where we need to segregate this data into multiple databases. One may be
catering Indian server and the other might be catering the US server. Now, this way we segregated our data into two
databases so that the requests can be faster and also with that the data will be segregated in such a manner that it
will be easy to maintain. Now, this can be one of the case where we don't need to have same data into two databases. In
this case, what we are doing, we are assuming that the data in this data table will be different from the data
into this database. Normally, this is not the case, right? Normally, what can happen is suppose we have multiple
servers for Indian region only. So, this is also Indian region, this is also Indian region. And we can have multiple
databases to cater the same server. Why? Because if we have a single database and if that fails, we don't have anywhere to
go and recover the data from. That's why we manage multiple databases in order to recover the data, maybe maintain the
data, and also it increase the efficiency of our system by reducing the latency. And also we may have some
portion of data here and some portion of data here. Both of these things are possible. These
things are basically replication and partitioning. In replication, what we do, we basically
copy the data from one database to other database so that even if anything happens to database one, we are able to
retrieve the data from database two. Also, if database one is handling so much calls, we can redirect those
requests to database two as well. And in partitioning, what we do is we basically divide the data into single database and
put one part here into other database and the other part anywhere else. So that if they are getting different
requests, it will be easier to cater each and every request with their specific database. We are going to cover
both of these topics, but for now let's talk about replication. So we have understood that replication basically
means keeping the copy of data. And why are we doing this? Why are we making copies? The first point is we are
avoiding or improving our single point of failure issue. We don't want our database to fail and our system to
collapse. We want some other database at that time so that our system will be working. The other reason of this is
availability. By having data into multiple databases, our availability as a system can increase. We can have
different data centers and it will increase our performance. Also, as we discussed, if we talk about different
localities or different regions of area, we are increasing the performance by adding particular database into that
site. So that will keep the data close to our server and will be retrieved faster. And also with all this, we are
eventually increasing our read throughput. So suppose with a single database, we were catering maybe 10,000
requests per second. Now with two databases of same storage and data, we can cater two times of the previous one.
So this is how the three throughput will also increase. So after understanding what replication is and why do we use
it, let's talk about how can we achieve replication. So we are going to talk about three algorithms. The first
algorithm is single leader replication. That means there will be a database which will be our leader and from the
leader only we will replicate the data to other follower nodes or follower data centers.
Suppose there is any user who is maybe updating their profile picture. And when they are updating their profile picture,
the request from our client basically moves to a server and from server we are only sending that request to our leader
only. We don't directly communicate to our followers because it is the leader who is going to then replicate our data
to followers. So every request will go to our leader. To be specific, every write request will go to our leader and
then the data will be replicated to our followers. This is how a single leader approach works. Let's understand this
through an another diagram. This is our user. Maybe this is our leader and we have our F1 follower database and
follower two. Assume this as a timeline, okay? Now what we are going to do, suppose at this point the user is
requesting to update the profile picture. And this request will eventually go to our leader node and
once the request is taken care by leader, they will send the same request to our
follower nodes as well. Follower node one, maybe it is taking longer time because it is far. So at
this point the request went to follower one and follower two. At this point two things can happen.
First thing is if the calls from leader node to follower node are async, in that case leader will do its work and update
the user that the task is done. And in the back processes, the follower nodes can continue updating the profile
picture and will eventually update our leader that the task is done. This type of request where leader updates the user
directly after updating the data without getting the acknowledgement from these follower nodes is called async
operation. Async basically means once the request is catered by a leader, it will directly update our user. And in
the back end, it will also share the same request for follower one and follower two node to perform the same
task and update our leader in the back end. In this case, even if our F1 fails to update, F2 fails to update, or
anything went wrong with these other nodes, the leader have already given the confirmation to our user. So, the task
is done. And eventually, when the nodes are recovered, these operations will be performed again on F1 and F2 to keep the
sync alive. After async, let's also talk about sync approach, where the user updates the profile picture.
The request basically carried by our leader, and it sends the update request to our
follower nodes as well, F1 and F2. And the leader in this case will not give any confirmation until it
get the okay response from both of our followers. So, this gives okay, and again, this gives okay. Once it get
confirmation from both of these follower nodes, then only our database is going to update our server, and eventually,
our user that the task is done. So, this kind of approach is called sync approach, where the leader will provide
the same operations to be performed by F1 and F2. It will wait for the responses, and once the response is
taken, then only it will update the user. So, this is going to take a lot of time, because our databases can be
placed into multiple places. There can be two follower nodes, as we have mentioned here, or more. So, in that
case, our time will increase, and eventually, it will update the user after getting the confirmation from each
and every follower. Now, let's talk about advantages and disadvantages of both of these approaches. If we talk
about synchronized approach, sync approach, the advantage is all of the followers will always have the updated
values. The other advantage of sync can be, even if our leader fails, it will be easier to select another leader, because
every node we know have the current updated data. So, it will be easy to migrate our leader to any of the
follower nodes, maybe follower one, maybe follower two. The disadvantages will be it is going to take a longer
time and also after getting any request, our leader is waiting for the responses from other follower nodes. It is going
to block that particular resource and other operations on that particular resource is also be blocked because of
our asset principles, right? And because of these two disadvantages, we can say that the sync operation here while
replication is an imprac- tical approach. Any leader can't wait for all the followers to update them first and
then they will update the user. So, we always prefer async approach here. In async approach, as we can see
clearly, no resources are blocked, right? And the responses will also be faster. And if we talk about
disadvantages, we are basically trading off our consistency. That means our data will be
mismatched. Suppose if a user is updating any data to leader node and leader provides the response to user,
our user will assume that the task is done. So, even if they read from any other follower nodes, the data should be
reflected. And in the async approach, it is very possible that if user is trying to access the data and it get connected
to any follower nodes, it will see the stale data, which is eventually going to update by our leader, but currently they
are seeing the stale data. So, this is the issue in our async approach. Now, before moving to the other algorithms,
let's first talk about how can we add a new follower? So, let's say we already have a system
where we have a leader and we have maybe follower one and follower two already in our system. Now, we need to add our
follower three. How can we add it? Now, in order to add a new node into our system, we need to perform some
operations. Our follower three will also need to have all the data which our leader have.
So, for that, what we do, we create snapshot of the data from leader or maybe we can create it from follower as
well. But, we need to make sure that if we are picking follower two in order to do the replication, we will again need
to use F2 only to update the data. Let's say the current time is 12:55 p.m. and we are taking the snapshot of F1 or F2
at this point. So, that means whatever the state of our F2 is at this 12:55 p.m., we are only capturing the current
state of that particular database and we are that dumping it into new node, which is F3. Now, this process is going to
take time for sure. So, this is our snapshot one, which is at 2:55. So, this will contain the entire state of our F2
node. Now, what will happen again is suppose the replication process took 2 hours or 3 hours, maybe, because the
data is huge and everything. Now, we need to know what changes did the F2 went through in this 2 and 3
hours. So, in that case, what we will do, we will again take the snapshot, but this time we don't have to capture the
entire state. We only need to capture the difference between the snapshot and the current time. So, we will create
another snapshot, which will include the changes from 12:55 to current time. And finally, we are going to dump this data
also in F3. Once we get to know that the F3 is completely in sync with F2 and F2 is already in sync with leader, we will
make the connections of leader to our F3 also. And with this, we will have a new follower node into our system with all
the updated data. After this, what happens if the leader only fails? Let's say we are having this topology again,
leader, F1, and F2. Again, we have some databases linking. The replication process is working fine,
then suddenly our leader fails. In this case, we need to elect a new leader. Now, how will we elect?
Basically, we will take care of two factors. The first factor is which of these F1 and F2 have the latest
information. So, we can take care of that by seeing the timestamp of the latest update. So, whichever we will
see, we will pick from F1 and F2, and we will see which one is updated recently. And the second step is
we need to update F1 to become a leader, and our F2 to see F1 as a leader now. So, this is the process of electing a
new leader when the leader goes down. With this, you may have also heard these terms like FS image or maybe added logs.
These things are highly used in the replication process. FS image basically con- tains the full metadata snapshot,
and added logs is used to update the current changes. So, these both are also used while doing the replication. And
while choosing the new leader or adding a new follower, these two things are also helpful. Now that we understand how
to add new leader, how to add new follower, how to take care of things, let's move to our second algorithm,
which is multi-leader algorithm. So, as the name suggests, it is having multiple leaders instead of one. So, that means
there is a possibility that every data center, be it DS1 and DS2, will have their bunch of leaders and followers.
So, maybe for DS1, we have this leader, and we have follower like F1 here. Also, here we have another leader and
follower. So, for DS2, the leader is L2, and for DS1, the leader is L. Now, what is going to happen? Let's say the
user is maintaining their records through DS1. So, every record which DS1 handles will eventually first go to
their leader, and then the leader will update their nodes. With this, now leader also needs to update the other
leader into other data centers. And then this leader is going to update these nodes. This is how the process flows.
And similarly, if any request is being made to this data center two, our leader two is going to update their followers,
and also will send some query to other leader to update the leader as well as their followers. So, this is how a
multi-leader replication works, where the leader will not communicate to each and every nodes, but it will communicate
to leader nodes, and maybe their follower nodes. So, this is going to increase our performance. Also, we have
multiple leaders, that means even if a leader node breaks, we can use the other leaders to update the other nodes. So,
we have more tolerance to threats. Now, since we have two leaders, we can also perform collaborative operations, like
they can be working on the same file. Now, we can work on the same Google file, same Google sheet, those kind of
operation also we can perform, because one request will not block that resource, and the two requests can
eventually work simultaneously. Now, when we are saying that both of these resources are going to perform actions
on a similar table or single data, that is going to increase our chances of conflicts. Let me explain it through
another diagram. Let's say this is user one, this is leader one. Here we have leader two, and then we have another
user. Let me create the timelines again. Let's say, user one and user two both working on the same file, which is named
as A. Now, user one wants to rename that file to B, and user two want to rename that file to maybe C. Now, these both
will eventually hit their request to their particular leaders. And based on the async approaches we have seen, our
leaders can eventually give the okay response to our users, right? That okay, it is renamed. And here also, it can
give the response that okay, it is renamed. So, these two users will see two different data. This is not
possible, right? And also, our leader is going to communicate with each other. So, this will send the request to update
the data with C. And also, this leader will also send the same request to update it to B. Now, this is the
conflict, and how do we resolve it? Basically, there are three ways of resolving it. The first way of resolving
is the last right win. So, that is this right is performed, let's say, at this point, and this right is performed at
this point. So, as this operation is performed later, so that means our L2 needs to update our file to C. So,
eventually, the file name will be C. This is our first approach. The second approach here is while working with
different leaders, we assign some IDs to each and every leader. Suppose, leader one have ID 10, and leader two have ID
11. Now, based on the ID, we can decide which leader is more powerful, and which leader we have to follow. Ultimately, if
leader two is of higher ID, that means we have to follow leader two. And now, it doesn't matter if the last right was
performed by L1 or L2. It is going to update it with the value of L2 only. So, here, instead of the last right, we are
going to focus on the ID. So, we can say replica with higher ID wins. Replica is nothing but a replication. Like, every
node which have the replica of content can be called replica. So, we can write leader also here. Okay, no issues. The
third approach basically is the approach which we also see sometimes on Git when we try to solve merge requests. So, the
same approach is used by the replication process as well. So, whenever there are conflicting situations, our leaders or
any other nodes are not going to solve it. In this case, we will prompt our user directly that the file name has
been changed to C and the last change which you have made was B. So, do you want to keep the current changes or do
you want to update it to C? It is up to user. So, in this case, we need to add the application logic to basically pass
our conflict to the users and eventually user will update it. After this, the third algorithm is
leaderless replication. So, in this replication, we don't assign a specific leader to any data center or to any
other node. We don't have any kind of leader in this leaderless replication. As the name suggests correctly, so here,
what do we do? Here, let's say we have any client there. So, he is our user and now we have some databases. Let's say
this is our node one, this is our node two, this is our node three. So, these are the three databases onto which our
data needs to be replicated. So, this time whenever our user request for any update, let's say again this user is
updating the profile picture, but this time the request basically goes to all of the nodes and eventually all will
update the data and provides a response to the user. Here, the thing is in other applications where we had a leader, we
can basically rely on that leader replica and based on the response which we get from leader, we can eventually
decide that whether the operation is performed correctly or not. But here, we don't have any leader. So, what can we
do? Do we wait for all the replicas to answer us and at this point we update our user or there is some other method?
Let's talk about that in a bit, but before that, let's jot down some points. So, the first point here is we don't
have any leader here. So, here we are bound to send our queries to more than one replicas, right? More than one
nodes. So, every query, when I say every query, I mean both read as well as write queries, will go to multiple nodes and
then collaboratively we'll get the response. When I say read also, that means let's say in this database only,
if there is some other user who is also trying to access some values, and while accessing some values, the user can use
this replica at this point or this replica at this point or this replica at this point. In all of these three
scenarios, the user is going to get different values. So, here the user will get updated profile, and from here they
will get the stale data. So, based on multiple entries, we have to decide whether to give this user updated data
or the stale data. So, this is our struggle with this kind of replication. But, apart from the struggles which we
see here, most of the applications in databases use this setup. It is used by various applications like Amazon Dynamo,
Riak, Cassandra, VM. All of these use leaderless application. So, now the only issue is should we wait for each and
every node to give us confirmation so that we can proceed to tell any user any response or is there any other way? So,
as you may have guessed, definitely we can't wait for this long. There is a way. That way is basically termed as
quorum. So, in quorum, what do we do? Suppose we have total number of nodes as three. We denote them by N, and for
reading, we have to use more than half of the total nodes. So, it should be more than N by 2. Again, for writing, we
should get the confirmation from more than the half nodes. This way we maintain the quorum and we will get the
responses which we want for. So, in the same example, if I redraw it, suppose the user is again going to update any
information, so updating profile picture again maybe, and the request went like this for first node, for second node
maybe the network is faster here, and for the third node. Now, this is the time each node took in order to perform
the operation. So, here we don't have to wait for this response because we already got the responses from more than
the half of the nodes. So, we can easily say that profile picture is updated. So, here we can confirm that the operation
of updating profile picture is done. Again, if a new user appears here, and the user is trying to access the value,
so it can basically sends a read operation from these all nodes. Maybe at this point, maybe at this point. Suppose
this point gives a result here, then this point also gives a result here. So, in these two responses, the user is
getting different values because this is giving them stale data, and this is giving them updated data because the
update is already done here, right? So, in this case, there are various other mechanisms also. You can go for
timestamp, or they can go for any other mechanisms. But, let's say they don't go for any other mechanism. There is one
more mechanism here where that is if the requests are equal, and we are still getting the different data, we will
consider the third request and response, and then again calculate the results. So, here we are getting updated from two
of the nodes, and we are getting stale value from one of them. So, we will move ahead with the updated value, and this
user will get the updated profile picture. So, there can be various ways of solving this conflicts, but the thing
is ultimately the user don't have to wait for each and every response. This is a special case where half of the
nodes are saying that things are done, and half are saying things are not done. We don't have three servers also. We
have more than 10 or 12 servers in a large system. So, this case becomes a rare case. So, yeah, this is all about
the replication which we needs to know at this point. So, after replication, the other way of segregating our data
into multiple nodes in order to scale them properly is partition. Now, before moving to partition, first see what can
be the issue with replication. Replication basically suggests that we will copy entire data to other node.
What if the data is so huge that we can't have them in one node? With this, the second concern is what if the data
is so huge that even with secondary indexes, our time of searching any element is getting worse and worse. So,
with these two concerns, we can't really have all of the data in one database and same data in other database. What can we
do then? We can easily or through some logic break the system. We will do the partitioning of data and maybe place
some amount of data here and other amount of data in some other node. And then, we can replicate this partition
data. Why? Because this data is comparatively smaller and it will be easier to manage. Let's say, in a
particular node, we are only saying that we will store up to 10,000 records. And then, once we hit this point, we will
again create a new partition and will add the new records there. Or maybe divide these records into 5,000. So,
these all things can be done only when we have a new way of partitioning the data. So, this is what partition really
is. While partitioning the data, there is only a single rule. That is, if we are doing the partitions
like this, then if we combine them, it should give us the entire data set. This is the only condition which is required
for partition. And And makes sense, too. We don't want to lose any data, right? So, this is our first condition and the
second condition is that let's say we have again two partitions. Now, we want our data to be equally distributed among
them. We don't want any nodes to get let's say consumed by 90% of data and here we only have 10% of data because
this basically beats the reasoning of partitioning. We are doing partitioning so that every node will be able to
access some data and will be able to work frequently. So, basically we are saying that the data should be evenly
distributed. So, these are the two major conditions of partitioning. Apart from this, everything works fine. So, the
first condition makes sense. It will take care of itself and in order to fulfill second condition, we have to
basically maintain some system where we will protect the database to get overloaded or to get underutilized like
this. So, now let's talk about those solutions. So, the first thing here is we will partition using key. Let's say
we have some table, maybe user table. We are handling these entities and then we have some ID here, user one or user two
and the series goes like this. So, what we will do? We will basically in the partitioning based on the keys will
insert the values. Let's say this user entity can hold up to 10,000 or maybe 100,000 values. So, what we
will do? We will divide these 100,000 to 50,000 here and 50,000 here. So, that means from user one to user 50,000,
these data will go to the node one or we can say partition one and the users from user ID maybe 50,000 one to user ID
100,000 will go into partition two. So, this is how we will segregate our data so that it will be easier to
retrieve the data afterwards as well. This may looks fair on the surface value, but it is quite messy. What if
these users are from different regions? What if half of them from 1 to 50,000 are from India and half of them are from
US? Maybe here also they are distributed like this only. So, half of them are from India and half of them are from US.
So, in that case, both of these nodes will be busy. This is a good scenario because both of the nodes will be used
and will be busy in order to provide the information. And also it is possible that the node one is only taking care of
India request and the US request are being taken care by partition two or node two. So, in
this case what happens? Let's say the application is very popular in US, but is not that popular in India. So, maybe
P1 is getting maybe 100 requests per second and on the other hand our partition two is getting maybe 10,000
requests per second. Now, earlier this was looking good on the surface level, but now this is messy because one
partition here is clearly becoming the hotspot. Now, what is hotspot? A hotspot is that partition which is getting more
requests compared to other ones. So, it is our duty to maintain this hotspot as well. We don't want any node to become
hotspot because eventually a hotspot can leads to the node failure. And if that node fails, we have to basically
distribute the data into multiple nodes then and then do the configuration again or maybe change this node or maybe
transfer this data to other node. All the things we have to manage, but that thing is surely not recommended. So, we
avoid any node from becoming a hotspot. I hope this is making sense to you. So, one way of handling the hotspot is let's
say for every request, we also attach timestamp and if most of these requests are going to node one and very less are
going to node two, that means the data distribution which we did is not good. So, we have to rearrange our data with
some other methods. So, let's see what the other points are. The second approach here can be partitioning the
data with hash value of key. What we will do here is, let's say we have user one, user two, maybe user three as the
ID or unique identifier of the data. And now we have to put that data into different partitions. We will not here
choose ID, but we will use any hash function to basically transfer the ID to a unique number. Maybe this is one, this
is two. And user three belongs to maybe one again because of the hash function which we are using. So, based on this,
one and one will go to partition one and two will go here. Or maybe we are catering some range here, so maybe one
to 10 can go into one direction and 10 to 15 can go into other direction. So, this is also one way of doing the
partition. The challenge here is still same. If the request bombarded at P1, we don't have any other way, but we have to
rearrange like we discussed in the previous example. So, based on our discussion so far, the hotspot is
becoming a real issue. Before solving it for partitions, let's first see how do we solve this issue in a local DB. Let's
say we have a table for products. And in products, we have various attributes. Let's say we have some name and color or
maybe price. We can have more filter values, right? And like this, we can have as many attributes as we want. So,
name, color, price, material. All these attributes basically are used in order to filter these products. Name can be
used in query parameters. Other than that, color, price, and range can be used in filter easily. So, what we will
do in normal scenario? We will create indexes based on material, based on color, maybe based on price as well. I'm
not sure about it because price normally goes into a range, but we can easily make a secondary index out of this and
by that, whenever any user basically tries to search that particular product with material, maybe material is wood.
So, what will happen? We will not go to our table or this huge table directly. Where we will redirect this query to, it
is going to redirect it to our second query, which is maintained for material. And if that data or that value is here,
we will then redirect those records particularly to our display. So, this is how we manage the queries to find out if
we have the data or not, and if we have the data, where is that data particularly. This is how we solve it in
local DB or a single instance. Now, we are going to use the same way into partitioning as well. So, what we are
going to do? We are going to do the partitioning by the secondary index. What do we mean by this is, we are going
to maintain secondary indexes in each partition. So, that means all the secondary indexes will be present in
each partition respectively. This way is also used by Cassandra and also by Elastic Search. What we are going to do
here is, let's say this is our partition one, which have some data. Maybe this partition have car data from ID, let's
say 200 to 400. And we have partition two, where also we have some car data from ID, let's say 500 to 700. Here, we
are maintaining some secondary indexes. Maybe we are maintaining color as a secondary index. And for color, we know
that the blue color is for car maybe with ID 209 and 305. Similarly, for red, we can say the ID is 350
or 359, anything, right? For P2 also, we are maintaining it like this only. Again, for color, we know
that the blue color cars are maybe ID 509 and 609 and red cars are 610 690. So,
these are the indexing which is done in both of these partitions. So, whenever a query comes for a blue car, we don't
have to scan to all the records, but we can directly see the blue car here belongs to 209 and 305. And here, we
have blue car for 509 and 609 only. Let's say a new request come for silver car. So, we don't have to basically
indulge into all the records, but by seeing secondary indexes on color only, we will get that this partition doesn't
have any car which is of silver color. So, we don't scan this partition entirely and this request can now go to
some other partition. So, this is how we are maintaining the request so that our data doesn't get overloaded with a lot
of request, but based on the request, if they are present in our secondary indexes, then only we will entertain
them and show them the records or fetch some records, otherwise, we will directly skip them. Now, this kind of
setup also have its challenges, right? Because partitioning is done, but secondary indexes are also present in
each partition. That means even if the partition doesn't have any value, we still have to send a request to that
particular partition. This is again a drawback of the system which can make the system heavy, which can send the
request to many partitions where the data is not there and we are still getting the request. So, this is also
wrong. So, that's why we have a fourth and final way of solving this, which is by having common global index. What we
will do here is instead of having secondary indexes in each and every partition, we will choose a partition or
any data center where we will maintain the secondary indexes. So, if we are saying that we want the car with color
blue, we should know that blue car colors, according to the previous examples, are only 209305
and here we have 509 and 609. And basically, this belongs to our P1 and this belongs to our P2.
So, we should know this first-handedly. So, that whenever anyone is requesting to get any information about blue color
car, we will directly redirect it to P1 and P2. And suppose we have some other partitions, P3, P4, and others, we don't
send the request there. So, that these partitions can take care of other requests. So, this is our fourth way of
maintaining the global index. So, with this, definitely, we are going to improve our reading, but the writing
becomes a challenge. Why? Because even if we are working with partition 79, where we are adding some values, and
again have to update that, "Okay, we have some new car, maybe 1011, which is of blue color." So, like this, it is
handled. So, these are the ways of handling partitioning into our system. After understanding about partitioning,
replication, and load balancing, we now know that we need to segregate our data into multiple nodes in order to scale
them. We can't scale them into a single database after an extent. So, after understanding this, let's see what CAP
theorem basically says. CAP theorem is one of those theorems which we have to know whenever you think
about trade-offs between capacity and availability. So, first see what CAP theorem really stands for. In CAP
theorem, there are three terms basically. C stands for consistency, whereas A stands for availability, and P
stands for partition tolerance. So, CAP theorem basically states that you can only provide two of these three
variables, which means if we talk about consistency, what does it mean? Consistency basically means providing
latest information to every user. So, consistency stands for latest information. Now, if we are providing
latest information, let's see what it really means. Let's say this is our partition one,
this is our partition two. Some data is updated on partition one, and someone is reading that data
immediately after the update. So, this update from partition one and partition two will also take some time. And at
that time, we basically show something like system is loading or data is loading kind of thing to user until the
change is completed on P2. Then only we can show the latest data. So, this means that we will trade off our availability
of that particular information for consistency. The second thing is availability. Availability basically
states that we will always respond to every request. Now, what does this mean? In the same scenario, if it is taking
time to upload a data from B1 to B2, and while updating someone request in between, so they will not see loading
icon. Instead, they will see the stale data. So, this is what availability means. Now, the third term, which is
partitioning. Partitioning, we all know now we need to divide our data into multiple nodes, right? Now, with
partition, we can also see tolerance here. What does it mean? Let's say there are some nodes attached or linked to
each other. Now, this is partition tolerant when the network of this node is completely broken. So, this can't be
updated from any other method. We can either wait to basically fix this partition connection and update the
value or we can keep it working to provide the information. But, then the information we are going to provide is
going to be stale data. Why? Because the connections between this node and the other nodes are broken. Now, let's talk
about the combinations one by one. So, first combination is CA, which means we are saying that our data
will be consistent and it will be available also. In this case, we can't provide the partitioning part. Because
if we are saying that our data is consistent and available, that means we are maintaining a single database. We
can't basically have two data centers or two nodes and think about consistency and availability at the same time.
Suppose we have updated any data here, it is going to take at least some fraction of time to update the data to
other node. So, in that meantime, if someone request about that particular data, we can't decide basically whether
to show that data or node. Because if we show that data, that means we are compromising our consistency there. And
if we don't show that data and wait for the update, that means we are compromising with the availability. So,
this combination is only possible when we are working with a single database or a single node. We can't do partitioning
here. With this, let's move to our second combination, which is CP. CP means we are choosing
consistency over availability. So, here we are saying consistency and partitioning will be there, but we'll be
trade-off availability. Now, we have understood this term, let me take the example again. These are our two nodes
and the partition happens. Basically, the connection between these nodes break. Now, this doesn't have the
updated value. Now, when we are choosing consistency, we have to let go of the availability. So, we are saying no
request will be processed by this node. Until when? Until it gets updated with the latest data. So, this is what
happens when we are working in a partitioned environment and choose consistency over availability. That
means the requests which are calling this node will be redirected to some other nodes which have the latest or
consistent data. With this, let's see the third combination as well, which is AP. In AP, we are providing availability
to every request, right? So, we are providing availability with partitioning. So, what is going to
happen here? When there are two nodes and the partition tolerance happen, that means this node doesn't have updated no
updates. At that time also, because we have chosen availability over consistency, we will keep addressing the
request by showing them the stale data. And that means we are compromising with consistency. So, why are we learning
this CAP theorem? The CAP theorem basically states that you have to do some trade-offs in order to keep your
application working. Suppose we are working on Instagram. In Instagram, the availability matters over consistency.
Suppose you have updated a video or a photo, it doesn't matter for your followers to see that photo immediately.
They can wait for 1 or 2 seconds basically. So, in Instagram, we will be following AP. But in any application,
let's say banking application, where each transaction matters to us, where each change matters to us. So, if I have
sent 100 rupees to any other account, the person will have to wait in order to process that transaction, and after that
only they will be able to see. So, here CP matters. And for CA, CA will be there if we have a single data. That means for
every small application, CA can work. But when the system scales, we don't have an option to pick CA. That's when
we have to choose between consistency and availability. So, this is what CAP theorem is. I hope you all understand
it. Now, let's understand one of the most important component of our system design, which is message queue. Now,
before going into depth of message queue, let's first understand the type of request we deal with. We normally
deal with two type of request. Or we can also say requests or maybe processes. Some requests are sync. That means
whenever the request is received, we have to immediately provide them some output or response for the system to
keep working. On the other hand, we have async calls, requests, or processes in which we get the request, but there is
no hurry for our responses. Also, it is also possible that we don't give any response at all. So, so that means our
system only takes the request and basically fulfill the requirement in the backend
processes. So, in case of sync type of calls, requests, or processes, we can't do anything. Our system needs to be
concentrated on these requests and have to provide the responses as soon as possible, which we have discussed
throughout these courses. Now comes the second part, which are async requests. So, let's understand async process or
request with some example. Let's say we are an e-commerce application. We have some store, maybe some product is there,
and finally someone purchased this particular product. So, at the time of purchase, what are the operations we
need to perform? First is, we need to send some email to the user that order is confirmed and here are your details.
We have to also update the inventory so that if, let's say, only one product is left, that product should immediately be
out of stock so that there will be no confusion for any other record. Other than this, we also have to inform our
delivery partner, right? So, that they will start packaging and moving the order further. Now, out of these three
processes or request, which do you think is the most immediate action? So, let's see. Updating inventory is one of the
crucial task which have to be performed immediately. We can't wait for it, right? Otherwise, it can create
blunders. So, this is definitely a synchronous task. But, other than that, we can use some third-party email
service to process the email, right? We don't have to send the emails immediately. Even if the email goes,
let's say, 1 minute late, that is also fine, and our system will be keep working.
Similarly, for delivery, we can say that delivery also is an async operation because we are not in any hurry. Also,
our system doesn't need to take care of delivery, as well. So, we will simply pass this information to any delivery
service, and our current system can basically focus on some other task. So, this is how a distributed environment
works. Basically, we have multiple services who communicate with each other in order to fulfill the requirement. So,
if the services are different, and we have to use, let's say, email, with email, we can also use
SMS service, or maybe WhatsApp service. We can use them, and these all are async kind of calls. We don't have to stop our
system in order to process this request. So, I hope up to now we have understood about async and sync kind of request,
and in async, why we can use some other services, and for sync, we have to wait for the response in order to show some
immediate data, like out of service product. Now, let's talk about some of the examples for sync. Every financial
application can be an example here. Wherever we are concerned about security, we can't rely on async kind of
operation. We have to basically provide the communication immediately. The third area can be dependent. Wherever there is
a dependency on one or the other task to be performed after a particular task in a synchronous manner, we have to
complete this task in a synchronous manner. So that the dependent processes can also
work. For example, we can't process any emails for order details before processing the order. Once the order is
processed and confirmed, then only we can put down the emails or other notifications. So after this, let's talk
about async also. Async request basically works wherever we need to simply fire our request and our system
can then forget about that. So this is the golden rule to understand what system is async or what request is async
versus sync. Whenever we get the feeling or we get the components where we can simply fire a request and then our
system can forget about that, we can easily move to async calls, which will not only speed our systems but also free
our system in order to work on different tasks. So some examples on free and forget can be broadcasting. Broadcasting
can be of anything like email broadcasting, even the matches, the live matches which we see online on any
application, there also we can feel some latency. The latency can be from 0 seconds to 7 or 8 or 10 seconds also
based on the network connections you have. So broadcasting we have discussed, email we can write here. Also we have
seen SMS or WhatsApp. Basically any communication
can be held asynchronously. Also the OTP applications. In all the OTP application, there is a timer and that
timer is mostly more than 30 seconds. Why? Because in 30 seconds our system can handle this request in an async
manner. So, now we have understood sync and async, let's talk about message queues. In order to understand message
queue, let's take the same example. So, this is our e-commerce application and we are using the service for emails.
Now, on our e-commerce application, we are getting some order and based on that order, we have to send emails.
But, in between, we will implant message queue. In diagrams, message queue is denoted like this because it is
basically a queue and each of these order request or email request can be hold in the message queue
and then through our message queues, it can go directly to our email services. So, what our application needs to do, it
will simply dump all the email request to our message queue and from there, the message queue will take care of those
requests. It is like a buffer or we can say broker in between two apps or services. Now, whichever party or
application is putting the request into message queues are known as producers and whichever service is taking the
request from message queues are called consumers. With this, producers can also be called publishers and consumers can
also be called subscribers because publishers are publishing the message to message queue and then subscribers are
the message who are subscribed to take those request from our message queue. So, this is how a message queue
basically works. You may think now that why are we using message queue? Can't we directly call the consumer or the email
service from our application? Yes, we can call, but that will keep our system busy. We don't want to keep our system
busy and we also want that whatever request we are sending should be taken care by. So, these two things can only
be done by a message queue. Now, if we talk about qualities of message queue, before writing the qualities, I just
want to say that if you still don't get what message queues are, just bear with me for some minutes and you will be
absolutely get why do we use message queues. So, the first quality here is that our message queues can basically
hold the request. With holding the request, our message queues will have to communicate those requests to our
servers. So, they can communicate to servers or we can say services here. With this, why do we use message queue?
The reason is it can also be used to handle the failed calls. And with this, one of the major reason of using message
queue is handles the load which we are giving to the third party or the service application here, email service. At this
point, let's talk about direct communication again. If we again send the request directly from our e-commerce
application to email services, that means every time a new order is placed, this call is going to be there. If our
email server is down or anything, that call is going to handle the failure case also. That will be responsibility of our
e-commerce application. But now, since we are using message queue, these both queries and these both troubles will be
handled by our message queue itself. So, we don't have to care about the failed request also. So now, what happens is
there are various ways of sending request from message queue to the server. Normally, our message queues
follows FIFO. We all know about FIFO. FIFO basically means first-in, first-out. That means whatever request
comes first will be delivered first to our consumer. And this is how the series will follow. So, in this case, the first
request will be taken care and then after that second, after then third, and so forth.
So, this is what the first-in, first-out principle is and it is used by our message queue in order to process the
request. So, in FIFO, we have two approaches. First is strict order approach, which basically
means, let's say this is our message queue again, and while processing the orders or
requests, let's say first process is processed by our consumer, done. After that, we'll come to the second
one. So, the second one is also processed. Then, while processing the third
request, it breaks down. So, maybe something is wrong with the request or maybe our consumer is down for some
time. Because of any reason, our request was not processed. So, if we are following strict order, even if
anything fails at this level, our queue will not proceed. Our consumer, even if it starts working again, can't process
the fourth request. So, this is not recommended at all because of obvious reasons, because we want our process to
keep continue even if one or other request fails. So, the second part solve this issue,
which is unordered queue. So, in unordered queue, what we'll do, we'll again create like this only structure of
our message queue. So, every time our consumer or subscriber takes back our request, so one, two, and
even if there is some issue with the third request, our system will jump to the other request in order to fulfill
that. So, our consumer will get the request to perform the operations. Our system will not break. What will happen
to the third request? Let's talk about that in a bit. So, after unordered queue, it is also possible that the
things which we are getting in request in the message queues, maybe our first request, second request, third
request, and fourth request, between all of them, our fourth request is like very important to us, is of high
priority, and maybe second request is also of high priority, but not first and third. So, in this case we can't follow
FIFO principle, right? So, our consumer needs to get the second and the fourth request first. This is where we used
priority queue. What we will do, we will attest a priority
like number or some ID to every call. And whose priority is higher, those calls will be picked first by the
consumers. So, in this structure if we talk about if the priority of every call is set, let's say the priority of first
call is 10, the priority of second call is maybe three, the priority of third call is eight, and priority of fourth
call is one. So, that means the first call which our C is going to cater is fourth one.
After fourth, it will cater the second call. After second, it will cater the third call, and after third, it can
cater the first call. Also, these numbers are in ascending order. I have created uh my system in that way that
one will be the most important priority request, but you can reverse the order as well. So, this is how our priority
queue is going to take request. After understanding priority queue, there are two more concepts here.
So, the one concept is pull request. So, in pull request or pull-based request, what happens is whenever we have some
data in our queue, let's again put 1 2 3 or some random values. Now, these requests are in queue, which means that
the queue message queue will take care of them. But, the consumer here will decide when
to pick the request. Maybe our consumer is working with some other task, and the moment it gets free, it can pull the
request. So, message queue will not send the request to consumers. Our consumer will pull the request accordingly.
Again, the reverse of this is push-based request. In push-based request, what happens is the opposite of pull-based
request, which means if we have some request in our queue, our message queue is going to start the communication with
consumer and going to push our request to the consumers. So, here the communication starts from message queue,
and in the previous case of pull-based, it will start with consumer. Also, I can change the
uh direction of our request. So, because it is pulling, so request will come from here. So, I hope up to
now the things are clear. Now, let's talk about pub/sub models. So, in pub/sub kind of architecture,
what normally happens is we have publishers here who are like sending the message to our message queue.
We have message queue in between, and here we can have multiple subscribers as well. So, what we are
going to do, these publishers are going to send some or the other message to our message queue, which will be taken care
by our message queue by providing them to the subscribers. Maybe this is using push, and this is using pull. So, this
is how the system works in pub/sub model. Pub/sub is nothing but the understanding of our message queues
only. So, in this model, we include subscribers and publishers as well. Now, as
discussed in the previous example, our message queue needs to perform some operations in pub/sub model as well.
First thing is it will hold the request. It will send these requests one by one to the subscribers. And also, in case of
any failed response, message queue needs to take care of that. With this, our message queue is also responsible in a
way to take care of subscribers as well. By taking care, I mean our message queue needs to check subscriber is working or
not. If it is not working, our message queue will not send the request to that particular subscriber, and it will
redirect the request to maybe some other subscriber. This is how the things work. So, our message queue basically
maintains or the whole state of the request as well. Send the request to subscribers. Take
care of the failed request by using DLQ. We will see that in a minute. And also take care of the subscribers health.
Also keep checking whether the subscriber is it still alive to work or not. We can also say that it's still
alive or not. We can also say that our message queue takes care of request and takes the response from subscribers as
well. So, that means our message queue know which subscriber is working fine or fast and which is not working that
delicately. So, every time we get a priority queue or a priority task, it will be sent to the subscriber who is
working efficiently. So, this is what Pub/Sub basically is. Let's talk about some of the factors which can affect
Pub/Sub. The first thing which you can talk about is message order. Let's talk about message order. So, we are saying
that all the calls which are going to message queue are async calls. Then why do we need to maintain order? Okay.
First thing is that even if we want to maintain some order, we know that we can use priority queue.
And this is the only way of maintaining any kind of order in message queue. There is no other way. Maybe it is
handled by our application or maybe we can add some additional information of order in the request itself. This is how
we can handle message orders. Other than that, it is for async calls where the order doesn't matter. The second thing
we can talk about is how our messages are going to be consumed. So, we know that publishers publish the message and
they will store in message queue and consumers are going to consume this. But how they are going to consume this? We
are not maintaining any order. That means these consumptions are random. Maybe out of 100 request, the consumer
can pick up the 100 requests first. Even if we are using priority queue, we can make the priority of 100 requests first.
So, there is no particular order in which the requests are going to be fulfilled.
Consumers can pick any of the request anytime. Also, message queue can send any of the request anytime based on the
priority either or it is completely random. Let's talk about the third factor, which is poison message. So, all
the poison messages means all the messages that subscribers are not able to process. Maybe because they are bad
requests or maybe invalid. It is also possible that they are failing like after multiple returns as well. Also, if
we think about it, let's say a request failed at the consumer side. And then, if we send those requests again and
again, those are going to use or consume the consumers energy and will do nothing. Those requests are basically
our poison request who are taking time and resource from our consumer as well as message queue but are not able to
fulfill. Now, at this point, let's talk about DLQ. So, this is our message queue. This is our publisher or
producer, we can say. And here, we have our consumer or subscriber. Now, let's say we have a request which has not been
processed by our subscriber due to any of the reasons. So, in this case, our message queue can't stop. It will send
the next request. But, this first request will go to a different queue for retry or to maintain that this request
was failed. This queue basically, which is maintained by our message queue only, is called DLQ. DLQ stands for dead queue
or dead letter queue. So, our message queue maintains this DLQ in order to keep track of the failed request as
well. So, that even if the publisher have put the async operations, eventually
they will have to know that these cases are failed. So, we will see the failed logs or failed requests through any
logs, from which we will see, "Okay, this request was failed." Or other than that, we can also send
some or the other information to our publisher in a async manner or in a report kind of manner, where we will
give the stats of these many requests are passed, these many requests are failed. And here are the reasons of the
failed request. So, this DLQ is the mechanisms through which our message queue will take care of the failed
requests as well. I hope you get it now. Also, poison. Poison means any request which can't be processed, which is using
or consuming the resources, but can't be processed. So, with this, let's move to our four
factor. Fourth factor basically is duplicacy. So, if, let's say, our message queue have processed this
request when it is already processed by subscriber one, then it should not travel to subscriber two by any other
way. Because the request is already processed, it should not be processed
again. What if this request contains some important information which can't be done twice? So, in order to maintain
duplicacy, our message queue only takes care of this thing. So, with taking care of each request and
dead request, it is also duty of message queue that whenever a process is done, it should be deleted from our message
queue, so that we don't perform any operation on that particular request. It should be deleted. And if configured,
our message queue keeps end track of this, that there was some request which was obtained from P1 producer
and was handled by our S1 subscriber. So, this is how our message queue will take care of the duplicacy of the
request as well. After discussing all these things, let's talk about some of the use cases. Like where we can use
message Q easily and where we should avoid using message Q. Let's say if we are working on any async kind of
environment, any async operations, any async request, we can easily go for message Q. There is no doubt about that.
Similarly, if we talk about any decoupling activity, which can be like taking care of user analytics we want to
see and capture, maybe we want to capture where our user lands, if our order is failing, why they are failing,
and all the log systems. We can easily do this with our message Q as well. There is no doubt about this.
The third area can be our load balancing acts. So, if we think about it, our message Q does everything what a load
balancer does, right? So, our message Qs can be used as load balancers as well. Where it will take care of all the
request, it will keep them in a Q, and it will provide those requests to servers respectively.
Also, we can use the message Q with this with the deferred system. Where we have some operations, but we can basically do
them later, like generating reports and everything. After every day, we can generate some reports. Or maybe there
can be any schedule processes for doing anything. Let's say we are adding products into our catalogs like whole
day, and in the evening we want them to be live. So, we will schedule a process in order to work with them. So, the
message Qs can be used with load balancer as well as they can be used by our deferred system. Now, message Qs are
a costly service. So, if we don't have a lot of request, we should not use message Qs, right? Because it is a
costly service, and why waste our cost when we can use them in a synchronous manner? Or maybe if the request count is
low, we will directly call the subscriber from producers. Like for emailing, we will directly send the
request for our email service. With this, if we are using any real-time application, we can't use message queues
basically. Why? Because we need the response to be in a synchronous manner. We need the response to be immediate.
And if we are using message queue, our request can take some time to basically respond from any email
service or any other services which we are using. So, in these cases, we should avoid using message queues. And also, we
should avoid using message queues whenever we need some acknowledgement. Because if we need some acknowledgement,
that means our server or our system is waiting for the some response to get. But message queue normally doesn't do
it. In this example, where we were sending some reports, this will be also done by some or the other intervention
of our code only, so that we can see the processes. But if we want response from every request, it can't be done by
message queue. So, whenever we need acknowledgement, we don't use message queues here. And with this, we have
covered about message queues. Now, let's talk about faults. Whenever we think about faults or error, we basically,
while building any system or while building any software as well, we have to take care of each and every fault and
error. Now, while studying system design, we have encountered very components and we know what the
complexity our software also holds. So, let's talk about faults and error. So, our faults or errors can be called as in
any condition, if our system is not responding in a way it should respond, that means we have some issue or some
fault or some error with our system. We can easily categorize them into three categories. The first category is
hardware. So, hardware faults are basically belongs to our server. Maybe we are using databases, so DB can also
crash. We are using message queues, we are using each and every components, and
every component can fail our system if they stops working. So, this hardware issue or hardware fault is our first
type, and the nature of our hardware failures is it can be random, right? Like our system can fail entirely
anytime. There can be various reasons of its failure. We can have like a disk is full or something out of memory issue,
or let's say our server is down. We also use network cables, so maybe they are damaged. Maybe there is an issue with
the power supply. Maybe one or the other node is not configured correctly, or maybe it is experiencing some spike in
request, so it is down because of overheating. Also, we are using databases, so database can also be
compromised, right? These all things are basically the fault of our hardware. We do have some control over it, like we
can pass some alerts whenever we are sensing any risk out of them, but they are like inevitable. Whenever a hardware
fails, it mostly fails abruptly. That's why we use multiple systems in databases as well, and in terms of servers as
well, so that if any server crashes or any databases goes down, we can use our system by using the other
database or other server, maybe. So, this is what the hardware issue is. Now, let's talk about the second category of
our issues, which is software issue. Now, our software issue can be caused through anything. Maybe the code which
we are working on is written badly. Maybe we have forgot to handle any exception or anything. Maybe while
testing our application, we tested all of the cases, but there are some edge cases which comes to our picture when
the user actually uses the application. So, there can be some edge cases. Also, while coding in microservices or using
APIs, we also configure them into our configurations. Maybe there can be some issue with the configuration itself that
our system stops responding or maybe not responding in a way which it should be responding. Other than that, we can have
some deployment issues as well. There can be some issues with the development environment and the production
environment mismatch. So, something is working on production but not working on our development or vice versa. These
things come on a day-to-day basis in the life of software developers. So, don't worry about this but we have to take
care of this and this is our software issue or software fault we can say. With this, if I have to write any other
thing, uh one of the major pain points can be merge requests. So, let's say two developers working on similar feature or
same file parallelly and when the code compiles of both of them, we need to thoroughly test both of the
functionalities so that it doesn't break down each other on the production or on staging or on any environment. So, this
is one of the most pain point thing of any developer. Other than that, our software fault or software issue can be,
let's say, our requests are taking too much time. So, the performance is also something which software should take
care of. But the good and the bad thing about software issues is that it is not random. There has to be something in the
configuration or code file which we can find out and solve that issue. Also, this is not random, this is a good thing
so that we can find out the issue. But with not random, it is also deterministic which means by following
the same steps, we can get that proper error again and again. And if that error is not fixed sooner, that can basically
break our system or give a bad experience to our users. So, we need to fix software issues very
carefully and ASAP. Now, the third fault is not really a fault but it comes into the category of fault and is considered
the most important faults in our system which is human fault. We know that every human is unpredictable and we don't have
any control over ourselves. We can do basically anything. So, this kind of issue or error can cause unpredictably.
Why? Because humans are considered to be most unreliable components of software development among all.
Every component, whenever they are failing or not working, give us an alert beforehand. But with humans, it is
sometimes possible, sometimes not possible based on the value of that particular developer or anyone attached
to the software development cycle. But with saying these all things about developers or humans, we also need to
know that it is one of the most powerful component or person in the software development life cycle. If human stops
working, nothing is going to work. Even if the code is written by AI agents or AI anything, Claude, OpenAI, or any
other open source, it is still needs to be rectified by human only. Because we need some accountability over that code
because that code is going to build a product which is going to be used by humans only. So, we need human in
between all these processes. So, this is one of the most powerful thing. That means that makes us responsible for
handling the code even more. So, these are the three categories in which we divide our faults normally into. The
software issue, the hardware issue, and the human fault or human error. Human issues can be solved by having good
behavior or not doing the band-aid fixes or quick fixes in the wrong manner, but going deep into the system and fixing
the thing entirely for each and every area. Also, by software issues, we should not write bad code, handle
exceptions properly, see the edge cases, maybe do testing thoroughly of each and every component every time you make a
build. It can be solved, right? Also, deployment issues can be solved if you configure each and every environment
variable carefully. There can be other configurations for databases or for other APIs which can be configured on
development environment as well in order to replicate that issue and eventually solve it. Now, if we talk about
hardwares, hardwares are something which we don't have any control on. So, if any hardware is going down, it is mostly
unpredictable. So, this is also a danger point. That's why we have to scale our system in order to handle the system
perfectly or in a better manner. So, this is all about faults and errors. Now, let's understand about the last
topic of system design, which is most important but many playlists skip this one. This is our monitoring and
observability. We all know that in our product life cycle, our product is once developed will be deployed to any of
these servers and then will be maintained. Now, creating a product is entirely different from maintaining that
product. Here, we don't have to maybe change a lot of code and add lot of features, but we have to keep monitoring
whether our product is working fine or not. So, we have to take care of these following things. Are there any errors
in our system or not? Is every component basically is working fine or not? How's the health of each and every server,
every component, every database? If there are coming edge cases or any errors in our system, are we recording
them correctly? Do we have logs in place so that each and every customer request can be tracked and can be seen whether
they are working fine or not. And if there is any error, can we like go to the RCA of that particular error so that
our developer can debug and perform the required operation. And also, if any other component is going down or
basically down at this particular moment, do we have other systems in place who will take care of the system?
And also, do we have proper alert systems so that we can rectify or get these messages beforehand so that we can
take care of the situation in a better way. So we need to take care of all of these things plus more in the
maintenance phase or while maintaining our application. For example, let's talk about microservices. So one microservice
can do two operations, right? Either it can handle a request and work on it or the other thing it can do is it can even
redirect the request to any other microservices. So in this case, we have to monitor two things. The first thing
is how our APIs are performing and while handling any request or whenever a microservice or a server is working on
anything, we need to also check how it is affecting our machine. Like how much memory it is consuming, what
are the consequences of this particular request if it gets overloaded or something. So we have to take care of
the machine as well as the APIs at the same time. Now let's talk about both of them one by one. So if we talk about API
monitoring, the first thing we need to check is our throughput. Now what is throughput? Throughput basically means
how many requests can our server handle. Let's say our server can handle 10k requests per second. So that means if
our server is already hitting 9k or maybe 8k responses, we have to configure some alerts in order to know whether we
are going to hit our throughput or not and maybe we'll migrate the data or the request to some other servers at the
particular time because we need this server to perform or to be alive. The other thing which we can monitor while
using the APIs can be our error codes. Like we all know about 500 issues and 400 issues and maybe we are using some
redirects also so 300 issues as well. We need to know how many 500s are we getting, how many 400 requests are we
getting, how many 300 requests are we getting. So the first thing is when we are getting too many error things, we
need to know the RCA of it. So, that means our logging should be in places so that our developer can work. One thing
is that. Also, we need to put some alert whenever the count increases after a particular point so that we can get to
know about the 500 issues or 400 issues or 300 issues and fix them accordingly. Third thing is our health check. So, in
order to check health of any components, we have already seen that we can do it by two ways, passive and active. So, we
have to constantly monitor the 200 responses as well and see if our component is in a working condition or
not and is giving the positive replies or not. The fourth thing is to check our latency. Latency is a very good metrics
in order to check the performance of any system. How can we check the latency? Suppose we have some 10 requests and
suppose these values are the response time of those APIs. Now, we need to know the response time of our particular
server, of our particular API system. So, how do we calculate them? If we use, let's say, average, the average gives
the wrong value because, let's say, our average value for this will come to 6.8 seconds, right? But, if we see most of
our requests are being handled in a much lower rate. So, this average value basically gives a wrong impression of
our services. So, apart from average, we check P values. What is P? P basically stands for percentile. So, what is a
percentile? Percentile basically says if we are writing P90, we are saying that our 90% of the request can be handled in
a particular seconds or maybe milliseconds, whatever the time is. In our current example, if we arrange them
in an ascending order, something like this, we can see that we have 10 requests and P90 will be calculated here
and all of the requests like 90% of the requests will take lesser time than this time. So, this is our 12 seconds maybe.
So, let me also remove this milliseconds for a bit. 12 seconds should not be idle time for any API response. This is just
for the example, okay? Similarly, if we talk about let's say P50 here, P50 will be 4 seconds, right? So, that means 50%
of our request are currently covered under 4 seconds. So, we can see that P50 is 4 second and somehow P90 is 12
second, that means something should be off between these two. We have to check all the APIs which are giving us the
response of near 12 seconds. And we have to basically optimize them and improve the performance of our entire system.
Again, these P90, P50, P70, P maybe 99.9, when we have a large system and a lot of
calls, so all these matrices and values percentiles help us in jot down how our system is working in a majority portion.
And also, we can optimize the performance of our system by checking the differences of different P values.
So, it is very useful in case of optimizing our code as well and also, we need to know how our system is
eventually working. So, this is what percentile is used for and we use it to calculate the latency of our project.
Again, here also, we can provide some alert system. Let's say our P90 here is 12 second and if our responses are
taking 50 second or something, we should get an additional alert because this is an unusual behavior for us now. So, this
is how we take care of latency of our system as well. So, after APIs, let's talk about machine monitoring. By
machine, I mean hardware, right? The factors of measuring how our hardwares are working, the first factor if we talk
about it can be our CPU usage, like how much power is being consumed by that particular operation or that particular
hardware while doing some particular operation. And if that usage basically exceeds any limit, let's say I'm taking
example of 75, maybe you can take 70, 90 accordingly. We should get an alert that your CPU usage is already attained a
75%. So you need to take care of the systems. Again, the other thing can be memory usage. Let's say already we have
consumed 90% of the memory. So we know now that our database will not be able to hold more values. So we
need to immediately either scale our system or we need to see whether we are getting some error or not. Similarly, we
have a lot of input-output operations as well. So we have to take care of disk IO operations as well as network also. In
both of these cases, we can set the limit and once the limit is exceed, we can send alerts accordingly. So this is
all about monitoring our APIs and machine. Now, let me give you a quick recap of all the things which we have
discussed so far. We have discussed about system design, what is system design, what are the different
components of system design. Then we have discussed about data intensive and compute intensive applications, then
functionality and non-functionality requirements, what is DNS, APIs. We have also understood REST APIs in details.
After that we move on to DB, our SQL and no SQL DB with detail. Also, cache and their strategies of eviction as well as
how do we store the data in cache. We have completed all the cache part very extensively so that we don't get any
issues afterwards. We have also covered load balancing and load balancer in which we have talked
about how a load balancer acts as a mediator between the coming request and the request which goes to server. Also,
it works as an orchestration between different servers and take care of each and everyone's health. With that, we
have covered various forms of replication, partitioning. Ultimately, one of the most important theorems which
we're going to cover in each and every interview, that is CAP theorem. Whether you'll choose availability over
consistency or vice versa. After CAP theorem, we have covered message queues, fault tolerance, and then we had a brief
discussion about monitoring and observability also. So, with all these topics, now we are
ready to give any interview or to design any system design component which we want to. We have a solid foundation now.
Now, we can design any system. So, congratulations, you have unlocked a new trick, a new component, a new system in
your resume, in your abilities. Also, if you feel like I have missed any point or any topic, please mention that in the
comment. I may make a video of it in the coming days. With this, let's move to the last part
of our video, which is designing a system. So, whenever you appear in an interview of system design or maybe you
will design a system by yourself, it is very important to think out loud. So, you have to take your decisions very
specifically. No one is looking for the perfect solution, but everyone here is looking for what are the trade-offs you
make, what are the components you use. Also, when I'm talking about components, whenever you are using a component, it
is very important that you use it for the right reasons. Just because you know about all the components, it is not
recommended to add all the components into your system. Because remember, every component you add will add the
costing of your system also. So, we have to use the component and design the system very efficiently so that it can
be cost-efficient as well as work efficiently to solve our purpose. So, for now, let's design a video streaming
application. So, see, we already have a lot of applications. We have YouTube for
streaming. We also have, let's say, Hotstar, Netflix, Prime. Also, if we talk about streaming applications, we
can also say G Meet is also a streaming application because we can do the live streaming.
Uh we can call it a Zoom call or a G Meet also. So, Zoom can also come into this category where we stream our video
live to other members. So, a call is basically also an example of a streaming application. Now, with this, we can also
use LMS or any other software for streaming our data. We can have more applications. I'm just jotting this down
so that you get the idea of the applications which use streaming. Now, before starting, it is very important to
jot down the requirement. So, for our video, we doesn't care about user login. We don't care about their reviews and
maybe rating also. We don't care about any other operation, but we only need to know how a video basically process from
one system to finally our client system. This is what we need to know in this particular example. Now, whenever you
appear on an interview also, it is very important to jot down the requirement because let's say they give you an
example of designing any software like Netflix, you need to know what are the features the interviewer is interested
in. With that, while creating your systems also, you need to know specifically what you are designing
because you may be designing an entire LMS, but for that moment, you need to pick feature by feature and design each
component and at the end, maybe collect all the findings and create a system. This is how every system is built. Now,
here, we only are talking about this process of getting a video to our client system
from any server or from any other source. Okay, so let's start from very basic.
What is a video? A video is nothing but a collection of different images we can say and with this we also have some
audio here. So different images are combined with audio to create a video, okay? So in
order to calculate different images we can see FPS or frame per second. We can have 30 or 60.
These two things are available. Also, we can use 120 or 240 if they're available. I'm not that sure about it. But yeah,
these two frame rates are quite common in our day-to-day life. So what does it mean to have 30 and 60
FPS? Basically, in 1 second, if we are using 30 FPS, our video will be divided into 30 images.
And if we are using 60, for 1 second, it will be divided into 60 images. So that means the size of 60 FPS will be
more than that of a 30 FPS video. But if we talk about the video smoothness, also the 60 FPS video will be more smoother
than a 30 FPS video. Now with this, let's stick to 30 FPS thing. Let's say a size of a normal video, let I'm like I
am recording a video in 30 FPS right now. My recording, let's say went for 1 hour
and for the 1 hour recording video, the size of my recording is basically around 2GB.
So if, let's say you are watching this video and if you want to download the entire video in the start before playing
and then you play, what will happen? It will add latency to our system because downloading 2GB will
take time for sure. Also, you have to see buffer and let's say you downloaded all the 2GBs of content into your system
just to see a particular section or maybe you see the first 10 seconds of the video and didn't like it. So, you
wasted your internet and storage. So, this is not a good option to download 2GB entirely first hand and then see a
video. It is completely waste of time. So, we have to think about something else. So, the thing is here we have to
use some network which is suitable for our system. So, in order to process information in video or images form, we
normally go for TCP. Because TCP maintains a sequence of things. Why do we need sequence? Let's see in a bit.
So, in TCP we have two protocols. One is RTMP which is Real Time Messaging Protocol and the other one is RTSP which
is Real Time Streaming Protocols. Both of these protocols basically help us in order to stream a particular video to
our device. Now, what this protocol does is let's say we have a complete video here. It is going to divide our video
into different chunks called as segments. And our client machine or the server from which we are watching is
going to pull these segments one by one. Now, in message queues we have talked about pull and push. This time I'm
saying pull. So, just connect the dots. Keep connecting the dots while I'm saying things, okay? So, this is going
to pull our segments one by one based on the network. Now, we'll come to this part in a bit. So, this will solve our
issues. This will give us a low latency because we don't have to download the entire
video. We can download some segments also. With this we'll now be able to stream in a much better way and it will
be more efficient than downloading the entire video which was consuming our resources and network bandwidth. Now,
this time we have to only download some segments which is going to be more efficient because this time we are
downloading in parts. So, suppose we are watching one video, first 10 seconds is over and now we don't want to watch the
entire video. So maybe some or the other sections will be pre-downloaded for us so that if you go ahead we don't see
buffer, but we don't have to download the entire video which will save us a lot of space and lot of network
bandwidth as well. Now with FPS we also have one more factor which changed the game entirely which is resolution. With
videos we have different resolutions, right? We can use 4K, we can use HD, we can use maybe 720p,
we can use 480 or 240 or even 144p. Even 64p was an option. I'm not sure whether our video will be able to render in that
quality or not. I have like way before when I was in school, maybe I saw that. After that I didn't. So yeah, these are
all the resolutions on which we can see our video. 4K will obviously have better quality and 144p
is going to have bad quality. But why are we talking about resolutions is even if let's say we are streaming a 4K
video, which I already said for 1 hour video we need to store it in 2GB space. Suppose
we divide this into multiple segments for each second, then 1 second will be 0.57
MB. Now this looks small but is a huge size if we talk about network. What if we have a poor network? What if we are
traveling? So our network is keep on fluctuating on a different level. Also if we sit by working on our office or on
system, our networks keep on fluctuating as well. So that means we are not going to have a common bandwidth throughout
our video viewing experience. So our network will keep on increasing and decreasing. So we have to take care of
that also. Let's say I record videos in HD only and if I start recording in 4K, that means the size of 2GB can become
big, right? The 2GB can go to 4GB. Maybe I use multi-cam setups. At that time, the 4GB can also go up to 8GB, maybe.
Also, in my videos, I'm not doing much activity. The frames are pretty still. Only my hand movements can be shown once
in a while. I'm sitting still. I'm just facing the camera or the screen. I'm not doing much activities. Also, the screen
you are seeing is black on which I'm writing in white. So, there are no colors, also. So, this is pretty easy
video to see. But, if I start recording outside or maybe I start recording any action sequence, so in that case, the
size can also get to 16GB for the same 4K video of 1 hour. So, this size can also be increased to four times. So,
that will be around 4MB. So, the issue here is for even 1-second video, we are using 4MB. And if our network is not
that good, we are still going to see buffer or face loading issues, which is going to basically ruin the experience
of our viewer, and they will leave the application. This is not what we want entirely, right? So, we have to do
something about it. Also, if you think about it, there are different screens on which you work on. We maybe watching
something on TV. We maybe using our laptop screen. Uh also, we can use our mobile screen, maybe our watch as well.
Maybe we are seeing it on iPad. We don't want 4K qualities in all of them, right? Let's say we are watching on watch, we
don't want 4K. Maybe on watch, 480 works fine. And also, on iPad, also, I don't think 4K is
required. Maybe we can show them in SD, and it will be enough. On TV, we basically require 4K. Like, we don't
have to compromise here. But, on laptop, also, we can switch between HD and 4K. We can choose. We can pick based on the
requirement or based on the configuration, based on the network also. On mobile also, we can work pretty
well with SD. We don't need 4K here as well. So, that means not only network, but based on our screen size, we can
change the resolution of our video. So, why are we changing? Because it is going to reduce the size
of our segment. And the lesser the size, the faster it will be to show. So, in order to maintain streaming, we have one
mechanisms, which is adaptive bitrate streaming. What it basically conveys is, let's say you start watching a video on
300 Mbps. And your networks keep on fluctuating. Maybe it went down till 10 Mbps. So, when the network fluctuates,
we can't download with the same consistency. And if we are not able to download with the same consistency,
there's high chance of seeing the buffer icon. And we don't want that. So, in order to achieve this, what can
we do? We have a source video of 4K, right? We have to divide this video into
different segments, that we know. Also, we have to divide those segments into different qualities. So, for example,
this 4K video is divided into 10 segments of 4K quality. But now, also we have to divide these segments into more
configurations, the lower configurations. Now, our basic resolutions and segments are ready. Now,
let's see in real time how our function really works. So, let's say here we have our client who is watching something on
our desktop screen. In starting, the network connection was 300 Mbps. So, we start showing them the 4K video. So, it
sees segment one, maybe segment two also he watches. And then, the network drops to 10 Mbps. So, at this point, maybe
this is already loaded into their system in the form of cache. So, they can still see this section into 4K. But after
that, we need to load the data again. And the network is now at 10 Mbps. So, we can't load the 4K data. What we will
do, we will load the lower configuration data. Maybe according to this we will pick 480p.
So, we will start downloading this data. And once the network restores to maybe 300 or 150 Mbps, we will start switching
our configurations accordingly. So, we can go to 1080p as well. And if it then drops to 50 Mbps, we can still give them
720p. And at the end, if network basically restores to 300 Mbps, we can show them the last segment in 4K
again. So, this is how an YouTube or any other stream basically works. They will keep switching our quality
based on our network bandwidth because we are mostly seeing that entire video on a single screen.
And based on our network, they will keep switching the qualities. And we see the entire video into one go
without seeing the much difference there because normally the network doesn't drop so drastically. We just keep these
segments in order to show them into different devices. If you pay attention in most of the streaming applications
where you are seeing the video, in the side corner, you also see a segment where you can switch your configurations
again. You can choose between auto, maybe HD, or maybe they will directly use the configuration that is 720p or
480p, something like this. So, you can choose between them. And if
you choose auto, they are going to perform this orchestration or this game while you
watch the entire video. So, what did we see here? If the network throughput, that means our network speed, is greater
than the bitrate of the downloaded segment, that means we have downloaded in a lower quality and now network is
greater than that. So we can now switch to a better quality. Similarly, if the network throughput is less than
the bitrate of downloaded segment, so in this case what we will do, we will switch down the
quality of our video. So after getting all this knowledge, after discussing all these things with your interviewer or
with your team, then you start deciding what components do you use in your system design. Now
the path is very clear, right? We need to store the current data or the actual data somewhere. Maybe this is our CDN
where the actual data or the actual video is uploaded and it is uploaded from where? From any server. Now from
here we need to create the segments, so that means we need to give this data to a service which can basically transform
our video. So we can name that we can say it is a transformation service. Now after this we have to send the segments
to our queue. We can use priority queue here, right? So we can create a queue. And why we are using priority queue? You
need to answer yourself. Now from this message queue, our system is going to use the services of downgrading
each and every quality. So we use worker term here. Worker is basically used in order to denote another service. So we
may be using worker one for downgrading it to HD. Also we can use worker two for maybe downgrading it to 720p and so
forth. So after working with these resolutions, again we need a message queue to store the results into segments
and again we need a priority queue here. Okay, with this we will send this request to a distributed CDN kind of
services from where it can go into different part of our servers. So, if someone is watching from India, they can
access the data from this server and they can watch it from US server and the other servers are for other regions.
Now, by doing this, we have completed the entire system design of our video streaming application. And from this
server, eventually, we are going to access it on our client machine on which our user is
going to watch their video. So, now after creating this diagram, let's also do some maths.
Let's say our source video is of 50 GB and it is of 20 minutes. So, it is a high-quality video, maybe 4K, and it is
having 60 FPS. So, that's why the size is too big. So, this is we are taking as an average size of the video which we
are going to stream. Maybe we are streaming an entire match. So, this is the size which we are going for our
20-minute video. Now, let's decide how many segments can we divide it to. So, I'm doing an easy maths. So, 20 minutes
multiplied by 60, that will convert it into seconds. So, it will be 1 2 0 0 seconds. And
also, I am making the segments also equal, so that means 1 2 0 0 segments as well. Also, with this, let's
talk about different resolutions. So, let's say 4K is of 50 GB, which will be divided by our segment. So, here the
size of our segment is too large, right? Because it is a 4K video. Maybe we can divide it into more segments. So, but
for this example, let's stick to it. Let's say our 1080 pixel video, let it be for 20 GB and again, we are
going to divide it by 1200. It is going to be again 16.6 MB. With this, let me write all the
resolutions. Now, this is what the calculation looks like if we divide it for each segment
for each resolution. For 720, it will be 8.3 MB. For 480, it will be 4.17. And for 250, it will be 2.08 MB. That means
if we combine them all, it is going to be 72.65 MB, approximately. So, that means the
size of one segment of our video is going to be 72 MB, which is too huge, I feel. So, maybe we can divide it into
further segments. But for now, this is going to be the size of our one segment. And accordingly, we need to load the
data into our stream. Also, while calculating the size, we should also take care of our user. That
means we should know how many users are going to see the stream. So, if the user count is, let's say, 100, we can use the
same server or a single server only, right? For 100 number. If we count this, it will comes down to 87.5 GB.
So, we can store 87.5 GB into a single server, and from which
these 100 users can view our streaming easily. What if the user changes from 100 users to 100,000 users? Now, this
will change our picture. Now, this is where we will know that our one server is maybe not enough to cater so much
requests. So, we will divide our servers into two, maybe. And based on the number of users, we have to keep increasing the
count of servers. Maybe we'll use message queues. Maybe we'll add some caching algorithms. And all of this will
be managed through our diagram. Here, I have not used any caching. But we can use the caching on CDN level.
Or we can use the cache on our browser level, which is going to hold the segments of
the coming part. So, this is what the design look like. This is what the calculation is. And this is what is the
logic behind the system design of my system, which will stream our video to a 10,000 or a 100,000 user. Now, this
diagram can be different in your case also. It is very possible that you will design a system better than me, and it
is also possible that you will see some other YouTube video who have designed the video better than me or maybe worse
than me. So, these designs are basically your practice. The more you practice, the more you see
other designs, the more you read different papers, your designs are going to improve. And more your design
improves, the quality of your system or your workflow increases with time. So, with this, I end the discussion of
system design. And I really hope you all enjoyed this series so far. This was a long video,
and we covered a lot of topic. I hope you all enjoyed it. Now, you can like this video or comment the most
interesting section you found out in this video. Also, you can comment out any topic which you want to see in
future on Telescop. With this, my name is Akshat Agarwal, signing off. Thank you.
Data-intensive applications, like Instagram feeds, prioritize fast data movement and worry about read speed, storage safety, and concurrent user handling. Compute-intensive applications, such as ML training, focus on heavy calculations and worry about computation speed, parallel processing, and cost reduction. The key trick to differentiate is: if time is lost in data movement, it's data-intensive; if lost in computation, it's compute-intensive.
The CAP theorem states a distributed data store can only guarantee two of three: Consistency (C), Availability (A), and Partition Tolerance (P). In practice, partition tolerance is unavoidable in distributed systems, so the choice is between CP and AP. For example, banking systems prioritize consistency (CP) so transactions are accurate, while social media like Instagram prioritize availability (AP) so users can always access the app, even if data is slightly stale.
Caching acts as a temporary, high-speed storage layer for frequently accessed data, reducing database load and response latency. Common write strategies include: Write-Through, where writes go to cache then DB for strong consistency; Write-Around, where writes go directly to DB and cache is populated on read; and Write-Back, where writes go to cache and are asynchronously synced to DB for high speed but with a risk of data loss. Cache eviction policies like LRU determine which data to remove when the cache is full.
Sharding, or horizontal partitioning, is used when a single database node can't handle the data volume or query performance. The goal is to evenly distribute data to avoid hotspots. Common methods include Key Range partitioning, where data is divided by ranges of keys (e.g., user IDs), and Hash of Key partitioning, which uses a hash function for more even distribution. You can also partition by secondary indexes locally (each partition maintains its own index) or globally (a single index maps data to partitions).
A message queue decouples producers and consumers, allowing asynchronous processing of tasks like email or order processing. This improves resilience because if a downstream service is slow, the producer can still queue messages and continue. A Dead Letter Queue (DLQ) holds messages that repeatedly fail to process (e.g., after multiple retries), preventing them from blocking the main queue and allowing engineers to debug the problem without losing the message.
In the project, video segments are distributed across a CDN (Content Delivery Network) to reduce latency by serving users from geographically closer servers (e.g., servers in India for Indian users). Adaptive bitrate streaming works client-side—the video player automatically selects the highest quality segment (e.g., 4K, 1080p) based on the user's current network bandwidth, ensuring smooth playback and preventing buffering.
REST APIs are the most common, using stateless operations and JSON, ideal for general web and mobile apps. GraphQL, with a single endpoint, allows clients to define the exact data structure they need, which is perfect for flexible data fetching. WebSockets provide full-duplex, real-time communication, making them essential for applications like chat apps, live notifications, and online gaming where low-latency, bidirectional data flow is required. SOAP is a legacy XML-based protocol, and gRPC is a fast, binary protocol often used for internal microservice communication.
Keep this summary
Save it to LunaNotes and it becomes a real note in your library — editable, searchable, and ready to turn into flashcards or a diagram. Free to start.
Save to LunaNotesOr summarise for another video.
This summary and transcript were automatically generated using AI with the Free YouTube Transcript Summary Tool by LunaNotes.
Related summaries
Complete Architecture of Instagram Reels and YouTube Shorts Explained
Explore the end-to-end architecture of short video platforms like Instagram Reels and YouTube Shorts. Learn about upload workflows, video processing, personalized feed generation, engagement tracking, and scalable data handling designed to serve billions of views daily with low latency.
Comprehensive System Design Series: From Monolith to Microservices and Beyond
This extensive video series covers crucial system design concepts essential for software engineers, students, and developers preparing for FAANG interviews or building scalable startup systems. Dive deep into foundational topics like monolithic vs microservice architectures, API gateways, load balancers, networking protocols, caching strategies, distributed systems, rate limiting, SSL certificates, database choices, avoiding single points of failure, messaging queues, consistent hashing, and more with real-world examples and hands-on coding projects.
System Design Basics: Scalability, Cloud Hosting & API Explained
Learn the fundamentals of system design, including how to expose algorithms via APIs, the role of cloud hosting, and essential concepts like vertical and horizontal scaling. Discover the trade-offs between scalability, resilience, and consistency to design robust systems that meet real-world business requirements.
Scalable System Design Explained Using a Restaurant Analogy
Explore how building a scalable, resilient system parallels running a growing pizza parlor. This guide covers vertical and horizontal scaling, fault tolerance, microservices, load balancing, and decoupling with real-world examples to simplify complex technical concepts.
HTTP Protocol Fundamentals: Statelessness, Methods, CORS, Caching, and Status Codes for Backend Developers
This comprehensive guide explains core HTTP concepts every backend developer needs to know, covering stateless architecture, request/response headers, methods, status codes, CORS flow, caching strategies, content negotiation, and large data transfer. You'll learn why HTTP is stateless, how browsers handle cross-origin requests with preflight checks, and practical ways to optimize performance using caching and compression.
Most viewed summaries
A Comprehensive Guide to Using Stable Diffusion Forge UI
Explore the Stable Diffusion Forge UI, customizable settings, models, and more to enhance your image generation experience.
Kolonyalismo at Imperyalismo: Ang Kasaysayan ng Pagsakop sa Pilipinas
Tuklasin ang kasaysayan ng kolonyalismo at imperyalismo sa Pilipinas sa pamamagitan ni Ferdinand Magellan.
Mastering Inpainting with Stable Diffusion: Fix Mistakes and Enhance Your Images
Learn to fix mistakes and enhance images with Stable Diffusion's inpainting features effectively.
Pamamaraan at Patakarang Kolonyal ng mga Espanyol sa Pilipinas
Tuklasin ang mga pamamaraan at patakaran ng mga Espanyol sa Pilipinas, at ang epekto nito sa mga Pilipino.
How to Install and Configure Forge: A New Stable Diffusion Web UI
Learn to install and configure the new Forge web UI for Stable Diffusion, with tips on models and settings.
Found this summary useful?
Take it with you. One click puts it in your own LunaNotes library.
Save to LunaNotes