-
Notifications
You must be signed in to change notification settings - Fork 186
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
QueueIterator raises StopAsyncIteration when iterator/channel is closed. #615
base: master
Are you sure you want to change the base?
QueueIterator raises StopAsyncIteration when iterator/channel is closed. #615
Conversation
a5180c0
to
15ca5ee
Compare
@@ -201,8 +204,7 @@ async def ready(self) -> None: | |||
def __del__(self) -> None: | |||
if ( | |||
self.is_closed or | |||
self.loop.is_closed() or | |||
not hasattr(self, "connection") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I overlooked some method how self
could end up with a connection
attribute I will undo this change.
9ea9416
to
f12ecfb
Compare
a2e3647
to
5652d2d
Compare
5652d2d
to
71fc125
Compare
@mosquito ping |
@Darsstar this request contains a lot of changes that could potentially breaks backward compatibility. I'm still thinking about how to test it so that I understand I need to make a separate major release, or make do with a minor one. |
I assume you are refering to:
9.4.0 already dropped Python 3.7. The changes taking advantage of 3.8, all contained in a single commit, were made in a way it should be backward compatible. It probably should be a new major version due to the protected properties. Although, I think those could be rewritten as |
709ecc8
to
8155901
Compare
I rebased, which added the 3.12 tests, and now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for not giving up on this improvement, I'm sorry that I don't have much time to devote to it right now. But I'm open to discussion.
if self._closed.is_set(): | ||
raise StopAsyncIteration | ||
|
||
message = asyncio.create_task( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating task on each message is significantly slower. Task creation works fine when the coroutine do some long job or waiting network.
Here, as many as three tasks are created that must not only be queued for execution, but also canceled upon the occurrence of one of the events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of Python 3.11 passing coroutines to asyncio.wait()
is forbidden. So I recuded it to one task per message, instead of three.
I don't think it is possible to remove that last task without effectively reimplementing asyncio.wait()
.
Small update: I am currently under the impression this PR isn't at fault. I branched from I don't have a strong lead, but I started looking at |
8155901
to
6ec53f1
Compare
6ec53f1
to
0d2ae5b
Compare
The tests are passing again :) Public backward incompatible API changes:
Internal backward incompatible API changes:
Those are all I found going over all the changes again. My preference would be leaving it as is and reflecting that in the version number. Say the words and I will will turn |
d720840
to
c5626ad
Compare
c5626ad
to
9abe45f
Compare
Hey @Darsstar, I am coming from #623 and I have a question about how we should be handling the |
I just re-read the issue and this PR may also be fixing a memory leak I have been facing for a while. I am going to pull this version down and see if it fixes it. Will report back. |
This branch should only throw
Alternatively you could create a wrapper
|
@LockedThread @Darsstar sorry, lots of changes, should planning to retest all these myself. |
All good, we appreciate your diligent work in supporting this project. |
Unsurprisingly fixing this requires more code and therefor increases the runtime. @gglluukk's benchmark (thanks!) show about 10% runtime degredation:
|
It would be great to get the time down but I am personally willing to accept a 10% degradation in order to not have a memory leak. |
I don't see optimisation potential without porting the library to C/C++/Rust. (While keeping the implementation correct.) |
Hopefully PyO3 gets better support for async/await, while maintaining interoperability with asyncio. There is a lot of active work on this right now. Once that happens, using Rust will be feasible. In the meantime, losing 10% in performance is worthwhile to make sure I dont get OOM kills. |
You should do a performance test with cProfile for example. The last time I did this, it marshall in pamqp the slowest one. |
See #358
Currently QueueIterator never throws a StopAsyncIterator exception. Not when the channel is closed, and not after QueueIterator's close method is called.
Which implies that starting an
async for message in queue.iterator():
loop will keep running "forever" even if no new message will ever arrive. ("forever" because the asyncio task can be canceled, etc.)This PR fixes that in a backwards compatible way. Some tests are refactored to rely on this implicitly, and new ones that explicitly test QueueIterator.anext() throws certain exceptions have been added as well.