smart_open icon indicating copy to clipboard operation
smart_open copied to clipboard

Automatically reactivate (reconnect) inactive SFTP clients

Open Kache opened this issue 3 years ago • 2 comments
trafficstars

Problem description

smart_open currently caches SSH clients, (see _SSH and usages in same file).

After opening a connection (_connect()), after it's been unused for some time, the client will time out, e.g.:

`SSHException: SSH session not active`
~/REDACTED/.venv/lib/python3.7/site-packages/smart_open/ssh.py in open(path, mode, host, user, password, port, transport_params)
    144
    145     conn = _connect(host, user, port, password, transport_params)
--> 146     sftp_client = conn.get_transport().open_sftp_client()
    147     fobj = sftp_client.open(path, mode)
    148     fobj.name = path

~/REDACTED/.venv/lib/python3.7/site-packages/paramiko/transport.py in open_sftp_client(self)
   1095             this transport
   1096         """
-> 1097         return SFTPClient.from_transport(self)
   1098
   1099     def send_ignore(self, byte_count=None):

~/REDACTED/.venv/lib/python3.7/site-packages/paramiko/sftp_client.py in from_transport(cls, t, window_size, max_packet_size)
    163         """
    164         chan = t.open_session(
--> 165             window_size=window_size, max_packet_size=max_packet_size
    166         )
    167         if chan is None:

~/REDACTED/.venv/lib/python3.7/site-packages/paramiko/transport.py in open_session(self, window_size, max_packet_size, timeout)
    877             window_size=window_size,
    878             max_packet_size=max_packet_size,
--> 879             timeout=timeout,
    880         )
    881

~/REDACTED/.venv/lib/python3.7/site-packages/paramiko/transport.py in open_channel(self, kind, dest_addr, src_addr, window_size, max_packet_size, timeout)
    967         """
    968         if not self.active:
--> 969             raise SSHException("SSH session not active")
    970         timeout = 3600 if timeout is None else timeout
    971         self.lock.acquire()

SSHException: SSH session not active

It seems to me this exception can be caught when open_sftp_client() in order to automatically reattempt a reconnect.

Should be straightforward to catch this specific exception and attempt a reconnect, e.g. something like:

def connect_and_open():
    conn = _connect(host, user, port, password, transport_params)
    return conn.get_transport().open_sftp_client()


try:
    sftp_client = connect_and_open()
except: SSHException ex:
    if 'SSH session not active' not in ex.message:
        raise

   _SSH.pop((host, user), None)  # invalidate cache
   sftp_client = connect_and_open()  # try to reconnect, once (and re-cache)

(though in practice, would probably want to some light refactoring to manage the cache cohesively, rather than in multiple places)

Steps/code to reproduce the problem

open() an SFTP endpoint, wait until the client times out, then try to open() it again, such that the same client is returned from the _SSH cache (keyed by hostname and user).

Versions

Darwin-20.6.0-x86_64-i386-64bit
Python 3.7.6 (default, Nov 24 2021, 00:59:23)
[Clang 13.0.0 (clang-1300.0.29.3)]
smart_open 5.2.1

Checklist

Before you create the issue, please make sure you have:

  • [X] Described the problem clearly
  • [X] Provided a minimal reproducible example, including any required data
  • [X] Provided the version numbers of the relevant software

Kache avatar Aug 16 '22 22:08 Kache

If you're open to accepting a PR, I'd be willing to create a PR based on the example above.

Kache avatar Aug 16 '22 23:08 Kache

Yes, thank you, please go ahead.

mpenkov avatar Aug 21 '22 12:08 mpenkov