Share a cache across application workers¶
Use SharedCacheServer when trusted workers on one host repeatedly make eligible collection reads. The server starts one cache-owner process; workers reuse its values and one change stream per active database. Sync and asyncio workers can share the same server. Each worker still owns a PyMongo client for misses, writes and uncached reads.
Compare the two managers before adopting this model. Sharing supports fewer cached operations and requires an application parent that stays alive until every worker has stopped.
Supported deployment¶
Check the package, shared-host and MongoDB requirements, and review the worker trust boundary before passing a connection to workers.
Startup and shutdown¶
Run the FastAPI and Granian application for a complete multi-process integration. Its parent starts the server once and passes cache_server.connection through a picklable application loader. Each worker's lifespan creates its client and attaches its manager.
The same ownership pattern works for background workers:
from concurrent.futures import ProcessPoolExecutor
from multiprocessing import get_context
from pymongo import MongoClient
from client_query_cache import SharedCacheManager, SharedCacheServer
def worker(connection):
with (
MongoClient("mongodb://localhost:27017", timeoutMS=5000) as client,
SharedCacheManager(client, connection) as manager,
):
return manager["catalogue"]["products"].find_one({"_id": "book"})
if __name__ == "__main__":
with SharedCacheServer(
"mongodb://localhost:27017",
("catalogue",),
client_options={"timeoutMS": 5000},
) as cache_server:
with ProcessPoolExecutor(
max_workers=4, mp_context=get_context("spawn")
) as pool:
print(list(pool.map(worker, (cache_server.connection,) * 4)))
This small program demonstrates ownership, not a guaranteed hit: the document must exist and initial reads can bypass while the stream starts. The web example verifies actual cross-process reuse.
An asyncio worker uses async with AsyncMongoClient(...) as client, SharedCacheManager(client, connection) as manager with the manager from client_query_cache.asynchronous. Alternatively, call await SharedCacheManager.attach(client, connection) and later await manager.close(). Construct and use that manager in one event loop.
Close managers before clients, stop workers, then close the server. Pass only cache_server.connection to workers. Clients, managers and server handles must not be reused after fork. Never start a server in each FastAPI lifespan: that produces independent caches. If the application supervisor cannot own and distribute one connection, use process-local managers.
Which reads share data?¶
The shared-read contract defines eligible queries, supported options, direct-read fallback and admission behavior. Use it to check whether your application's read mix can benefit from sharing.
Collection names and capacity¶
namespaces sets how many database/collection pairs the server can track at once. Size it for the collections workers use concurrently. A processed collection drop releases its capacity, so applications can create, use and remove collections over time.
When every slot is in use, reads for another collection still run through MongoDB and bypass shared caching. They do not add namespace metadata; caching can resume after a tracked collection is dropped. For example, events_2026_10_17 and events_2026_10_18 can be separate collections when the configured capacity covers the number used at the same time.
For example, store events from every day in one events collection, with the day recorded in each document:
Then collection.find({"day": "2026-10-17"}) selects a day's events without creating a new collection. See the shared-read contract for caching eligibility. MongoDB's indexing guidance explains when an index on the queried field helps.
Use the SharedCacheConfig field reference for memory and index sizing, and the memory accounting reference when interpreting process measurements.
Connection capacity must cover each worker's control and request connections; synchronous request threads can need additional connections. If a needed connection cannot be opened, the read falls back to MongoDB with bounded retry cooldowns. Size the owner's MongoDB pool for one concurrent stream poll per active database, and worker pools for their own traffic. Precise native layout bounds and memory accounting are in the maintainer reference.
Consistency and recovery¶
For read-after-write and authorization decisions, follow the consistency guidance.
If the owner silently stalls, hits stop within three seconds of its last successful upstream stream poll. That is an outage safeguard, not a maximum write-to-invalidation delay. Detected transport disconnects disable the affected mapping immediately. During temporary owner outages, workers use their own clients for direct reads, with bounded attachment attempts and retry cooldowns. The database operation still depends on the client's timeouts and can fail.
Use cache_server.restart() for explicit cache-owner recovery. There is no automatic election or cross-host failover. Parent loss stops the owner. See the API failure reference for errors that require application or operator action.
Security and monitoring¶
Keep SharedCacheConnection private; do not log or persist it. All participants must use the same MongoDB deployment, credentials and read visibility. A connection grants access to every configured database. Never share it across security principals or mutually untrusted workers, even if collection names contain tenant IDs.
The private Unix endpoint is restricted to its operating-system user and requires the connection credential. Workers receive read-only shared value mappings, but every credential holder is trusted to read all shared values and submit values for caching. Sharing is not an authorization boundary. Follow the freshness and authorization guidance when access changes.
Use the shared-manager monitoring guide to interpret worker counters and investigate low reuse. The release workload measurements provide a comparison to reproduce for your application.