Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .evergreen/resync-specs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ do
apm|APM|command-monitoring|command_monitoring)
cpjson command-logging-and-monitoring/tests/monitoring command_monitoring
;;
causal|causal-consistency|causal_consistency)
cpjson causal-consistency/tests/ causal_consistency
;;
command-logging|command_logging)
cpjson command-logging-and-monitoring/tests/logging command_logging
;;
Expand Down
97 changes: 0 additions & 97 deletions .evergreen/spec-patch/PYTHON-5809.patch

This file was deleted.

8 changes: 7 additions & 1 deletion pymongo/asynchronous/client_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@
WTimeoutError,
)
from pymongo.helpers_shared import _RETRYABLE_ERROR_CODES
from pymongo.operations import _WRITES_WITH_CLUSTER_TIME
from pymongo.read_concern import ReadConcern
from pymongo.read_preferences import ReadPreference, _ServerMode
from pymongo.server_type import SERVER_TYPE
Expand Down Expand Up @@ -1111,7 +1112,12 @@ def _apply_to(
return
self._check_ended()
self._materialize(conn.logical_session_timeout_minutes)
if self.options.snapshot:
# Add afterClusterTime on snapshot reads or writes in causally-consistent sessions
if self.options.snapshot or (
self.options.causal_consistency
and not self.in_transaction
and operation in _WRITES_WITH_CLUSTER_TIME
):
self._update_read_concern(command, conn)

self._server_session.last_use = time.monotonic()
Expand Down
16 changes: 16 additions & 0 deletions pymongo/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ class _Op(str, enum.Enum):
TEST = "testOperation"


_WRITES_WITH_CLUSTER_TIME = frozenset(
{
_Op.INSERT.value,
_Op.UPDATE.value,
_Op.FIND_AND_MODIFY.value,
_Op.DELETE.value,
_Op.BULK_WRITE.value,
_Op.CREATE.value,
_Op.CREATE_INDEXES.value,
_Op.DROP.value,
_Op.DROP_DATABASE.value,
_Op.DROP_INDEXES.value,
}
)


class InsertOne(Generic[_DocumentType]):
"""Represents an insert_one operation."""

Expand Down
8 changes: 7 additions & 1 deletion pymongo/synchronous/client_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
WTimeoutError,
)
from pymongo.helpers_shared import _RETRYABLE_ERROR_CODES
from pymongo.operations import _WRITES_WITH_CLUSTER_TIME
from pymongo.read_concern import ReadConcern
from pymongo.read_preferences import ReadPreference, _ServerMode
from pymongo.server_type import SERVER_TYPE
Expand Down Expand Up @@ -1107,7 +1108,12 @@ def _apply_to(
return
self._check_ended()
self._materialize(conn.logical_session_timeout_minutes)
if self.options.snapshot:
# Add afterClusterTime on snapshot reads or writes in causally-consistent sessions
if self.options.snapshot or (
self.options.causal_consistency
and not self.in_transaction
and operation in _WRITES_WITH_CLUSTER_TIME
):
self._update_read_concern(command, conn)

self._server_session.last_use = time.monotonic()
Expand Down
33 changes: 33 additions & 0 deletions test/asynchronous/test_causal_consistency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2021-present MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Test the causal consistency unified spec tests."""
from __future__ import annotations

import os
import pathlib
import sys

sys.path[0:0] = [""]

from test import unittest
from test.asynchronous.unified_format import generate_test_classes, get_test_path

_IS_SYNC = False

# Generate unified tests.
globals().update(generate_test_classes(get_test_path("causal_consistency"), module=__name__))

if __name__ == "__main__":
unittest.main()
47 changes: 1 addition & 46 deletions test/asynchronous/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -1138,52 +1138,7 @@ async def _test_no_read_concern(self, op):
self.assertIsNone(rc)

@async_client_context.require_no_standalone
async def test_writes_do_not_include_read_concern(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these tests removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a result of these changes, causally-consistent writes (the default for an explicit session) now include an empty read concern to communicate afterClusterTime.

await self._test_no_read_concern(
lambda coll, session: coll.bulk_write([InsertOne[dict]({})], session=session)
)
await self._test_no_read_concern(lambda coll, session: coll.insert_one({}, session=session))
await self._test_no_read_concern(
lambda coll, session: coll.insert_many([{}], session=session)
)
await self._test_no_read_concern(
lambda coll, session: coll.replace_one({"_id": 1}, {"x": 1}, session=session)
)
await self._test_no_read_concern(
lambda coll, session: coll.update_one({}, {"$set": {"X": 1}}, session=session)
)
await self._test_no_read_concern(
lambda coll, session: coll.update_many({}, {"$set": {"x": 1}}, session=session)
)
await self._test_no_read_concern(lambda coll, session: coll.delete_one({}, session=session))
await self._test_no_read_concern(
lambda coll, session: coll.delete_many({}, session=session)
)
await self._test_no_read_concern(
lambda coll, session: coll.find_one_and_replace({"x": 1}, {"y": 1}, session=session)
)
await self._test_no_read_concern(
lambda coll, session: coll.find_one_and_update(
{"y": 1}, {"$set": {"x": 1}}, session=session
)
)
await self._test_no_read_concern(
lambda coll, session: coll.find_one_and_delete({"x": 1}, session=session)
)
await self._test_no_read_concern(
lambda coll, session: coll.create_index("foo", session=session)
)
await self._test_no_read_concern(
lambda coll, session: coll.create_indexes(
[IndexModel([("bar", ASCENDING)])], session=session
)
)
await self._test_no_read_concern(
lambda coll, session: coll.drop_index("foo_1", session=session)
)
await self._test_no_read_concern(lambda coll, session: coll.drop_indexes(session=session))

# Not a write, but explain also doesn't support readConcern.
async def test_explain_does_not_include_read_concern(self):
await self._test_no_read_concern(
lambda coll, session: coll.find({}, session=session).explain()
)
Expand Down
6 changes: 6 additions & 0 deletions test/asynchronous/unified_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,12 @@ async def _databaseOperation_createChangeStream(self, target, *args, **kwargs):
async def _collectionOperation_createChangeStream(self, target, *args, **kwargs):
return await self.__entityOperation_createChangeStream(target, *args, **kwargs)

async def _clientOperation_dropDatabase(self, target, **kwargs):
self.__raise_if_unsupported("dropDatabase", target, AsyncMongoClient)
return await target.drop_database(
name_or_database=kwargs.pop("database"), session=kwargs.pop("session", None)
)

async def _databaseOperation_runCommand(self, target, **kwargs):
self.__raise_if_unsupported("runCommand", target, AsyncDatabase)
# Ensure the first key is the command name.
Expand Down
Loading
Loading