sanic: Blueprint with multiple workers not usable on Windows due to pickling error

Describe the bug When using a blueprint with multiple workers on windows, sanic fails on startup due to a failure to pickle the route:

Exception has occurred: _pickle.PicklingError Can’t pickle <class ‘sanic.blueprints.Route’>: attribute lookup Route on sanic.blueprints failed

Code snippet

from sanic import Sanic
from sanic import Blueprint
from sanic.response import json

blueprint = Blueprint("API_blueprint")

@blueprint.route("/")
async def test(request):
    return json({"hello": "world"})

app = Sanic()

app.blueprint(blueprint)

def main():
    app.run(workers=2)

Expected behavior It should be possible to run with multiple workers using blueprints in the same way as it is using the sanic app object directly

Environment (please complete the following information):

  • OS: Windows
  • Version 7

Additional context A similar issue with pickling was recently fixed in sanic-cors. No idea if the resolution to that one will be helpful

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 16 (10 by maintainers)

Commits related to this issue

Most upvoted comments

PR created: https://github.com/huge-success/sanic/pull/1393 The blueprint pickling problem is fixed, though I don’t have a windows machine to test that it fixes the multiprocessing issue on Windows.

If it does returns different pids, then I guess it supports this feature now

Just tried it on my Windows 10 machine and it works fine. Different pids

I would suggest docker and kubernetes as the easiest way to spin up multiple processes in production.

Does that mean that if I had multiple workers working the other day, then Windows must support it?

Perhaps. Try this simple application (based on yours at the start of this issue) and see if it returns different pid numbers (and if this is the correct way to get the pid under Windows as well):

import os
from sanic import Sanic
from sanic.response import text

app = Sanic()


@app.route("/")
async def test(request):
    return text(str(os.getpid()))


def main():
    app.run(workers=4)


if __name__ == "__main__":
    main()

If it does returns different pids, then I guess it supports this feature now 😃

@mungojam

I haven’t done it before and I think I’ve been spoilt by frameworks and languages that handled all that for me and did multithreading without a GIL to worry about

No problem, in fact this has not to do with GIL or even Python. I used to deliver solutions for the Windows platform for quite some time, and from what I can remember (at least up until Windows 2008 Server), one process (no matter which technology it was written) simply could not share the same socket with another process (which can be easily achieved in Linux and I guess most BSD based systems, including macOS if I’m not mistaken).

This option (to share a socket connection) can be seen here in Sanic, under the serve_multiple function (that is called if worker is greater than 1 in app.run).

I might be - and probably am - very outdated regarding this feature, even though my only concern is that the Windows kernel is the same for the past 20 years (NT). That’s what I want to check out 😉

I can look into this.