Skip to content

Commit

Permalink
Drive health (#269)
Browse files Browse the repository at this point in the history
* Intial commit of Updated Health Checks
* Update to dotnet 8 (and needed updates)
* Add grpc endpoint for health checks with rich data
* Add brief public health endpoint

* Update danger level to 1GB of hard drive space left

* Fixes from review:
* Caching still doesn't work !?!?!

* Fix caching - only works for non-authenticated endpoints (well, we can make auth'd work, but we don't need to, or really want to.)

* Responding to reviewer comments

* Run csharpier formatter on all cs files.

* Reviewer comments
  • Loading branch information
johnml1135 authored Jan 16, 2024
1 parent 72df4d2 commit cb56fe7
Show file tree
Hide file tree
Showing 45 changed files with 422 additions and 317 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"csharpier": {
"version": "0.22.1",
"version": "0.27.0",
"commands": [
"dotnet-csharpier"
]
Expand Down
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/Serval.ApiServer/bin/Debug/net6.0/Serval.ApiServer.dll",
"program": "${workspaceFolder}/src/Serval.ApiServer/bin/Debug/net8.0/Serval.ApiServer.dll",
"args": [],
"cwd": "${workspaceFolder}/src/Serval.ApiServer",
"stopAtEntry": false,
Expand All @@ -48,7 +48,7 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/samples/EchoTranslationEngine/bin/Debug/net6.0/EchoTranslationEngine.dll",
"program": "${workspaceFolder}/samples/EchoTranslationEngine/bin/Debug/net8.0/EchoTranslationEngine.dll",
"args": [],
"cwd": "${workspaceFolder}/samples/EchoTranslationEngine",
"stopAtEntry": false,
Expand Down
4 changes: 2 additions & 2 deletions dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0-jammy AS build-env
FROM mcr.microsoft.com/dotnet/sdk:8.0-jammy AS build-env
WORKDIR /app

RUN apt-get update && apt-get install -y g++ curl cmake
Expand All @@ -12,7 +12,7 @@ RUN dotnet publish ./src/Serval.ApiServer/Serval.ApiServer.csproj -c Release -o
RUN dotnet publish ./samples/EchoTranslationEngine/EchoTranslationEngine.csproj -c Release -o out_echo_server

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0-jammy as production
FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy as production
WORKDIR /app
COPY --from=build-env /app/out_api_server ./api_server
COPY --from=build-env /app/out_echo_server ./echo_server
Expand Down
2 changes: 1 addition & 1 deletion dockerfile.development
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0-jammy
FROM mcr.microsoft.com/dotnet/sdk:8.0-jammy
RUN apt update && apt install unzip && \
curl -sSL https://aka.ms/getvsdbgsh | /bin/sh /dev/stdin -v latest -l /remote_debugger
2 changes: 1 addition & 1 deletion samples/EchoTranslationEngine/EchoTranslationEngine.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand Down
5 changes: 2 additions & 3 deletions samples/EchoTranslationEngine/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,20 @@
// Add services to the container.
builder.Services.AddGrpcClient<TranslationPlatformApi.TranslationPlatformApiClient>(o =>
{
o.Address = new Uri(builder.Configuration.GetConnectionString("TranslationPlatformApi"));
o.Address = new Uri(builder.Configuration.GetConnectionString("TranslationPlatformApi")!);
});
builder.Services.AddGrpc();

builder.Services.AddHostedService<BackgroundTaskService>();
builder.Services.AddSingleton<BackgroundTaskQueue>();

builder.Services.AddGrpcHealthChecks().AddCheck("Live", () => HealthCheckResult.Healthy());
builder.Services.AddHealthChecks().AddCheck("Live", () => HealthCheckResult.Healthy());

var app = builder.Build();

// Configure the HTTP request pipeline.
app.UseHttpsRedirection();

app.MapGrpcService<TranslationEngineServiceV1>();
app.MapGrpcHealthChecksService();

app.Run();
66 changes: 33 additions & 33 deletions samples/EchoTranslationEngine/TranslationEngineServiceV1.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
namespace EchoTranslationEngine;

public class TranslationEngineServiceV1 : TranslationEngineApi.TranslationEngineApiBase
public class TranslationEngineServiceV1(BackgroundTaskQueue taskQueue, HealthCheckService healthCheckService)
: TranslationEngineApi.TranslationEngineApiBase
{
private static readonly Empty Empty = new();
private readonly BackgroundTaskQueue _taskQueue;
private readonly BackgroundTaskQueue _taskQueue = taskQueue;

public TranslationEngineServiceV1(BackgroundTaskQueue taskQueue)
{
_taskQueue = taskQueue;
}
private readonly HealthCheckService _healthCheckService = healthCheckService;

public override Task<Empty> Create(CreateRequest request, ServerCallContext context)
{
Expand Down Expand Up @@ -86,18 +84,16 @@ await client.BuildStartedAsync(
if (!corpus.PretranslateAll && corpus.PretranslateTextIds.Count == 0)
continue;

var sourceFiles = corpus.SourceFiles
.Where(
f =>
(corpus.PretranslateAll || corpus.PretranslateTextIds.Contains(f.TextId))
&& f.Format == FileFormat.Text
var sourceFiles = corpus
.SourceFiles.Where(f =>
(corpus.PretranslateAll || corpus.PretranslateTextIds.Contains(f.TextId))
&& f.Format == FileFormat.Text
)
.ToDictionary(f => f.TextId, f => f.Location);
var targetFiles = corpus.TargetFiles
.Where(
f =>
(corpus.PretranslateAll || corpus.PretranslateTextIds.Contains(f.TextId))
&& f.Format == FileFormat.Text
var targetFiles = corpus
.TargetFiles.Where(f =>
(corpus.PretranslateAll || corpus.PretranslateTextIds.Contains(f.TextId))
&& f.Format == FileFormat.Text
)
.ToDictionary(f => f.TextId, f => f.Location);

Expand Down Expand Up @@ -270,23 +266,20 @@ public override Task<GetWordGraphResponse> GetWordGraph(GetWordGraphRequest requ
{
Enumerable
.Range(0, tokens.Length - 1)
.Select(
index =>
new WordGraphArc
{
PrevState = index,
NextState = index + 1,
Score = 1.0,
TargetTokens = { tokens[index] },
Confidences = { 1.0 },
SourceSegmentStart = index,
SourceSegmentEnd = index + 1,
Alignment =
{
new AlignedWordPair { SourceIndex = 0, TargetIndex = 0 }
}
}
)
.Select(index => new WordGraphArc
{
PrevState = index,
NextState = index + 1,
Score = 1.0,
TargetTokens = { tokens[index] },
Confidences = { 1.0 },
SourceSegmentStart = index,
SourceSegmentEnd = index + 1,
Alignment =
{
new AlignedWordPair { SourceIndex = 0, TargetIndex = 0 }
}
})
},
FinalStates = { tokens.Length }
}
Expand All @@ -298,4 +291,11 @@ public override Task<GetQueueSizeResponse> GetQueueSize(GetQueueSizeRequest requ
{
return Task.FromResult(new GetQueueSizeResponse { Size = 0 });
}

public override async Task<HealthCheckResponse> HealthCheck(Empty request, ServerCallContext context)
{
HealthReport healthReport = await _healthCheckService.CheckHealthAsync();
HealthCheckResponse healthCheckResponse = WriteGrpcHealthCheckResponse.Generate(healthReport);
return healthCheckResponse;
}
}
11 changes: 5 additions & 6 deletions src/SIL.DataAccess/IMongoDataAccessConfiguratorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ public static IMongoDataAccessConfigurator AddRepository<T>(
});
}

configurator.Services.AddScoped<IRepository<T>>(
sp =>
CreateRepository(
sp.GetRequiredService<IMongoDataAccessContext>(),
sp.GetRequiredService<IMongoDatabase>().GetCollection<T>(collectionName)
)
configurator.Services.AddScoped<IRepository<T>>(sp =>
CreateRepository(
sp.GetRequiredService<IMongoDataAccessContext>(),
sp.GetRequiredService<IMongoDatabase>().GetCollection<T>(collectionName)
)
);
return configurator;
}
Expand Down
7 changes: 3 additions & 4 deletions src/SIL.DataAccess/IServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ Action<IMongoDataAccessConfigurator> configure
clientSettings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber());
clientSettings.LinqProvider = LinqProvider.V2;
services.AddSingleton<IMongoClient>(sp => new MongoClient(clientSettings));
services.AddSingleton(
sp =>
sp.GetRequiredService<IMongoClient>()
.GetDatabase(sp.GetRequiredService<IOptions<MongoDataAccessOptions>>().Value.Url.DatabaseName)
services.AddSingleton(sp =>
sp.GetRequiredService<IMongoClient>()
.GetDatabase(sp.GetRequiredService<IOptions<MongoDataAccessOptions>>().Value.Url.DatabaseName)
);
services.TryAddScoped<IMongoDataAccessContext, MongoDataAccessContext>();
services.AddScoped<IDataAccessContext>(sp => sp.GetRequiredService<IMongoDataAccessContext>());
Expand Down
12 changes: 8 additions & 4 deletions src/SIL.DataAccess/MongoRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,14 +204,18 @@ public async Task<ISubscription<T>> SubscribeAsync(
BsonDocument result;
if (_context.Session is not null)
{
result = await _collection.Database
.RunCommandAsync<BsonDocument>(_context.Session, findCommand, cancellationToken: cancellationToken)
result = await _collection
.Database.RunCommandAsync<BsonDocument>(
_context.Session,
findCommand,
cancellationToken: cancellationToken
)
.ConfigureAwait(false);
}
else
{
result = await _collection.Database
.RunCommandAsync<BsonDocument>(findCommand, cancellationToken: cancellationToken)
result = await _collection
.Database.RunCommandAsync<BsonDocument>(findCommand, cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
BsonDocument? initialEntityDoc = result["cursor"]["firstBatch"].AsBsonArray.FirstOrDefault()?.AsBsonDocument;
Expand Down
4 changes: 2 additions & 2 deletions src/SIL.DataAccess/SIL.DataAccess.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>0.5.2</Version>
Expand All @@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.20.0" />
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="1.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="SIL.Core" Version="12.0.1" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/SIL.DataAccess/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
global using MongoDB.Bson.Serialization;
global using MongoDB.Bson.Serialization.Conventions;
global using MongoDB.Bson.Serialization.Serializers;
global using MongoDB.Driver.Core.Extensions.DiagnosticSources;
global using MongoDB.Driver;
global using MongoDB.Driver.Core.Extensions.DiagnosticSources;
global using MongoDB.Driver.Linq;
global using Nito.AsyncEx;
global using SIL.DataAccess;
Expand Down
45 changes: 28 additions & 17 deletions src/Serval.ApiServer/Controllers/StatusController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,17 @@ namespace Serval.ApiServer.Controllers;
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/status")]
[OpenApiTag("Status")]
public class StatusController : ServalControllerBase
public class StatusController(
HealthCheckService healthCheckService,
IAuthorizationService authService,
IWebHostEnvironment env,
IConfiguration configuration
) : ServalControllerBase(authService)
{
private readonly HealthCheckService _healthCheckService;
private readonly IWebHostEnvironment _env;
private readonly HealthCheckService _healthCheckService = healthCheckService;
private readonly IWebHostEnvironment _env = env;

private readonly IConfiguration _configuration;

public StatusController(
HealthCheckService healthCheckService,
IAuthorizationService authService,
IWebHostEnvironment env,
IConfiguration configuration
)
: base(authService)
{
_healthCheckService = healthCheckService;
_env = env;
_configuration = configuration;
}
private readonly IConfiguration _configuration = configuration;

/// <summary>
/// Get Health
Expand All @@ -41,6 +33,25 @@ public async Task<ActionResult<HealthReportDto>> GetHealthAsync()
return Ok(Map(report));
}

/// <summary>
/// Get Summary of Health on Publically available endpoint, cached for 10 seconds (if not authenticated).
/// </summary>
/// <remarks>Provides an indication about the health of the API</remarks>
/// <response code="200">The API health status</response>
[HttpGet("ping")]
[OutputCache]
[ProducesResponseType(typeof(HealthReportDto), (int)HttpStatusCode.OK)]
public async Task<ActionResult<HealthReportDto>> GetPingAsync()
{
var report = await _healthCheckService.CheckHealthAsync();
HealthReportDto reportDto = Map(report);

// remove results as this is a public endpoint
reportDto.Results = new Dictionary<string, HealthReportEntryDto>();

return Ok(reportDto);
}

/// <summary>
/// Application Version
/// </summary>
Expand Down
7 changes: 4 additions & 3 deletions src/Serval.ApiServer/Serval.ApiServer.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<Version>1.0.0</Version>
<ImplicitUsings>enable</ImplicitUsings>
Expand All @@ -14,11 +14,12 @@
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="6.4.0" />
<PackageReference Include="AspNetCore.HealthChecks.Aws.S3" Version="6.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.OpenIdConnectServer" Version="6.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.System" Version="6.0.2" />
<PackageReference Include="Hangfire" Version="1.7.33" />
<PackageReference Include="Hangfire.Mongo" Version="1.9.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.14" />
<PackageReference Include="NSwag.AspNetCore" Version="13.18.2" />
<PackageReference Include="NSwag.MSBuild" Version="13.18.2">
<PackageReference Include="NSwag.AspNetCore" Version="13.20.0" />
<PackageReference Include="NSwag.MSBuild" Version="13.20.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Loading

0 comments on commit cb56fe7

Please sign in to comment.