refactor(BA-4186): move out of convention function into valid directory#8509
refactor(BA-4186): move out of convention function into valid directory#8509HyeockJinKim merged 4 commits intomainfrom
Conversation
| @@ -36,6 +45,28 @@ def __init__(self, client_session: aiohttp.ClientSession, address: str, token: s | |||
| self._address = address | |||
| self._token = token | |||
|
|
|||
| @staticmethod | |||
There was a problem hiding this comment.
As the purpose and how it initialze is little bit different, query_status and AppProxy,
I've put it as a method but to keep it's purpose, leave arguments and logic the same.
There was a problem hiding this comment.
Pull request overview
This PR refactors the query_wsproxy_status function by moving it from the API layer (manager/api/scaling_group.py) to the client layer (manager/clients/appproxy/client.py) as a static method AppProxyClient.query_status(). This resolves a circular import issue and improves code organization by placing the client logic in the appropriate directory.
Changes:
- Moved
query_wsproxy_statusfunction toAppProxyClient.query_status()static method - Updated import statements and function calls across affected files
- Improved error message terminology from "wsproxy" to "app-proxy" for consistency
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/ai/backend/manager/api/scaling_group.py | Removed query_wsproxy_status function and related imports (json, aiotools, InternalServerError); added import for AppProxyClient and updated function call |
| src/ai/backend/manager/clients/appproxy/client.py | Added query_status static method with required imports (json, logging, aiotools, BraceStyleAdapter, InternalServerError) |
| src/ai/backend/manager/services/session/service.py | Updated import from scaling_group to appproxy.client and changed function call to use new static method |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @staticmethod | ||
| @aiotools.lru_cache(expire_after=30) # expire after 30 seconds | ||
| async def query_status( | ||
| appproxy_addr: str, | ||
| ) -> dict[str, Any]: | ||
| """Query the status of an app-proxy (wsproxy) at the given address.""" | ||
| async with ( | ||
| aiohttp.ClientSession() as session, | ||
| session.get( | ||
| appproxy_addr + "/status", | ||
| headers={"Accept": "application/json"}, | ||
| ) as resp, | ||
| ): | ||
| try: | ||
| result = await resp.json() | ||
| except (aiohttp.ContentTypeError, json.JSONDecodeError) as e: | ||
| log.error("Failed to parse app-proxy status response from {}: {}", appproxy_addr, e) | ||
| raise InternalServerError( | ||
| "Got invalid response from app-proxy when querying status" | ||
| ) from e | ||
| return result |
There was a problem hiding this comment.
The query_status method is inconsistent with other methods in this class in several ways:
-
Static method pattern: This is a static method that creates its own
aiohttp.ClientSession, while other methods (create_endpoint,delete_endpoint) are instance methods that useself._client_session. This pattern prevents connection pooling and reuse. -
Missing HTTP status check: Unlike other methods in this class (e.g., line 83:
resp.raise_for_status()), this method doesn't validate the HTTP response status. Non-2xx responses will be silently processed, potentially leading to confusing errors. -
Missing resilience policy: Other methods in this class use the
@appproxy_client_resilience.apply()decorator (lines 70, 86) to apply retry and metrics policies, but this method doesn't have this protection.
Consider refactoring this to be an instance method that follows the established pattern in this class. However, note that this would require updating the call sites to instantiate AppProxyClient instead of calling it as a static method, which may not be feasible given the current usage pattern where an address is passed but no client instance exists.
There was a problem hiding this comment.
I'm not sure modifying code logic would be in the scope for this pr.
There was a problem hiding this comment.
Also, query_status usually done with passing the paramethers which were retrieved from DB_conntection.
It seems out of scope to modify that relevant functions.
moving this method into standalone function can make sense though.
| appproxy_addr + "/status", | ||
| headers={"Accept": "application/json"}, | ||
| ) as resp, | ||
| ): |
There was a problem hiding this comment.
Missing HTTP status validation. The response status should be checked before attempting to parse the JSON body. If the app-proxy returns a non-2xx status code (e.g., 404, 500), the code will attempt to parse the error response as JSON without properly handling the HTTP error.
Add resp.raise_for_status() after line 60 and before attempting to parse the response, similar to the pattern used in the create_endpoint method at line 83.
| ): | |
| ): | |
| resp.raise_for_status() |
dc27a4b to
def3e5a
Compare
| @@ -109,7 +86,7 @@ async def get_wsproxy_version(request: web.Request, params: Any) -> web.Response | |||
| wsproxy_version = "v1" | |||
| else: | |||
| try: | |||
| wsproxy_status = await query_wsproxy_status(wsproxy_addr) | |||
| wsproxy_status = await AppProxyClient.query_status(wsproxy_addr) | |||
There was a problem hiding this comment.
AppProxyClient should be initialized only once when starting the app and query_status() should not be static or class method.
Or you can load the client from a client pool. Please check how AppProxyClient is initialized in AgentRegistry.
There was a problem hiding this comment.
Understood, AppProxyClient is class only should be called when creating or deleting Client.
But as query_status seems more like an utility function which it's role is just being used when terminating or checking the version, I guess i should just split it out of the class for now.
|
Minimized modification's scope as purpose of this PR is preventing circular imports |
646774b to
1469c3c
Compare
|
|
||
| from ai.backend.common import validators as tx | ||
| from ai.backend.logging import BraceStyleAdapter | ||
| from ai.backend.manager.clients.appproxy.client import query_appproxy_status |
There was a problem hiding this comment.
It wasn't about providing it as a generic function, but rather implementing it as a method in the client layer.
There was a problem hiding this comment.
Can you have a look on my first commit?
I firstly tried to put it as @dataclass method, but i felt query_appproxy_status has less relevant features with other methods of AppProxyClient.
If it feels tolerable, I will work that way, or If i'm missing other better way.
please let me know!
e4d29d1 to
78b3f93
Compare
Summary
Note
|
78b3f93 to
992dc90
Compare
| async def fetch_status(self) -> dict[str, Any]: | ||
| try: | ||
| async with self._client_session.get( | ||
| "/status", | ||
| headers={"Accept": "application/json"}, | ||
| ) as resp: |
There was a problem hiding this comment.
TODO: strict return type such as pydantic or dataclass
There was a problem hiding this comment.
I was working on minor fixes on appproxy, i will refactor to this in between.
992dc90 to
43a49ad
Compare
| client = root_ctx.appproxy_client_pool.load_client( | ||
| sgroup.wsproxy_addr, sgroup.wsproxy_api_token or "" | ||
| ) | ||
| status = await client.fetch_status() |
There was a problem hiding this comment.
When this code snippet including data base connection moves to seperate module, we can remove root_ctx.appproxy_client_pool and pass it to service layer arguments only.
43a49ad to
15c028b
Compare
15c028b to
65618a5
Compare
HyeockJinKim
left a comment
There was a problem hiding this comment.
OTEL related modification seems to be invalid. Could you check this.
resolves #8507 (BA-4186)
Move out of convention
query_wsproxy_statusfunction into dedicated class,which prevent cicular import issue and clean up the code structure.
`
Checklist: (if applicable)
ai.backend.testdocsdirectory