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 aCursoracquires a shared reader lock. - Exclusive Writes: Modifying methods like
insert_one(),update_many(), ordelete_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:
- Index Definition: When you define an index with
expireAfterSeconds, Mongomock registers it as a TTL index inside theCollectionStore's_ttl_indexesmap. - On-Demand Purging: Mongomock runs a purge sweep (
_remove_expired_documents()) before executing any read operation (find(),count_documents(), etc.). - Age Calculation: The sweeper compares the current system time (via
mongomock.utcnow()) against the indexed date field of each document. Any documents older thanexpireAfterSecondsare 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.