Fix a Round-Robin Load Balancer, Then Implement Consistent Hashing

Read the full interview experience this question came from →

Quick Overview

A debugging-style coding round on request routing: make a round-robin load balancer behave correctly as servers join and leave, then implement consistent hashing so requests with the same key stay on the same server. It tests careful state handling, concurrency, edge cases such as an empty pool, and reasoning about how many keys move when membership changes.

Fix a Round-Robin Load Balancer, Then Implement Consistent Hashing

Company: DoorDash

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Onsite

In a debugging-format onsite round, you work on the component that decides which backend server handles each incoming request. The round has two pieces. First comes a classic round-robin load balancer that has to be made to behave correctly. Then you write an implementation of consistent hashing yourself. The original code from the round was not reported, so practice it as follows. Build the round-robin balancer yourself, and for each defect you would hunt for in an existing implementation, explain how it shows up and which test would expose it. Then write the consistent-hashing router. Use Python or another language you are comfortable with, and keep both components runnable and testable. ### Clarifying Questions - Is the balancer called by many threads at once, or by a single event loop? - Can servers be added or removed while traffic is flowing? - Should the balancer skip servers that are unhealthy, and who tells it that a server is unhealthy? - For consistent hashing, what is the routing key (for example a user or order identifier), and is any hash function acceptable? ### Part 1 — Make the round-robin balancer correct Implement a balancer over an ordered list of server identifiers. It needs `next_server()`, which hands out servers in rotation, plus `add_server(server)` and `remove_server(server)`. Then list the defects you would look for when an existing round-robin implementation misbehaves. For each one, give the symptom it causes and a test that reproduces it deterministically. ```hint Watch the position Trace the stored rotation position when the list shrinks below it, and when a server that comes before it in the list is removed. ``` ```hint Two callers at once Ask whether reading the current position and advancing it happen as one step when two requests arrive together. ``` #### Clarifying Questions for this Part - After a server is removed mid-rotation, must the next server be exactly the removed server's successor, or is one skipped or repeated turn acceptable? - What should `next_server()` do when the pool is empty? #### What This Part Should Cover - Rotation semantics, including wraparound and an empty or single-server pool - How the rotation position stays valid when servers join or leave mid-rotation - Atomicity of the read-and-advance step under concurrent callers - A deterministic test for each defect named ### Part 2 — Implement consistent hashing Requests now carry a key, and every request with the same key should reach the same server. When a server joins or leaves, only a small fraction of keys may change servers. Implement a router with `add_server(server)`, `remove_server(server)` and `get_server(key)`, and state the time complexity of each operation. ```hint Lookup speed Avoid examining every server on each lookup. Think about how the server positions could be stored so that the right one is found quickly. ``` ```hint Uneven shares With only a few servers, check how evenly the key space is divided among them, and what you could change to even it out without adding machines. ``` #### What This Part Should Cover - How servers and keys are mapped into one hash space, and how a key finds its server, including at the top of the range - How evenly load is spread with few servers, and how that is improved - Exactly which keys move when a server joins or leaves - The choice of hash function, and the complexity of add, remove and lookup ### What a Strong Answer Covers - Defects found by reproducing them with failing tests before fixing them, not by rewriting blindly - Correct behavior at the boundaries of both components: empty pool, single server, wraparound - Thread safety and runtime membership changes handled in both parts - A clear contrast between round robin (even spreading, no affinity) and consistent hashing (key affinity, minimal movement) - Accurate complexity for every operation ### Follow-up Questions - How would you add weights so that a server with twice the capacity receives twice the traffic, first in round robin and then in the hash ring? - If a server is unhealthy but still on the ring, where should its keys go, and how do you avoid overloading a single neighbor? - How would you verify, with tests or metrics, that keys are spread evenly and that only the expected keys move when a server is added? - If several balancer instances run in separate processes, how do they agree on the rotation or on the ring?

Overview: A debugging-style coding round on request routing: make a round-robin load balancer behave correctly as servers join and leave, then implement consistent hashing so requests with the same key stay on the same server. It tests careful state handling, concurrency, edge cases such as an empty pool, and reasoning about how many keys move when membership changes.

Read the full DoorDash Software Engineer interview experience this question came from

|Home/Software Engineering Fundamentals/DoorDash
DoorDash logo
DoorDash
Jan 18, 2026
mediumSoftware EngineerOnsiteSoftware Engineering Fundamentals
0
0

In a debugging-format onsite round, you work on the component that decides which backend server handles each incoming request. The round has two pieces. First comes a classic round-robin load balancer that has to be made to behave correctly. Then you write an implementation of consistent hashing yourself.

The original code from the round was not reported, so practice it as follows. Build the round-robin balancer yourself, and for each defect you would hunt for in an existing implementation, explain how it shows up and which test would expose it. Then write the consistent-hashing router. Use Python or another language you are comfortable with, and keep both components runnable and testable.

Clarifying Questions Guidance

  • Is the balancer called by many threads at once, or by a single event loop?
  • Can servers be added or removed while traffic is flowing?
  • Should the balancer skip servers that are unhealthy, and who tells it that a server is unhealthy?
  • For consistent hashing, what is the routing key (for example a user or order identifier), and is any hash function acceptable?

Part 1 — Make the round-robin balancer correct

Implement a balancer over an ordered list of server identifiers. It needs next_server(), which hands out servers in rotation, plus add_server(server) and remove_server(server). Then list the defects you would look for when an existing round-robin implementation misbehaves. For each one, give the symptom it causes and a test that reproduces it deterministically.

Clarifying Questions for this Part Guidance

  • After a server is removed mid-rotation, must the next server be exactly the removed server's successor, or is one skipped or repeated turn acceptable?
  • What should next_server() do when the pool is empty?

What This Part Should Cover Guidance

  • Rotation semantics, including wraparound and an empty or single-server pool
  • How the rotation position stays valid when servers join or leave mid-rotation
  • Atomicity of the read-and-advance step under concurrent callers
  • A deterministic test for each defect named

Part 2 — Implement consistent hashing

Requests now carry a key, and every request with the same key should reach the same server. When a server joins or leaves, only a small fraction of keys may change servers. Implement a router with add_server(server), remove_server(server) and get_server(key), and state the time complexity of each operation.

What This Part Should Cover Guidance

  • How servers and keys are mapped into one hash space, and how a key finds its server, including at the top of the range
  • How evenly load is spread with few servers, and how that is improved
  • Exactly which keys move when a server joins or leaves
  • The choice of hash function, and the complexity of add, remove and lookup

What a Strong Answer Covers Guidance

  • Defects found by reproducing them with failing tests before fixing them, not by rewriting blindly
  • Correct behavior at the boundaries of both components: empty pool, single server, wraparound
  • Thread safety and runtime membership changes handled in both parts
  • A clear contrast between round robin (even spreading, no affinity) and consistent hashing (key affinity, minimal movement)
  • Accurate complexity for every operation

Follow-up Questions Guidance

  • How would you add weights so that a server with twice the capacity receives twice the traffic, first in round robin and then in the hash ring?
  • If a server is unhealthy but still on the ring, where should its keys go, and how do you avoid overloading a single neighbor?
  • How would you verify, with tests or metrics, that keys are spread evenly and that only the expected keys move when a server is added?
  • If several balancer instances run in separate processes, how do they agree on the rotation or on the ring?
Loading comments...