remove op_query

This commit is contained in:
Iris Ho 2026-05-07 14:22:54 -07:00
parent 8dc7efade2
commit ebb7b1fdae
No known key found for this signature in database
GPG Key ID: 1D844EFC10209186
3 changed files with 67 additions and 19 deletions

View File

@ -46,6 +46,8 @@ from pymongo.common import (
MAX_MESSAGE_SIZE,
MAX_WIRE_VERSION,
MAX_WRITE_BATCH_SIZE,
MIN_SUPPORTED_SERVER_VERSION,
MIN_SUPPORTED_WIRE_VERSION,
ORDERED_TYPES,
)
from pymongo.errors import ( # type:ignore[attr-defined]
@ -235,13 +237,12 @@ class AsyncConnection:
await self.close_conn(ConnectionClosedReason.STALE)
def hello_cmd(self) -> dict[str, Any]:
# Handshake spec requires us to use OP_MSG+hello command for the
# initial handshake in load balanced or stable API mode.
# As of PYTHON-5713, always use OP_MSG for the handshake since all
# supported servers (MongoDB 4.2+, wire version >= 8) support it.
self.op_msg_enabled = True
if self.opts.server_api or self.hello_ok or self.opts.load_balanced:
self.op_msg_enabled = True
return {HelloCompat.CMD: 1}
else:
return {HelloCompat.LEGACY_CMD: 1, "helloOk": True}
return {HelloCompat.LEGACY_CMD: 1, "helloOk": True}
async def hello(self) -> Hello[dict[str, Any]]:
return await self._hello(None, None)
@ -291,6 +292,19 @@ class AsyncConnection:
if performing_handshake:
self.connect_rtt = time.monotonic() - start
hello = Hello(doc, awaitable=awaitable)
# OP_MSG requires wire version 6+.
if hello.max_wire_version < 6:
raise ConfigurationError(
"Server at %s:%d reports wire version %d, but this version of "
"PyMongo requires at least %d (MongoDB %s)."
% (
self.address[0],
self.address[1] or 0,
hello.max_wire_version,
MIN_SUPPORTED_WIRE_VERSION,
MIN_SUPPORTED_SERVER_VERSION,
)
)
self.is_writable = hello.is_writable
self.max_wire_version = hello.max_wire_version
self.max_bson_size = hello.max_bson_size

View File

@ -43,6 +43,8 @@ from pymongo.common import (
MAX_MESSAGE_SIZE,
MAX_WIRE_VERSION,
MAX_WRITE_BATCH_SIZE,
MIN_SUPPORTED_SERVER_VERSION,
MIN_SUPPORTED_WIRE_VERSION,
ORDERED_TYPES,
)
from pymongo.errors import ( # type:ignore[attr-defined]
@ -235,13 +237,12 @@ class Connection:
self.close_conn(ConnectionClosedReason.STALE)
def hello_cmd(self) -> dict[str, Any]:
# Handshake spec requires us to use OP_MSG+hello command for the
# initial handshake in load balanced or stable API mode.
# As of PYTHON-5713, always use OP_MSG for the handshake since all
# supported servers (MongoDB 4.2+, wire version >= 8) support it.
self.op_msg_enabled = True
if self.opts.server_api or self.hello_ok or self.opts.load_balanced:
self.op_msg_enabled = True
return {HelloCompat.CMD: 1}
else:
return {HelloCompat.LEGACY_CMD: 1, "helloOk": True}
return {HelloCompat.LEGACY_CMD: 1, "helloOk": True}
def hello(self) -> Hello[dict[str, Any]]:
return self._hello(None, None)
@ -291,6 +292,19 @@ class Connection:
if performing_handshake:
self.connect_rtt = time.monotonic() - start
hello = Hello(doc, awaitable=awaitable)
# OP_MSG requires wire version 6+.
if hello.max_wire_version < 6:
raise ConfigurationError(
"Server at %s:%d reports wire version %d, but this version of "
"PyMongo requires at least %d (MongoDB %s)."
% (
self.address[0],
self.address[1] or 0,
hello.max_wire_version,
MIN_SUPPORTED_WIRE_VERSION,
MIN_SUPPORTED_SERVER_VERSION,
)
)
self.is_writable = hello.is_writable
self.max_wire_version = hello.max_wire_version
self.max_bson_size = hello.max_bson_size

View File

@ -13,12 +13,13 @@
# limitations under the License.
from __future__ import annotations
import re
import unittest
import pytest
try:
from mockupdb import Command, MockupDB, OpMsg, OpMsgReply, OpQuery, OpReply, absent, go
from mockupdb import Command, MockupDB, OpMsg, OpMsgReply, OpReply, absent, go
_HAVE_MOCKUPDB = True
except ImportError:
@ -28,8 +29,8 @@ except ImportError:
from bson.objectid import ObjectId
from pymongo import MongoClient, has_c
from pymongo import version as pymongo_version
from pymongo.common import MIN_SUPPORTED_WIRE_VERSION
from pymongo.errors import OperationFailure
from pymongo.common import MIN_SUPPORTED_SERVER_VERSION, MIN_SUPPORTED_WIRE_VERSION
from pymongo.errors import ConfigurationError, OperationFailure, ServerSelectionTimeoutError
from pymongo.server_api import ServerApi, ServerApiVersion
pytestmark = pytest.mark.mockupdb
@ -53,7 +54,7 @@ def _check_handshake_data(request):
class TestHandshake(unittest.TestCase):
def hello_with_option_helper(self, protocol, **kwargs):
hello = "ismaster" if isinstance(protocol(), OpQuery) else "hello"
hello = "hello" if ("apiVersion" in kwargs or "loadBalanced" in kwargs) else "ismaster"
# `db.command("hello"|"ismaster")` commands are the same for primaries and
# secondaries, so we only need one server.
primary = MockupDB()
@ -165,7 +166,7 @@ class TestHandshake(unittest.TestCase):
future = go(client.db.command, "whatever")
for request in primary:
if request.matches(Command("ismaster")):
if request.matches("ismaster"):
if request.client_port == heartbeat.client_port:
# This is the monitor again, keep going.
request.ok(primary_response)
@ -242,11 +243,10 @@ class TestHandshake(unittest.TestCase):
self.hello_with_option_helper(Command, apiVersion="1")
def test_handshake_not_either(self):
# If we don't specify either option then it should be using
# OP_QUERY for the initial step of the handshake.
self.hello_with_option_helper(Command)
# As of PYTHON-5713, always use OP_MSG for the initial handshake.
self.hello_with_option_helper(OpMsg)
with self.assertRaisesRegex(AssertionError, "does not match"):
self.hello_with_option_helper(OpMsg)
self.hello_with_option_helper(Command)
def test_handshake_max_wire(self):
server = MockupDB()
@ -292,6 +292,26 @@ class TestHandshake(unittest.TestCase):
self.found_auth_msg, "Could not find authentication command with correct protocol"
)
def test_handshake_op_msg_not_supported(self):
# If a server responds with maxWireVersion < 6 (no OP_MSG support),
# the wire version error must surface to the user.
server = MockupDB()
server.autoresponds("ismaster", ok=1, ismaster=True, minWireVersion=0, maxWireVersion=5)
server.run()
self.addCleanup(server.stop)
client = MongoClient(server.uri, serverSelectionTimeoutMS=500)
self.addCleanup(client.close)
# The ConfigurationError from _hello() is stored as the server's error
# and surfaces inside ServerSelectionTimeoutError.
expected = re.escape(
"reports wire version 5, but this version of PyMongo requires at least "
"%d (MongoDB %s)." % (MIN_SUPPORTED_WIRE_VERSION, MIN_SUPPORTED_SERVER_VERSION)
)
with self.assertRaisesRegex(ServerSelectionTimeoutError, expected):
client.db.command("ping")
if __name__ == "__main__":
unittest.main()