Motor与多Sanic Worker兼容性疑问及相关实践咨询
Great question! Let's break this down clearly for you:
Why a naive setup causes problems
First, let's recap how Sanic's multi-worker mode works: it forks the main process to spawn worker processes. As Motor's docs explicitly state, it doesn't support forking or multi-threading—it’s built for single-threaded Tornado environments where the IOLoop and connection resources are isolated to one process.
If you initialize a Motor client in the main process before Sanic forks workers, all workers will end up sharing the same underlying socket connections and IOLoop state. This leads to all sorts of unpredictable behavior: connection drops, data corruption, deadlocks, or requests hanging indefinitely. Motor’s internals simply aren’t designed to handle shared resources across separate processes.
Yes, developers have tried this (and there are proven solutions)
The key fix is to initialize Motor separately in each worker process, not in the main process before forking. Here are the most common, community-vetted approaches:
- Initialize Motor in a post-fork listener
Sanic provides listeners that run after each worker finishes starting up. Use theafter_server_startlistener to create your Motor client instance per worker. This ensures every worker gets its own isolated connection pool and IOLoop association.
Example code snippet:from sanic import Sanic from sanic.response import json from motor.motor_asyncio import AsyncIOMotorClient app = Sanic("MotorSanicApp") @app.listener("after_server_start") async def setup_motor(app, loop): # Create a unique Motor client for this worker app.ctx.mongo_client = AsyncIOMotorClient("mongodb://localhost:27017") app.ctx.db = app.ctx.mongo_client.my_database @app.route("/get-document") async def fetch_doc(request): doc = await request.app.ctx.db.test_collection.find_one() return json({"document": doc}) if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, workers=4) - Avoid global Motor instances
Never create a Motor client at the module level (which runs in the main process). Instead, create it within the worker’s context—either via the listener above, or lazy-initialize it when the first request hits the worker.
Community feedback
Plenty of Sanic developers have successfully used Motor with multiple workers using these patterns. You’ll find discussions in Sanic’s GitHub issues, Reddit threads, and community forums where folks share their working setups. The core takeaway is simple: never share a Motor client across forked processes; let each worker manage its own client instance.
内容的提问来源于stack exchange,提问作者CarlJogenburg

