Skip to content

Commit 00c85b4

Browse files
authored
Merge pull request #1315 from hchen2020/master
BuildRetryPolicy for reliable membase
2 parents 712bfd7 + e2b2452 commit 00c85b4

File tree

5 files changed

+57
-18
lines changed

5 files changed

+57
-18
lines changed

src/Plugins/BotSharp.Plugin.Membase/BotSharp.Plugin.Membase.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13+
<PackageReference Include="Polly" />
1314
<PackageReference Include="Refit" />
1415
<PackageReference Include="Refit.HttpClientFactory" />
1516
</ItemGroup>

src/Plugins/BotSharp.Plugin.Membase/GraphDb/MembaseGraphDb.cs

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
1-
using BotSharp.Abstraction.Graph;
2-
using BotSharp.Abstraction.Graph.Models;
3-
using BotSharp.Abstraction.Graph.Options;
4-
using BotSharp.Abstraction.Models;
5-
using BotSharp.Abstraction.Options;
6-
using BotSharp.Abstraction.Utilities;
7-
using BotSharp.Plugin.Membase.Interfaces;
8-
using Microsoft.Extensions.Logging;
9-
using System.Text.Json;
1+
using Polly;
2+
using Polly.Timeout;
3+
using Refit;
104

115
namespace BotSharp.Plugin.Membase.GraphDb;
126

@@ -28,6 +22,33 @@ public MembaseGraphDb(
2822

2923
public string Provider => "membase";
3024

25+
private const int RetryCount = 3;
26+
27+
private AsyncPolicy BuildRetryPolicy()
28+
{
29+
var settings = _services.GetRequiredService<MembaseSettings>();
30+
var timeoutSeconds = (double)settings.TimeoutSecond / RetryCount;
31+
32+
var timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromSeconds(timeoutSeconds));
33+
34+
var retryPolicy = Policy
35+
.Handle<HttpRequestException>()
36+
.Or<TaskCanceledException>()
37+
.Or<TimeoutRejectedException>()
38+
.Or<ApiException>(ex => ex.StatusCode == HttpStatusCode.ServiceUnavailable)
39+
.WaitAndRetryAsync(
40+
retryCount: RetryCount,
41+
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
42+
onRetry: (ex, timespan, retryAttempt, _) =>
43+
{
44+
_logger.LogWarning(ex,
45+
"CypherQueryAsync retry {RetryAttempt}/{MaxRetries} after {Delay}s. Exception: {Message}",
46+
retryAttempt, RetryCount, timespan.TotalSeconds, ex.Message);
47+
});
48+
49+
return Policy.WrapAsync(retryPolicy, timeoutPolicy);
50+
}
51+
3152
public async Task<GraphQueryResult> ExecuteQueryAsync(string query, GraphQueryExecuteOptions? options = null)
3253
{
3354
if (string.IsNullOrEmpty(options?.GraphId))
@@ -36,14 +57,17 @@ public async Task<GraphQueryResult> ExecuteQueryAsync(string query, GraphQueryEx
3657
}
3758

3859
var args = options?.Arguments ?? new();
60+
var argLogs = JsonSerializer.Serialize(args, BotSharpOptions.defaultJsonOptions);
3961

4062
try
4163
{
42-
var response = await _membaseApi.CypherQueryAsync(options!.GraphId, new CypherQueryRequest
43-
{
44-
Query = query,
45-
Parameters = args
46-
});
64+
var retryPolicy = BuildRetryPolicy();
65+
var response = await retryPolicy.ExecuteAsync(() =>
66+
_membaseApi.CypherQueryAsync(options!.GraphId, new CypherQueryRequest
67+
{
68+
Query = query,
69+
Parameters = args
70+
}));
4771

4872
return new GraphQueryResult
4973
{
@@ -52,10 +76,14 @@ public async Task<GraphQueryResult> ExecuteQueryAsync(string query, GraphQueryEx
5276
Result = JsonSerializer.Serialize(response.Data)
5377
};
5478
}
79+
catch (ApiException ex)
80+
{
81+
_logger.LogError($"Error when executing query in {Provider} graph db:\r\n{ex.Content}\r\n{query}\r\n{argLogs}");
82+
throw;
83+
}
5584
catch (Exception ex)
5685
{
57-
var argLogs = args.Select(x => (new KeyValue(x.Key, x.Value.ConvertToString(BotSharpOptions.defaultJsonOptions))).ToString());
58-
_logger.LogError(ex, $"Error when executing query in {Provider} graph db. (Query: {query}), (Argments: \r\n{string.Join("\r\n", argLogs)})");
86+
_logger.LogError(ex, $"Error when executing query in {Provider} graph db. (Query: {query}), (Argments: \r\n{argLogs})");
5987
throw;
6088
}
6189
}

src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
3333
.ConfigureHttpClient(c =>
3434
{
3535
c.BaseAddress = new Uri(settings.Host);
36-
c.Timeout = TimeSpan.FromSeconds(settings.TimeoutSecond);
36+
// Timeout is set by MembaseGrapbDb internally, but we set it here as well to ensure that the Refit client does not timeout before the graph db does.
37+
c.Timeout = TimeSpan.FromSeconds(90);
3738
});
3839

3940
services.AddScoped<IGraphDb, MembaseGraphDb>();

src/Plugins/BotSharp.Plugin.Membase/Settings/MembaseSettings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ public class MembaseSettings
77
public string Host { get; set; } = "localhost";
88
public string ProjectId { get; set; } = string.Empty;
99
public string ApiKey { get; set; } = string.Empty;
10-
public int TimeoutSecond { get; set; } = 10;
10+
public int TimeoutSecond { get; set; } = 30;
1111
public GraphInstance[] GraphInstances { get; set; } = [];
1212
}

src/Plugins/BotSharp.Plugin.Membase/Using.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
global using System.Collections.Generic;
33
global using System.Linq;
44
global using System.Threading.Tasks;
5+
global using System.Net;
6+
global using System.Net.Http;
7+
global using System.Text.Json;
58

69
global using Microsoft.AspNetCore.Authorization;
710
global using Microsoft.AspNetCore.Mvc;
811
global using Microsoft.Extensions.Configuration;
912
global using Microsoft.Extensions.DependencyInjection;
13+
global using Microsoft.Extensions.Logging;
1014

1115
global using BotSharp.Abstraction.Users;
1216
global using BotSharp.Abstraction.Knowledges;
@@ -15,3 +19,8 @@
1519
global using BotSharp.Plugin.Membase.Models;
1620
global using BotSharp.Plugin.Membase.Services;
1721
global using BotSharp.Plugin.Membase.Settings;
22+
global using BotSharp.Abstraction.Graph;
23+
global using BotSharp.Abstraction.Graph.Models;
24+
global using BotSharp.Abstraction.Graph.Options;
25+
global using BotSharp.Abstraction.Options;
26+
global using BotSharp.Plugin.Membase.Interfaces;

0 commit comments

Comments
 (0)