1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
/**
* Utilities for testing transactions.
*/
var TransactionsUtil = (function() {
load("jstests/libs/override_methods/override_helpers.js");
const kCmdsSupportingTransactions = new Set([
'aggregate',
'delete',
'find',
'findAndModify',
'findandmodify',
'getMore',
'insert',
'update',
]);
const kCmdsThatWrite = new Set([
'insert',
'update',
'findAndModify',
'findandmodify',
'delete',
]);
// Indicates an aggregation command with a pipeline that cannot run in a transaction but can
// still execute concurrently with other transactions. Pipelines with $changeStream or $out
// cannot run within a transaction.
function commandIsNonTxnAggregation(cmdName, cmdObj) {
return OverrideHelpers.isAggregationWithOutOrMergeStage(cmdName, cmdObj) ||
OverrideHelpers.isAggregationWithChangeStreamStage(cmdName, cmdObj);
}
function commandSupportsTxn(dbName, cmdName, cmdObj) {
if (cmdName === 'commitTransaction' || cmdName === 'abortTransaction') {
return true;
}
if (!kCmdsSupportingTransactions.has(cmdName) ||
commandIsNonTxnAggregation(cmdName, cmdObj)) {
return false;
}
if (dbName === 'local' || dbName === 'config' || dbName === 'admin') {
return false;
}
if (kCmdsThatWrite.has(cmdName)) {
if (cmdObj[cmdName].startsWith('system.')) {
return false;
}
}
if (cmdObj.lsid === undefined) {
return false;
}
return true;
}
function commandTypeCanSupportTxn(cmdName) {
if (cmdName === 'commitTransaction' || cmdName === 'abortTransaction') {
return true;
}
if (kCmdsSupportingTransactions.has(cmdName)) {
return true;
}
return false;
}
// Make a deep copy of an object for retrying transactions. We make deep copies of object and
// array literals but not custom types like DB and DBCollection because they could have been
// modified before a transaction aborts. This function is adapted from the implementation of
// Object.extend() in src/mongo/shell/types.js.
function deepCopyObject(dst, src) {
for (var k in src) {
var v = src[k];
if (typeof (v) == "object" && v !== null) {
if (v.constructor === ObjectId) { // convert ObjectId properly
eval("v = " + tojson(v));
} else if (v instanceof NumberLong) { // convert NumberLong properly
eval("v = " + tojson(v));
} else if (v instanceof Date) { // convert Date properly
eval("v = " + tojson(v));
} else if (v instanceof Timestamp) { // convert Timestamp properly
eval("v = " + tojson(v));
} else if (Object.getPrototypeOf(v) === Object.prototype) {
v = deepCopyObject({}, v);
} else if (Array.isArray(v)) {
v = deepCopyObject([], v);
}
}
var desc = Object.getOwnPropertyDescriptor(src, k);
desc.value = v;
Object.defineProperty(dst, k, desc);
}
return dst;
}
function isTransientTransactionError(res) {
return res.hasOwnProperty('errorLabels') &&
res.errorLabels.includes('TransientTransactionError');
}
return {
commandIsNonTxnAggregation,
commandSupportsTxn,
commandTypeCanSupportTxn,
deepCopyObject,
isTransientTransactionError,
};
})();
|