@@ -4,10 +4,12 @@ import * as sinon from 'sinon';
44import {
55 type Collection ,
66 INITIAL_TOKEN_BUCKET_SIZE ,
7+ MAX_RETRIES ,
78 type MongoClient ,
89 MongoServerError
910} from '../../mongodb' ;
1011import { clearFailPoint , configureFailPoint , measureDuration } from '../../tools/utils' ;
12+ import { filterForCommands } from '../shared' ;
1113
1214describe ( 'Client Backpressure (Prose)' , function ( ) {
1315 let client : MongoClient ;
@@ -64,18 +66,141 @@ describe('Client Backpressure (Prose)', function () {
6466 }
6567 ) ;
6668
67- it ( 'Test 2: Token Bucket capacity is Enforced' , async ( ) => {
68- // 1-2. Assert that the client's retry token bucket is at full capacity and that the capacity
69- // is DEFAULT_RETRY_TOKEN_CAPACITY.
69+ it ( 'Test 2: Token Bucket capacity is Enforced' , async function ( ) {
70+ // 1. Let client be a MongoClient with adaptiveRetries=True.
71+ const client = this . configuration . newClient ( {
72+ adaptiveRetries : true
73+ } ) ;
74+ await client . connect ( ) ;
75+
76+ // 2. Assert that the client's retry token bucket is at full capacity and that the capacity is DEFAULT_RETRY_TOKEN_CAPACITY.
7077 const tokenBucket = client . topology . tokenBucket ;
7178 expect ( tokenBucket ) . to . have . property ( 'budget' , INITIAL_TOKEN_BUCKET_SIZE ) ;
7279 expect ( tokenBucket ) . to . have . property ( 'capacity' , INITIAL_TOKEN_BUCKET_SIZE ) ;
7380
74- // 3. Execute a successful ping command.
81+ // 3. Using client, execute a successful ping command.
7582 await client . db ( 'admin' ) . command ( { ping : 1 } ) ;
7683
77- // 4. Assert that the successful command did not increase the number of tokens in the bucket
78- // above DEFAULT_RETRY_TOKEN_CAPACITY.
84+ // 4. Assert that the successful command did not increase the number of tokens in the bucket above DEFAULT_RETRY_TOKEN_CAPACITY.
7985 expect ( tokenBucket ) . to . have . property ( 'budget' ) . that . is . at . most ( INITIAL_TOKEN_BUCKET_SIZE ) ;
86+
87+ await client . close ( ) ;
8088 } ) ;
89+
90+ it (
91+ 'Test 3: Overload Errors are Retried a Maximum of MAX_RETRIES times' ,
92+ {
93+ requires : {
94+ mongodb : '>=4.4'
95+ }
96+ } ,
97+ async function ( ) {
98+ // 1. Let `client` be a `MongoClient` with command event monitoring enabled.
99+ const client = this . configuration . newClient ( {
100+ monitorCommands : true
101+ } ) ;
102+ await client . connect ( ) ;
103+
104+ // 2. Let `coll` be a collection.
105+ const collection = client . db ( 'foo' ) . collection ( 'bar' ) ;
106+ const commandsStarted = [ ] ;
107+ client . on ( 'commandStarted' , filterForCommands ( [ 'find' ] , commandsStarted ) ) ;
108+
109+ /*
110+ * 3. Configure the following failpoint:
111+ {
112+ configureFailPoint: 'failCommand',
113+ mode: 'alwaysOn',
114+ data: {
115+ failCommands: ['find'],
116+ errorCode: 462, // IngressRequestRateLimitExceeded
117+ errorLabels: ['SystemOverloadedError', 'RetryableError']
118+ }
119+ }
120+ * */
121+ await configureFailPoint ( this . configuration , {
122+ configureFailPoint : 'failCommand' ,
123+ mode : 'alwaysOn' ,
124+ data : {
125+ failCommands : [ 'find' ] ,
126+ errorCode : 462 ,
127+ errorLabels : [ 'RetryableError' , 'SystemOverloadedError' ]
128+ }
129+ } ) ;
130+
131+ // 4. Perform a find operation with `coll` that fails.
132+ const error = await collection . findOne ( { } ) . catch ( e => e ) ;
133+
134+ // 5. Assert that the raised error contains both the `RetryableError` and `SystemOverloadedError` error labels.
135+ expect ( error ) . to . be . instanceof ( MongoServerError ) ;
136+ expect ( error . hasErrorLabel ( 'RetryableError' ) ) . to . be . true ;
137+ expect ( error . hasErrorLabel ( 'SystemOverloadedError' ) ) . to . be . true ;
138+
139+ // 6. Assert that the total number of started commands is MAX_RETRIES + 1 (6).
140+ expect ( commandsStarted ) . to . have . length ( MAX_RETRIES + 1 ) ;
141+
142+ await client . close ( ) ;
143+ }
144+ ) ;
145+
146+ it (
147+ 'Test 4: Adaptive Retries are Limited by Token Bucket Tokens' ,
148+ {
149+ requires : {
150+ mongodb : '>=4.4'
151+ }
152+ } ,
153+ async function ( ) {
154+ // 1. Let `client` be a `MongoClient` with `adaptiveRetries=True` and command event monitoring enabled.
155+ const client = this . configuration . newClient ( {
156+ adaptiveRetries : true ,
157+ monitorCommands : true
158+ } ) ;
159+ await client . connect ( ) ;
160+
161+ // 2. Set `client`'s retry token bucket to have 2 tokens.
162+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
163+ client . topology ! . tokenBucket [ 'budget' ] = 2 ;
164+
165+ // 3. Let `coll` be a collection.
166+ const collection = client . db ( 'foo' ) . collection ( 'bar' ) ;
167+ const commandsStarted = [ ] ;
168+ client . on ( 'commandStarted' , filterForCommands ( [ 'find' ] , commandsStarted ) ) ;
169+
170+ /*
171+ * 4. Configure the following failpoint:
172+ {
173+ configureFailPoint: 'failCommand',
174+ mode: {times: 3},
175+ data: {
176+ failCommands: ['find'],
177+ errorCode: 462, // IngressRequestRateLimitExceeded
178+ errorLabels: ['SystemOverloadedError', 'RetryableError']
179+ }
180+ }
181+ * */
182+ await configureFailPoint ( this . configuration , {
183+ configureFailPoint : 'failCommand' ,
184+ mode : { times : 3 } ,
185+ data : {
186+ failCommands : [ 'find' ] ,
187+ errorCode : 462 ,
188+ errorLabels : [ 'RetryableError' , 'SystemOverloadedError' ]
189+ }
190+ } ) ;
191+
192+ // 5. Perform a find operation with `coll` that fails.
193+ const error = await collection . findOne ( { } ) . catch ( e => e ) ;
194+
195+ // 6. Assert that the raised error contains both the `RetryableError` and `SystemOverloadedError` error labels.
196+ expect ( error ) . to . be . instanceof ( MongoServerError ) ;
197+ expect ( error . hasErrorLabel ( 'RetryableError' ) ) . to . be . true ;
198+ expect ( error . hasErrorLabel ( 'SystemOverloadedError' ) ) . to . be . true ;
199+
200+ // 7. Assert that the total number of started commands is 3: one for the initial attempt and two for the retries.
201+ expect ( commandsStarted ) . to . have . length ( 3 ) ;
202+
203+ await client . close ( ) ;
204+ }
205+ ) ;
81206} ) ;
0 commit comments