Skip to content

PYTHON-4617 Skip unified retryable writes tests on MMAPv1 #1841

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

Merged
merged 5 commits into from
Sep 6, 2024
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
58 changes: 27 additions & 31 deletions test/asynchronous/test_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@
"""Execute Transactions Spec tests."""
from __future__ import annotations

import os
import sys
from io import BytesIO

from gridfs.asynchronous.grid_file import AsyncGridFS, AsyncGridFSBucket

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

from test.asynchronous import async_client_context, unittest
from test.asynchronous.utils_spec_runner import AsyncSpecRunner
from test.asynchronous import AsyncIntegrationTest, async_client_context, unittest
from test.utils import (
OvertCommandListener,
async_rs_client,
Expand Down Expand Up @@ -54,40 +52,14 @@

_IS_SYNC = False

_TXN_TESTS_DEBUG = os.environ.get("TRANSACTION_TESTS_DEBUG")

# Max number of operations to perform after a transaction to prove unpinning
# occurs. Chosen so that there's a low false positive rate. With 2 mongoses,
# 50 attempts yields a one in a quadrillion chance of a false positive
# (1/(0.5^50)).
UNPIN_TEST_MAX_ATTEMPTS = 50


class AsyncTransactionsBase(AsyncSpecRunner):
@classmethod
async def _setup_class(cls):
await super()._setup_class()
if async_client_context.supports_transactions():
for address in async_client_context.mongoses:
cls.mongos_clients.append(await async_single_client("{}:{}".format(*address)))

@classmethod
async def _tearDown_class(cls):
for client in cls.mongos_clients:
await client.close()
await super()._tearDown_class()

def maybe_skip_scenario(self, test):
super().maybe_skip_scenario(test)
if (
"secondary" in self.id()
and not async_client_context.is_mongos
and not async_client_context.has_secondaries
):
raise unittest.SkipTest("No secondaries")


class TestTransactions(AsyncTransactionsBase):
class TestTransactions(AsyncIntegrationTest):
RUN_ON_SERVERLESS = True

@async_client_context.require_transactions
Expand Down Expand Up @@ -421,7 +393,31 @@ def __exit__(self, exc_type, exc_val, exc_tb):
client_session._WITH_TRANSACTION_RETRY_TIME_LIMIT = self.real_timeout


class TestTransactionsConvenientAPI(AsyncTransactionsBase):
class TestTransactionsConvenientAPI(AsyncIntegrationTest):
@classmethod
async def _setup_class(cls):
await super()._setup_class()
cls.mongos_clients = []
if async_client_context.supports_transactions():
for address in async_client_context.mongoses:
cls.mongos_clients.append(await async_single_client("{}:{}".format(*address)))

@classmethod
async def _tearDown_class(cls):
for client in cls.mongos_clients:
await client.close()
await super()._tearDown_class()

async def _set_fail_point(self, client, command_args):
cmd = {"configureFailPoint": "failCommand"}
cmd.update(command_args)
await client.admin.command(cmd)

async def set_fail_point(self, command_args):
clients = self.mongos_clients if self.mongos_clients else [self.client]
for client in clients:
await self._set_fail_point(client, command_args)

@async_client_context.require_transactions
async def test_callback_raises_custom_error(self):
class _MyException(Exception):
Expand Down
55 changes: 0 additions & 55 deletions test/crud_v2_format.py

This file was deleted.

81 changes: 0 additions & 81 deletions test/test_retryable_reads.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import sys
import threading

from bson import SON
from pymongo.errors import AutoReconnect

sys.path[0:0] = [""]
Expand All @@ -34,14 +33,10 @@
)
from test.utils import (
CMAPListener,
EventListener,
OvertCommandListener,
SpecTestCreator,
rs_client,
rs_or_single_client,
set_fail_point,
)
from test.utils_spec_runner import SpecRunner

from pymongo.monitoring import (
ConnectionCheckedOutEvent,
Expand All @@ -50,7 +45,6 @@
PoolClearedEvent,
)
from pymongo.synchronous.mongo_client import MongoClient
from pymongo.write_concern import WriteConcern

# Location of JSON test specifications.
_TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "retryable_reads", "legacy")
Expand All @@ -74,81 +68,6 @@ def test_uri(self):
self.assertEqual(client.options.retry_reads, False)


class TestSpec(SpecRunner):
RUN_ON_LOAD_BALANCER = True
RUN_ON_SERVERLESS = True

@classmethod
@client_context.require_failCommand_fail_point
# TODO: remove this once PYTHON-1948 is done.
@client_context.require_no_mmap
def setUpClass(cls):
super().setUpClass()

def maybe_skip_scenario(self, test):
super().maybe_skip_scenario(test)
skip_names = ["listCollectionObjects", "listIndexNames", "listDatabaseObjects"]
for name in skip_names:
if name.lower() in test["description"].lower():
self.skipTest(f"PyMongo does not support {name}")

# Serverless does not support $out and collation.
if client_context.serverless:
for operation in test["operations"]:
if operation["name"] == "aggregate":
for stage in operation["arguments"]["pipeline"]:
if "$out" in stage:
self.skipTest("MongoDB Serverless does not support $out")
if "collation" in operation["arguments"]:
self.skipTest("MongoDB Serverless does not support collations")

# Skip changeStream related tests on MMAPv1 and serverless.
test_name = self.id().rsplit(".")[-1]
if "changestream" in test_name.lower():
if client_context.storage_engine == "mmapv1":
self.skipTest("MMAPv1 does not support change streams.")
if client_context.serverless:
self.skipTest("Serverless does not support change streams.")

def get_scenario_coll_name(self, scenario_def):
"""Override a test's collection name to support GridFS tests."""
if "bucket_name" in scenario_def:
return scenario_def["bucket_name"]
return super().get_scenario_coll_name(scenario_def)

def setup_scenario(self, scenario_def):
"""Override a test's setup to support GridFS tests."""
if "bucket_name" in scenario_def:
data = scenario_def["data"]
db_name = self.get_scenario_db_name(scenario_def)
db = client_context.client[db_name]
# Create a bucket for the retryable reads GridFS tests with as few
# majority writes as possible.
wc = WriteConcern(w="majority")
if data:
db["fs.chunks"].drop()
db["fs.files"].drop()
db["fs.chunks"].insert_many(data["fs.chunks"])
db.get_collection("fs.files", write_concern=wc).insert_many(data["fs.files"])
else:
db.get_collection("fs.chunks").drop()
db.get_collection("fs.files", write_concern=wc).drop()
else:
super().setup_scenario(scenario_def)


def create_test(scenario_def, test, name):
@client_context.require_test_commands
def run_scenario(self):
self.run_scenario(scenario_def, test)

return run_scenario


test_creator = SpecTestCreator(create_test, TestSpec, _TEST_PATH)
test_creator.create_tests()


class FindThread(threading.Thread):
def __init__(self, collection):
super().__init__()
Expand Down
44 changes: 0 additions & 44 deletions test/test_retryable_writes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from __future__ import annotations

import copy
import os
import pprint
import sys
import threading
Expand All @@ -29,11 +28,9 @@
DeprecationFilter,
EventListener,
OvertCommandListener,
SpecTestCreator,
rs_or_single_client,
set_fail_point,
)
from test.utils_spec_runner import SpecRunner
from test.version import Version

from bson.codec_options import DEFAULT_CODEC_OPTIONS
Expand Down Expand Up @@ -65,9 +62,6 @@
from pymongo.synchronous.mongo_client import MongoClient
from pymongo.write_concern import WriteConcern

# Location of JSON test specifications.
_TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "retryable_writes", "legacy")


class InsertEventListener(EventListener):
def succeeded(self, event: CommandSucceededEvent) -> None:
Expand All @@ -89,44 +83,6 @@ def succeeded(self, event: CommandSucceededEvent) -> None:
)


class TestAllScenarios(SpecRunner):
RUN_ON_LOAD_BALANCER = True
RUN_ON_SERVERLESS = True

def get_object_name(self, op):
return op.get("object", "collection")

def get_scenario_db_name(self, scenario_def):
return scenario_def.get("database_name", "pymongo_test")

def get_scenario_coll_name(self, scenario_def):
return scenario_def.get("collection_name", "test")

def run_test_ops(self, sessions, collection, test):
# Transform retryable writes spec format into transactions.
operation = test["operation"]
outcome = test["outcome"]
if "error" in outcome:
operation["error"] = outcome["error"]
if "result" in outcome:
operation["result"] = outcome["result"]
test["operations"] = [operation]
super().run_test_ops(sessions, collection, test)


def create_test(scenario_def, test, name):
@client_context.require_test_commands
@client_context.require_no_mmap
def run_scenario(self):
self.run_scenario(scenario_def, test)

return run_scenario


test_creator = SpecTestCreator(create_test, TestAllScenarios, _TEST_PATH)
test_creator.create_tests()


def retryable_single_statement_ops(coll):
return [
(coll.bulk_write, [[InsertOne({}), InsertOne({})]], {}),
Expand Down
Loading
Loading