mongodb 6.8.0-dev.20240703.sha.5abf5fca → 6.8.0-dev.20240709.sha.9a5e6110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/operations/command.js +1 -1
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/execute_operation.js +110 -108
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/operation.js +2 -2
- package/lib/operations/operation.js.map +1 -1
- package/package.json +1 -1
- package/src/operations/command.ts +1 -1
- package/src/operations/execute_operation.ts +132 -128
- package/src/operations/operation.ts +2 -2
|
@@ -38,7 +38,7 @@ class CommandOperation extends operation_1.AbstractOperation {
|
|
|
38
38
|
if (this.hasAspect(operation_1.Aspect.EXPLAINABLE)) {
|
|
39
39
|
return this.explain == null;
|
|
40
40
|
}
|
|
41
|
-
return
|
|
41
|
+
return super.canRetryWrite;
|
|
42
42
|
}
|
|
43
43
|
async executeCommand(server, session, cmd, responseType) {
|
|
44
44
|
this.server = server;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/operations/command.ts"],"names":[],"mappings":";;;AAEA,oCAAqD;AACrD,wCAA0D;AAC1D,kDAA8C;AAG9C,+DAA4E;AAE5E,oCAKkB;AAClB,oDAA0E;AAE1E,2CAA+E;AAoD/E,gBAAgB;AAChB,MAAsB,gBAAoB,SAAQ,6BAAoB;IAMpE,YAAY,MAAwB,EAAE,OAAiC;QACrE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;QAE7B,kFAAkF;QAClF,kFAAkF;QAClF,qBAAqB;QACrB,MAAM,cAAc,GAAG,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,CAAC;QAC1D,IAAI,cAAc,EAAE;YAClB,IAAI,CAAC,EAAE,GAAG,IAAI,wBAAgB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;SACxD;aAAM;YACL,IAAI,CAAC,EAAE,GAAG,MAAM;gBACd,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC;gBAC3C,CAAC,CAAC,IAAI,wBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;SAC3C;QAED,IAAI,CAAC,WAAW,GAAG,0BAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,YAAY,GAAG,4BAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAEtD,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAM,CAAC,WAAW,CAAC,EAAE;YACtC,IAAI,CAAC,OAAO,GAAG,iBAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;SAC7C;aAAM,IAAI,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE;YACnC,MAAM,IAAI,iCAAyB,CAAC,mDAAmD,CAAC,CAAC;SAC1F;IACH,CAAC;IAED,IAAa,aAAa;QACxB,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAM,CAAC,WAAW,CAAC,EAAE;YACtC,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;SAC7B;QACD,OAAO,
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/operations/command.ts"],"names":[],"mappings":";;;AAEA,oCAAqD;AACrD,wCAA0D;AAC1D,kDAA8C;AAG9C,+DAA4E;AAE5E,oCAKkB;AAClB,oDAA0E;AAE1E,2CAA+E;AAoD/E,gBAAgB;AAChB,MAAsB,gBAAoB,SAAQ,6BAAoB;IAMpE,YAAY,MAAwB,EAAE,OAAiC;QACrE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;QAE7B,kFAAkF;QAClF,kFAAkF;QAClF,qBAAqB;QACrB,MAAM,cAAc,GAAG,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,CAAC;QAC1D,IAAI,cAAc,EAAE;YAClB,IAAI,CAAC,EAAE,GAAG,IAAI,wBAAgB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;SACxD;aAAM;YACL,IAAI,CAAC,EAAE,GAAG,MAAM;gBACd,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC;gBAC3C,CAAC,CAAC,IAAI,wBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;SAC3C;QAED,IAAI,CAAC,WAAW,GAAG,0BAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,YAAY,GAAG,4BAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAEtD,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAM,CAAC,WAAW,CAAC,EAAE;YACtC,IAAI,CAAC,OAAO,GAAG,iBAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;SAC7C;aAAM,IAAI,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE;YACnC,MAAM,IAAI,iCAAyB,CAAC,mDAAmD,CAAC,CAAC;SAC1F;IACH,CAAC;IAED,IAAa,aAAa;QACxB,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAM,CAAC,WAAW,CAAC,EAAE;YACtC,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;SAC7B;QACD,OAAO,KAAK,CAAC,aAAa,CAAC;IAC7B,CAAC;IAeD,KAAK,CAAC,cAAc,CAClB,MAAc,EACd,OAAkC,EAClC,GAAa,EACb,YAAyC;QAEzC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,MAAM,OAAO,GAAG;YACd,GAAG,IAAI,CAAC,OAAO;YACf,GAAG,IAAI,CAAC,WAAW;YACnB,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,OAAO;SACR,CAAC;QAEF,MAAM,iBAAiB,GAAG,IAAA,sBAAc,EAAC,MAAM,CAAC,CAAC;QACjD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAEnE,IAAI,IAAI,CAAC,WAAW,IAAI,IAAA,kCAA0B,EAAC,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE;YACzE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;SACvD;QAED,IAAI,IAAI,CAAC,iBAAiB,IAAI,iBAAiB,GAAG,mDAAgC,EAAE;YAClF,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;SACnC;QAED,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAM,CAAC,eAAe,CAAC,IAAI,CAAC,aAAa,EAAE;YACjF,4BAAY,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;SAC5C;QAED,IACE,OAAO,CAAC,SAAS;YACjB,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ;YACrC,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAM,CAAC,cAAc,CAAC,EACtC;YACA,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;SACtD;QAED,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,EAAE;YACzC,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;SACnC;QAED,IAAI,IAAI,CAAC,SAAS,CAAC,kBAAM,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;YACtD,GAAG,GAAG,IAAA,2BAAmB,EAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;SAC9C;QAED,OAAO,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IACnE,CAAC;CACF;AApGD,4CAoGC"}
|
|
@@ -18,10 +18,9 @@ const MMAPv1_RETRY_WRITES_ERROR_MESSAGE = 'This MongoDB deployment does not supp
|
|
|
18
18
|
* not provided.
|
|
19
19
|
*
|
|
20
20
|
* The expectation is that this function:
|
|
21
|
-
* - Connects the MongoClient if it has not already been connected
|
|
21
|
+
* - Connects the MongoClient if it has not already been connected, see {@link autoConnect}
|
|
22
22
|
* - Creates a session if none is provided and cleans up the session it creates
|
|
23
|
-
* -
|
|
24
|
-
* - Retries an operation if it fails for certain errors, see {@link retryOperation}
|
|
23
|
+
* - Tries an operation and retries under certain conditions, see {@link tryOperation}
|
|
25
24
|
*
|
|
26
25
|
* @typeParam T - The operation's type
|
|
27
26
|
* @typeParam TResult - The type of the operation's result, calculated from T
|
|
@@ -34,23 +33,7 @@ async function executeOperation(client, operation) {
|
|
|
34
33
|
// TODO(NODE-3483): Extend MongoRuntimeError
|
|
35
34
|
throw new error_1.MongoRuntimeError('This method requires a valid operation instance');
|
|
36
35
|
}
|
|
37
|
-
|
|
38
|
-
// Auto connect on operation
|
|
39
|
-
if (client.s.hasBeenClosed) {
|
|
40
|
-
throw new error_1.MongoNotConnectedError('Client must be connected before running operations');
|
|
41
|
-
}
|
|
42
|
-
client.s.options[Symbol.for('@@mdb.skipPingOnConnect')] = true;
|
|
43
|
-
try {
|
|
44
|
-
await client.connect();
|
|
45
|
-
}
|
|
46
|
-
finally {
|
|
47
|
-
delete client.s.options[Symbol.for('@@mdb.skipPingOnConnect')];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const { topology } = client;
|
|
51
|
-
if (topology == null) {
|
|
52
|
-
throw new error_1.MongoRuntimeError('client.connect did not create a topology but also did not throw');
|
|
53
|
-
}
|
|
36
|
+
const topology = await autoConnect(client);
|
|
54
37
|
// The driver sessions spec mandates that we implicitly create sessions for operations
|
|
55
38
|
// that are not explicitly provided with a session.
|
|
56
39
|
let session = operation.session;
|
|
@@ -74,7 +57,6 @@ async function executeOperation(client, operation) {
|
|
|
74
57
|
const readPreference = operation.readPreference ?? read_preference_1.ReadPreference.primary;
|
|
75
58
|
const inTransaction = !!session?.inTransaction();
|
|
76
59
|
const hasReadAspect = operation.hasAspect(operation_1.Aspect.READ_OPERATION);
|
|
77
|
-
const hasWriteAspect = operation.hasAspect(operation_1.Aspect.WRITE_OPERATION);
|
|
78
60
|
if (inTransaction &&
|
|
79
61
|
!readPreference.equals(read_preference_1.ReadPreference.primary) &&
|
|
80
62
|
(hasReadAspect || operation.commandName === 'runCommand')) {
|
|
@@ -83,6 +65,62 @@ async function executeOperation(client, operation) {
|
|
|
83
65
|
if (session?.isPinned && session.transaction.isCommitted && !operation.bypassPinningCheck) {
|
|
84
66
|
session.unpin();
|
|
85
67
|
}
|
|
68
|
+
try {
|
|
69
|
+
return await tryOperation(operation, {
|
|
70
|
+
topology,
|
|
71
|
+
session,
|
|
72
|
+
readPreference
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
if (session?.owner != null && session.owner === owner) {
|
|
77
|
+
await session.endSession();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.executeOperation = executeOperation;
|
|
82
|
+
/**
|
|
83
|
+
* Connects a client if it has not yet been connected
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
async function autoConnect(client) {
|
|
87
|
+
if (client.topology == null) {
|
|
88
|
+
if (client.s.hasBeenClosed) {
|
|
89
|
+
throw new error_1.MongoNotConnectedError('Client must be connected before running operations');
|
|
90
|
+
}
|
|
91
|
+
client.s.options[Symbol.for('@@mdb.skipPingOnConnect')] = true;
|
|
92
|
+
try {
|
|
93
|
+
await client.connect();
|
|
94
|
+
if (client.topology == null) {
|
|
95
|
+
throw new error_1.MongoRuntimeError('client.connect did not create a topology but also did not throw');
|
|
96
|
+
}
|
|
97
|
+
return client.topology;
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
delete client.s.options[Symbol.for('@@mdb.skipPingOnConnect')];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return client.topology;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Executes an operation and retries as appropriate
|
|
107
|
+
* @internal
|
|
108
|
+
*
|
|
109
|
+
* @remarks
|
|
110
|
+
* Implements behaviour described in [Retryable Reads](https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.md) and [Retryable
|
|
111
|
+
* Writes](https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.md) specification
|
|
112
|
+
*
|
|
113
|
+
* This function:
|
|
114
|
+
* - performs initial server selection
|
|
115
|
+
* - attempts to execute an operation
|
|
116
|
+
* - retries the operation if it meets the criteria for a retryable read or a retryable write
|
|
117
|
+
*
|
|
118
|
+
* @typeParam T - The operation's type
|
|
119
|
+
* @typeParam TResult - The type of the operation's result, calculated from T
|
|
120
|
+
*
|
|
121
|
+
* @param operation - The operation to execute
|
|
122
|
+
* */
|
|
123
|
+
async function tryOperation(operation, { topology, session, readPreference }) {
|
|
86
124
|
let selector;
|
|
87
125
|
if (operation.hasAspect(operation_1.Aspect.MUST_SELECT_SAME_SERVER)) {
|
|
88
126
|
// GetMore and KillCursor operations must always select the same server, but run through
|
|
@@ -98,109 +136,73 @@ async function executeOperation(client, operation) {
|
|
|
98
136
|
else {
|
|
99
137
|
selector = readPreference;
|
|
100
138
|
}
|
|
101
|
-
|
|
139
|
+
let server = await topology.selectServer(selector, {
|
|
102
140
|
session,
|
|
103
141
|
operationName: operation.commandName
|
|
104
142
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
if (!operation.hasAspect(operation_1.Aspect.RETRYABLE)) {
|
|
110
|
-
// non-retryable operation, early exit
|
|
111
|
-
try {
|
|
112
|
-
return await operation.execute(server, session);
|
|
113
|
-
}
|
|
114
|
-
finally {
|
|
115
|
-
if (session?.owner != null && session.owner === owner) {
|
|
116
|
-
try {
|
|
117
|
-
await session.endSession();
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
(0, utils_1.squashError)(error);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
143
|
+
const hasReadAspect = operation.hasAspect(operation_1.Aspect.READ_OPERATION);
|
|
144
|
+
const hasWriteAspect = operation.hasAspect(operation_1.Aspect.WRITE_OPERATION);
|
|
145
|
+
const inTransaction = session?.inTransaction() ?? false;
|
|
125
146
|
const willRetryRead = topology.s.options.retryReads && !inTransaction && operation.canRetryRead;
|
|
126
147
|
const willRetryWrite = topology.s.options.retryWrites &&
|
|
127
148
|
!inTransaction &&
|
|
128
149
|
(0, utils_1.supportsRetryableWrites)(server) &&
|
|
129
150
|
operation.canRetryWrite;
|
|
130
|
-
const willRetry = (
|
|
131
|
-
|
|
151
|
+
const willRetry = operation.hasAspect(operation_1.Aspect.RETRYABLE) &&
|
|
152
|
+
session != null &&
|
|
153
|
+
((hasReadAspect && willRetryRead) || (hasWriteAspect && willRetryWrite));
|
|
154
|
+
if (hasWriteAspect && willRetryWrite && session != null) {
|
|
132
155
|
operation.options.willRetryWrite = true;
|
|
133
156
|
session.incrementTransactionNumber();
|
|
134
157
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
158
|
+
// TODO(NODE-6231): implement infinite retry within CSOT timeout here
|
|
159
|
+
const maxTries = willRetry ? 2 : 1;
|
|
160
|
+
let previousOperationError;
|
|
161
|
+
let previousServer;
|
|
162
|
+
// TODO(NODE-6231): implement infinite retry within CSOT timeout here
|
|
163
|
+
for (let tries = 0; tries < maxTries; tries++) {
|
|
164
|
+
if (previousOperationError) {
|
|
165
|
+
if (hasWriteAspect && previousOperationError.code === MMAPv1_RETRY_WRITES_ERROR_CODE) {
|
|
166
|
+
throw new error_1.MongoServerError({
|
|
167
|
+
message: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
|
|
168
|
+
errmsg: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
|
|
169
|
+
originalError: previousOperationError
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (hasWriteAspect && !(0, error_1.isRetryableWriteError)(previousOperationError))
|
|
173
|
+
throw previousOperationError;
|
|
174
|
+
if (hasReadAspect && !(0, error_1.isRetryableReadError)(previousOperationError))
|
|
175
|
+
throw previousOperationError;
|
|
176
|
+
if (previousOperationError instanceof error_1.MongoNetworkError &&
|
|
177
|
+
operation.hasAspect(operation_1.Aspect.CURSOR_CREATING) &&
|
|
178
|
+
session != null &&
|
|
179
|
+
session.isPinned &&
|
|
180
|
+
!session.inTransaction()) {
|
|
181
|
+
session.unpin({ force: true, forceClear: true });
|
|
182
|
+
}
|
|
183
|
+
server = await topology.selectServer(selector, {
|
|
141
184
|
session,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
previousServer: server.description
|
|
185
|
+
operationName: operation.commandName,
|
|
186
|
+
previousServer
|
|
145
187
|
});
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
finally {
|
|
150
|
-
if (session?.owner != null && session.owner === owner) {
|
|
151
|
-
try {
|
|
152
|
-
await session.endSession();
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
(0, utils_1.squashError)(error);
|
|
188
|
+
if (hasWriteAspect && !(0, utils_1.supportsRetryableWrites)(server)) {
|
|
189
|
+
throw new error_1.MongoUnexpectedServerResponseError('Selected server does not support retryable writes');
|
|
156
190
|
}
|
|
157
191
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
if (isWriteOperation && !(0, error_1.isRetryableWriteError)(originalError)) {
|
|
172
|
-
throw originalError;
|
|
173
|
-
}
|
|
174
|
-
if (isReadOperation && !(0, error_1.isRetryableReadError)(originalError)) {
|
|
175
|
-
throw originalError;
|
|
176
|
-
}
|
|
177
|
-
if (originalError instanceof error_1.MongoNetworkError &&
|
|
178
|
-
session.isPinned &&
|
|
179
|
-
!session.inTransaction() &&
|
|
180
|
-
operation.hasAspect(operation_1.Aspect.CURSOR_CREATING)) {
|
|
181
|
-
// If we have a cursor and the initial command fails with a network error,
|
|
182
|
-
// we can retry it on another connection. So we need to check it back in, clear the
|
|
183
|
-
// pool for the service id, and retry again.
|
|
184
|
-
session.unpin({ force: true, forceClear: true });
|
|
185
|
-
}
|
|
186
|
-
// select a new server, and attempt to retry the operation
|
|
187
|
-
const server = await topology.selectServer(selector, {
|
|
188
|
-
session,
|
|
189
|
-
operationName: operation.commandName,
|
|
190
|
-
previousServer
|
|
191
|
-
});
|
|
192
|
-
if (isWriteOperation && !(0, utils_1.supportsRetryableWrites)(server)) {
|
|
193
|
-
throw new error_1.MongoUnexpectedServerResponseError('Selected server does not support retryable writes');
|
|
194
|
-
}
|
|
195
|
-
try {
|
|
196
|
-
return await operation.execute(server, session);
|
|
197
|
-
}
|
|
198
|
-
catch (retryError) {
|
|
199
|
-
if (retryError instanceof error_1.MongoError &&
|
|
200
|
-
retryError.hasErrorLabel(error_1.MongoErrorLabel.NoWritesPerformed)) {
|
|
201
|
-
throw originalError;
|
|
192
|
+
try {
|
|
193
|
+
return await operation.execute(server, session);
|
|
194
|
+
}
|
|
195
|
+
catch (operationError) {
|
|
196
|
+
if (!(operationError instanceof error_1.MongoError))
|
|
197
|
+
throw operationError;
|
|
198
|
+
if (previousOperationError != null &&
|
|
199
|
+
operationError.hasErrorLabel(error_1.MongoErrorLabel.NoWritesPerformed)) {
|
|
200
|
+
throw previousOperationError;
|
|
201
|
+
}
|
|
202
|
+
previousServer = server.description;
|
|
203
|
+
previousOperationError = operationError;
|
|
202
204
|
}
|
|
203
|
-
throw retryError;
|
|
204
205
|
}
|
|
206
|
+
throw previousOperationError;
|
|
205
207
|
}
|
|
206
208
|
//# sourceMappingURL=execute_operation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute_operation.js","sourceRoot":"","sources":["../../src/operations/execute_operation.ts"],"names":[],"mappings":";;;AAAA,oCAekB;AAElB,wDAAoD;AAEpD,+DAIkC;AAGlC,
|
|
1
|
+
{"version":3,"file":"execute_operation.js","sourceRoot":"","sources":["../../src/operations/execute_operation.ts"],"names":[],"mappings":"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"}
|
|
@@ -47,10 +47,10 @@ class AbstractOperation {
|
|
|
47
47
|
this[kSession] = undefined;
|
|
48
48
|
}
|
|
49
49
|
get canRetryRead() {
|
|
50
|
-
return
|
|
50
|
+
return this.hasAspect(exports.Aspect.RETRYABLE) && this.hasAspect(exports.Aspect.READ_OPERATION);
|
|
51
51
|
}
|
|
52
52
|
get canRetryWrite() {
|
|
53
|
-
return
|
|
53
|
+
return this.hasAspect(exports.Aspect.RETRYABLE) && this.hasAspect(exports.Aspect.WRITE_OPERATION);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
exports.AbstractOperation = AbstractOperation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operation.js","sourceRoot":"","sources":["../../src/operations/operation.ts"],"names":[],"mappings":";;;AAAA,kCAAuF;AACvF,wDAA6E;AAKhE,QAAA,MAAM,GAAG;IACpB,cAAc,EAAE,MAAM,CAAC,gBAAgB,CAAC;IACxC,eAAe,EAAE,MAAM,CAAC,iBAAiB,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC,gBAAgB,CAAC;IACxC,eAAe,EAAE,MAAM,CAAC,iBAAiB,CAAC;IAC1C,uBAAuB,EAAE,MAAM,CAAC,yBAAyB,CAAC;CAClD,CAAC;AA2BX,gBAAgB;AAChB,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAEnC;;;;;;GAMG;AACH,MAAsB,iBAAiB;IAcrC,YAAY,UAA4B,EAAE;QACxC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,cAAM,CAAC,eAAe,CAAC;YAC1D,CAAC,CAAC,gCAAc,CAAC,OAAO;YACxB,CAAC,CAAC,gCAAc,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,gCAAc,CAAC,OAAO,CAAC;QAElE,oEAAoE;QACpE,IAAI,CAAC,WAAW,GAAG,IAAA,yBAAkB,EAAC,OAAO,CAAC,CAAC;QAE/C,IAAI,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAEvE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC;QACvD,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACjC,CAAC;IAQD,SAAS,CAAC,MAAc;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAmC,CAAC;QACtD,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,EAAE;YACxB,OAAO,KAAK,CAAC;SACd;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC;IAED,YAAY;QACV,IAAI,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"operation.js","sourceRoot":"","sources":["../../src/operations/operation.ts"],"names":[],"mappings":";;;AAAA,kCAAuF;AACvF,wDAA6E;AAKhE,QAAA,MAAM,GAAG;IACpB,cAAc,EAAE,MAAM,CAAC,gBAAgB,CAAC;IACxC,eAAe,EAAE,MAAM,CAAC,iBAAiB,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC,gBAAgB,CAAC;IACxC,eAAe,EAAE,MAAM,CAAC,iBAAiB,CAAC;IAC1C,uBAAuB,EAAE,MAAM,CAAC,yBAAyB,CAAC;CAClD,CAAC;AA2BX,gBAAgB;AAChB,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAEnC;;;;;;GAMG;AACH,MAAsB,iBAAiB;IAcrC,YAAY,UAA4B,EAAE;QACxC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,cAAM,CAAC,eAAe,CAAC;YAC1D,CAAC,CAAC,gCAAc,CAAC,OAAO;YACxB,CAAC,CAAC,gCAAc,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,gCAAc,CAAC,OAAO,CAAC;QAElE,oEAAoE;QACpE,IAAI,CAAC,WAAW,GAAG,IAAA,yBAAkB,EAAC,OAAO,CAAC,CAAC;QAE/C,IAAI,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAEvE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC;QACvD,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACjC,CAAC;IAQD,SAAS,CAAC,MAAc;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAmC,CAAC;QACtD,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,EAAE;YACxB,OAAO,KAAK,CAAC;SACd;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC;IAED,YAAY;QACV,IAAI,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,cAAM,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,cAAM,CAAC,cAAc,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,SAAS,CAAC,cAAM,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,cAAM,CAAC,eAAe,CAAC,CAAC;IACpF,CAAC;CACF;AA3DD,8CA2DC;AAED,SAAgB,aAAa,CAC3B,SAA+B,EAC/B,OAAwC;IAExC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,YAAY,GAAG,CAAC,EAAE;QACxD,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;KACrB;IAED,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE;QAC1C,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC;AAfD,sCAeC"}
|
package/package.json
CHANGED
|
@@ -104,7 +104,7 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
|
|
|
104
104
|
if (this.hasAspect(Aspect.EXPLAINABLE)) {
|
|
105
105
|
return this.explain == null;
|
|
106
106
|
}
|
|
107
|
-
return
|
|
107
|
+
return super.canRetryWrite;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
public async executeCommand<T extends MongoDBResponseConstructor>(
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from '../sdam/server_selection';
|
|
25
25
|
import type { Topology } from '../sdam/topology';
|
|
26
26
|
import type { ClientSession } from '../sessions';
|
|
27
|
-
import {
|
|
27
|
+
import { supportsRetryableWrites } from '../utils';
|
|
28
28
|
import { AbstractOperation, Aspect } from './operation';
|
|
29
29
|
|
|
30
30
|
const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
|
|
@@ -45,10 +45,9 @@ type ResultTypeFromOperation<TOperation> = TOperation extends AbstractOperation<
|
|
|
45
45
|
* not provided.
|
|
46
46
|
*
|
|
47
47
|
* The expectation is that this function:
|
|
48
|
-
* - Connects the MongoClient if it has not already been connected
|
|
48
|
+
* - Connects the MongoClient if it has not already been connected, see {@link autoConnect}
|
|
49
49
|
* - Creates a session if none is provided and cleans up the session it creates
|
|
50
|
-
* -
|
|
51
|
-
* - Retries an operation if it fails for certain errors, see {@link retryOperation}
|
|
50
|
+
* - Tries an operation and retries under certain conditions, see {@link tryOperation}
|
|
52
51
|
*
|
|
53
52
|
* @typeParam T - The operation's type
|
|
54
53
|
* @typeParam TResult - The type of the operation's result, calculated from T
|
|
@@ -65,23 +64,7 @@ export async function executeOperation<
|
|
|
65
64
|
throw new MongoRuntimeError('This method requires a valid operation instance');
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
// Auto connect on operation
|
|
70
|
-
if (client.s.hasBeenClosed) {
|
|
71
|
-
throw new MongoNotConnectedError('Client must be connected before running operations');
|
|
72
|
-
}
|
|
73
|
-
client.s.options[Symbol.for('@@mdb.skipPingOnConnect')] = true;
|
|
74
|
-
try {
|
|
75
|
-
await client.connect();
|
|
76
|
-
} finally {
|
|
77
|
-
delete client.s.options[Symbol.for('@@mdb.skipPingOnConnect')];
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const { topology } = client;
|
|
82
|
-
if (topology == null) {
|
|
83
|
-
throw new MongoRuntimeError('client.connect did not create a topology but also did not throw');
|
|
84
|
-
}
|
|
67
|
+
const topology = await autoConnect(client);
|
|
85
68
|
|
|
86
69
|
// The driver sessions spec mandates that we implicitly create sessions for operations
|
|
87
70
|
// that are not explicitly provided with a session.
|
|
@@ -108,7 +91,6 @@ export async function executeOperation<
|
|
|
108
91
|
const inTransaction = !!session?.inTransaction();
|
|
109
92
|
|
|
110
93
|
const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
|
|
111
|
-
const hasWriteAspect = operation.hasAspect(Aspect.WRITE_OPERATION);
|
|
112
94
|
|
|
113
95
|
if (
|
|
114
96
|
inTransaction &&
|
|
@@ -124,6 +106,73 @@ export async function executeOperation<
|
|
|
124
106
|
session.unpin();
|
|
125
107
|
}
|
|
126
108
|
|
|
109
|
+
try {
|
|
110
|
+
return await tryOperation(operation, {
|
|
111
|
+
topology,
|
|
112
|
+
session,
|
|
113
|
+
readPreference
|
|
114
|
+
});
|
|
115
|
+
} finally {
|
|
116
|
+
if (session?.owner != null && session.owner === owner) {
|
|
117
|
+
await session.endSession();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Connects a client if it has not yet been connected
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
async function autoConnect(client: MongoClient): Promise<Topology> {
|
|
127
|
+
if (client.topology == null) {
|
|
128
|
+
if (client.s.hasBeenClosed) {
|
|
129
|
+
throw new MongoNotConnectedError('Client must be connected before running operations');
|
|
130
|
+
}
|
|
131
|
+
client.s.options[Symbol.for('@@mdb.skipPingOnConnect')] = true;
|
|
132
|
+
try {
|
|
133
|
+
await client.connect();
|
|
134
|
+
if (client.topology == null) {
|
|
135
|
+
throw new MongoRuntimeError(
|
|
136
|
+
'client.connect did not create a topology but also did not throw'
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return client.topology;
|
|
140
|
+
} finally {
|
|
141
|
+
delete client.s.options[Symbol.for('@@mdb.skipPingOnConnect')];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return client.topology;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** @internal */
|
|
148
|
+
type RetryOptions = {
|
|
149
|
+
session: ClientSession | undefined;
|
|
150
|
+
readPreference: ReadPreference;
|
|
151
|
+
topology: Topology;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Executes an operation and retries as appropriate
|
|
156
|
+
* @internal
|
|
157
|
+
*
|
|
158
|
+
* @remarks
|
|
159
|
+
* Implements behaviour described in [Retryable Reads](https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.md) and [Retryable
|
|
160
|
+
* Writes](https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.md) specification
|
|
161
|
+
*
|
|
162
|
+
* This function:
|
|
163
|
+
* - performs initial server selection
|
|
164
|
+
* - attempts to execute an operation
|
|
165
|
+
* - retries the operation if it meets the criteria for a retryable read or a retryable write
|
|
166
|
+
*
|
|
167
|
+
* @typeParam T - The operation's type
|
|
168
|
+
* @typeParam TResult - The type of the operation's result, calculated from T
|
|
169
|
+
*
|
|
170
|
+
* @param operation - The operation to execute
|
|
171
|
+
* */
|
|
172
|
+
async function tryOperation<
|
|
173
|
+
T extends AbstractOperation<TResult>,
|
|
174
|
+
TResult = ResultTypeFromOperation<T>
|
|
175
|
+
>(operation: T, { topology, session, readPreference }: RetryOptions): Promise<TResult> {
|
|
127
176
|
let selector: ReadPreference | ServerSelector;
|
|
128
177
|
|
|
129
178
|
if (operation.hasAspect(Aspect.MUST_SELECT_SAME_SERVER)) {
|
|
@@ -139,30 +188,14 @@ export async function executeOperation<
|
|
|
139
188
|
selector = readPreference;
|
|
140
189
|
}
|
|
141
190
|
|
|
142
|
-
|
|
191
|
+
let server = await topology.selectServer(selector, {
|
|
143
192
|
session,
|
|
144
193
|
operationName: operation.commandName
|
|
145
194
|
});
|
|
146
195
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (!operation.hasAspect(Aspect.RETRYABLE)) {
|
|
153
|
-
// non-retryable operation, early exit
|
|
154
|
-
try {
|
|
155
|
-
return await operation.execute(server, session);
|
|
156
|
-
} finally {
|
|
157
|
-
if (session?.owner != null && session.owner === owner) {
|
|
158
|
-
try {
|
|
159
|
-
await session.endSession();
|
|
160
|
-
} catch (error) {
|
|
161
|
-
squashError(error);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
196
|
+
const hasReadAspect = operation.hasAspect(Aspect.READ_OPERATION);
|
|
197
|
+
const hasWriteAspect = operation.hasAspect(Aspect.WRITE_OPERATION);
|
|
198
|
+
const inTransaction = session?.inTransaction() ?? false;
|
|
166
199
|
|
|
167
200
|
const willRetryRead = topology.s.options.retryReads && !inTransaction && operation.canRetryRead;
|
|
168
201
|
|
|
@@ -172,105 +205,76 @@ export async function executeOperation<
|
|
|
172
205
|
supportsRetryableWrites(server) &&
|
|
173
206
|
operation.canRetryWrite;
|
|
174
207
|
|
|
175
|
-
const willRetry =
|
|
208
|
+
const willRetry =
|
|
209
|
+
operation.hasAspect(Aspect.RETRYABLE) &&
|
|
210
|
+
session != null &&
|
|
211
|
+
((hasReadAspect && willRetryRead) || (hasWriteAspect && willRetryWrite));
|
|
176
212
|
|
|
177
|
-
if (hasWriteAspect && willRetryWrite) {
|
|
213
|
+
if (hasWriteAspect && willRetryWrite && session != null) {
|
|
178
214
|
operation.options.willRetryWrite = true;
|
|
179
215
|
session.incrementTransactionNumber();
|
|
180
216
|
}
|
|
181
217
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return await retryOperation(operation, operationError, {
|
|
187
|
-
session,
|
|
188
|
-
topology,
|
|
189
|
-
selector,
|
|
190
|
-
previousServer: server.description
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
throw operationError;
|
|
194
|
-
} finally {
|
|
195
|
-
if (session?.owner != null && session.owner === owner) {
|
|
196
|
-
try {
|
|
197
|
-
await session.endSession();
|
|
198
|
-
} catch (error) {
|
|
199
|
-
squashError(error);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
218
|
+
// TODO(NODE-6231): implement infinite retry within CSOT timeout here
|
|
219
|
+
const maxTries = willRetry ? 2 : 1;
|
|
220
|
+
let previousOperationError: MongoError | undefined;
|
|
221
|
+
let previousServer: ServerDescription | undefined;
|
|
204
222
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
223
|
+
// TODO(NODE-6231): implement infinite retry within CSOT timeout here
|
|
224
|
+
for (let tries = 0; tries < maxTries; tries++) {
|
|
225
|
+
if (previousOperationError) {
|
|
226
|
+
if (hasWriteAspect && previousOperationError.code === MMAPv1_RETRY_WRITES_ERROR_CODE) {
|
|
227
|
+
throw new MongoServerError({
|
|
228
|
+
message: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
|
|
229
|
+
errmsg: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
|
|
230
|
+
originalError: previousOperationError
|
|
231
|
+
});
|
|
232
|
+
}
|
|
212
233
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
TResult = ResultTypeFromOperation<T>
|
|
216
|
-
>(
|
|
217
|
-
operation: T,
|
|
218
|
-
originalError: MongoError,
|
|
219
|
-
{ session, topology, selector, previousServer }: RetryOptions
|
|
220
|
-
): Promise<TResult> {
|
|
221
|
-
const isWriteOperation = operation.hasAspect(Aspect.WRITE_OPERATION);
|
|
222
|
-
const isReadOperation = operation.hasAspect(Aspect.READ_OPERATION);
|
|
234
|
+
if (hasWriteAspect && !isRetryableWriteError(previousOperationError))
|
|
235
|
+
throw previousOperationError;
|
|
223
236
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
message: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
|
|
227
|
-
errmsg: MMAPv1_RETRY_WRITES_ERROR_MESSAGE,
|
|
228
|
-
originalError
|
|
229
|
-
});
|
|
230
|
-
}
|
|
237
|
+
if (hasReadAspect && !isRetryableReadError(previousOperationError))
|
|
238
|
+
throw previousOperationError;
|
|
231
239
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
240
|
+
if (
|
|
241
|
+
previousOperationError instanceof MongoNetworkError &&
|
|
242
|
+
operation.hasAspect(Aspect.CURSOR_CREATING) &&
|
|
243
|
+
session != null &&
|
|
244
|
+
session.isPinned &&
|
|
245
|
+
!session.inTransaction()
|
|
246
|
+
) {
|
|
247
|
+
session.unpin({ force: true, forceClear: true });
|
|
248
|
+
}
|
|
239
249
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
) {
|
|
246
|
-
// If we have a cursor and the initial command fails with a network error,
|
|
247
|
-
// we can retry it on another connection. So we need to check it back in, clear the
|
|
248
|
-
// pool for the service id, and retry again.
|
|
249
|
-
session.unpin({ force: true, forceClear: true });
|
|
250
|
-
}
|
|
250
|
+
server = await topology.selectServer(selector, {
|
|
251
|
+
session,
|
|
252
|
+
operationName: operation.commandName,
|
|
253
|
+
previousServer
|
|
254
|
+
});
|
|
251
255
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
if (hasWriteAspect && !supportsRetryableWrites(server)) {
|
|
257
|
+
throw new MongoUnexpectedServerResponseError(
|
|
258
|
+
'Selected server does not support retryable writes'
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
258
262
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
263
|
+
try {
|
|
264
|
+
return await operation.execute(server, session);
|
|
265
|
+
} catch (operationError) {
|
|
266
|
+
if (!(operationError instanceof MongoError)) throw operationError;
|
|
264
267
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
if (
|
|
269
|
+
previousOperationError != null &&
|
|
270
|
+
operationError.hasErrorLabel(MongoErrorLabel.NoWritesPerformed)
|
|
271
|
+
) {
|
|
272
|
+
throw previousOperationError;
|
|
273
|
+
}
|
|
274
|
+
previousServer = server.description;
|
|
275
|
+
previousOperationError = operationError;
|
|
273
276
|
}
|
|
274
|
-
throw retryError;
|
|
275
277
|
}
|
|
278
|
+
|
|
279
|
+
throw previousOperationError;
|
|
276
280
|
}
|
|
@@ -102,11 +102,11 @@ export abstract class AbstractOperation<TResult = any> {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
get canRetryRead(): boolean {
|
|
105
|
-
return
|
|
105
|
+
return this.hasAspect(Aspect.RETRYABLE) && this.hasAspect(Aspect.READ_OPERATION);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
get canRetryWrite(): boolean {
|
|
109
|
-
return
|
|
109
|
+
return this.hasAspect(Aspect.RETRYABLE) && this.hasAspect(Aspect.WRITE_OPERATION);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|