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

Socket timeout in python2 #2425

Closed
jliu90 opened this issue Sep 4, 2019 · 2 comments
Closed

Socket timeout in python2 #2425

jliu90 opened this issue Sep 4, 2019 · 2 comments

Comments

@jliu90
Copy link

jliu90 commented Sep 4, 2019

  • OS: MacOS 10.14.5
  • python: 2.7.14
  • docker/docker-py version:
pip freeze | grep docker && python --version && docker version
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
docker==3.2.0
docker-compose==1.20.0
docker-pycreds==0.4.0
dockerpty==0.4.1
Python 2.7.14
Client: Docker Engine - Community
 Version:           19.03.1
 API version:       1.40
 Go version:        go1.12.5
 Git commit:        74b1e89
 Built:             Thu Jul 25 21:18:17 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.1
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.5
  Git commit:       74b1e89
  Built:            Thu Jul 25 21:17:52 2019
  OS/Arch:          linux/amd64
  Experimental:     true
 containerd:
  Version:          v1.2.6
  GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc:
  Version:          1.0.0-rc8
  GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

Description:

I use docker-compose to start 3 containers, one of them being a web server. The web server
prints some debug info to stdout when processing requests.

I was on a very old docker-py version (2.2.1) but it has been working fine. I recently upgraded to docker-py 3.7.3 and realized that docker-compose is not printing anything if the web server has been idle for a while. Tried to find a threshold of idle time and it's about 60 seconds.

I went on and tried multiple docker-py versions. The latest version that works fine is 3.1.4. I looked the diff between version 3.2.0 and 3.1.4, and found this:
(line 25 in docker/utils/socket.py in 284c3d9)

if six.PY3 and not isinstance(socket, NpipeSocket):
    if not isinstance(socket, NpipeSocket):	
        select.select([socket], [], [])	        select.select([socket], [], [])

Looks like it's not doing the select.select in python2, hence the socket times out after 60 seconds in read().
I switched to 3.2.0, removed the condition for six.PY3 and it works perfectly fine.

So my question is: is there a reason why docker-py only do the select.select call in python3? If there's a good reason we can't do it python2, is there anything a docker-compose user can do to avoid socket timeout? Should that be a docker-compose issue?

(This docker-compose issue may be related docker/compose#6261)

@shin-
Copy link
Contributor

shin- commented Sep 4, 2019

See #2300

I'd recommend switching to Python 3 if possible. Python 2 is reaching EOL in a few months, and it doesn't make a lot of sense for us to keep investing in supporting older versions of Python.

Alternatively, you can use the binary builds of Compose which are already compiled with Python 3.7.

@jliu90
Copy link
Author

jliu90 commented Sep 9, 2019

Thanks you @shin- for the response. Switching to python3 does seem to fix this problem.

I looked the failed test in #2300. I'm no expert but I don't think closing the socket in a different thread is a good idea. I wrote this snippet for testing:

from __future__ import print_function

import socket
import threading
import select
import functools

sock = socket.socket()
sock.connect(('127.0.0.1', 8765))

def close_socket(sock):
    sock.shutdown(socket.SHUT_RDWR)
    sock.close()
    print('socket closed in thread')

threading.Timer(3, functools.partial(close_socket, sock)).start()
print('calling select')

select.select([sock], [], [])
print('select returned, calling recv')

data = sock.recv(4096)
print(data, 'end')

This blocks forever in select.select, in both python2 and python3.

You may wonder why the test passes with the if six.PY3 condition. I don't have an answer but I can share my findings. When socket.close is called here https://github.com/docker/docker-py/blob/master/docker/types/daemon.py#L74, it doesn't actually close the socket. The reason, I think, is that some SocketIO objects are still holding a reference to the socket, hence https://github.com/python/cpython/blob/3.7/Lib/socket.py#L416 does not call self._real_close. I added some debug messages and they confirm it.

Again, I agree that with python2 reaching its EOL it doesn't make sense to fix non-critical bugs like this. Posted the information above just as an FYI.

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

No branches or pull requests

2 participants