Skip to content

Commit beaf117

Browse files
committed
Fix wtimeout tests for MongoDB 2.8.
1 parent c2d4ca1 commit beaf117

File tree

2 files changed

+62
-21
lines changed

2 files changed

+62
-21
lines changed

test/test_bulk.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@
2121
from bson import InvalidDocument, SON
2222
from bson.py3compat import string_type
2323
from pymongo import MongoClient
24+
from pymongo.common import partition_node
2425
from pymongo.errors import BulkWriteError, InvalidOperation, OperationFailure
25-
from test import client_context, unittest, host, port, IntegrationTest
26+
from test import (client_context,
27+
unittest,
28+
host,
29+
port,
30+
IntegrationTest,
31+
SkipTest)
2632
from test.utils import oid_generated_on_client, remove_all_users
2733

2834

@@ -909,6 +915,36 @@ class TestBulkWriteConcern(BulkTestBase):
909915
def setUpClass(cls):
910916
super(TestBulkWriteConcern, cls).setUpClass()
911917
cls.w = client_context.w
918+
cls.secondary = None
919+
if cls.w > 1:
920+
for member in client_context.ismaster['hosts']:
921+
if member != client_context.ismaster['primary']:
922+
cls.secondary = MongoClient(*partition_node(member))
923+
break
924+
925+
# We tested wtimeout errors by specifying a write concern greater than
926+
# the number of members, but in MongoDB 2.7.8+ this causes a different
927+
# sort of error, "Not enough data-bearing nodes". In recent servers we
928+
# use a failpoint to pause replication on a secondary.
929+
cls.need_replication_stopped = client_context.version.at_least(2, 7, 8)
930+
931+
def cause_wtimeout(self, batch):
932+
if self.need_replication_stopped:
933+
if not client_context.test_commands_enabled:
934+
raise SkipTest("Test commands must be enabled.")
935+
936+
self.secondary.admin.command('configureFailPoint',
937+
'rsSyncApplyStop',
938+
mode='alwaysOn')
939+
940+
try:
941+
return batch.execute({'w': self.w, 'wtimeout': 1})
942+
finally:
943+
self.secondary.admin.command('configureFailPoint',
944+
'rsSyncApplyStop',
945+
mode='off')
946+
else:
947+
return batch.execute({'w': self.w + 1, 'wtimeout': 1})
912948

913949
@client_context.require_version_min(1, 8, 2)
914950
def test_fsync_and_j(self):
@@ -932,7 +968,7 @@ def test_write_concern_failure_ordered(self):
932968
# Replication wtimeout is a 'soft' error.
933969
# It shouldn't stop batch processing.
934970
try:
935-
batch.execute({'w': self.w + 1, 'wtimeout': 1})
971+
self.cause_wtimeout(batch)
936972
except BulkWriteError as exc:
937973
result = exc.details
938974
self.assertEqual(exc.code, 65)
@@ -969,7 +1005,7 @@ def test_write_concern_failure_ordered(self):
9691005
batch.insert({'a': 1})
9701006
batch.insert({'a': 2})
9711007
try:
972-
batch.execute({'w': self.w + 1, 'wtimeout': 1})
1008+
self.cause_wtimeout(batch)
9731009
except BulkWriteError as exc:
9741010
result = exc.details
9751011
self.assertEqual(exc.code, 65)
@@ -1011,7 +1047,7 @@ def test_write_concern_failure_unordered(self):
10111047
# Replication wtimeout is a 'soft' error.
10121048
# It shouldn't stop batch processing.
10131049
try:
1014-
batch.execute({'w': self.w + 1, 'wtimeout': 1})
1050+
self.cause_wtimeout(batch)
10151051
except BulkWriteError as exc:
10161052
result = exc.details
10171053
self.assertEqual(exc.code, 65)
@@ -1038,7 +1074,7 @@ def test_write_concern_failure_unordered(self):
10381074
batch.insert({'a': 1})
10391075
batch.insert({'a': 2})
10401076
try:
1041-
batch.execute({'w': self.w + 1, 'wtimeout': 1})
1077+
self.cause_wtimeout(batch)
10421078
except BulkWriteError as exc:
10431079
result = exc.details
10441080
self.assertEqual(exc.code, 65)

test/test_collection.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,23 +1281,28 @@ def test_last_error_options(self):
12811281
if client_context.setname:
12821282
# client_context.w is the number of hosts in the replica set
12831283
w = client_context.w + 1
1284-
self.assertRaises(WTimeoutError, self.db.test.save,
1285-
{"x": 1}, w=w, wtimeout=1)
1286-
self.assertRaises(WTimeoutError, self.db.test.insert,
1287-
{"x": 1}, w=w, wtimeout=1)
1288-
self.assertRaises(WTimeoutError, self.db.test.update,
1289-
{"x": 1}, {"y": 2}, w=w, wtimeout=1)
1290-
self.assertRaises(WTimeoutError, self.db.test.remove,
1291-
{"x": 1}, w=w, wtimeout=1)
12921284

1293-
try:
1294-
self.db.test.save({"x": 1}, w=w, wtimeout=1)
1295-
except WTimeoutError as exc:
1296-
# Just check that we set the error document. Fields
1297-
# vary by MongoDB version.
1298-
self.assertTrue(exc.details is not None)
1299-
else:
1300-
self.fail("WTimeoutError was not raised")
1285+
# MongoDB 2.8+ raises error code 100, CannotSatisfyWriteConcern,
1286+
# if w > number of members. Older versions just time out after 1 ms
1287+
# as if they had enough secondaries but some are lagging. They
1288+
# return an error with 'wtimeout': True and no code.
1289+
def wtimeout_err(f, *args, **kwargs):
1290+
try:
1291+
f(*args, **kwargs)
1292+
except WTimeoutError as exc:
1293+
self.assertIsNotNone(exc.details)
1294+
except OperationFailure as exc:
1295+
self.assertIsNotNone(exc.details)
1296+
self.assertEqual(100, exc.code,
1297+
"Unexpected error: %r" % exc)
1298+
else:
1299+
self.fail("%s should have failed" % f)
1300+
1301+
coll = self.db.test
1302+
wtimeout_err(coll.save, {"x": 1}, w=w, wtimeout=1)
1303+
wtimeout_err(coll.insert, {"x": 1}, w=w, wtimeout=1)
1304+
wtimeout_err(coll.update, {"x": 1}, {"y": 2}, w=w, wtimeout=1)
1305+
wtimeout_err(coll.remove, {"x": 1}, w=w, wtimeout=1)
13011306

13021307
# can't use fsync and j options together
13031308
if client_context.version.at_least(1, 8, 2):

0 commit comments

Comments
 (0)