gdtyra

Distributed Systems

This is a collection of notes relating to distributed system design based on my experience and digesting things I've read.

CAP Theorem

A distributed system can realistically only provide 2 out of 3 of the following at the same time:

Because network failure is unavoidable in practice distributed systems must be partition tolerant and make trade-offs between consistency and availability.

Concerns

Most OOP design principles also apply to distributed systems. Components of the system should have thoughtfully designed interfaces, decoupled components with limited responsibilities, and should tolerate change well. The difference is that distributed systems have additional concerns relating to security, availability, consistency, and many failure cases that are not practical concerns within a single application instance but become plausible and likely in a distributed system.

Tools and Countermeasures

These are some options for dealing with the concerns above.

There are also some practices that can work to our advantage:

Vertical and Horizontal Scaling

Vertical scaling refers to simply adding more CPU, network, and memory resources to a fixed number of instances to increase throughput. It is simple and can be done without architectural changes, but there are limits to how far it can get you. Additionally, vertical scaling does not provide additional redundancy or resilience to the system. Therefore, it is best to design systems with horizontal scaling in mind.

Horizontal scaling refers to spreading workloads across an adjustable fleet of interchangeable instances. It requires more consideration for the problems inherent in distributed systems, but the benefit is a more scalable and resilient system.

It is easy enough to run logic on a scalable fleet of stateless servers that share a common data source, so the difficulty in horizontal scaling often comes down to where the shared data sources are and how they are accessed. If a fleet of stateless web servers read and write to a single shared database instance, then the system is not actually scalable.

Storage Options

Compute Options

Interface Options

Caches and CDNs

Caches such as Redis or Memcache can be used to optimize and offload read operations for data that is read frequently

CDNs are essentially a form of cache that is placed closer to the clients rather than close to the other components of the application. They are also distinct in that they are directly accessed by the client as opposed to sitting behind the application's other interfaces

Rate Limiting

Rate limiting is often a necessity even if you expect all your clients to behave reasonably. Without it, it is too easy for one client to accidentally or intentionally impact system performance.

Aside from determining how to identify or label traffic, the main decision is what the rate limiting policy/algorithm will be:

If we're dealing with an HTTP API, then HTTP 429 and associated response headers can be used to inform the client of when they may try again.

Generating Unique IDs

The straightforward approach of generating incremental IDs is difficult in a distributed system, so alternatives are needed.

Client-Server Interaction

HTTP is client-initiated which works just fine for many situations, but situations where the client needs to receive events from a server are less straightforward.

Other Thoughts