Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation regarding "Attached to a different loop" error #688

Closed
frankie567 opened this issue Aug 12, 2021 · 8 comments
Closed

Add documentation regarding "Attached to a different loop" error #688

frankie567 opened this issue Aug 12, 2021 · 8 comments
Labels
documentation Improvements or additions to documentation

Comments

@frankie567
Copy link
Member

Following discussion #663 and issue #679, we should add documentation regarding the "Attached to a different loop" error, since it's quite common, especially with Motor.

@frankie567 frankie567 added the documentation Improvements or additions to documentation label Aug 12, 2021
@rnd42
Copy link
Contributor

rnd42 commented Aug 24, 2021

FYI thanks to the above issues I think I figured out my own variant of this issues. It seems you can hit the same error when using Gunicorn with Uvicorn workers and the --preload/preload_app option. I just figured I'd add that here so the eventual documentation can inclued it.

@frankie567
Copy link
Member Author

Thanks @rnd42 for raising this! Is the workaround the same in this case?

@rnd42
Copy link
Contributor

rnd42 commented Aug 26, 2021

In my case I was trying to move the database setup into an on startup function which just had more and more knock on effects... Had to move creating the FastAPIUsers instance into on startup so the database would exist, then had to move the router generation because the FastAPIUsers didn't exist yet, etc, etc... I finally stopped to do some research, found these issues, reverted my changes and just disabled the Gunicorn preload option and everything just worked.

@frankie567
Copy link
Member Author

Ok, it doesn't surprise me that --preload creates unintented side effects.

Actually, I feel that all this happens because Motor, contrary to most database drivers, doesn't have a "connect"/"disconnect" mechanism: it directly wires the I/O loop at initialization.

@renan-r-santos
Copy link

renan-r-santos commented Sep 1, 2021

I'm facing similar issues too with gunicorn. I don't use --preload option in gunicorn but I'm still getting "Attached to a different loop" error. In my case this is what I'm doing:

  • Dockerfile
    CMD ["gunicorn", "--config=gunicorn_conf.py", "--worker-class=uvicorn.workers.UvicornWorker", "main:app"]

  • gunicorn_conf.py

import json
import multiprocessing
import os

workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1")
max_workers_str = os.getenv("MAX_WORKERS")
use_max_workers = None
if max_workers_str:
    use_max_workers = int(max_workers_str)
web_concurrency_str = os.getenv("WEB_CONCURRENCY", None)

host = os.getenv("HOST", "0.0.0.0")
port = os.getenv("PORT", "80")
bind_env = os.getenv("BIND", None)
use_loglevel = os.getenv("LOG_LEVEL", "info")
if bind_env:
    use_bind = bind_env
else:
    use_bind = f"{host}:{port}"

cores = multiprocessing.cpu_count()
workers_per_core = float(workers_per_core_str)
default_web_concurrency = workers_per_core * cores
if web_concurrency_str:
    web_concurrency = int(web_concurrency_str)
    assert web_concurrency > 0
else:
    web_concurrency = max(int(default_web_concurrency), 2)
    if use_max_workers:
        web_concurrency = min(web_concurrency, use_max_workers)
accesslog_var = os.getenv("ACCESS_LOG", "-")
use_accesslog = accesslog_var or None
errorlog_var = os.getenv("ERROR_LOG", "-")
use_errorlog = errorlog_var or None
graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120")
timeout_str = os.getenv("TIMEOUT", "120")
keepalive_str = os.getenv("KEEP_ALIVE", "5")

# Gunicorn config variables
loglevel = use_loglevel
workers = web_concurrency
bind = use_bind
errorlog = use_errorlog
worker_tmp_dir = "/dev/shm"
accesslog = use_accesslog
graceful_timeout = int(graceful_timeout_str)
timeout = int(timeout_str)
keepalive = int(keepalive_str)

@frankie567
Copy link
Member Author

@scherzocrk Could you start a discussion and share with us your main.py file?

@renan-r-santos
Copy link

Just create a discussion here: #711
Thanks for looking into this :)

@frankie567
Copy link
Member Author

Since v8, FastAPI Users work entirely inside the dependency injection system of FastAPI. Thanks to this, I'm quite confident that we should avoid 99% of "Attached to a different loop" errors.

Closing this for now, but feel free to leave a message below if you encounter the problem in v8+.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

3 participants