-
Notifications
You must be signed in to change notification settings - Fork 1.1k
PYTHON-2453 Add MongoDB Versioned API #536
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
Conversation
dfcf2cc
to
30a43fe
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.
- Can you edit the docstring for
Database.command
to specify that users must not explicitly add the serverApi key to the command document? This is mandated by the spec: https://github.com/mongodb/specifications/blob/master/source/versioned-api/versioned-api.rst#generic-command-helper - How does this implementation ensure that serverApi params don't get added to getMore commands?
if (session and session.in_transaction and | ||
not session._starting_transaction): | ||
return | ||
if self.opts.server_api: |
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.
To make this more explicitly adhere to the spec, I think we should use if self.opts.server_api is not None
. Though the enum doesn't have any such value, the above comparison could evaluate to false if the value is set to something which evaluates to a bool false.
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.
self.opts.server_api
is either None
or an instance of ServerApi
so I'm not sure I follow.
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.
My point here was that since the spec asks us to check if the user passed a value at all, that's exactly what we should do. Since None
is the default for this kwarg, if <kwarg name> is not None
is a (more) explicit check for whether it contains its default value.
It doesn't really affect behavior so if you prefer it the way it is, leave it.
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 a user passes something that's not a ServerApi they will get an error when creating the client:
>>> MongoClient(server_api=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pymongo/mongo_client.py", line 678, in __init__
keyword_opts.cased_key(k), v) for k, v in keyword_opts.items()))
File "pymongo/mongo_client.py", line 678, in <genexpr>
keyword_opts.cased_key(k), v) for k, v in keyword_opts.items()))
File "pymongo/common.py", line 764, in validate
value = validator(option, value)
File "pymongo/common.py", line 537, in validate_server_api_or_none
raise TypeError("%s must be an instance of ServerApi" % (option,))
TypeError: server_api must be an instance of ServerApi
"""Server API version "1".""" | ||
|
||
|
||
class ServerApi(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.
What do you think about declaring this as an overridden namedtuple instance much like CodecOptions
? If you think that's a good idea, I think this would also benefit from having a with_options
method like CodecOptions
.
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 don't see much value in making this a namedtuple. CodecOptions inherits from namedtuple to make it easier to work with in the C extensions. I also see no reason to add a with_options
. The intended use case is to create this class once.
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.
One benefit would be immuatability. I believe the spec mandates that this be an immutable 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.
This class is already effectively immutable because there are no public attributes which can be mutated.
test/__init__.py
Outdated
@@ -751,6 +764,8 @@ def sanitize_cmd(cmd): | |||
cp.pop('$db', None) | |||
cp.pop('$readPreference', None) | |||
cp.pop('lsid', None) | |||
# Versioned api parameters | |||
cp.pop('apiVersion', None) |
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 remove the serverApi as opped to skipping the monitoring tests when testing against versioned API?
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 recall, lot's of tests failed because of this. What do you think of:
cp.pop('apiVersion', None) | |
if MONGODB_API_VERSION: | |
cp.pop('apiVersion', None) |
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.
Thanks for the review. I'll make the requested changes soon. Responding to a few of the comments now.
- How does this implementation ensure that serverApi params don't get added to getMore commands?
We don't add serverApi to getMore commands because ... well... we don't add them. The pymongo.message._GetMore class does not call sock_info.add_server_api(command, session)
. The tests also verify this behavior.
if (session and session.in_transaction and | ||
not session._starting_transaction): | ||
return | ||
if self.opts.server_api: |
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.
self.opts.server_api
is either None
or an instance of ServerApi
so I'm not sure I follow.
"""Server API version "1".""" | ||
|
||
|
||
class ServerApi(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.
I don't see much value in making this a namedtuple. CodecOptions inherits from namedtuple to make it easier to work with in the C extensions. I also see no reason to add a with_options
. The intended use case is to create this class once.
test/__init__.py
Outdated
@@ -751,6 +764,8 @@ def sanitize_cmd(cmd): | |||
cp.pop('$db', None) | |||
cp.pop('$readPreference', None) | |||
cp.pop('lsid', None) | |||
# Versioned api parameters | |||
cp.pop('apiVersion', None) |
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 recall, lot's of tests failed because of this. What do you think of:
cp.pop('apiVersion', None) | |
if MONGODB_API_VERSION: | |
cp.pop('apiVersion', None) |
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.
Can you edit the docstring for Database.command to specify that users must not explicitly add the serverApi key to the command document?
Done.
if (session and session.in_transaction and | ||
not session._starting_transaction): | ||
return | ||
if self.opts.server_api: |
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.
My point here was that since the spec asks us to check if the user passed a value at all, that's exactly what we should do. Since None
is the default for this kwarg, if <kwarg name> is not None
is a (more) explicit check for whether it contains its default value.
It doesn't really affect behavior so if you prefer it the way it is, leave it.
"""Server API version "1".""" | ||
|
||
|
||
class ServerApi(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.
One benefit would be immuatability. I believe the spec mandates that this be an immutable object.
if COMPRESSORS: | ||
self.default_client_options["compressors"] = COMPRESSORS | ||
if MONGODB_API_VERSION: |
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.
Instead, should we check for REQUIRE_API_VERSION
and then instantiate the server_api
object using MONGODB_API_VERSION
?
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 is contingent on us keeping the REQUIRE_API_VERSION
envvar. Seems like we might not need it?
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.
See my comment above about the difference between the two variables: https://github.com/mongodb/mongo-python-driver/pull/536/files/cfd91bd63ec0dcfb2becdeb2a6481861caf0af7e#r553177142
display_name: "requireApiVersion1" | ||
tags: [ "requireApiVersion_tag" ] | ||
variables: | ||
REQUIRE_API_VERSION: "1" |
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.
What is the REQUIRE_API_VERSION
variable used for? I don't see it used anywhere.
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.
We need both variables. REQUIRE_API_VERSION tells drivers-evg-tools to start the cluster with the requireApiVersion parameter. MONGODB_API_VERSION tells the test suite which ServerApi.version to use. When we eventually test version 2, we'll need to use:
REQUIRE_API_VERSION: "1"
MONGODB_API_VERSION: "2"
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.
Ah, I see! That's certainly going to be hard to figure out for a newcomer. Can we add a comment that REQUIRE_API_VERSION
is used by cluster orchestration scripts in drivers-evergreen-tools?
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.
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.
One suggested addition of a comment. Otherwise, LGTM
Now that these changes were approved I'm going to rebase to fix the merge conflicts. |
Support Unified Test Format version 1.1 (serverParameters in runOnRequirements)
7828c14
to
3fa1f93
Compare
Add pymongo.server_api.ServerApi and the MongoClient server_api option. Support Unified Test Format version 1.1 (serverParameters in runOnRequirements) Skip dropRole tests due to SERVER-53499. (cherry picked from commit ac2f506)
This PR adds support for the MongoDB Versioned API version 1: https://github.com/mongodb/specifications/blob/784857f/source/versioned-api/versioned-api.rst
TODO:
server_api
MongoClient option.mongod --setParameter "requireApiVersion=1"
). See: https://github.com/mongodb/specifications/tree/784857f548747ee6f894a5b6f7b6e5bb89103709/source/versioned-api/tests