Core Concepts & Architecture

Mongomock runs entirely in-memory, simulating MongoDB's document storage, synchronization model, and time-to-live indexes. This guide explains how these components work under the hood.

The Memory Hierarchy (store.py)

Mongomock represents the MongoDB server hierarchy using three core storage classes in mongomock/store.py:

ServerStore (Simulates a single physical database server)
  └── DatabaseStore (Simulates logical databases; manages collection lifecycles)
        └── CollectionStore (Simulates individual tables; manages documents, indexes, and locks)

Document Partitioning

Each CollectionStore holds an in-memory collections.OrderedDict that maps document _id values to their actual data dictionaries. Using an OrderedDict ensures that query results preserve document insertion order by default, mimicking MongoDB's natural disk storage behavior.

# Simplified visual of CollectionStore state:
self._documents = OrderedDict([
    (ObjectId('507f1f77bcf86cd799439011'), {'_id': ObjectId(...), 'name': 'alice'}),
    (ObjectId('507f1f77bcf86cd799439012'), {'_id': ObjectId(...), 'name': 'bob'}),
])

Copy-on-Write and Copy-on-Read Boundaries

To simulate network boundaries and independent transactional scopes, Mongomock performs deep copies on both inputs and outputs:

  • On Insertion/Update: When your application writes a document, Mongomock deep-copies the dictionary before writing it to the OrderedDict. Modifying the original dict in your application will not alter the stored database state.
  • On Query Retrieval: When iterating through a query Cursor, Mongomock deep-copies the stored document before returning it to the caller. This isolates test assertions from mutating the actual memory store.

Thread Synchronization (thread.py)

Applications running concurrent tasks, multi-threaded workers, or asynchronous event loops can trigger race conditions on shared memory databases. Mongomock implements a custom Reader-Writer Lock (RWLock) in mongomock/thread.py to handle this safely.

from mongomock.thread import RWLock

# Each CollectionStore initializes an exclusive RWLock
self._rwlock = RWLock()

This implementation uses a custom light-switch mechanism (_LightSwitch) to prioritize writers and prevent writer starvation:

  • Concurrent Reads: Multiple threads can safely read document states simultaneously. Calling find(), find_one(), or iterating over a Cursor acquires a shared reader lock.
  • Exclusive Writes: Modifying methods like insert_one(), update_many(), or delete_one() acquire an exclusive writer lock. This blocks all incoming reads and writes until the current operation completes, maintaining data consistency.

DateTime Truncation and UTC Normalization

When writing datetime values to a real MongoDB database, the database engine normalizes the values to naive UTC dates with millisecond-level precision. Mongomock simulates this process via helper methods in mongomock/helpers.py:

  • UTC Normalization: If your client is configured with tz_aware=True, datetime structures are normalized to UTC. Otherwise, timezone details are stripped entirely.
  • Microsecond Truncation: Python's datetime library tracks time down to the microsecond, but MongoDB only stores millisecond precision. Mongomock rounds microseconds down to the nearest millisecond:
# Microseconds are rounded down to match MongoDB's precision:
# Python Datetime: 2026-03-31 15:45:30.123456
# Mongomock Store: 2026-03-31 15:45:30.123000

TTL (Time-To-Live) Index Expiration

MongoDB automatically purges documents from a collection after a set period using TTL indexes. Mongomock simulates this process in memory:

  1. Index Definition: When you define an index with expireAfterSeconds, Mongomock registers it as a TTL index inside the CollectionStore's _ttl_indexes map.
  2. On-Demand Purging: Mongomock runs a purge sweep (_remove_expired_documents()) before executing any read operation (find(), count_documents(), etc.).
  3. Age Calculation: The sweeper compares the current system time (via mongomock.utcnow()) against the indexed date field of each document. Any documents older than expireAfterSeconds are deleted from the in-memory store before the query resolves.

Next, explore how to perform CRUD operations, updates, and custom filters in the Usage Guide.