-
Notifications
You must be signed in to change notification settings - Fork 491
/
CosmosClientOptions.cs
408 lines (355 loc) · 17.4 KB
/
CosmosClientOptions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Linq;
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Newtonsoft.Json;
/// <summary>
/// Defines all the configurable options that the CosmosClient requires.
/// </summary>
public class CosmosClientOptions
{
/// <summary>
/// Default connection mode
/// </summary>
private const ConnectionMode DefaultConnectionMode = ConnectionMode.Direct;
/// <summary>
/// Default Protocol mode
/// </summary>
private const Protocol DefaultProtocol = Protocol.Tcp;
private const string ConnectionStringAccountEndpoint = "AccountEndpoint";
private const string ConnectionStringAccountKey = "AccountKey";
private const ApiType DefaultApiType = ApiType.None;
/// <summary>
/// Default request timeout
/// </summary>
private static readonly CosmosSerializer propertiesSerializer = new CosmosJsonSerializerWrapper(new CosmosJsonSerializerCore());
private readonly Collection<RequestHandler> customHandlers;
private int gatewayModeMaxConnectionLimit;
/// <summary>
/// Creates a new CosmosClientOptions
/// </summary>
public CosmosClientOptions()
{
this.UserAgentContainer = new UserAgentContainer();
this.GatewayModeMaxConnectionLimit = ConnectionPolicy.Default.MaxConnectionLimit;
this.RequestTimeout = ConnectionPolicy.Default.RequestTimeout;
this.ConnectionMode = CosmosClientOptions.DefaultConnectionMode;
this.ConnectionProtocol = CosmosClientOptions.DefaultProtocol;
this.ApiType = CosmosClientOptions.DefaultApiType;
this.customHandlers = new Collection<RequestHandler>();
}
/// <summary>
/// Get or set user-agent suffix to include with every Azure Cosmos DB service interaction.
/// </summary>
/// <remarks>
/// Setting this property after sending any request won't have any effect.
/// </remarks>
public string ApplicationName
{
get => this.UserAgentContainer.Suffix;
set => this.UserAgentContainer.Suffix = value;
}
/// <summary>
/// Get or set the preferred geo-replicated region to be used for Azure Cosmos DB service interaction.
/// </summary>
/// <remarks>
/// When this property is specified, the SDK prefers the region to perform operations. Also SDK auto-selects
/// fallback geo-replicated regions for high availability.
/// When this property is not specified, the SDK uses the write region as the preferred region for all operations.
///
/// <seealso cref="CosmosClientBuilder.WithApplicationRegion(string)"/>
/// <seealso href="https://docs.microsoft.com/azure/cosmos-db/how-to-multi-master"/>
/// </remarks>
public string ApplicationRegion { get; set; }
/// <summary>
/// Get or set the maximum number of concurrent connections allowed for the target
/// service endpoint in the Azure Cosmos DB service.
/// </summary>
/// <remarks>
/// This setting is only applicable in Gateway mode.
/// </remarks>
/// <value>Default value is 50.</value>
/// <seealso cref="CosmosClientBuilder.WithConnectionModeGateway(int?)"/>
public int GatewayModeMaxConnectionLimit
{
get => this.gatewayModeMaxConnectionLimit;
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
if (this.ConnectionMode != ConnectionMode.Gateway)
{
throw new ArgumentException("Max connection limit is only valid for ConnectionMode.Gateway.");
}
this.gatewayModeMaxConnectionLimit = value;
}
}
/// <summary>
/// Gets the request timeout in seconds when connecting to the Azure Cosmos DB service.
/// The number specifies the time to wait for response to come back from network peer.
/// </summary>
/// <value>Default value is 1 minute.</value>
/// <seealso cref="CosmosClientBuilder.WithRequestTimeout(TimeSpan)"/>
public TimeSpan RequestTimeout { get; set; }
/// <summary>
/// Gets the handlers run before the process
/// </summary>
/// <seealso cref="CosmosClientBuilder.AddCustomHandlers(RequestHandler[])"/>
[JsonConverter(typeof(ClientOptionJsonConverter))]
public Collection<RequestHandler> CustomHandlers
{
get => this.customHandlers;
}
/// <summary>
/// Get or set the connection mode used by the client when connecting to the Azure Cosmos DB service.
/// </summary>
/// <value>
/// Default value is <see cref="Cosmos.ConnectionMode.Direct"/>
/// </value>
/// <remarks>
/// For more information, see <see href="https://docs.microsoft.com/azure/documentdb/documentdb-performance-tips#direct-connection">Connection policy: Use direct connection mode</see>.
/// </remarks>
/// <seealso cref="CosmosClientBuilder.WithConnectionModeDirect"/>
/// <seealso cref="CosmosClientBuilder.WithConnectionModeGateway(int?)"/>
public ConnectionMode ConnectionMode { get; set; }
/// <summary>
/// Get ot set the number of times client should retry on rate throttled requests.
/// </summary>
/// <seealso cref="CosmosClientBuilder.WithThrottlingRetryOptions(TimeSpan, int)"/>
public int? MaxRetryAttemptsOnRateLimitedRequests { get; set; }
/// <summary>
/// Get or set the max time to client is allowed to retry on rate throttled requests.
/// </summary>
/// <remarks>
/// The minimum interval is seconds. Any interval that is smaller will be ignored.
/// </remarks>
/// <seealso cref="CosmosClientBuilder.WithThrottlingRetryOptions(TimeSpan, int)"/>
public TimeSpan? MaxRetryWaitTimeOnRateLimitedRequests { get; set; }
/// <summary>
/// Get ot set an optional serializer client should use to serialize or de-serialize cosmos request/responses.
/// </summary>
[JsonConverter(typeof(ClientOptionJsonConverter))]
public CosmosSerializer Serializer { get; set; }
/// <summary>
/// A JSON serializer used by the CosmosClient to serialize or de-serialize cosmos request/responses.
/// The default serializer is always used for all system owned types like DatabaseProperties.
/// The default serializer is used for user types if no UserJsonSerializer is specified
/// </summary>
[JsonConverter(typeof(ClientOptionJsonConverter))]
internal CosmosSerializer PropertiesSerializer => CosmosClientOptions.propertiesSerializer;
/// <summary>
/// Gets the user json serializer with the CosmosJsonSerializerWrapper or the default
/// </summary>
[JsonConverter(typeof(ClientOptionJsonConverter))]
internal CosmosSerializer CosmosSerializerWithWrapperOrDefault => this.Serializer == null ? this.PropertiesSerializer : new CosmosJsonSerializerWrapper(this.Serializer);
/// <summary>
/// Gets or sets the connection protocol when connecting to the Azure Cosmos service.
/// </summary>
/// <value>
/// Default value is <see cref="Protocol.Tcp"/>.
/// </value>
/// <remarks>
/// This setting is not used when <see cref="ConnectionMode"/> is set to <see cref="Cosmos.ConnectionMode.Gateway"/>.
/// Gateway mode only supports HTTPS.
/// For more information, see <see href="https://docs.microsoft.com/azure/documentdb/documentdb-performance-tips#use-tcp">Connection policy: Use the TCP protocol</see>.
/// </remarks>
internal Protocol ConnectionProtocol { get; set; }
internal UserAgentContainer UserAgentContainer { get; private set; }
/// <summary>
/// The event handler to be invoked before the request is sent.
/// </summary>
internal EventHandler<SendingRequestEventArgs> SendingRequestEventArgs { get; set; }
/// <summary>
/// (Optional) transport interceptor factory
/// </summary>
internal Func<TransportClient, TransportClient> TransportClientHandlerFactory { get; set; }
/// <summary>
/// API type for the account
/// </summary>
internal ApiType ApiType { get; set; }
/// <summary>
/// Optional store client factory instance to use for all transport requests.
/// </summary>
internal IStoreClientFactory StoreClientFactory { get; set; }
/// <summary>
/// Gets or sets the initial delay retry time in milliseconds for the Azure Cosmos DB service
/// for requests that hit RetryWithExceptions. This covers errors that occur due to concurrency errors in the store.
/// </summary>
/// <value>
/// The default value is 1 second. For an example on how to set this value, please refer to <see cref="ConnectionPolicy.RetryOptions"/>.
/// </value>
/// <remarks>
/// <para>
/// When a request fails due to a RetryWith error, the client delays and retries the request. This configures the client
/// to delay the time specified before retrying the request.
/// </para>
/// </remarks>
internal int? InitialRetryForRetryWithMilliseconds { get; set; }
/// <summary>
/// Gets or sets the maximum delay retry time in milliseconds for the Azure Cosmos DB service
/// for requests that hit RetryWithExceptions. This covers errors that occur due to concurrency errors in the store.
/// </summary>
/// <value>
/// The default value is 30 seconds. For an example on how to set this value, please refer to <see cref="ConnectionPolicy.RetryOptions"/>.
/// </value>
/// <remarks>
/// <para>
/// When a request fails due to a RetryWith error, the client delays and retries the request. This configures the maximum time
/// the client should delay before failing the request.
/// </para>
/// </remarks>
internal int? MaximumRetryForRetryWithMilliseconds { get; set; }
/// <summary>
/// Gets or sets the interval to salt retry with value. This will spread the retry values from 1..n from the exponential back-off
/// subscribed.
/// </summary>
/// <value>
/// The default value is to not salt.
/// </value>
/// <remarks>
/// <para>
/// When a request fails due to a RetryWith error, the client delays and retries the request. This configures the jitter on the retry attempted.
/// </para>
/// </remarks>
internal int? RandomSaltForRetryWithMilliseconds { get; set; }
/// <summary>
/// Gets or sets the total time to wait before failing the request for retry with failures.
/// subscribed.
/// </summary>
/// <value>
/// The default value 30 seconds.
/// </value>
/// <remarks>
/// <para>
/// When a request fails due to a RetryWith error, the client delays and retries the request. This configures total time spent waiting on the request.
/// </para>
/// </remarks>
internal int? TotalWaitTimeForRetryWithMilliseconds { get; set; }
/// <summary>
/// Flag that controls whether CPU monitoring thread is created to enrich timeout exceptions with additional diagnostic. Default value is true.
/// </summary>
internal bool? EnableCpuMonitor { get; set; }
internal CosmosClientOptions Clone()
{
CosmosClientOptions cloneConfiguration = (CosmosClientOptions)this.MemberwiseClone();
return cloneConfiguration;
}
internal ConnectionPolicy GetConnectionPolicy()
{
ConnectionPolicy connectionPolicy = new ConnectionPolicy()
{
MaxConnectionLimit = this.GatewayModeMaxConnectionLimit,
RequestTimeout = this.RequestTimeout,
ConnectionMode = this.ConnectionMode,
ConnectionProtocol = this.ConnectionProtocol,
UserAgentContainer = this.UserAgentContainer,
UseMultipleWriteLocations = true,
};
if (this.ApplicationRegion != null)
{
connectionPolicy.SetCurrentLocation(this.ApplicationRegion);
}
if (this.MaxRetryAttemptsOnRateLimitedRequests != null)
{
connectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests = this.MaxRetryAttemptsOnRateLimitedRequests.Value;
}
if (this.MaxRetryWaitTimeOnRateLimitedRequests != null)
{
connectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds = (int)this.MaxRetryWaitTimeOnRateLimitedRequests.Value.TotalSeconds;
}
if (this.InitialRetryForRetryWithMilliseconds != null)
{
connectionPolicy.RetryOptions.InitialRetryForRetryWithMilliseconds =
this.InitialRetryForRetryWithMilliseconds;
}
if (this.MaximumRetryForRetryWithMilliseconds != null)
{
connectionPolicy.RetryOptions.MaximumRetryForRetryWithMilliseconds =
this.MaximumRetryForRetryWithMilliseconds;
}
if (this.RandomSaltForRetryWithMilliseconds != null)
{
connectionPolicy.RetryOptions.RandomSaltForRetryWithMilliseconds
= this.RandomSaltForRetryWithMilliseconds;
}
if (this.TotalWaitTimeForRetryWithMilliseconds != null)
{
connectionPolicy.RetryOptions.TotalWaitTimeForRetryWithMilliseconds
= this.TotalWaitTimeForRetryWithMilliseconds;
}
return connectionPolicy;
}
internal static string GetAccountEndpoint(string connectionString)
{
return CosmosClientOptions.GetValueFromConnectionString(connectionString, CosmosClientOptions.ConnectionStringAccountEndpoint);
}
internal static string GetAccountKey(string connectionString)
{
return CosmosClientOptions.GetValueFromConnectionString(connectionString, CosmosClientOptions.ConnectionStringAccountKey);
}
private static string GetValueFromConnectionString(string connectionString, string keyName)
{
if (connectionString == null)
{
throw new ArgumentNullException(nameof(connectionString));
}
DbConnectionStringBuilder builder = new DbConnectionStringBuilder { ConnectionString = connectionString };
if (builder.TryGetValue(keyName, out object value))
{
string keyNameValue = value as string;
if (!string.IsNullOrEmpty(keyNameValue))
{
return keyNameValue;
}
}
throw new ArgumentException("The connection string is missing a required property: " + keyName);
}
/// <summary>
/// Serialize the current configuration into a JSON string
/// </summary>
/// <returns>Returns a JSON string of the current configuration.</returns>
internal string GetSerializedConfiguration()
{
return JsonConvert.SerializeObject(this);
}
/// <summary>
/// The complex object passed in by the user can contain objects that can not be serialized. Instead just log the types.
/// </summary>
private class ClientOptionJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Collection<RequestHandler> handlers = value as Collection<RequestHandler>;
if (handlers != null)
{
writer.WriteValue(string.Join(":", handlers.Select(x => x.GetType())));
return;
}
CosmosJsonSerializerWrapper cosmosJsonSerializerWrapper = value as CosmosJsonSerializerWrapper;
if (value is CosmosJsonSerializerWrapper)
{
writer.WriteValue(cosmosJsonSerializerWrapper.InternalJsonSerializer.GetType().ToString());
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
}
}
}