From e1cd8b8f940ebec33c6c6a25b68a9a69cf5473e1 Mon Sep 17 00:00:00 2001 From: MBucari Date: Fri, 10 Mar 2023 18:17:24 -0700 Subject: [PATCH] Improve Library load and refresh performance --- Source/ApplicationServices/LibraryCommands.cs | 20 +++++++++++-- Source/FileManager/LogArchiver.cs | 30 ++++++++++++------- .../ViewModels/ProductsDisplayViewModel.cs | 14 ++++----- .../LibationWinForms/GridView/ProductsGrid.cs | 6 ++-- 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs index 9844909f..cfa9551a 100644 --- a/Source/ApplicationServices/LibraryCommands.cs +++ b/Source/ApplicationServices/LibraryCommands.cs @@ -95,7 +95,8 @@ namespace ApplicationServices stop(); var putBreakPointHere = logOutput; ScanEnd?.Invoke(null, null); - } + GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); + } } #region FULL LIBRARY scan and import @@ -166,6 +167,7 @@ namespace ApplicationServices stop(); var putBreakPointHere = logOutput; ScanEnd?.Invoke(null, null); + GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); } } @@ -173,7 +175,7 @@ namespace ApplicationServices { var tasks = new List>>(); - using LogArchiver archiver + await using LogArchiver archiver = Log.Logger.IsDebugEnabled() ? new LogArchiver(System.IO.Path.Combine(Configuration.Instance.LibationFiles, "LibraryScans.zip")) : default; @@ -208,7 +210,19 @@ namespace ApplicationServices var dtoItems = await apiExtended.GetLibraryValidatedAsync(libraryOptions, Configuration.Instance.ImportEpisodes); - archiver?.AddFile($"{DateTime.Now:u} {account.MaskedLogEntry}.json", new JObject { { "Account", account.MaskedLogEntry }, { "ScannedDateTime", DateTime.Now.ToString("u") }, {"Items", JArray.FromObject(dtoItems) } }); + if (archiver is not null) + { + var fileName = $"{DateTime.Now:u} {account.MaskedLogEntry}.json"; + + var scanFile = new JObject + { + { "Account", account.MaskedLogEntry }, + { "ScannedDateTime", DateTime.Now.ToString("u") }, + { "Items", await Task.Run(() => JArray.FromObject(dtoItems)) } + }; + + await archiver.AddFileAsync(fileName, scanFile); + } logTime($"post scanAccountAsync {account.AccountName} qty: {dtoItems.Count}"); diff --git a/Source/FileManager/LogArchiver.cs b/Source/FileManager/LogArchiver.cs index d78da314..248d26eb 100644 --- a/Source/FileManager/LogArchiver.cs +++ b/Source/FileManager/LogArchiver.cs @@ -6,10 +6,11 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Text; +using System.Threading.Tasks; namespace FileManager { - public class LogArchiver : IDisposable + public sealed class LogArchiver : IAsyncDisposable { public Encoding Encoding { get; set; } public string FileName { get; } @@ -38,21 +39,30 @@ namespace FileManager e.Delete(); } - public void AddFile(string name, JObject contents, string comment = null) - => AddFile(name, Encoding.GetBytes(contents.ToString(Newtonsoft.Json.Formatting.Indented)), comment); + public async Task AddFileAsync(string name, JObject contents, string comment = null) + { + ArgumentValidator.EnsureNotNull(contents, nameof(contents)); + await AddFileAsync(name, Encoding.GetBytes(contents.ToString(Newtonsoft.Json.Formatting.Indented)), comment); + } - public void AddFile(string name, string contents, string comment = null) - => AddFile(name, Encoding.GetBytes(contents), comment); + public async Task AddFileAsync(string name, string contents, string comment = null) + { + ArgumentValidator.EnsureNotNull(contents, nameof(contents)); + await AddFileAsync(name, Encoding.GetBytes(contents), comment); + } - private readonly object lockOob = new(); - - public void AddFile(string name, ReadOnlySpan contents, string comment = null) + public Task AddFileAsync(string name, ReadOnlyMemory contents, string comment = null) { ArgumentValidator.EnsureNotNull(name, nameof(name)); name = ReplacementCharacters.Barebones.ReplaceFilenameChars(name); + return Task.Run(() => AddfileInternal(name, contents.Span, comment)); + } - lock (lockOob) + private readonly object lockObj = new(); + private void AddfileInternal(string name, ReadOnlySpan contents, string comment) + { + lock (lockObj) { var entry = archive.CreateEntry(name, CompressionLevel.SmallestSize); @@ -62,6 +72,6 @@ namespace FileManager } } - public void Dispose() => archive.Dispose(); + public async ValueTask DisposeAsync() => await Task.Run(archive.Dispose); } } diff --git a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs index e7785de4..601b53fc 100644 --- a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs @@ -88,13 +88,7 @@ namespace LibationAvalonia.ViewModels internal void BindToGrid(List dbBooks) { - GridEntries = new(SOURCE) - { - Filter = CollectionFilter - }; - - GridEntries.CollectionChanged += (_, _) - => VisibleCountChanged?.Invoke(this, GridEntries.OfType().Count()); + GridEntries = new(SOURCE) { Filter = CollectionFilter }; var geList = dbBooks .Where(lb => lb.Book.IsProduct()) @@ -122,6 +116,9 @@ namespace LibationAvalonia.ViewModels //Create the filtered-in list before adding entries to avoid a refresh FilteredInGridEntries = QueryResults(geList, FilterString); SOURCE.AddRange(geList.OrderByDescending(e => e.DateAdded)); + VisibleCountChanged?.Invoke(this, GridEntries.OfType().Count()); + GridEntries.CollectionChanged += (_, _) + => VisibleCountChanged?.Invoke(this, GridEntries.OfType().Count()); } /// @@ -134,7 +131,7 @@ namespace LibationAvalonia.ViewModels //Add absent entries to grid, or update existing entry var allEntries = SOURCE.BookEntries(); var seriesEntries = SOURCE.SeriesEntries().ToList(); - var parentedEpisodes = dbBooks.ParentedEpisodes(); + var parentedEpisodes = dbBooks.ParentedEpisodes().ToList(); await Dispatcher.UIThread.InvokeAsync(() => { @@ -177,7 +174,6 @@ namespace LibationAvalonia.ViewModels #endregion await Filter(FilterString); - GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); } private void RemoveBooks(IEnumerable removedBooks, IEnumerable removedSeries) diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index a5376b56..9702c67b 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -266,7 +266,7 @@ namespace LibationWinForms.GridView var allEntries = bindingList.AllItems().BookEntries(); var seriesEntries = bindingList.AllItems().SeriesEntries().ToList(); - var parentedEpisodes = dbBooks.ParentedEpisodes(); + var parentedEpisodes = dbBooks.ParentedEpisodes().ToList(); foreach (var libraryBook in dbBooks.OrderBy(e => e.DateAdded)) { @@ -276,8 +276,8 @@ namespace LibationWinForms.GridView AddOrUpdateBook(libraryBook, existingEntry); else if(parentedEpisodes.Any(lb => lb == libraryBook)) //Only try to add or update is this LibraryBook is a know child of a parent - AddOrUpdateEpisode(libraryBook, existingEntry, seriesEntries, dbBooks); - } + AddOrUpdateEpisode(libraryBook, existingEntry, seriesEntries, dbBooks); + } bindingList.SuspendFilteringOnUpdate = false;