{
  "slug": "Design-specifications-for-a-click-based-application-resembling-a-hamster-themed-for-10-million--111775da0a5e",
  "title": "Design specifications for a click-based application resembling a hamster-themed for 10 million…",
  "subtitle": "Design specs for 10M-user app: tackles 100K clicks/sec with Redis, WebSockets, load balancing, and NoSQL. Focuses on scalability and…",
  "excerpt": "Design specs for 10M-user app: tackles 100K clicks/sec with Redis, WebSockets, load balancing, and NoSQL. Focuses on scalability and…",
  "date": "2024-08-10",
  "tags": [
    "My Experience",
    "Redis"
  ],
  "readingTime": "7 min",
  "url": "https://medium.com/@mobinshaterian/design-specifications-for-a-click-based-application-resembling-a-hamster-themed-for-10-million-111775da0a5e",
  "hero": "https://cdn-images-1.medium.com/max/800/1*YZAn9L001TW4vAuvx2UrOQ.png",
  "content": [
    {
      "type": "heading",
      "level": 2,
      "text": "Design specifications for a click-based application resembling a hamster-themed for 10 million users"
    },
    {
      "type": "paragraph",
      "html": "Special Thanks to <a href=\"https://medium.com/u/461dc5ed4580\" target=\"_blank\" rel=\"noreferrer noopener\">HamidReza</a>."
    },
    {
      "type": "paragraph",
      "html": "Suppose we will design a click-based application with more than 10 million users. One million users use it daily, 500k users online every hour, with 100k clicks per second."
    },
    {
      "type": "paragraph",
      "html": "This document outlines the design specifications for a high-traffic, hamster-themed click-based application that serves 10 million users. The application faces significant challenges in handling massive user interactions, with up to 100,000 clicks per second. The document explores various architectural approaches and database solutions to address these challenges, focusing on scalability, performance, and data integrity."
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Discusses several key aspects of the application’s design:"
    },
    {
      "type": "paragraph",
      "html": "1. Simple database approach: Consider a non-clustered Postgres database with front-end caching and direct HTTP requests to the backend."
    },
    {
      "type": "paragraph",
      "html": "2. Security measures: Implementing cryptographic methods to ensure data authenticity and prevent bot attacks."
    },
    {
      "type": "paragraph",
      "html": "3. Connection handling: Addressing the limitations of database connections through connection pooling and load balancing."
    },
    {
      "type": "paragraph",
      "html": "4. WebSocket implementation: Exploring the use of WebSockets and AWS Elastic Load Balancing (ELB) to reduce the number of requests and improve scalability."
    },
    {
      "type": "paragraph",
      "html": "5. Redis integration: Proposing a system using Redis for fast reads and a message queue for batch updates to the database."
    },
    {
      "type": "paragraph",
      "html": "6. Database comparisons: Evaluating various NoSQL databases like MongoDB, Cassandra, DynamoDB, and ScyllaDB for their suitability to the application’s needs."
    },
    {
      "type": "paragraph",
      "html": "7. Performance considerations: Discuss the performance differences between INSERT and UPDATE database operations."
    },
    {
      "type": "paragraph",
      "html": "The document concludes by suggesting a final solution combining HTTP requests for immediate read/write operations with Redis and asynchronous data transmission to the selected database. This approach aims to efficiently handle the application's high traffic and data processing requirements."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Simple database approach"
    },
    {
      "type": "paragraph",
      "html": "In the first step design, we designed the front end and back with a simple non-cluster Postgres database, and the front every minute cached-up clicked and it directly with Post request (Http) to o backend and backend update value of the user on the Postgres database."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*PSCNX7crUGIrI0KBo1-HNA.png",
      "alt": "Design specifications for a click-based application resembling a hamster-themed for 10 million…",
      "caption": "",
      "width": 752,
      "height": 190
    },
    {
      "type": "paragraph",
      "html": "<strong>How can we ensure the front end sends data from the application, not another bot request?</strong>"
    },
    {
      "type": "paragraph",
      "html": "We need some sort of cryptography, such as hashing data or encrypting the fingerprint of each request. The backend needs to check the hash or encrypted data to make sure data has been sent from our client's device. Chacha20, AES, or sha3 should be useful, but this process should not depend on the database."
    },
    {
      "type": "paragraph",
      "html": "<strong>Request Calculate:</strong>"
    },
    {
      "type": "code",
      "lang": "text",
      "code": "100K requests per second / 60 = 1666 (rps)"
    },
    {
      "type": "paragraph",
      "html": "Even 500 is way too many concurrent requests so that value is too high for <code>max_connections</code> (or the number of concurrent active sessions, to be precise)."
    },
    {
      "type": "paragraph",
      "html": "The good thing is that you don’t have to do that. You use a connection pool as a proxy between the application and the database. If your database statements are sufficiently short, you can easily handle thousands of concurrent application users with a few dozen database connections. This protects the database from getting overloaded and avoids opening database connections frequently, which is expensive."
    },
    {
      "type": "paragraph",
      "html": "<strong>In this case, Postgre becomes the system's bottleneck.</strong>"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Simple database with WebSocket connection"
    },
    {
      "type": "paragraph",
      "html": "In this approach, we reduce the number of requests and responses to the back-end server. One method is using web sockets and WebSocket pooling to handle several connections to the server. In addition, we can use AWS (ELB) to handle these requests."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*uKYbgvHWyzSkyh0csjTC7g.png",
      "alt": "Design specifications for a click-based application resembling a hamster-themed for 10 million…",
      "caption": "",
      "width": 726,
      "height": 167
    },
    {
      "type": "paragraph",
      "html": "For a better understanding of WS load balancing, let’s dive a bit deeper into the background of the TCP sockets. By default, a single server can handle 65,536 socket connections because it’s the maximum number of TCP ports available. So, as WS connections have a TCP nature and each WS client takes one port, we can definitely say that the number of WebSocket connections is also limited."
    },
    {
      "type": "paragraph",
      "html": "Elastic Load Balancing (ELB) is <strong>a fully managed load balancing service that automatically distributes incoming application traffic to multiple targets and virtual appliances across AWS and on-premises resources</strong>. You can use it to scale modern applications without complex configurations or API gateways."
    },
    {
      "type": "paragraph",
      "html": "Load balancing distributes network traffic across multiple servers to ensure high availability, scalability, and application performance. This concept is particularly relevant for WebSocket connections, which require persistent, long-lived connections between clients and servers."
    },
    {
      "type": "paragraph",
      "html": "Key points related to WebSocket connections:"
    },
    {
      "type": "paragraph",
      "html": "1. Application availability: Load balancers can help maintain WebSocket connections, even if some servers fail, by redirecting traffic to healthy servers."
    },
    {
      "type": "paragraph",
      "html": "2. Scalability: As WebSocket applications grow, load balancers can distribute connections across multiple servers, preventing any single server from becoming overwhelmed."
    },
    {
      "type": "paragraph",
      "html": "3. Performance: Load balancers can route WebSocket connections to the most appropriate server, potentially reducing latency and improving response times."
    },
    {
      "type": "paragraph",
      "html": "4. Connection persistence: Some load balancing algorithms, like IP hash, can ensure that a client’s WebSocket connection always goes to the same server, maintaining session consistency."
    },
    {
      "type": "paragraph",
      "html": "5. SSL/TLS termination: Application load balancers can handle SSL/TLS encryption, offloading this work from application servers and potentially improving WebSocket connection performance."
    },
    {
      "type": "paragraph",
      "html": "For WebSocket-specific problems:"
    },
    {
      "type": "paragraph",
      "html": "1. Choose appropriate load balancing algorithms: Methods like “least connections” or “least response time” might be more suitable for WebSocket traffic than simple round-robin."
    },
    {
      "type": "paragraph",
      "html": "2. Configure timeouts carefully: WebSocket connections are long-lived, so load balancer timeouts need to be set accordingly to prevent premature connection closure."
    },
    {
      "type": "paragraph",
      "html": "3. Enable WebSocket support: Ensure that your load balancer is configured to support WebSocket protocols (ws:// and wss://)."
    },
    {
      "type": "paragraph",
      "html": "4. Monitor connection health: Use load balancer health checks to ensure that WebSocket servers are functioning correctly."
    },
    {
      "type": "paragraph",
      "html": "5. Consider sticky sessions: For applications requiring session persistence, configure the load balancer to use sticky sessions or IP-based routing."
    },
    {
      "type": "paragraph",
      "html": "While the text doesn’t explicitly mention WebSockets, the principles of load balancing apply to WebSocket connections, and proper implementation can help solve many common WebSocket-related issues in distributed systems."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Read From Redis and Batch update (Insert)"
    },
    {
      "type": "paragraph",
      "html": "In this approach, a queue ( bus message ) could be useful for inserting data such as bach data into the database and reading data from Redis. Also, we need a persistence Redis to save all data."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*BfEOAHtYw-vVN0vNPpuavw.png",
      "alt": "Design specifications for a click-based application resembling a hamster-themed for 10 million…",
      "caption": "",
      "width": 980,
      "height": 140
    },
    {
      "type": "quote",
      "html": "With high-end configurations, the number of client connections is also important. Based on epoll/kqueue, the Redis event loop is quite scalable. Redis has already been benchmarked at more than 60000 connections and could still sustain 50000 q/s in these conditions. As a rule of thumb, an instance with 30000 connections can only process half the throughput achievable with 100 connections. Here is an example showing the throughput of a Redis instance per number of connections:"
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*wAwn-bNeplfIXpB5a7uKqQ.png",
      "alt": "https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/benchmarks/",
      "caption": "https://redis.io/docs/latest/operate/oss_and_stack/management/optimization/benchmarks/",
      "width": 713,
      "height": 416
    },
    {
      "type": "paragraph",
      "html": "<strong>Broken State:</strong>"
    },
    {
      "type": "paragraph",
      "html": "What will happen if the <strong>queue</strong> is <strong>down?</strong> Users' clicks would be sent to the Queue and database, but data still exists on Redis. Because we only use updates in this service, after the queue comes up and users click again on the button, it will send and save in the database."
    },
    {
      "type": "paragraph",
      "html": "What will happen if the <strong>Redis</strong> is <strong>down</strong>? Redis needs to read them from Postgres again, and we have a last state on Postgres."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Find the best database for the problem."
    },
    {
      "type": "paragraph",
      "html": "However, choosing a database to solve this problem depends on many conditions. It is recommended that MongoDB, Cassandra, DynamoDB, and ScyllaDB be used for the database."
    },
    {
      "type": "paragraph",
      "html": "Performance and scalability comparison However, <strong>Cassandra is better suited for write-heavy workloads, while MongoDB is better suited for read-heavy workloads</strong>. Cassandra’s masterless architecture allows for faster writes, while MongoDB’s document-oriented data model allows for faster reads."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*OuJZZ1oJlEBrfOAb5_UP1w.png",
      "alt": "https://www.geeksforgeeks.org/real-time-databases/",
      "caption": "https://www.geeksforgeeks.org/real-time-databases/",
      "width": 942,
      "height": 601
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Cassandra vs DynamoDB: A Comprehensive Comparison"
    },
    {
      "type": "list",
      "ordered": true,
      "items": [
        "Overview:<br> — Cassandra: Open-source, distributed NoSQL database with a peer-to-peer architecture.<br> — DynamoDB: Fully managed NoSQL database service offered by AWS."
      ]
    },
    {
      "type": "paragraph",
      "html": "2. Key Differences:<br> — Data Model: Cassandra uses a column-oriented model, while DynamoDB employs a key-value and document-oriented model.<br> — Querying: Cassandra uses CQL (Cassandra Query Language), and DynamoDB has a proprietary API.<br> — Scalability: Both offer high scalability, but Cassandra requires manual management while DynamoDB is automatically managed.<br> — Management: Cassandra needs manual configuration and monitoring. AWS fully manages DynamoDB."
    },
    {
      "type": "paragraph",
      "html": "3. Detailed Comparison:<br> — Flexible Data Modeling: Both support flexible schemas for unstructured data.<br> — Querying Capabilities: Cassandra’s CQL is SQL-like, and DynamoDB’s API is efficient for specific operations.<br> — Scalability and Performance: Both are designed for horizontal scaling.<br> — Cost: Cassandra is open-source but requires infrastructure management, and DynamoDB has usage-based pricing.<br> — Use Cases: Cassandra suits high availability and fault tolerance needs, and DynamoDB is versatile for various applications."
    },
    {
      "type": "paragraph",
      "html": "4. The article also includes a FAQ section addressing common questions about the two databases."
    },
    {
      "type": "paragraph",
      "html": "5. Conclusion: The choice between Cassandra and DynamoDB depends on specific requirements such as data model needs, scalability, management preferences, and integration with existing systems."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "ScyllaDB Cloud vs DynamoDB"
    },
    {
      "type": "paragraph",
      "html": "Performance Report: ScyllaDB Cloud performs superior to Amazon’s DynamoDB and is significantly less expensive for similar workloads."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*nWfhJ_oYv-Rou7Or6XWZnA.png",
      "alt": "https://www.scylladb.com/product/benchmarks/dynamodb-benchmark/",
      "caption": "https://www.scylladb.com/product/benchmarks/dynamodb-benchmark/",
      "width": 676,
      "height": 475
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*2DncjEvhn_pDfA-amnUu-g.png",
      "alt": "https://www.scylladb.com/product/benchmarks/dynamodb-benchmark/",
      "caption": "https://www.scylladb.com/product/benchmarks/dynamodb-benchmark/",
      "width": 688,
      "height": 492
    },
    {
      "type": "paragraph",
      "html": "<a href=\"https://www.scylladb.com/product/benchmarks/dynamodb-benchmark/\" target=\"_blank\" rel=\"noreferrer noopener\">https://www.scylladb.com/product/benchmarks/dynamodb-benchmark/</a>"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Insert, update benchmark performance"
    },
    {
      "type": "paragraph",
      "html": "INSERT is faster than a UPDATE statement, provided the database is properly optimized."
    },
    {
      "type": "paragraph",
      "html": "INSERT is always faster because UPDATE necessarily requires a scan&nbsp;<br>(whether aided by indexes or not) to find the individual row(s) to&nbsp;<br>alter. By contrast, the database engine already has the next&nbsp;<br>locations in each table allocated and identified for INSERT actions&nbsp;<br>and maintains this information in the control files.&nbsp;<br>INSERT is also faster than DELETE, and INSERT is a substitute for&nbsp;<br>UPDATE for two reasons: not only is the database required to _find_&nbsp;<br>the rows to DELETE must maintain the UNDO for BOTH the DELETE&nbsp;<br>and INSERT actions in the redo logs. A DELETE will take just as long&nbsp;<br>as an UPDATE by itself. Adding the additional INSERT afterward only&nbsp;<br>further increases the time differential."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Final solution"
    },
    {
      "type": "paragraph",
      "html": "In conclusion, using HTTP requests to read and write data from Redis immediately and send data async to the selected database can handle the problem."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*YZAn9L001TW4vAuvx2UrOQ.png",
      "alt": "Design specifications for a click-based application resembling a hamster-themed for 10 million…",
      "caption": "",
      "width": 941,
      "height": 237
    }
  ]
}