-
Notifications
You must be signed in to change notification settings - Fork 387
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
feat: add support for persistent recursive watches #715
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #715 +/- ##
==========================================
- Coverage 96.81% 96.56% -0.26%
==========================================
Files 27 27
Lines 3549 3639 +90
==========================================
+ Hits 3436 3514 +78
- Misses 113 125 +12 ☔ View full report in Codecov by Sentry. |
It looks like the test failures are not related to this change. |
Some changes to Kazoo are needed to support persistent recursive watches. Until those merge upstream, vendor and update the parts of Kazoo we need. Upstream PR: python-zk/kazoo#715 Change-Id: Id6372e4075b5b3ffeeef3e0f4751a71e59001ef9
Hi! Why not merge this? |
Hi! Whose approval is need? 🤔 |
Hello, Approval from @python-zk/maintainers is needed. Will try to take a look by end of the week. Can somebody that already tried the feature can confirm it is working as expected? |
While awaiting review/merge we in the Zuul project have vendored a copy[1] of this and used it successfully in production for about nine months (though it does look like our vendored copy omits the ability to remove watches since we never do so, so we'll have to rely on the tests for that). Thanks for taking a look. :) [1] https://opendev.org/zuul/nodepool/src/branch/master/nodepool/zk/vendor |
34adddf
to
625f7c3
Compare
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 the PR, the implementation is great, I just added some questions. Would it be possible to take into account the warning from codecov and cover some of the new lines with a test?
3b030f0
to
d2a911e
Compare
d2a911e
to
45cef47
Compare
ZooKeeper 3.6.0 added support for persistent, and persistent recursive watches. This adds the corresponding support to the Kazoo client class.
45cef47
to
415dc93
Compare
@jeffwidman @ceache @a-ungurianu this one is now ready to be reviewed |
The changes LGTM, thanks! And sorry I wasn't able to address those myself in a timely manner. |
Thank you @jeblair @jeffwidman @ceache @a-ungurianu gentle bump! |
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.
Looks good!
I made a few mostly cosmetic suggestions, but functionally it all looks good to me.
There's enough complexity in the protocol that even with the nice unit test coverage it wouldn't surprise me if there was a bug or two in here, either on our end or zookeeper's end, but given the solid UT coverage I think the best way to flush them out at this point is to merge and let folks bang on it for a while. The nice part is that this is a new feature, so any problems are unlikely to affect existing functionality.
|
||
class TransactionRequest(object): | ||
|
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.
Is this blank line extraneous? I don't normally recall a blank line between class definition and docstring...
if request.mode == AddWatchMode.PERSISTENT: | ||
client._persistent_watchers[request.path].add( | ||
watcher | ||
) | ||
elif request.mode == AddWatchMode.PERSISTENT_RECURSIVE: | ||
client._persistent_recursive_watchers[ | ||
request.path | ||
].add(watcher) |
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 wonder if this might benefit from an else
clause?
I realize it's a no-op, but it still took me a moment to realize that's because it might be a non-persistant watch that's being added... so might be easier for a reader to understand with a code comment...
But code comments can go out of sync, so a simple assertion that it's the expected mode might be useful like:
else:
if request.mode not in [expected modes]:
raise <relevant exception>
that way both clarifies the code and also catches stupid errors if somehow a case was missed down the road.
Alternatively, this might read even simpler as a switch statement with a default case... but I realize those require Python 3.10 so maybe leave a:
# TODO: switch these if/else clauses to switch statements where appropriate once we drop Python 3.9 support
if request.watcher_type == WatcherType.CHILDREN: | ||
client._child_watchers.pop(request.path, None) | ||
elif request.watcher_type == WatcherType.DATA: | ||
client._data_watchers.pop(request.path, None) | ||
elif request.watcher_type == WatcherType.ANY: |
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.
this might read simpler as a switch statement rather than the if/elif/elif
series? technically it should be more performant too
Oh, NVM, just realized switch statements won't work in Python 3.8/3.9 😢
|
||
.. attribute:: PERSISTENT | ||
|
||
The watch is not removed when trigged. |
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.
The watch is not removed when trigged. | |
The watch is not removed when triggered. |
|
||
.. attribute:: PERSISTENT_RECURSIVE | ||
|
||
The watch is not removed when trigged, and applies to all |
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.
The watch is not removed when trigged, and applies to all | |
The watch is not removed when triggered, and applies to all |
pytest.skip("Must use Zookeeper %s.%s or above" % (major, minor)) | ||
|
||
def test_persistent_recursive_watch(self): | ||
# This tests adding and removing a persistent recursive watch. |
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.
# This tests adding and removing a persistent recursive watch. | |
"""This tests adding and removing a persistent recursive watch.""" |
] | ||
|
||
def test_persistent_watch(self): | ||
# This tests adding and removing a persistent watch. |
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.
# This tests adding and removing a persistent watch. | |
"""This tests adding and removing a persistent watch.""" |
dict(type=EventType.DELETED, path="/a"), | ||
] | ||
|
||
def test_persistent_watch(self): |
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.
nit: I'd probably order the tests:
test_persistent_watch
test_persistent_recursive_watch
As 2️⃣ feels like a variant of 1️⃣ , so typically we put the simpler one first.
] | ||
|
||
def test_remove_data_watch(self): | ||
# Test that removing a data watch leaves a child watch in place. |
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.
# Test that removing a data watch leaves a child watch in place. | |
"""Test that removing a data watch leaves a child watch in place.""" |
callback_event.wait(30) | ||
|
||
def test_remove_children_watch(self): | ||
# Test that removing a children watch leaves a data watch in place. |
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.
# Test that removing a children watch leaves a data watch in place. | |
"""Test that removing a children watch leaves a data watch in place.""" |
Why is this needed?
ZooKeeper 3.6.0 added support for persistent, and persistent recursive watches. This adds the corresponding support to the Kazoo client class.
Does this PR introduce any breaking change?
No breaking changes.