Uploaded image for project: 'Python Driver'
  1. Python Driver
  2. PYTHON-353

Unbounded connection growth with Apache mod_wsgi 2.x

    • Type: Icon: Bug Bug
    • Resolution: Done
    • Priority: Icon: Critical - P2 Critical - P2
    • 2.2.1
    • Affects Version/s: 2.2
    • Component/s: None
    • None

      mod_wsgi 2.x, Python <= 2.6, apache prefork and worker, PyMongo >= 2.2 with auto_start_request True. Any other combination won't reproduce this behavior. Unfortunately this is a common combination.

      mod_wsgi reuses threads for multiple requests with the WSGIDaemonProcess directive. mod_wsgi 2.x, which is still widely deployed in, for example, Ubuntu 10.04, deletes thread state between requests even while it reuses threads, which causes strange behavior in PyMongo 2.2: it leaks one socket per two HTTP requests, resulting in unbounded growth in the number of sockets.

      PyMongo <= 2.1.1 puts a socket in a threadlocal variable; this socket is cleaned up (its sock_dealloc is called) when mod_wsgi 2.8 deletes the thread state between HTTP requests. PyMongo 2.2, however, puts a Python SocketInfo object into a thread local; this object's _del_ then chooses whether to return the socket to the pool or to close it; somehow under mod_wsgi 2.8, every second such SocketInfo is never deleted.

      Workarounds until this bug is understood and fixed in PyMongo (which will be ASAP):

      *) Pass auto_start_request=False to socket() or ReplicaSetsocket(). This avoids using thread locals and thus avoids mod_wsgi 2.8's strange handling of thread state.

      *) Upgrade mod_wsgi to the current version 3.2, which preserves thread state across requests.

      *) Upgrade Python to 2.7 (this requires recompiling mod_wsgi and setting WSGIPythonHome in your Apache configuration to point to Python 2.7's installation directory) – Python 2.7 sticks an object into the interpreter's thread state and uses a weakref callback to proactively clean up thread local state when a thread dies, so it doesn't leak connections with PyMongo 2.2

      *) Downgrade PyMongo to version 2.1.1

      For more on differences in thread state handling between mod_wsgi 2 and 3 see:

      https://groups.google.com/d/topic/modwsgi/sIs91L9XVgs/discussion

      http://code.google.com/p/modwsgi/issues/detail?id=120

      http://code.google.com/p/modwsgi/source/detail?spec=svn0eeee0e003b4a6e2e92a90fde05ab7f81c78b771&r=0eeee0e003b4a6e2e92a90fde05ab7f81c78b771

      Here's my current understanding of PyMongo, Python, and mod_wsgi:

      Python mod_wsgi PyMongo Behavior
      <=2.6 <=2.8 <=2.1.1 One new socket per HTTP request, no leaks: use end_request to reuse sockets
      <=2.6 <=2.8 2.2 This bug: unbounded socket growth with auto_start_request=True; use end_request or auto_start_request=False
      <=2.6 >=3.0 <=2.1.1 Each thread reuses same socket for all HTTP requests; no leaks
      <=2.6 >=3.0 2.2 Each thread reuses same socket for all HTTP requests; with auto_start_request=False threads safely share sockets
      >=2.7 <=2.8 <=2.1.1 One new socket per HTTP request, no leaks: use end_request to reuse sockets
      >=2.7 <=2.8 2.2 Threads get a socket per HTTP request, sockets returned to pool and reused as if calling end_request
      >=2.7 >=3.0 <=2.1.1 One new socket per HTTP request, no leaks: use end_request to reuse sockets
      >=2.7 >=3.0 2.2 Each thread reuses same socket for all HTTP requests; with auto_start_request=False threads safely share sockets

            Assignee:
            jesse@mongodb.com A. Jesse Jiryu Davis
            Reporter:
            jesse@mongodb.com A. Jesse Jiryu Davis
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: