From b1f87eda5d544768d64151f7c0424b143b09c47c Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 20 Oct 2025 09:08:13 -0500 Subject: [PATCH 1/6] feature: Loop on active workspace until endpoint is ready MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating a new workspace with wait_on_active=True, the system now waits for the workspace state to become Active, and then verifies that the endpoint is actually ready by attempting to establish a connection. This ensures that the workspace is truly ready for use before returning to the caller. Added _wait_on_endpoint method in WorkspaceManager that polls the workspace endpoint with configurable interval and timeout, using a context manager to properly handle database connections. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- singlestoredb/management/manager.py | 55 +++++++++++++++++++++++++++ singlestoredb/management/workspace.py | 6 +++ 2 files changed, 61 insertions(+) diff --git a/singlestoredb/management/manager.py b/singlestoredb/management/manager.py index 3d974950..79bf8745 100644 --- a/singlestoredb/management/manager.py +++ b/singlestoredb/management/manager.py @@ -310,3 +310,58 @@ def _wait_on_state( out = getattr(self, f'get_{self.obj_type}')(out.id) return out + + def _wait_on_endpoint( + self, + out: Any, + interval: int = 10, + timeout: int = 300, + ) -> Any: + """ + Wait for the endpoint to be ready by attempting to connect. + + Parameters + ---------- + out : Any + Workspace object with an endpoint attribute + interval : int, optional + Interval between each connection attempt (default: 10 seconds) + timeout : int, optional + Maximum time to wait before raising an exception (default: 300 seconds) + + Raises + ------ + ManagementError + If timeout is reached or endpoint is not available + + Returns + ------- + Same object type as `out` + + """ + if not hasattr(out, 'endpoint') or not out.endpoint: + raise ManagementError( + msg=f'{type(out).__name__} object does not have a valid endpoint', + ) + + # Import connection module here to avoid circular imports + from .. import connection + + while True: + try: + # Try to establish a connection to the endpoint using context manager + with connection.connect(host=out.endpoint, connect_timeout=5): + pass + # If connection succeeds, endpoint is ready + break + except Exception: + # If connection fails, check timeout and retry + if timeout <= 0: + raise ManagementError( + msg=f'Exceeded waiting time for {self.obj_type} endpoint ' + 'to become ready', + ) + time.sleep(interval) + timeout -= interval + + return out diff --git a/singlestoredb/management/workspace.py b/singlestoredb/management/workspace.py index 7c394ade..b92dc833 100644 --- a/singlestoredb/management/workspace.py +++ b/singlestoredb/management/workspace.py @@ -1794,6 +1794,12 @@ def create_workspace( interval=wait_interval, timeout=wait_timeout, ) + # After workspace is active, wait for endpoint to be ready + out = self._wait_on_endpoint( + out, + interval=wait_interval, + timeout=wait_timeout, + ) return out def get_workspace_group(self, id: str) -> WorkspaceGroup: From 6d6b505a7dc68c7a11fbda7f672c23b7a9b48288 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 21 Oct 2025 09:23:46 -0500 Subject: [PATCH 2/6] Add debugging messages --- singlestoredb/management/manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/singlestoredb/management/manager.py b/singlestoredb/management/manager.py index 79bf8745..3a1d46d6 100644 --- a/singlestoredb/management/manager.py +++ b/singlestoredb/management/manager.py @@ -353,8 +353,10 @@ def _wait_on_endpoint( with connection.connect(host=out.endpoint, connect_timeout=5): pass # If connection succeeds, endpoint is ready + print('CONNECTED') break except Exception: + print('STILL WAITING') # If connection fails, check timeout and retry if timeout <= 0: raise ManagementError( From 61044d7f81e4c2ca15504bf2bd83955cd639f8a3 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 21 Oct 2025 09:59:25 -0500 Subject: [PATCH 3/6] Add debugging messages --- singlestoredb/management/manager.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/singlestoredb/management/manager.py b/singlestoredb/management/manager.py index 3a1d46d6..b881014c 100644 --- a/singlestoredb/management/manager.py +++ b/singlestoredb/management/manager.py @@ -323,7 +323,7 @@ def _wait_on_endpoint( Parameters ---------- out : Any - Workspace object with an endpoint attribute + Workspace object with a connect method interval : int, optional Interval between each connection attempt (default: 10 seconds) timeout : int, optional @@ -339,18 +339,15 @@ def _wait_on_endpoint( Same object type as `out` """ - if not hasattr(out, 'endpoint') or not out.endpoint: + if not hasattr(out, 'connect') or not out.connect: raise ManagementError( msg=f'{type(out).__name__} object does not have a valid endpoint', ) - # Import connection module here to avoid circular imports - from .. import connection - while True: try: # Try to establish a connection to the endpoint using context manager - with connection.connect(host=out.endpoint, connect_timeout=5): + with out.connect(connect_timeout=5): pass # If connection succeeds, endpoint is ready print('CONNECTED') From 615ef0a8f47811893957699fe9eb327b0c5bfb11 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 21 Oct 2025 10:12:39 -0500 Subject: [PATCH 4/6] Add debugging messages --- singlestoredb/management/manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/singlestoredb/management/manager.py b/singlestoredb/management/manager.py index b881014c..5288295b 100644 --- a/singlestoredb/management/manager.py +++ b/singlestoredb/management/manager.py @@ -354,6 +354,8 @@ def _wait_on_endpoint( break except Exception: print('STILL WAITING') + import traceback + traceback.print_exc() # If connection fails, check timeout and retry if timeout <= 0: raise ManagementError( From 64cf446db4a089c0b8d1751d82e1e74b8e180b08 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 21 Oct 2025 10:27:24 -0500 Subject: [PATCH 5/6] Add debugging messages --- singlestoredb/management/manager.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/singlestoredb/management/manager.py b/singlestoredb/management/manager.py index 5288295b..06e776ee 100644 --- a/singlestoredb/management/manager.py +++ b/singlestoredb/management/manager.py @@ -15,6 +15,7 @@ from .. import config from ..exceptions import ManagementError +from ..exceptions import OperationalError from .utils import get_token @@ -349,10 +350,10 @@ def _wait_on_endpoint( # Try to establish a connection to the endpoint using context manager with out.connect(connect_timeout=5): pass - # If connection succeeds, endpoint is ready - print('CONNECTED') - break - except Exception: + except Exception as exc: + if isinstance(exc, OperationalError) and exc.errno == 1045: + print('CONNECTED') + break print('STILL WAITING') import traceback traceback.print_exc() From e175153a27c16e8bf400d8d75840f78d9a5c7d62 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 21 Oct 2025 10:39:17 -0500 Subject: [PATCH 6/6] Clean up --- singlestoredb/management/manager.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/singlestoredb/management/manager.py b/singlestoredb/management/manager.py index 06e776ee..076d0716 100644 --- a/singlestoredb/management/manager.py +++ b/singlestoredb/management/manager.py @@ -351,12 +351,10 @@ def _wait_on_endpoint( with out.connect(connect_timeout=5): pass except Exception as exc: + # If we get an 'access denied' error, that means that the server is + # up and we just aren't authenticating. if isinstance(exc, OperationalError) and exc.errno == 1045: - print('CONNECTED') break - print('STILL WAITING') - import traceback - traceback.print_exc() # If connection fails, check timeout and retry if timeout <= 0: raise ManagementError(