Code review exercise: find concurrency, failure and ownership bugs in a small job queue

Read the full interview experience this question came from →

Quick Overview

Code review interview exercise: review a small job queue in which several consumer threads claim and process work, identify gaps in concurrency, failure handling, job ownership and API design, rank them by severity, and propose a corrected design with tests. It tests practical review judgment on real code.

Code review exercise: find concurrency, failure and ownership bugs in a small job queue

Company: MongoDB

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Onsite

This round hands you a small coding project and asks you to review it as you would a colleague's work: find the gaps, the implementation problems and the API issues, explain why each one matters, and say what you would change. The project used in the interview is not reproduced here. For practice, review the job queue module below, in which producers submit jobs and several consumer threads claim and process them. Assume it is meant to run inside one long-running service process. ```python import threading import time import uuid class Job: def __init__(self, payload, max_retries=3): self.id = str(uuid.uuid4()) self.payload = payload self.max_retries = max_retries self.attempts = 0 self.status = "queued" # queued | running | done | failed self.owner = None class JobQueue: def __init__(self): self.jobs = [] self.lock = threading.Lock() def submit(self, payload, max_retries=3): job = Job(payload, max_retries) self.jobs.append(job) return job def claim(self, consumer_id): for job in self.jobs: if job.status == "queued": with self.lock: job.status = "running" job.owner = consumer_id return job return None def complete(self, job_id, consumer_id): job = self._find(job_id) job.status = "done" def fail(self, job_id, consumer_id): job = self._find(job_id) job.attempts += 1 if job.attempts < job.max_retries: job.status = "queued" job.status = "failed" def get_jobs(self): return self.jobs def _find(self, job_id): for job in self.jobs: if job.id == job_id: return job def consumer_loop(queue, consumer_id, handler): while True: job = queue.claim(consumer_id) if job is None: time.sleep(1) continue try: handler(job.payload) except Exception: pass queue.complete(job.id, consumer_id) ``` ### Constraints and Clarifications - Treat this as a real review: findings range from merge-blocking defects to minor polish, and you should say which is which. - Intended behavior: each submitted job is processed by one consumer at a time, a failed job is retried a limited number of times and then marked failed, and callers can check a job's status. - The module does not claim durability across process restarts; decide whether to raise it. ### Clarifying Questions - Is processing a job twice harmful, or are the handlers idempotent? - What should happen to a job whose consumer dies halfway through? - Who calls `get_jobs`, and what do they need from it? - Roughly how many jobs and consumers are expected? ### Part 1 — Find the problems List the problems you see, classify each as blocking or non-blocking, and explain the consequence of each one. ```hint Read it as two consumers Trace two consumer threads and one failing handler through the code at the same time. ``` #### What This Part Should Cover - Concurrency defects - Failure-handling defects, including the retry logic - API and ownership problems - A clear severity classification ### Part 2 — Fix the API and ownership model Propose a corrected interface and show corrected code for claiming a job and for reporting success or failure. ```hint Prove you still own it Think about what a consumer should present when it reports a result, so that a late or stale report can be rejected. ``` #### What This Part Should Cover - An atomic claim - A proof of ownership on completion and failure - Recovery of work from consumers that die - Explicit errors and correct retry semantics ### Part 3 — Tests and verdict Which tests would have caught these problems, and what is your verdict on the change as written? ```hint Test the unhappy paths List the scenarios the original code gets wrong and turn each into a deterministic test. ``` #### What This Part Should Cover - Deterministic tests for concurrency and failure paths - Time-dependent behavior tested without real sleeps - A clear verdict with the required changes ### What a Strong Answer Covers - Correctness and failure handling prioritized over style - Each finding tied to a concrete consequence (double processing, lost failures, stranded jobs) - A corrected design that fixes the root causes rather than patching symptoms - Tests that would fail on the original code - A clear verdict and priority order the author can act on ### Follow-up Questions - The handlers cannot be made idempotent. What guarantees can the queue offer, and at what cost? - How would you make the queue survive a process restart? - How would you add per-job priorities without starving low-priority work? - How would you review this differently if the author were new to concurrent code?

Overview: Code review interview exercise: review a small job queue in which several consumer threads claim and process work, identify gaps in concurrency, failure handling, job ownership and API design, rank them by severity, and propose a corrected design with tests. It tests practical review judgment on real code.

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

MongoDB logo
MongoDB
Jul 12, 2026
mediumSoftware EngineerOnsiteSoftware Engineering Fundamentals
2
0

This round hands you a small coding project and asks you to review it as you would a colleague's work: find the gaps, the implementation problems and the API issues, explain why each one matters, and say what you would change. The project used in the interview is not reproduced here. For practice, review the job queue module below, in which producers submit jobs and several consumer threads claim and process them. Assume it is meant to run inside one long-running service process.

import threading
import time
import uuid


class Job:
    def __init__(self, payload, max_retries=3):
        self.id = str(uuid.uuid4())
        self.payload = payload
        self.max_retries = max_retries
        self.attempts = 0
        self.status = "queued"  # queued | running | done | failed
        self.owner = None


class JobQueue:
    def __init__(self):
        self.jobs = []
        self.lock = threading.Lock()

    def submit(self, payload, max_retries=3):
        job = Job(payload, max_retries)
        self.jobs.append(job)
        return job

    def claim(self, consumer_id):
        for job in self.jobs:
            if job.status == "queued":
                with self.lock:
                    job.status = "running"
                    job.owner = consumer_id
                return job
        return None

    def complete(self, job_id, consumer_id):
        job = self._find(job_id)
        job.status = "done"

    def fail(self, job_id, consumer_id):
        job = self._find(job_id)
        job.attempts += 1
        if job.attempts < job.max_retries:
            job.status = "queued"
        job.status = "failed"

    def get_jobs(self):
        return self.jobs

    def _find(self, job_id):
        for job in self.jobs:
            if job.id == job_id:
                return job


def consumer_loop(queue, consumer_id, handler):
    while True:
        job = queue.claim(consumer_id)
        if job is None:
            time.sleep(1)
            continue
        try:
            handler(job.payload)
        except Exception:
            pass
        queue.complete(job.id, consumer_id)

Constraints and Clarifications

  • Treat this as a real review: findings range from merge-blocking defects to minor polish, and you should say which is which.
  • Intended behavior: each submitted job is processed by one consumer at a time, a failed job is retried a limited number of times and then marked failed, and callers can check a job's status.
  • The module does not claim durability across process restarts; decide whether to raise it.

Clarifying Questions Guidance

  • Is processing a job twice harmful, or are the handlers idempotent?
  • What should happen to a job whose consumer dies halfway through?
  • Who calls get_jobs , and what do they need from it?
  • Roughly how many jobs and consumers are expected?

Part 1 — Find the problems

List the problems you see, classify each as blocking or non-blocking, and explain the consequence of each one.

What This Part Should Cover Guidance

  • Concurrency defects
  • Failure-handling defects, including the retry logic
  • API and ownership problems
  • A clear severity classification

Part 2 — Fix the API and ownership model

Propose a corrected interface and show corrected code for claiming a job and for reporting success or failure.

What This Part Should Cover Guidance

  • An atomic claim
  • A proof of ownership on completion and failure
  • Recovery of work from consumers that die
  • Explicit errors and correct retry semantics

Part 3 — Tests and verdict

Which tests would have caught these problems, and what is your verdict on the change as written?

What This Part Should Cover Guidance

  • Deterministic tests for concurrency and failure paths
  • Time-dependent behavior tested without real sleeps
  • A clear verdict with the required changes

What a Strong Answer Covers Guidance

  • Correctness and failure handling prioritized over style
  • Each finding tied to a concrete consequence (double processing, lost failures, stranded jobs)
  • A corrected design that fixes the root causes rather than patching symptoms
  • Tests that would fail on the original code
  • A clear verdict and priority order the author can act on

Follow-up Questions Guidance

  • The handlers cannot be made idempotent. What guarantees can the queue offer, and at what cost?
  • How would you make the queue survive a process restart?
  • How would you add per-job priorities without starving low-priority work?
  • How would you review this differently if the author were new to concurrent code?