prototype transaction tests
This commit is contained in:
parent
04de1697cb
commit
9df87b6401
@ -260,15 +260,13 @@ class _Bulk(object):
|
||||
cmd['writeConcern'] = write_concern.document
|
||||
if self.bypass_doc_val and sock_info.max_wire_version >= 4:
|
||||
cmd['bypassDocumentValidation'] = True
|
||||
if session:
|
||||
cmd['lsid'] = session._use_lsid()
|
||||
bwc = _BulkWriteContext(db_name, cmd, sock_info, op_id,
|
||||
listeners, session)
|
||||
|
||||
results = []
|
||||
while run.idx_offset < len(run.ops):
|
||||
if session and retryable:
|
||||
cmd['txnNumber'] = session._transaction_id()
|
||||
if session:
|
||||
session._apply_to(cmd, retryable)
|
||||
sock_info.send_cluster_time(cmd, session, client)
|
||||
check_keys = run.op_type == _INSERT
|
||||
ops = islice(run.ops, run.idx_offset, None)
|
||||
@ -278,6 +276,8 @@ class _Bulk(object):
|
||||
self.collection.codec_options, bwc)
|
||||
if not to_send:
|
||||
raise InvalidOperation("cannot do an empty bulk write")
|
||||
if session:
|
||||
session._advance_statement_id(len(to_send))
|
||||
result = bwc.write_command(request_id, msg, to_send)
|
||||
client._receive_cluster_time(result, session)
|
||||
results.append((run.idx_offset, result))
|
||||
|
||||
@ -61,15 +61,51 @@ class SessionOptions(object):
|
||||
:Parameters:
|
||||
- `causal_consistency` (optional): If True (the default), read
|
||||
operations are causally ordered within the session.
|
||||
- `auto_start_transaction` (optional): If True, any operation using
|
||||
the session begins a transaction if none is in progress.
|
||||
"""
|
||||
def __init__(self, causal_consistency=True):
|
||||
# TODO: accept a TransactionOptions.
|
||||
def __init__(self,
|
||||
causal_consistency=True,
|
||||
auto_start_transaction=False):
|
||||
self._causal_consistency = causal_consistency
|
||||
self._auto_start_transaction = auto_start_transaction
|
||||
|
||||
@property
|
||||
def causal_consistency(self):
|
||||
"""Whether causal consistency is configured."""
|
||||
return self._causal_consistency
|
||||
|
||||
@property
|
||||
def auto_start_transaction(self):
|
||||
"""Whether the session is configured to always start a transaction."""
|
||||
return self._auto_start_transaction
|
||||
|
||||
|
||||
class TransactionOptions(object):
|
||||
"""Options for :meth:`ClientSession.start_transaction`.
|
||||
|
||||
:Parameters:
|
||||
- `read_concern`: The :class:`~read_concern.ReadConcern` to use for this
|
||||
transaction.
|
||||
- `write_concern`: The :class:`~write_concern.WriteConcern` to use for
|
||||
this transaction.
|
||||
"""
|
||||
def __init__(self, read_concern=None, write_concern=None):
|
||||
# TODO: validate arguments.
|
||||
self._read_concern = read_concern
|
||||
self._write_concern = write_concern
|
||||
|
||||
@property
|
||||
def read_concern(self):
|
||||
"""This transaction's :class:`~read_concern.ReadConcern`."""
|
||||
return self._read_concern
|
||||
|
||||
@property
|
||||
def write_concern(self):
|
||||
"""This transaction's :class:`~write_concern.WriteConcern`."""
|
||||
return self._write_concern
|
||||
|
||||
|
||||
class ClientSession(object):
|
||||
"""A session for ordering sequential operations."""
|
||||
@ -81,27 +117,41 @@ class ClientSession(object):
|
||||
self._authset = authset
|
||||
self._cluster_time = None
|
||||
self._operation_time = None
|
||||
self._transaction_options = None # Current transaction's options.
|
||||
if self.options.auto_start_transaction:
|
||||
# TODO: Get transaction options from self.options.
|
||||
self._transaction_options = TransactionOptions()
|
||||
self._server_session.start_transaction()
|
||||
|
||||
def end_session(self):
|
||||
"""Finish this session.
|
||||
"""Finish this session. If a transaction has started, abort it.
|
||||
|
||||
It is an error to use the session or any derived
|
||||
:class:`~pymongo.database.Database`,
|
||||
:class:`~pymongo.collection.Collection`, or
|
||||
:class:`~pymongo.cursor.Cursor` after the session has ended.
|
||||
"""
|
||||
self._end_session(True)
|
||||
self._end_session(lock=True, abort_txn=True)
|
||||
|
||||
def _end_session(self, lock):
|
||||
def _end_session(self, lock, abort_txn):
|
||||
if self._server_session is not None:
|
||||
self._client._return_server_session(self._server_session, lock)
|
||||
self._server_session = None
|
||||
try:
|
||||
if self.in_transaction:
|
||||
if abort_txn:
|
||||
self.abort_txn()
|
||||
else:
|
||||
self.commit_transaction()
|
||||
finally:
|
||||
self._client._return_server_session(self._server_session, lock)
|
||||
self._server_session = None
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.end_session()
|
||||
# Abort when exiting with an exception, otherwise commit.
|
||||
# TODO: test and document this.
|
||||
self._end_session(lock=True, abort_txn=exc_val is not None)
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
@ -137,6 +187,45 @@ class ClientSession(object):
|
||||
"""
|
||||
return self._operation_time
|
||||
|
||||
def start_transaction(self, **kwargs):
|
||||
"""Start a multi-statement transaction.
|
||||
|
||||
Takes the same arguments as :class:`TransactionOptions`.
|
||||
|
||||
Do not use this method if the session is configured to automatically
|
||||
start a transaction.
|
||||
"""
|
||||
self._transaction_options = TransactionOptions(**kwargs)
|
||||
self._server_session.start_transaction()
|
||||
|
||||
def commit_transaction(self):
|
||||
"""Commit a multi-statement transaction."""
|
||||
self._finish_transaction("commitTransaction")
|
||||
|
||||
def abort_txn(self):
|
||||
"""Abort a multi-statement transaction."""
|
||||
assert False, "Not implemented" # Await server.
|
||||
self._finish_transaction("abortTransaction")
|
||||
|
||||
def _finish_transaction(self, command_name):
|
||||
if (self.options.auto_start_transaction
|
||||
and self._server_session.statement_id == 0):
|
||||
# Not really started.
|
||||
return
|
||||
|
||||
try:
|
||||
# TODO: retryable. And it's weird to pass parse_write_concern_error
|
||||
# from outside database.py.
|
||||
self._client.admin.command(
|
||||
command_name,
|
||||
txnNumber=self._server_session.transaction_id,
|
||||
session=self,
|
||||
write_concern=self._transaction_options.write_concern,
|
||||
parse_write_concern_error=True)
|
||||
finally:
|
||||
self._server_session.reset_transaction()
|
||||
self._transaction_options = None
|
||||
|
||||
def _advance_cluster_time(self, cluster_time):
|
||||
"""Internal cluster time helper."""
|
||||
if self._cluster_time is None:
|
||||
@ -186,19 +275,28 @@ class ClientSession(object):
|
||||
"""True if this session is finished."""
|
||||
return self._server_session is None
|
||||
|
||||
def _use_lsid(self):
|
||||
@property
|
||||
def in_transaction(self):
|
||||
"""True if this session has an active multi-statement transaction."""
|
||||
return (self._server_session is not None
|
||||
and self._server_session.in_transaction)
|
||||
|
||||
def _apply_to(self, command, is_retryable):
|
||||
# Internal function.
|
||||
if self._server_session is None:
|
||||
raise InvalidOperation("Cannot use ended session")
|
||||
|
||||
return self._server_session.use_lsid()
|
||||
if self.options.auto_start_transaction and not self.in_transaction:
|
||||
self.start_transaction()
|
||||
|
||||
def _transaction_id(self):
|
||||
self._server_session.apply_to(command, is_retryable)
|
||||
|
||||
def _advance_statement_id(self, n):
|
||||
# Internal function.
|
||||
if self._server_session is None:
|
||||
raise InvalidOperation("Cannot use ended session")
|
||||
|
||||
return self._server_session.transaction_id()
|
||||
self._server_session.advance_statement_id(n)
|
||||
|
||||
def _retry_transaction_id(self):
|
||||
# Internal function.
|
||||
@ -213,7 +311,9 @@ class _ServerSession(object):
|
||||
# Ensure id is type 4, regardless of CodecOptions.uuid_representation.
|
||||
self.session_id = {'id': Binary(uuid.uuid4().bytes, 4)}
|
||||
self.last_use = monotonic.time()
|
||||
self.in_transaction = False
|
||||
self._transaction_id = 0
|
||||
self.statement_id = 0
|
||||
|
||||
def timed_out(self, session_timeout_minutes):
|
||||
idle_seconds = monotonic.time() - self.last_use
|
||||
@ -221,15 +321,43 @@ class _ServerSession(object):
|
||||
# Timed out if we have less than a minute to live.
|
||||
return idle_seconds > (session_timeout_minutes - 1) * 60
|
||||
|
||||
def use_lsid(self):
|
||||
self.last_use = monotonic.time()
|
||||
return self.session_id
|
||||
def apply_to(self, command, is_retryable):
|
||||
command['lsid'] = self.session_id
|
||||
|
||||
if is_retryable:
|
||||
self._transaction_id += 1
|
||||
command['txnNumber'] = self.transaction_id
|
||||
elif self.in_transaction:
|
||||
command['txnNumber'] = self.transaction_id
|
||||
# TODO: Allow stmtId for find/getMore, SERVER-33213.
|
||||
name = next(iter(command))
|
||||
if name not in ('find', 'getMore'):
|
||||
command['stmtId'] = self.statement_id
|
||||
if self.statement_id == 0:
|
||||
command['readConcern'] = {'level': 'snapshot'}
|
||||
command['autocommit'] = False
|
||||
self.statement_id += 1
|
||||
|
||||
self.last_use = monotonic.time()
|
||||
|
||||
def advance_statement_id(self, n):
|
||||
# Every command advances the statement id by 1 already.
|
||||
self.statement_id += (n - 1)
|
||||
|
||||
@property
|
||||
def transaction_id(self):
|
||||
"""Monotonically increasing positive 64-bit integer."""
|
||||
self._transaction_id += 1
|
||||
"""Positive 64-bit integer."""
|
||||
return Int64(self._transaction_id)
|
||||
|
||||
def start_transaction(self):
|
||||
self._transaction_id += 1
|
||||
self.statement_id = 0
|
||||
self.in_transaction = True
|
||||
|
||||
def reset_transaction(self):
|
||||
self.in_transaction = False
|
||||
self.statement_id = 0
|
||||
|
||||
def retry_transaction_id(self):
|
||||
self._transaction_id -= 1
|
||||
|
||||
|
||||
@ -87,7 +87,7 @@ class CommandCursor(object):
|
||||
|
||||
def __end_session(self, synchronous):
|
||||
if self.__session and not self.__explicit_session:
|
||||
self.__session._end_session(lock=synchronous)
|
||||
self.__session._end_session(lock=synchronous, abort_txn=False)
|
||||
self.__session = None
|
||||
|
||||
def close(self):
|
||||
|
||||
@ -316,7 +316,7 @@ class Cursor(object):
|
||||
if self.__exhaust and self.__exhaust_mgr:
|
||||
self.__exhaust_mgr.close()
|
||||
if self.__session and not self.__explicit_session:
|
||||
self.__session._end_session(lock=synchronous)
|
||||
self.__session._end_session(lock=synchronous, abort_txn=False)
|
||||
self.__session = None
|
||||
|
||||
def close(self):
|
||||
|
||||
@ -279,10 +279,11 @@ class _Query(object):
|
||||
cmd = SON([('explain', cmd)])
|
||||
session = self.session
|
||||
if session:
|
||||
cmd['lsid'] = session._use_lsid()
|
||||
session._apply_to(cmd, False)
|
||||
# Explain does not support readConcern.
|
||||
if (not explain and session.options.causal_consistency
|
||||
and session.operation_time is not None):
|
||||
and session.operation_time is not None
|
||||
and not session.in_transaction):
|
||||
cmd.setdefault(
|
||||
'readConcern', {})[
|
||||
'afterClusterTime'] = session.operation_time
|
||||
@ -353,7 +354,7 @@ class _GetMore(object):
|
||||
self.max_await_time_ms)
|
||||
|
||||
if self.session:
|
||||
cmd['lsid'] = self.session._use_lsid()
|
||||
self.session._apply_to(cmd, False)
|
||||
sock_info.send_cluster_time(cmd, self.session, self.client)
|
||||
return cmd, self.db
|
||||
|
||||
@ -653,9 +654,6 @@ class _BulkWriteContext(object):
|
||||
def write_command(self, request_id, msg, docs):
|
||||
"""A proxy for SocketInfo.write_command that handles event publishing.
|
||||
"""
|
||||
if self.session:
|
||||
# Update last_use time.
|
||||
self.session._use_lsid()
|
||||
if self.publish:
|
||||
duration = datetime.datetime.now() - self.start_time
|
||||
self._start(request_id, docs)
|
||||
|
||||
@ -1344,7 +1344,9 @@ class MongoClient(common.BaseObject):
|
||||
except Exception:
|
||||
helpers._handle_exception()
|
||||
|
||||
def start_session(self, causal_consistency=True):
|
||||
def start_session(self,
|
||||
causal_consistency=True,
|
||||
auto_start_transaction=False):
|
||||
"""Start a logical session.
|
||||
|
||||
This method takes the same parameters as
|
||||
@ -1374,7 +1376,8 @@ class MongoClient(common.BaseObject):
|
||||
# Raises ConfigurationError if sessions are not supported.
|
||||
server_session = self._get_server_session()
|
||||
opts = client_session.SessionOptions(
|
||||
causal_consistency=causal_consistency)
|
||||
causal_consistency=causal_consistency,
|
||||
auto_start_transaction=auto_start_transaction)
|
||||
return client_session.ClientSession(
|
||||
self, server_session, opts, authset)
|
||||
|
||||
|
||||
@ -88,7 +88,8 @@ def command(sock, dbname, spec, slave_ok, is_mongos,
|
||||
if read_concern.level:
|
||||
spec['readConcern'] = read_concern.document
|
||||
if (session and session.options.causal_consistency
|
||||
and session.operation_time is not None):
|
||||
and session.operation_time is not None
|
||||
and not session.in_transaction):
|
||||
spec.setdefault(
|
||||
'readConcern', {})['afterClusterTime'] = session.operation_time
|
||||
if collation is not None:
|
||||
|
||||
@ -502,9 +502,7 @@ class SocketInfo(object):
|
||||
# Ensure command name remains in first place.
|
||||
spec = SON(spec)
|
||||
if session:
|
||||
spec['lsid'] = session._use_lsid()
|
||||
if retryable_write:
|
||||
spec['txnNumber'] = session._transaction_id()
|
||||
session._apply_to(spec, retryable_write)
|
||||
self.send_cluster_time(spec, session, client)
|
||||
listeners = self.listeners if publish_events else None
|
||||
try:
|
||||
|
||||
358
test/test_transactions.py
Normal file
358
test/test_transactions.py
Normal file
@ -0,0 +1,358 @@
|
||||
# Copyright 2018-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.
|
||||
|
||||
"""Execute Transactions Spec tests."""
|
||||
|
||||
import collections
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from bson import json_util, py3compat
|
||||
from pymongo.errors import OperationFailure
|
||||
from pymongo.read_concern import ReadConcern
|
||||
from pymongo.read_preferences import (make_read_preference,
|
||||
read_pref_mode_from_name)
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.py3compat import iteritems
|
||||
from pymongo import operations, WriteConcern
|
||||
from pymongo.command_cursor import CommandCursor
|
||||
from pymongo.cursor import Cursor
|
||||
from pymongo.results import _WriteResult, BulkWriteResult
|
||||
|
||||
from test import unittest, client_context, IntegrationTest
|
||||
from test.utils import EventListener, rs_client
|
||||
|
||||
# Location of JSON test specifications.
|
||||
_TEST_PATH = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), 'transactions')
|
||||
|
||||
_TXN_TESTS_DEBUG = os.environ.get('TRANSACTION_TESTS_DEBUG')
|
||||
|
||||
|
||||
# TODO: factor the following functions with test_crud.py.
|
||||
def camel_to_snake(camel):
|
||||
# Regex to convert CamelCase to snake_case.
|
||||
snake = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel)
|
||||
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', snake).lower()
|
||||
|
||||
|
||||
def camel_to_upper_camel(camel):
|
||||
return camel[0].upper() + camel[1:]
|
||||
|
||||
|
||||
def camel_to_snake_args(arguments):
|
||||
for arg_name in list(arguments):
|
||||
c2s = camel_to_snake(arg_name)
|
||||
arguments[c2s] = arguments.pop(arg_name)
|
||||
return arguments
|
||||
|
||||
|
||||
class TestTransactions(IntegrationTest):
|
||||
def transaction_test_debug(self, msg):
|
||||
if _TXN_TESTS_DEBUG:
|
||||
print(msg)
|
||||
|
||||
# TODO: factor the following function with test_crud.py.
|
||||
def check_result(self, expected_result, result):
|
||||
if isinstance(result, Cursor) or isinstance(result, CommandCursor):
|
||||
self.assertEqual(list(result), expected_result)
|
||||
|
||||
elif isinstance(result, _WriteResult):
|
||||
for res in expected_result:
|
||||
prop = camel_to_snake(res)
|
||||
# SPEC-869: Only BulkWriteResult has upserted_count.
|
||||
if (prop == "upserted_count" and
|
||||
not isinstance(result, BulkWriteResult)):
|
||||
if result.upserted_id is not None:
|
||||
upserted_count = 1
|
||||
else:
|
||||
upserted_count = 0
|
||||
self.assertEqual(upserted_count, expected_result[res])
|
||||
elif prop == "inserted_ids":
|
||||
# BulkWriteResult does not have inserted_ids.
|
||||
if isinstance(result, BulkWriteResult):
|
||||
self.assertEqual(len(expected_result[res]), result.inserted_count)
|
||||
else:
|
||||
# InsertManyResult may be compared to [id1] from the
|
||||
# crud spec or {"0": id1} from the retryable write spec.
|
||||
ids = expected_result[res]
|
||||
if isinstance(ids, dict):
|
||||
ids = [ids[str(i)] for i in range(len(ids))]
|
||||
self.assertEqual(ids, result.inserted_ids)
|
||||
elif prop == "upserted_ids":
|
||||
# Convert indexes from strings to integers.
|
||||
ids = expected_result[res]
|
||||
expected_ids = {}
|
||||
for str_index in ids:
|
||||
expected_ids[int(str_index)] = ids[str_index]
|
||||
self.assertEqual(expected_ids != result.upserted_ids)
|
||||
self.assertEqual(getattr(result, prop), expected_result[res])
|
||||
return True
|
||||
else:
|
||||
self.assertEqual(result, expected_result)
|
||||
|
||||
def run_operation(self, sessions, collection, operation):
|
||||
session = None
|
||||
name = camel_to_snake(operation['name'])
|
||||
self.transaction_test_debug(name)
|
||||
session_name = operation['arguments'].pop('session', None)
|
||||
if session_name:
|
||||
session = sessions[session_name]
|
||||
|
||||
# Convert arguments to snake_case and handle special cases.
|
||||
arguments = operation['arguments']
|
||||
options = arguments.pop("options", {})
|
||||
for option_name in options:
|
||||
arguments[camel_to_snake(option_name)] = options[option_name]
|
||||
|
||||
pref = arguments.pop('readPreference', None)
|
||||
if pref:
|
||||
mode = read_pref_mode_from_name(pref['mode'])
|
||||
collection = collection.with_options(
|
||||
read_preference=make_read_preference(mode, None))
|
||||
|
||||
if name.endswith('_transaction'):
|
||||
cmd = getattr(session, name)
|
||||
else:
|
||||
cmd = getattr(collection, name)
|
||||
arguments['session'] = session
|
||||
|
||||
if operation == "bulk_write":
|
||||
# Parse each request into a bulk write model.
|
||||
requests = []
|
||||
for request in arguments["requests"]:
|
||||
bulk_model = camel_to_upper_camel(request["name"])
|
||||
bulk_class = getattr(operations, bulk_model)
|
||||
bulk_arguments = camel_to_snake_args(request["arguments"])
|
||||
requests.append(bulk_class(**bulk_arguments))
|
||||
arguments["requests"] = requests
|
||||
else:
|
||||
for arg_name in list(arguments):
|
||||
c2s = camel_to_snake(arg_name)
|
||||
# PyMongo accepts sort as list of tuples. Asserting len=1
|
||||
# because ordering dicts from JSON in 2.6 is unwieldy.
|
||||
if arg_name == "sort":
|
||||
sort_dict = arguments[arg_name]
|
||||
assert len(sort_dict) == 1, 'test can only have 1 sort key'
|
||||
arguments[arg_name] = list(iteritems(sort_dict))
|
||||
# Named "key" instead not fieldName.
|
||||
if arg_name == "fieldName":
|
||||
arguments["key"] = arguments.pop(arg_name)
|
||||
# Aggregate uses "batchSize", while find uses batch_size.
|
||||
elif arg_name == "batchSize" and operation == "aggregate":
|
||||
continue
|
||||
# Requires boolean returnDocument.
|
||||
elif arg_name == "returnDocument":
|
||||
arguments[c2s] = arguments[arg_name] == "After"
|
||||
elif arg_name == "readConcern":
|
||||
arguments[c2s] = ReadConcern(**arguments.pop(arg_name))
|
||||
elif arg_name == "writeConcern":
|
||||
arguments[c2s] = WriteConcern(**arguments.pop(arg_name))
|
||||
else:
|
||||
arguments[c2s] = arguments.pop(arg_name)
|
||||
|
||||
result = cmd(**arguments)
|
||||
|
||||
if operation == "aggregate":
|
||||
if arguments["pipeline"] and "$out" in arguments["pipeline"][-1]:
|
||||
out = collection.database[arguments["pipeline"][-1]["$out"]]
|
||||
return out.find()
|
||||
|
||||
return result
|
||||
|
||||
# TODO: factor with test_command_monitoring.py
|
||||
def check_events(self, test, listener, sessions):
|
||||
res = listener.results
|
||||
if not len(test['expectations']):
|
||||
return
|
||||
|
||||
self.assertEqual(len(res['started']), len(test['expectations']))
|
||||
for i, expectation in enumerate(test['expectations']):
|
||||
event_type = next(iter(expectation))
|
||||
event = res['started'][i]
|
||||
|
||||
# The tests substitute 42 for any number other than 0.
|
||||
if (event.command_name == 'getMore'
|
||||
and event.command['getMore']):
|
||||
event.command['getMore'] = 42
|
||||
elif event.command_name == 'killCursors':
|
||||
event.command['cursors'] = [42]
|
||||
|
||||
# Replace lsid with a name like "session0" to match test.
|
||||
if 'lsid' in event.command:
|
||||
for name, session in sessions.items():
|
||||
if event.command['lsid'] == session.session_id:
|
||||
event.command['lsid'] = name
|
||||
break
|
||||
|
||||
# TODO: Allow stmtId for find/getMore, SERVER-33213.
|
||||
if event.command_name in ('find', 'getMore'):
|
||||
expectation[event_type]['command'].pop('stmtId', None)
|
||||
|
||||
for attr, expected in expectation[event_type].items():
|
||||
actual = getattr(event, attr)
|
||||
if isinstance(expected, dict):
|
||||
for key, val in expected.items():
|
||||
if val is None:
|
||||
if key in actual:
|
||||
self.fail("Unexpected key [%s] in %r" % (
|
||||
key, actual))
|
||||
elif key not in actual:
|
||||
self.fail("Expected key [%s] in %r" % (
|
||||
key, actual))
|
||||
else:
|
||||
self.assertEqual(val, actual[key],
|
||||
"Key [%s] in %s" % (key, actual))
|
||||
else:
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
||||
def expect_error(expected_result):
|
||||
if isinstance(expected_result, dict):
|
||||
return expected_result['errorContains']
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def end_sessions(sessions):
|
||||
for s in sessions.values():
|
||||
try:
|
||||
s.commit_transaction()
|
||||
except Exception:
|
||||
# Ignore errors from committing without an open transaction.
|
||||
pass
|
||||
|
||||
for s in sessions.values():
|
||||
s.end_session()
|
||||
|
||||
|
||||
def create_test(scenario_def, test):
|
||||
def run_scenario(self):
|
||||
listener = EventListener()
|
||||
# New client to avoid interference from pooled sessions.
|
||||
client = rs_client(event_listeners=[listener])
|
||||
write_concern_db = client.get_database(
|
||||
'transaction-tests', write_concern=WriteConcern(w='majority'))
|
||||
|
||||
write_concern_db.test.drop()
|
||||
write_concern_db.create_collection('test')
|
||||
if scenario_def['data']:
|
||||
# Load data.
|
||||
write_concern_db.test.insert_many(scenario_def['data'])
|
||||
|
||||
# Create session0 and session1.
|
||||
sessions = {}
|
||||
for i in range(2):
|
||||
session_name = 'session%d' % i
|
||||
sessions[session_name] = client.start_session(
|
||||
**camel_to_snake_args(test['transactionOptions'][session_name]))
|
||||
|
||||
self.addCleanup(end_sessions, sessions)
|
||||
|
||||
listener.results.clear()
|
||||
collection = client['transaction-tests'].test
|
||||
|
||||
if _TXN_TESTS_DEBUG:
|
||||
self.transaction_test_debug("")
|
||||
|
||||
for op in test['operations']:
|
||||
expected_result = op.get('result')
|
||||
if expect_error(expected_result):
|
||||
with self.assertRaises(OperationFailure) as context:
|
||||
self.run_operation(sessions, collection, op.copy())
|
||||
|
||||
self.assertIn(expected_result['errorContains'],
|
||||
str(context.exception))
|
||||
|
||||
else:
|
||||
result = self.run_operation(sessions, collection, op.copy())
|
||||
self.check_result(expected_result, result)
|
||||
|
||||
self.check_events(test, listener, sessions)
|
||||
|
||||
# Assert final state is expected.
|
||||
expected_c = test['outcome'].get('collection')
|
||||
if expected_c is not None:
|
||||
self.assertEqual(list(collection.find()), expected_c['data'])
|
||||
|
||||
return run_scenario
|
||||
|
||||
|
||||
class ScenarioDict(dict):
|
||||
"""Dict that returns {} for any unknown key, recursively."""
|
||||
def __init__(self, data):
|
||||
def convert(v):
|
||||
if isinstance(v, collections.Mapping):
|
||||
return ScenarioDict(v)
|
||||
if isinstance(v, py3compat.string_type):
|
||||
return v
|
||||
if isinstance(v, collections.Sequence):
|
||||
return [convert(item) for item in v]
|
||||
return v
|
||||
|
||||
dict.__init__(self, [(k, convert(v)) for k, v in data.items()])
|
||||
|
||||
def __getitem__(self, item):
|
||||
try:
|
||||
return dict.__getitem__(self, item)
|
||||
except KeyError:
|
||||
# Unlike a defaultdict, don't set the key, just return a dict.
|
||||
return ScenarioDict({})
|
||||
|
||||
|
||||
def create_tests():
|
||||
for dirpath, _, filenames in os.walk(_TEST_PATH):
|
||||
dirname = os.path.split(dirpath)[-1]
|
||||
|
||||
for filename in filenames:
|
||||
test_type, ext = os.path.splitext(filename)
|
||||
if ext != '.json':
|
||||
continue
|
||||
|
||||
with open(os.path.join(dirpath, filename)) as scenario_stream:
|
||||
scenario_def = ScenarioDict(
|
||||
json_util.loads(scenario_stream.read()))
|
||||
|
||||
# Construct test from scenario.
|
||||
for test in scenario_def['tests']:
|
||||
test_name = 'test_%s_%s_%s' % (
|
||||
dirname,
|
||||
test_type.replace("-", "_"),
|
||||
str(test['description'].replace(" ", "_")))
|
||||
|
||||
new_test = create_test(scenario_def, test)
|
||||
new_test = client_context.require_version_min(3, 7)(new_test)
|
||||
new_test = client_context.require_replica_set(new_test)
|
||||
new_test = client_context._require(
|
||||
not test.get('skipReason'),
|
||||
test.get('skipReason'),
|
||||
new_test)
|
||||
|
||||
if 'secondary' in test_name:
|
||||
new_test = client_context._require(
|
||||
client_context.has_secondaries,
|
||||
'No secondaries',
|
||||
new_test)
|
||||
|
||||
new_test.__name__ = test_name
|
||||
setattr(TestTransactions, new_test.__name__, new_test)
|
||||
|
||||
|
||||
create_tests()
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
181
test/transactions/abort.json
Normal file
181
test/transactions/abort.json
Normal file
@ -0,0 +1,181 @@
|
||||
{
|
||||
"data": [],
|
||||
"tests": [
|
||||
{
|
||||
"description": "abort",
|
||||
"skipReason": "Server must implement abortTransaction",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "abortTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "abortTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"abortTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "abortTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"abortTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "abortTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "implicit abort",
|
||||
"skipReason": "Server must implement abortTransaction",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
578
test/transactions/auto-start.json
Normal file
578
test/transactions/auto-start.json
Normal file
@ -0,0 +1,578 @@
|
||||
{
|
||||
"data": [],
|
||||
"tests": [
|
||||
{
|
||||
"description": "commit",
|
||||
"transactionOptions": {
|
||||
"session0": {
|
||||
"autoStartTransaction": true
|
||||
}
|
||||
},
|
||||
"operations": [
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 2
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 3
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 2
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 3
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"stmtId": 1,
|
||||
"autocommit": null,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
},
|
||||
{
|
||||
"_id": 2
|
||||
},
|
||||
{
|
||||
"_id": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "explicit start",
|
||||
"skipReason": "Server must implement abortTransaction",
|
||||
"transactionOptions": {
|
||||
"session0": {
|
||||
"autoStartTransaction": true
|
||||
}
|
||||
},
|
||||
"operations": [
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 2
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"errorContains": "Cannot start"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 2
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "abort",
|
||||
"skipReason": "Server must implement abortTransaction",
|
||||
"transactionOptions": {
|
||||
"session0": {
|
||||
"autoStartTransaction": true
|
||||
}
|
||||
},
|
||||
"operations": [
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "abortTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 2
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"abortTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "abortTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 2
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "commit empty transaction",
|
||||
"transactionOptions": {
|
||||
"session0": {
|
||||
"autoStartTransaction": true
|
||||
}
|
||||
},
|
||||
"operations": [
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "isolation",
|
||||
"transactionOptions": {
|
||||
"session0": {
|
||||
"autoStartTransaction": true
|
||||
}
|
||||
},
|
||||
"operations": [
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"find": "test",
|
||||
"lsid": "session1",
|
||||
"txnNumber": null,
|
||||
"stmtId": null,
|
||||
"autocommit": null
|
||||
},
|
||||
"command_name": "find",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"find": "test",
|
||||
"lsid": "session1",
|
||||
"txnNumber": null,
|
||||
"stmtId": null,
|
||||
"autocommit": null
|
||||
},
|
||||
"command_name": "find",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
136
test/transactions/commit.json
Normal file
136
test/transactions/commit.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"data": [],
|
||||
"tests": [
|
||||
{
|
||||
"description": "commit",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 2
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 2
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
},
|
||||
{
|
||||
"_id": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
47
test/transactions/errors.json
Normal file
47
test/transactions/errors.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"data": [],
|
||||
"tests": [
|
||||
{
|
||||
"description": "start twice",
|
||||
"skipReason": "Server hangs afterward",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"errorContains": "Cannot start"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
392
test/transactions/isolation.json
Normal file
392
test/transactions/isolation.json
Normal file
@ -0,0 +1,392 @@
|
||||
{
|
||||
"data": [],
|
||||
"tests": [
|
||||
{
|
||||
"description": "one transaction",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": 1
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id",
|
||||
"session": "session0"
|
||||
},
|
||||
"result": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": 0
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id",
|
||||
"session": "session1"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
}
|
||||
},
|
||||
"result": 0
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
}
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": 1
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id",
|
||||
"session": "session1"
|
||||
},
|
||||
"result": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
}
|
||||
},
|
||||
"result": 1
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
}
|
||||
},
|
||||
"result": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id"
|
||||
},
|
||||
"result": [
|
||||
1
|
||||
]
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "two transactions",
|
||||
"skipReason": "TODO: Crashes server.",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": 1
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id",
|
||||
"session": "session0"
|
||||
},
|
||||
"result": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": 0
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id",
|
||||
"session": "session1"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
}
|
||||
},
|
||||
"result": 0
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
}
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": 0
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session1"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id",
|
||||
"session": "session1"
|
||||
},
|
||||
"result": []
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
}
|
||||
},
|
||||
"result": 1
|
||||
},
|
||||
{
|
||||
"name": "find",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
}
|
||||
},
|
||||
"result": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "distinct",
|
||||
"arguments": {
|
||||
"fieldName": "_id"
|
||||
},
|
||||
"result": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
180
test/transactions/read-pref.json
Normal file
180
test/transactions/read-pref.json
Normal file
@ -0,0 +1,180 @@
|
||||
{
|
||||
"data": [],
|
||||
"tests": [
|
||||
{
|
||||
"description": "primary",
|
||||
"skipReason": "Implement read preference rules",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"filter": {
|
||||
"_id": 1
|
||||
},
|
||||
"readPreference": {
|
||||
"mode": "primary"
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": 1
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "write and read",
|
||||
"skipReason": "Implement read preference rules",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"readPreference": {
|
||||
"mode": "secondary"
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"errorContains": "cannot use read preference \"secondary\" in a transaction that began with a write"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "conflict",
|
||||
"skipReason": "Implement read preference rules",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"readPreference": {
|
||||
"mode": "primaryPreferred"
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": 0
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"readPreference": {
|
||||
"mode": "primary"
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"errorContains": "read preference \"primary\" does not match the transaction's original read preference \"primaryPreferred\""
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "read and write",
|
||||
"skipReason": "Implement read preference rules",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"arguments": {
|
||||
"readPreference": {
|
||||
"mode": "secondary"
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": 0
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"errorContains": "cannot write in a transaction that began with read preference \"secondary\""
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1164
test/transactions/snapshot-reads.json
Normal file
1164
test/transactions/snapshot-reads.json
Normal file
File diff suppressed because it is too large
Load Diff
218
test/transactions/statement-ids.json
Normal file
218
test/transactions/statement-ids.json
Normal file
@ -0,0 +1,218 @@
|
||||
{
|
||||
"data": [],
|
||||
"tests": [
|
||||
{
|
||||
"description": "insertMany",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertMany",
|
||||
"arguments": {
|
||||
"documents": [
|
||||
{
|
||||
"_id": 2
|
||||
},
|
||||
{
|
||||
"_id": 3
|
||||
}
|
||||
],
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedIds": [
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 4
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 5
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 2
|
||||
},
|
||||
{
|
||||
"_id": 3
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 1,
|
||||
"autocommit": null,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 4
|
||||
}
|
||||
],
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 3,
|
||||
"autocommit": null,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 5
|
||||
}
|
||||
],
|
||||
"ordered": true,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 2,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
},
|
||||
{
|
||||
"_id": 2
|
||||
},
|
||||
{
|
||||
"_id": 3
|
||||
},
|
||||
{
|
||||
"_id": 4
|
||||
},
|
||||
{
|
||||
"_id": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
301
test/transactions/write-concern.json
Normal file
301
test/transactions/write-concern.json
Normal file
@ -0,0 +1,301 @@
|
||||
{
|
||||
"data": [],
|
||||
"tests": [
|
||||
{
|
||||
"description": "commit with majority",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0",
|
||||
"writeConcern": {
|
||||
"w": "majority"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": {
|
||||
"w": "majority"
|
||||
}
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "commit with default",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "commitTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autocommit": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"commitTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "commitTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "abort with majority",
|
||||
"skipReason": "Server must implement abortTransaction",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0",
|
||||
"writeConcern": {
|
||||
"w": "majority"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "abortTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autoabort": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"abortTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": {
|
||||
"w": "majority"
|
||||
}
|
||||
},
|
||||
"command_name": "abortTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "abort with default",
|
||||
"skipReason": "Server must implement abortTransaction",
|
||||
"operations": [
|
||||
{
|
||||
"name": "startTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "insertOne",
|
||||
"arguments": {
|
||||
"document": {
|
||||
"_id": 1
|
||||
},
|
||||
"session": "session0"
|
||||
},
|
||||
"result": {
|
||||
"insertedId": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "abortTransaction",
|
||||
"arguments": {
|
||||
"session": "session0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"insert": "test",
|
||||
"documents": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
],
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"stmtId": 0,
|
||||
"autoabort": false,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "insert",
|
||||
"database_name": "transaction-tests"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command_started_event": {
|
||||
"command": {
|
||||
"abortTransaction": 1,
|
||||
"lsid": "session0",
|
||||
"txnNumber": 1,
|
||||
"writeConcern": null
|
||||
},
|
||||
"command_name": "abortTransaction",
|
||||
"database_name": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outcome": {
|
||||
"collection": {
|
||||
"data": [
|
||||
{
|
||||
"_id": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user