Scalability
Explore vertical and horizontal scaling, stateless application design, database bottlenecks, indexing, connection pooling, replicas, partitioning and sharding.
Concept Overview
Scalability is the ability of a system to handle increasing workload by adding resources. Workload can increase through more users, requests, stored data, concurrent connections, larger payloads or background jobs. Scaling is not just adding servers: it requires understanding which resource is constrained, which work can be distributed, and where shared dependencies become bottlenecks.
1. Vertical scaling
Vertical scaling increases the capacity of an existing machine, for example by increasing CPU and memory. It is conceptually simple and often requires fewer application changes because the system still runs on one larger host. Its limits are the maximum capacity of the machine, cost and the fact that the machine remains a failure dependency. Vertical scaling can be a sensible first response when the workload and operational model are still simple.
2. Horizontal scaling
Horizontal scaling adds more instances and distributes work among them. A load balancer can route requests to App 1, App 2 and App 3, and more instances can be added as capacity needs rise. It can improve capacity incrementally and reduce dependence on one application server. The trade-off is additional infrastructure and distributed-system complexity, including shared state, connection budgets, deployment coordination and failure handling.
3. Why stateless application instances matter
An application instance is stateless when it does not depend on its local memory for important user state that must be available to another instance. If a session exists only in App 1's RAM, a later request routed to App 2 may not find it. Persisting appropriate shared state in a database, Redis or an external session store allows application instances to remain interchangeable. Statelessness does not mean the entire system stores no state; it means state is not trapped in an individual application process.
Client
|
Load Balancer
|---- App 1 ----|
|---- App 2 ----|---- Shared state (Redis / PostgreSQL)
|---- App 3 ----|4. The database often becomes the next bottleneck
Adding application instances can increase the number of concurrent database requests. The database may become constrained before the application layer. The supplied scalability notes list indexing, query optimisation, connection pooling, caching, read replicas, partitioning and sharding as possible techniques. They should be introduced based on measured bottlenecks and query patterns rather than automatically.
5. Indexing and connection pooling
An index can help PostgreSQL locate rows that match common filters without scanning the entire table. For example, a query filtering tasks by project_id may benefit from an index on that column. A composite index on (project_id, status) may help a common query that filters by both values; index order and usefulness must be checked against actual query plans. Connection pooling reuses a bounded set of database connections instead of continuously opening new ones for every request. Pool limits must be sized with the database's total connection capacity in mind.
CREATE INDEX idx_tasks_project_id
ON tasks(project_id);
CREATE INDEX idx_tasks_project_status
ON tasks(project_id, status);These are examples from the supplied learning notes. Validate them against the application's actual query patterns and EXPLAIN plans.
6. Read replicas, partitioning and sharding
Read replicas can serve selected read traffic while the primary database handles writes. Replication lag means a replica may not immediately show a recent write, so read-after-write behaviour must be considered. Partitioning divides a large table into smaller physical partitions and can help with large datasets and lifecycle management. Sharding distributes data across multiple database nodes; it adds routing, operational and cross-shard query complexity. These are later-stage strategies, not default optimisations for every project.
7. Apply scaling strategies in stages
For the Task Management API, start with stateless Spring Boot instances, a load balancer, PostgreSQL connection pooling and appropriately designed queries. Add indexes based on observed query patterns. Use Redis for repeated reads that have a clear freshness and invalidation policy. Only consider replicas, partitioning or sharding when measurements show that simpler techniques are insufficient. This staged approach keeps complexity proportional to the workload.
Key Takeaways
- ✓Vertical scaling increases one machine's capacity; horizontal scaling adds instances.
- ✓Stateless application instances can be routed interchangeably.
- ✓Database scaling should begin with query analysis, indexes and connection management.
- ✓Replicas, partitioning and sharding introduce consistency and operational trade-offs.