From 33856f99274e7216d23100e2c745c8763799105c Mon Sep 17 00:00:00 2001 From: Glax Date: Sun, 18 May 2025 20:31:46 +0200 Subject: [PATCH] Fix infinity joby (because we did not create new Scope on every cycle) --- API/Program.cs | 2 +- API/Schema/Jobs/Job.cs | 22 +++++++++++++++++----- API/Tranga.cs | 30 ++++++++++++------------------ 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/API/Program.cs b/API/Program.cs index a51747e..c1d3591 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -124,7 +124,7 @@ using (IServiceScope scope = app.Services.CreateScope()) context.Jobs.AddRange(context.Jobs.Where(j => j.JobType == JobType.DownloadAvailableChaptersJob) .Include(downloadAvailableChaptersJob => ((DownloadAvailableChaptersJob)downloadAvailableChaptersJob).Manga) .ToList() - .Select(dacj => new UpdateChaptersDownloadedJob(((DownloadAvailableChaptersJob)dacj).Manga, 0))); + .Select(dacj => new UpdateChaptersDownloadedJob(((DownloadAvailableChaptersJob)dacj).Manga, 0, dacj))); context.Jobs.RemoveRange(context.Jobs.Where(j => j.state == JobState.Completed && j.RecurrenceMs < 1)); foreach (Job job in context.Jobs.Where(j => j.state == JobState.Running)) { diff --git a/API/Schema/Jobs/Job.cs b/API/Schema/Jobs/Job.cs index 4e41a50..97ce835 100644 --- a/API/Schema/Jobs/Job.cs +++ b/API/Schema/Jobs/Job.cs @@ -66,32 +66,31 @@ public abstract class Job this.Log = LogManager.GetLogger(this.GetType()); } - public IEnumerable Run(PgsqlContext context) + public IEnumerable Run(PgsqlContext context, ref bool running) { Log.Info($"Running job {JobId}"); DateTime jobStart = DateTime.UtcNow; - context.Attach(this); Job[]? ret = null; try { this.state = JobState.Running; context.SaveChanges(); + running = true; ret = RunInternal(context).ToArray(); + Log.Info($"Job {JobId} completed. Generated {ret.Length} new jobs."); this.state = this.RecurrenceMs > 0 ? JobState.CompletedWaiting : JobState.Completed; this.LastExecution = DateTime.UtcNow; - context.Jobs.AddRange(ret); - Log.Info($"Job {JobId} completed. Generated {ret.Length} new jobs."); context.SaveChanges(); } catch (Exception e) { if (e is not DbUpdateException) { + Log.Error($"Failed to run job {JobId}", e); this.state = JobState.Failed; this.Enabled = false; this.LastExecution = DateTime.UtcNow; - Log.Error($"Failed to run job {JobId}", e); context.SaveChanges(); } else @@ -99,6 +98,19 @@ public abstract class Job Log.Error($"Failed to update Database {JobId}", e); } } + + try + { + if (ret != null) + { + context.Jobs.AddRange(ret); + context.SaveChanges(); + } + } + catch (DbUpdateException e) + { + Log.Error($"Failed to update Database {JobId}", e); + } Log.Info($"Finished Job {JobId}! (took {DateTime.UtcNow.Subtract(jobStart).TotalMilliseconds}ms)"); return ret ?? []; diff --git a/API/Tranga.cs b/API/Tranga.cs index 85a84b7..3b0e49a 100644 --- a/API/Tranga.cs +++ b/API/Tranga.cs @@ -117,32 +117,21 @@ public static class Tranga return; } IServiceProvider serviceProvider = (IServiceProvider)serviceProviderObj; - using IServiceScope scope = serviceProvider.CreateScope(); while (true) { Log.Debug("Starting Job-Cycle..."); DateTime cycleStart = DateTime.UtcNow; + using IServiceScope scope = serviceProvider.CreateScope(); PgsqlContext cycleContext = scope.ServiceProvider.GetRequiredService(); - Log.Debug("Loading Jobs..."); - DateTime loadStart = DateTime.UtcNow; - Log.Debug($"Jobs Loaded! (took {DateTime.UtcNow.Subtract(loadStart).TotalMilliseconds}ms)"); - //Update finished Jobs to new states - IQueryable completedJobs = cycleContext.Jobs.Where(j => j.state == JobState.Completed); - foreach (Job completedJob in completedJobs) - if (completedJob.RecurrenceMs <= 0) - { - cycleContext.Jobs.Remove(completedJob); - } - //Retrieve waiting and due Jobs - IQueryable runningJobs = cycleContext.Jobs.Where(j => j.state == JobState.Running); + List runningJobs = cycleContext.Jobs.Where(j => j.state == JobState.Running).ToList(); DateTime filterStart = DateTime.UtcNow; Log.Debug("Filtering Jobs..."); List busyConnectors = GetBusyConnectors(runningJobs); - IQueryable waitingJobs = cycleContext.Jobs.Where(j => j.state == JobState.CompletedWaiting || j.state == JobState.FirstExecution); + List waitingJobs = cycleContext.Jobs.Where(j => j.state == JobState.CompletedWaiting || j.state == JobState.FirstExecution).ToList(); List dueJobs = FilterDueJobs(waitingJobs); List jobsWithoutBusyConnectors = FilterJobWithBusyConnectors(dueJobs, busyConnectors); List jobsWithoutMissingDependencies = FilterJobDependencies(jobsWithoutBusyConnectors); @@ -175,16 +164,21 @@ public static class Tranga //Start Jobs that are allowed to run (preconditions match) foreach (Job job in startJobs) { + bool running = false; Thread t = new(() => { using IServiceScope jobScope = serviceProvider.CreateScope(); PgsqlContext jobContext = jobScope.ServiceProvider.GetRequiredService(); - jobContext.Jobs.Find(job.JobId)?.Run(jobContext); //FIND the job IN THE NEW CONTEXT!!!!!!! SO WE DON'T GET TRACKING PROBLEMS AND AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + if (jobContext.Jobs.Find(job.JobId) is not { } inContext) + return; + inContext.Run(jobContext, ref running); //FIND the job IN THE NEW CONTEXT!!!!!!! SO WE DON'T GET TRACKING PROBLEMS AND AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA }); RunningJobs.Add(t, job); t.Start(); + while(!running) + Thread.Sleep(10); } - Log.Debug($"Jobs Completed: {completedJobs.Count()} Running: {runningJobs.Count()}\n" + + Log.Debug($"Running: {runningJobs.Count()}\n" + $"Waiting: {waitingJobs.Count()}\n" + $"\tof which Due: {dueJobs.Count()}\n" + $"\t\tof which can be started: {jobsWithoutMissingDependencies.Count()}\n" + @@ -211,7 +205,7 @@ public static class Tranga } } - private static List GetBusyConnectors(IQueryable runningJobs) + private static List GetBusyConnectors(List runningJobs) { HashSet busyConnectors = new(); foreach (Job runningJob in runningJobs) @@ -222,7 +216,7 @@ public static class Tranga return busyConnectors.ToList(); } - private static List FilterDueJobs(IQueryable jobs) => + private static List FilterDueJobs(List jobs) => jobs.ToList() .Where(j => j.NextExecution < DateTime.UtcNow) .ToList();