diff --git a/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.md b/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.md index b6e5ebc81b..7515325c3e 100644 --- a/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.md +++ b/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.md @@ -284,6 +284,15 @@ Endpoint. The pool has the following properties: - **Rate-limited:** A Pool MUST limit the number of [Connections](#connection) being [established](#establishing-a-connection-internal-implementation) concurrently via the **maxConnecting** [pool option](#connection-pool-options). +- **Backoff-capable** A pool MUST be able to enter backoff mode. A pool will automatically enter backoff mode when a + connection checkout fails under conditions that indicate server overload. The rules for entering backoff mode are as + follows: - A network error or network timeout during the TCP handshake or the `hello` message for a new connection + MUST trigger the backoff state. - Other pending connections MUST not be canceled. - In the case of multiple pending + connections, the backoff attempt number MUST only be incremented once. This can be done by recording the state prior + to attempting the connection. While the Pool is in backoff, it exhibits the following behaviors: - **maxConnecting** + MUST be set to 1. - The Pool MUST wait for the backoff duration before another connection attempt. - A successful + heartbeat MUST NOT change the state of the pool. - A failed heartbeat MUST clear the pool. - A subsequent failed + connection MUST increase the backoff attempt. - A successful connection MUST return the Pool to ready state. ```typescript interface ConnectionPool { @@ -314,12 +323,17 @@ interface ConnectionPool { * - "ready": The healthy state of the pool. It can service checkOut requests and create * connections in the background. The pool can be set to this state via the * ready() method. + * + * - "backoff": The pool is in backoff state. MaxConnecting is set to 1 and the pool backoff period + * must be observed before attempting another connection. A subsequent failed connection + * attempt increases the backoff duration. The pool can be set to this state via the + * backoff() method. * * - "closed": The pool is destroyed. No more Connections may ever be checked out nor any * created in the background. The pool can be set to this state via the close() * method. The pool cannot transition to any other state after being closed. */ - state: "paused" | "ready" | "closed"; + state: "paused" | "ready" | "backoff" | "closed"; // Any of the following connection counts may be computed rather than // actually stored on the pool. @@ -360,6 +374,11 @@ interface ConnectionPool { */ clear(interruptInUseConnections: Optional): void; + /** + * Enter backoff mode or increase backoff amount if already in backoff mode. Mark the pool as "backoff". + */ + backoff(): void + /** * Mark the pool as "ready", allowing checkOuts to resume and connections to be created in the background. * A pool can only transition from "paused" to "ready". A "closed" pool @@ -829,6 +848,34 @@ interface PoolClearedEvent { interruptInUseConnections: Optional; } +/** + * Emitted when a Connection Pool is in backoff + */ +interface PoolBackoffEvent { + /** + * The ServerAddress of the Endpoint the pool is attempting to connect to. + */ + address: string; + + /** + * The backoff attempt number. + * + * The incrementing backoff attempt number. This is included because + * the backoff duration is non-deterministic due to jitter. + */ + attempt: int64; + + /** + * The duration the pool will not allow new connection establishments. + * + * A driver MAY choose the type idiomatic to the driver. + * If the type chosen does not convey units, e.g., `int64`, + * then the driver MAY include units in the name, e.g., `durationMS`. + */ + duration: Duration; +} + + /** * Emitted when a Connection Pool is closed */ @@ -1074,6 +1121,21 @@ placeholders as appropriate: > Connection pool for {{serverHost}}:{{serverPort}} cleared for serviceId {{serviceId}} +#### Pool Backoff Message + +In addition to the common fields defined above, this message MUST contain the following key-value pairs: + +| Key | Suggested Type | Value | +| ---------- | -------------- | ---------------------------- | +| message | String | "Connection pool in backoff" | +| attempt | Int | The backoff attempt number. | +| durationMS | Int | Int32/Int64/Double | + +The unstructured form SHOULD be as follows, using the values defined in the structured format above to fill in +placeholders as appropriate: + +> Connection pool for {{serverHost}}:{{serverPort}} in backoff. Attempt: {{attempt}}. Duration: {{durationMS}} ms + #### Pool Closed Message In addition to the common fields defined above, this message MUST contain the following key-value pairs: @@ -1375,6 +1437,8 @@ to close and remove from its pool a [Connection](#connection) which has unread e ## Changelog +- 2025-XX-YY: Introduce "backoff" state. + - 2025-01-22: Clarify durationMS in logs may be Int32/Int64/Double. - 2024-11-27: Relaxed the WaitQueue fairness requirement. diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/README.md b/source/connection-monitoring-and-pooling/tests/cmap-format/README.md index ced96961f5..740e8b4e5d 100644 --- a/source/connection-monitoring-and-pooling/tests/cmap-format/README.md +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/README.md @@ -74,6 +74,7 @@ Valid Unit Test Operations are the following: - `interruptInUseConnections`: Determines whether "in use" connections should be also interrupted - `pool.close()`: call `close` on Pool - `pool.ready()`: call `ready` on Pool +- `pool.backoff()`: call `backoff` on Pool ## Integration Test Format @@ -88,6 +89,8 @@ The integration test format is identical to the unit test format with the additi - `maxServerVersion` (optional): The maximum server version (inclusive) against which the tests can be run successfully. If this field is omitted, it should be assumed that there is no upper bound on the required server version. + - `poolBackoff` (optional): If it is true, tests MUST only run if the driver supports backoff state in connection + pools. If it is false, tests MUST only run if the driver does not support backoff state in connection pools. - `failPoint`: optional, a document containing a `configureFailPoint` command to run against the endpoint being used for the test. - `poolOptions.appName` (optional): appName attribute to be set in connections, which will be affected by the fail diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-connection-close.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-connection-close.json new file mode 100644 index 0000000000..571cd9f769 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-connection-close.json @@ -0,0 +1,72 @@ +{ + "version": 1, + "style": "integration", + "description": "pool enters backoff on connection close", + "runOn": [ + { + "minServerVersion": "4.9.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true + } + }, + "poolOptions": { + "minPoolSize": 0 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCreated", + "count": 1 + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 1 + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted" + }, + { + "type": "ConnectionCreated" + }, + { + "type": "ConnectionClosed" + }, + { + "type": "ConnectionPoolBackoff" + }, + { + "type": "ConnectionCheckOutFailed" + } + ], + "ignore": [ + "ConnectionCheckedIn", + "ConnectionCheckedOut", + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-connection-close.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-connection-close.yml new file mode 100644 index 0000000000..20a086007e --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-connection-close.yml @@ -0,0 +1,39 @@ +version: 1 +style: integration +description: pool enters backoff on connection close +runOn: + - minServerVersion: 4.9.0 +failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - isMaster + - hello + closeConnection: true +poolOptions: + minPoolSize: 0 +operations: + - name: ready + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: waitForEvent + event: ConnectionCreated + count: 1 + - name: waitForEvent + event: ConnectionCheckOutFailed + count: 1 +events: + - type: ConnectionCheckOutStarted + - type: ConnectionCreated + - type: ConnectionClosed + - type: ConnectionPoolBackoff + - type: ConnectionCheckOutFailed +ignore: + - ConnectionCheckedIn + - ConnectionCheckedOut + - ConnectionPoolCreated + - ConnectionPoolReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-interrupting-pending-connections.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-interrupting-pending-connections.json new file mode 100644 index 0000000000..808f51838c --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-interrupting-pending-connections.json @@ -0,0 +1,76 @@ +{ + "version": 1, + "style": "integration", + "description": "backoff closes pending connections", + "runOn": [ + { + "minServerVersion": "4.9.0", + "poolBackoff": true + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": false, + "blockConnection": true, + "blockTimeMS": 10000 + } + }, + "poolOptions": { + "minPoolSize": 0 + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCreated", + "count": 1 + }, + { + "name": "backoff" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 1 + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted" + }, + { + "type": "ConnectionCreated" + }, + { + "type": "ConnectionPoolBackoff" + }, + { + "type": "ConnectionClosed" + }, + { + "type": "ConnectionCheckOutFailed" + } + ], + "ignore": [ + "ConnectionCheckedIn", + "ConnectionCheckedOut", + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-interrupting-pending-connections.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-interrupting-pending-connections.yml new file mode 100644 index 0000000000..e35b8cc3e7 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-backoff-interrupting-pending-connections.yml @@ -0,0 +1,42 @@ +version: 1 +style: integration +description: backoff closes pending connections +runOn: + - minServerVersion: 4.9.0 + poolBackoff: true +failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + closeConnection: false + blockConnection: true + blockTimeMS: 10000 +poolOptions: + minPoolSize: 0 +operations: + - name: ready + - name: start + target: thread1 + - name: checkOut + thread: thread1 + - name: waitForEvent + event: ConnectionCreated + count: 1 + - name: backoff + - name: waitForEvent + event: ConnectionCheckOutFailed + count: 1 +events: + - type: ConnectionCheckOutStarted + - type: ConnectionCreated + - type: ConnectionPoolBackoff + - type: ConnectionClosed + - type: ConnectionCheckOutFailed +ignore: + - ConnectionCheckedIn + - ConnectionCheckedOut + - ConnectionPoolCreated + - ConnectionPoolReady diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error-with-backoff.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error-with-backoff.json new file mode 100644 index 0000000000..a9debb8d38 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error-with-backoff.json @@ -0,0 +1,65 @@ +{ + "version": 1, + "style": "integration", + "description": "error during minPoolSize population clears pool", + "runOn": [ + { + "minServerVersion": "4.9.0", + "poolBackoff": true + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "errorCode": 18, + "appName": "poolCreateMinSizeErrorTest" + } + }, + "poolOptions": { + "minPoolSize": 1, + "backgroundThreadIntervalMS": 50, + "appName": "poolCreateMinSizeErrorTest" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "waitForEvent", + "event": "ConnectionPoolCleared", + "count": 1 + }, + { + "name": "wait", + "ms": 200 + } + ], + "events": [ + { + "type": "ConnectionPoolReady", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionClosed", + "address": 42, + "connectionId": 42, + "reason": "error" + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolCreated" + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error-with-backoff.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error-with-backoff.yml new file mode 100644 index 0000000000..547f07bfaf --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error-with-backoff.yml @@ -0,0 +1,39 @@ +version: 1 +style: integration +description: error during minPoolSize population clears pool +runOn: + - minServerVersion: 4.9.0 + poolBackoff: true +failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + errorCode: 18 + appName: poolCreateMinSizeErrorTest +poolOptions: + minPoolSize: 1 + backgroundThreadIntervalMS: 50 + appName: poolCreateMinSizeErrorTest +operations: + - name: ready + - name: waitForEvent + event: ConnectionPoolCleared + count: 1 + - name: wait + ms: 200 +events: + - type: ConnectionPoolReady + address: 42 + - type: ConnectionCreated + address: 42 + - type: ConnectionClosed + address: 42 + connectionId: 42 + reason: error + - type: ConnectionPoolCleared + address: 42 +ignore: + - ConnectionPoolCreated diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json index 509b2a2356..b1bca0c3fd 100644 --- a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.json @@ -4,7 +4,8 @@ "description": "error during minPoolSize population clears pool", "runOn": [ { - "minServerVersion": "4.9.0" + "minServerVersion": "4.9.0", + "poolBackoff": false } ], "failPoint": { diff --git a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml index f43c4ee154..5428dd1e3e 100644 --- a/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml +++ b/source/connection-monitoring-and-pooling/tests/cmap-format/pool-create-min-size-error.yml @@ -2,27 +2,27 @@ version: 1 style: integration description: error during minPoolSize population clears pool runOn: - - - # required for appName in fail point - minServerVersion: "4.9.0" + - minServerVersion: 4.9.0 + poolBackoff: false failPoint: configureFailPoint: failCommand - # high amount to ensure not interfered with by monitor checks. - mode: { times: 50 } + mode: + times: 50 data: - failCommands: ["isMaster","hello"] + failCommands: + - isMaster + - hello closeConnection: true - appName: "poolCreateMinSizeErrorTest" + appName: poolCreateMinSizeErrorTest poolOptions: minPoolSize: 1 backgroundThreadIntervalMS: 50 - appName: "poolCreateMinSizeErrorTest" + appName: poolCreateMinSizeErrorTest operations: - name: ready - name: waitForEvent event: ConnectionPoolCleared count: 1 - # ensure pool doesn't start making new connections - name: wait ms: 200 events: diff --git a/source/connection-monitoring-and-pooling/tests/logging/connection-logging-pool-backoff.json b/source/connection-monitoring-and-pooling/tests/logging/connection-logging-pool-backoff.json new file mode 100644 index 0000000000..6dcad2824d --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/logging/connection-logging-pool-backoff.json @@ -0,0 +1,379 @@ +{ + "description": "connection-logging", + "schemaVersion": "1.28", + "runOnRequirements": [ + { + "topologies": [ + "single" + ], + "poolBackoff": true + } + ], + "createEntities": [ + { + "client": { + "id": "failPointClient" + } + } + ], + "tests": [ + { + "description": "Connection enters backoff on closed connection", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "poolBackoff": true + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "retryReads": true, + "appname": "clientAppName", + "heartbeatFrequencyMS": 5000 + }, + "observeEvents": [ + "serverHeartbeatSucceededEvent" + ], + "observeLogMessages": { + "connection": "debug" + } + } + }, + { + "database": { + "id": "database0", + "client": "client", + "databaseName": "ci-tests" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "clientAppName" + } + } + } + }, + { + "object": "database0", + "name": "runCommand", + "arguments": { + "command": { + "find": "test" + }, + "commandName": "find" + } + } + ], + "expectLogMessages": [ + { + "client": "client", + "messages": [ + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool created", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool ready", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout started", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection created", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection closed", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "An error occurred while using the connection", + "error": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection pool backoff", + "serverHost": { + "$$type": "string" + }, + "durationMS": { + "$$type": "int" + }, + "attempt": { + "$$type": "int" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "Connection pool is in backoff", + "error": { + "$$exists": true + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout failed", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "reason": "An error occurred while trying to establish a new connection", + "error": { + "$$exists": true + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checkout started", + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection created", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection ready", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checked out", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + }, + "durationMS": { + "$$type": [ + "double", + "int", + "long" + ] + } + } + }, + { + "level": "debug", + "component": "connection", + "data": { + "message": "Connection checked in", + "driverConnectionId": { + "$$type": [ + "int", + "long" + ] + }, + "serverHost": { + "$$type": "string" + }, + "serverPort": { + "$$type": [ + "int", + "long" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/source/connection-monitoring-and-pooling/tests/logging/connection-logging-pool-backoff.yml b/source/connection-monitoring-and-pooling/tests/logging/connection-logging-pool-backoff.yml new file mode 100644 index 0000000000..a966b8c0c7 --- /dev/null +++ b/source/connection-monitoring-and-pooling/tests/logging/connection-logging-pool-backoff.yml @@ -0,0 +1,235 @@ +description: connection-logging +schemaVersion: "1.28" +runOnRequirements: + - topologies: + - single + poolBackoff: true +createEntities: + - client: + id: failPointClient +tests: + - description: Connection enters backoff on closed connection + runOnRequirements: + - minServerVersion: "4.4" + poolBackoff: true + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + uriOptions: + retryReads: true + appname: clientAppName + heartbeatFrequencyMS: 5000 + observeEvents: + - serverHeartbeatSucceededEvent + observeLogMessages: + connection: debug + - database: + id: database0 + client: client + databaseName: ci-tests + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - isMaster + - hello + closeConnection: true + appName: clientAppName + - object: database0 + name: runCommand + arguments: + command: + find: test + commandName: find + expectLogMessages: + - client: client + messages: + - level: debug + component: connection + data: + message: Connection pool created + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + - level: debug + component: connection + data: + message: Connection pool ready + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + - level: debug + component: connection + data: + message: Connection checkout started + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + - level: debug + component: connection + data: + message: Connection created + driverConnectionId: + $$type: + - int + - long + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + - level: debug + component: connection + data: + message: Connection closed + driverConnectionId: + $$type: + - int + - long + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + reason: An error occurred while using the connection + error: + $$exists: true + - level: debug + component: connection + data: + message: Connection pool backoff + serverHost: + $$type: string + durationMS: + $$type: int + attempt: + $$type: int + serverPort: + $$type: + - int + - long + reason: Connection pool is in backoff + error: + $$exists: true + - level: debug + component: connection + data: + message: Connection checkout failed + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + reason: An error occurred while trying to establish a new connection + error: + $$exists: true + durationMS: + $$type: + - double + - int + - long + - level: debug + component: connection + data: + message: Connection checkout started + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + - level: debug + component: connection + data: + message: Connection created + driverConnectionId: + $$type: + - int + - long + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + - level: debug + component: connection + data: + message: Connection ready + driverConnectionId: + $$type: + - int + - long + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + durationMS: + $$type: + - double + - int + - long + - level: debug + component: connection + data: + message: Connection checked out + driverConnectionId: + $$type: + - int + - long + serverHost: + $$type: string + serverPort: + $$type: + - int + - long + durationMS: + $$type: + - double + - int + - long + - level: debug + component: connection + data: + message: Connection checked in + driverConnectionId: + $$type: + - int + - long + serverHost: + $$type: string + serverPort: + $$type: + - int + - long diff --git a/source/connection-monitoring-and-pooling/tests/logging/connection-logging.yml b/source/connection-monitoring-and-pooling/tests/logging/connection-logging.yml index 49868a062e..435b4b8fc8 100644 --- a/source/connection-monitoring-and-pooling/tests/logging/connection-logging.yml +++ b/source/connection-monitoring-and-pooling/tests/logging/connection-logging.yml @@ -221,4 +221,4 @@ tests: serverPort: { $$type: [int, long] } reason: "An error occurred while trying to establish a new connection" error: { $$exists: true } - durationMS: { $$type: [double, int, long] } + durationMS: { $$type: [double, int, long] } \ No newline at end of file diff --git a/source/load-balancers/tests/sdam-error-handling-pool-backoff.json b/source/load-balancers/tests/sdam-error-handling-pool-backoff.json new file mode 100644 index 0000000000..fa04b03bfb --- /dev/null +++ b/source/load-balancers/tests/sdam-error-handling-pool-backoff.json @@ -0,0 +1,541 @@ +{ + "description": "state change errors are correctly handled", + "schemaVersion": "1.28", + "runOnRequirements": [ + { + "topologies": [ + "load-balanced" + ], + "poolBackoff": true + } + ], + "_yamlAnchors": { + "observedEvents": [ + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent" + ] + }, + "createEntities": [ + { + "client": { + "id": "failPointClient", + "useMultipleMongoses": false + } + }, + { + "client": { + "id": "singleClient", + "useMultipleMongoses": false, + "uriOptions": { + "appname": "lbSDAMErrorTestClient", + "retryWrites": false + }, + "observeEvents": [ + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent", + "poolBackoffEvent" + ] + } + }, + { + "database": { + "id": "singleDB", + "client": "singleClient", + "databaseName": "singleDB" + } + }, + { + "collection": { + "id": "singleColl", + "database": "singleDB", + "collectionName": "singleColl" + } + }, + { + "client": { + "id": "multiClient", + "useMultipleMongoses": true, + "uriOptions": { + "retryWrites": false + }, + "observeEvents": [ + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent" + ] + } + }, + { + "database": { + "id": "multiDB", + "client": "multiClient", + "databaseName": "multiDB" + } + }, + { + "collection": { + "id": "multiColl", + "database": "multiDB", + "collectionName": "multiColl" + } + } + ], + "initialData": [ + { + "collectionName": "singleColl", + "databaseName": "singleDB", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + }, + { + "collectionName": "multiColl", + "databaseName": "multiDB", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + }, + { + "_id": 3 + } + ] + } + ], + "tests": [ + { + "description": "only connections for a specific serviceId are closed when pools are cleared", + "runOnRequirements": [ + { + "serverless": "forbid" + } + ], + "operations": [ + { + "name": "createFindCursor", + "object": "multiColl", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "createFindCursor", + "object": "multiColl", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor1" + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "close", + "object": "cursor1" + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "multiClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "insert" + ], + "errorCode": 11600 + } + } + } + }, + { + "name": "insertOne", + "object": "multiColl", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "errorCode": 11600 + } + }, + { + "name": "insertOne", + "object": "multiColl", + "arguments": { + "document": { + "x": 1 + } + } + } + ], + "expectEvents": [ + { + "client": "multiClient", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "stale" + } + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "errors during the initial connection hello trigger backoff", + "runOnRequirements": [ + { + "minServerVersion": "4.4.7" + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "lbSDAMErrorTestClient" + } + } + } + }, + { + "name": "insertOne", + "object": "singleColl", + "arguments": { + "document": { + "x": 1 + } + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "singleClient", + "event": { + "poolBackoffEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "singleClient", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "poolBackoffEvent": { + "attempt": 1 + } + }, + { + "connectionCheckOutFailedEvent": { + "reason": "connectionError" + } + }, + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCheckedInEvent": {} + } + ] + } + ] + }, + { + "description": "errors during authentication are processed", + "runOnRequirements": [ + { + "auth": true + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "closeConnection": true, + "appName": "lbSDAMErrorTestClient" + } + } + } + }, + { + "name": "insertOne", + "object": "singleColl", + "arguments": { + "document": { + "x": 1 + } + }, + "expectError": { + "isClientError": true + } + } + ], + "expectEvents": [ + { + "client": "singleClient", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "connectionCheckOutFailedEvent": { + "reason": "connectionError" + } + }, + { + "poolClearedEvent": {} + } + ] + } + ] + }, + { + "description": "stale errors are ignored", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "failPointClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "getMore" + ], + "closeConnection": true + } + } + } + }, + { + "name": "createFindCursor", + "object": "singleColl", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor0" + }, + { + "name": "createFindCursor", + "object": "singleColl", + "arguments": { + "filter": {}, + "batchSize": 2 + }, + "saveResultAsEntity": "cursor1" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor0", + "expectError": { + "isClientError": true + } + }, + { + "name": "close", + "object": "cursor0" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor1" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor1" + }, + { + "name": "iterateUntilDocumentOrError", + "object": "cursor1", + "expectError": { + "isClientError": true + } + }, + { + "name": "close", + "object": "cursor1" + } + ], + "expectEvents": [ + { + "client": "singleClient", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": {} + } + ] + } + ] + } + ] +} diff --git a/source/load-balancers/tests/sdam-error-handling-pool-backoff.yml b/source/load-balancers/tests/sdam-error-handling-pool-backoff.yml new file mode 100644 index 0000000000..bb3f0f7df4 --- /dev/null +++ b/source/load-balancers/tests/sdam-error-handling-pool-backoff.yml @@ -0,0 +1,282 @@ +description: state change errors are correctly handled +schemaVersion: "1.28" +runOnRequirements: + - topologies: + - load-balanced + poolBackoff: true +_yamlAnchors: + observedEvents: + - connectionCreatedEvent + - connectionReadyEvent + - connectionCheckedOutEvent + - connectionCheckOutFailedEvent + - connectionCheckedInEvent + - connectionClosedEvent + - poolClearedEvent +createEntities: + - client: + id: failPointClient + useMultipleMongoses: false + - client: + id: singleClient + useMultipleMongoses: false + uriOptions: + appname: lbSDAMErrorTestClient + retryWrites: false + observeEvents: + - connectionCreatedEvent + - connectionReadyEvent + - connectionCheckedOutEvent + - connectionCheckOutFailedEvent + - connectionCheckedInEvent + - connectionClosedEvent + - poolClearedEvent + - poolBackoffEvent + - database: + id: singleDB + client: singleClient + databaseName: singleDB + - collection: + id: singleColl + database: singleDB + collectionName: singleColl + - client: + id: multiClient + useMultipleMongoses: true + uriOptions: + retryWrites: false + observeEvents: + - connectionCreatedEvent + - connectionReadyEvent + - connectionCheckedOutEvent + - connectionCheckOutFailedEvent + - connectionCheckedInEvent + - connectionClosedEvent + - poolClearedEvent + - database: + id: multiDB + client: multiClient + databaseName: multiDB + - collection: + id: multiColl + database: multiDB + collectionName: multiColl +initialData: + - collectionName: singleColl + databaseName: singleDB + documents: + - _id: 1 + - _id: 2 + - _id: 3 + - collectionName: multiColl + databaseName: multiDB + documents: + - _id: 1 + - _id: 2 + - _id: 3 +tests: + - description: only connections for a specific serviceId are closed when pools are cleared + runOnRequirements: + - serverless: forbid + operations: + - name: createFindCursor + object: multiColl + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: cursor0 + - name: createFindCursor + object: multiColl + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: cursor1 + - name: close + object: cursor0 + - name: close + object: cursor1 + - name: failPoint + object: testRunner + arguments: + client: multiClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - insert + errorCode: 11600 + - name: insertOne + object: multiColl + arguments: + document: + x: 1 + expectError: + errorCode: 11600 + - name: insertOne + object: multiColl + arguments: + document: + x: 1 + expectEvents: + - client: multiClient + eventType: cmap + events: + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - connectionCheckedInEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - connectionCheckedOutEvent: {} + - poolClearedEvent: {} + - connectionCheckedInEvent: {} + - connectionClosedEvent: + reason: stale + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - description: errors during the initial connection hello trigger backoff + runOnRequirements: + - minServerVersion: 4.4.7 + operations: + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - isMaster + - hello + closeConnection: true + appName: lbSDAMErrorTestClient + - name: insertOne + object: singleColl + arguments: + document: + x: 1 + - name: waitForEvent + object: testRunner + arguments: + client: singleClient + event: + poolBackoffEvent: {} + count: 1 + expectEvents: + - client: singleClient + eventType: cmap + events: + - connectionCreatedEvent: {} + - connectionClosedEvent: + reason: error + - poolBackoffEvent: + attempt: 1 + - connectionCheckOutFailedEvent: + reason: connectionError + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCheckedInEvent: {} + - description: errors during authentication are processed + runOnRequirements: + - auth: true + operations: + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - saslContinue + closeConnection: true + appName: lbSDAMErrorTestClient + - name: insertOne + object: singleColl + arguments: + document: + x: 1 + expectError: + isClientError: true + expectEvents: + - client: singleClient + eventType: cmap + events: + - connectionCreatedEvent: {} + - connectionClosedEvent: + reason: error + - connectionCheckOutFailedEvent: + reason: connectionError + - poolClearedEvent: {} + - description: stale errors are ignored + operations: + - name: failPoint + object: testRunner + arguments: + client: failPointClient + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - getMore + closeConnection: true + - name: createFindCursor + object: singleColl + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: cursor0 + - name: createFindCursor + object: singleColl + arguments: + filter: {} + batchSize: 2 + saveResultAsEntity: cursor1 + - name: iterateUntilDocumentOrError + object: cursor0 + - name: iterateUntilDocumentOrError + object: cursor0 + - name: iterateUntilDocumentOrError + object: cursor0 + expectError: + isClientError: true + - name: close + object: cursor0 + - name: iterateUntilDocumentOrError + object: cursor1 + - name: iterateUntilDocumentOrError + object: cursor1 + - name: iterateUntilDocumentOrError + object: cursor1 + expectError: + isClientError: true + - name: close + object: cursor1 + expectEvents: + - client: singleClient + eventType: cmap + events: + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - connectionCreatedEvent: {} + - connectionReadyEvent: {} + - connectionCheckedOutEvent: {} + - poolClearedEvent: {} + - connectionCheckedInEvent: {} + - connectionClosedEvent: {} + - connectionCheckedInEvent: {} + - connectionClosedEvent: {} diff --git a/source/load-balancers/tests/sdam-error-handling.json b/source/load-balancers/tests/sdam-error-handling.json index 47323fae4f..425144a9db 100644 --- a/source/load-balancers/tests/sdam-error-handling.json +++ b/source/load-balancers/tests/sdam-error-handling.json @@ -1,11 +1,12 @@ { "description": "state change errors are correctly handled", - "schemaVersion": "1.4", + "schemaVersion": "1.28", "runOnRequirements": [ { "topologies": [ "load-balanced" - ] + ], + "poolBackoff": false } ], "_yamlAnchors": { diff --git a/source/load-balancers/tests/sdam-error-handling.yml b/source/load-balancers/tests/sdam-error-handling.yml index b81d811dc8..98a4e01bb5 100644 --- a/source/load-balancers/tests/sdam-error-handling.yml +++ b/source/load-balancers/tests/sdam-error-handling.yml @@ -1,9 +1,10 @@ description: state change errors are correctly handled -schemaVersion: '1.4' +schemaVersion: '1.28' runOnRequirements: - topologies: [ load-balanced ] + poolBackoff: false _yamlAnchors: observedEvents: &observedEvents @@ -270,4 +271,4 @@ tests: # getMore should not clear the pool because the connection's # generation number is stale. - connectionCheckedInEvent: {} - - connectionClosedEvent: {} + - connectionClosedEvent: {} \ No newline at end of file diff --git a/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md b/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md index 206e8236c3..395d7bc795 100644 --- a/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md +++ b/source/server-discovery-and-monitoring/server-discovery-and-monitoring.md @@ -1056,6 +1056,10 @@ def handleError(error): if isNotWritablePrimary(error): check failing server elif isNetworkError(error) or (not error.completedHandshake and (isNetworkTimeout(error) or isAuthError(error))): + # Ignore network errors and network timeout errors during TLS handshake or "hello" messages. + # These will be handled by the pool backoff. + if error.occurredDuringHello or error.occurredDuringTLSHandshake: + continue if type != LoadBalanced # Mark the server Unknown unknown = new ServerDescription(type=Unknown, error=error) @@ -1169,8 +1173,9 @@ TopologyType is ReplicaSetWithPrimary: referring to the table above we see the s [checkIfHasPrimary](#checkifhasprimary). The result is the TopologyType changes to ReplicaSetNoPrimary. See the test scenario called "Network error writing to primary". -The client MUST close all idle sockets in its connection pool for the server: if one socket is bad, it is likely that -all are. +The clients MUST NOT clear the connection pool when a connection TLS handshake or MongoDB handshake fail with network +errors or timeouts. If the network error or timeout occurs during TCP connection establishment, DNS lookup, or during +the authentication step, then client MUST close all idle sockets in its connection pool for the server. Clients MUST NOT request an immediate check of the server; since application sockets are used frequently, a network error likely means the server has just become unavailable, so an immediate refresh is likely to get a network error, @@ -2027,6 +2032,8 @@ oversaw the specification process. - 2025-01-22: Add error messages when a new primary is elected or a primary with a stale electionId or setVersion is discovered. +- 2025-XX-YY: Add support for pool backoff state. + ______________________________________________________________________ [^1]: "localThresholdMS" was called "secondaryAcceptableLatencyMS" in the Read Preferences Spec, before it was superseded diff --git a/source/server-discovery-and-monitoring/server-monitoring.md b/source/server-discovery-and-monitoring/server-monitoring.md index 5f1be7fa04..58fa43c7ba 100644 --- a/source/server-discovery-and-monitoring/server-monitoring.md +++ b/source/server-discovery-and-monitoring/server-monitoring.md @@ -482,7 +482,8 @@ When a monitor completes a successful check against a server, it MUST mark the c "ready", and doing so MUST be synchronized with the update to the topology (e.g. by marking the pool as ready in onServerDescriptionChanged). This is required to ensure a server does not get selected while its pool is still paused. See the [Connection Pool](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#connection-pool) -definition in the CMAP specification for more details on marking the pool as "ready". +definition in the CMAP specification for more details on marking the pool as "ready". If the pool is in "backoff" state, +the monitor MUST NOT mark the connection pool as "ready". ### Error handling @@ -971,6 +972,8 @@ outdated or inaccurate. ## Changelog +- 2025-XX-YY: Add support for pool "backoff" state. + - 2024-05-02: Migrated from reStructuredText to Markdown. - 2020-02-20: Extracted server monitoring from SDAM into this new spec. diff --git a/source/server-discovery-and-monitoring/tests/unified/backoff-heartbeat-failure.json b/source/server-discovery-and-monitoring/tests/unified/backoff-heartbeat-failure.json new file mode 100644 index 0000000000..aa253edc71 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backoff-heartbeat-failure.json @@ -0,0 +1,161 @@ +{ + "description": "heartbeat-failure-clears-backoff-pool", + "schemaVersion": "1.28", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "poolBackoff": true, + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "heartbeat-backoff-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "A heartbeat failure during backoff should clear the pool", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "commandStartedEvent", + "poolBackoffEvent", + "poolClearedEvent", + "serverHeartbeatFailedEvent", + "serverHeartbeatSucceededEvent" + ], + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 10000, + "serverMonitoringMode": "poll", + "appname": "heartbeatBackoffFailTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "heartbeat-backoff-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "appName": "heartbeatBackoffFailTest", + "closeConnection": true + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolBackoffEvent": { + "attempt": 1 + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatFailedEvent": {} + }, + "count": 1 + } + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backoff-heartbeat-failure.yml b/source/server-discovery-and-monitoring/tests/unified/backoff-heartbeat-failure.yml new file mode 100644 index 0000000000..9b23d54093 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backoff-heartbeat-failure.yml @@ -0,0 +1,95 @@ +description: heartbeat-failure-clears-backoff-pool +schemaVersion: "1.28" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + poolBackoff: true + topologies: + - single + - replicaset + - sharded +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +initialData: + - collectionName: heartbeat-backoff-error + databaseName: sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: A heartbeat failure during backoff should clear the pool + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - commandStartedEvent + - poolBackoffEvent + - poolClearedEvent + - serverHeartbeatFailedEvent + - serverHeartbeatSucceededEvent + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 10000 + serverMonitoringMode: poll + appname: heartbeatBackoffFailTest + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: heartbeat-backoff-error + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + appName: heartbeatBackoffFailTest + closeConnection: true + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + poolBackoffEvent: + attempt: 1 + count: 1 + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatFailedEvent: {} + count: 1 diff --git a/source/server-discovery-and-monitoring/tests/unified/backoff-network-error-fail.json b/source/server-discovery-and-monitoring/tests/unified/backoff-network-error-fail.json new file mode 100644 index 0000000000..648008e944 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backoff-network-error-fail.json @@ -0,0 +1,332 @@ +{ + "description": "backoff-network-error-fail", + "schemaVersion": "1.28", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "poolBackoff": true, + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "backoff-network-error-fail", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Backoff and fail after network connection errors during connection establishment", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatSucceededEvent", + "commandStartedEvent", + "poolBackoffEvent", + "poolReadyEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 10000, + "serverMonitoringMode": "poll", + "appname": "backoffNetworkErrorFailTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "backoff-network-error-fail" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "appName": "backoffNetworkErrorFailTest", + "closeConnection": true + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolBackoffEvent": { + "attempt": 1 + } + }, + "count": 1 + } + } + ] + }, + { + "description": "Backoff and clear the pool after network failures followed by server error", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatSucceededEvent", + "commandStartedEvent", + "poolBackoffEvent", + "poolReadyEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 10000, + "serverMonitoringMode": "poll", + "appname": "backoffNetworkErrorFailClearTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "backoff-network-error-fail-clear" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "alwaysOn", + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "appName": "backoffNetworkErrorFailClearTest", + "closeConnection": true + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolBackoffEvent": { + "attempt": 1 + } + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": "off" + } + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "backoffNetworkErrorFailClearTest", + "errorCode": 1 + } + } + } + }, + { + "name": "insertOne", + "object": "collection", + "arguments": { + "document": { + "_id": 3 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolClearedEvent": {} + }, + "count": 1 + } + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backoff-network-error-fail.yml b/source/server-discovery-and-monitoring/tests/unified/backoff-network-error-fail.yml new file mode 100644 index 0000000000..2d76b300cd --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backoff-network-error-fail.yml @@ -0,0 +1,198 @@ +description: backoff-network-error-fail +schemaVersion: "1.28" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + poolBackoff: true + topologies: + - single + - replicaset + - sharded +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +initialData: + - collectionName: backoff-network-error-fail + databaseName: sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: Backoff and fail after network connection errors during connection establishment + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - serverHeartbeatSucceededEvent + - commandStartedEvent + - poolBackoffEvent + - poolReadyEvent + - poolClearedEvent + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 10000 + serverMonitoringMode: poll + appname: backoffNetworkErrorFailTest + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: backoff-network-error-fail + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + appName: backoffNetworkErrorFailTest + closeConnection: true + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + poolBackoffEvent: + attempt: 1 + count: 1 + - description: Backoff and clear the pool after network failures followed by server error + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - serverHeartbeatSucceededEvent + - commandStartedEvent + - poolBackoffEvent + - poolReadyEvent + - poolClearedEvent + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 10000 + serverMonitoringMode: poll + appname: backoffNetworkErrorFailClearTest + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: backoff-network-error-fail-clear + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: alwaysOn + data: + failCommands: + - isMaster + - hello + appName: backoffNetworkErrorFailClearTest + closeConnection: true + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + poolBackoffEvent: + attempt: 1 + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: off + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 1 + data: + failCommands: + - hello + - isMaster + appName: backoffNetworkErrorFailClearTest + errorCode: 1 + - name: insertOne + object: collection + arguments: + document: + _id: 3 + expectError: + isError: true + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + poolClearedEvent: {} + count: 1 diff --git a/source/server-discovery-and-monitoring/tests/unified/backoff-network-error.json b/source/server-discovery-and-monitoring/tests/unified/backoff-network-error.json new file mode 100644 index 0000000000..3101451d8b --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backoff-network-error.json @@ -0,0 +1,188 @@ +{ + "description": "backoff-network-error", + "schemaVersion": "1.28", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "poolBackoff": true, + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "backoff-network-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Backoff and retry after network connection error during connection establishment", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatSucceededEvent", + "commandStartedEvent", + "poolBackoffEvent", + "poolReadyEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "appname": "backoffNetworkErrorTest", + "heartbeatFrequencyMS": 10000, + "serverMonitoringMode": "poll", + "connectTimeoutMS": 250, + "socketTimeoutMS": 250 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "backoff-network-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "backoffNetworkErrorTest" + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolBackoffEvent": { + "attempt": 1 + } + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolBackoffEvent": { + "attempt": 2 + } + }, + "count": 1 + } + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backoff-network-error.yml b/source/server-discovery-and-monitoring/tests/unified/backoff-network-error.yml new file mode 100644 index 0000000000..b7f7d34051 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backoff-network-error.yml @@ -0,0 +1,110 @@ +description: backoff-network-error +schemaVersion: "1.28" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + poolBackoff: true + topologies: + - single + - replicaset + - sharded +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +initialData: + - collectionName: backoff-network-error + databaseName: sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: Backoff and retry after network connection error during connection establishment + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - serverHeartbeatSucceededEvent + - commandStartedEvent + - poolBackoffEvent + - poolReadyEvent + - poolClearedEvent + uriOptions: + retryWrites: false + appname: backoffNetworkErrorTest + heartbeatFrequencyMS: 10000 + serverMonitoringMode: poll + connectTimeoutMS: 250 + socketTimeoutMS: 250 + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: backoff-network-error + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - isMaster + - hello + closeConnection: true + appName: backoffNetworkErrorTest + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + poolBackoffEvent: + attempt: 1 + count: 1 + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + poolBackoffEvent: + attempt: 2 + count: 1 diff --git a/source/server-discovery-and-monitoring/tests/unified/backoff-network-timeout-error.json b/source/server-discovery-and-monitoring/tests/unified/backoff-network-timeout-error.json new file mode 100644 index 0000000000..7969e4b908 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backoff-network-timeout-error.json @@ -0,0 +1,188 @@ +{ + "description": "backoff-network-timeout-error", + "schemaVersion": "1.28", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "poolBackoff": true, + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "backoff-network-timeout-error", + "databaseName": "sdam-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Backoff and retry after network timeout error during connection establishment", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "serverHeartbeatSucceededEvent", + "commandStartedEvent", + "poolBackoffEvent", + "poolReadyEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "heartbeatFrequencyMS": 10000, + "appname": "backoffNetworkTimeoutErrorTest", + "serverMonitoringMode": "poll", + "connectTimeoutMS": 250, + "socketTimeoutMS": 250 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "sdam-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "backoff-network-timeout-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "serverHeartbeatSucceededEvent": {} + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "backoffNetworkTimeoutErrorTest" + } + } + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolBackoffEvent": { + "attempt": 1 + } + }, + "count": 1 + } + }, + { + "name": "insertMany", + "object": "collection", + "arguments": { + "documents": [ + { + "_id": 3 + }, + { + "_id": 4 + } + ] + }, + "expectError": { + "isError": true, + "errorLabelsContain": [ + "SystemOverloadedError", + "RetryableError" + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolBackoffEvent": { + "attempt": 2 + } + }, + "count": 1 + } + } + ] + } + ] +} diff --git a/source/server-discovery-and-monitoring/tests/unified/backoff-network-timeout-error.yml b/source/server-discovery-and-monitoring/tests/unified/backoff-network-timeout-error.yml new file mode 100644 index 0000000000..b173c772d8 --- /dev/null +++ b/source/server-discovery-and-monitoring/tests/unified/backoff-network-timeout-error.yml @@ -0,0 +1,110 @@ +description: backoff-network-timeout-error +schemaVersion: "1.28" +runOnRequirements: + - minServerVersion: "4.4" + serverless: forbid + poolBackoff: true + topologies: + - single + - replicaset + - sharded +createEntities: + - client: + id: setupClient + useMultipleMongoses: false +initialData: + - collectionName: backoff-network-timeout-error + databaseName: sdam-tests + documents: + - _id: 1 + - _id: 2 +tests: + - description: Backoff and retry after network timeout error during connection establishment + operations: + - name: createEntities + object: testRunner + arguments: + entities: + - client: + id: client + useMultipleMongoses: false + observeEvents: + - serverHeartbeatSucceededEvent + - commandStartedEvent + - poolBackoffEvent + - poolReadyEvent + - poolClearedEvent + uriOptions: + retryWrites: false + heartbeatFrequencyMS: 10000 + appname: backoffNetworkTimeoutErrorTest + serverMonitoringMode: poll + connectTimeoutMS: 250 + socketTimeoutMS: 250 + - database: + id: database + client: client + databaseName: sdam-tests + - collection: + id: collection + database: database + collectionName: backoff-network-timeout-error + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + serverHeartbeatSucceededEvent: {} + count: 1 + - name: failPoint + object: testRunner + arguments: + client: setupClient + failPoint: + configureFailPoint: failCommand + mode: + times: 2 + data: + failCommands: + - isMaster + - hello + closeConnection: true + appName: backoffNetworkTimeoutErrorTest + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + poolBackoffEvent: + attempt: 1 + count: 1 + - name: insertMany + object: collection + arguments: + documents: + - _id: 3 + - _id: 4 + expectError: + isError: true + errorLabelsContain: + - SystemOverloadedError + - RetryableError + - name: waitForEvent + object: testRunner + arguments: + client: client + event: + poolBackoffEvent: + attempt: 2 + count: 1 diff --git a/source/unified-test-format/schema-1.28.json b/source/unified-test-format/schema-1.28.json new file mode 100644 index 0000000000..f133df24af --- /dev/null +++ b/source/unified-test-format/schema-1.28.json @@ -0,0 +1,1256 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema#", + "title": "Unified Test Format", + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "schemaVersion", + "tests" + ], + "properties": { + "description": { + "type": "string" + }, + "schemaVersion": { + "$ref": "#/definitions/version" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "createEntities": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/entity" + } + }, + "initialData": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + }, + "tests": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/test" + } + }, + "_yamlAnchors": { + "type": "object", + "additionalProperties": true + } + }, + "definitions": { + "version": { + "type": "string", + "pattern": "^[0-9]+(\\.[0-9]+){1,2}$" + }, + "runOnRequirement": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "maxServerVersion": { + "$ref": "#/definitions/version" + }, + "minServerVersion": { + "$ref": "#/definitions/version" + }, + "topologies": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "single", + "replicaset", + "sharded", + "sharded-replicaset", + "load-balanced" + ] + } + }, + "serverless": { + "type": "string", + "enum": [ + "require", + "forbid", + "allow" + ] + }, + "serverParameters": { + "type": "object", + "minProperties": 1 + }, + "auth": { + "type": "boolean" + }, + "authMechanism": { + "type": "string" + }, + "csfle": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "minLibmongocryptVersion": { + "$ref": "#/definitions/version" + } + } + } + ] + }, + "poolBackoff": { + "type": "boolean" + } + } + }, + "entity": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "client": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "uriOptions": { + "type": "object" + }, + "useMultipleMongoses": { + "type": "boolean" + }, + "observeEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "commandStartedEvent", + "commandSucceededEvent", + "commandFailedEvent", + "poolCreatedEvent", + "poolReadyEvent", + "poolClearedEvent", + "poolBackoffEvent", + "poolClosedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedOutEvent", + "connectionCheckedInEvent", + "serverDescriptionChangedEvent", + "topologyDescriptionChangedEvent", + "topologyOpeningEvent", + "topologyClosedEvent" + ] + } + }, + "ignoreCommandMonitoringEvents": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "storeEventsAsEntities": { + "deprecated": true, + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/storeEventsAsEntity" + } + }, + "observeLogMessages": { + "type": "object", + "minProperties": 1, + "additionalProperties": false, + "properties": { + "command": { + "$ref": "#/definitions/logSeverityLevel" + }, + "topology": { + "$ref": "#/definitions/logSeverityLevel" + }, + "serverSelection": { + "$ref": "#/definitions/logSeverityLevel" + }, + "connection": { + "$ref": "#/definitions/logSeverityLevel" + } + } + }, + "observeTracingMessages": { + "type": "object", + "additionalProperties": false, + "properties": { + "enableCommandPayload": { + "type": "boolean" + } + } + }, + "serverApi": { + "$ref": "#/definitions/serverApi" + }, + "observeSensitiveCommands": { + "type": "boolean" + }, + "autoEncryptOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultNamespace": { + "type": "string" + }, + "bypassAutoEncryption": { + "type": "boolean" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "schemaMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "extraOptions": { + "type": "object" + }, + "encryptedFieldsMap": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "bypassQueryAnalysis": { + "type": "boolean" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "awaitMinPoolSizeMS": { + "type": "integer" + } + } + }, + "clientEncryption": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "clientEncryptionOpts" + ], + "properties": { + "id": { + "type": "string" + }, + "clientEncryptionOpts": { + "$ref": "#/definitions/clientEncryptionOpts" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client", + "databaseName" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "databaseOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "collection": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database", + "collectionName" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "collectionName": { + "type": "string" + }, + "collectionOptions": { + "$ref": "#/definitions/collectionOrDatabaseOptions" + } + } + }, + "session": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "client" + ], + "properties": { + "id": { + "type": "string" + }, + "client": { + "type": "string" + }, + "sessionOptions": { + "type": "object" + } + } + }, + "bucket": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "database" + ], + "properties": { + "id": { + "type": "string" + }, + "database": { + "type": "string" + }, + "bucketOptions": { + "type": "object" + } + } + }, + "thread": { + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + } + } + } + } + }, + "logComponent": { + "type": "string", + "enum": [ + "command", + "topology", + "serverSelection", + "connection" + ] + }, + "spanComponent": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "attributes" + ], + "properties": { + "name": { + "type": "string" + }, + "attributes": { + "type": "object" + }, + "nested": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/spanComponent" + } + } + } + }, + "logSeverityLevel": { + "type": "string", + "enum": [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "info", + "debug", + "trace" + ] + }, + "clientEncryptionOpts": { + "type": "object", + "additionalProperties": false, + "required": [ + "keyVaultClient", + "keyVaultNamespace", + "kmsProviders" + ], + "properties": { + "keyVaultClient": { + "type": "string" + }, + "keyVaultNamespace": { + "type": "string" + }, + "kmsProviders": { + "$ref": "#/definitions/kmsProviders" + }, + "keyExpirationMS": { + "type": "integer" + } + } + }, + "kmsProviders": { + "$defs": { + "stringOrPlaceholder": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "$$placeholder" + ], + "properties": { + "$$placeholder": {} + } + } + ] + } + }, + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^aws(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "accessKeyId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "secretAccessKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "sessionToken": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^azure(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "tenantId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientId": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "clientSecret": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "identityPlatformEndpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^gcp(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "privateKey": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + }, + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^kmip(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + }, + "^local(:[a-zA-Z0-9_]+)?$": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "$ref": "#/definitions/kmsProviders/$defs/stringOrPlaceholder" + } + } + } + } + }, + "storeEventsAsEntity": { + "deprecated": true, + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "events" + ], + "properties": { + "id": { + "type": "string" + }, + "events": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "PoolCreatedEvent", + "PoolReadyEvent", + "PoolClearedEvent", + "PoolBackoffEvent", + "PoolClosedEvent", + "ConnectionCreatedEvent", + "ConnectionReadyEvent", + "ConnectionClosedEvent", + "ConnectionCheckOutStartedEvent", + "ConnectionCheckOutFailedEvent", + "ConnectionCheckedOutEvent", + "ConnectionCheckedInEvent", + "CommandStartedEvent", + "CommandSucceededEvent", + "CommandFailedEvent", + "ServerDescriptionChangedEvent", + "TopologyDescriptionChangedEvent" + ] + } + } + } + }, + "collectionData": { + "type": "object", + "additionalProperties": false, + "required": [ + "collectionName", + "databaseName", + "documents" + ], + "properties": { + "collectionName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "createOptions": { + "type": "object", + "properties": { + "writeConcern": false + } + }, + "documents": { + "type": "array", + "items": { + "type": "object" + } + } + } + }, + "expectedEventsForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "events" + ], + "properties": { + "client": { + "type": "string" + }, + "eventType": { + "type": "string", + "enum": [ + "command", + "cmap", + "sdam" + ] + }, + "events": { + "type": "array" + }, + "ignoreExtraEvents": { + "type": "boolean" + } + }, + "oneOf": [ + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "command" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "cmap" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCmapEvent" + } + } + } + }, + { + "required": [ + "eventType" + ], + "properties": { + "eventType": { + "const": "sdam" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedSdamEvent" + } + } + } + }, + { + "additionalProperties": false, + "properties": { + "client": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedCommandEvent" + } + }, + "ignoreExtraEvents": { + "type": "boolean" + } + } + } + ] + }, + "expectedCommandEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "commandStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reply": { + "type": "object" + }, + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + }, + "commandFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "commandName": { + "type": "string" + }, + "databaseName": { + "type": "string" + }, + "hasServiceId": { + "type": "boolean" + }, + "hasServerConnectionId": { + "type": "boolean" + } + } + } + } + }, + "expectedCmapEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "poolCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "poolClearedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "hasServiceId": { + "type": "boolean" + }, + "interruptInUseConnections": { + "type": "boolean" + } + } + }, + "poolBackoffEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "attempt": { + "type": "integer" + } + } + }, + "poolClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCreatedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionReadyEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckOutStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckOutFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + } + } + }, + "connectionCheckedOutEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "connectionCheckedInEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "expectedSdamEvent": { + "type": "object", + "additionalProperties": false, + "maxProperties": 1, + "minProperties": 1, + "properties": { + "serverDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/serverDescription" + }, + "newDescription": { + "$ref": "#/definitions/serverDescription" + } + } + }, + "topologyDescriptionChangedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousDescription": { + "$ref": "#/definitions/topologyDescription" + }, + "newDescription": { + "$ref": "#/definitions/topologyDescription" + } + } + }, + "serverHeartbeatStartedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatSucceededEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "serverHeartbeatFailedEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "awaited": { + "type": "boolean" + } + } + }, + "topologyOpeningEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + }, + "topologyClosedEvent": { + "type": "object", + "additionalProperties": false, + "properties": {} + } + } + }, + "serverDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Standalone", + "Mongos", + "PossiblePrimary", + "RSPrimary", + "RSSecondary", + "RSOther", + "RSArbiter", + "RSGhost", + "LoadBalancer", + "Unknown" + ] + } + } + }, + "topologyDescription": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "Single", + "Unknown", + "ReplicaSetNoPrimary", + "ReplicaSetWithPrimary", + "Sharded", + "LoadBalanced" + ] + } + } + }, + "expectedLogMessagesForClient": { + "type": "object", + "additionalProperties": false, + "required": [ + "client", + "messages" + ], + "properties": { + "client": { + "type": "string" + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + }, + "ignoreExtraMessages": { + "type": "boolean" + }, + "ignoreMessages": { + "type": "array", + "items": { + "$ref": "#/definitions/expectedLogMessage" + } + } + } + }, + "expectTracingMessagesForClient": { + "additionalProperties": false, + "type": "object", + "required": [ + "client", + "spans" + ], + "properties": { + "client": { + "type": "string" + }, + "ignoreExtraSpans": { + "type": "boolean" + }, + "spans": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/spanComponent" + } + } + } + }, + "expectedLogMessage": { + "type": "object", + "additionalProperties": false, + "required": [ + "level", + "component", + "data" + ], + "properties": { + "level": { + "$ref": "#/definitions/logSeverityLevel" + }, + "component": { + "$ref": "#/definitions/logComponent" + }, + "data": { + "type": "object" + }, + "failureIsRedacted": { + "type": "boolean" + } + } + }, + "collectionOrDatabaseOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "readConcern": { + "type": "object" + }, + "readPreference": { + "type": "object" + }, + "writeConcern": { + "type": "object" + }, + "timeoutMS": { + "type": "integer" + } + } + }, + "serverApi": { + "type": "object", + "additionalProperties": false, + "required": [ + "version" + ], + "properties": { + "version": { + "type": "string" + }, + "strict": { + "type": "boolean" + }, + "deprecationErrors": { + "type": "boolean" + } + } + }, + "operation": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "object" + ], + "properties": { + "name": { + "type": "string" + }, + "object": { + "type": "string" + }, + "arguments": { + "type": "object" + }, + "ignoreResultAndError": { + "type": "boolean" + }, + "expectError": { + "$ref": "#/definitions/expectedError" + }, + "expectResult": {}, + "saveResultAsEntity": { + "type": "string" + } + }, + "allOf": [ + { + "not": { + "required": [ + "expectError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "expectError", + "saveResultAsEntity" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectResult" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "expectError" + ] + } + }, + { + "not": { + "required": [ + "ignoreResultAndError", + "saveResultAsEntity" + ] + } + } + ] + }, + "expectedError": { + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "isError": { + "type": "boolean", + "const": true + }, + "isClientError": { + "type": "boolean" + }, + "isTimeoutError": { + "type": "boolean" + }, + "errorContains": { + "type": "string" + }, + "errorCode": { + "type": "integer" + }, + "errorCodeName": { + "type": "string" + }, + "errorLabelsContain": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "errorLabelsOmit": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "writeErrors": { + "type": "object" + }, + "writeConcernErrors": { + "type": "array", + "items": { + "type": "object" + } + }, + "errorResponse": { + "type": "object" + }, + "expectResult": {} + } + }, + "test": { + "type": "object", + "additionalProperties": false, + "required": [ + "description", + "operations" + ], + "properties": { + "description": { + "type": "string" + }, + "runOnRequirements": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/runOnRequirement" + } + }, + "skipReason": { + "type": "string" + }, + "operations": { + "type": "array", + "items": { + "$ref": "#/definitions/operation" + } + }, + "expectEvents": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedEventsForClient" + } + }, + "expectLogMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectedLogMessagesForClient" + } + }, + "expectTracingMessages": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/expectTracingMessagesForClient" + } + }, + "outcome": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/collectionData" + } + } + } + } + } +} diff --git a/source/unified-test-format/schema-latest.json b/source/unified-test-format/schema-latest.json index d195ba5af0..f133df24af 100644 --- a/source/unified-test-format/schema-latest.json +++ b/source/unified-test-format/schema-latest.json @@ -112,6 +112,9 @@ } } ] + }, + "poolBackoff": { + "type": "boolean" } } }, @@ -149,6 +152,7 @@ "poolCreatedEvent", "poolReadyEvent", "poolClearedEvent", + "poolBackoffEvent", "poolClosedEvent", "connectionCreatedEvent", "connectionReadyEvent", @@ -553,6 +557,7 @@ "PoolCreatedEvent", "PoolReadyEvent", "PoolClearedEvent", + "PoolBackoffEvent", "PoolClosedEvent", "ConnectionCreatedEvent", "ConnectionReadyEvent", @@ -790,6 +795,15 @@ } } }, + "poolBackoffEvent": { + "type": "object", + "additionalProperties": false, + "properties": { + "attempt": { + "type": "integer" + } + } + }, "poolClosedEvent": { "type": "object", "additionalProperties": false, diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolBackoffEvent-attempt-type.json b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolBackoffEvent-attempt-type.json new file mode 100644 index 0000000000..bd76644f2e --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolBackoffEvent-attempt-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedCmapEvent-poolClearedEvent-hasServiceId-type", + "schemaVersion": "1.27", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "cmap", + "events": [ + { + "poolBackoffEvent": { + "attempt": "foo" + } + } + ] + } + ] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/expectedCmapEvent-poolBackoffEvent-attempt-type.yml b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolBackoffEvent-attempt-type.yml new file mode 100644 index 0000000000..e095db151a --- /dev/null +++ b/source/unified-test-format/tests/invalid/expectedCmapEvent-poolBackoffEvent-attempt-type.yml @@ -0,0 +1,13 @@ +description: expectedCmapEvent-poolClearedEvent-hasServiceId-type + +schemaVersion: '1.27' + +tests: + - description: foo + operations: [] + expectEvents: + - client: client0 + eventType: cmap + events: + - poolBackoffEvent: + attempt: foo diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-poolBackoff-type.json b/source/unified-test-format/tests/invalid/runOnRequirement-poolBackoff-type.json new file mode 100644 index 0000000000..9c107e55f3 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-poolBackoff-type.json @@ -0,0 +1,15 @@ +{ + "description": "runOnRequirement-auth-type", + "schemaVersion": "1.3", + "runOnRequirements": [ + { + "poolBackoff": "foo" + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/source/unified-test-format/tests/invalid/runOnRequirement-poolBackoff-type.yml b/source/unified-test-format/tests/invalid/runOnRequirement-poolBackoff-type.yml new file mode 100644 index 0000000000..da96fe23a8 --- /dev/null +++ b/source/unified-test-format/tests/invalid/runOnRequirement-poolBackoff-type.yml @@ -0,0 +1,10 @@ +description: runOnRequirement-auth-type + +schemaVersion: '1.3' + +runOnRequirements: + - poolBackoff: foo + +tests: + - description: foo + operations: [] diff --git a/source/unified-test-format/unified-test-format.md b/source/unified-test-format/unified-test-format.md index 241835e2b4..f153e4103b 100644 --- a/source/unified-test-format/unified-test-format.md +++ b/source/unified-test-format/unified-test-format.md @@ -382,6 +382,9 @@ The structure of this object is as follows: tests. If this field is omitted, there is no lower bound on the required libmongocrypt version. The format of this string is defined in [Version String](#version-string). +- `poolBackoff`: Optional boolean. If it is true, tests MUST only run if the driver supports backoff state in connection + pools. If it is false, tests MUST only run if the driver does not support backoff state in connection pools. + Test runners MAY evaluate these conditions in any order. For example, it may be more efficient to evaluate `serverless` or `auth` before communicating with a server to check its version. @@ -478,6 +481,7 @@ The structure of this object is as follows: - [poolCreatedEvent](#expectedEvent_poolCreatedEvent) - [poolReadyEvent](#expectedEvent_poolReadyEvent) - [poolClearedEvent](#expectedEvent_poolClearedEvent) + - [poolBackoffEvent](#expectedEvent_poolBackoffEvent) - [poolClosedEvent](#expectedEvent_poolClosedEvent) - [connectionCreatedEvent](#expectedEvent_connectionCreatedEvent) - [connectionReadyEvent](#expectedEvent_connectionReadyEvent) @@ -1082,6 +1086,15 @@ The structure of this object is as follows: - `interruptInUseConnections`: Optional boolean. If specified, test runners MUST assert that the field is set and matches this value. + + +- `poolBackoffEvent`: Optional object. Assertions for one or more + [PoolBackoffEvent](../connection-monitoring-and-pooling/connection-monitoring-and-pooling.md#events) fields. + + The structure of this object is as follows: + + - `attempt`: Optional integer. If specified, test runners MUST assert that the field is set and matches this value. + - `poolClosedEvent`: Optional object. If present, this object MUST be an empty document as all fields in this event are @@ -3467,6 +3480,8 @@ other specs *and* collating spec changes developed in parallel or during the sam ## Changelog +- 2025-XX-YY: **Scheam version 1.28.** Add `poolBackoffEvent`. + - 2025-10-22: Added `getSnapshotTime` operation on sessions. - 2025-09-17: **Schema version 1.27.**