Implemented a startup task that will instantiate all the DI so first API isn't having to eat that overhead.

This commit is contained in:
Joseph Milazzo 2021-03-13 17:44:29 -06:00
parent 983078de02
commit 6e6e5ee9f2
7 changed files with 100 additions and 7 deletions

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using API.Data;
using API.DTOs; using API.DTOs;
using API.Entities.Enums; using API.Entities.Enums;
using API.Extensions; using API.Extensions;
@ -9,6 +10,8 @@ using API.Helpers.Converters;
using API.Interfaces; using API.Interfaces;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace API.Controllers namespace API.Controllers
@ -19,12 +22,14 @@ namespace API.Controllers
private readonly ILogger<SettingsController> _logger; private readonly ILogger<SettingsController> _logger;
private readonly IUnitOfWork _unitOfWork; private readonly IUnitOfWork _unitOfWork;
private readonly ITaskScheduler _taskScheduler; private readonly ITaskScheduler _taskScheduler;
private readonly IConfiguration _configuration;
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler) public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler, IConfiguration configuration)
{ {
_logger = logger; _logger = logger;
_unitOfWork = unitOfWork; _unitOfWork = unitOfWork;
_taskScheduler = taskScheduler; _taskScheduler = taskScheduler;
_configuration = configuration;
} }
[HttpGet("")] [HttpGet("")]
@ -51,6 +56,9 @@ namespace API.Controllers
// We do not allow CacheDirectory changes, so we will ignore. // We do not allow CacheDirectory changes, so we will ignore.
var currentSettings = await _unitOfWork.SettingsRepository.GetSettingsAsync(); var currentSettings = await _unitOfWork.SettingsRepository.GetSettingsAsync();
var logLevelOptions = new LogLevelOptions();
_configuration.GetSection("Logging:LogLevel").Bind(logLevelOptions);
foreach (var setting in currentSettings) foreach (var setting in currentSettings)
{ {
@ -72,8 +80,15 @@ namespace API.Controllers
Environment.SetEnvironmentVariable("KAVITA_PORT", setting.Value); Environment.SetEnvironmentVariable("KAVITA_PORT", setting.Value);
_unitOfWork.SettingsRepository.Update(setting); _unitOfWork.SettingsRepository.Update(setting);
} }
if (setting.Key == ServerSettingKey.LoggingLevel && updateSettingsDto.LoggingLevel + "" != setting.Value)
{
setting.Value = updateSettingsDto.LoggingLevel + "";
_unitOfWork.SettingsRepository.Update(setting);
}
} }
_configuration.GetSection("Logging:LogLevel:Default").Value = updateSettingsDto.LoggingLevel + "";
if (!_unitOfWork.HasChanges()) return Ok("Nothing was updated"); if (!_unitOfWork.HasChanges()) return Ok("Nothing was updated");
if (!_unitOfWork.HasChanges() || !await _unitOfWork.Complete()) if (!_unitOfWork.HasChanges() || !await _unitOfWork.Complete())
@ -90,5 +105,12 @@ namespace API.Controllers
{ {
return Ok(CronConverter.Options); return Ok(CronConverter.Options);
} }
[Authorize(Policy = "RequireAdminRole")]
[HttpGet("log-levels")]
public ActionResult<IEnumerable<string>> GetLogLevels()
{
return Ok(new string[] {"Trace", "Debug", "Information", "Warning", "Critical", "None"});
}
} }
} }

View File

@ -0,0 +1,9 @@
namespace API.Data
{
public class LogLevelOptions
{
public const string Logging = "LogLevel";
public string Default { get; set; }
}
}

View File

@ -56,5 +56,9 @@ namespace API.Extensions
return services; return services;
} }
public static IServiceCollection AddStartupTask<T>(this IServiceCollection services)
where T : class, IStartupTask
=> services.AddTransient<IStartupTask, T>();
} }
} }

View File

@ -0,0 +1,10 @@
using System.Threading;
using System.Threading.Tasks;
namespace API.Interfaces.Services
{
public interface IStartupTask
{
Task ExecuteAsync(CancellationToken cancellationToken = default);
}
}

View File

@ -39,9 +39,9 @@ namespace API.Services
_cleanupService = cleanupService; _cleanupService = cleanupService;
_directoryService = directoryService; _directoryService = directoryService;
//Hangfire.RecurringJob.RemoveIfExists();
ScheduleTasks(); ScheduleTasks();
//JobStorage.Current.GetMonitoringApi(). //JobStorage.Current.GetMonitoringApi().EnqueuedJobs()
} }

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using API.Interfaces.Services;
using Microsoft.Extensions.DependencyInjection;
namespace API.Services
{
public class WarmupServicesStartupTask : IStartupTask
{
private readonly IServiceCollection _services;
private readonly IServiceProvider _provider;
public WarmupServicesStartupTask(IServiceCollection services, IServiceProvider provider)
{
_services = services;
_provider = provider;
}
public Task ExecuteAsync(CancellationToken cancellationToken)
{
using (var scope = _provider.CreateScope())
{
foreach (var singleton in GetServices(_services))
{
scope.ServiceProvider.GetServices(singleton);
}
}
return Task.CompletedTask;
}
static IEnumerable<Type> GetServices(IServiceCollection services)
{
return services
.Where(descriptor => descriptor.ImplementationType != typeof(WarmupServicesStartupTask))
.Where(descriptor => !descriptor.ServiceType.ContainsGenericParameters)
.Select(descriptor => descriptor.ServiceType)
.Distinct();
}
}
}

View File

@ -1,8 +1,10 @@
using System; using System;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using API.Data;
using API.Extensions; using API.Extensions;
using API.Middleware; using API.Middleware;
using API.Services;
using Hangfire; using Hangfire;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -12,7 +14,9 @@ using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.AspNetCore.StaticFiles; using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace API namespace API
@ -42,7 +46,6 @@ namespace API
{ {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" }); c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
}); });
// This doesn't seem to work.
services.AddResponseCompression(options => services.AddResponseCompression(options =>
{ {
options.Providers.Add<BrotliCompressionProvider>(); options.Providers.Add<BrotliCompressionProvider>();
@ -60,7 +63,9 @@ namespace API
services.AddResponseCaching(); services.AddResponseCaching();
services
.AddStartupTask<WarmupServicesStartupTask>()
.TryAddSingleton(services);
} }
@ -113,7 +118,6 @@ namespace API
await next(); await next();
}); });
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {