Skip to content
Reliable Data Engineering
Practice problem hard external-sortheapgenerators
Solve it in the browser (Python editor)

Sort Data Larger Than Memory (External Merge Sort)

Difficulty: Hard · Topics: external-sort, heap, generators · Asked at: Google, Snowflake, Oracle, Databricks

Problem

Implement external_sort(stream, max_in_memory) that yields all items of stream in sorted order while holding at most max_in_memory items in memory during the run-creation phase. Simulate “disk” with tempfile.TemporaryFile (write one item per line as text; items are strings without newlines). Return a generator.

Starter code

import heapq, tempfile
from typing import Iterable, Iterator

def external_sort(stream: Iterable[str], max_in_memory: int) -> Iterator[str]:
    pass

Hints

Hint 1

Phase 1: read chunks of max_in_memory, sort each, write each to its own temp file (a sorted run).

Hint 2

Phase 2: k-way merge the runs with heapq.merge over line iterators, stripping newlines.

Solution

import heapq, tempfile
from itertools import islice
from typing import Iterable, Iterator

def external_sort(stream: Iterable[str], max_in_memory: int) -> Iterator[str]:
    it = iter(stream)
    runs = []
    while chunk := list(islice(it, max_in_memory)):
        chunk.sort()
        f = tempfile.TemporaryFile("w+")
        f.writelines(x + "\n" for x in chunk)
        f.seek(0)
        runs.append(f)
    try:
        for line in heapq.merge(*runs):
            yield line.rstrip("\n")
    finally:
        for f in runs:
            f.close()

Tests

Your solution should pass these:

import random
rng = random.Random(3)
data = [f"{rng.randint(0, 10**6):07d}" for _ in range(5000)]
assert list(external_sort(data, 300)) == sorted(data)
assert list(external_sort([], 10)) == []
assert list(external_sort(["b", "a"], 1)) == ["a", "b"]

Explanation

Phase 1 creates ⌈N/M⌉ sorted runs; phase 2 merges them in O(N log runs). If there are too many runs to open at once, merge in multiple passes (merge 100 runs at a time). This is how databases sort beyond memory (the “spill to disk” you see in Spark UI during sort-merge joins and sorts), and why spill makes jobs slow: every spilled byte is written and read again.