Skip to content

Commit 484ecf3

Browse files
committed
nuget add
1 parent d23c6da commit 484ecf3

File tree

12 files changed

+213
-26
lines changed

12 files changed

+213
-26
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace Pandatech.VerticalSlices;
2+
3+
public record AssemblyReference();

src/Pandatech.VerticalSlices/Pandatech.VerticalSlices.csproj

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,45 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<ItemGroup>
4-
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="8.0.1"/>
4+
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="8.0.2" />
55
<PackageReference Include="AspNetCore.HealthChecks.Prometheus.Metrics" Version="8.0.1"/>
6-
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="8.0.1"/>
6+
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="8.0.2" />
77
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="8.0.1"/>
88
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1"/>
99
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/>
1010
<PackageReference Include="Elastic.CommonSchema.Serilog" Version="8.11.1" />
11+
<PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.1.3" />
1112
<PackageReference Include="FluentDateTime" Version="3.0.0"/>
1213
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0"/>
1314
<PackageReference Include="Hangfire" Version="1.8.14"/>
1415
<PackageReference Include="Hangfire.Dashboard.Basic.Authentication" Version="7.0.1"/>
1516
<PackageReference Include="Hangfire.EntityFrameworkCore" Version="0.6.0"/>
1617
<PackageReference Include="Hangfire.PostgreSql" Version="1.20.9"/>
1718
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.5" />
18-
<PackageReference Include="MediatR" Version="12.4.0" />
19+
<PackageReference Include="MediatR" Version="12.4.1" />
1920
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
2021
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
2122
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
2223
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
24+
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.9.1" />
2325
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/>
2426
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0"/>
2527
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.8.0-rc.1"/>
2628
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0"/>
2729
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0"/>
28-
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.8.0-beta.1"/>
2930
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0"/>
3031
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0"/>
3132
<PackageReference Include="Pandatech.Communicator" Version="1.0.6" />
3233
<PackageReference Include="Pandatech.Crypto" Version="2.5.0"/>
33-
<PackageReference Include="Pandatech.DistributedCache" Version="1.2.3" />
34+
<PackageReference Include="Pandatech.DistributedCache" Version="2.0.0" />
3435
<PackageReference Include="Pandatech.EFCore.AuditBase" Version="1.1.0"/>
3536
<PackageReference Include="PandaTech.FileExporter" Version="3.3.2" />
3637
<PackageReference Include="Pandatech.FluentMinimalApiMapper" Version="1.1.0"/>
3738
<PackageReference Include="Pandatech.GridifyExtensions" Version="1.5.4" />
3839
<PackageReference Include="Pandatech.MassTransit.PostgresOutbox" Version="1.0.7"/>
3940
<PackageReference Include="Pandatech.PandaVaultClient" Version="3.1.0"/>
4041
<PackageReference Include="Pandatech.RegexBox" Version="2.0.1" />
41-
<PackageReference Include="Pandatech.ResponseCrafter" Version="3.0.0"/>
42+
<PackageReference Include="Pandatech.ResponseCrafter" Version="3.0.1" />
4243
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
4344
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
4445
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />

src/Pandatech.VerticalSlices/Program.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
.ConfigureOpenTelemetry()
3636
.AddEndpoints()
3737
.AddGridify()
38+
.AddResilienceDefaultPipeline()
3839
.AddCommunicator()
3940
.AddDistributedCache(options =>
4041
{
@@ -63,7 +64,7 @@
6364
app.MapPandaEndpoints();
6465
app.MapEndpoints();
6566

66-
StartupLogger.LogStartSuccess();
67+
app.LogStartSuccess();
6768
app.Run();
6869

6970
//todo Set appropriate name in github repo (ex. be-pt-pandatech-website).
@@ -75,11 +76,4 @@
7576
//todo Delete health checks and other configs of unrelated services. For example you might not need RMQ or Redis in this project.
7677
//todo Update all Nuget packages.
7778
//todo Include all required configurations in appsettings{environment}.json.
78-
//todo Update ReadMm.md file.
79-
80-
//Delete below rows if you have no integration Pandatech.VerticalSlices.Tests in your solution.
81-
82-
namespace Pandatech.VerticalSlices
83-
{
84-
public class Program;
85-
}
79+
//todo Update ReadMm.md file.

src/Pandatech.VerticalSlices/SharedKernel/Extensions/DatabaseExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using EFCore.PostgresExtensions.Extensions;
2+
using EntityFramework.Exceptions.PostgreSQL;
23
using Microsoft.EntityFrameworkCore;
34
using Pandatech.VerticalSlices.Context;
45
using Pandatech.VerticalSlices.SharedKernel.Helpers;
@@ -15,6 +16,7 @@ public static WebApplicationBuilder AddPostgresContext(this WebApplicationBuilde
1516
builder.Services.AddDbContextPool<PostgresContext>(options =>
1617
options.UseNpgsql(connectionString)
1718
.UseQueryLocks()
19+
.UseExceptionProcessor()
1820
.UseSnakeCaseNamingConvention());
1921
return builder;
2022
}

src/Pandatech.VerticalSlices/SharedKernel/Extensions/OpenTelemetryExtension.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ public static WebApplicationBuilder ConfigureOpenTelemetry(this WebApplicationBu
2727
.WithTracing(tracing =>
2828
{
2929
tracing.AddAspNetCoreInstrumentation()
30-
.AddHttpClientInstrumentation()
31-
.AddGrpcClientInstrumentation();
30+
.AddHttpClientInstrumentation();
3231
});
3332

3433
return builder;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Microsoft.Extensions.Http.Resilience;
2+
using Pandatech.VerticalSlices.SharedKernel.Helpers;
3+
using Polly;
4+
5+
namespace Pandatech.VerticalSlices.SharedKernel.Extensions;
6+
7+
public static class ResilienceExtensions
8+
{
9+
public static WebApplicationBuilder AddResilienceDefaultPipeline(this WebApplicationBuilder builder)
10+
{
11+
builder.Services.AddResiliencePipeline(ResilienceDefaultPipelineProvider.DefaultPipelineName,
12+
pipelineBuilder =>
13+
{
14+
pipelineBuilder.AddRetry(ResilienceDefaultPipelineProvider.DefaultNetworkRetryOptions)
15+
.AddRetry(ResilienceDefaultPipelineProvider.TooManyRequestsRetryOptions)
16+
.AddCircuitBreaker(ResilienceDefaultPipelineProvider.DefaultCircuitBreakerOptions)
17+
.AddTimeout(TimeSpan.FromSeconds(8));
18+
});
19+
return builder;
20+
}
21+
22+
public static IHttpResiliencePipelineBuilder AddResilienceDefaultPipeline(this IHttpClientBuilder builder)
23+
{
24+
return builder.AddResilienceHandler("DefaultPipeline",
25+
resilienceBuilder =>
26+
{
27+
resilienceBuilder.AddRetry(ResilienceHttpOptions.DefaultTooManyRequestsRetryOptions)
28+
.AddRetry(ResilienceHttpOptions.DefaultNetworkRetryOptions)
29+
.AddCircuitBreaker(ResilienceHttpOptions.DefaultCircuitBreakerOptions)
30+
.AddTimeout(TimeSpan.FromSeconds(8));
31+
});
32+
}
33+
}

src/Pandatech.VerticalSlices/SharedKernel/Extensions/StartupLogger.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static WebApplicationBuilder LogStartAttempt(this WebApplicationBuilder b
2222
return builder;
2323
}
2424

25-
public static void LogStartSuccess()
25+
public static WebApplication LogStartSuccess(this WebApplication app)
2626
{
2727
_stopwatch.Stop();
2828
var now = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
@@ -33,5 +33,7 @@ public static void LogStartSuccess()
3333
Event = "ApplicationStartSuccess",
3434
InitializationTime = $"{initializationTime} seconds"
3535
}));
36+
37+
return app;
3638
}
3739
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Net;
2+
using Polly;
3+
using Polly.CircuitBreaker;
4+
using Polly.Registry;
5+
using Polly.Retry;
6+
7+
namespace Pandatech.VerticalSlices.SharedKernel.Helpers;
8+
9+
public static class ResilienceDefaultPipelineProvider
10+
{
11+
public static ResiliencePipeline GetDefaultPipeline(
12+
this ResiliencePipelineProvider<string> resiliencePipelineProvider)
13+
{
14+
return resiliencePipelineProvider.GetPipeline(DefaultPipelineName);
15+
}
16+
17+
internal const string DefaultPipelineName = "DefaultPipeline";
18+
19+
internal static RetryStrategyOptions TooManyRequestsRetryOptions =>
20+
new()
21+
{
22+
MaxRetryAttempts = 5,
23+
BackoffType = DelayBackoffType.Exponential,
24+
UseJitter = true,
25+
Delay = TimeSpan.FromMilliseconds(3000),
26+
ShouldHandle = new PredicateBuilder()
27+
.Handle<HttpRequestException>(exception => exception.StatusCode == HttpStatusCode.TooManyRequests)
28+
};
29+
30+
internal static RetryStrategyOptions DefaultNetworkRetryOptions =>
31+
new()
32+
{
33+
MaxRetryAttempts = 7,
34+
BackoffType = DelayBackoffType.Exponential,
35+
UseJitter = true,
36+
Delay = TimeSpan.FromMilliseconds(800),
37+
ShouldHandle = new PredicateBuilder()
38+
.Handle<HttpRequestException>(exception => exception.StatusCode == HttpStatusCode.RequestTimeout ||
39+
(int)exception.StatusCode! >= 500)
40+
};
41+
42+
internal static CircuitBreakerStrategyOptions DefaultCircuitBreakerOptions =>
43+
new()
44+
{
45+
FailureRatio = 0.5,
46+
SamplingDuration = TimeSpan.FromSeconds(30),
47+
MinimumThroughput = 200,
48+
BreakDuration = TimeSpan.FromSeconds(45),
49+
ShouldHandle = new PredicateBuilder().Handle<Exception>()
50+
};
51+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System.Net;
2+
using Microsoft.Extensions.Http.Resilience;
3+
using Polly;
4+
5+
namespace Pandatech.VerticalSlices.SharedKernel.Helpers;
6+
7+
internal static class ResilienceHttpOptions
8+
{
9+
public static HttpRetryStrategyOptions DefaultTooManyRequestsRetryOptions =>
10+
new()
11+
{
12+
MaxRetryAttempts = 5,
13+
BackoffType = DelayBackoffType.Exponential,
14+
UseJitter = true,
15+
Delay = TimeSpan.FromMilliseconds(3000),
16+
ShouldHandle = args =>
17+
{
18+
if (args.Outcome.Exception is HttpRequestException httpException)
19+
{
20+
return ValueTask.FromResult((int)httpException.StatusCode! == 429);
21+
}
22+
23+
if (args.Outcome.Result is not null && args.Outcome.Result.StatusCode == HttpStatusCode.TooManyRequests)
24+
{
25+
return ValueTask.FromResult(true);
26+
}
27+
28+
return ValueTask.FromResult(false);
29+
},
30+
DelayGenerator = args =>
31+
{
32+
if (args.Outcome.Result is null)
33+
{
34+
return ValueTask.FromResult<TimeSpan?>(null);
35+
}
36+
37+
if (!args.Outcome.Result.Headers.TryGetValues("Retry-After", out var values))
38+
{
39+
return ValueTask.FromResult<TimeSpan?>(null);
40+
}
41+
42+
var retryAfterValue = values.FirstOrDefault();
43+
44+
if (int.TryParse(retryAfterValue, out var retryAfterSeconds))
45+
{
46+
return ValueTask.FromResult<TimeSpan?>(TimeSpan.FromSeconds(retryAfterSeconds));
47+
}
48+
49+
if (!DateTimeOffset.TryParseExact(retryAfterValue,
50+
"R", // RFC1123 pattern
51+
System.Globalization.CultureInfo.InvariantCulture,
52+
System.Globalization.DateTimeStyles.None,
53+
out var retryAfterDate))
54+
{
55+
return ValueTask.FromResult<TimeSpan?>(null);
56+
}
57+
58+
var retryDelay = retryAfterDate - DateTimeOffset.UtcNow;
59+
return ValueTask.FromResult<TimeSpan?>(retryDelay > TimeSpan.Zero ? retryDelay : TimeSpan.MinValue);
60+
}
61+
};
62+
63+
public static HttpRetryStrategyOptions DefaultNetworkRetryOptions =>
64+
new()
65+
{
66+
MaxRetryAttempts = 7,
67+
BackoffType = DelayBackoffType.Exponential,
68+
UseJitter = true,
69+
Delay = TimeSpan.FromMilliseconds(800),
70+
ShouldHandle = args =>
71+
{
72+
if (args.Outcome.Exception is HttpRequestException httpException)
73+
{
74+
return ValueTask.FromResult((int)httpException.StatusCode! >= 500 ||
75+
(int)httpException.StatusCode! == 408);
76+
}
77+
78+
return ValueTask.FromResult(args.Outcome.Result is not null &&
79+
(args.Outcome.Result.StatusCode == HttpStatusCode.RequestTimeout ||
80+
(int)args.Outcome.Result.StatusCode >= 500));
81+
}
82+
};
83+
84+
public static HttpCircuitBreakerStrategyOptions DefaultCircuitBreakerOptions =>
85+
new()
86+
{
87+
FailureRatio = 0.5,
88+
SamplingDuration = TimeSpan.FromSeconds(30),
89+
MinimumThroughput = 200,
90+
BreakDuration = TimeSpan.FromSeconds(45),
91+
ShouldHandle = args =>
92+
{
93+
if (args.Outcome.Exception is not null)
94+
{
95+
return ValueTask.FromResult(true);
96+
}
97+
98+
return args.Outcome.Result is null
99+
? ValueTask.FromResult(false)
100+
: ValueTask.FromResult(!args.Outcome.Result.IsSuccessStatusCode);
101+
}
102+
};
103+
}

src/Pandatech.VerticalSlices/appsettings.Local.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"ConnectionStrings": {
2121
"Postgres": "Server=localhost;Port=5432;Database=pandatech_vertical_slices;User Id=test;Password=test;Pooling=true;",
2222
"Redis": "localhost:6379",
23-
"RabbitMq": "amqp://test:test@localhost:5672"
23+
"RabbitMq": "amqp://test:test@localhost:5672",
24+
"PersistentStorage": "/persistence"
2425
},
2526
"Security": {
2627
"SuperUser": {

0 commit comments

Comments
 (0)