Skip to content

Commit 3030b82

Browse files
fix: error on connect_async() event loop mismatch (#1113)
Today, the Python Connector for async usage does not allow being called across multiple event loops. However, we do not explicitly define that logic nor make it clear via an error message. This PR adds an error message to check in connect_async if the Connector._loop attribute matches the current running event loop and errors if it does not (multiple event loops). Related to #1107
1 parent 2385f4f commit 3030b82

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

google/cloud/sql/connector/connector.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from google.cloud.sql.connector.enums import DriverMapping
3535
from google.cloud.sql.connector.enums import IPTypes
3636
from google.cloud.sql.connector.enums import RefreshStrategy
37+
from google.cloud.sql.connector.exceptions import ConnectorLoopError
3738
from google.cloud.sql.connector.instance import RefreshAheadCache
3839
from google.cloud.sql.connector.lazy import LazyRefreshCache
3940
from google.cloud.sql.connector.monitored_cache import MonitoredCache
@@ -281,6 +282,15 @@ async def connect_async(
281282
KeyError: Unsupported database driver Must be one of pymysql, asyncpg,
282283
pg8000, and pytds.
283284
"""
285+
# check if event loop is running in current thread
286+
if self._loop != asyncio.get_running_loop():
287+
raise ConnectorLoopError(
288+
"Running event loop does not match 'connector._loop'. "
289+
"Connector.connect_async() must be called from the event loop "
290+
"the Connector was initialized with. If you need to connect "
291+
"across event loops, please use a new Connector object."
292+
)
293+
284294
if self._keys is None:
285295
self._keys = asyncio.create_task(generate_keys())
286296
if self._client is None:

tests/unit/test_connector.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import asyncio
1818
import os
19+
from threading import Thread
1920
from typing import Union
2021

2122
from aiohttp import ClientResponseError
@@ -29,6 +30,7 @@
2930
from google.cloud.sql.connector.client import CloudSQLClient
3031
from google.cloud.sql.connector.connection_name import ConnectionName
3132
from google.cloud.sql.connector.exceptions import CloudSQLIPTypeError
33+
from google.cloud.sql.connector.exceptions import ConnectorLoopError
3234
from google.cloud.sql.connector.exceptions import IncompatibleDriverError
3335
from google.cloud.sql.connector.instance import RefreshAheadCache
3436

@@ -280,6 +282,38 @@ async def test_Connector_connect_async(
280282
assert connection is True
281283

282284

285+
@pytest.mark.asyncio
286+
async def test_Connector_connect_async_multiple_event_loops(
287+
fake_credentials: Credentials, fake_client: CloudSQLClient
288+
) -> None:
289+
"""Test that Connector.connect_async errors when run on wrong event loop."""
290+
291+
new_loop = asyncio.new_event_loop()
292+
thread = Thread(target=new_loop.run_forever, daemon=True)
293+
thread.start()
294+
295+
async with Connector(
296+
credentials=fake_credentials, loop=asyncio.get_running_loop()
297+
) as connector:
298+
connector._client = fake_client
299+
with pytest.raises(ConnectorLoopError) as exc_info:
300+
future = asyncio.run_coroutine_threadsafe(
301+
connector.connect_async(
302+
"test-project:test-region:test-instance", "asyncpg"
303+
),
304+
loop=new_loop,
305+
)
306+
future.result()
307+
assert (
308+
exc_info.value.args[0] == "Running event loop does not match "
309+
"'connector._loop'. Connector.connect_async() must be called from "
310+
"the event loop the Connector was initialized with. If you need to "
311+
"connect across event loops, please use a new Connector object."
312+
)
313+
new_loop.call_soon_threadsafe(new_loop.stop)
314+
thread.join()
315+
316+
283317
@pytest.mark.asyncio
284318
async def test_create_async_connector(fake_credentials: Credentials) -> None:
285319
"""Test that create_async_connector properly initializes connector

0 commit comments

Comments
 (0)