From b17f1e70a1b0351b5577195207198653793f0e19 Mon Sep 17 00:00:00 2001 From: d-w-moore Date: Tue, 28 Mar 2023 17:13:43 -0400 Subject: [PATCH] [#377] fix iRODSSession connection timeout This had stopped working due to changes in session reinitialization code. --- irods/session.py | 8 +++++- irods/test/connection_test.py | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/irods/session.py b/irods/session.py index 2026cc80..f32db664 100644 --- a/irods/session.py +++ b/irods/session.py @@ -20,6 +20,7 @@ from irods import NATIVE_AUTH_SCHEME, PAM_AUTH_SCHEME import threading import weakref +from . import DEFAULT_CONNECTION_TIMEOUT _sessions = None _sessions_lock = threading.Lock() @@ -131,6 +132,7 @@ def __init__(self, configure = True, auto_cleanup = True, **kwargs): self._env_file = '' self._auth_file = '' self.do_configure = (kwargs if configure else {}) + self._cached_connection_timeout = kwargs.pop('connection_timeout', DEFAULT_CONNECTION_TIMEOUT) self.__configured = None if configure: self.__configured = self.configure(**kwargs) @@ -232,6 +234,8 @@ def configure(self, **kwargs): connection_refresh_time = self.get_connection_refresh_time(**kwargs) logger.debug("In iRODSSession's configure(). connection_refresh_time set to {}".format(connection_refresh_time)) self.pool = Pool(account, application_name=kwargs.pop('application_name',''), connection_refresh_time=connection_refresh_time) + conn_timeout = getattr(self,'_cached_connection_timeout',None) + self.pool.connection_timeout = conn_timeout return account def query(self, *args): @@ -294,7 +298,9 @@ def connection_timeout(self): @connection_timeout.setter def connection_timeout(self, seconds): - self.pool.connection_timeout = seconds + self._cached_connection_timeout = seconds + if seconds is not None: + self.pool.connection_timeout = seconds @staticmethod def get_irods_password_file(): diff --git a/irods/test/connection_test.py b/irods/test/connection_test.py index a7eb164b..b358af31 100644 --- a/irods/test/connection_test.py +++ b/irods/test/connection_test.py @@ -2,6 +2,7 @@ from __future__ import absolute_import import os import sys +import tempfile import unittest from irods.exception import NetworkException import irods.test.helpers as helpers @@ -65,6 +66,57 @@ def test_reply_failure(self): with self.assertRaises(NetworkException): conn.reply(0) + def test_that_connection_timeout_works__issue_377(self): + sess = self.sess + h = helpers.home_collection(sess) + logical_path = h + '/issue_377_test.file_timeout_test_on_chksum' + rand = os.urandom(1024)*64 + obj = local_file = None + try: + # Create a large file. + size = 1024**2 * 100 + with tempfile.NamedTemporaryFile(delete = False) as local_file: + while local_file.tell() < size: + local_file.write(rand) + obj = sess.data_objects.put(local_file.name, logical_path, return_data_object = True) + + # Set a very short socket timeout and remove all pre-existing socket connections. + # This forces a new connection to be made for any ensuing connections to the iRODS server. + + sess.connection_timeout = timeout = 0.01 + sess.cleanup() + + # Make sure the newly formed connection pool inherits the timeout value. + self.assertAlmostEqual(sess.pool.connection_timeout, timeout) + + # Perform a time-consuming operation in the server (ie. computing the checksum of a + # large data object) during which the socket will time out. + with self.assertRaises(NetworkException): + obj.chksum() + finally: + # Set the connection pool's socket timeout interval back to default, and clean up. + sess.connection_timeout = None + sess.cleanup() + obj.unlink(force = True) + if local_file: + os.unlink(local_file.name) + + def assert_timeout_value_propagated_to_socket(self, session, timeout_value): + session.collections.get(helpers.home_collection(session)) + conn = next(iter(session.pool.idle)) + self.assertEqual(conn.socket.gettimeout(), timeout_value) + + def test_connection_timeout_parameter_in_session_init__issue_377(self): + timeout = 1.0 + sess = helpers.make_session(connection_timeout = timeout) + self.assert_timeout_value_propagated_to_socket(sess, timeout) + + def test_assigning_session_connection_timeout__issue_377(self): + sess = self.sess + for timeout in (999999, None): + sess.connection_timeout = timeout + sess.cleanup() + self.assert_timeout_value_propagated_to_socket(sess, timeout) if __name__ == '__main__': # let the tests find the parent irods lib