diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index dc3e77d..7ebac0c 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -2,6 +2,7 @@ using Moonlight.App.Database.Entities; using Moonlight.App.Database.Entities.LogsEntries; using Moonlight.App.Database.Entities.Notification; +using Moonlight.App.Database.Interceptors; using Moonlight.App.Models.Misc; using Moonlight.App.Services; @@ -65,6 +66,9 @@ public class DataContext : DbContext builder.EnableRetryOnFailure(5); } ); + + if(ConfigService.SqlDebugMode) + optionsBuilder.AddInterceptors(new SqlLoggingInterceptor()); } } } \ No newline at end of file diff --git a/Moonlight/App/Database/Interceptors/SqlLoggingInterceptor.cs b/Moonlight/App/Database/Interceptors/SqlLoggingInterceptor.cs new file mode 100644 index 0000000..06e5141 --- /dev/null +++ b/Moonlight/App/Database/Interceptors/SqlLoggingInterceptor.cs @@ -0,0 +1,40 @@ +using System.Data.Common; +using Logging.Net; +using Microsoft.EntityFrameworkCore.Diagnostics; + +namespace Moonlight.App.Database.Interceptors; + +public class SqlLoggingInterceptor : DbCommandInterceptor +{ + public override InterceptionResult ReaderExecuting( + DbCommand command, + CommandEventData eventData, + InterceptionResult result) + { + LogSql(command.CommandText); + return base.ReaderExecuting(command, eventData, result); + } + + public override InterceptionResult ScalarExecuting( + DbCommand command, + CommandEventData eventData, + InterceptionResult result) + { + LogSql(command.CommandText); + return base.ScalarExecuting(command, eventData, result); + } + + public override InterceptionResult NonQueryExecuting( + DbCommand command, + CommandEventData eventData, + InterceptionResult result) + { + LogSql(command.CommandText); + return base.NonQueryExecuting(command, eventData, result); + } + + private void LogSql(string sql) + { + Logger.Info($"[SQL DEBUG] {sql.Replace("\n", "")}"); + } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/NodeAllocationRepository.cs b/Moonlight/App/Repositories/NodeAllocationRepository.cs new file mode 100644 index 0000000..529c9fb --- /dev/null +++ b/Moonlight/App/Repositories/NodeAllocationRepository.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class NodeAllocationRepository : IDisposable +{ + // This repository is ONLY for the server creation service, so allocations can be found + // using raw sql. DO NOT use this in any other component + + private readonly DataContext DataContext; + + public NodeAllocationRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.NodeAllocations; + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/ConfigService.cs b/Moonlight/App/Services/ConfigService.cs index 27bc5bc..6aa11c4 100644 --- a/Moonlight/App/Services/ConfigService.cs +++ b/Moonlight/App/Services/ConfigService.cs @@ -12,6 +12,7 @@ public class ConfigService : IConfiguration private IConfiguration Configuration; public bool DebugMode { get; private set; } = false; + public bool SqlDebugMode { get; private set; } = false; public ConfigService(StorageService storageService) { @@ -28,6 +29,14 @@ public class ConfigService : IConfiguration if (DebugMode) Logger.Debug("Debug mode enabled"); + + var sqlDebugVar = Environment.GetEnvironmentVariable("ML_SQL_DEBUG"); + + if (sqlDebugVar != null) + SqlDebugMode = bool.Parse(sqlDebugVar); + + if (SqlDebugMode) + Logger.Debug("Sql debug mode enabled"); } public void Reload() diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 14f5394..1ccf204 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -21,6 +21,7 @@ public class ServerService private readonly UserRepository UserRepository; private readonly ImageRepository ImageRepository; private readonly NodeRepository NodeRepository; + private readonly NodeAllocationRepository NodeAllocationRepository; private readonly WingsApiHelper WingsApiHelper; private readonly MessageService MessageService; private readonly UserService UserService; @@ -44,7 +45,8 @@ public class ServerService SecurityLogService securityLogService, AuditLogService auditLogService, ErrorLogService errorLogService, - NodeService nodeService) + NodeService nodeService, + NodeAllocationRepository nodeAllocationRepository) { ServerRepository = serverRepository; WingsApiHelper = wingsApiHelper; @@ -59,6 +61,7 @@ public class ServerService AuditLogService = auditLogService; ErrorLogService = errorLogService; NodeService = nodeService; + NodeAllocationRepository = nodeAllocationRepository; } private Server EnsureNodeData(Server s) @@ -268,32 +271,19 @@ public class ServerService .Include(x => x.DockerImages) .First(x => x.Id == i.Id); - Node node; - - if (n == null) - { - node = NodeRepository - .Get() - .Include(x => x.Allocations) - .First(); //TODO: Add smart deploy maybe - } - else - { - node = NodeRepository - .Get() - .Include(x => x.Allocations) - .First(x => x.Id == n.Id); - } + Node node = n ?? NodeRepository.Get().First(); NodeAllocation[] freeAllocations; try { - freeAllocations = node.Allocations - .Where(a => !ServerRepository.Get() - .SelectMany(s => s.Allocations) - .Any(b => b.Id == a.Id)) - .Take(allocations).ToArray(); + // We have sadly no choice to use entity framework to do what the sql call does, there + // are only slower ways, so we will use a raw sql call as a exception + + freeAllocations = NodeAllocationRepository + .Get() + .FromSqlRaw($"SELECT * FROM `NodeAllocations` WHERE ServerId IS NULL AND NodeId={node.Id} LIMIT {allocations}") + .ToArray(); } catch (Exception) { diff --git a/Moonlight/App/Services/SubscriptionService.cs b/Moonlight/App/Services/SubscriptionService.cs index 95c190d..2dcc797 100644 --- a/Moonlight/App/Services/SubscriptionService.cs +++ b/Moonlight/App/Services/SubscriptionService.cs @@ -89,7 +89,7 @@ public class SubscriptionService } } - public async Task GetLimit(string identifier) + public async Task GetLimit(string identifier) // Cache, optimize sql code { var subscription = await GetCurrent(); var defaultLimits = await GetDefaultLimits(); diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 4ecb04e..ffd7894 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -71,9 +71,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - + builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Moonlight/Properties/launchSettings.json b/Moonlight/Properties/launchSettings.json index acd90c6..237a632 100644 --- a/Moonlight/Properties/launchSettings.json +++ b/Moonlight/Properties/launchSettings.json @@ -17,12 +17,15 @@ "applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118", "dotnetRunMessages": true }, - "IIS Express": { - "commandName": "IISExpress", + "Sql Debug": { + "commandName": "Project", "launchBrowser": true, "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "ASPNETCORE_ENVIRONMENT": "Development", + "ML_SQL_DEBUG": "true" + }, + "applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118", + "dotnetRunMessages": true }, "Docker": { "commandName": "Docker", diff --git a/Moonlight/Shared/Views/Servers/Index.razor b/Moonlight/Shared/Views/Servers/Index.razor index d204748..b0131db 100644 --- a/Moonlight/Shared/Views/Servers/Index.razor +++ b/Moonlight/Shared/Views/Servers/Index.razor @@ -103,6 +103,7 @@ .Include(x => x.Node) .Include(x => x.Image) .Where(x => x.Owner.Id == User.Id) + .OrderBy(x => x.Name) .ToArray(); foreach (var server in AllServers)