Distributed Systems Fundamentals Questions
Core theory that underpins any multi-node system: the CAP and PACELC theorems, consistency models (strong, causal, eventual), partitioning, replication, and the fundamental tradeoffs between latency, availability, and consistency. Covers how network partitions, clock skew, and partial failure change the reasoning compared to single-node systems. This is the vocabulary layer every distributed design question builds on.
Design a membership and failure-detection scheme for a cluster of several thousand nodes, where a fixed health-check threshold is too crude. Walk through how a Phi Accrual-style failure detector produces a continuous suspicion level instead of a binary up/down verdict, and why that matters at this scale.
Sample Answer
At a handful of nodes a fixed heartbeat timeout works fine: miss N beats, declare the node down. At several thousand nodes spread across racks and regions, the same fixed threshold produces both false positives (a node under scheduling pressure or behind a congested link gets marked down while still alive) and slow true detections (a threshold loose enough to avoid those false positives takes too long to catch a real failure), because network and scheduling jitter is not uniform across the cluster. A Phi Accrual failure detector replaces the single global timeout with a statistical model of each node's own recent heartbeat behavior, so the question becomes how improbable this silence is given what has actually been observed from that specific node, rather than whether more than T seconds have passed, and it outputs a continuously increasing suspicion level, phi, instead of a binary up-or-down verdict.
Membership and probing, before the suspicion model
- Partial views and gossip: each node keeps a small, mostly random subset of the membership rather than the full list, and periodically gossips membership updates and heartbeats to a few random peers, so state propagation stays cheap instead of all-to-all.
- Direct and indirect probing (SWIM-style; SWIM: a gossip-based membership protocol combining direct pings with an indirect fallback): a node pings a random peer directly, and if it gets no acknowledgment, asks a handful of other random members to ping that peer on its behalf, since one lossy path between two specific nodes should not be read as evidence the target itself is down.
- A minimal membership-manager state machine per monitored node: ALIVE, SUSPECT, DEAD. Failed direct-plus-indirect probes move a node from ALIVE to SUSPECT rather than straight to DEAD, and the suspicion is gossiped with an incarnation number, so a node that is actually alive can refute it by re-announcing itself with a higher incarnation before it is declared DEAD and dropped from membership.
From a binary probe result to a continuous suspicion level
Probing only tells you whether a heartbeat arrived, which is still binary. Phi accrual sits on top of that: instead of one global timeout, each node keeps a short sliding window of the actual inter-arrival times it has observed from a given peer, uses that history to estimate how spread out that peer's heartbeats normally are, and converts the elapsed time since the last heartbeat into a suspicion level using roughly:
ϕ(t)=−log10(Plater(t))where Plater(t) is the estimated probability, given that peer's own recent history, that a heartbeat still arrives more than t time units after the previous one. As t grows past what the peer's history says is normal, Plater(t) shrinks and phi grows smoothly and without bound, instead of snapping from 0 to 1 at one fixed cutoff.
Why this matters at thousands of nodes: a peer on a congested cross-region link naturally has wider jitter in its own history, so it takes a longer silence to reach the same phi value as a peer on a quiet local link, without anyone hand-tuning a per-peer timeout. It also lets different consumers of the same signal act at different confidence levels: something that only needs to stop routing traffic to a possibly-dead node can act at a low phi and cheaply reverse course if the node turns out fine, while something that removes a node from a lock's quorum permanently can wait for a much higher phi, all from the same underlying measurement.
Worked example: computing phi from a pinned heartbeat history
Take a 6-sample sliding window of observed inter-arrival times, in milliseconds, for one peer: [1000, 1300, 700, 1100, 900, 1200].
Mean μ=1033.33 ms, population standard deviation σ=197.2 ms.
Approximating the interval distribution as Normal(μ,σ2), so that Plater(t)=21erfc(σ2t−μ) (erfc is the complementary error function, a standard tool for turning a distance from the mean into the probability mass remaining in the distribution's tail beyond that point), gives:
| Elapsed since last heartbeat | phi |
|---|---|
| 1000 ms | 0.25 |
| 1500 ms | 2.05 |
| 2500 ms | 13.29 |
| 3500 ms | 35.47 |
Tracing the 1500 ms row end to end: standardize the elapsed time against this peer's own mean and spread, z=(1500−1033.33)/(197.2×2)=466.67/278.88≈1.673; convert that into a tail probability, Plater(1500)=21erfc(1.673)≈21×0.0180≈0.00898; then take −log10 of that probability, ϕ(1500)=−log10(0.00898)≈2.05. The other three rows fall out of the same three steps with a different elapsed time plugged in.
At t = 1000 ms, right around this peer's own mean, phi stays near zero: nothing unusual. At t = 1500 ms, phi crosses 2, a mild anomaly worth noting but not worth acting on for most purposes. By t = 2500 ms, phi is in the double digits, meaning the observed silence is astronomically improbable given this specific peer's recent behavior, which is enough statistical confidence for even a conservative consumer to move the node to SUSPECT, or to DEAD if it was already SUSPECT and never refuted with a higher incarnation.
Trade-offs and pitfalls
Phi is not a probability itself, it is a log-scaled confidence score, and reading a given phi value as a direct percentage chance of failure is a common misunderstanding; the number only means something relative to whatever threshold a given consumer chooses. A second pitfall is picking one global phi threshold across a heterogeneous fleet, which reintroduces the exact problem this design was meant to solve, since the entire benefit comes from calibrating to each peer's own recent history rather than from a smarter constant. Third, phi accrual detects the absence of a heartbeat; it cannot distinguish a genuinely dead node from a network partition dropping every packet between two specific nodes while both are alive and healthy elsewhere, so membership decisions that need to be safe, like removing a node from a lock's quorum, still need a consensus-backed or fencing mechanism layered on top of the suspicion signal, not phi alone.
What is a gossip protocol, and where do distributed systems typically use one? Describe the basic mechanics (peer-to-peer state exchange, periodic random fan-out) and explain roughly how convergence time scales as cluster size grows.
Sample Answer
A gossip protocol is a decentralized way for nodes to spread state (cluster membership, health, small pieces of shared metadata) by periodically picking one or a few random peers and exchanging what each side knows, the same way a rumor spreads through a population. There is no coordinator and no single point of failure: every node's job is identical, and information reaches the whole cluster in a small, predictable number of rounds even as the cluster grows large. Distributed systems reach for gossip for membership tracking and metadata propagation specifically because it scales without needing a central registry to stay in sync.
Basic mechanics
- Each node keeps a small local view of cluster state: who is alive, version numbers, small metadata.
- On a fixed interval, each node picks one or a handful of random peers and exchanges state with them, in one of three common shapes:
- Push: a node sends its state to a random peer, unprompted.
- Pull: a node asks a random peer for its state.
- Push-pull: both directions in one round trip, which converges roughly twice as fast for the same message volume.
- On receipt, each side merges what it learned (for example, keeping whichever version of each entry has the higher counter) and continues gossiping on the next interval.
- This exchange is also called anti-entropy when it specifically reconciles divergent replicas rather than just spreading membership news. A well-known concrete implementation of gossip-based membership is SWIM (Scalable Weakly-consistent Infection-style process group Membership protocol), which layers a lightweight ping and acknowledgment failure-detection scheme on top of the same gossip fan-out.
Convergence: why it scales like an epidemic
Assume, as a simplifying model, that each round of push gossip roughly doubles the number of nodes that have heard a given piece of information, since every already-informed node infects one new random peer per round. Starting from one informed node, after r rounds roughly 2 to the power r nodes are informed. For the whole cluster of N nodes to be informed:
2r≥N⟹r≥log2N
For a 1,000-node cluster, log base 2 of 1000 is about 9.97, so full propagation takes on the order of 10 rounds. For a 10,000-node cluster, log base 2 of 10,000 is about 13.3, so about 14 rounds. Ten-fold-ing the cluster size only adds a handful of rounds, because the doubling model grows exponentially, not linearly, in the number of informed nodes; this is the scaling property that makes gossip viable at cluster sizes where a centralized broadcast would become a bottleneck.
Trade-offs & pitfalls
- The doubling assumption above is a simplified model (uniform random peer selection, no message loss, no adversarial behavior); real convergence is probabilistic, and pathological cases (a persistently unlucky peer-selection pattern, high churn, network partitions) can leave a minority of nodes lagging well past the expected round count.
- Load per node stays roughly constant regardless of cluster size, since each node only ever talks to a handful of peers per round, which is the actual scalability win over a centralized registry that every node would otherwise have to poll or push to directly.
- Common wrong turn: assuming gossip gives a hard, guaranteed-delivery bound. It gives a probabilistic, high-confidence bound. A system that needs a strict deadline for propagation, a security revocation for instance, usually pairs gossip with an explicit acknowledgment or a stronger consensus-backed registry for the small set of facts that truly cannot wait.
Walk me through the CAP theorem: what do consistency, availability, and partition tolerance each guarantee, and why can a distributed system only provide two of the three once a network partition actually occurs? Give one example of a system design that would lean toward consistency (CP) and one that would lean toward availability (AP), and state precisely what each choice gives up. Also clarify how this notion of 'consistency' differs from the one used in ACID transactions.
Sample Answer
Direct Answer
The CAP theorem says a distributed system that can be split by a network partition can only guarantee two of three properties at once: Consistency, Availability, and Partition tolerance. Because real networks do partition (links fail, messages get delayed or dropped), partition tolerance isn't really an optional design choice, so the actual trade-off every replicated system makes, and only makes while a partition is actually happening, is between Consistency and Availability.
What Each Property Guarantees
- Consistency (C): every read returns the result of the most recent completed write, as if there were only one copy of the data (this is the strong, linearizable notion of consistency).
- Availability (A): every request that reaches a non-failed node gets a response, without a guarantee that the response reflects the latest write.
- Partition tolerance (P): the system keeps operating even when the network drops or delays messages between nodes, splitting them into groups that can't talk to each other.
Why You Only Get Two, and Only During a Partition
When there is no partition, a well-built system can offer both C and A: every node can talk to every other node, so it can confirm it has the latest data before answering. The theorem only bites once a partition actually separates the cluster into two or more groups. At that point, a node in the minority (or either side, in a symmetric split) that receives a request has exactly two choices:
- Answer immediately with whatever data it has locally. That satisfies Availability, but the data might be stale relative to a write that landed on the other side of the partition, so it does not satisfy strong Consistency.
- Refuse to answer (return an error or block) until it can confirm it isn't giving out stale data, typically by waiting for the partition to heal or for enough of the cluster to be reachable. That satisfies Consistency, but it fails Availability for that request.
There is no third option that gives both while the partition is open. That is the entire content of the theorem: it's about behavior during the partition window, not a permanent label on a system.
CP and AP Examples
- A CP-leaning example: a consensus-backed coordination store, such as etcd (a distributed key-value store built on the Raft consensus protocol). If a partition isolates a minority of nodes from the quorum, that minority stops serving both reads and writes rather than risk returning stale or conflicting data. It gives up availability on the minority side to preserve strong consistency everywhere it does respond.
- An AP-leaning example: a Dynamo-style, eventually-consistent key-value store. During a partition, every reachable node keeps accepting reads and writes on both sides, so the system stays available, but the two sides can accumulate divergent writes that must be reconciled once the partition heals (via version vectors, last-write-wins, or application-level merge logic). It gives up guaranteed-fresh reads to preserve availability.
CAP's "Consistency" vs. ACID's "Consistency"
These are two different axes, and conflating them is a common interview trap. ACID (atomicity, consistency, isolation, durability) describes properties of a single transaction, typically on one database: its "C" means a transaction only ever moves the database from one state that satisfies its own defined invariants (foreign keys, uniqueness constraints, application-level rules) to another such state. It says nothing about how fresh a read on a different replica is.
CAP's "C" is about replication: whether a read anywhere in the system reflects the most recent completed write, regardless of which physical replica served it. A system can be perfectly ACID-consistent (every transaction respects its constraints) on every individual replica while still being CAP-inconsistent overall, because a stale replica can return an old value that was, at the time it was written, a perfectly valid state.
Trade-offs and Common Pitfalls
- Treating CAP as a fixed label for an entire system is a common misreading. The choice is scoped to a partition and can even be scoped per operation: a single system can serve some requests (say, checkout) with a CP posture and others (say, product-view counts) with an AP posture.
- Don't assume "P" is a design choice you can decline. Every distributed system that spans more than one process over a real network needs to survive partial network failure, so the honest framing is which of C or A you give up when partitioned, not whether to support partition tolerance.
- A frequent good follow-up is PACELC, which asks what you trade off between latency and consistency even when there is no partition happening, since CAP alone is silent about that normal-operation case.
What problems does clock skew between machines create in a distributed system? Give at least three concrete examples (event ordering across services, a lease that expires early or late, a TLS certificate that appears valid or invalid depending on which node's clock you ask) and describe, at a high level, why this makes naive wall-clock-based ordering unsafe.
Sample Answer
Direct Answer
Clock skew is the difference between what two machines' clocks read at the same real instant. It matters because any decision that compares timestamps from different machines to decide what happened first, whether a lease is still valid, or whether a certificate is still in its valid window, quietly assumes those clocks agree, and in a real network they don't. A numerically later timestamp on one machine's clock does not reliably mean later in real time once you're comparing across machines.
Three Concrete Problems
1. Event ordering across services. If service A stamps an event with its own local clock and service B stamps a related event with its own local clock, and A's clock runs even slightly ahead of B's, an event that actually happened after (in real time) on B can end up with a numerically smaller timestamp than an earlier event on A. Anything that reconstructs what happened in what order by sorting on raw timestamps can get the sequence backwards. The same failure shows up in machine learning feature pipelines: if a feature-store write on one node happens slightly after a model-serving read on another node consumed the old value, but the writer's clock runs a bit fast, the write can carry an earlier or overlapping timestamp than the read, which corrupts any point-in-time audit of which feature value was actually used for a given prediction, if that audit trusts the raw timestamps.
2. A lease that expires early or late. Distributed locks are commonly granted as leases valid until wall-clock time T. If the holder's clock runs slow relative to the granting service's clock, the holder can believe it still owns the lease past the point the granting service has already reassigned it, expiring late from the holder's point of view and risking two nodes both acting as if they hold the resource. If the holder's clock runs fast instead, it can abandon a still-valid lease early and stop acting well before the granting service considers it expired, causing unnecessary churn.
3. A TLS (Transport Layer Security) certificate that looks valid on one node and invalid on another. Certificate validity is a wall-clock range check, evaluated locally by whichever machine happens to be doing the handshake, against the certificate's not-before and not-after dates. If one node's clock has drifted backward past the not-before date, or forward past the not-after date, that single node rejects a certificate every correctly-clocked node accepts, or the reverse, producing a confusing, node-specific TLS failure that looks like a certificate problem but is actually a clock problem.
A Concrete Trace of Why Naive Ordering Is Unsafe
Node A's clock reads 100 and Node B's clock reads 96 at the same real instant, a 4-unit skew. Event E1 happens on Node A at that instant and is stamped 100. Two time units later in real time, event E2 happens on Node B; by then B's clock reads 98 (96 plus 2), so E2 is stamped 98. Comparing the raw timestamps, 100 is greater than 98, so E1 looks like it happened after E2. But in real time, E2 actually happened after E1. Any process that orders events purely by comparing these timestamps gets the sequence exactly backwards, even though the comparison itself, 100 greater than 98, is arithmetically correct.
Why This Isn't Just a Sync-the-Clocks-Better Problem
Synchronizing physical clocks, with NTP (the Network Time Protocol) or a hardware-disciplined protocol like PTP (Precision Time Protocol), reduces the size of the skew, but it does not make comparing two independently-running clocks perfectly safe; it only shrinks the window in which the trace above can happen. The standard engineering answer for ordering that has to be correct, not just human-readable, is to stop relying on raw wall-clock comparison for that purpose and use a logical clock instead: a counter that each node increments on its own events and carries along with outgoing messages, which correctly captures which events could have influenced which others regardless of clock drift. Production distributed databases often go a step further and use a hybrid logical clock (HLC), which combines a physical-time component with a logical counter, to get correct ordering without giving up a timestamp that's still roughly readable as wall-clock time.
Trade-offs and Pitfalls
- Don't confuse clock skew, which is that two clocks disagree right now, with clock drift, the rate at which they diverge over time; skew is the instantaneous symptom you observe, drift is the ongoing cause, and a monitoring setup that only alerts on one of them will miss the other.
- A common wrong turn is assuming that running NTP means this is handled. NTP typically keeps clocks within milliseconds of each other, which is fine for human-readable log timestamps, but any nonzero skew is still a real correctness risk for anything that depends on strict ordering, so it reduces the problem rather than eliminating it.
- The lease-expiry problem specifically is best addressed by combining a conservative time-to-live with fencing at the protected resource, rejecting stale operations based on a monotonically increasing token rather than on wall-clock time at all, instead of trying to shrink clock skew to zero, which isn't achievable.
Compare NTP-synchronized wall clocks, pure Lamport clocks, and Hybrid Logical Clocks (HLC) as ways to order events across a distributed system. Why do production distributed databases favor HLC over relying on strict NTP synchronization alone or a pure logical clock, and what does HLC give you that neither of the other two does by itself?
Sample Answer
Direct answer
A pure Lamport clock gives causal ordering: if event a happened-before event b, its counter guarantees a's value is less than b's. But the numbers carry no relationship to real elapsed time, so you can't use them to answer "how stale is this" or "did this happen a minute ago or an hour ago." An NTP-synchronized wall clock gives a number that approximates real time, but NTP only bounds error statistically and gives zero causal guarantee: two events with a genuine happens-before relationship on different nodes can still get timestamps in the wrong order if the nodes' clocks are skewed. A hybrid logical clock (HLC) combines a physical-time component with a logical counter so it inherits the causal correctness of a Lamport clock while staying close to real time, which is why production distributed databases (CockroachDB is the standard example) use it instead of relying on either mechanism alone.
Happens-before, briefly
Event a happens-before event b if a causes b directly (same process, sequential order), or a is a message send and b is its corresponding receive, or the relation holds transitively through a chain of such steps. Two events with no such chain between them are concurrent, and no clock discussed here can tell you which one "really" happened first, because there isn't a meaningful answer to that question for concurrent events.
Pure Lamport clocks
Each process keeps an integer counter C. On a local event, C = C + 1. When sending a message, the current C is attached to it. On receiving a message carrying Cm, the process sets C = max(C, Cm) + 1. This guarantees: if a happened-before b, then C(a) < C(b). It does not guarantee the converse: C(a) < C(b) does not mean a happened-before b, since a and b could be concurrent and simply landed on different counter values by coincidence of ordering. A Lamport timestamp also has no connection to real time at all: a value of "3" tells you nothing about whether the event happened a millisecond or a year ago.
NTP-synchronized wall clocks
NTP periodically corrects a machine's local clock against a reference, keeping it within a bounded error window that is typically small on a well-run local network and considerably looser across the public internet. Two problems follow for ordering purposes: first, if two nodes' clocks are off from each other by even a small nonzero amount, a later event on a fast node can get a smaller raw timestamp than an earlier, causally-prior event on a slow node, so ordering purely by wall-clock timestamp can violate causality. Second, an NTP correction can step a machine's clock backward when it's found to be running ahead, so consecutive readings from the very same machine aren't even guaranteed to be increasing, which breaks any protocol that assumes "later timestamp means later real event" on a single node, let alone across two.
Hybrid logical clocks (HLC)
An HLC keeps a pair (pt, l): a physical-time estimate pt and a logical counter l. Update rule:
- On a local event:
pt' = max(local physical clock now, pt). Ifpt'changed (moved forward from a fresh physical reading), resetl = 0; otherwisel = l + 1. Set(pt, l) = (pt', l). - On sending a message: attach the current
(pt, l). - On receiving a message with
(pt_m, l_m):pt' = max(local physical clock now, pt, pt_m). Ifpt'equals both the localptand the message'spt_m, setl' = max(l, l_m) + 1; if it equals only one of them, take that side'sland add 1; if it equals neither (a fresh physical reading is ahead of both), resetl' = 0. - Compare two HLC values by
ptfirst, breaking ties withl.
Because pt is always a running maximum, it can never move backward, even if the underlying physical clock is momentarily behind or gets stepped back by an NTP correction. And because a message receive always folds in the sender's pt_m and bumps l accordingly, HLC preserves the same causal guarantee Lamport clocks give: a send always compares less than its corresponding receive.
Worked example: two nodes with skewed clocks
Node A's physical clock and node B's physical clock disagree by a few units (B is running behind A). Initialize both HLC as (pt=0, l=0).
Pure Lamport clock trace:
- A does local event e1:
Lamport(A) = 1. - A sends message m to B carrying
Lamport = 1. - B does its own local event e2, concurrently, before m arrives:
Lamport(B) = 1. - B receives m:
Lamport(B) = max(1, 1) + 1 = 2. The receive event correctly showsLamport = 2, after both e1 and e2 (each at 1). But e1 and e2 both show1and are genuinely concurrent; the equal values don't mean "simultaneous," they mean "no causal relationship was ever established between them," and the values give no hint whether e1 happened at real time 100 or real time 100,000.
Raw NTP-timestamp trace, same events:
- e1 on A is stamped with A's clock reading: 100.
- A sends m, timestamped 101 by A's clock (one tick later).
- B's local event e2 is stamped with B's clock reading: 99 (B is running 3 units behind A for this illustration).
- B receives m; its clock reads 100 at that moment, so the receive event is stamped 100.
Comparing raw timestamps: m was sent at A-time 101 but appears received at B-time 100, a smaller number, even though the receive must happen after the send. Ordering purely by these NTP timestamps would incorrectly conclude the receive happened before the send.
HLC trace, same events, applying the update rule above:
- e1 on A: physical clock reads 100.
pt' = max(100, 0) = 100, changed, sol = 0.HLC(e1) = (100, 0). - A sends m; A's clock has ticked to 101 by send time:
pt' = max(101, 100) = 101, changed,l = 0.HLC(send) = (101, 0); message m carries(101, 0). - B's local event e2: B's clock reads 99.
pt' = max(99, 0) = 99, changed,l = 0.HLC(e2) = (99, 0). - B receives m=(101,0); B's own physical clock reads 100 at that moment. Apply the receive rule:
pt' = max(100, 99, 101) = 101. Sincept'equals the message'spt_m(101) but not B's ownpt(99) or the local physical reading (100),l' = l_m + 1 = 0 + 1 = 1.HLC(receive) = (101, 1).
Result: HLC(receive) = (101, 1) compares greater than HLC(send) = (101, 0), correctly ordering receive after send, even though B's own physical clock (100) was behind A's send-time clock (101). The HLC borrowed A's physical time component through the max() step specifically to preserve this ordering, which the raw NTP-timestamp comparison above could not do. And the HLC's physical component, 101, stays in the same real-time neighborhood as both clocks (99-101), unlike a pure Lamport counter, which would have produced something like "3" with no relationship to real time at all.
What HLC gives you that neither gives alone
- Monotonicity even when the underlying physical clock runs behind or is stepped backward by an NTP correction, because the
ptcomponent is a running maximum that only ever advances. - The same happens-before guarantee Lamport clocks give, since a message receive always compares greater than its corresponding send.
- A value that stays close to real wall-clock time, which lets you do things a pure Lamport counter cannot: read "as of physical time roughly T", size a lease's expiry in real seconds, or meaningfully compare timestamps from two nodes that never directly exchanged a message. Two Lamport clocks that never communicated are not comparable in any useful sense, since Lamport's ordering guarantee only holds across a causal chain; two HLCs that never communicated still each track real elapsed time, so they remain approximately comparable.
Leases and distributed coordination
A lease grants a component an exclusive right to act until an expiry, and that expiry check has to survive the granting node's physical clock moving. If a lease's expiry were checked against a raw NTP-disciplined wall clock, a backward correction on that clock could make an already-expired lease look valid again. Because HLC's physical component only advances, timestamps derived from it never regress even if the underlying physical clock momentarily does, which is why HLC (or something with the same monotonicity property) is the safer building block underneath lease-based coordination than a raw wall clock.
Trade-offs & pitfalls
| Property | NTP wall clock alone | Pure Lamport clock | HLC |
|---|---|---|---|
| Approximates real time | Yes, bounded by sync accuracy | No | Yes, bounded similarly to the underlying physical clock |
| Guarantees happens-before ordering | No | Yes | Yes |
| Monotonic despite a backward clock correction | No | Yes (it's just a counter) | Yes |
| Comparable across nodes that never exchanged a message | Approximately, subject to skew | No | Approximately |
A common trap is reading Lamport(a) < Lamport(b) as "a happened first" when a and b are actually concurrent; the clock never promised that. Another is assuming HLC removes the need for a synchronized physical clock entirely: HLC's pt component is still fed by the same underlying, NTP-disciplined system clock, so gross clock error still degrades how close HLC stays to real time. HLC protects the ordering and monotonicity guarantees from that error; it doesn't eliminate the error itself.
That is every published Distributed Systems Fundamentals question for Network Engineer so far. Browse the other topics in this category, or practice this one interactively.