|
13 | 13 | # limitations under the License. |
14 | 14 |
|
15 | 15 | import base64 |
| 16 | +import contextlib |
| 17 | +import google.auth.transport.requests |
16 | 18 | import copy |
17 | 19 | import csv |
18 | 20 | import datetime |
@@ -155,6 +157,25 @@ def _load_json_schema(filename="schema.json"): |
155 | 157 | return _parse_schema_resource(json.load(schema_file)) |
156 | 158 |
|
157 | 159 |
|
| 160 | +@contextlib.contextmanager |
| 161 | +def patch_tracked_requests(): |
| 162 | + original_init = google.auth.transport.requests.Request.__init__ |
| 163 | + tracked_requests = [] |
| 164 | + |
| 165 | + def patched_init(self, session=None): |
| 166 | + original_init(self, session=session) |
| 167 | + tracked_requests.append(self) |
| 168 | + |
| 169 | + google.auth.transport.requests.Request.__init__ = patched_init |
| 170 | + try: |
| 171 | + yield tracked_requests |
| 172 | + finally: |
| 173 | + google.auth.transport.requests.Request.__init__ = original_init |
| 174 | + for req in tracked_requests: |
| 175 | + if hasattr(req, "session") and req.session is not None: |
| 176 | + req.session.close() |
| 177 | + |
| 178 | + |
158 | 179 | class Config(object): |
159 | 180 | """Run-time configuration to be modified at set-up. |
160 | 181 |
|
@@ -234,23 +255,34 @@ def _create_bucket(self, bucket_name, location=None): |
234 | 255 |
|
235 | 256 | def test_close_releases_open_sockets(self): |
236 | 257 | current_process = psutil.Process() |
237 | | - conn_count_start = len(current_process.net_connections()) |
| 258 | + conn_start = current_process.net_connections() |
| 259 | + conn_count_start = len(conn_start) |
| 260 | + |
| 261 | + with patch_tracked_requests() as tracked_requests: |
| 262 | + client = Config.CLIENT |
| 263 | + client.query( |
| 264 | + """ |
| 265 | + SELECT |
| 266 | + source_year AS year, COUNT(is_male) AS birth_count |
| 267 | + FROM `bigquery-public-data.samples.natality` |
| 268 | + GROUP BY year |
| 269 | + ORDER BY year DESC |
| 270 | + LIMIT 15 |
| 271 | + """ |
| 272 | + ) |
238 | 273 |
|
239 | | - client = Config.CLIENT |
240 | | - client.query( |
241 | | - """ |
242 | | - SELECT |
243 | | - source_year AS year, COUNT(is_male) AS birth_count |
244 | | - FROM `bigquery-public-data.samples.natality` |
245 | | - GROUP BY year |
246 | | - ORDER BY year DESC |
247 | | - LIMIT 15 |
248 | | - """ |
249 | | - ) |
| 274 | + client.close() |
250 | 275 |
|
251 | | - client.close() |
| 276 | + import gc |
252 | 277 |
|
253 | | - conn_count_end = len(current_process.net_connections()) |
| 278 | + gc.collect() |
| 279 | + conn_end = current_process.net_connections() |
| 280 | + conn_count_end = len(conn_end) |
| 281 | + if conn_count_end > conn_count_start: |
| 282 | + print("DEBUG: test_close_releases_open_sockets failed!") |
| 283 | + print(f"DEBUG: Start connections ({conn_count_start}): {conn_start}") |
| 284 | + print(f"DEBUG: End connections ({conn_count_end}): {conn_end}") |
| 285 | + print(f"DEBUG: Tracked requests: {tracked_requests}") |
254 | 286 | self.assertLessEqual(conn_count_end, conn_count_start) |
255 | 287 |
|
256 | 288 | def test_create_dataset(self): |
@@ -2174,25 +2206,36 @@ def test_dbapi_dry_run_query(self): |
2174 | 2206 | def test_dbapi_connection_does_not_leak_sockets(self): |
2175 | 2207 | pytest.importorskip("google.cloud.bigquery_storage") |
2176 | 2208 | current_process = psutil.Process() |
2177 | | - conn_count_start = len(current_process.net_connections()) |
2178 | | - |
2179 | | - # Provide no explicit clients, so that the connection will create and own them. |
2180 | | - connection = dbapi.connect() |
2181 | | - cursor = connection.cursor() |
2182 | | - |
2183 | | - cursor.execute( |
| 2209 | + conn_start = current_process.net_connections() |
| 2210 | + conn_count_start = len(conn_start) |
| 2211 | + |
| 2212 | + with patch_tracked_requests() as tracked_requests: |
| 2213 | + # Provide no explicit clients, so that the connection will create and own them. |
| 2214 | + connection = dbapi.connect() |
| 2215 | + cursor = connection.cursor() |
| 2216 | + |
| 2217 | + cursor.execute( |
| 2218 | + """ |
| 2219 | + SELECT id, `by`, timestamp |
| 2220 | + FROM `bigquery-public-data.hacker_news.full` |
| 2221 | + ORDER BY `id` ASC |
| 2222 | + LIMIT 100000 |
2184 | 2223 | """ |
2185 | | - SELECT id, `by`, timestamp |
2186 | | - FROM `bigquery-public-data.hacker_news.full` |
2187 | | - ORDER BY `id` ASC |
2188 | | - LIMIT 100000 |
2189 | | - """ |
2190 | | - ) |
2191 | | - rows = cursor.fetchall() |
2192 | | - self.assertEqual(len(rows), 100000) |
2193 | | - |
2194 | | - connection.close() |
2195 | | - conn_count_end = len(current_process.net_connections()) |
| 2224 | + ) |
| 2225 | + rows = cursor.fetchall() |
| 2226 | + self.assertEqual(len(rows), 100000) |
| 2227 | + |
| 2228 | + connection.close() |
| 2229 | + import gc |
| 2230 | + |
| 2231 | + gc.collect() |
| 2232 | + conn_end = current_process.net_connections() |
| 2233 | + conn_count_end = len(conn_end) |
| 2234 | + if conn_count_end > conn_count_start: |
| 2235 | + print("DEBUG: test_dbapi_connection_does_not_leak_sockets failed!") |
| 2236 | + print(f"DEBUG: Start connections ({conn_count_start}): {conn_start}") |
| 2237 | + print(f"DEBUG: End connections ({conn_count_end}): {conn_end}") |
| 2238 | + print(f"DEBUG: Tracked requests: {tracked_requests}") |
2196 | 2239 | self.assertLessEqual(conn_count_end, conn_count_start) |
2197 | 2240 |
|
2198 | 2241 | def _load_table_for_dml(self, rows, dataset_id, table_id): |
|
0 commit comments