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

Some points may need reconsideration #4

Open
Klavionik opened this issue Oct 29, 2023 · 0 comments
Open

Some points may need reconsideration #4

Klavionik opened this issue Oct 29, 2023 · 0 comments

Comments

@Klavionik
Copy link

Klavionik commented Oct 29, 2023

I’m reading this tutorial right now, particularly the second part. I like it overall, but I believe some things might be not entirely correct. Even though this tutorial is 4 years old, it's still valuable as there are not many advanced asyncio tutorials around the web. I believe it deserves some discussion.

I'm concerned with that part, where the author says that one of the first examples of the Mayhem service is blocking.

“We are essentially blocking ourselves; first we produce all the messages, one by one. Then we consume them, one by one.”

The author also presents a synchronous example and says it’s equivalent to the async one (in the sense that they’re both non-concurrent). Then she suggests wrapping every await queue.put(msg) in a separate task to make it really concurrent.

  1. Wrapping every queue.put(msg) with a task is not what makes this example actually concurrent. In fact, it's a short sleep inside the for loop. If you only add a sleep call, the producer and the consumer will start working concurrently (because they are two separate coroutines created with create_task in the first place). As far as I understand from looking at the source code, await queue.put(msg) does not yield control to the event loop, unless the queue is full. If the queue has free slots, this method delegates to put_nowait, which is a regular sync method. This gives no time to the consumer coroutine to do any work. In the meantime, if you add a sleep to the sync example, nothing will change, it will indeed produce all the messages and only then consume them.

  2. The author says that at some point the queue may be full, and awaiting put call will block the producer. That’s true, but I believe this is fine. I might be mistaken, but isn’t this an example of backpressure? If your consumer(s) cannot process the flood of events in a timely fashion, then you want to stop producing. Otherwise, you may end up creating thousands of tasks, all awaiting the queue, and at some point, your producer will run out of memory.

This is just my two cents, I would be happy to hear other opinions (maybe @econchick will jump in?). I'll keep on reading anyway, and maybe add some notes later.

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

No branches or pull requests

1 participant