How to mock directory-based sharding in Python

Simulates distributing files into logical shards using a deterministic hash of each filename, mocking how a database might shard rows across nodes.

Easy Python 3.9+ Aug 9, 2026 Database scaling & optimization 14 views 0 copies

Python code

42 lines
Python 3.9+
import os
import hashlib
from collections import defaultdict
from pathlib import Path


def get_shard_for_key(key: str, num_shards: int) -> int:
    """Return a deterministic shard index (0..num_shards-1) for a key."""
    digest = hashlib.md5(key.encode('utf-8')).hexdigest()
    return int(digest, 16) % num_shards


def distribute_files_to_shards(source_dir: str, num_shards: int) -> dict:
    """Distribute files from a flat source directory into logical shards."""
    shard_buckets = defaultdict(list)
    source_path = Path(source_dir)

    if not source_path.is_dir():
        raise ValueError(f"Not a directory: {source_path}")

    for file_path in source_path.iterdir():
        if not file_path.is_file():
            continue
        filename = file_path.name
        shard_index = get_shard_for_key(filename, num_shards)
        shard_buckets[shard_index].append(filename)

    return dict(sorted(shard_buckets.items()))


if __name__ == "__main__":
    demo_dir = Path("./data")
    demo_dir.mkdir(exist_ok=True)

    for name in ("alice.csv", "bob.txt", "carol.log", "dave.json",
                 "eve.txt", "frank.csv", "grace.md", "heidi.log"):
        (demo_dir / name).write_text("sample content\n")

    shards = distribute_files_to_shards(str(demo_dir), num_shards=3)

    for shard_id, files in shards.items():
        print(f"Shard {shard_id}: {', '.join(files)}")

Output

stdout
Shard 0: alice.csv, carol.log, eve.txt
Shard 1: bob.txt, frank.csv, heidi.log
Shard 2: dave.json, grace.md

How it works

get_shard_for_key hashes the filename with MD5, converts the hex digest to an integer, and modulates by the shard count — matching how many databases pick a node for a row key. distribute_files_to_shards walks a directory, collects only regular files, and buckets them into a defaultdict keyed by shard index. Sorting the dict produces stable, readable output. This lightweight pattern is a useful prototype for testing partition logic before wiring up a real database.

Common mistakes

  • Using a weak hash like `hash()` that is randomized per process and breaks determinism
  • Forgetting to skip subdirectories, which pollutes shard buckets with non-file entries
  • Hard-coding the shard count instead of passing it as a parameter, making scale tests awkward

Variations

  1. Use `struct.unpack` on the digest bytes for a slightly faster arithmetic path
  2. Replace MD5 with SHA-256 if your mock needs to mirror a security-conscious real system

Real-world use cases

  • Prototyping a partition key strategy in a design doc before implementing it in PostgreSQL or MySQL.
  • Building a quick load-balancing simulation for a file-processing pipeline across worker nodes.
  • Testing an ETL tool's expected file-to-shard mapping without spinning up an actual cluster.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Database scaling & optimization

Related tutorials and quizzes for this topic.