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

[Experimental] add asyncio support #853

Closed
wants to merge 3 commits into from
Closed

Conversation

garrettheel
Copy link
Member

I will likely break this up into separate PRs if we decide to move ahead, but opening this up as a place to discuss what asyncio support might look like.

The approach is inspired by this comment in python-trio, which details an interesting way to run async functions in a sync context, so long as they're actually synchronous under the hood. This allows us to write a single async implementation, and then dynamically generate the sync interface.

As far as the public interface goes, this currently leaves the sync interface unchanged and exposes _async variants for methods. I've only implemented the base connection so far, so this is far from complete.

I'm interested in any feedback on the approach and the interface in particular.

@garrettheel garrettheel changed the title [Experiment] add asyncio support [Experimental] add asyncio support Sep 28, 2020
@svix-yair
Copy link

Hey @garrettheel,
Can you please shed some light on why you abandoned this PR?
We are experimenting with Pynamodb and we need async support.

It would be great to give us some pointers and share any information you already have so we can help adding the Async support for Pynamodb.

@garrettheel
Copy link
Member Author

hey @svix-yair, apologies for forgetting to leave an update here. I'll need to page this back in, but I think the main issue that I hit was aiobotocore being the only library that supports this and:

  1. it brings along a large number of dependencies, where we've typically tried to keep deps very minimal for PynamoDB
  2. it's not sanctioned by AWS or officially supported. there's little to suggest that this will stick around for the long term

I was waiting/hoping that botocore would implement asyncio natively, which would be a much better path to supporting it, but that still hasn't happened sadly. The other alternative is to forgo botocore for everything but a few utility tasks (e.g request signing, auth) and construct/send the requests ourselves (either using lower-level native python APIs or with something like httpx).

I'm definitely still interested in making progress on this and would welcome any help from others.


for attr_name, attr_value in attrs.items():
if attr_name.endswith('_async') and asyncio.iscoroutinefunction(attr_value):
setattr(self, attr_name.rstrip("_async"), wrap_secretly_sync_async_fn(attr_value))
Copy link
Contributor

Choose a reason for hiding this comment

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

removesuffix

@tasn
Copy link

tasn commented Aug 9, 2021

Hey @garrettheel, thanks for all of the information and all of your work on pynamo! I work with @svix-yair, and I've been trying to get an async pynamo working for us.

I understand your concerns about using a 3rd party not sanctioned by AWS, though realistically AWS isn't going to have an async version any time soon, so a community effort is the best we've got. I think the aiobotocore people aren't going anyway, it's the same ones behind apg and aiohttp (which aiobotocore uses). Additionally, many of the deps are just other deps of their own projects. In summary, I don't think it's a massive concern.

The httpx idea sounds very promising, though it also sounds like a lot of work and essentially re-implementing aiobotocore. I think aiobotocore just uses aiohttp so maybe you'd have better luck replacing aiohttp there with something that doesn't pull as many deps?

I wish I could have helped you with all of the above, though tbh, it sounds like a very difficult task for an AWS noob like myself, especially since this task requires deep AWS expertise, or at least deep understanding of how to interact with the AWS services.

I'll try to get this patch working with master and test it with our code. Happy to contribute it back if of interest, let me know.

@tasn tasn mentioned this pull request Aug 10, 2021
@ottokruse
Copy link

Maybe very simple/dumb but I'd be happy if every method call would just have an async variant, that would do this under the hood:

# async variant of get()
def get_async(self, *args, executor=None, **kwargs):
    return loop.run_in_executor(
        executor, lambda: self.get(*args, **kwargs) # Call sync version
    )

Saves me a lot of typing as I'm now wrapping every pynamodb call that goes to DynamoDB with loop.run_in_executor myself. (Could of course find more clever ways, e.g. use __getattr__ to dynamically find the sync version to wrap)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants