Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Client's Artifact CRUD methods into Mixin #16496

Merged
merged 20 commits into from
Dec 31, 2024

Conversation

aaazzam
Copy link
Collaborator

@aaazzam aaazzam commented Dec 24, 2024

Related to #16472, this PR reduces the memory footprint of importing PrefectClient from 34MB to 33.5MB.

Artifacts has the smallest footprint of the 12 primitives in PrefectClient, so I expect replicating this strategy should land us something north of a 25% memory reduction when all is said and done. It accomplishes this by:

  • adds a Literal type for server routes to add a layer of type safety to client-side calls.
  • refactors the client's artifact CRUD methods into a standalone module.
  • uses ForwardRefs when necessary, moving "heavy" imports into the call itself.

This is a wash on readability IMO - isolating "Artifact CRUD" into a standalone module makes reasoning about the "full client" take one more click, but not having to dig around for artifact-y things and being able to share TypedDict stuff makes up for it.

@github-actions github-actions bot added the bug Something isn't working label Dec 24, 2024
Copy link

codspeed-hq bot commented Dec 24, 2024

CodSpeed Performance Report

Merging #16496 will not alter performance

Comparing orchestration-refactor (da3f3c3) with main (307b0aa)

Summary

✅ 3 untouched benchmarks

Copy link
Member

@desertaxle desertaxle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this direction!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a loosely-held preference to house this somewhere other than the orchestration module because nesting artifacts under orchestration strikes me as unintuitive. I don't have a good alternative, but we'll need a sane organization system to accommodate all the different domain clients.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, makes sense. other clients do directories by namespace. So whatever module holds your client, there's a file per namespace adjacent to it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To give ourselves some wiggle room to reorganize later, can you rename this to _artifacts.py to let users know the name and location of this module might change later?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me!

Comment on lines 100 to 121
def request(
client: Client,
method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"],
path: ServerRoutes,
path_params: Optional[dict[str, Any]] = None,
**kwargs: Any,
) -> Response:
if path_params:
path = path.format(**path_params) # type: ignore
return client.request(method, path, **kwargs)


async def arequest(
client: AsyncClient,
method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"],
path: ServerRoutes,
path_params: Optional[dict[str, Any]] = None,
**kwargs: Any,
) -> Response:
if path_params:
path = path.format(**path_params) # type: ignore
return await client.request(method, path, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you turn these two functions into base classes from which domain clients can inherit? It feels a bit weird for a domain client to need to pass its 'private' client to another function to make a request. The base class could also include the __init__ that each of the artifact clients you created declares.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is addressed in:

7c95e8b

src/prefect/client/orchestration/routes.py Show resolved Hide resolved
src/prefect/utilities/generics.py Outdated Show resolved Hide resolved
class BaseClient:
def __init__(self, client: "Client"):
self._client = client
super().__init__()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary since BaseClient doesn't have a base class declared.

Suggested change
super().__init__()

class BaseAsyncClient:
def __init__(self, client: "AsyncClient"):
self._client = client
super().__init__()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary since BaseAsyncClient doesn't have a base class declared.

Suggested change
super().__init__()


from typing import TYPE_CHECKING, Any, Literal, TypeAlias

from prefect.client.orchestration.routes import ServerRoutes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a forward ref too, right?

Comment on lines 232 to 234
"limit": kwargs.get("limit", None),
"offset": kwargs.get("offset", 0),
"sort": kwargs.get("sort", None),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the defaults are declared on the typed dicts at the top of this module, do we still need to provide a default value for the .get call here?

@aaazzam aaazzam mentioned this pull request Dec 29, 2024
@desertaxle desertaxle self-assigned this Dec 31, 2024
Copy link
Member

@desertaxle desertaxle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@desertaxle desertaxle merged commit c03b7b5 into main Dec 31, 2024
38 checks passed
@desertaxle desertaxle deleted the orchestration-refactor branch December 31, 2024 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants