Implemented basic virtual disk support

This commit is contained in:
2025-06-03 16:43:48 +02:00
parent 2bf56f6963
commit f78e97aff4
8 changed files with 358 additions and 179 deletions

View File

@@ -17,7 +17,7 @@ namespace MoonlightServers.Daemon.Services;
[Singleton]
public class ServerService : IHostedLifecycleService
{
private readonly Dictionary<int, Server> Servers = new();
private readonly ConcurrentDictionary<int, Server> Servers = new();
private readonly RemoteService RemoteService;
private readonly DockerClient DockerClient;
@@ -58,82 +58,9 @@ public class ServerService : IHostedLifecycleService
else
await Initialize(serverId);
}
public async Task InitializeAll()
{
var initialPage = await RemoteService.GetServers(0, 1);
const int pageSize = 25;
var pages = (initialPage.TotalItems == 0 ? 0 : (initialPage.TotalItems - 1) / pageSize) +
1; // The +1 is to handle the pages starting at 0
// Create and fill a queue with pages to initialize
var batchesLeft = new ConcurrentQueue<int>();
for (var i = 0; i < pages; i++)
batchesLeft.Enqueue(i);
var tasksCount = pages > 5 ? 5 : pages;
var tasks = new List<Task>();
Logger.LogInformation(
"Starting initialization for {count} server(s) with {tasksCount} worker(s)",
initialPage.TotalItems,
tasksCount
);
for (var i = 0; i < tasksCount; i++)
{
var id = i + 0;
var task = Task.Run(() => BatchRunner(batchesLeft, id));
tasks.Add(task);
}
await Task.WhenAll(tasks);
Logger.LogInformation("Initialization completed");
}
private async Task BatchRunner(ConcurrentQueue<int> queue, int id)
{
while (!queue.IsEmpty)
{
if (!queue.TryDequeue(out var page))
continue;
await InitializeBatch(page, 25);
Logger.LogDebug("Worker {id}: Finished initialization of page {page}", id, page);
}
Logger.LogDebug("Worker {id}: Finished", id);
}
private async Task InitializeBatch(int page, int pageSize)
{
var servers = await RemoteService.GetServers(page, pageSize);
var configurations = servers.Items
.Select(x => x.ToServerConfiguration())
.ToArray();
foreach (var configuration in configurations)
{
try
{
await Sync(configuration.Id, configuration);
}
catch (Exception e)
{
Logger.LogError(
"An unhandled error occured while initializing server {id}: {e}",
configuration.Id,
e
);
}
}
}
public Server? Find(int serverId)
=> Servers.GetValueOrDefault(serverId);
public async Task Initialize(int serverId)
{
@@ -143,9 +70,6 @@ public class ServerService : IHostedLifecycleService
await Initialize(configuration);
}
public Server? Find(int serverId)
=> Servers.GetValueOrDefault(serverId);
public async Task Initialize(ServerConfiguration configuration)
{
var serverScope = ServiceProvider.CreateScope();
@@ -235,16 +159,95 @@ public class ServerService : IHostedLifecycleService
private async Task DeleteServer_Unhandled(Server server)
{
await server.DisposeAsync();
await server.Delete();
await server.DisposeAsync();
lock (Servers)
Servers.Remove(server.Configuration.Id);
Servers.Remove(server.Configuration.Id, out _);
}
#region Batch Initialization
public async Task InitializeAll()
{
var initialPage = await RemoteService.GetServers(0, 1);
const int pageSize = 25;
var pages = (initialPage.TotalItems == 0 ? 0 : (initialPage.TotalItems - 1) / pageSize) +
1; // The +1 is to handle the pages starting at 0
// Create and fill a queue with pages to initialize
var batchesLeft = new ConcurrentQueue<int>();
for (var i = 0; i < pages; i++)
batchesLeft.Enqueue(i);
var tasksCount = pages > 5 ? 5 : pages;
var tasks = new List<Task>();
Logger.LogInformation(
"Starting initialization for {count} server(s) with {tasksCount} worker(s)",
initialPage.TotalItems,
tasksCount
);
for (var i = 0; i < tasksCount; i++)
{
var id = i + 0;
var task = Task.Run(() => BatchRunner(batchesLeft, id));
tasks.Add(task);
}
await Task.WhenAll(tasks);
Logger.LogInformation("Initialization completed");
}
private async Task BatchRunner(ConcurrentQueue<int> queue, int id)
{
while (!queue.IsEmpty)
{
if (!queue.TryDequeue(out var page))
continue;
await InitializeBatch(page, 25);
Logger.LogDebug("Worker {id}: Finished initialization of page {page}", id, page);
}
Logger.LogDebug("Worker {id}: Finished", id);
}
private async Task InitializeBatch(int page, int pageSize)
{
var servers = await RemoteService.GetServers(page, pageSize);
var configurations = servers.Items
.Select(x => x.ToServerConfiguration())
.ToArray();
foreach (var configuration in configurations)
{
try
{
await Sync(configuration.Id, configuration);
}
catch (Exception e)
{
Logger.LogError(
"An unhandled error occured while initializing server {id}: {e}",
configuration.Id,
e
);
}
}
}
#endregion
#region Docker Monitoring
private async Task MonitorContainers()
private Task StartContainerMonitoring()
{
Task.Run(async () =>
{
@@ -296,6 +299,8 @@ public class ServerService : IHostedLifecycleService
}
}
});
return Task.CompletedTask;
}
#endregion
@@ -310,7 +315,7 @@ public class ServerService : IHostedLifecycleService
public async Task StartedAsync(CancellationToken cancellationToken)
{
await MonitorContainers();
await StartContainerMonitoring();
await InitializeAll();
}