From a400294518460b524ce74a69023a97eba5f9b605 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 24 Oct 2022 10:51:16 +0300 Subject: [PATCH 1/7] api: make connection close idempotent After this patch, it is possible to call `conn.close()` method multiple times. Part of #250 --- CHANGELOG.md | 1 + tarantool/connection.py | 5 +++-- test/suites/test_dml.py | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89ef6128..6b8b2a75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -149,6 +149,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Package build (#238). - Allow any MessagePack supported type as a request key (#240). - Puting test files in pip package (#238). +- Make connection close idempotent (#250). ## 0.9.0 - 2022-06-20 diff --git a/tarantool/connection.py b/tarantool/connection.py index 3fd1ed05..3df82a9e 100644 --- a/tarantool/connection.py +++ b/tarantool/connection.py @@ -520,10 +520,11 @@ def __init__(self, host, port, def close(self): """ - Close a connection to the server. + Close a connection to the server. The method is idempotent. """ - self._socket.close() + if self._socket is not None: + self._socket.close() self._socket = None def is_closed(self): diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 95ff1c18..f7939972 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -309,6 +309,13 @@ def test_13_unix_socket_connect(self): self.sock_con = tarantool.connect(self.sock_srv.host, self.sock_srv.args['primary']) self.assertEqual(self.sock_con.ping(notime=True), "Success") + def test_14_idempotent_close(self): + con = tarantool.connect(self.srv.host, self.srv.args['primary']) + con.close() + self.assertEqual(con.is_closed(), True) + con.close() + self.assertEqual(con.is_closed(), True) + @classmethod def tearDownClass(self): self.con.close() From 772bb00d024254d437f274481454078240f2072e Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 24 Oct 2022 11:19:01 +0300 Subject: [PATCH 2/7] test: replace deprecated methods assertRaisesRegexp and assertEquals were renamed in version 3.2 [1]. 1. https://docs.python.org/3/library/unittest.html#deprecated-aliases Part of #250 --- test/suites/test_dml.py | 12 ++++++------ test/suites/test_pool.py | 4 ++-- test/suites/test_schema.py | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index f7939972..30aac007 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -124,7 +124,7 @@ def test_03_delete(self): self.assertSequenceEqual(self.con.delete('space_1', [20]), []) self.assertSequenceEqual(self.con.select('space_1', [20], index='primary'), []) # Check that field has no meaning, yet. - with self.assertRaisesRegexp(tarantool.DatabaseError, + with self.assertRaisesRegex(tarantool.DatabaseError, '(19, .*)'): self.con.delete('space_1', [1, 'tuple_21']) self.assertSequenceEqual(self.con.select('space_1', [21], index='primary'), [[21, 1, 'tuple_21']]) @@ -134,7 +134,7 @@ def test_04_replace(self): self.assertSequenceEqual(self.con.replace('space_1', [2, 2, 'tuple_3']), [[2, 2, 'tuple_3']]) self.assertSequenceEqual(self.con.select('space_1', 2), [[2, 2, 'tuple_3']]) # Check replace that isn't Ok. - with self.assertRaisesRegexp(tarantool.DatabaseError, + with self.assertRaisesRegex(tarantool.DatabaseError, '(39, .*)'): self.assertSequenceEqual(self.con.replace('space_1', [2, 2]), [[2, 2, 'tuple_2']]) @@ -168,9 +168,9 @@ def test_07_call_16(self): self.assertSequenceEqual(con.call('json.decode', '[123, 234, 345]'), [[123, 234, 345]]) self.assertSequenceEqual(con.call('json.decode', ['[123, 234, 345]']), [[123, 234, 345]]) self.assertSequenceEqual(con.call('json.decode', ('[123, 234, 345]',)), [[123, 234, 345]]) - with self.assertRaisesRegexp(tarantool.DatabaseError, '(32, .*)'): + with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): con.call('json.decode') - with self.assertRaisesRegexp(tarantool.DatabaseError, '(32, .*)'): + with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): con.call('json.decode', '{[1, 2]: "world"}') ans = con.call('fiber.time') self.assertEqual(len(ans), 1) @@ -194,9 +194,9 @@ def test_07_call_17(self): self.assertSequenceEqual(con.call('json.decode', '[123, 234, 345]'), [[123, 234, 345]]) self.assertSequenceEqual(con.call('json.decode', ['[123, 234, 345]']), [[123, 234, 345]]) self.assertSequenceEqual(con.call('json.decode', ('[123, 234, 345]',)), [[123, 234, 345]]) - with self.assertRaisesRegexp(tarantool.DatabaseError, '(32, .*)'): + with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): con.call('json.decode') - with self.assertRaisesRegexp(tarantool.DatabaseError, '(32, .*)'): + with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): con.call('json.decode', '{[1, 2]: "world"}') ans = con.call('fiber.time') self.assertEqual(len(ans), 1) diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index f10b6287..01352161 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -520,11 +520,11 @@ def test_16_is_closed(self): user='test', password='test',) - self.assertEquals(self.pool.is_closed(), False) + self.assertEqual(self.pool.is_closed(), False) self.pool.close() - self.assertEquals(self.pool.is_closed(), True) + self.assertEqual(self.pool.is_closed(), True) def tearDown(self): if hasattr(self, 'pool'): diff --git a/test/suites/test_schema.py b/test/suites/test_schema.py index f289fe74..09017b2a 100644 --- a/test/suites/test_schema.py +++ b/test/suites/test_schema.py @@ -104,30 +104,30 @@ def test_00_authenticate(self): self.assertEqual(self.con.authenticate('test', 'test')._data, None) def test_01_space_bad(self): - with self.assertRaisesRegexp(tarantool.SchemaError, + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_space(0) - with self.assertRaisesRegexp(tarantool.SchemaError, + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_space(0) - with self.assertRaisesRegexp(tarantool.SchemaError, + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_space('bad_name') def test_02_index_bad(self): - with self.assertRaisesRegexp(tarantool.SchemaError, + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_index(0, 'primary') - with self.assertRaisesRegexp(tarantool.SchemaError, + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no space.*'): self.sch.get_index('bad_space', 'primary') - with self.assertRaisesRegexp(tarantool.SchemaError, + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no index.*'): self.sch.get_index(280, 'bad_index') - with self.assertRaisesRegexp(tarantool.SchemaError, + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no index.*'): self.sch.get_index(280, 'bad_index') - with self.assertRaisesRegexp(tarantool.SchemaError, + with self.assertRaisesRegex(tarantool.SchemaError, 'There\'s no index.*'): self.sch.get_index(280, 3) From 56577aad341a4b8bd70749d725b4aa95b29aa360 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 24 Oct 2022 11:12:14 +0300 Subject: [PATCH 3/7] test: fix server resource warnings Fix several ResourceWarning messages related to test Tarantool server methods not cleaning up sockets. Part of #250 --- test/suites/lib/tarantool_server.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/suites/lib/tarantool_server.py b/test/suites/lib/tarantool_server.py index 8fcc72fc..2640f5f4 100644 --- a/test/suites/lib/tarantool_server.py +++ b/test/suites/lib/tarantool_server.py @@ -22,6 +22,8 @@ def check_port(port, rais=True): sock = socket.create_connection(("localhost", port)) except socket.error: return True + sock.close() + if rais: raise RuntimeError("The server is already running on port {0}".format(port)) return False @@ -136,7 +138,7 @@ def __init__(self, ssl_ca_file=None, ssl_ciphers=None, create_unix_socket=False): - os.popen('ulimit -c unlimited') + os.popen('ulimit -c unlimited').close() if create_unix_socket: self.host = None @@ -210,6 +212,7 @@ def wait_until_started(self): while True: ans = temp('box.info.status')[0] if ans in ('running', 'hot_standby', 'orphan') or ans.startswith('replica'): + temp.disconnect() return True elif ans in ('loading',): continue @@ -262,6 +265,8 @@ def clean(self): if (self._socket is not None) and (not self._socket.file.closed): self._socket.close() + del self.log_des + def __del__(self): self.stop() self.clean() From 5852a25d8949b2e2d5fef65d3c9ea2c584670d7c Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 24 Oct 2022 11:35:18 +0300 Subject: [PATCH 4/7] test: fix unix socket test resource warnings hasattr check actually has no effect for unittest runtime-added fields, so both server and connection for unix socket test weren't closed after run. Part of #250 --- test/suites/test_dml.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 30aac007..1ecffd9f 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -38,6 +38,13 @@ def setUpClass(self): self.adm("fiber = require('fiber')") self.adm("uuid = require('uuid')") + if not sys.platform.startswith("win"): + self.sock_srv = TarantoolServer(create_unix_socket=True) + self.sock_srv.script = 'test/suites/box.lua' + self.sock_srv.start() + else: + self.sock_srv = None + def setUp(self): # prevent a remote tarantool from clean our session if self.srv.is_started(): @@ -302,12 +309,11 @@ def test_13_unix_socket_connect(self): if sys.platform.startswith("win"): self.skipTest("Skip UNIX socket tests on Windows since it uses remote server") - self.sock_srv = TarantoolServer(create_unix_socket=True) - self.sock_srv.script = 'test/suites/box.lua' - self.sock_srv.start() - - self.sock_con = tarantool.connect(self.sock_srv.host, self.sock_srv.args['primary']) - self.assertEqual(self.sock_con.ping(notime=True), "Success") + sock_con = tarantool.connect(self.sock_srv.host, self.sock_srv.args['primary']) + try: + self.assertEqual(sock_con.ping(notime=True), "Success") + finally: + sock_con.close() def test_14_idempotent_close(self): con = tarantool.connect(self.srv.host, self.srv.args['primary']) @@ -322,9 +328,6 @@ def tearDownClass(self): self.srv.stop() self.srv.clean() - if hasattr(self, 'sock_srv'): + if self.sock_srv is not None: self.sock_srv.stop() self.sock_srv.clean() - - if hasattr(self, 'sock_con'): - self.sock_con.close() From 1e89471acc508c15a1ca35373093bccb89e59174 Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 24 Oct 2022 11:46:33 +0300 Subject: [PATCH 5/7] test: fix test connection resource warnings Close all connection opened in tests even in case of failed asserts. Part of #250 --- test/suites/test_dml.py | 47 +++++---- test/suites/test_mesh.py | 214 +++++++++++++++++++++------------------ test/suites/test_pool.py | 154 ++++++++++++++++------------ 3 files changed, 227 insertions(+), 188 deletions(-) diff --git a/test/suites/test_dml.py b/test/suites/test_dml.py index 1ecffd9f..2a4c9481 100644 --- a/test/suites/test_dml.py +++ b/test/suites/test_dml.py @@ -171,29 +171,32 @@ def test_06_update(self): def test_07_call_16(self): con = tarantool.Connection(self.srv.host, self.srv.args['primary'], call_16 = True) - con.authenticate('test', 'test') - self.assertSequenceEqual(con.call('json.decode', '[123, 234, 345]'), [[123, 234, 345]]) - self.assertSequenceEqual(con.call('json.decode', ['[123, 234, 345]']), [[123, 234, 345]]) - self.assertSequenceEqual(con.call('json.decode', ('[123, 234, 345]',)), [[123, 234, 345]]) - with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): - con.call('json.decode') - with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): - con.call('json.decode', '{[1, 2]: "world"}') - ans = con.call('fiber.time') - self.assertEqual(len(ans), 1) - self.assertEqual(len(ans[0]), 1) - self.assertIsInstance(ans[0][0], float) - ans = con.call('fiber.time64') - self.assertEqual(len(ans), 1) - self.assertEqual(len(ans[0]), 1) - self.assertIsInstance(ans[0][0], int) - ans = con.call('uuid.str') - self.assertEqual(len(ans), 1) - self.assertEqual(len(ans[0]), 1) - self.assertIsInstance(ans[0][0], str) + try: + con.authenticate('test', 'test') + self.assertSequenceEqual(con.call('json.decode', '[123, 234, 345]'), [[123, 234, 345]]) + self.assertSequenceEqual(con.call('json.decode', ['[123, 234, 345]']), [[123, 234, 345]]) + self.assertSequenceEqual(con.call('json.decode', ('[123, 234, 345]',)), [[123, 234, 345]]) + with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): + con.call('json.decode') + with self.assertRaisesRegex(tarantool.DatabaseError, '(32, .*)'): + con.call('json.decode', '{[1, 2]: "world"}') + ans = con.call('fiber.time') + self.assertEqual(len(ans), 1) + self.assertEqual(len(ans[0]), 1) + self.assertIsInstance(ans[0][0], float) + ans = con.call('fiber.time64') + self.assertEqual(len(ans), 1) + self.assertEqual(len(ans[0]), 1) + self.assertIsInstance(ans[0][0], int) + ans = con.call('uuid.str') + self.assertEqual(len(ans), 1) + self.assertEqual(len(ans[0]), 1) + self.assertIsInstance(ans[0][0], str) - self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), [[1, 2, 3, 'fld_1']]) - self.assertSequenceEqual(con.call('box.tuple.new', 'fld_1'), [['fld_1']]) + self.assertSequenceEqual(con.call('box.tuple.new', [1, 2, 3, 'fld_1']), [[1, 2, 3, 'fld_1']]) + self.assertSequenceEqual(con.call('box.tuple.new', 'fld_1'), [['fld_1']]) + finally: + con.close() def test_07_call_17(self): con = tarantool.Connection(self.srv.host, self.srv.args['primary']) diff --git a/test/suites/test_mesh.py b/test/suites/test_mesh.py index cb0bce7c..ae621bf5 100644 --- a/test/suites/test_mesh.py +++ b/test/suites/test_mesh.py @@ -80,27 +80,30 @@ def assert_srv_id(con, srv_id): {'host': self.host_2, 'port': self.port_2}, ], user='test', password='test') - # Response from instance#1. - assert_srv_id(con, 1) - - # Stop instance#1 -- response from instance#2. - self.srv.stop() - assert_srv_id(con, 2) - - # Start instance#1, stop instance#2 -- response from - # instance#1 again. - self.srv.start() - self.srv.admin('function srv_id() return 1 end') - self.srv2.stop() - assert_srv_id(con, 1) - - # Stop instance #2 -- NetworkError (because we have no - # alive servers anymore). - self.srv.stop() - with self.assertRaises(NetworkError): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=NetworkWarning) - con.ping() + try: + # Response from instance#1. + assert_srv_id(con, 1) + + # Stop instance#1 -- response from instance#2. + self.srv.stop() + assert_srv_id(con, 2) + + # Start instance#1, stop instance#2 -- response from + # instance#1 again. + self.srv.start() + self.srv.admin('function srv_id() return 1 end') + self.srv2.stop() + assert_srv_id(con, 1) + + # Stop instance #2 -- NetworkError (because we have no + # alive servers anymore). + self.srv.stop() + with self.assertRaises(NetworkError): + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=NetworkWarning) + con.ping() + finally: + con.close() def test_01_contructor(self): # Verify that an error is risen when no addresses are @@ -126,7 +129,10 @@ def test_01_contructor(self): addrs = [{"host": "localhost", "port": 1234}] con = tarantool.MeshConnection("localhost", 1234, addrs=addrs, connect_now=False) - self.assertEqual(len(con.strategy.addrs), 1) + try: + self.assertEqual(len(con.strategy.addrs), 1) + finally: + con.close() def test_02_discovery_bad_address(self): retvals = [ @@ -147,22 +153,23 @@ def test_02_discovery_bad_address(self): self.define_custom_cluster_function(func_name, retval) con = tarantool.MeshConnection(self.host_1, self.port_1, user='test', password='test') - con.cluster_discovery_function = func_name - - # Verify that a cluster discovery (that is triggered - # by ping) give one or two warnings. - with warnings.catch_warnings(record=True) as ws: - con.ping() - self.assertTrue(len(ws) in (1, 2)) - for w in ws: - self.assertIs(w.category, ClusterDiscoveryWarning) - - # Verify that incorrect or empty result was discarded. - self.assertEqual(len(con.strategy.addrs), 1) - self.assertEqual(con.strategy.addrs[0]['host'], self.host_1) - self.assertEqual(con.strategy.addrs[0]['port'], self.port_1) - - con.close() + try: + con.cluster_discovery_function = func_name + + # Verify that a cluster discovery (that is triggered + # by ping) give one or two warnings. + with warnings.catch_warnings(record=True) as ws: + con.ping() + self.assertTrue(len(ws) in (1, 2)) + for w in ws: + self.assertIs(w.category, ClusterDiscoveryWarning) + + # Verify that incorrect or empty result was discarded. + self.assertEqual(len(con.strategy.addrs), 1) + self.assertEqual(con.strategy.addrs[0]['host'], self.host_1) + self.assertEqual(con.strategy.addrs[0]['port'], self.port_1) + finally: + con.close() def test_03_discovery_bad_good_addresses(self): func_name = 'bad_and_good_addresses' @@ -170,21 +177,22 @@ def test_03_discovery_bad_good_addresses(self): self.define_custom_cluster_function(func_name, retval) con = tarantool.MeshConnection(self.host_1, self.port_1, user='test', password='test') - con.cluster_discovery_function = func_name - - # Verify that a cluster discovery (that is triggered - # by ping) give one warning. - with warnings.catch_warnings(record=True) as ws: - con.ping() - self.assertEqual(len(ws), 1) - self.assertIs(ws[0].category, ClusterDiscoveryWarning) + try: + con.cluster_discovery_function = func_name - # Verify that only second address was accepted. - self.assertEqual(len(con.strategy.addrs), 1) - self.assertEqual(con.strategy.addrs[0]['host'], self.host_2) - self.assertEqual(con.strategy.addrs[0]['port'], self.port_2) + # Verify that a cluster discovery (that is triggered + # by ping) give one warning. + with warnings.catch_warnings(record=True) as ws: + con.ping() + self.assertEqual(len(ws), 1) + self.assertIs(ws[0].category, ClusterDiscoveryWarning) - con.close() + # Verify that only second address was accepted. + self.assertEqual(len(con.strategy.addrs), 1) + self.assertEqual(con.strategy.addrs[0]['host'], self.host_2) + self.assertEqual(con.strategy.addrs[0]['port'], self.port_2) + finally: + con.close() def test_04_discovery_add_address(self): # Create a mesh connection; pass only the first server @@ -194,20 +202,21 @@ def test_04_discovery_add_address(self): cluster_discovery_function=self.get_all_nodes_func_name, connect_now=False) - # Verify that the strategy has one address that comes from - # the constructor arguments. - self.assertEqual(len(con.strategy.addrs), 1) - con.connect() - - # Verify that we work with the first server. - resp = con.call('srv_id') - self.assertEqual(resp.data and resp.data[0], 1) + try: + # Verify that the strategy has one address that comes from + # the constructor arguments. + self.assertEqual(len(con.strategy.addrs), 1) + con.connect() - # Verify that the refresh was successful and the strategy - # has 2 addresses. - self.assertEqual(len(con.strategy.addrs), 2) + # Verify that we work with the first server. + resp = con.call('srv_id') + self.assertEqual(resp.data and resp.data[0], 1) - con.close() + # Verify that the refresh was successful and the strategy + # has 2 addresses. + self.assertEqual(len(con.strategy.addrs), 2) + finally: + con.close() def test_05_discovery_delay(self): # Create a mesh connection, pass only the first server address. @@ -216,25 +225,26 @@ def test_05_discovery_delay(self): cluster_discovery_function=self.get_all_nodes_func_name, cluster_discovery_delay=1) - # Verify that the strategy has two addresses come from - # the function right after connecting. - self.assertEqual(len(con.strategy.addrs), 2) - - # Drop addresses list to the initial state. - con.strategy.update([con.strategy.addrs[0], ]) + try: + # Verify that the strategy has two addresses come from + # the function right after connecting. + self.assertEqual(len(con.strategy.addrs), 2) - # Verify that the discovery will not be performed until - # 'cluster_discovery_delay' seconds will be passed. - con.ping() - self.assertEqual(len(con.strategy.addrs), 1) + # Drop addresses list to the initial state. + con.strategy.update([con.strategy.addrs[0], ]) - sleep(1.1) + # Verify that the discovery will not be performed until + # 'cluster_discovery_delay' seconds will be passed. + con.ping() + self.assertEqual(len(con.strategy.addrs), 1) - # Refresh after cluster_discovery_delay. - con.ping() - self.assertEqual(len(con.strategy.addrs), 2) + sleep(1.1) - con.close() + # Refresh after cluster_discovery_delay. + con.ping() + self.assertEqual(len(con.strategy.addrs), 2) + finally: + con.close() def test_06_reconnection(self): # Create a mesh connection; pass only the first server @@ -243,24 +253,25 @@ def test_06_reconnection(self): self.host_1, self.port_1, user='test', password='test', cluster_discovery_function=self.get_all_nodes_func_name) - con.last_nodes_refresh = 0 - resp = con.call('srv_id') - self.assertEqual(resp.data and resp.data[0], 1) - - # Verify that the last discovery was successful and the - # strategy has 2 addresses. - self.assertEqual(len(con.strategy.addrs), 2) + try: + con.last_nodes_refresh = 0 + resp = con.call('srv_id') + self.assertEqual(resp.data and resp.data[0], 1) - self.srv.stop() + # Verify that the last discovery was successful and the + # strategy has 2 addresses. + self.assertEqual(len(con.strategy.addrs), 2) - # Verify that we switched to the second server. - with warnings.catch_warnings(): - # Suppress reconnection warnings. - warnings.simplefilter("ignore") - resp = con.call('srv_id') - self.assertEqual(resp.data and resp.data[0], 2) + self.srv.stop() - con.close() + # Verify that we switched to the second server. + with warnings.catch_warnings(): + # Suppress reconnection warnings. + warnings.simplefilter("ignore") + resp = con.call('srv_id') + self.assertEqual(resp.data and resp.data[0], 2) + finally: + con.close() def test_07_discovery_exclude_address(self): # Define function to get back only second server. @@ -272,15 +283,16 @@ def test_07_discovery_exclude_address(self): self.host_1, self.port_1, user='test', password='test', cluster_discovery_function=func_name) - # Verify that discovery was successful and the strategy - # has 1 address. - self.assertEqual(len(con.strategy.addrs), 1) - - # Verify that the current server is second one. - resp = con.call('srv_id') - self.assertEqual(resp.data and resp.data[0], 2) + try: + # Verify that discovery was successful and the strategy + # has 1 address. + self.assertEqual(len(con.strategy.addrs), 1) - con.close() + # Verify that the current server is second one. + resp = con.call('srv_id') + self.assertEqual(resp.data and resp.data[0], 2) + finally: + con.close() def tearDown(self): self.srv.stop() diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index 01352161..bb475f48 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -226,9 +226,12 @@ def test_03_insert(self): user='test', password='test') - self.assertSequenceEqual( - conn_2.select('test', 'test_03_insert_1'), - [['test_03_insert_1', 1]]) + try: + self.assertSequenceEqual( + conn_2.select('test', 'test_03_insert_1'), + [['test_03_insert_1', 1]]) + finally: + conn_2.close() def test_04_delete(self): self.set_cluster_ro([True, True, True, False, True]) @@ -243,22 +246,25 @@ def test_04_delete(self): user='test', password='test') - conn_3.insert('test', ['test_04_delete_1', 1]) - conn_3.insert('test', ['test_04_delete_2', 2]) + try: + conn_3.insert('test', ['test_04_delete_1', 1]) + conn_3.insert('test', ['test_04_delete_2', 2]) - self.assertSequenceEqual( - self.pool.delete('test', 'test_04_delete_1'), - [['test_04_delete_1', 1]]) - self.assertSequenceEqual( - conn_3.select('test', 'test_04_delete_1'), - []) + self.assertSequenceEqual( + self.pool.delete('test', 'test_04_delete_1'), + [['test_04_delete_1', 1]]) + self.assertSequenceEqual( + conn_3.select('test', 'test_04_delete_1'), + []) - self.assertSequenceEqual( - self.pool.delete('test', 2, index='id', mode=tarantool.Mode.RW), - [['test_04_delete_2', 2]]) - self.assertSequenceEqual( - conn_3.select('test', 'test_04_delete_2'), - []) + self.assertSequenceEqual( + self.pool.delete('test', 2, index='id', mode=tarantool.Mode.RW), + [['test_04_delete_2', 2]]) + self.assertSequenceEqual( + conn_3.select('test', 'test_04_delete_2'), + []) + finally: + conn_3.close() def test_05_upsert(self): self.set_cluster_ro([True, False, True, True, True]) @@ -273,19 +279,22 @@ def test_05_upsert(self): user='test', password='test') - self.assertSequenceEqual( - self.pool.upsert('test', ['test_05_upsert', 3], [('+', 1, 1)]), - []) - self.assertSequenceEqual( - conn_1.select('test', 'test_05_upsert'), - [['test_05_upsert', 3]]) + try: + self.assertSequenceEqual( + self.pool.upsert('test', ['test_05_upsert', 3], [('+', 1, 1)]), + []) + self.assertSequenceEqual( + conn_1.select('test', 'test_05_upsert'), + [['test_05_upsert', 3]]) - self.assertSequenceEqual( - self.pool.upsert('test', ['test_05_upsert', 3], - [('+', 1, 1)], mode=tarantool.Mode.RW), []) - self.assertSequenceEqual( - conn_1.select('test', 'test_05_upsert'), - [['test_05_upsert', 4]]) + self.assertSequenceEqual( + self.pool.upsert('test', ['test_05_upsert', 3], + [('+', 1, 1)], mode=tarantool.Mode.RW), []) + self.assertSequenceEqual( + conn_1.select('test', 'test_05_upsert'), + [['test_05_upsert', 4]]) + finally: + conn_1.close() def test_06_update(self): self.set_cluster_ro([True, True, True, True, False]) @@ -299,23 +308,27 @@ def test_06_update(self): port=self.addrs[4]['port'], user='test', password='test') - conn_4.insert('test', ['test_06_update_1', 3]) - conn_4.insert('test', ['test_06_update_2', 14]) - self.assertSequenceEqual( - self.pool.update('test', ('test_06_update_1',), [('+', 1, 1)]), - [['test_06_update_1', 4]]) - self.assertSequenceEqual( - conn_4.select('test', 'test_06_update_1'), - [['test_06_update_1', 4]]) + try: + conn_4.insert('test', ['test_06_update_1', 3]) + conn_4.insert('test', ['test_06_update_2', 14]) - self.assertSequenceEqual( - self.pool.update('test', ('test_06_update_2',), - [('=', 1, 10)], mode=tarantool.Mode.RW), - [['test_06_update_2', 10]]) - self.assertSequenceEqual( - conn_4.select('test', 'test_06_update_2'), - [['test_06_update_2', 10]]) + self.assertSequenceEqual( + self.pool.update('test', ('test_06_update_1',), [('+', 1, 1)]), + [['test_06_update_1', 4]]) + self.assertSequenceEqual( + conn_4.select('test', 'test_06_update_1'), + [['test_06_update_1', 4]]) + + self.assertSequenceEqual( + self.pool.update('test', ('test_06_update_2',), + [('=', 1, 10)], mode=tarantool.Mode.RW), + [['test_06_update_2', 10]]) + self.assertSequenceEqual( + conn_4.select('test', 'test_06_update_2'), + [['test_06_update_2', 10]]) + finally: + conn_4.close() def test_07_replace(self): self.set_cluster_ro([True, True, True, True, False]) @@ -329,22 +342,26 @@ def test_07_replace(self): port=self.addrs[4]['port'], user='test', password='test') - conn_4.insert('test', ['test_07_replace', 3]) - self.assertSequenceEqual( - self.pool.replace('test', ['test_07_replace', 4], - mode=tarantool.Mode.RW), - [['test_07_replace', 4]]) - self.assertSequenceEqual( - conn_4.select('test', 'test_07_replace'), - [['test_07_replace', 4]]) + try: + conn_4.insert('test', ['test_07_replace', 3]) - self.assertSequenceEqual( - self.pool.replace('test', ['test_07_replace', 5]), - [['test_07_replace', 5]]) - self.assertSequenceEqual( - conn_4.select('test', 'test_07_replace'), - [['test_07_replace', 5]]) + self.assertSequenceEqual( + self.pool.replace('test', ['test_07_replace', 4], + mode=tarantool.Mode.RW), + [['test_07_replace', 4]]) + self.assertSequenceEqual( + conn_4.select('test', 'test_07_replace'), + [['test_07_replace', 4]]) + + self.assertSequenceEqual( + self.pool.replace('test', ['test_07_replace', 5]), + [['test_07_replace', 5]]) + self.assertSequenceEqual( + conn_4.select('test', 'test_07_replace'), + [['test_07_replace', 5]]) + finally: + conn_4.close() def test_08_select(self): self.set_cluster_ro([False, False, False, False, False]) @@ -355,7 +372,11 @@ def test_08_select(self): port=addr['port'], user='test', password='test') - conn.insert('test', ['test_08_select', 3]) + + try: + conn.insert('test', ['test_08_select', 3]) + finally: + conn.close() self.set_cluster_ro([False, True, False, True, True]) self.pool = tarantool.ConnectionPool( @@ -457,12 +478,15 @@ def test_12_execute(self): user='test', password='test') - self.assertSequenceEqual( - conn_0.select('test', 'test_12_execute_1'), - [['test_12_execute_1', 1]]) - self.assertSequenceEqual( - conn_0.select('test', 'test_12_execute_2'), - [['test_12_execute_2', 2]]) + try: + self.assertSequenceEqual( + conn_0.select('test', 'test_12_execute_1'), + [['test_12_execute_1', 1]]) + self.assertSequenceEqual( + conn_0.select('test', 'test_12_execute_2'), + [['test_12_execute_2', 2]]) + finally: + conn_0.close() def test_13_failover(self): self.set_cluster_ro([False, True, True, True, True]) From c8b379d6c4ad9c4d255bef6b38c8f8a4b1ad4d8a Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 24 Oct 2022 11:54:16 +0300 Subject: [PATCH 6/7] test: fix dbapi test connection resource warnings DBAPI2 compliance tests are not implemented here but inherited from external module [1]. Two tests from this module open a connection and forget to close it. The issue [2] had been fixed and PR had been merged, but there is no tagged release yet. 1. https://pypi.org/project/dbapi-compliance/ 2. https://github.com/baztian/dbapi-compliance/issues/5 Part of #250 --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index c0eb11f4..ae4091d2 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,3 +1,3 @@ -dbapi-compliance==1.15.0 +git+https://github.com/baztian/dbapi-compliance.git@ea7cb1b4#egg=dbapi-compliance pyyaml==6.0 importlib-metadata >= 1.0 ; python_version < '3.8' From 7baa48116e657b0b3b780d0f0d2d5f9f588284ef Mon Sep 17 00:00:00 2001 From: Georgy Moiseev Date: Mon, 24 Oct 2022 12:00:08 +0300 Subject: [PATCH 7/7] test: filter warnings for disabled instances There are several ConnectionPool tests that stop some pool instances and verify that everything works fine even for semi-functional cluster. Different network and cluster state warning are issued in this case. They are expected and not informative, thus it is better to filter them in final output. Closes #250 --- test/suites/test_pool.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/suites/test_pool.py b/test/suites/test_pool.py index bb475f48..5c5aaeb6 100644 --- a/test/suites/test_pool.py +++ b/test/suites/test_pool.py @@ -4,7 +4,14 @@ import warnings import tarantool -from tarantool.error import PoolTolopogyError, DatabaseError, NetworkError +from tarantool.error import ( + ClusterConnectWarning, + DatabaseError, + NetworkError, + NetworkWarning, + PoolTolopogyError, + PoolTolopogyWarning, +) from .lib.skip import skip_or_run_sql_test, skip_or_run_conn_pool_test from .lib.tarantool_server import TarantoolServer @@ -489,6 +496,9 @@ def test_12_execute(self): conn_0.close() def test_13_failover(self): + warnings.simplefilter('ignore', category=NetworkWarning) + warnings.simplefilter('ignore', category=PoolTolopogyWarning) + self.set_cluster_ro([False, True, True, True, True]) self.pool = tarantool.ConnectionPool( addrs=self.addrs, @@ -508,6 +518,8 @@ def expect_RW_request_execute_on_new_master(): self.retry(func=expect_RW_request_execute_on_new_master) def test_14_cluster_with_instances_dead_in_runtime_is_ok(self): + warnings.simplefilter('ignore', category=ClusterConnectWarning) + self.set_cluster_ro([False, True, False, True, True]) self.servers[0].stop() @@ -520,6 +532,8 @@ def test_14_cluster_with_instances_dead_in_runtime_is_ok(self): self.pool.ping(mode=tarantool.Mode.RW) def test_15_cluster_with_dead_instances_on_start_is_ok(self): + warnings.simplefilter('ignore', category=ClusterConnectWarning) + self.set_cluster_ro([False, True, True, True, True]) self.servers[0].stop()