Skip to content

Commit

Permalink
feat: add database migrator hosted service (#55)
Browse files Browse the repository at this point in the history
* feat: add hosted service to apply migrations on startup

* refactor: remove indentation in disposable blocks

* refactor: add migrator hosted service to ef core infrastructure

* refactor: use ilogger instead of serilog to output logs

* feat: use use migrator service to migrate encoder database
  • Loading branch information
PHILLIPS71 authored May 28, 2024
1 parent 98f392e commit 8fc7de6
Show file tree
Hide file tree
Showing 17 changed files with 187 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Giantnodes.Infrastructure.EntityFrameworkCore;

public sealed class MigratorHostedService<TDbContext> : IHostedService
where TDbContext : DbContext
{
private readonly IServiceScopeFactory _factory;
private readonly ILogger<MigratorHostedService<TDbContext>> _logger;

public MigratorHostedService(IServiceScopeFactory factory, ILogger<MigratorHostedService<TDbContext>> logger)
{
_factory = factory;
_logger = logger;
}

public async Task StartAsync(CancellationToken cancellationToken)
{
var stopwatch = new Stopwatch();
using var scope = _factory.CreateScope();

var database = scope.ServiceProvider.GetRequiredService<TDbContext>();
var pending = await database.Database.GetPendingMigrationsAsync(cancellationToken);
var total = pending.Count();

if (total <= 0)
{
_logger.LogInformation("no pending migrations for database context {0}.", typeof(TDbContext).Name);
return;
}

_logger.LogInformation("applying {0} pending migrations for database context {1}...", total, typeof(TDbContext).Name);
stopwatch.Start();
await database.Database.MigrateAsync(cancellationToken);
stopwatch.Stop();
_logger.LogInformation("successfully applied {0} migrations for database context {1} in {2} ms.", total, typeof(TDbContext).Name, stopwatch.ElapsedMilliseconds);
}

public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@
using System.IO.Abstractions;
using Giantnodes.Service.Dashboard.Persistence.Sagas;
using MassTransit;
using Serilog;
using Microsoft.Extensions.Logging;

namespace Giantnodes.Service.Dashboard.Application.Components.Encodes.Sagas.Activities;

public class FileCleanUpActivity : IStateMachineActivity<EncodeSagaState>
{
private readonly IFileSystem _fs;
private readonly ILogger<FileCleanUpActivity> _logger;

public FileCleanUpActivity(IFileSystem fs)
public FileCleanUpActivity(IFileSystem fs, ILogger<FileCleanUpActivity> logger)
{
_fs = fs;
_logger = logger;
}

public void Probe(ProbeContext context)
Expand Down Expand Up @@ -74,6 +76,6 @@ private void Execute(SagaConsumeContext<EncodeSagaState> context)
_fs.FileStream.New(file.FullName, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose | FileOptions.Asynchronous);
stopwatch.Stop();

Log.Information("Successfully deleted source file {0} for encode {1} in {2:000ms}", file.FullName, context.Saga.EncodeId, stopwatch.ElapsedMilliseconds);
_logger.LogInformation("successfully deleted source file {FileName} for encode {Id} in {Duration:000ms}", file.FullName, context.Saga.EncodeId, stopwatch.ElapsedMilliseconds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="System.Reactive" Version="6.0.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,32 @@

namespace Giantnodes.Service.Dashboard.Infrastructure.HostedServices;

public class FileSystemWatcherBackgroundService : BackgroundService
public sealed class FileSystemWatcherBackgroundService : BackgroundService
{
private readonly IServiceProvider _provider;
private readonly IServiceScopeFactory _factory;
private readonly ILibraryMonitoringService _monitoring;

public FileSystemWatcherBackgroundService(IServiceProvider provider, ILibraryMonitoringService monitoring)
public FileSystemWatcherBackgroundService(IServiceScopeFactory factory, ILibraryMonitoringService monitoring)
{
_provider = provider;
_factory = factory;
_monitoring = monitoring;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using (var scope = _provider.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<IUnitOfWorkService>();
var repository = scope.ServiceProvider.GetRequiredService<ILibraryRepository>();
using var scope = _factory.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IUnitOfWorkService>();
var repository = scope.ServiceProvider.GetRequiredService<ILibraryRepository>();

using (var uow = await service.BeginAsync(stoppingToken))
{
var libraries = await repository.ToListAsync(x => x.IsWatched, stoppingToken);
using var uow = await service.BeginAsync(stoppingToken);
var libraries = await repository.ToListAsync(x => x.IsWatched, stoppingToken);

var tasks = libraries
.Where(library => library.IsWatched)
.Select(library => _monitoring.TryMonitorAsync(library))
.ToList();
var tasks = libraries
.Where(library => library.IsWatched)
.Select(library => _monitoring.TryMonitorAsync(library))
.ToList();

await Task.WhenAll(tasks);
await uow.CommitAsync(stoppingToken);
}
}
await Task.WhenAll(tasks);
await uow.CommitAsync(stoppingToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Reactive.Linq;
using Giantnodes.Service.Dashboard.Domain.Services;
using MassTransit;
using Serilog;
using Microsoft.Extensions.Logging;

namespace Giantnodes.Service.Dashboard.Infrastructure.Services;

Expand All @@ -14,12 +14,18 @@ public class FileSystemWatcherService : IFileSystemWatcherService
private readonly IFileSystemWatcherFactory _factory;
private readonly IFileSystemService _fileSystemService;
private readonly IBus _bus;
private readonly ILogger<FileSystemWatcherService> _logger;

public FileSystemWatcherService(IFileSystemWatcherFactory factory, IFileSystemService fileSystemService, IBus bus)
public FileSystemWatcherService(
IFileSystemWatcherFactory factory,
IFileSystemService fileSystemService,
IBus bus,
ILogger<FileSystemWatcherService> logger)
{
_factory = factory;
_fileSystemService = fileSystemService;
_bus = bus;
_logger = logger;
}

/// <inheritdoc />
Expand All @@ -35,9 +41,9 @@ public async Task<bool> TryWatchAsync<TEvent>(string path, Func<FileSystemEventA
_watching.Add(path, watcher);
return true;
}
catch (DirectoryNotFoundException)
catch (DirectoryNotFoundException ex)
{
Log.Warning("Cannot watch path {0} as it cannot be found or accessed.", path);
_logger.LogWarning(ex, "cannot watch path {Path} as it cannot be found or accessed.", path);
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,4 @@
<ProjectReference Include="..\Domain\Giantnodes.Service.Dashboard.Domain.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Migrations\" />
</ItemGroup>

</Project>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
is_watched = table.Column<bool>(type: "boolean", nullable: false),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
concurrency_token = table.Column<byte[]>(type: "bytea", nullable: true)
concurrency_token = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
Expand Down Expand Up @@ -151,7 +151,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
deleted_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
concurrency_token = table.Column<byte[]>(type: "bytea", nullable: true)
concurrency_token = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
Expand All @@ -175,7 +175,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
path_info_extension = table.Column<string>(type: "text", nullable: true),
path_info_directory_path = table.Column<string>(type: "text", nullable: true),
path_info_directory_separator_char = table.Column<char>(type: "character(1)", nullable: false),
concurrency_token = table.Column<byte[]>(type: "bytea", nullable: true)
concurrency_token = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
Expand Down Expand Up @@ -213,7 +213,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
path_info_extension = table.Column<string>(type: "text", nullable: true),
path_info_directory_path = table.Column<string>(type: "text", nullable: true),
path_info_directory_separator_char = table.Column<char>(type: "character(1)", nullable: false),
concurrency_token = table.Column<byte[]>(type: "bytea", nullable: true)
concurrency_token = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
Expand Down Expand Up @@ -245,10 +245,12 @@ protected override void Up(MigrationBuilder migrationBuilder)
speed_bitrate = table.Column<long>(type: "bigint", nullable: true),
speed_scale = table.Column<float>(type: "real", nullable: true),
status = table.Column<string>(type: "text", nullable: false),
percent = table.Column<float>(type: "real", precision: 3, scale: 2, nullable: true),
ffmpeg_command = table.Column<string>(type: "text", nullable: true),
machine_name = table.Column<string>(type: "text", nullable: true),
machine_user_name = table.Column<string>(type: "text", nullable: true),
machine_processor_type = table.Column<string>(type: "text", nullable: true),
percent = table.Column<float>(type: "real", precision: 3, scale: 2, nullable: true),
command = table.Column<string>(type: "text", nullable: true),
output = table.Column<string>(type: "text", nullable: true),
started_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
failed_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
failure_reason = table.Column<string>(type: "text", nullable: true),
Expand All @@ -257,7 +259,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
completed_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
created_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
updated_at = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
concurrency_token = table.Column<byte[]>(type: "bytea", nullable: true)
concurrency_token = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
Expand Down
Loading

0 comments on commit 8fc7de6

Please sign in to comment.