Mongodb 用Polly处理CosmosDb Mongo 429错误的正确方法
我在azure web apps上部署了一个应用程序,该应用程序使用.netCore 3中的mongoDb驱动程序与CosmosDB数据库交互 在以下文档中,我必须设置重试策略,以便在RU/s不可用时处理429错误代码。我找不到正确的方法来处理Polly的策略,因为我还没有看到429错误发生时显示的错误 唯一正确的方法是使用以下策略:Mongodb 用Polly处理CosmosDb Mongo 429错误的正确方法,mongodb,azure,asp.net-core,azure-cosmosdb,azure-cosmosdb-mongoapi,Mongodb,Azure,Asp.net Core,Azure Cosmosdb,Azure Cosmosdb Mongoapi,我在azure web apps上部署了一个应用程序,该应用程序使用.netCore 3中的mongoDb驱动程序与CosmosDB数据库交互 在以下文档中,我必须设置重试策略,以便在RU/s不可用时处理429错误代码。我找不到正确的方法来处理Polly的策略,因为我还没有看到429错误发生时显示的错误 唯一正确的方法是使用以下策略: _retryPolicy = Policy .Handle<MongoCommandException>(r => r.Message.Cont
_retryPolicy = Policy
.Handle<MongoCommandException>(r => r.Message.Contains("Request rate is large"))
.WaitAndRetry(3, i => TimeSpan.FromSeconds(1));
当Mongodb驱动程序在CosmosDb中发生429异常时,是否有人有正确的错误显示,或者有人能告诉我他如何正确处理它。为了正确处理速率限制和超时,实际上还有一些异常需要处理,尤其是在使用较新的Mongodb V3.6端点时(与较旧的V3.2端点相反)
- 对于V3.2端点:您关心的两个异常是
和MongoCommandException
MongoExecutionTimeoutException
在其MongoCommandException
字段。此文档有一个Result
可用于检测429。也就是说,从我的测试中,我还发现我必须处理Http服务不可用(1),并且操作超过了时间限制 (50)状态代码StatusCode
- 对于V3.6端点:您可能还希望处理
和MongoWriteException
。这些异常 在异常消息中包含MongoBulkWriteException
值(非 但始终如此!)。不幸的是,此值似乎不正确 通过类属性直接公开-很可能是因为 CosmosDB特有的功能,因此不会映射到MongoDB 驱动程序定义的异常RetryAfterMs=
public static class Policies
{
public const int HttpThrottleErrorCode = 429;
public const int HttpServiceIsUnavailable = 1;
public const int HttpOperationExceededTimeLimit = 50;
public const int RateLimitCode = 16500;
public const string RetryAfterToken = "RetryAfterMs=";
public const int MaxRetries = 10;
public static readonly int RetryAfterTokenLength = RetryAfterToken.Length;
private static readonly Random JitterSeed = new Random();
public static readonly IAsyncPolicy NoPolicy = Policy.NoOpAsync();
public static Func<int, TimeSpan> SleepDurationProviderWithJitter(double exponentialBackoffInSeconds, int maxBackoffTimeInSeconds) => retryAttempt
=> TimeSpan.FromSeconds(Math.Min(Math.Pow(exponentialBackoffInSeconds, retryAttempt), maxBackoffTimeInSeconds)) // exponential back-off: 2, 4, 8 etc
+ TimeSpan.FromMilliseconds(JitterSeed.Next(0, 1000)); // plus some jitter: up to 1 second
public static readonly Func<int, TimeSpan> DefaultSleepDurationProviderWithJitter =
SleepDurationProviderWithJitter(1.5, 23);
public static readonly IAsyncPolicy MongoCommandExceptionPolicy = Policy
.Handle<MongoCommandException>(e =>
{
if (e.Code != RateLimitCode || !(e.Result is BsonDocument bsonDocument))
{
return false;
}
if (bsonDocument.TryGetValue("StatusCode", out var statusCode) && statusCode.IsInt32)
{
switch (statusCode.AsInt32)
{
case HttpThrottleErrorCode:
case HttpServiceIsUnavailable:
case HttpOperationExceededTimeLimit:
return true;
default:
return false;
}
}
if (bsonDocument.TryGetValue("IsValid", out var isValid) && isValid.IsBoolean)
{
return isValid.AsBoolean;
}
return true;
})
.WaitAndRetryAsync(
retryCount: MaxRetries,
DefaultSleepDurationProviderWithJitter
);
public static readonly IAsyncPolicy ExecutionTimeoutPolicy = Policy
.Handle<MongoExecutionTimeoutException>(e =>
e.Code == RateLimitCode || e.Code == HttpOperationExceededTimeLimit
)
.WaitAndRetryAsync(
retryCount: MaxRetries,
DefaultSleepDurationProviderWithJitter
);
public static readonly IAsyncPolicy MongoWriteExceptionPolicy = Policy
.Handle<MongoWriteException>(e =>
{
return e.WriteError?.Code == RateLimitCode
|| (e.InnerException is MongoBulkWriteException bulkException &&
bulkException.WriteErrors.Any(error => error.Code == RateLimitCode));
})
.WaitAndRetryAsync(
retryCount: MaxRetries,
sleepDurationProvider: (retryAttempt, e, ctx) =>
{
var timeToWaitInMs = ExtractTimeToWait(e.Message);
if (!timeToWaitInMs.HasValue && e.InnerException != null)
{
timeToWaitInMs = ExtractTimeToWait(e.InnerException.Message);
}
return timeToWaitInMs ?? DefaultSleepDurationProviderWithJitter(retryAttempt);
},
onRetryAsync: (e, ts, i, ctx) => Task.CompletedTask
);
public static readonly IAsyncPolicy MongoBulkWriteExceptionPolicy = Policy
.Handle<MongoBulkWriteException>(e =>
{
return e.WriteErrors.Any(error => error.Code == RateLimitCode);
})
.WaitAndRetryAsync(
retryCount: MaxRetries,
sleepDurationProvider: (retryAttempt, e, ctx) =>
{
var timeToWaitInMs = ExtractTimeToWait(e.Message);
return timeToWaitInMs ?? DefaultSleepDurationProviderWithJitter(retryAttempt);
},
onRetryAsync: (e, ts, i, ctx) => Task.CompletedTask
);
/// <summary>
/// It doesn't seem like RetryAfterMs is a property value - so unfortunately, we have to extract it from a string... (crazy??!)
/// </summary>
private static TimeSpan? ExtractTimeToWait(string messageToParse)
{
var retryPos = messageToParse.IndexOf(RetryAfterToken, StringComparison.OrdinalIgnoreCase);
if (retryPos >= 0)
{
retryPos += RetryAfterTokenLength;
var endPos = messageToParse.IndexOf(',', retryPos);
if (endPos > 0)
{
var timeToWaitInMsString = messageToParse.Substring(retryPos, endPos - retryPos);
if (Int32.TryParse(timeToWaitInMsString, out int timeToWaitInMs))
{
return TimeSpan.FromMilliseconds(timeToWaitInMs)
+ TimeSpan.FromMilliseconds(JitterSeed.Next(100, 1000));
}
}
}
return default;
}
/// <summary>
/// Use this policy if your CosmosDB MongoDB endpoint is V3.2
/// </summary>
public static readonly IAsyncPolicy DefaultPolicyForMongo3_2 = Policy.WrapAsync(MongoCommandExceptionPolicy, ExecutionTimeoutPolicy);
/// <summary>
/// Use this policy if your CosmosDB MongoDB endpoint is V3.6 or V3.2
/// </summary>
public static readonly IAsyncPolicy DefaultPolicyForMongo3_6 = Policy.WrapAsync(MongoCommandExceptionPolicy, ExecutionTimeoutPolicy, MongoWriteExceptionPolicy, MongoBulkWriteExceptionPolicy);
}
public static IAsyncPolicy DefaultPolicy { get; set; } = Policies.DefaultPolicyForMongo3_6;
公共静态类策略
{
公共常数int HttpThrottleErrorCode=429;
public const int HttpServiceIsUnavailable=1;
公共常数int HttpOperationExceededTimeLimit=50;
公共常数int RateLimitCode=16500;
public const string RetryAfterToken=“RetryAfterMs=”;
public const int MaxRetries=10;
public static readonly int RetryAfterTokenLength=RetryAfterToken.Length;
私有静态只读随机JitterSeed=new Random();
public static readonly IAsyncPolicy NoPolicy=Policy.NoOpAsync();
public static Func SleepDurationProviderWithJitter(双指数BackOffInSeconds,int maxBackoffTimeInSeconds)=>RetryTest
=>TimeSpan.FromSeconds(Math.Min(Math.Pow(exponentialBackoffInSeconds,retrytry),maxBackoffTimeInSeconds))//指数退避:2、4、8等
+TimeSpan.FromMillistics(JitterSeed.Next(0,1000));//加上一些抖动:最多1秒
公共静态只读函数DefaultSleepDurationProviderWithJitter=
SleepDurationProviderWithJitter(1.5,23);
公共静态只读IAsyncPolicy MongoCommandExceptionPolicy=策略
.Handle(e=>
{
如果(e.Code!=RateLimitCode | |!(e.Result为B.ondocument B.ondocument))
{
返回false;
}
if(bsonDocument.TryGetValue(“状态代码”,输出变量状态代码)和&StatusCode.IsInt32)
{
开关(状态代码AsInt32)
{
案例HttpThrottleErrorCode:
案例HttpServiceIsUnavailable:
案例HttpOperationExceededTimeLimit:
返回true;
违约:
返回false;
}
}
if(bsonDocument.TryGetValue(“IsValid”,out变量IsValid)和&IsValid.IsBoolean)
{
返回isValid.AsBoolean;
}
返回true;
})
.WaitAndRetryAsync(
retryCount:MaxRetries,
DefaultSleepDurationProviderWithJitter
);
公共静态只读IAsyncPolicy ExecutionTimeoutPolicy=策略
.Handle(e=>
e、 代码==RateLimitCode | | e.代码==HttpOperationExceededTimeLimit
)
.WaitAndRetryAsync(
retryCount:MaxRetries,
DefaultSleepDurationProviderWithJitter
);
公共静态只读IAsyncPolicy MongoWriteExceptionPolicy=策略
.Handle(e=>
{
返回e.WriteError?.Code==RateLimitCode
||(e.InnerException是MongoBulkWriteException bulkException&&
bulkException.WriteErrors.Any(error=>error.Code==RateLimitCode));
})
.WaitAndRetryAsync(
retryCount:MaxRetries,
sleepDurationProvider:(重试,e,ctx)=>
{
var timeToWaitInMs=提取TIMETOWAIT(e.Message);
如果(!timeToWaitInMs.HasValue&&e.InnerException!=null)
{
timeToWaitInMs=ExtractTimeToWait(例如InnerException.Message);
}
return timeToWaitInMs??DefaultSleepDurationProviderWithJitter(retrytry);
},
onRetryAsync:(e,ts,i,ctx)=>Task.CompletedTask
);
公共静态只读IAsyncPolicy MongoBulkWriteExceptionPolicy=策略
.Handle(e=>
{
返回e.WriteErrors.Any(error=>error.Code==RateLimitCode);
public static class Policies
{
public const int HttpThrottleErrorCode = 429;
public const int HttpServiceIsUnavailable = 1;
public const int HttpOperationExceededTimeLimit = 50;
public const int RateLimitCode = 16500;
public const string RetryAfterToken = "RetryAfterMs=";
public const int MaxRetries = 10;
public static readonly int RetryAfterTokenLength = RetryAfterToken.Length;
private static readonly Random JitterSeed = new Random();
public static readonly IAsyncPolicy NoPolicy = Policy.NoOpAsync();
public static Func<int, TimeSpan> SleepDurationProviderWithJitter(double exponentialBackoffInSeconds, int maxBackoffTimeInSeconds) => retryAttempt
=> TimeSpan.FromSeconds(Math.Min(Math.Pow(exponentialBackoffInSeconds, retryAttempt), maxBackoffTimeInSeconds)) // exponential back-off: 2, 4, 8 etc
+ TimeSpan.FromMilliseconds(JitterSeed.Next(0, 1000)); // plus some jitter: up to 1 second
public static readonly Func<int, TimeSpan> DefaultSleepDurationProviderWithJitter =
SleepDurationProviderWithJitter(1.5, 23);
public static readonly IAsyncPolicy MongoCommandExceptionPolicy = Policy
.Handle<MongoCommandException>(e =>
{
if (e.Code != RateLimitCode || !(e.Result is BsonDocument bsonDocument))
{
return false;
}
if (bsonDocument.TryGetValue("StatusCode", out var statusCode) && statusCode.IsInt32)
{
switch (statusCode.AsInt32)
{
case HttpThrottleErrorCode:
case HttpServiceIsUnavailable:
case HttpOperationExceededTimeLimit:
return true;
default:
return false;
}
}
if (bsonDocument.TryGetValue("IsValid", out var isValid) && isValid.IsBoolean)
{
return isValid.AsBoolean;
}
return true;
})
.WaitAndRetryAsync(
retryCount: MaxRetries,
DefaultSleepDurationProviderWithJitter
);
public static readonly IAsyncPolicy ExecutionTimeoutPolicy = Policy
.Handle<MongoExecutionTimeoutException>(e =>
e.Code == RateLimitCode || e.Code == HttpOperationExceededTimeLimit
)
.WaitAndRetryAsync(
retryCount: MaxRetries,
DefaultSleepDurationProviderWithJitter
);
public static readonly IAsyncPolicy MongoWriteExceptionPolicy = Policy
.Handle<MongoWriteException>(e =>
{
return e.WriteError?.Code == RateLimitCode
|| (e.InnerException is MongoBulkWriteException bulkException &&
bulkException.WriteErrors.Any(error => error.Code == RateLimitCode));
})
.WaitAndRetryAsync(
retryCount: MaxRetries,
sleepDurationProvider: (retryAttempt, e, ctx) =>
{
var timeToWaitInMs = ExtractTimeToWait(e.Message);
if (!timeToWaitInMs.HasValue && e.InnerException != null)
{
timeToWaitInMs = ExtractTimeToWait(e.InnerException.Message);
}
return timeToWaitInMs ?? DefaultSleepDurationProviderWithJitter(retryAttempt);
},
onRetryAsync: (e, ts, i, ctx) => Task.CompletedTask
);
public static readonly IAsyncPolicy MongoBulkWriteExceptionPolicy = Policy
.Handle<MongoBulkWriteException>(e =>
{
return e.WriteErrors.Any(error => error.Code == RateLimitCode);
})
.WaitAndRetryAsync(
retryCount: MaxRetries,
sleepDurationProvider: (retryAttempt, e, ctx) =>
{
var timeToWaitInMs = ExtractTimeToWait(e.Message);
return timeToWaitInMs ?? DefaultSleepDurationProviderWithJitter(retryAttempt);
},
onRetryAsync: (e, ts, i, ctx) => Task.CompletedTask
);
/// <summary>
/// It doesn't seem like RetryAfterMs is a property value - so unfortunately, we have to extract it from a string... (crazy??!)
/// </summary>
private static TimeSpan? ExtractTimeToWait(string messageToParse)
{
var retryPos = messageToParse.IndexOf(RetryAfterToken, StringComparison.OrdinalIgnoreCase);
if (retryPos >= 0)
{
retryPos += RetryAfterTokenLength;
var endPos = messageToParse.IndexOf(',', retryPos);
if (endPos > 0)
{
var timeToWaitInMsString = messageToParse.Substring(retryPos, endPos - retryPos);
if (Int32.TryParse(timeToWaitInMsString, out int timeToWaitInMs))
{
return TimeSpan.FromMilliseconds(timeToWaitInMs)
+ TimeSpan.FromMilliseconds(JitterSeed.Next(100, 1000));
}
}
}
return default;
}
/// <summary>
/// Use this policy if your CosmosDB MongoDB endpoint is V3.2
/// </summary>
public static readonly IAsyncPolicy DefaultPolicyForMongo3_2 = Policy.WrapAsync(MongoCommandExceptionPolicy, ExecutionTimeoutPolicy);
/// <summary>
/// Use this policy if your CosmosDB MongoDB endpoint is V3.6 or V3.2
/// </summary>
public static readonly IAsyncPolicy DefaultPolicyForMongo3_6 = Policy.WrapAsync(MongoCommandExceptionPolicy, ExecutionTimeoutPolicy, MongoWriteExceptionPolicy, MongoBulkWriteExceptionPolicy);
}
public static IAsyncPolicy DefaultPolicy { get; set; } = Policies.DefaultPolicyForMongo3_6;