Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/retryable-reads/retryable-reads.md
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ any customers experiencing degraded performance can simply disable `retryableRea

## Changelog

- 2026-12-08: Clarified that server deprioritization during retries must use a list of server addresses.
- 2025-12-08: Clarified that server deprioritization during retries must use a list of server addresses.

- 2024-04-30: Migrated from reStructuredText to Markdown.

Expand Down
24 changes: 19 additions & 5 deletions source/retryable-writes/retryable-writes.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,22 @@ retrying is not possible and drivers MUST raise the retryable error from the pre
is able to infer that an attempt was made.

If a retry attempt also fails, drivers MUST update their topology according to the SDAM spec (see:
[Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling)). If an error
would not allow the caller to infer that an attempt was made (e.g. connection pool exception originating from the
driver) or the error is labeled "NoWritesPerformed", the error from the previous attempt should be raised. If all server
errors are labeled "NoWritesPerformed", then the first error should be raised.
[Error Handling](../server-discovery-and-monitoring/server-discovery-and-monitoring.md#error-handling)).

If the driver is unable to retry an operation, an error MUST be returned to the user. Some errors that a driver
encounters indicate that no writes were attempted (i.e., the operation is a no-op). These errors include any client-side
error that occurs before a command is sent (e.g., a server selection or connection checkout error) or any server error
with the `NoWritesPerformed` error label. When the driver encounters multiple errors, the driver MUST ensure that if an
error has been encountered which indicates that a write was attempted, this error is returned. This behavior is
summarized below in the following rules:

- If the driver has encountered only errors that indicate write attempts were made, the most recently encountered error
must be returned.
- If all errors indicate no attempt was made (e.g., all errors contain the `NoWritesPerformed` error label or are
client-side errors before a command is sent), the first error encountered must be returned.
- If the driver has encountered some errors which indicate a write attempt was made and some which indicate no write
attempt was made (e.g., a retryable server error followed by a checkout error), the most recently encountered error
which indicates a write attempt occurred must be returned.

If a driver associates server information (e.g. the server address or description) with an error, the driver MUST ensure
that the reported server information corresponds to the server that originated the error.
Expand Down Expand Up @@ -681,7 +693,9 @@ retryWrites is not true would be inconsistent with the server and potentially co

## Changelog

- 2026-12-08: Clarified that server deprioritization during retries must use a list of server addresses.
- 2026-01-14: Clarify which error to return when more than one error with the `NoWritesPerformed` label is encountered.

- 2025-12-08: Clarified that server deprioritization during retries must use a list of server addresses.

- 2024-05-08: Add guidance for client-level `bulkWrite()` retryability.

Expand Down
159 changes: 158 additions & 1 deletion source/retryable-writes/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,15 +262,172 @@ debugger, code coverage tool, etc.

7. Disable the fail point on `s0`.

### 6. Test error propagation after encountering multiple errors.

These tests MUST:

- be implemented by any driver that implements the Command Monitoring specification.
- only run against replica sets as mongos does not propagate the NoWritesPerformed label to the drivers.
- be run against server versions 6.0 and above.
- be implemented by any driver that has implemented the Client Backpressure specification.

Additionally, this test requires drivers to set a fail point after an `insertOne` operation but before the subsequent
retry. Drivers that are unable to set a failCommand after the CommandFailedEvent SHOULD use mocking or write a unit test
to cover the same sequence of events.

#### Case 1: Test that drivers return the correct error when receiving only errors without `NoWritesPerformed`

1. Create a client with `retryWrites=true`.

2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and
`SystemOverloadedError` error labels:

```javascript
{
configureFailPoint: "failCommand",
mode: {times: 1},
data: {
failCommands: ["insert"],
errorLabels: ["RetryableError", "SystemOverloadedError"],
errorCode: 91
}
}
```

3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `10107` (NotWritablePrimary):

```javascript
{
configureFailPoint: "failCommand",
mode: "alwaysOn",
data: {
failCommands: ["insert"],
errorCode: 10107,
errorLabels: ["RetryableError", "SystemOverloadedError"]
}
}
```

Configure the `10107` fail point command only if the the failed event is for the `91` error configured in step 2.

4. Attempt an `insertOne` operation on any record for any database and collection. Expect the `insertOne` to fail with a
server error. Assert that the error code of the server error is `10107`.

5. Disable the fail point:

```javascript
{
configureFailPoint: "failCommand",
mode: "off"
}
```

#### Case 2: Test that drivers return the correct error when receiving only errors with `NoWritesPerformed`

1. Create a client with `retryWrites=true`.

2. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and
`SystemOverloadedError` error labels:

```javascript
{
configureFailPoint: "failCommand",
mode: {times: 1},
data: {
failCommands: ["insert"],
errorLabels: ["RetryableError", "SystemOverloadedError", "NoWritesPerformed"],
errorCode: 91
}
}
```

3. Via the command monitoring CommandFailedEvent, configure a fail point with error code `10107` (NotWritablePrimary)
and a NoWritesPerformed label:

```javascript
{
configureFailPoint: "failCommand",
mode: "alwaysOn",
data: {
failCommands: ["insert"],
errorCode: 10107,
errorLabels: ["RetryableError", "SystemOverloadedError", "NoWritesPerformed"]
}
}
```

Configure the `10107` fail point command only if the the failed event is for the `91` error configured in step 2.

4. Attempt an `insertOne` operation on any record for any database and collection. Expect the `insertOne` to fail with a
server error. Assert that the error code of the server error is 91.

5. Disable the fail point:

```javascript
{
configureFailPoint: "failCommand",
mode: "off"
}
```

#### Case 3: Test that drivers return the correct error when receiving some errors with `NoWritesPerformed` and some without `NoWritesPerformed`

1. Create a client with `retryWrites=true` and `monitorCommands=true`.

2. Configure the client to listen to CommandFailedEvents. In the attached listener, configure a fail point with error
code `91` (NotWritablePrimary) and the `NoWritesPerformed`, `RetryableError` and `SystemOverloadedError` labels:

```javascript
{
configureFailPoint: "failCommand",
mode: {times: 1},
data: {
failCommands: ["insert"],
errorLabels: ["RetryableError", "SystemOverloadedError", "NoWritesPerformed"],
errorCode: 91
}
}
```

3. Configure a fail point with error code `91` (ShutdownInProgress) with the `RetryableError` and
`SystemOverloadedError` error labels but without the `NoWritesPerformed` error label:

```javascript
{
configureFailPoint: "failCommand",
mode: {times: 1},
data: {
failCommands: ["insert"],
errorLabels: ["RetryableError", "SystemOverloadedError"],
errorCode: 91
}
}
```

4. Attempt an `insertOne` operation on any record for any database and collection. Expect the `insertOne` to fail with a
server error. Assert that the error code of the server error is 91. Assert that the error does not contain the
error label `NoWritesPerformed`.

5. Disable the fail point:

```javascript
{
configureFailPoint: "failCommand",
mode: "off"
}
```

## Changelog

- 2026-02-03: Add tests for error propagation behavior when multiple errors are encountered.

- 2024-10-29: Convert command construction tests to unified format.

- 2024-05-30: Migrated from reStructuredText to Markdown.

- 2024-02-27: Convert legacy retryable writes tests to unified format.

- 2024-02-21: Update prose test 4 and 5 to workaround SDAM behavior preventing execution of deprioritization code paths.
- 2024-02-21: Update prosetest 4 and 5 to workaround SDAM behavior preventing execution of deprioritization code paths.

- 2024-01-05: Fix typo in prose test title.

Expand Down
Loading