-
Notifications
You must be signed in to change notification settings - Fork 102
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
Removing most of inlineCallbacks for the sake of cancellation support #191
Conversation
I think this is a really good change. I haven't reviewed the code yet, but It seemed to me there were a lot of |
@trenton42 Thanks! Code review would be very appreciated. I'm afraid I might be messing something with complex cases like bulk_write and related functions. |
Looks like something went bad in the last commit... I'll give this a go over today at lunch. Same with other PR. I would still like for there to be timeouts and deadlines, I'm hoping this will just help simplify the code base? |
74d8784
to
4c80a0f
Compare
@psi29a, aah, sorry, there was a stupid mistake in last commit. Fixed. Actualy, I don't much like current timeout implementation for two reasons:
Anyway, even if you want to leave the current API, def timeout(func):
def _timeout(*args, **kwargs):
d = func(*args, **kwargs)
timeout = calc_timeout_from_kwargs(kwargs)
if timeout:
d.addTimeout(timeout, reactor)
return d
return _timeout (To support older versions, existing code with |
Tests against Twisted trunk are failing because today they have changed attributes of |
The reasons why it was done inside of txmongo was because canceling deferreds with @inline.callbacks was an impossibility. ;) Chicken/Egg problem... How much older do you mean, in terms of Twisted? Are we OK with Twisted >= 15.0 |
In theory, now that we have cancelable deferreds, a lot of the timeout/deadline code can go away... along with that nasty hack. |
This PR is meant to remove the Chicken :)
|
Currently, after accepting this PR we might only simplify the implementation of |
4c80a0f
to
dd70401
Compare
Rebased |
#self.assertFailure(self.db.test.find(spec="test"), TypeError) | ||
#self.assertFailure(self.db.test.find(fields="test"), TypeError) | ||
#self.assertFailure(self.db.test.find(skip="test"), TypeError) | ||
#self.assertFailure(self.db.test.find(limit="test"), TypeError) |
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.
Why are these commented?
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.
They are replaced by assertRaises
below. Commented ones should be removed, just forgot it.
|
||
d_insert = self.named_conn.db.coll.insert({'x': 42}, safe=True, timeout=-10) | ||
yield self.assertFailure(d_insert, TimeExceeded) | ||
self.assertRaises(TimeExceeded, self.named_conn.db.coll.insert, {'x': 42}, safe=True, timeout=-10) |
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.
Great! 👍
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.
I'm not sure... Previously all exceptions from @inlineCallbacks
-based methods were returned as failed Deferred. And calling code might be sure that it will always return Deferred. This PR changes this: for some errors it will still return failed Deferreds, but other errors might be raised immediately as direct exceptions. Hence all these changes in testing code.
This actually changes current API and potentially may break some existing code. (inlineCallbacks-based user code won't be affected).
Previous behavior can be easily saved by returning defer.fail(...)
instead of raising, but it looks ugly.
What do you think?
|
||
return self._database.command(SON([("listCollections", 1), | ||
("filter", {"name": self.name})]))\ | ||
.addCallback(on_ok) |
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.
For readability, can you do a carriage return before SON to get it to the next line? Will this give us enough room to attach .addCallback(on_ok) without having to append an \ ?
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.
done
Accepting your Chicken removal code. :) This has been along time coming. Waiting on travis re-build... |
I wish I had more time to dive in and help out here. Thanks for this, @IlyaSkriblovsky 👏 |
And thanks @psi29a for reviewing 👏 |
inlineCallbacks
is totally cool with one drawback: it doesn't properly supports.cancel()
. If you cancelinlineCallbacks
-based function it will silently continue to work and just ignore the result. Currently there is no any way for external code to stopinlineCallbacks
in the middle of its execution.On the other hand, plain old Deferreds are rather good at cancellation: when all the call stack is written in plain
addCallback
-style,.cancel()
is able to stop execution at any stage without any explicit support from the code.So, I suggest to get rid of almost all
inlineCallbacks
in txmongo stack to make it possible to cancel all DB operations.It should be noted though that
cancel()
will have limited usefulness because in normal operation txmongo sends requests to DB immediately and such queries can't be really cancelled..cancel()
can only prevent execution of queries that was held back by waiting for active connection in the pool or complex multi-stage operations like query with getmore's or bulk writes.Other minor advantage of removing
inlineCallbacks
is that it takes considerably more RAM, up to ×3 times according to my benchmarks. In app with tons of concurrent queries it may result in performance increase under high loads (high memory usage calls GC more often).This PR changes a lots of code and also replaces some nice
yield
-based loops with tricky recursive callbacks. So it needs careful code review!If this PR will be merged, we can eventually get rid of
@timeout
andcheck_deadline()
in favor of upcomingDeferred.addTimeout()
(twisted/twisted@f4b3869)Remaining issues:
find()
should kill cursor if cancelled between loading result batchesQuestions to discuss:
txmongo.protocol
andtxmongo.connection
that simply unregister deferred from the waiting lists. Such cancellers are actually useless, because they are effectively identical to Deferreds without cancellers (because Deferred without canceller have special semantics: they are just swallowing.callback()
call after cancellation and doesn't call the callback chain, so effect would be the same). Should I keep them or its better to remove them?inlineCallback
-based, previously returned failed deferreds if arguments didn't passed checks. They are now raising argument checking errors directly, without wrapping withDeferred
. Hence someassertFailure
was replaced byassertRaises
in testing code. This is actually the backwards-incompatible change. Should I wrap theseTypeErrors
withdefer.fail()
to make code backwards-compatible?