33#
44# Licensed under the Apache License, Version 2.0 (the "License");
55# you may not use this file except in compliance with the License.
6- # You may obtain a copy of the License at
6+ # You may obtain a copy of the License at:
77#
8- # http://www.apache.org/licenses/LICENSE-2.0
8+ # http://www.apache.org/licenses/LICENSE-2.0
99#
1010# Unless required by applicable law or agreed to in writing, software
1111# distributed under the License is distributed on an "AS IS" BASIS,
1212# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313# See the License for the specific language governing permissions and
1414# limitations under the License.
15-
16- # Copyright (c) 2013-2017 Uber Technologies, Inc.
15+ #
16+ # ----------------------------------------------------------------------
17+ # Legacy Uber License (2013–2017)
1718#
1819# Permission is hereby granted, free of charge, to any person obtaining a copy
19- # of this software and associated documentation files (the " Software" ), to deal
20+ # of this software and associated documentation files (the “ Software” ), to deal
2021# in the Software without restriction, including without limitation the rights
2122# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2223# copies of the Software, and to permit persons to whom the Software is
2526# The above copyright notice and this permission notice shall be included in
2627# all copies or substantial portions of the Software.
2728#
28- # THE SOFTWARE IS PROVIDED " AS IS" , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29+ # THE SOFTWARE IS PROVIDED “ AS IS” , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2930# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3031# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3132# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3435# THE SOFTWARE.
3536
3637from __future__ import annotations
37-
3838from .base import VerticaPythonIntegrationTestCase
3939
4040
4141class LoadBalanceTestCase (VerticaPythonIntegrationTestCase ):
42+
4243 def setUp (self ):
4344 super (LoadBalanceTestCase , self ).setUp ()
4445 self ._host = self ._conn_info ['host' ]
@@ -54,18 +55,23 @@ def tearDown(self):
5455 cur = conn .cursor ()
5556 cur .execute ("SELECT set_load_balance_policy('NONE')" )
5657 cur .execute ("DROP TABLE IF EXISTS test_loadbalance" )
58+
5759 super (LoadBalanceTestCase , self ).tearDown ()
5860
5961 def get_node_num (self ):
6062 with self ._connect () as conn :
6163 cur = conn .cursor ()
6264 cur .execute ("SELECT count(*) FROM nodes WHERE node_state='UP'" )
63- db_node_num = cur .fetchone ()[0 ]
64- return db_node_num
65+ return cur .fetchone ()[0 ]
66+
67+ # -------------------------
68+ # Load Balancing Test Cases
69+ # -------------------------
6570
6671 def test_loadbalance_option_disabled (self ):
6772 if 'connection_load_balance' in self ._conn_info :
6873 del self ._conn_info ['connection_load_balance' ]
74+
6975 self .assertConnectionSuccess ()
7076
7177 self ._conn_info ['connection_load_balance' ] = False
@@ -74,162 +80,190 @@ def test_loadbalance_option_disabled(self):
7480 def test_loadbalance_random (self ):
7581 self .require_DB_nodes_at_least (3 )
7682 self ._conn_info ['connection_load_balance' ] = True
83+
7784 rowsToInsert = 3 * self .db_node_num
7885
7986 with self ._connect () as conn :
8087 cur = conn .cursor ()
8188 cur .execute ("SELECT set_load_balance_policy('RANDOM')" )
8289 cur .execute ("DROP TABLE IF EXISTS test_loadbalance" )
8390 cur .execute ("CREATE TABLE test_loadbalance (n varchar)" )
84- # record which node the client has connected to
91+
92+ # Record the node each connection lands on
8593 for i in range (rowsToInsert ):
8694 with self ._connect () as conn1 :
8795 cur1 = conn1 .cursor ()
88- cur1 .execute ("INSERT INTO test_loadbalance (SELECT node_name FROM sessions "
89- "WHERE session_id = (SELECT current_session()))" )
96+ cur1 .execute ("""
97+ INSERT INTO test_loadbalance
98+ (SELECT node_name FROM sessions
99+ WHERE session_id = (SELECT current_session()))
100+ """ )
90101 conn1 .commit ()
91102
92- cur .execute ("SELECT count(DISTINCT n)> 1 FROM test_loadbalance" )
103+ cur .execute ("SELECT count(DISTINCT n) > 1 FROM test_loadbalance" )
93104 res = cur .fetchone ()
94105 self .assertTrue (res [0 ])
95106
96107 def test_loadbalance_none (self ):
97- # Client turns on connection_load_balance but server is unsupported
108+ # Server LB disabled
98109 with self ._connect () as conn :
99110 cur = conn .cursor ()
100111 cur .execute ("SELECT set_load_balance_policy('NONE')" )
112+
113+ # Client LB enabled (should be ignored)
101114 self ._conn_info ['connection_load_balance' ] = True
102115
103- # Client will proceed with the existing connection with initiator
116+ # Should still connect to initiator
104117 self .assertConnectionSuccess ()
105118
106- # Test for multi -node DB
119+ # Multi -node test
107120 self .require_DB_nodes_at_least (3 )
108121 rowsToInsert = 3 * self .db_node_num
109122
110123 with self ._connect () as conn :
111124 cur = conn .cursor ()
112125 cur .execute ("DROP TABLE IF EXISTS test_loadbalance" )
113126 cur .execute ("CREATE TABLE test_loadbalance (n varchar)" )
114- # record which node the client has connected to
127+
128+ # Record connection node for each new session
115129 for i in range (rowsToInsert ):
116130 with self ._connect () as conn1 :
117131 cur1 = conn1 .cursor ()
118- cur1 .execute ("INSERT INTO test_loadbalance (SELECT node_name FROM sessions "
119- "WHERE session_id = (SELECT current_session()))" )
132+ cur1 .execute ("""
133+ INSERT INTO test_loadbalance
134+ (SELECT node_name FROM sessions
135+ WHERE session_id = (SELECT current_session()))
136+ """ )
120137 conn1 .commit ()
121138
122- cur .execute ("SELECT count(DISTINCT n)= 1 FROM test_loadbalance" )
139+ cur .execute ("SELECT count(DISTINCT n) = 1 FROM test_loadbalance" )
123140 res = cur .fetchone ()
141+
142+ # Debug logging
143+ print ("DEBUG: count(DISTINCT n)=1 result:" , res )
144+ cur .execute ("SELECT DISTINCT n FROM test_loadbalance" )
145+ all_nodes = [r [0 ] for r in cur .fetchall ()]
146+ print ("DEBUG: nodes inserted:" , all_nodes )
147+
124148 self .assertTrue (res [0 ])
125149
126150 def test_loadbalance_roundrobin (self ):
127151 self .require_DB_nodes_at_least (3 )
128152 self ._conn_info ['connection_load_balance' ] = True
153+
129154 rowsToInsert = 3 * self .db_node_num
130155
131156 with self ._connect () as conn :
132157 cur = conn .cursor ()
133158 cur .execute ("SELECT set_load_balance_policy('ROUNDROBIN')" )
134159 cur .execute ("DROP TABLE IF EXISTS test_loadbalance" )
135160 cur .execute ("CREATE TABLE test_loadbalance (n varchar)" )
136- # record which node the client has connected to
161+
137162 for i in range (rowsToInsert ):
138163 with self ._connect () as conn1 :
139164 cur1 = conn1 .cursor ()
140- cur1 .execute ("INSERT INTO test_loadbalance (SELECT node_name FROM sessions "
141- "WHERE session_id = (SELECT current_session()))" )
165+ cur1 .execute ("""
166+ INSERT INTO test_loadbalance
167+ (SELECT node_name FROM sessions
168+ WHERE session_id = (SELECT current_session()))
169+ """ )
142170 conn1 .commit ()
143171
144172 cur .execute ("SELECT count(n)=3 FROM test_loadbalance GROUP BY n" )
145173 res = cur .fetchall ()
146- # verify that all db_node_num nodes are represented equally
174+
175+ # All nodes should have equal distribution
147176 self .assertEqual (len (res ), self .db_node_num )
148177 for i in res :
149178 self .assertEqual (i , [True ])
150179
180+ # -------------------------
181+ # Failover Test Cases
182+ # -------------------------
183+
151184 def test_failover_empty_backup (self ):
152- # Connect to primary server
153185 if 'backup_server_node' in self ._conn_info :
154186 del self ._conn_info ['backup_server_node' ]
155187 self .assertConnectionSuccess ()
188+
156189 self ._conn_info ['backup_server_node' ] = []
157190 self .assertConnectionSuccess ()
158191
159- # Set primary server to invalid host and port
160192 self ._conn_info ['host' ] = 'invalidhost'
161193 self ._conn_info ['port' ] = 9999
162-
163- # Fail to connect to primary server
164194 self .assertConnectionFail ()
165195
166196 def test_failover_one_backup (self ):
167- # Set primary server to invalid host and port
168197 self ._conn_info ['host' ] = 'invalidhost'
169198 self ._conn_info ['port' ] = 9999
170199
171- # One valid address in backup_server_node: port is an integer
200+ # Valid entry ( port int)
172201 self ._conn_info ['backup_server_node' ] = [(self ._host , self ._port )]
173202 self .assertConnectionSuccess ()
174203
175- # One valid address in backup_server_node: port is a string
204+ # Valid entry ( port string)
176205 self ._conn_info ['backup_server_node' ] = [(self ._host , str (self ._port ))]
177206 self .assertConnectionSuccess ()
178207
179- # One invalid address in backup_server_node: DNS failed, Name or service not known
208+ # Invalid cases
180209 self ._conn_info ['backup_server_node' ] = [('invalidhost2' , 8888 )]
181210 self .assertConnectionFail ()
182211
183- # One invalid address in backup_server_node: DNS failed, Name or service not known
184212 self ._conn_info ['backup_server_node' ] = [('123.456.789.123' , 8888 )]
185213 self .assertConnectionFail ()
186214
187- # One invalid address in backup_server_node: DNS failed, Address family for hostname not supported
188215 self ._conn_info ['backup_server_node' ] = [('fd76:6572:7469:6361:0:242:ac11:4' , 8888 )]
189216 self .assertConnectionFail ()
190217
191- # One invalid address in backup_server_node: Wrong port, connection refused
192218 self ._conn_info ['backup_server_node' ] = [(self ._host , 8888 )]
193219 self .assertConnectionFail ()
194220
195221 def test_failover_multi_backup (self ):
196- # Set primary server to invalid host and port
197222 self ._conn_info ['host' ] = 'invalidhost'
198223 self ._conn_info ['port' ] = 9999
199224
200- # One valid and two invalid addresses in backup_server_node
201- self ._conn_info ['backup_server_node' ] = [(self ._host , self ._port ), 'invalidhost2' , 'foo' ]
225+ self ._conn_info ['backup_server_node' ] = [
226+ (self ._host , self ._port ), 'invalidhost2' , 'foo'
227+ ]
202228 self .assertConnectionSuccess ()
203- self ._conn_info ['backup_server_node' ] = ['foo' , (self ._host , self ._port ), ('123.456.789.1' , 888 )]
229+
230+ self ._conn_info ['backup_server_node' ] = [
231+ 'foo' , (self ._host , self ._port ), ('123.456.789.1' , 888 )
232+ ]
204233 self .assertConnectionSuccess ()
205- self ._conn_info ['backup_server_node' ] = ['foo' , ('invalidhost2' , 8888 ), (self ._host , self ._port )]
234+
235+ self ._conn_info ['backup_server_node' ] = [
236+ 'foo' , ('invalidhost2' , 8888 ), (self ._host , self ._port )
237+ ]
206238 self .assertConnectionSuccess ()
207239
208- # Three invalid addresses in backup_server_node
209- self ._conn_info ['backup_server_node' ] = ['foo' , (self ._host , 9999 ), ('123.456.789.1' , 888 )]
240+ # All invalid
241+ self ._conn_info ['backup_server_node' ] = [
242+ 'foo' , (self ._host , 9999 ), ('123.456.789.1' , 888 )
243+ ]
210244 self .assertConnectionFail ()
211245
212246 def test_failover_backup_format (self ):
213- # Set primary server to invalid host and port
214247 self ._conn_info ['host' ] = 'invalidhost'
215248 self ._conn_info ['port' ] = 9999
216249
217250 err_msg = 'Connection option "backup_server_node" must be a list'
218251 self ._conn_info ['backup_server_node' ] = (self ._host , self ._port )
219252 self .assertConnectionFail (TypeError , err_msg )
220253
221- err_msg = ('Each item of connection option "backup_server_node"'
222- r' must be a host string or a \(host, port\) tuple' )
254+ err_msg = (
255+ 'Each item of connection option "backup_server_node"'
256+ r' must be a host string or a \(host, port\) tuple'
257+ )
223258 self ._conn_info ['backup_server_node' ] = [9999 ]
224259 self .assertConnectionFail (TypeError , err_msg )
260+
225261 self ._conn_info ['backup_server_node' ] = [(self ._host , self ._port , 'foo' , 9999 )]
226262 self .assertConnectionFail (TypeError , err_msg )
227263
228264 err_msg = 'Host must be a string: invalid value: .*'
229265 self ._conn_info ['backup_server_node' ] = [(9999 , self ._port )]
230266 self .assertConnectionFail (TypeError , err_msg )
231- self ._conn_info ['backup_server_node' ] = [(9999 , 'port_num' )]
232- self .assertConnectionFail (TypeError , err_msg )
233267
234268 err_msg = 'Port must be an integer or a string: invalid value: .*'
235269 self ._conn_info ['backup_server_node' ] = [(self ._host , 5433.0022 )]
@@ -238,28 +272,33 @@ def test_failover_backup_format(self):
238272 err_msg = r'Port .* is not a valid string: invalid literal for int\(\) with base 10: .*'
239273 self ._conn_info ['backup_server_node' ] = [(self ._host , 'port_num' )]
240274 self .assertConnectionFail (ValueError , err_msg )
275+
241276 self ._conn_info ['backup_server_node' ] = [(self ._host , '5433.0022' )]
242277 self .assertConnectionFail (ValueError , err_msg )
243278
244279 err_msg = 'Invalid port number: .*'
245280 self ._conn_info ['backup_server_node' ] = [(self ._host , - 1000 )]
246281 self .assertConnectionFail (ValueError , err_msg )
282+
247283 self ._conn_info ['backup_server_node' ] = [(self ._host , 66000 )]
248284 self .assertConnectionFail (ValueError , err_msg )
285+
249286 self ._conn_info ['backup_server_node' ] = [(self ._host , '-1000' )]
250287 self .assertConnectionFail (ValueError , err_msg )
288+
251289 self ._conn_info ['backup_server_node' ] = [(self ._host , '66000' )]
252290 self .assertConnectionFail (ValueError , err_msg )
253291
254292 def test_failover_with_loadbalance_roundrobin (self ):
255293 self .require_DB_nodes_at_least (3 )
256294
257- # Set primary server to invalid host and port
258295 self ._conn_info ['host' ] = 'invalidhost'
259296 self ._conn_info ['port' ] = 9999
260297 self .assertConnectionFail ()
261298
262- self ._conn_info ['backup_server_node' ] = [('invalidhost2' , 8888 ), (self ._host , self ._port )]
299+ self ._conn_info ['backup_server_node' ] = [
300+ ('invalidhost2' , 8888 ), (self ._host , self ._port )
301+ ]
263302 self .assertConnectionSuccess ()
264303
265304 self ._conn_info ['connection_load_balance' ] = True
@@ -270,18 +309,20 @@ def test_failover_with_loadbalance_roundrobin(self):
270309 cur .execute ("SELECT set_load_balance_policy('ROUNDROBIN')" )
271310 cur .execute ("DROP TABLE IF EXISTS test_loadbalance" )
272311 cur .execute ("CREATE TABLE test_loadbalance (n varchar)" )
273- # record which node the client has connected to
312+
274313 for i in range (rowsToInsert ):
275314 with self ._connect () as conn1 :
276315 cur1 = conn1 .cursor ()
277- cur1 .execute ("INSERT INTO test_loadbalance ("
278- "SELECT node_name FROM sessions "
279- "WHERE session_id = (SELECT current_session()))" )
316+ cur1 .execute ("""
317+ INSERT INTO test_loadbalance
318+ (SELECT node_name FROM sessions
319+ WHERE session_id = (SELECT current_session()))
320+ """ )
280321 conn1 .commit ()
281322
282323 cur .execute ("SELECT count(n)=3 FROM test_loadbalance GROUP BY n" )
283324 res = cur .fetchall ()
284- # verify that all db_node_num nodes are represented equally
325+
285326 self .assertEqual (len (res ), self .db_node_num )
286327 for i in res :
287328 self .assertEqual (i , [True ])
0 commit comments