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

Add support for async path operations in the server #4617

Closed
robhowley opened this issue Oct 11, 2024 · 7 comments · Fixed by #4647
Closed

Add support for async path operations in the server #4617

robhowley opened this issue Oct 11, 2024 · 7 comments · Fixed by #4647
Labels
kind/feature New feature or request

Comments

@robhowley
Copy link
Contributor

robhowley commented Oct 11, 2024

Is your feature request related to a problem? Please describe.

Add opt in support for async path operations on the python feature server. This would allow the server to take advantage of the async libraries that are slowly becoming available for the various data stores. Lookups to multiple feature views with online stores such as dynamo and redis can be done in parallel. This would provide significant performance gains when calling feature services.

Describe the solution you'd like

If we are willing to move to async methods for path operations, i'd like to to see run_in_threadpool used for the sync methods. example pr here. This is the recommended approach to sync operations in async methods and is more or less what fastapi does under the hood to sync endpoints anyway.

If there is a preference for a manual opt in, then ...

A cli parameter on feast serve to indicate preference for deploying the async paths which will leverage the async feature store sdk methods.

# simple flag param
feast serve --async-read

Under the hood it would declare

@app.post(
    "/get-online-features",
    dependencies=[Depends(inject_user_details)],
)
async def get_online_features(body=Depends(get_body)):
    ...
    # and call
    response_proto = await store.get_online_features(

As async store.push functionality gets added an --async-push flag could be added as well. This approach is an explicit opt-in which is desirable when overriding default behavior. Lets the person deploying the server make informed decisions about performance based upon the backend being used. The caller need not worry about the internals of a service, just the api contract.

Describe alternatives you've considered

  • a separate cli feast-async but
    • it's nice having feast be the one universal cli
    • it implies everything will be async, which it wouldn't be
  • a cli subgroup feast serve async
    • adds more layers and indirection in the code without a lot of value
  • new base class AsyncOnlineDataStore to differentiate from the default synchronous calls
    • when the server is constructed it will define the path operation based upon type
    • this would require a nontrivial refactor

Additional context

Here is a flame graph for a call to a feature service with

  • 3 feature views
  • 11 total features
  • dynamo backend

image

We see default batching behavior and 3 calls per batch all of which are done sequentially. These can be done in parallel.

@robhowley robhowley added the kind/feature New feature or request label Oct 11, 2024
@tokoko
Copy link
Collaborator

tokoko commented Oct 11, 2024

Why don't we simply (at least) default to the async methods if they're available?

@robhowley
Copy link
Contributor Author

Why don't we simply (at least) default to the async methods if they're available?

im def open to that, it's the least work for people hosting the servers. in slack, however, @franciscojavierarceo expressed a preference for an explicit opt-in and that position is def reasonable. personally, im fine either way and happy to implement either one.

iirc all data stores have an async method, just left unimplemented so we'd either want to remove it from the interface and check if the attribute exists or provide some kind of flag in a given impl saying async is available.

@robhowley
Copy link
Contributor Author

made a branch of what the flag option would look like here

@robhowley
Copy link
Contributor Author

robhowley commented Oct 15, 2024

@tokoko @franciscojavierarceo here is what it would look like if async support was set at the data store level. the key lines are here.

im partial to this bc it's not doing anything magical, maintains default behavior where async support doesnt exist but also doesn't require work from the person deploying the service.

if you can get behind this approach i can finish off the pr (get it passing and all that) and have it up in the next day or two.

@robhowley
Copy link
Contributor Author

robhowley commented Oct 16, 2024

one last option here. It is def the cleanest and uses fastapi recommended approach, but makes endpoint async all the time ie not an opt in. this approach would be my preferred given that it follows fastapi patterns, doesn't involve having to know "too much" about feast internals to successfully deploy a service, and it is still explicit at the data store level as to which methods are supported.

@tokoko
Copy link
Collaborator

tokoko commented Oct 17, 2024

+1 on the async_supported method.

I also think making the endpoints themselves async even if calls are synchronous is fine. I understand we're potentially degrading performance, but what's the chance of someone caring about all that and running sync online store calls at the same time? iiuc, the alternative is to have two separate "servers", right? Or is there a way to dynamically inject async qualifier to the endpoint functions?

@robhowley
Copy link
Contributor Author

Or is there a way to dynamically inject async qualifier to the endpoint functions?

@tokoko, not exactly, but that's more or less what I did in option 1 and option 2. You write two path operations and the one that gets executed is controlled by the async flag/indicator. It's a little clumsy looking, makes the code bloated and less readable.

I'm with you on just doing async def and run_in_threadpool when async isn't supported. As you point out, if you're using the sync method, you're already conceding that an extra milli or two isn't your top priority.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/feature New feature or request
Projects
None yet
2 participants